@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,45 +1,69 @@
1
- import { Component, Inject, ViewChild } from '@angular/core';
2
- import { OverlayPanel } from 'primeng/overlaypanel';
1
+ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, Inject, ViewChild, inject, NgZone, } from '@angular/core';
2
+ import { markForCheckInZone } from '../util/mark-for-check';
3
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
+ import { CommonModule } from '@angular/common';
5
+ import { FormsModule } from '@angular/forms';
6
+ import { ButtonModule } from 'primeng/button';
7
+ import { ConfirmDialogModule } from 'primeng/confirmdialog';
8
+ import { DropdownModule } from 'primeng/dropdown';
9
+ import { InputTextModule } from 'primeng/inputtext';
10
+ import { OverlayPanel, OverlayPanelModule } from 'primeng/overlaypanel';
11
+ import { ToolbarModule } from 'primeng/toolbar';
12
+ import { TooltipModule } from 'primeng/tooltip';
3
13
  import { saveAs } from 'file-saver';
4
- import { Subject, Subscription } from 'rxjs';
5
- import { debounceTime, switchMap } from 'rxjs/operators';
6
- import { Polygon, Rectangle, Region, MultiPolygon } from '../models/region';
7
- import { defaultPresetSet } from '../models/class-preset';
8
- import { colorForLabel } from '../store/class-color.util';
9
- import { ConfirmationService, MessageService } from 'primeng/api';
14
+ import { Subject } from 'rxjs';
15
+ import { Rectangle } from '../models/region';
16
+ import { withRegionPatch } from '../models/region-clone';
17
+ import { defaultPresetSet, parsePresetSet } from '../models/class-preset';
18
+ import { colorForLabel, presetKey } from '../store/class-color.util';
19
+ import { ConfirmationService, MessageService, SharedModule } from 'primeng/api';
10
20
  import { REGION_EDITOR_API } from '../contracts/region-editor-api.contract';
11
- import { REGION_IO_PORT } from '../contracts/ports/region-io.port';
12
- import { regionToParts, scaleParts, maskScaleFor } from './mask-raster';
13
21
  import { VIZ_TOAST_KEY } from '../toast-outlets';
22
+ import { RegionPersistenceService } from './region-persistence.service';
23
+ import { RegionColorDialogComponent } from './region-color-dialog/region-color-dialog.component';
24
+ import { ClassesPanelComponent } from './classes-panel/classes-panel.component';
25
+ import { RegionTableComponent } from './region-table/region-table.component';
26
+ import { SaveRegionsDialogComponent } from './save-regions-dialog/save-regions-dialog.component';
27
+ import { SaveMaskDialogComponent } from './save-mask-dialog/save-mask-dialog.component';
28
+ import { ManageClassesDialogComponent } from './manage-classes-dialog/manage-classes-dialog.component';
29
+ import { RegionEditorHelpComponent } from './region-editor-help/region-editor-help.component';
30
+ import { MaskExportService } from './mask-export.service';
31
+ import { formatArea, pickMpp, regionAreaPx } from './region-metrics';
14
32
  import * as i0 from "@angular/core";
15
33
  import * as i1 from "primeng/api";
16
- import * as i2 from "@angular/common";
17
- import * as i3 from "@angular/forms";
18
- import * as i4 from "primeng/toolbar";
34
+ import * as i2 from "./region-persistence.service";
35
+ import * as i3 from "./mask-export.service";
36
+ import * as i4 from "@angular/forms";
19
37
  import * as i5 from "primeng/button";
20
- import * as i6 from "primeng/tooltip";
38
+ import * as i6 from "primeng/confirmdialog";
21
39
  import * as i7 from "primeng/dropdown";
22
- import * as i8 from "primeng/dialog";
23
- import * as i9 from "primeng/progressbar";
24
- import * as i10 from "primeng/ripple";
25
- import * as i11 from "primeng/table";
26
- import * as i12 from "primeng/paginator";
27
- import * as i13 from "primeng/overlaypanel";
28
- import * as i14 from "primeng/confirmdialog";
29
- import * as i15 from "primeng/inputtext";
30
- import * as i16 from "primeng/checkbox";
31
- import * as i17 from "primeng/radiobutton";
32
- import * as i18 from "primeng/selectbutton";
33
- import * as i19 from "../hex-color-picker/hex-color-picker.component";
40
+ import * as i8 from "primeng/inputtext";
41
+ import * as i9 from "primeng/overlaypanel";
42
+ import * as i10 from "primeng/toolbar";
43
+ import * as i11 from "primeng/tooltip";
44
+ /**
45
+ * The Region Editor (`<region-editor>`): the table of the active image's
46
+ * annotation regions with class presets, colours, labels, GeoJSON import /
47
+ * export / server save and mask export. Self-contained: it talks to the viewer
48
+ * only through {@link REGION_EDITOR_API} (and the host's REGION_IO_PORT), so it
49
+ * has no inputs or outputs.
50
+ *
51
+ * OnPush: template events re-render it; state that lands asynchronously (the
52
+ * API's region / selection / preset / image-meta streams, save and mask-export
53
+ * progress, file reads) marks it for check.
54
+ */
34
55
  export class RegionEditorComponent {
35
- constructor(regionApi, messageService, confirmationService, regionIo) {
56
+ /** Re-render this OnPush view, re-entering the zone when the change came from outside it. */
57
+ changed() {
58
+ markForCheckInZone(this.cdr, this.zone);
59
+ }
60
+ constructor(regionApi, messageService, confirmationService, persistence, maskExport) {
36
61
  this.regionApi = regionApi;
37
62
  this.messageService = messageService;
38
63
  this.confirmationService = confirmationService;
39
- this.regionIo = regionIo;
40
- this.Array = Array;
64
+ this.persistence = persistence;
65
+ this.maskExport = maskExport;
41
66
  this.regions = [];
42
- this.regionsCopy = [];
43
67
  this.selectedRegions = [];
44
68
  this.displayHelpDialog = false;
45
69
  this.showColorDialog = false;
@@ -47,15 +71,13 @@ export class RegionEditorComponent {
47
71
  * "edit colour of selected regions" dialog. Unclassified regions (no label)
48
72
  * are grouped under a single entry with an empty `label`. */
49
73
  this.classColorEdits = [];
50
- this.labelColors = new Map();
51
- this.selectedLabelColor = '';
52
74
  /** "Edit class on selected rows" popup: chosen existing class, and a new-class name. */
53
75
  this.bulkClass = '';
54
76
  this.newBulkClass = '';
55
77
  // ── annotation-class presets (jit-ui#70) ──
56
78
  /** Local mirror of the per-user preset set (chip strip, Class dropdown, dialog). */
57
79
  this.presetSet = defaultPresetSet();
58
- /** Class applied to newly drawn/added regions and to bulk chip-clicks. */
80
+ /** The class last picked in the panel; a chip-click applies it to the selected rows. */
59
81
  this.activeClass = null;
60
82
  this.showManageDialog = false;
61
83
  /** Working copy edited inside the Manage-classes dialog; committed on Apply/Done. */
@@ -66,23 +88,22 @@ export class RegionEditorComponent {
66
88
  this.defaultClassName = 'Region';
67
89
  /** Classes as shown in the panel: sorted by region count (desc), then name. */
68
90
  this.displayClasses = [];
69
- this.matchModeOptions = [
70
- { label: 'Exact', value: 'exact' },
71
- { label: 'Normalized', value: 'normalized' },
72
- ];
73
91
  this.paginatorFirst = 0;
74
92
  this.paginatorRows = 10;
75
93
  this.rowsPerPageOptions = [10, 25, 50];
76
- /** Regions whose Class cell is currently in edit mode.
77
- * Using object identity as the key avoids needing a unique id field. */
78
- this.editingLabelRegions = new Set();
94
+ /** Regions whose Class cell is currently in edit mode → the label being typed.
95
+ * The draft is committed on Enter and discarded on Escape/✕, so typing never
96
+ * writes into the store's region (RT-18). Keyed by object identity, which
97
+ * avoids needing a unique id field. */
98
+ this.editingLabelRegions = new Map();
79
99
  this.showSaveAsDialog = false;
80
100
  this.saveAsFilename = '';
81
101
  this.saveAsFileExists = false;
82
102
  /** True while a GeoJSON persist is serializing/uploading — drives the dialog's
83
103
  * progress bar and Cancel button (parity with the Save-mask dialog). */
84
104
  this.saveAsBusy = false;
85
- this._saveAsCheck$ = new Subject();
105
+ /** Save-as file names to check for an existing file (debounced). */
106
+ this.saveAsCheck$ = new Subject();
86
107
  this.showExportDialog = false;
87
108
  this.exportFilename = '';
88
109
  this.showSaveMaskDialog = false;
@@ -96,7 +117,6 @@ export class RegionEditorComponent {
96
117
  /** 0–100 rasterization progress; switches to indeterminate during encoding. */
97
118
  this.maskProgress = 0;
98
119
  this.maskEncoding = false;
99
- this.pendingMaskFilename = '';
100
120
  /**
101
121
  * PrimeNG p-table multi-selection mode toggle. When true, single-click
102
122
  * selects one row (replacing prior); meta/ctrl-click toggles a row in/out
@@ -106,32 +126,14 @@ export class RegionEditorComponent {
106
126
  this.metaKey = true;
107
127
  this._updatingFromEditor = false;
108
128
  this._suppressSelectionSyncToPlot = false;
109
- this._regionSub = new Subscription();
110
- this._selectedIdxSub = new Subscription();
111
- this._metaSub = new Subscription();
112
- this._presetSub = new Subscription();
113
- /** trackBy for the class rows so re-sorting doesn't re-create the pickers. */
114
- this.trackByClassName = (_, c) => c.name;
115
- }
116
- /**
117
- * Deep copy of regions for dirty-tracking (`regionsCopy`). Returns plain-object
118
- * clones — only structural equality matters here, not prototypes.
119
- * TODO: replace with `structuredClone` once verified.
120
- */
121
- deepClone(items) {
122
- return items.map((item) => {
123
- if (Array.isArray(item)) {
124
- return this.deepClone(item);
125
- }
126
- else if (typeof item === 'object' && item !== null) {
127
- const cloned = {};
128
- for (const key in item) {
129
- cloned[key] = this.deepClone([item[key]])[0];
130
- }
131
- return cloned;
132
- }
133
- return item;
134
- });
129
+ this.destroyRef = inject(DestroyRef);
130
+ this.cdr = inject(ChangeDetectorRef);
131
+ this.zone = inject(NgZone);
132
+ /** Physical pixel size (µm/pixel) of the active image, for region areas in
133
+ * µm²; empty when the format reports no physical size. */
134
+ this.mpp = {};
135
+ this._pageKey = {};
136
+ this._page = [];
135
137
  }
136
138
  ngOnInit() {
137
139
  this.showShapeLabel = this.regionApi.getShowShapeLabel();
@@ -145,10 +147,11 @@ export class RegionEditorComponent {
145
147
  this.presetSet = initialPresets;
146
148
  const presets$ = this.regionApi.getPresetSet$?.();
147
149
  if (presets$) {
148
- this._presetSub = presets$.subscribe((set) => {
150
+ presets$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((set) => {
149
151
  if (set)
150
152
  this.presetSet = set;
151
153
  this.updateDisplayClasses();
154
+ this.changed();
152
155
  });
153
156
  }
154
157
  // Seed from the visualizer's current regions — already scoped to the
@@ -157,12 +160,14 @@ export class RegionEditorComponent {
157
160
  // Annotation regions only — intensity-profile lines are owned by the
158
161
  // intensity tool and excluded by the contract, so the editor never sees them.
159
162
  this.regions = this.applyRegionColors(this.regionApi.getAnnotationRegions());
160
- this.regionsCopy = this.deepClone(this.regions);
161
163
  this.syncClassesFromRegions(this.regions);
162
164
  this.recomputeClassCounts();
163
165
  // The update event is just a change signal; re-read the regions from the
164
166
  // visualizer rather than parsing whatever payload it carries.
165
- this._regionSub = this.regionApi.getRegionUpdateEvent().subscribe(() => {
167
+ this.regionApi
168
+ .getRegionUpdateEvent()
169
+ .pipe(takeUntilDestroyed(this.destroyRef))
170
+ .subscribe(() => {
166
171
  if (this._updatingFromEditor)
167
172
  return;
168
173
  const updated = this.applyRegionColors(this.regionApi.getAnnotationRegions());
@@ -170,33 +175,35 @@ export class RegionEditorComponent {
170
175
  // is a user-editable display label and may collide)
171
176
  const selectedIds = new Set(this.selectedRegions.map((r) => r.id));
172
177
  this.regions = updated;
173
- this.regionsCopy = this.deepClone(this.regions);
174
178
  this.selectedRegions = updated.filter((r) => selectedIds.has(r.id));
175
179
  this.clampPaginatorFirst();
176
180
  this.syncClassesFromRegions(updated);
177
181
  this.recomputeClassCounts();
182
+ this.changed();
178
183
  });
179
- this._saveAsCheck$.pipe(debounceTime(400), switchMap(name => this.regionIo.roiFileExists(name))).subscribe({
180
- next: exists => { this.saveAsFileExists = exists; },
181
- error: () => { this.saveAsFileExists = false; },
184
+ this.persistence
185
+ .fileExists(this.saveAsCheck$)
186
+ .pipe(takeUntilDestroyed(this.destroyRef))
187
+ .subscribe((exists) => {
188
+ this.saveAsFileExists = exists;
189
+ this.changed();
182
190
  });
183
191
  // Physical pixel size of the active image (for region areas in µm²/mm²).
184
- // Mirror PlotlyService.currentMpp: the calibration may sit on any channel
185
- // entry, not necessarily [0], so pick the first entry with a positive mppX
186
- // rather than reading [0] blindly. Fall back to square pixels (mppY = mppX)
187
- // when only one axis is reported.
188
- this._metaSub = this.regionApi.getImageMeta().subscribe((meta) => {
189
- const { mppX, mppY } = this.pickMpp(meta);
190
- this.mppX = mppX;
191
- this.mppY = mppY;
192
+ this.regionApi
193
+ .getImageMeta()
194
+ .pipe(takeUntilDestroyed(this.destroyRef))
195
+ .subscribe((meta) => {
196
+ this.mpp = pickMpp(meta);
197
+ this.changed();
192
198
  });
193
- this._selectedIdxSub = this.regionApi.getSelectedRegions$().subscribe((selected) => {
199
+ this.regionApi
200
+ .getSelectedRegions$()
201
+ .pipe(takeUntilDestroyed(this.destroyRef))
202
+ .subscribe((selected) => {
194
203
  if (this._suppressSelectionSyncToPlot)
195
204
  return;
196
205
  // Map the contract's selected regions to the editor's own instances by id.
197
- const next = selected
198
- .map((s) => this.regions.find((r) => r.id === s.id))
199
- .filter((r) => !!r);
206
+ const next = selected.map((s) => this.regions.find((r) => r.id === s.id)).filter((r) => !!r);
200
207
  const same = next.length === this.selectedRegions.length &&
201
208
  next.every((r, i) => r.id === this.selectedRegions[i]?.id);
202
209
  if (!same)
@@ -210,17 +217,14 @@ export class RegionEditorComponent {
210
217
  this.paginatorFirst = pageStart;
211
218
  }
212
219
  }
220
+ this.changed();
213
221
  });
214
222
  }
223
+ /** Cancels a running GeoJSON save (aborts its upload) and mask export (terminates
224
+ * its worker); the API subscriptions end through `takeUntilDestroyed`. */
215
225
  ngOnDestroy() {
216
- this._regionSub.unsubscribe();
217
- this._selectedIdxSub.unsubscribe();
218
- this._metaSub.unsubscribe();
219
- this._presetSub.unsubscribe();
220
- if (this._saveAsTimer !== undefined)
221
- clearTimeout(this._saveAsTimer);
222
226
  this._saveAsSub?.unsubscribe();
223
- this.teardownMaskWorker();
227
+ this.maskJob?.unsubscribe();
224
228
  }
225
229
  /**
226
230
  * Table → Plot: PrimeNG fires onRowSelect/onRowUnselect after updating
@@ -238,157 +242,96 @@ export class RegionEditorComponent {
238
242
  }
239
243
  }
240
244
  /**
241
- * Apply classification colours and refresh the label→colour map for regions
242
- * coming from the visualizer. The visualizer hands back neutral `Region`
243
- * objects with bounds, colour and label already populated, so the editor no
244
- * longer parses any backend-specific shape format — it only fills in a
245
- * fallback colour and rebuilds its local label→colour lookup.
245
+ * Rows for regions coming from the visualizer: neutral `Region`s with bounds,
246
+ * colour and label already populated. A region without a colour is shown with
247
+ * its classification colour (or the default shape colour) — as a copy: the
248
+ * store's instances are shared with its undo history and must not be changed
249
+ * in place (RT-1).
246
250
  */
247
251
  applyRegionColors(regions) {
248
- for (const region of regions) {
249
- region.color =
250
- region.color ||
251
- this.regionApi.getClassificationColors().get(region.label ?? '') ||
252
- this.shapeColor;
253
- }
254
- // Rebuild labelColors: seed from persisted map, then overlay actual region colors
255
- this.labelColors.clear();
256
- for (const [label, color] of this.regionApi.getClassificationColors()) {
257
- this.labelColors.set(label, color);
258
- }
259
- for (const region of regions) {
260
- if (region.label && region.color) {
261
- this.labelColors.set(region.label, region.color);
262
- }
263
- }
264
- return regions;
252
+ const classColors = this.regionApi.getClassificationColors();
253
+ return regions.map((region) => region.color
254
+ ? region
255
+ : withRegionPatch(region, { color: classColors.get(region.label ?? '') || this.shapeColor }));
265
256
  }
266
257
  /**
267
- * Push edits from the editor to the diagram in live-edit mode (no save /
268
- * cancel buttons). The `_updatingFromEditor` guard is still used so the
269
- * resulting regionUpdateEvent doesn't bounce back as an external change.
258
+ * The editor's one write path (RT-1): make `next` the table's rows and commit
259
+ * them to the store — live, as one undoable step (isRegionSaveOn=true; the
260
+ * router keeps the intensity-profile lines). Rows are the store's instances,
261
+ * so an edit passes copies ({@link patchRegions}), never changed rows. The
262
+ * resulting region-update event is ignored (`_updatingFromEditor`), so the
263
+ * class list and counts are refreshed here (a typed class is added; jit-ui#70).
270
264
  */
271
- setRegionsFromEditor(fillColor) {
265
+ commit(next = this.regions) {
266
+ this.regions = next;
272
267
  this._updatingFromEditor = true;
273
- // isRegionSaveOn=true so changes commit to the region store (and the per-image
274
- // cache) immediately. setAnnotationRegions preserves the intensity-profile
275
- // lines internally, so editor edits never disturb them.
276
- this.regionApi.setAnnotationRegions(this.regions, this.showShapeLabel, true, fillColor ?? this.fillColor);
277
- this._updatingFromEditor = false;
278
- // The editor's own commit is ignored by the region-update subscription (the
279
- // _updatingFromEditor guard), so refresh here: a class label typed in the
280
- // Class column gets added to the list, and the panel counts/order stay in
281
- // sync after any edit. (jit-ui#70)
282
- this.syncClassesFromRegions(this.regions);
268
+ try {
269
+ this.regionApi.setAnnotationRegions(next, this.showShapeLabel, true, this.fillColor);
270
+ }
271
+ finally {
272
+ this._updatingFromEditor = false;
273
+ }
274
+ this.syncClassesFromRegions(next);
283
275
  this.recomputeClassCounts();
284
- }
285
- /** Set one region's outline colour from the per-row picker and commit live.
286
- * Remembers it as the label's colour so same-class regions added later match
287
- * (jit-ui#85 — the Region Editor's per-region Color column). */
288
- changeRegionColor(region, color) {
289
- if (!color || region.color === color)
290
- return;
291
- region.color = color;
292
- region.colorOverridden = true; // explicit per-region colour — preserve it against preset (re)apply (jit-ui#70)
293
- if (region.label)
294
- this.labelColors.set(region.label, color);
295
- this.setRegionsFromEditor();
296
- }
297
- addRectangle() {
298
- const region = new Region();
299
- region.bounds = new Rectangle();
300
- region.bounds.width = 512;
301
- region.bounds.height = 512;
302
- region.label = this.activeClass ?? 'Region';
303
- // id and a non-colliding name are minted by the visualizer's setRegions
304
- this.regions = [...this.regions, region];
305
- this.setRegionsFromEditor();
306
- this.regionsCopy = this.deepClone(this.regions);
276
+ this.changed();
307
277
  }
308
278
  /**
309
- * Update the label of the region on enter key pressed (when editing a label cell in the table)
310
- * @param region
311
- * @param setRegion
279
+ * Copy-on-write edit (review RT-1). The editor's rows are the region store's
280
+ * live instances, and the store takes its undo snapshot only when the edit is
281
+ * committed — so an edit must replace a region with a patched copy (and any
282
+ * bounds it changes with new bounds), never mutate the instance the store
283
+ * holds. Returns the copies by original; the selection and an open label edit
284
+ * follow them.
312
285
  */
313
- labelRegionUpdate(region, setRegion = false) {
314
- if (region.label) {
315
- // if label doesn't exist
316
- if (!this.labelColors.has(region.label)) {
317
- this.labelColors.set(region.label, this.shapeColor);
318
- }
319
- }
320
- // update colors of the regions
321
- for (const reg of this.regions) {
322
- if (!reg.color) {
323
- if (reg.label && this.labelColors.has(reg.label)) {
324
- reg.color = this.labelColors.get(reg.label);
325
- }
326
- }
327
- }
328
- // update labels map
329
- this.labelColors.clear();
330
- for (const reg of this.regions) {
331
- if (reg.label && reg.color) {
332
- this.labelColors.set(reg.label, reg.color);
286
+ patchRegions(patchFor) {
287
+ const copies = new Map();
288
+ this.regions = this.regions.map((r) => {
289
+ const patch = patchFor(r);
290
+ if (!patch)
291
+ return r;
292
+ const copy = withRegionPatch(r, patch);
293
+ copies.set(r, copy);
294
+ return copy;
295
+ });
296
+ if (copies.size) {
297
+ this.selectedRegions = (this.selectedRegions ?? []).map((r) => copies.get(r) ?? r);
298
+ if ([...copies.keys()].some((r) => this.editingLabelRegions.has(r))) {
299
+ this.editingLabelRegions = new Map([...this.editingLabelRegions].map(([r, d]) => [copies.get(r) ?? r, d]));
333
300
  }
334
301
  }
335
- if (setRegion) {
336
- this.setRegionsFromEditor();
337
- }
302
+ return copies;
338
303
  }
339
- xRectUpdate(region, event) {
340
- const rectangle = region.bounds;
341
- if (rectangle && rectangle instanceof Rectangle) {
342
- rectangle.x = event.value;
343
- this.setRegionsFromEditor();
344
- }
304
+ /** Set one region's outline colour (an explicit override) and commit live. */
305
+ changeRegionColor(region, color) {
306
+ if (!color || region.color === color)
307
+ return;
308
+ // colorOverridden: explicit per-region colour — preserve it against preset (re)apply (jit-ui#70)
309
+ this.patchRegions((r) => (r === region ? { color, colorOverridden: true } : null));
310
+ this.commit();
345
311
  }
346
- yRectUpdate(region, event) {
347
- const rectangle = region.bounds;
348
- if (rectangle && rectangle instanceof Rectangle) {
349
- rectangle.y = event.value;
350
- this.setRegionsFromEditor();
351
- }
312
+ /**
313
+ * After a label edit on `region`: give every labelled row without a colour its
314
+ * class colour (preset or fallback, as the store will), and commit when
315
+ * `commit` is set.
316
+ */
317
+ labelRegionUpdate(region, commit = false) {
318
+ this.fillClassColors();
319
+ if (commit)
320
+ this.commit();
352
321
  }
353
- widthRectUpdate(region, event) {
354
- if (event.value === null || event.value === undefined)
355
- return;
356
- const rectangle = region.bounds;
357
- if (rectangle && rectangle instanceof Rectangle) {
358
- const reg = this.regionsCopy.filter((element) => element.id === region.id);
359
- if (reg.length) {
360
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
361
- // @ts-ignore
362
- const oldWidth = reg[0].bounds.width;
363
- const diffWidth = event.value - oldWidth;
364
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
365
- // @ts-ignore
366
- rectangle.x = Math.round(reg[0].bounds.x - diffWidth / 2);
367
- }
368
- rectangle.width = event.value;
369
- this.setRegionsFromEditor();
370
- this.regionsCopy = this.deepClone(this.regions);
371
- }
322
+ /** Labelled rows without a colour get their class colour (copies). */
323
+ fillClassColors() {
324
+ this.patchRegions((r) => (!r.color && r.label ? { color: this.colorForName(r.label) } : null));
372
325
  }
373
- heightRectUpdate(region, event) {
374
- if (event.value === null || event.value === undefined)
375
- return;
376
- const rectangle = region.bounds;
377
- if (rectangle && rectangle instanceof Rectangle) {
378
- const reg = this.regionsCopy.filter((element) => element.id === region.id);
379
- if (reg.length) {
380
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
381
- // @ts-ignore
382
- const oldHeight = reg[0].bounds.height;
383
- const diffHeight = event.value - oldHeight;
384
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
385
- // @ts-ignore
386
- rectangle.y = Math.round(reg[0].bounds.y - diffHeight / 2);
387
- }
388
- rectangle.height = event.value;
389
- this.setRegionsFromEditor();
390
- this.regionsCopy = this.deepClone(this.regions);
326
+ /** The current page of the table. Memoized on (regions, first, rows), so
327
+ * change detection doesn't hand p-table a new array every tick (RT-20). */
328
+ get pagedRegions() {
329
+ const key = this._pageKey;
330
+ if (key.regions !== this.regions || key.first !== this.paginatorFirst || key.rows !== this.paginatorRows) {
331
+ this._pageKey = { regions: this.regions, first: this.paginatorFirst, rows: this.paginatorRows };
332
+ this._page = this.regions.slice(this.paginatorFirst, this.paginatorFirst + this.paginatorRows);
391
333
  }
334
+ return this._page;
392
335
  }
393
336
  onPageChange(event) {
394
337
  this.paginatorFirst = event.first ?? 0;
@@ -404,10 +347,9 @@ export class RegionEditorComponent {
404
347
  // Filter by id — object identity isn't reliable across re-parses,
405
348
  // and name is a user-editable label that may collide.
406
349
  const removed = new Set(this.selectedRegions.map((r) => r.id));
407
- this.regions = this.regions.filter((r) => !removed.has(r.id));
408
350
  this.selectedRegions = [];
351
+ this.commit(this.regions.filter((r) => !removed.has(r.id)));
409
352
  this.clampPaginatorFirst();
410
- this.setRegionsFromEditor();
411
353
  // Sync the cleared selection with the plot's highlight state.
412
354
  this.onSelectionChanged();
413
355
  }
@@ -417,38 +359,20 @@ export class RegionEditorComponent {
417
359
  key: 'positionDialog',
418
360
  message: 'Are you sure you want to delete all regions?',
419
361
  accept: () => {
420
- this.regions = [];
421
362
  this.selectedRegions = [];
422
363
  this.paginatorFirst = 0;
423
- this.setRegionsFromEditor();
364
+ this.commit([]);
424
365
  this.onSelectionChanged();
425
366
  },
426
367
  });
427
368
  }
428
- addPolygon() {
429
- const region = new Region();
430
- region.bounds = new Polygon();
431
- region.label = this.activeClass ?? 'Region';
432
- region.bounds.ypoints = [0, 0, 0];
433
- region.bounds.xpoints = [0, 0, 0];
434
- region.bounds.coordinates = [
435
- [0, 0],
436
- [0, 0],
437
- [0, 0],
438
- ];
439
- region.bounds.npoints = 3;
440
- this.regions = [...this.regions, region];
441
- this.setRegionsFromEditor();
442
- this.regionsCopy = this.deepClone(this.regions);
443
- }
444
369
  deleteRegion(shapeIdx) {
445
370
  const removed = this.regions[shapeIdx];
446
- this.regions = this.regions.filter((_, i) => i !== shapeIdx);
447
371
  if (removed) {
448
372
  this.selectedRegions = this.selectedRegions.filter((r) => r.id !== removed.id);
449
373
  }
374
+ this.commit(this.regions.filter((_, i) => i !== shapeIdx));
450
375
  this.clampPaginatorFirst();
451
- this.setRegionsFromEditor();
452
376
  // Re-emit the (possibly trimmed) selection so the plot's highlight stays
453
377
  // in sync with what's still in the table.
454
378
  this.onSelectionChanged();
@@ -460,139 +384,65 @@ export class RegionEditorComponent {
460
384
  }
461
385
  }
462
386
  changeShowShapeLabel(showLabel) {
463
- this.setRegionsFromEditor();
387
+ this.commit();
464
388
  }
465
389
  isEditingLabel(region) {
466
390
  return this.editingLabelRegions.has(region);
467
391
  }
468
392
  startEditLabel(region, event) {
469
393
  event?.stopPropagation(); // don't toggle row selection
470
- this.editingLabelRegions.add(region);
394
+ // A new map per start/stop, so the OnPush table sees the change.
395
+ this.editingLabelRegions = new Map(this.editingLabelRegions).set(region, region.label ?? '');
396
+ }
397
+ /** The label typed so far for a row in edit mode (no re-render needed). */
398
+ setLabelDraft(region, value) {
399
+ if (this.editingLabelRegions.has(region))
400
+ this.editingLabelRegions.set(region, value);
471
401
  }
472
402
  stopEditLabel(region, commit, event) {
473
403
  event?.stopPropagation();
474
- this.editingLabelRegions.delete(region);
404
+ const draft = this.editingLabelRegions.get(region);
405
+ if (this.editingLabelRegions.has(region)) {
406
+ const next = new Map(this.editingLabelRegions);
407
+ next.delete(region);
408
+ this.editingLabelRegions = next;
409
+ }
475
410
  if (commit) {
476
- this.labelRegionUpdate(region, true);
411
+ // A changed label commits as a replacement region, so it is undoable (RT-1).
412
+ const edited = draft !== undefined && draft !== region.label
413
+ ? (this.patchRegions((r) => (r === region ? { label: draft } : null)).get(region) ?? region)
414
+ : region;
415
+ this.labelRegionUpdate(edited, true);
477
416
  }
478
417
  }
479
- isRectangle(region) {
480
- return region.bounds instanceof Rectangle;
481
- }
482
- /**
483
- * Region area for display: in µm² (or mm²) when the image reports a physical
484
- * pixel size, otherwise in px². Empty string for degenerate regions.
485
- */
418
+ /** Region area for display (µm²/mm² when the image is scaled, else px²);
419
+ * empty for a degenerate region. See `region-metrics`. */
486
420
  regionArea(region) {
487
- const px = this.areaInPixels(region);
488
- if (px <= 0)
489
- return '';
490
- if (this.mppX && this.mppY && this.mppX > 0 && this.mppY > 0) {
491
- const um2 = px * this.mppX * this.mppY;
492
- return um2 >= 1e6 ? `${this.fmtArea(um2 / 1e6)} mm²` : `${this.fmtArea(um2)} µm²`;
493
- }
494
- return `${this.fmtArea(px)} px²`;
495
- }
496
- /**
497
- * Choose the physical pixel size (µm/pixel) for area display from the image
498
- * meta. The calibration may sit on any channel entry — not necessarily [0] —
499
- * so pick the first entry with a positive mppX (mirrors
500
- * PlotlyService.currentMpp). Falls back to square pixels (mppY = mppX) when
501
- * only one axis is reported, so a scaled image shows µm²/mm² rather than px².
502
- */
503
- pickMpp(meta) {
504
- const m = Array.isArray(meta)
505
- ? (meta.find((e) => e && (e.mppX ?? 0) > 0) ?? meta[0])
506
- : undefined;
507
- const mx = m && (m.mppX ?? 0) > 0 ? m.mppX : undefined;
508
- const my = m && (m.mppY ?? 0) > 0 ? m.mppY : undefined;
509
- return { mppX: mx, mppY: my ?? mx };
510
- }
511
- /** Pixel area: width·height for rectangles, shoelace formula for polygons.
512
- * Interior rings (holes) are subtracted, so a donut reports the annulus area,
513
- * not the filled exterior (jit-ui#85). */
514
- areaInPixels(region) {
515
- const b = region.bounds;
516
- if (b instanceof Rectangle)
517
- return Math.abs(b.width * b.height);
518
- if (b instanceof Polygon)
519
- return this.polygonArea(b);
520
- // Multi-part region: sum each part's (exterior − holes) area (jit-ui#85).
521
- if (b instanceof MultiPolygon) {
522
- return b.polygons.reduce((sum, p) => sum + this.polygonArea(p), 0);
523
- }
524
- return 0;
525
- }
526
- /** Polygon area: exterior shoelace minus each interior ring (hole). */
527
- polygonArea(p) {
528
- if ((p.xpoints?.length ?? 0) < 3)
529
- return 0;
530
- let a = this.ringArea(p.xpoints, p.ypoints);
531
- if (p.holes) {
532
- for (const ring of p.holes)
533
- a -= this.ringArea(ring.map(pt => pt[0]), ring.map(pt => pt[1]));
534
- }
535
- return Math.max(0, a);
536
- }
537
- /** Absolute shoelace area of a single ring. */
538
- ringArea(xs, ys) {
539
- const n = xs?.length ?? 0;
540
- if (n < 3)
541
- return 0;
542
- let a = 0;
543
- for (let i = 0, j = n - 1; i < n; j = i++)
544
- a += (xs[j] + xs[i]) * (ys[j] - ys[i]);
545
- return Math.abs(a / 2);
546
- }
547
- fmtArea(n) {
548
- return (n >= 1000 ? Math.round(n) : Math.round(n * 100) / 100).toLocaleString();
421
+ return formatArea(regionAreaPx(region), this.mpp);
549
422
  }
550
423
  /**
551
424
  * This method rounds all the rectangle regions lengths to multiple of 512
552
425
  */
553
426
  roundRectangleLengths() {
554
- for (const region of this.regions) {
555
- if (region.bounds instanceof Rectangle) {
556
- const rect = region.bounds;
557
- rect.height = Math.round(rect.height / 512) * 512;
558
- rect.width = Math.round(rect.width / 512) * 512;
559
- }
560
- }
561
- this.setRegionsFromEditor();
562
- }
563
- /**
564
- * Stop key arrow event propagation
565
- * @param event
566
- */
567
- disableArrowKeys(event) {
568
- if (event.key === 'ArrowDown' ||
569
- event.key === 'Down' ||
570
- event.key === 'ArrowUp' ||
571
- event.key === 'Up' ||
572
- event.key === 'ArrowLeft' ||
573
- event.key === 'Left' ||
574
- event.key === 'ArrowRight' ||
575
- event.key === 'Right') {
576
- event.stopPropagation();
577
- }
578
- }
579
- changeShapeColor(event) {
580
- this.shapeColor = event.value;
581
- for (const region of this.regions) {
582
- if (region.label === this.selectedLabelColor && this.labelColors.has(region.label)) {
583
- this.labelColors.set(region.label, this.shapeColor);
584
- region.color = this.shapeColor;
585
- }
586
- }
587
- this.setRegionsFromEditor(this.fillColor);
427
+ this.patchRegions((region) => {
428
+ const rect = region.bounds;
429
+ if (!(rect instanceof Rectangle))
430
+ return null;
431
+ return {
432
+ bounds: Object.assign(new Rectangle(), rect, {
433
+ height: Math.round(rect.height / 512) * 512,
434
+ width: Math.round(rect.width / 512) * 512,
435
+ }),
436
+ };
437
+ });
438
+ this.commit();
588
439
  }
589
440
  showHelp() {
590
441
  this.displayHelpDialog = true;
591
442
  }
592
443
  /** Open the colour dialog for the currently-selected region(s), building one
593
444
  * colour picker per unique class in the selection. Each picker is seeded with
594
- * the class's current colour (the label→colour map, falling back to the first
595
- * selected region of that class). */
445
+ * the first selected region of that class's colour (else the class colour). */
596
446
  openColorDialog() {
597
447
  if (!this.selectedRegions?.length)
598
448
  return;
@@ -601,27 +451,27 @@ export class RegionEditorComponent {
601
451
  const label = region.label?.trim() ?? '';
602
452
  if (seedByLabel.has(label))
603
453
  continue;
604
- seedByLabel.set(label, (label ? this.labelColors.get(label) : undefined) ?? region.color ?? this.shapeColor);
454
+ seedByLabel.set(label, region.color ?? (label ? this.colorForName(label) : this.shapeColor));
605
455
  }
606
456
  this.classColorEdits = [...seedByLabel].map(([label, color]) => ({ label, color }));
607
457
  this.showColorDialog = true;
608
458
  }
609
- /** Apply each class's chosen colour to the selected regions of that class and
610
- * commit live. Remembers the colour per class so same-class regions added
611
- * later match. */
612
- applyColorToSelected() {
613
- const colorByLabel = new Map(this.classColorEdits.map((e) => [e.label, e.color]));
614
- for (const region of this.selectedRegions ?? []) {
459
+ /** Apply each class's chosen colour to the selected regions of that class (an
460
+ * explicit override) and commit live. */
461
+ applyColorToSelected(edits = this.classColorEdits) {
462
+ const colorByLabel = new Map(edits.map((e) => [e.label, e.color]));
463
+ const selected = new Set(this.selectedRegions ?? []);
464
+ this.patchRegions((region) => {
465
+ if (!selected.has(region))
466
+ return null;
615
467
  const label = region.label?.trim() ?? '';
616
468
  const color = colorByLabel.get(label);
617
469
  if (!color)
618
- continue;
619
- region.color = color;
620
- region.colorOverridden = true; // explicit colour — preserve against preset (re)apply (jit-ui#70)
621
- if (label)
622
- this.labelColors.set(label, color);
623
- }
624
- this.setRegionsFromEditor();
470
+ return null;
471
+ // explicit colour — preserve against preset (re)apply (jit-ui#70)
472
+ return { color, colorOverridden: true };
473
+ });
474
+ this.commit();
625
475
  this.showColorDialog = false;
626
476
  }
627
477
  // ── annotation-class presets (jit-ui#70) ─────────────────────────────
@@ -638,8 +488,7 @@ export class RegionEditorComponent {
638
488
  * labels are ignored. (jit-ui#70)
639
489
  */
640
490
  syncClassesFromRegions(regions) {
641
- const normalized = this.presetSet.matchMode === 'normalized';
642
- const keyOf = (l) => (normalized ? l.trim().toLowerCase() : l);
491
+ const keyOf = (l) => presetKey(this.presetSet, l);
643
492
  const known = new Set(this.presetSet.classes.map((c) => keyOf(c.name)));
644
493
  const added = new Set();
645
494
  for (const r of regions) {
@@ -660,12 +509,11 @@ export class RegionEditorComponent {
660
509
  // ── Docked Classes panel (jit-ui#70) ─────────────────────────────────
661
510
  /** Rebuild the per-class region counts shown in the panel. */
662
511
  recomputeClassCounts() {
663
- const normalized = this.presetSet.matchMode === 'normalized';
664
512
  const counts = new Map();
665
513
  for (const r of this.regions) {
666
514
  if (!r.label)
667
515
  continue;
668
- const k = normalized ? r.label.trim().toLowerCase() : r.label;
516
+ const k = presetKey(this.presetSet, r.label);
669
517
  counts.set(k, (counts.get(k) ?? 0) + 1);
670
518
  }
671
519
  this.classCounts = counts;
@@ -680,22 +528,14 @@ export class RegionEditorComponent {
680
528
  }
681
529
  /** Number of regions currently using a class (keyed by the active match mode). */
682
530
  classCount(name) {
683
- const k = this.presetSet.matchMode === 'normalized' ? name.trim().toLowerCase() : name;
684
- return this.classCounts.get(k) ?? 0;
531
+ return this.classCounts.get(presetKey(this.presetSet, name)) ?? 0;
685
532
  }
686
533
  /** Recolour a class from its panel swatch and repaint its (non-overridden) regions. */
687
534
  setClassColor(name, color) {
688
535
  if (!color)
689
536
  return;
690
537
  this.regionApi.setClassificationColor(name, color);
691
- this.setRegionsFromEditor();
692
- }
693
- /** Tooltip for a class's delete button in the panel. */
694
- deleteClassTooltip(name) {
695
- const inUse = this.classCount(name) > 0;
696
- if (name === this.defaultClassName && inUse)
697
- return 'The default class cannot be removed while in use';
698
- return inUse ? 'Remove class — its regions revert to Region' : 'Remove class';
538
+ this.commit();
699
539
  }
700
540
  /** Remove a class. Any regions still using it fall back to the default
701
541
  * "Region" class, so they aren't orphaned (and the class isn't immediately
@@ -705,36 +545,31 @@ export class RegionEditorComponent {
705
545
  this.regionApi.removeClass(name);
706
546
  if (this.activeClass === name)
707
547
  this.activeClass = null;
708
- this.setRegionsFromEditor();
548
+ this.commit();
709
549
  }
710
550
  /** Regions labelled with any of `removedNames` revert to the default
711
551
  * {@link defaultClassName} ("Region"): its class colour, override cleared.
712
552
  * Skips the default itself so deleting "Region" can't self-reassign. (jit-ui#70) */
713
553
  reassignRegionsToDefaultClass(removedNames) {
714
- const norm = this.presetSet.matchMode === 'normalized';
715
- const keyOf = (s) => (norm ? s.trim().toLowerCase() : s);
554
+ const keyOf = (s) => presetKey(this.presetSet, s);
716
555
  const removed = new Set(removedNames.map(keyOf));
717
556
  removed.delete(keyOf(this.defaultClassName));
718
557
  if (!removed.size)
719
558
  return;
720
559
  const color = colorForLabel(this.defaultClassName, this.presetSet);
721
- for (const r of this.regions) {
722
- if (r.label && removed.has(keyOf(r.label))) {
723
- r.label = this.defaultClassName;
724
- r.colorOverridden = false;
725
- r.color = color;
726
- }
727
- }
560
+ this.patchRegions((r) => r.label && removed.has(keyOf(r.label))
561
+ ? { label: this.defaultClassName, colorOverridden: false, color }
562
+ : null);
728
563
  }
729
564
  /** Stamp a class (and its preset/fallback colour) onto one region from the Class
730
565
  * dropdown; choosing a class opts the region back into the preset colour. */
731
566
  applyPresetToRegion(region, className) {
732
567
  const name = (className ?? '').trim();
733
- region.label = name;
734
- region.colorOverridden = false;
568
+ const patch = { label: name, colorOverridden: false };
735
569
  if (name)
736
- region.color = colorForLabel(name, this.presetSet);
737
- this.setRegionsFromEditor();
570
+ patch.color = colorForLabel(name, this.presetSet);
571
+ this.patchRegions((r) => (r === region ? patch : null));
572
+ this.commit();
738
573
  }
739
574
  /** Make a class active (used for new regions) and, if rows are selected, apply
740
575
  * it to them in one commit. */
@@ -744,15 +579,12 @@ export class RegionEditorComponent {
744
579
  }
745
580
  /** Apply a class (label + preset/fallback colour) to the current selection and commit. */
746
581
  applyClassToSelected(name) {
747
- const selected = this.selectedRegions ?? [];
748
- if (!selected.length)
582
+ const selected = new Set(this.selectedRegions ?? []);
583
+ if (!selected.size)
749
584
  return;
750
- for (const r of selected) {
751
- r.label = name;
752
- r.colorOverridden = false;
753
- r.color = colorForLabel(name, this.presetSet);
754
- }
755
- this.setRegionsFromEditor();
585
+ const color = colorForLabel(name, this.presetSet);
586
+ this.patchRegions((r) => (selected.has(r) ? { label: name, colorOverridden: false, color } : null));
587
+ this.commit();
756
588
  }
757
589
  /** "Edit class on selected rows" popup — apply the chosen existing class. */
758
590
  applyBulkClass() {
@@ -766,8 +598,8 @@ export class RegionEditorComponent {
766
598
  const name = this.newBulkClass.trim();
767
599
  if (!name)
768
600
  return;
769
- const norm = this.presetSet.matchMode === 'normalized';
770
- const exists = this.presetSet.classes.some((c) => norm ? c.name.trim().toLowerCase() === name.toLowerCase() : c.name === name);
601
+ const key = presetKey(this.presetSet, name);
602
+ const exists = this.presetSet.classes.some((c) => presetKey(this.presetSet, c.name) === key);
771
603
  if (!exists) {
772
604
  this.regionApi.upsertClass({ name, color: colorForLabel(name, this.presetSet), source: 'user' });
773
605
  }
@@ -789,18 +621,6 @@ export class RegionEditorComponent {
789
621
  matchMode: s.matchMode === 'normalized' ? 'normalized' : 'exact',
790
622
  };
791
623
  }
792
- addPresetClass() {
793
- this.presetDraft?.classes.push({ name: '', color: '#888888', source: 'user' });
794
- }
795
- removePresetClass(i) {
796
- this.presetDraft?.classes.splice(i, 1);
797
- }
798
- addFallbackColor() {
799
- this.presetDraft?.fallbackPalette.push('#888888');
800
- }
801
- removeFallbackColor(i) {
802
- this.presetDraft?.fallbackPalette.splice(i, 1);
803
- }
804
624
  /** Commit the draft: drop blank/duplicate names, persist, and recolour
805
625
  * non-overridden regions with the updated presets. */
806
626
  applyManageDialog(close) {
@@ -808,109 +628,95 @@ export class RegionEditorComponent {
808
628
  return;
809
629
  // De-duplicate using the active match mode's key so normalized mode can't keep
810
630
  // both "Tumor" and "tumor" (which would collide at runtime in findPreset()).
811
- const norm = this.presetDraft.matchMode === 'normalized';
631
+ const draft = this.presetDraft;
632
+ const keyOf = (s) => presetKey(draft, s);
812
633
  const seen = new Set();
813
634
  const classes = [];
814
- for (const c of this.presetDraft.classes) {
635
+ for (const c of draft.classes) {
815
636
  const name = (c.name ?? '').trim();
816
- const key = norm ? name.toLowerCase() : name;
637
+ const key = keyOf(name);
817
638
  if (!name || seen.has(key))
818
639
  continue;
819
640
  seen.add(key);
820
641
  classes.push({ ...c, name });
821
642
  }
822
- this.presetDraft.classes = classes;
643
+ this.presetDraft = { ...draft, classes };
823
644
  // Classes dropped in the dialog: their regions revert to the default "Region"
824
645
  // class (else syncClassesFromRegions would just re-add them) (jit-ui#70).
825
- const keyOf = (s) => (norm ? s.trim().toLowerCase() : s);
826
646
  const kept = new Set(classes.map((c) => keyOf(c.name)));
827
- const removed = this.presetSet.classes
828
- .map((c) => c.name)
829
- .filter((n) => !kept.has(keyOf(n)));
647
+ const removed = this.presetSet.classes.map((c) => c.name).filter((n) => !kept.has(keyOf(n)));
830
648
  this.reassignRegionsToDefaultClass(removed);
831
649
  this.regionApi.setPresetSet(this.presetDraft);
832
- this.setRegionsFromEditor(); // recolour existing (non-overridden) regions from the new presets
650
+ this.commit(); // recolour existing (non-overridden) regions from the new presets
833
651
  if (close)
834
652
  this.showManageDialog = false;
835
653
  }
836
654
  resetPresetsToDefaults() {
837
655
  this.regionApi.resetPresets();
838
656
  this.presetDraft = this.clonePresetSet(this.regionApi.getPresetSet());
839
- this.setRegionsFromEditor();
657
+ this.commit();
840
658
  }
841
659
  exportPresets() {
842
660
  const json = JSON.stringify(this.regionApi.getPresetSet(), null, 2);
843
661
  saveAs(new Blob([json], { type: 'application/json' }), 'annotation-classes.json');
844
662
  }
845
663
  importPresets(event) {
846
- const input = event.target;
847
- const file = input.files?.[0];
848
- if (!file)
849
- return;
850
- const reader = new FileReader();
851
- reader.onload = (e) => {
852
- try {
853
- const set = JSON.parse(e.target.result);
854
- this.regionApi.setPresetSet(set);
855
- this.presetDraft = this.clonePresetSet(this.regionApi.getPresetSet());
856
- this.setRegionsFromEditor();
857
- this.messageService.add({ key: VIZ_TOAST_KEY, severity: 'success',
858
- summary: 'Classes imported', detail: 'Annotation classes loaded.' });
859
- }
860
- catch (err) {
861
- this.messageService.add({ key: VIZ_TOAST_KEY, severity: 'error',
862
- summary: 'Import failed', detail: `${err}` });
863
- }
864
- };
865
- reader.readAsText(file);
664
+ this.persistence.readChosenFile(event, (text) => this.applyImportedPresets(text));
665
+ }
666
+ /** Validate and apply an imported annotation-classes JSON file. */
667
+ applyImportedPresets(text) {
668
+ try {
669
+ const set = parsePresetSet(JSON.parse(text));
670
+ if (!set)
671
+ throw new Error('The file is not an annotation-class list (no valid classes).');
672
+ this.regionApi.setPresetSet(set);
673
+ this.presetDraft = this.clonePresetSet(this.regionApi.getPresetSet());
674
+ this.commit();
675
+ this.messageService.add({
676
+ key: VIZ_TOAST_KEY,
677
+ severity: 'success',
678
+ summary: 'Classes imported',
679
+ detail: 'Annotation classes loaded.',
680
+ });
681
+ }
682
+ catch (err) {
683
+ this.messageService.add({
684
+ key: VIZ_TOAST_KEY,
685
+ severity: 'error',
686
+ summary: 'Import failed',
687
+ detail: `${err?.message ?? err}`,
688
+ });
689
+ }
690
+ this.changed();
866
691
  }
867
692
  importRois(event) {
868
- const input = event.target;
869
- const file = input.files?.[0];
870
- if (!file)
871
- return;
872
- const reader = new FileReader();
873
- reader.onload = (e) => {
874
- const fileContent = e.target.result;
875
- try {
876
- this.regions = this.regionApi.importRegions(fileContent);
877
- // set label colors
878
- for (const region of this.regions) {
879
- this.labelRegionUpdate(region, false);
880
- }
881
- this.syncClassesFromRegions(this.regions);
882
- this.setRegionsFromEditor();
883
- }
884
- catch (event) {
885
- this.messageService.add({
886
- severity: 'error',
887
- summary: 'Error importing the file',
888
- detail: `${event}`,
889
- });
890
- console.error('Error reading the file: ' + event);
891
- }
892
- };
893
- reader.readAsText(file);
693
+ this.persistence.readChosenFile(event, (text) => this.applyImportedRois(text));
894
694
  }
895
- /**
896
- * Regions to serialize on save/export. For a single-file z-stack the store
897
- * keeps only the current slice live, so pull EVERY slice's annotations (each
898
- * tagged with its zero-based Region.z) to write one combined z-indexed
899
- * geojson (jit-ui#93). Otherwise (single-plane image, or a folder stack whose
900
- * slices save to their own per-slice files) the current set. (jit-ui#93)
901
- */
902
- regionsForSave() {
903
- if (this.regionApi.isStackMode() && this.regionApi.getStackSaveLayout() === 'combined') {
904
- return this.regionApi.getSliceAnnotationRegions();
695
+ /** Replace the regions with an imported GeoJSON file's. */
696
+ applyImportedRois(text) {
697
+ try {
698
+ this.regions = this.regionApi.importRegions(text);
699
+ this.fillClassColors();
700
+ this.commit();
905
701
  }
906
- return this.regions;
702
+ catch (err) {
703
+ this.messageService.add({
704
+ key: VIZ_TOAST_KEY,
705
+ severity: 'error',
706
+ summary: 'Error importing the file',
707
+ detail: `${err?.message ?? err}`,
708
+ });
709
+ console.error('Error reading the file:', err);
710
+ }
711
+ }
712
+ /** Regions to serialize on save/export (every slice for a combined z-stack). */
713
+ regionsForSave() {
714
+ return this.persistence.regionsForSave(this.regions);
907
715
  }
908
716
  exportRois() {
909
717
  if (!this.regionsForSave().length)
910
718
  return;
911
- const name = this.regionIo.getSelectedFileName();
912
- const stem = name ? name.substring(0, name.lastIndexOf('.')) : 'rois';
913
- this.exportFilename = `${stem}.geojson`;
719
+ this.exportFilename = this.persistence.defaultExportName();
914
720
  this.showExportDialog = true;
915
721
  }
916
722
  confirmExport() {
@@ -919,29 +725,21 @@ export class RegionEditorComponent {
919
725
  if (!filename || !regions.length)
920
726
  return;
921
727
  this.showExportDialog = false;
922
- const jsonString = this.regionApi.getGeoJsonString(regions);
923
- const blob = new Blob([jsonString], { type: 'application/json' });
924
- saveAs(blob, filename);
728
+ this.persistence.download(regions, filename);
925
729
  }
926
730
  /** Open the "Save mask" dialog, seeded with `<image-stem>_mask.png`. */
927
731
  openSaveMaskDialog() {
928
732
  if (!this.regions.length)
929
733
  return;
930
- const name = this.regionIo.getSelectedFileName();
931
- // Strip the extension only when there is one; an extension-less name keeps
932
- // its whole stem (a leading-dot dotfile is treated as having no extension)
933
- // so we never produce a bare "_mask.png".
934
- const dot = name ? name.lastIndexOf('.') : -1;
935
- const stem = name ? (dot > 0 ? name.substring(0, dot) : name) : 'regions';
936
- this.saveMaskFilename = `${stem}_mask.png`;
734
+ this.saveMaskFilename = this.persistence.defaultMaskName();
937
735
  this.maskMode = 'binary';
938
736
  this.showSaveMaskDialog = true;
939
737
  }
940
738
  /**
941
739
  * Rasterize the regions to the chosen mask type and download as a PNG. The
942
- * heavy work (full-res rasterize + PNG encode) runs in a Web Worker so the UI
943
- * never freezes on large/whole-slide images and the job can be cancelled
944
- * (jit-ui#95). The dialog stays open showing a progress bar until done.
740
+ * heavy work runs in a Web Worker ({@link MaskExportService}) so the UI never
741
+ * freezes on whole-slide images and the job can be cancelled (jit-ui#95);
742
+ * the dialog stays open with a progress bar until done.
945
743
  */
946
744
  confirmSaveMask() {
947
745
  const filename = this.saveMaskFilename.trim();
@@ -952,83 +750,64 @@ export class RegionEditorComponent {
952
750
  this.maskError('No image size is available, so the mask cannot be sized.');
953
751
  return;
954
752
  }
955
- // Whole-slide images exceed the browser's typed-array/memory limits, so cap
956
- // the mask to a safe pixel budget and scale the geometry to match.
957
- const scale = maskScaleFor(size.width, size.height);
958
- const width = Math.max(1, Math.round(size.width * scale));
959
- const height = Math.max(1, Math.round(size.height * scale));
960
- if (scale < 1) {
961
- this.messageService.add({
962
- key: VIZ_TOAST_KEY,
963
- severity: 'info',
964
- summary: 'Mask downscaled',
965
- detail: `Image too large for a full-resolution mask; saving at ${width}×${height}.`,
966
- });
967
- }
968
- const regions = this.regions.map((r) => scaleParts(regionToParts(r), scale));
969
- this.pendingMaskFilename = filename;
970
753
  this.maskBusy = true;
971
754
  this.maskEncoding = false;
972
755
  this.maskProgress = 0;
973
- const payload = {
974
- width, height,
975
- originalWidth: size.width, originalHeight: size.height, scale,
756
+ this.maskJob = this.maskExport
757
+ .export({
758
+ regions: this.regions,
759
+ imageSize: size,
976
760
  mode: this.maskMode,
977
- sourceName: this.regionIo.getSelectedFileName(),
978
- regions,
979
- };
980
- // createMaskWorker() is async (the worker module is imported dynamically so
981
- // its import.meta never reaches the CommonJS test compile). Wire up once it
982
- // resolves — unless the user cancelled while it was loading.
983
- this.createMaskWorker().then((worker) => {
984
- if (!this.maskBusy) {
985
- worker.terminate();
986
- return;
987
- }
988
- this.maskWorker = worker;
989
- worker.onmessage = ({ data }) => {
990
- switch (data?.type) {
761
+ sourceName: this.persistence.selectedFileName(),
762
+ })
763
+ .subscribe({
764
+ next: (e) => {
765
+ this.changed();
766
+ switch (e.type) {
767
+ case 'planned':
768
+ if (e.scale < 1) {
769
+ this.messageService.add({
770
+ key: VIZ_TOAST_KEY,
771
+ severity: 'info',
772
+ summary: 'Mask downscaled',
773
+ detail: `Image too large for a full-resolution mask; saving at ${e.width}×${e.height}.`,
774
+ });
775
+ }
776
+ break;
991
777
  case 'progress':
992
- this.maskProgress = data.total ? Math.round((data.done / data.total) * 100) : 0;
778
+ this.maskProgress = e.percent;
993
779
  break;
994
780
  case 'encoding':
995
781
  this.maskEncoding = true;
996
782
  break;
997
783
  case 'done':
998
- this.finishMask(new Blob([data.png], { type: 'image/png' }));
999
- break;
1000
- case 'error':
1001
- this.maskError(data.error || 'The mask could not be generated.');
784
+ this.finishMask(e.blob, filename);
1002
785
  break;
1003
786
  }
1004
- };
1005
- worker.onerror = () => this.maskError('The mask worker failed.');
1006
- worker.postMessage(payload);
1007
- }).catch(() => this.maskError('The mask worker failed to start.'));
787
+ },
788
+ error: (err) => {
789
+ this.maskError(err.message);
790
+ this.changed();
791
+ },
792
+ });
1008
793
  }
1009
- /** Cancel an in-progress mask export: terminate the worker and reset state. */
794
+ /** Cancel an in-progress mask export (terminates its worker) and reset state. */
1010
795
  cancelSaveMask() {
1011
- this.teardownMaskWorker();
796
+ this.maskJob?.unsubscribe();
797
+ this.maskJob = undefined;
1012
798
  this.maskBusy = false;
1013
799
  this.maskEncoding = false;
1014
800
  this.maskProgress = 0;
1015
801
  }
1016
- /** Worker factory — async so the worker module (and its `import.meta.url`,
1017
- * which the CommonJS test compile rejects) is loaded via a dynamic import,
1018
- * exactly like the segmentation worker. Overridable in tests. */
1019
- async createMaskWorker() {
1020
- const { createMaskWorker } = await import('./mask-worker');
1021
- return createMaskWorker();
1022
- }
1023
- finishMask(blob) {
1024
- this.teardownMaskWorker();
802
+ finishMask(blob, filename) {
803
+ this.maskJob = undefined;
1025
804
  this.maskBusy = false;
1026
805
  this.maskEncoding = false;
1027
806
  this.showSaveMaskDialog = false;
1028
- saveAs(blob, this.pendingMaskFilename);
807
+ saveAs(blob, filename);
1029
808
  }
1030
809
  maskError(detail) {
1031
- this.teardownMaskWorker();
810
+ this.maskJob = undefined;
1032
811
  this.maskBusy = false;
1033
812
  this.maskEncoding = false;
1034
813
  this.messageService.add({
@@ -1038,115 +817,73 @@ export class RegionEditorComponent {
1038
817
  detail,
1039
818
  });
1040
819
  }
1041
- teardownMaskWorker() {
1042
- this.maskWorker?.terminate();
1043
- this.maskWorker = undefined;
1044
- }
1045
820
  persistRegions() {
1046
821
  // Folder stack: write each slice's regions back to its own slice-file's
1047
- // sibling geojson — no single-filename prompt (the filenames are the slice
1048
- // files'). (jit-ui#93)
1049
- if (this.regionApi.isStackMode() && this.regionApi.getStackSaveLayout() === 'per-slice-file') {
822
+ // sibling geojson — no single-filename prompt (jit-ui#93).
823
+ if (this.persistence.savesPerSlice()) {
1050
824
  this.saveStackSlices();
1051
825
  return;
1052
826
  }
1053
- const name = this.regionIo.getSelectedFileName();
1054
- if (!name || !this.regionsForSave().length)
827
+ const filename = this.persistence.defaultSaveName();
828
+ if (!filename || !this.regionsForSave().length)
1055
829
  return;
1056
- this.saveAsFilename = name.substring(0, name.lastIndexOf('.')) + '.geojson';
830
+ this.saveAsFilename = filename;
1057
831
  this.saveAsFileExists = false;
1058
832
  this.showSaveAsDialog = true;
1059
- this._saveAsCheck$.next(this.saveAsFilename);
833
+ this.saveAsCheck$.next(this.saveAsFilename);
1060
834
  }
1061
- /**
1062
- * Save a folder stack's regions as one geojson per slice-file (jit-ui#93).
1063
- * Groups the store's per-slice regions by slice index and serializes each on
1064
- * the default plane (z=0) — each slice-file is itself one plane, and the
1065
- * loader re-derives the slice index from the file's position in the series.
1066
- * Slices cleared since load are included (empty) so their file is overwritten.
1067
- */
835
+ /** Save a folder stack's regions as one geojson per slice-file (jit-ui#93).
836
+ * No dialog covers the toolbar meanwhile, so a Save while one is uploading
837
+ * is ignored rather than starting a second write (and orphaning the first). */
1068
838
  saveStackSlices() {
1069
- const bySlice = this.regionApi.getStackSaveAnnotationSlices();
1070
- if (!bySlice.size)
839
+ if (this.saveAsBusy)
840
+ return;
841
+ const slices = this.persistence.sliceGeoJsons();
842
+ if (!slices.length)
1071
843
  return;
1072
- const slices = [];
1073
- for (const [z, regs] of bySlice) {
1074
- const flat = regs.map((r) => Object.assign(new Region(), r, { z: 0 }));
1075
- slices.push({ z, geoJsonStr: this.regionApi.getGeoJsonString(flat) });
1076
- }
1077
844
  this.saveAsBusy = true;
1078
- this._saveAsSub = this.regionIo.saveSliceGeoJsons(slices).subscribe({
845
+ this._saveAsSub = this.persistence.saveSlices(slices).subscribe({
1079
846
  next: () => {
1080
847
  this.saveAsBusy = false;
1081
- this.messageService.add({
1082
- key: VIZ_TOAST_KEY,
1083
- severity: 'success',
1084
- summary: 'Regions saved',
1085
- detail: `Saved ROIs for ${slices.length} slice${slices.length === 1 ? '' : 's'}`,
1086
- });
848
+ this.changed();
849
+ this.toast('success', 'Regions saved', `Saved ROIs for ${slices.length} slice${slices.length === 1 ? '' : 's'}`);
1087
850
  },
1088
851
  error: (err) => {
1089
852
  this.saveAsBusy = false;
1090
- this.messageService.add({
1091
- key: VIZ_TOAST_KEY,
1092
- severity: 'error',
1093
- summary: 'Error saving regions',
1094
- detail: `${err?.message ?? err}`,
1095
- });
853
+ this.changed();
854
+ this.toast('error', 'Error saving regions', `${err?.message ?? err}`);
1096
855
  },
1097
856
  });
1098
857
  }
1099
858
  checkSaveAsFileExists() {
1100
- this._saveAsCheck$.next(this.saveAsFilename);
859
+ this.saveAsCheck$.next(this.saveAsFilename);
1101
860
  }
1102
861
  confirmSaveAs() {
1103
- if (!this.regionIo.getSelectedFileName())
862
+ if (!this.persistence.selectedFileName())
1104
863
  return;
1105
864
  const filename = this.saveAsFilename.trim();
1106
865
  if (!filename)
1107
866
  return;
867
+ // Keep the dialog open showing an (indeterminate) progress bar while the
868
+ // regions serialize and upload, then close on success.
1108
869
  const doSave = () => {
1109
- // Keep the dialog open showing an (indeterminate) progress bar while the
1110
- // regions serialize and upload, then close on success. Defer one tick so
1111
- // the bar paints before a large synchronous GeoJSON serialize.
1112
870
  this.saveAsBusy = true;
1113
- this._saveAsTimer = setTimeout(() => {
1114
- this._saveAsTimer = undefined;
1115
- let geoJsonStr;
1116
- try {
1117
- geoJsonStr = this.regionApi.getGeoJsonString(this.regionsForSave());
1118
- }
1119
- catch (err) {
871
+ // Also run from the overwrite confirmation, which the host's dialog may answer.
872
+ this.changed();
873
+ this._saveAsSub = this.persistence
874
+ .save(() => this.regionsForSave(), filename)
875
+ .subscribe({
876
+ next: () => {
1120
877
  this.saveAsBusy = false;
1121
- this.messageService.add({
1122
- key: VIZ_TOAST_KEY,
1123
- severity: 'error',
1124
- summary: 'Error saving regions',
1125
- detail: `${err?.message ?? err}`,
1126
- });
1127
- return;
1128
- }
1129
- this._saveAsSub = this.regionIo.saveGeoJson(geoJsonStr, filename).subscribe({
1130
- next: () => {
1131
- this.saveAsBusy = false;
1132
- this.showSaveAsDialog = false;
1133
- this.messageService.add({
1134
- key: VIZ_TOAST_KEY,
1135
- severity: 'success',
1136
- summary: 'Regions saved',
1137
- detail: `Saved as ${filename}`,
1138
- });
1139
- },
1140
- error: (err) => {
1141
- this.saveAsBusy = false;
1142
- this.messageService.add({
1143
- key: VIZ_TOAST_KEY,
1144
- severity: 'error',
1145
- summary: 'Error saving regions',
1146
- detail: `${err.message || err}`,
1147
- });
1148
- },
1149
- });
878
+ this.showSaveAsDialog = false;
879
+ this.changed();
880
+ this.toast('success', 'Regions saved', `Saved as ${filename}`);
881
+ },
882
+ error: (err) => {
883
+ this.saveAsBusy = false;
884
+ this.changed();
885
+ this.toast('error', 'Error saving regions', `${err?.message || err}`);
886
+ },
1150
887
  });
1151
888
  };
1152
889
  if (this.saveAsFileExists) {
@@ -1164,28 +901,42 @@ export class RegionEditorComponent {
1164
901
  /** Cancel an in-progress GeoJSON persist: abort the deferred serialize and/or
1165
902
  * the in-flight upload, and reset state. */
1166
903
  cancelSaveAs() {
1167
- if (this._saveAsTimer !== undefined) {
1168
- clearTimeout(this._saveAsTimer);
1169
- this._saveAsTimer = undefined;
1170
- }
1171
904
  this._saveAsSub?.unsubscribe();
1172
905
  this._saveAsSub = undefined;
1173
906
  this.saveAsBusy = false;
1174
907
  }
1175
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: RegionEditorComponent, deps: [{ token: REGION_EDITOR_API }, { token: i1.MessageService }, { token: i1.ConfirmationService }, { token: REGION_IO_PORT }], target: i0.ɵɵFactoryTarget.Component }); }
1176
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: RegionEditorComponent, selector: "region-editor", viewQueries: [{ propertyName: "overlayPanel", first: true, predicate: ["op"], descendants: true }], ngImport: i0, template: "\n<p-toolbar styleClass=\"region-toolbar\">\n <div class=\"p-toolbar-group-start align-items-center flex-wrap gap-1\">\n <p-button icon=\"pi pi-tag\" size=\"small\"\n [text]=\"!showShapeLabel\"\n pTooltip=\"Show region labels.\"\n tooltipPosition=\"bottom\"\n (onClick)=\"showShapeLabel = !showShapeLabel; changeShowShapeLabel(showShapeLabel)\">\n </p-button>\n <span class=\"toolbar-separator\"></span>\n <!-- New regions are now created from the diagram toolbar\n (rectangle, polyline, freeform, wand). -->\n <p-button icon=\"pi pi-upload\" size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n pTooltip=\"Upload and display regions from geojson file.\"\n tooltipPosition=\"bottom\"\n (click)=\"roiFileInput.click()\">\n </p-button>\n <input #roiFileInput type=\"file\" accept=\".geojson,.json\" hidden\n (change)=\"importRois($event); roiFileInput.value = ''\"/>\n <p-button size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n pTooltip=\"Download regions as geojson file.\"\n tooltipPosition=\"bottom\" icon=\"pi pi-download\"\n (click)=\"exportRois()\">\n </p-button>\n <p-button icon=\"pi pi-save\" size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n pTooltip=\"Save regions to server.\"\n tooltipPosition=\"bottom\"\n (click)=\"persistRegions()\"\n [disabled]=\"!regions || !regions.length\">\n </p-button>\n <p-button size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n pTooltip=\"Download mask of the regions as PNG\"\n tooltipPosition=\"bottom\"\n (click)=\"openSaveMaskDialog()\"\n [disabled]=\"!regions || !regions.length\">\n <!-- References the file so Inkscape edits to mask.svg show up directly\n (the lib's assets/** are served at assets/plotting/). The\n toolbar-icon-dark filter recolours the black SVG to the theme blue,\n same as region-editor.svg and the other SVG toolbar icons. -->\n <img src=\"assets/plotting/mask.svg\" class=\"toolbar-icon-dark\"\n style=\"width: 1rem; height: 1rem; vertical-align: middle;\"\n alt=\"Download mask\"/>\n </p-button>\n <p-button label=\"x512\" size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n pTooltip=\"Round all rectangle regions\n to width and height that are multiple of 512\" tooltipPosition=\"bottom\"\n (click)=\"roundRectangleLengths()\">\n </p-button>\n <p-button icon=\"pi pi-check-square\" size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n pTooltip=\"Select all regions\" tooltipPosition=\"bottom\"\n (click)=\"selectAllRegions()\"\n [disabled]=\"!regions || !regions.length\">\n </p-button>\n <p-button icon=\"pi pi-tags\" size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n pTooltip=\"Edit class on selected rows\"\n tooltipPosition=\"bottom\"\n (click)=\"(!selectedRegions || !selectedRegions.length)? op.hide() : op.toggle($event)\"\n [disabled]=\"!selectedRegions || !selectedRegions.length\">\n </p-button>\n <p-button icon=\"pi pi-palette\" size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n pTooltip=\"Edit colour of selected regions\"\n tooltipPosition=\"bottom\"\n (click)=\"openColorDialog()\"\n [disabled]=\"!selectedRegions || !selectedRegions.length\">\n </p-button>\n <p-overlayPanel #op (onHide)=\"bulkClass=''; newBulkClass=''\">\n <div class=\"flex flex-column gap-2\" style=\"width: 18rem\">\n <span class=\"font-medium text-900\">Set class on {{ selectedRegions?.length || 0 }} selected region(s)</span>\n <!-- Selecting a class applies it immediately (choosing an option closes the\n overlay, so there's no separate Apply button). -->\n <p-dropdown styleClass=\"w-full\" appendTo=\"body\" [options]=\"presetSet.classes\"\n optionLabel=\"name\" optionValue=\"name\" [(ngModel)]=\"bulkClass\"\n placeholder=\"Choose a class\u2026\"\n [filter]=\"presetSet.classes.length > 8\" filterBy=\"name\"\n (onChange)=\"applyBulkClass()\">\n <ng-template let-c pTemplate=\"item\">\n <span class=\"dd-value\"><span class=\"chip-swatch\" [style.background]=\"c.color\"></span>{{ c.name }}</span>\n </ng-template>\n </p-dropdown>\n <div class=\"p-inputgroup\">\n <input type=\"text\" pInputText placeholder=\"\u2026or a new class\" [(ngModel)]=\"newBulkClass\"\n (keydown.enter)=\"addAndApplyBulkClass()\"/>\n <button pButton type=\"button\" icon=\"pi pi-plus\" pTooltip=\"Add class &amp; set it\"\n tooltipPosition=\"bottom\" [disabled]=\"!newBulkClass.trim()\"\n (click)=\"addAndApplyBulkClass()\"></button>\n </div>\n </div>\n </p-overlayPanel>\n <p-button icon=\"pi pi-trash\" size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n (click)=\"deleteSelectedRegions()\"\n pTooltip=\"Delete selected regions\" tooltipPosition=\"bottom\"\n [disabled]=\"!selectedRegions || !selectedRegions.length\"></p-button>\n <p-button icon=\"pi pi-times-circle\" size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n (click)=\"clearAllRegions()\"\n pTooltip=\"Delete all regions\" tooltipPosition=\"bottom\"\n [disabled]=\"!regions || !regions.length\"></p-button>\n <p-button icon=\"pi pi-question\" size=\"small\" [text]=\"true\"\n (click)=\"showHelp()\" pTooltip=\"Help\" tooltipPosition=\"bottom\"></p-button>\n </div>\n</p-toolbar>\n\n<!-- Docked \"Classes\" panel (jit-ui#70): the always-visible list of annotation\n classes above the regions table. Click a row to make it the active class\n (new regions inherit it; with rows selected it re-classifies them); recolour\n via the swatch; the badge shows how many regions use the class. \"Manage\"\n opens the full editor (fallback palette, auto-add, matching, import/export). -->\n<div class=\"classes-panel\">\n <div class=\"classes-panel-header\">\n <span class=\"classes-panel-title\">Classes</span>\n <button pButton type=\"button\" icon=\"pi pi-cog\" label=\"Manage\"\n class=\"p-button-sm p-button-text\"\n pTooltip=\"Manage classes \u2014 add / remove, palette, matching, import/export\"\n tooltipPosition=\"bottom\" (click)=\"openManageDialog()\"></button>\n </div>\n <div class=\"classes-panel-body\">\n <div *ngFor=\"let c of displayClasses; trackBy: trackByClassName\"\n class=\"class-row\" [class.active]=\"activeClass === c.name\"\n (click)=\"selectActiveClass(c.name)\"\n [pTooltip]=\"selectedRegions?.length\n ? 'Apply \u201C' + c.name + '\u201D to the selected region(s)'\n : 'Set \u201C' + c.name + '\u201D active \u2014 new regions use it'\"\n tooltipPosition=\"bottom\">\n <span class=\"row-swatch\" (click)=\"$event.stopPropagation()\">\n <hex-color-picker [color]=\"c.color\" (colorChange)=\"setClassColor(c.name, $event)\"></hex-color-picker>\n </span>\n <span class=\"row-name\">{{ c.name }}</span>\n <span class=\"row-count\" [class.zero]=\"classCount(c.name) === 0\">{{ classCount(c.name) }}</span>\n <button pButton type=\"button\" icon=\"pi pi-trash\"\n class=\"p-button-sm p-button-text p-button-warning row-del\"\n [disabled]=\"c.name === defaultClassName && classCount(c.name) > 0\"\n [pTooltip]=\"deleteClassTooltip(c.name)\"\n tooltipPosition=\"bottom\"\n (click)=\"$event.stopPropagation(); deleteClass(c.name)\"></button>\n </div>\n <div *ngIf=\"!displayClasses.length\" class=\"classes-empty\">\n No classes yet \u2014 add one from Manage.\n </div>\n </div>\n</div>\n\n<div class=\"region-editor-content\">\n <p-table #dt [value]=\"regions.slice(paginatorFirst, paginatorFirst + paginatorRows)\"\n [tableStyle]=\"{'width': '100%'}\" [(selection)]=\"selectedRegions\"\n selectionMode=\"multiple\" [metaKeySelection]=\"metaKey\"\n (onRowSelect)=\"onSelectionChanged()\" (onRowUnselect)=\"onSelectionChanged()\"\n [resizableColumns]=\"true\"\n [rowHover]=\"true\" dataKey=\"id\" styleClass=\"p-datatable-sm region-editor-table\"\n [scrollable]=\"true\" scrollHeight=\"100%\">\n <ng-template pTemplate=\"header\">\n <!-- Hide the column header when there are no regions to show. -->\n <tr *ngIf=\"regions.length > 0\">\n <!-- Class column: shrinks to fit \"Region\" by default, grows with longer labels. -->\n <th class=\"class-col-header\" pResizableColumn>Class <p-sortIcon field=\"label\"></p-sortIcon></th>\n <th class=\"surface-col-header\" pResizableColumn>Surface</th>\n <!-- Trash column: just enough for the icon. -->\n <th style=\"width:2rem\"></th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-region let-shapeIdx=\"rowIndex\">\n <tr [pSelectableRow]=\"region\" [pSelectableRowIndex]=\"shapeIdx\">\n <td class=\"class-cell\">\n <ng-container *ngIf=\"!isEditingLabel(region); else editLabelTpl\">\n <span class=\"class-cell-display\">\n <!-- Quick-pick a preset class (its colour follows); the pencil is for\n a free-form name / rename (jit-ui#70). -->\n <p-dropdown class=\"class-dropdown\" appendTo=\"body\"\n [options]=\"presetSet.classes\" optionLabel=\"name\" optionValue=\"name\"\n [ngModel]=\"region.label\" [autoDisplayFirst]=\"false\"\n (onChange)=\"applyPresetToRegion(region, $event.value)\">\n <ng-template pTemplate=\"selectedItem\">\n <span class=\"dd-value\">\n <span class=\"chip-swatch\" [style.background]=\"colorForName(region.label)\"></span>\n <span class=\"class-name\" [pTooltip]=\"region.label\" tooltipPosition=\"bottom\">{{ region.label || '\u2014' }}</span>\n </span>\n </ng-template>\n <ng-template let-c pTemplate=\"item\">\n <span class=\"dd-value\">\n <span class=\"chip-swatch\" [style.background]=\"c.color\"></span>\n <span>{{ c.name }}</span>\n </span>\n </ng-template>\n </p-dropdown>\n <i class=\"pi pi-pencil icon-hover ml-2\"\n pTooltip=\"Rename / free-form label\" tooltipPosition=\"bottom\"\n (click)=\"startEditLabel(region, $event)\"></i>\n </span>\n </ng-container>\n <ng-template #editLabelTpl>\n <span class=\"p-input-icon-right class-cell-edit\">\n <i class=\"pi pi-times icon-hover\"\n pTooltip=\"Close\" tooltipPosition=\"bottom\"\n (click)=\"stopEditLabel(region, false, $event)\"></i>\n <input type=\"text\" pInputText class=\"p-inputtext-sm w-full\"\n [(ngModel)]=\"region.label\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"stopEditLabel(region, true, $event)\"\n (keydown.escape)=\"stopEditLabel(region, false, $event)\"/>\n </span>\n </ng-template>\n </td>\n <!-- Surface area (\u00B5m\u00B2/mm\u00B2 when the image is scaled, else px\u00B2). -->\n <td class=\"surface-cell\">{{ regionArea(region) || '\u2014' }}</td>\n <td>\n <button pButton pRipple icon=\"pi pi-trash\"\n\t\t\t\t\t\t\tclass=\"p-button-sm p-button-rounded p-button-text p-button-warning\"\n (click)=\"deleteRegion(paginatorFirst + shapeIdx)\"></button>\n </td>\n </tr>\n </ng-template>\n </p-table>\n</div>\n\n<p-paginator *ngIf=\"regions.length > 0\"\n [rows]=\"paginatorRows\" [totalRecords]=\"regions.length\"\n [first]=\"paginatorFirst\"\n [rowsPerPageOptions]=\"rowsPerPageOptions\"\n [showCurrentPageReport]=\"true\"\n currentPageReportTemplate=\"{first} to {last} of {totalRecords}\"\n (onPageChange)=\"onPageChange($event)\"\n styleClass=\"region-editor-paginator\">\n</p-paginator>\n\n<!-- Live-edit mode: no Save/Cancel \u2014 every edit commits immediately to the\n active image's region cache. -->\n\n\n<p-confirmDialog #cd [style]=\"{width: '24rem', height: '14rem'}\" key=\"positionDialog\"\n [baseZIndex]=\"10000\" rejectButtonStyleClass=\"p-button-outlined\">\n <ng-template pTemplate=\"footer\">\n <button type=\"button\" pButton icon=\"pi pi-check\" label=\"Yes\" (click)=\"cd.accept()\"></button>\n <button type=\"button\" class=\"p-button-outlined\" pButton icon=\"pi pi-times\" label=\"No\"\n (click)=\"cd.reject()\"></button>\n </ng-template>\n</p-confirmDialog>\n\n<p-dialog header=\"Save Regions As\" [(visible)]=\"showSaveAsDialog\" [modal]=\"true\" appendTo=\"body\"\n [style]=\"{width: '30rem'}\" [draggable]=\"false\" [resizable]=\"false\"\n [closable]=\"!saveAsBusy\" [closeOnEscape]=\"!saveAsBusy\">\n <!-- Idle: filename + overwrite warning. -->\n <div *ngIf=\"!saveAsBusy\" class=\"flex flex-column gap-3 pt-2\">\n <label for=\"geojson-filename\" class=\"font-medium\">GeoJSON filename</label>\n <input id=\"geojson-filename\" type=\"text\" pInputText\n [(ngModel)]=\"saveAsFilename\"\n (ngModelChange)=\"checkSaveAsFileExists()\"\n class=\"w-full\" />\n <small *ngIf=\"saveAsFileExists\" class=\"p-error\">\n <i class=\"pi pi-exclamation-triangle mr-1\"></i>\n This file already exists and will be overwritten.\n </small>\n </div>\n\n <!-- Busy: progress while regions serialize + upload. -->\n <div *ngIf=\"saveAsBusy\" class=\"flex flex-column gap-3 pt-2\">\n <span>Saving regions\u2026</span>\n <p-progressBar mode=\"indeterminate\" [style]=\"{height: '0.75rem'}\"></p-progressBar>\n </div>\n\n <ng-template pTemplate=\"footer\">\n <p-button *ngIf=\"!saveAsBusy\" label=\"Cancel\" icon=\"pi pi-times\" [text]=\"true\"\n (onClick)=\"showSaveAsDialog = false\"></p-button>\n <p-button *ngIf=\"!saveAsBusy\" label=\"Save\" icon=\"pi pi-save\"\n [disabled]=\"!saveAsFilename || !saveAsFilename.trim()\"\n (onClick)=\"confirmSaveAs()\"></p-button>\n <p-button *ngIf=\"saveAsBusy\" label=\"Cancel\" icon=\"pi pi-times\" [text]=\"true\"\n (onClick)=\"cancelSaveAs()\"></p-button>\n </ng-template>\n</p-dialog>\n\n<p-dialog header=\"Export Regions\" [(visible)]=\"showExportDialog\" [modal]=\"true\" appendTo=\"body\"\n [style]=\"{width: '30rem'}\" [draggable]=\"false\" [resizable]=\"false\">\n <div class=\"flex flex-column gap-3 pt-2\">\n <label for=\"export-filename\" class=\"font-medium\">GeoJSON filename</label>\n <input id=\"export-filename\" type=\"text\" pInputText\n [(ngModel)]=\"exportFilename\"\n class=\"w-full\" />\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Cancel\" icon=\"pi pi-times\" [text]=\"true\"\n (onClick)=\"showExportDialog = false\"></p-button>\n <p-button label=\"Export\" icon=\"pi pi-download\"\n [disabled]=\"!exportFilename || !exportFilename.trim()\"\n (onClick)=\"confirmExport()\"></p-button>\n </ng-template>\n</p-dialog>\n\n<p-dialog header=\"Save Mask\" [(visible)]=\"showSaveMaskDialog\" [modal]=\"true\" appendTo=\"body\"\n [style]=\"{width: '30rem'}\" [draggable]=\"false\" [resizable]=\"false\"\n [closable]=\"!maskBusy\" [closeOnEscape]=\"!maskBusy\">\n <!-- Idle: filename + mask-type form. -->\n <div *ngIf=\"!maskBusy\" class=\"flex flex-column gap-3 pt-2\">\n <label for=\"mask-filename\" class=\"font-medium\">Mask filename</label>\n <input id=\"mask-filename\" type=\"text\" pInputText\n [(ngModel)]=\"saveMaskFilename\"\n class=\"w-full\" />\n <span class=\"font-medium\">Mask type</span>\n <div class=\"flex align-items-center gap-2\">\n <p-radioButton inputId=\"mask-binary\" name=\"maskMode\" value=\"binary\"\n [(ngModel)]=\"maskMode\"></p-radioButton>\n <label for=\"mask-binary\">Binary <span class=\"text-color-secondary\">(regions 1, background 0)</span></label>\n </div>\n <div class=\"flex align-items-center gap-2\">\n <p-radioButton inputId=\"mask-multiclass\" name=\"maskMode\" value=\"multiclass\"\n [(ngModel)]=\"maskMode\"></p-radioButton>\n <label for=\"mask-multiclass\">Multi-class <span class=\"text-color-secondary\">(unique id per region, background 0)</span></label>\n </div>\n </div>\n\n <!-- Busy: progress while the worker rasterizes + encodes the PNG. -->\n <div *ngIf=\"maskBusy\" class=\"flex flex-column gap-3 pt-2\">\n <span>{{ maskEncoding ? 'Encoding PNG\u2026' : 'Rasterizing regions\u2026' }}</span>\n <p-progressBar [mode]=\"maskEncoding ? 'indeterminate' : 'determinate'\"\n [value]=\"maskProgress\"\n [style]=\"{height: '0.75rem'}\"></p-progressBar>\n </div>\n\n <ng-template pTemplate=\"footer\">\n <p-button *ngIf=\"!maskBusy\" label=\"Cancel\" icon=\"pi pi-times\" [text]=\"true\"\n (onClick)=\"showSaveMaskDialog = false\"></p-button>\n <p-button *ngIf=\"!maskBusy\" label=\"Download\" icon=\"pi pi-download\"\n [disabled]=\"!saveMaskFilename || !saveMaskFilename.trim()\"\n (onClick)=\"confirmSaveMask()\"></p-button>\n <p-button *ngIf=\"maskBusy\" label=\"Cancel\" icon=\"pi pi-times\" [text]=\"true\"\n (onClick)=\"cancelSaveMask()\"></p-button>\n </ng-template>\n</p-dialog>\n\n<p-dialog header=\"Region colour\" [(visible)]=\"showColorDialog\" [modal]=\"false\" appendTo=\"body\"\n [style]=\"{width: '24rem'}\" [draggable]=\"true\" [resizable]=\"false\">\n <div class=\"flex flex-column gap-3 pt-2\">\n <span>Outline colour per class for the\n <b>{{ selectedRegions?.length || 0 }}</b> selected region(s):</span>\n <div *ngFor=\"let edit of classColorEdits; let i = index\"\n class=\"flex align-items-center gap-3\">\n <span class=\"font-medium flex-1\">{{ edit.label || 'Unclassified' }}</span>\n <hex-color-picker [color]=\"edit.color\"\n (colorChange)=\"classColorEdits[i].color = $event\"></hex-color-picker>\n </div>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Cancel\" icon=\"pi pi-times\" [text]=\"true\" (onClick)=\"showColorDialog = false\">\n </p-button>\n <p-button label=\"Apply\" icon=\"pi pi-check\" (onClick)=\"applyColorToSelected()\">\n </p-button>\n </ng-template>\n</p-dialog>\n\n<!-- Manage annotation classes (jit-ui#70): CRUD + fallback palette + matching/auto-add\n settings + reset/import/export. Edits a working copy, committed on Apply/Done. -->\n<p-dialog header=\"Manage annotation classes\" [(visible)]=\"showManageDialog\" [modal]=\"true\" appendTo=\"body\"\n [style]=\"{width: '34rem'}\" [draggable]=\"true\" [resizable]=\"false\">\n <div *ngIf=\"presetDraft\" class=\"manage-dialog flex flex-column gap-3 pt-2\">\n <div class=\"manage-classes flex flex-column gap-2\">\n <div *ngFor=\"let c of presetDraft.classes; let i = index\"\n class=\"manage-class-row flex align-items-center gap-2\">\n <hex-color-picker [color]=\"c.color\" (colorChange)=\"c.color = $event\"></hex-color-picker>\n <input type=\"text\" pInputText class=\"p-inputtext-sm flex-1\"\n [(ngModel)]=\"c.name\" placeholder=\"Class name\"/>\n <button pButton type=\"button\" icon=\"pi pi-trash\"\n class=\"p-button-sm p-button-text p-button-warning\"\n pTooltip=\"Remove class\" tooltipPosition=\"bottom\"\n (click)=\"removePresetClass(i)\"></button>\n </div>\n <button pButton type=\"button\" icon=\"pi pi-plus\" label=\"Add class\"\n class=\"p-button-sm p-button-text align-self-start\" (click)=\"addPresetClass()\"></button>\n </div>\n\n <div class=\"manage-section flex flex-column gap-2\">\n <span class=\"section-title font-medium\">Fallback palette\n <small class=\"text-color-secondary\">\u2014 colours for classes not listed</small></span>\n <div class=\"fallback-palette flex align-items-center flex-wrap gap-2\">\n <span *ngFor=\"let col of presetDraft.fallbackPalette; let i = index\"\n class=\"fallback-swatch flex align-items-center\">\n <hex-color-picker [color]=\"col\" (colorChange)=\"presetDraft.fallbackPalette[i] = $event\"></hex-color-picker>\n <i class=\"pi pi-times icon-hover ml-1\" pTooltip=\"Remove\" tooltipPosition=\"bottom\"\n (click)=\"removeFallbackColor(i)\"></i>\n </span>\n <button pButton type=\"button\" icon=\"pi pi-plus\"\n class=\"p-button-sm p-button-text\" pTooltip=\"Add fallback colour\"\n tooltipPosition=\"bottom\" (click)=\"addFallbackColor()\"></button>\n </div>\n </div>\n\n <div class=\"flex align-items-center gap-2\">\n <p-checkbox [(ngModel)]=\"presetDraft.autoPromote\" [binary]=\"true\" inputId=\"autoPromote\"></p-checkbox>\n <label for=\"autoPromote\">Auto-add detected classes to this list as they appear</label>\n </div>\n\n <div class=\"flex align-items-center gap-3\">\n <span class=\"font-medium\">Label matching</span>\n <p-selectButton [options]=\"matchModeOptions\" [(ngModel)]=\"presetDraft.matchMode\"\n optionLabel=\"label\" optionValue=\"value\"></p-selectButton>\n </div>\n </div>\n\n <ng-template pTemplate=\"footer\">\n <div class=\"manage-footer flex align-items-center flex-wrap gap-2 w-full\">\n <span class=\"flex align-items-center flex-wrap gap-1\">\n <p-button label=\"Reset\" icon=\"pi pi-refresh\" [text]=\"true\"\n (onClick)=\"resetPresetsToDefaults()\"></p-button>\n <p-button label=\"Import\" icon=\"pi pi-upload\" [text]=\"true\"\n (onClick)=\"presetFileInput.click()\"></p-button>\n <input #presetFileInput type=\"file\" accept=\".json,application/json\" hidden\n (change)=\"importPresets($event); presetFileInput.value = ''\"/>\n <p-button label=\"Export\" icon=\"pi pi-download\" [text]=\"true\"\n (onClick)=\"exportPresets()\"></p-button>\n </span>\n <span class=\"flex-1\"></span>\n <!-- Apply persists to the server and closes; it replaces the old Apply+Done pair. -->\n <p-button label=\"Apply\" icon=\"pi pi-check\" (onClick)=\"applyManageDialog(true)\"></p-button>\n </div>\n </ng-template>\n</p-dialog>\n\n<p-dialog header=\"Region editor help\" [(visible)]=\"displayHelpDialog\" [style]=\"{width: '50vw'}\"\n [maximizable]=\"true\" [draggable]=\"true\" appendTo=\"body\">\n <p>\n The Region Editor lists every region drawn on the currently displayed image.\n Open it with the\n <img src=\"assets/plotting/region-editor.svg\" width=\"14\" height=\"14\" alt=\"Region Editor\"/>\n button in the diagram toolbar: it opens as a floating dialog you can drag,\n resize, and leave open while you draw on the image.\n Regions are tracked <b>per image</b>: switching to another image hides\n these and shows that image's regions; switching back restores them.\n Edits in the table apply <b>live</b> to the diagram \u2014 there is no save or\n cancel.\n </p>\n\n <h3>Creating regions</h3>\n <p>\n Use the diagram toolbar tools to draw new regions:\n <i class=\"pi pi-stop\"></i> rectangle,\n <object data=\"assets/icons/polyline.svg\" width=\"14\" height=\"14\"></object> polyline,\n <i class=\"pi pi-pencil\"></i> freeform path, brush, or\n <object data=\"assets/icons/wand.svg\" width=\"14\" height=\"14\"></object> wand.\n Anything you draw appears immediately in the table, with the class name\n \"<b>Region</b>\" by default.\n </p>\n\n <h3>The table columns</h3>\n <ul>\n <li><b>Class</b> \u2014 the region's class. Pick a class from the dropdown (its\n colour follows), or use the <i class=\"pi pi-pencil\"></i> for a free-form name.</li>\n <li><b>Surface</b> \u2014 the region's area, in \u00B5m\u00B2/mm\u00B2 when the image carries a\n physical pixel size, otherwise in px\u00B2. Holes (donuts) and multi-part\n regions are accounted for.</li>\n <li>The trash icon at the end of a row deletes that one region.</li>\n <li>Use \"<b>x512</b>\" to round all rectangle widths/heights to the\n nearest multiple of 512.</li>\n </ul>\n <p>Region geometry is edited on the diagram itself (drag vertices, the vertex\n tools, brush/wand, and the right-click region actions), not in this table.</p>\n\n <h3>Selecting rows</h3>\n <ul>\n <li>Click a row to select it. The corresponding region is highlighted in the\n diagram (Plotly shows its edit handles; the OpenSeadragon image view\n highlights it and shows its handles in select mode).</li>\n <li><kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+click to add or remove a row from the\n selection. <kbd>Shift</kbd>+click to extend the selection in a range.</li>\n <li>Selecting a region in the diagram highlights the matching row in the\n table.</li>\n <li>Clicks on the colour picker, the trash icon, or the active class-edit\n input do not change the selection \u2014 that's standard PrimeNG behaviour.</li>\n </ul>\n\n <h3>Bulk actions on the selected rows</h3>\n <ul>\n <li><i class=\"pi pi-check-square\"></i> Select every region in the table at once (also a\n right-click action on the image).</li>\n <li><i class=\"pi pi-tags\"></i> Edit the label on every selected row at once.</li>\n <li><i class=\"pi pi-trash\"></i> Delete every selected region \u2014 both from\n the table and the diagram.</li>\n <li><i class=\"pi pi-times-circle\"></i> Delete every region on this image.</li>\n </ul>\n\n <h3>Annotation classes &amp; colours (presets)</h3>\n <p>\n Preset a class once \u2014 a <b>name</b> and a <b>colour</b> \u2014 and every region with\n that class name is drawn in that colour, <b>including regions loaded from a\n GeoJSON later</b>. The preset colour is the source of truth: on load it\n <b>overrides the colour stored in the file</b>, so a given annotation type always\n looks the same across images and sessions.\n </p>\n <p>Set up your classes in the annotation-class manager \u2014\n <i class=\"pi pi-cog\"></i> <b>Manage</b>:</p>\n <ol>\n <li><b>Add class</b>, then type the class <b>name exactly as it appears in your\n data</b> (e.g. <code>Tumor</code>) and pick its <b>colour</b> from the swatch.\n Repeat for each class.</li>\n <li>Optionally: edit the <b>fallback palette</b> (colours given to classes that\n aren't in your list), toggle <b>auto-add</b> (adds unmatched classes found on\n load), and choose <b>exact</b> vs. <b>normalized</b> name matching \u2014 normalized\n ignores case and surrounding spaces, so <code>tumor</code> matches\n <code>Tumor</code>.</li>\n <li><b>Apply.</b> Presets are saved <b>per user</b> on the server, so they follow\n you across images and sessions. You can also <b>reset to defaults</b> or\n <b>import / export</b> the set as JSON to share it.</li>\n </ol>\n <p>\n Next time you open or <i class=\"pi pi-upload\"></i> import a GeoJSON whose regions\n carry those class names, each region is shown in its preset colour automatically \u2014\n no re-colouring needed. A class that isn't in your list instead gets a stable\n colour from the fallback palette.\n </p>\n <p>Day to day:</p>\n <ul>\n <li>The <b>class strip</b> above the table lists your presets with their colours.\n Click a class to make it <b>active</b> (new regions inherit it); with rows\n selected, clicking a class re-classifies them.</li>\n <li>The <b>Class</b> column dropdown re-classifies one region to a preset (its\n colour follows), or use the <i class=\"pi pi-pencil\"></i> for a free-form name.</li>\n <li>Recolour a class from its swatch (class strip or Manage) and every region of\n that class updates at once.</li>\n <li>Deleting a class reassigns its regions to the default <b>Region</b> class.</li>\n <li>A colour you set on a <b>single</b> region (<i class=\"pi pi-palette\"></i>\n Selected regions) is kept and is not overridden by its class.</li>\n </ul>\n\n <h3>Region actions (right-click the diagram)</h3>\n <p>Right-click a region \u2014 or the current multi-selection \u2014 on the diagram for\n the \"Selected region(s)\" actions:</p>\n <ul>\n <li><b>Merge / group</b> \u2014 combine 2+ selected regions into one (it may end\n up multi-part if the pieces don't touch).</li>\n <li><b>Ungroup</b> \u2014 split a multi-part region back into separate regions.</li>\n <li><b>Inverse</b> \u2014 replace the selection with everything outside it (the\n regions become holes in the image rectangle).</li>\n <li><b>Simplify</b> \u2014 reduce the vertex count (Douglas\u2013Peucker) by a pixel\n threshold; pick a preset or a custom value.</li>\n <li><b>Convert to B\u00E9zier / to polygon</b> and <b>Delete</b>.</li>\n </ul>\n\n <h3>Display options</h3>\n <ul>\n <li><i class=\"pi pi-tag\"></i> Toggle showing class labels on every\n region in the diagram (slower with many regions).</li>\n </ul>\n\n <h3>Import / export</h3>\n <ul>\n <li><i class=\"pi pi-upload\"></i> Import regions from a GeoJSON file.</li>\n <li><i class=\"pi pi-download\"></i> Export the current image's regions to\n a GeoJSON file. (The per-image cache is in-memory only \u2014 exporting is\n how you persist regions across sessions.)</li>\n </ul>\n\n <ng-template pTemplate=\"footer\">\n <p-button icon=\"pi pi-check\" (click)=\"displayHelpDialog=false\" label=\"Ok\" styleClass=\"p-button-text\">\n </p-button>\n </ng-template>\n</p-dialog>\n\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;height:100%;min-height:0;overflow:hidden}.region-editor-content{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden}::ng-deep .region-editor-content .p-3{padding:0!important}::ng-deep .region-editor-content>p-table{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden}::ng-deep .region-editor-content .p-datatable.region-editor-table{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden}::ng-deep .region-editor-content .p-datatable.region-editor-table>.p-datatable-header{flex:0 0 auto}::ng-deep .region-editor-content .p-datatable.region-editor-table>.p-datatable-wrapper,::ng-deep .region-editor-content .p-datatable.region-editor-table>.p-datatable-table-container,::ng-deep .region-editor-content .p-datatable.region-editor-table .p-scroller-viewport{flex:1 1 0;min-height:0;height:100%;max-height:100%;overflow:auto}:host>p-paginator{display:block;flex:0 0 auto;border-top:1px solid var(--surface-border);padding:.25rem 0}::ng-deep region-editor .p-toolbar.region-toolbar{padding:.25rem .5rem;border-radius:0;border-left:none;border-right:none;border-top:none}::ng-deep region-editor .p-toolbar.region-toolbar .p-button.p-button-icon-only{width:auto;padding:.25rem .5rem}::ng-deep region-editor .p-toolbar.region-toolbar .p-button{padding:.25rem .5rem}.toolbar-icon-dark{filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.class-col-header,.class-cell{white-space:nowrap;max-width:16rem}.class-cell .class-cell-display{display:flex;align-items:center;min-width:0}.class-cell .class-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.class-cell .icon-hover{flex:0 0 auto}.class-cell .icon-hover{cursor:pointer;color:var(--text-color-secondary);transition:color .15s ease}.class-cell .icon-hover:hover{color:var(--primary-color)}.class-cell .class-cell-edit{display:inline-block;width:100%}::ng-deep .p-datatable-sm .p-datatable-tbody>tr>td .p-button.p-button-rounded,::ng-deep .p-datatable-sm .p-datatable-tbody>tr>td .p-button.p-button-icon-only{width:1.5rem;height:1.5rem;padding:0}::ng-deep .p-datatable-sm .p-datatable-tbody>tr>td .p-button.p-button-rounded .p-button-icon,::ng-deep .p-datatable-sm .p-datatable-tbody>tr>td .p-button.p-button-icon-only .p-button-icon{font-size:.75rem}::ng-deep .p-datatable-sm .p-datatable-tbody>tr>td .p-inputtext{padding:.25rem .5rem;font-size:.875rem}::ng-deep .p-datatable-sm .p-paginator{padding:.15rem .25rem!important;min-height:unset!important}::ng-deep .p-datatable-sm .p-paginator,::ng-deep .p-datatable-sm .p-paginator *{font-size:.75rem!important}::ng-deep .p-datatable-sm .p-paginator .p-paginator-element{min-width:1.5rem!important;height:1.5rem!important}::ng-deep .p-datatable-sm .p-paginator .p-paginator-current{font-size:.75rem!important;height:1.5rem!important;min-width:unset!important}::ng-deep .p-datatable-sm .p-paginator .p-dropdown{height:1.5rem!important}::ng-deep .p-datatable-sm .p-paginator .p-dropdown .p-dropdown-label{padding:.1rem .25rem!important;line-height:1.2!important}::ng-deep .p-datatable-sm .p-paginator .p-dropdown .p-dropdown-trigger{width:1.25rem!important}::ng-deep .p-paginator-rpp-options .p-dropdown-panel .p-dropdown-items .p-dropdown-item{padding:.25rem .5rem!important;font-size:.75rem!important}.region-area{font-size:.8rem;font-weight:600;color:var(--primary-color, #3399ff)}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}.classes-panel{display:flex;flex-direction:column;flex:0 0 auto;border-bottom:1px solid var(--surface-border)}.classes-panel-header{display:flex;align-items:center;gap:.35rem;padding:.35rem .5rem;border-bottom:1px solid var(--surface-border)}.classes-panel-title{font-size:.75rem;font-weight:600;letter-spacing:.03em;text-transform:uppercase;color:var(--text-color-secondary);margin-right:auto}.classes-panel-body{height:7rem;min-height:2.5rem;max-height:60vh;overflow-x:hidden;overflow-y:auto;resize:vertical}::ng-deep .classes-panel-body .hex-picker-swatch{width:1.25rem;height:1.25rem;border-width:1px}.class-row{display:flex;align-items:center;gap:.5rem;padding:.1rem .5rem;cursor:pointer;border-left:3px solid transparent}.class-row:hover{background:var(--surface-hover, rgba(0, 0, 0, .04))}.class-row.active{background:var(--highlight-bg, rgba(51, 153, 255, .12));border-left-color:var(--primary-color)}.class-row .row-swatch{flex:0 0 auto;display:inline-flex}.class-row .row-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.85rem}.class-row.active .row-name{color:var(--primary-color);font-weight:600}.class-row .row-count{flex:0 0 auto;min-width:1.5rem;text-align:center;font-size:.72rem;font-variant-numeric:tabular-nums;color:var(--text-color-secondary);background:var(--surface-200, rgba(0, 0, 0, .06));border-radius:10px;padding:0 .4rem}.class-row .row-count.zero{opacity:.5}.class-row .row-del{flex:0 0 auto}.classes-empty{font-size:.8rem;color:var(--text-color-secondary);padding:.5rem}::ng-deep .classes-panel-body .class-row .p-button.p-button-icon-only{width:1.75rem;height:1.75rem;padding:0}.chip-swatch{display:inline-block;width:.75rem;height:.75rem;border-radius:50%;border:1px solid rgba(0,0,0,.2);flex:0 0 auto}.class-cell .class-dropdown{min-width:0;flex:1 1 auto}::ng-deep .class-cell .class-dropdown.p-dropdown{width:100%}::ng-deep .class-cell .class-dropdown .p-dropdown-label{padding:.15rem .5rem;display:flex;align-items:center}.dd-value{display:inline-flex;align-items:center;gap:.4rem;min-width:0}.dd-value .class-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.manage-dialog .manage-class-row hex-color-picker,.manage-dialog .fallback-palette hex-color-picker{flex:0 0 auto}.manage-dialog .fallback-palette .icon-hover{cursor:pointer;color:var(--text-color-secondary)}.manage-dialog .fallback-palette .icon-hover:hover{color:var(--primary-color)}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4.Toolbar, selector: "p-toolbar", inputs: ["style", "styleClass", "ariaLabelledBy"] }, { kind: "directive", type: i1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i5.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "label", "icon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain"] }, { kind: "component", type: i5.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "style", "styleClass", "badgeClass", "ariaLabel", "autofocus"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: i7.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: i8.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: i9.ProgressBar, selector: "p-progressBar", inputs: ["value", "showValue", "styleClass", "style", "unit", "mode", "color"] }, { kind: "directive", type: i10.Ripple, selector: "[pRipple]" }, { kind: "component", type: i11.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "style", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "scrollDirection", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "responsive", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "autoLayout", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll", "virtualRowHeight"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i11.SelectableRow, selector: "[pSelectableRow]", inputs: ["pSelectableRow", "pSelectableRowIndex", "pSelectableRowDisabled"] }, { kind: "directive", type: i11.ResizableColumn, selector: "[pResizableColumn]", inputs: ["pResizableColumnDisabled"] }, { kind: "component", type: i11.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { kind: "component", type: i12.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "style", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "appendTo", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first"], outputs: ["onPageChange"] }, { kind: "component", type: i13.OverlayPanel, selector: "p-overlayPanel", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "showCloseIcon", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: i14.ConfirmDialog, selector: "p-confirmDialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "visible", "position"], outputs: ["onHide"] }, { kind: "directive", type: i15.InputText, selector: "[pInputText]", inputs: ["variant"] }, { kind: "component", type: i16.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: i17.RadioButton, selector: "p-radioButton", inputs: ["value", "formControlName", "name", "disabled", "label", "variant", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "style", "styleClass", "labelStyleClass", "autofocus"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i18.SelectButton, selector: "p-selectButton", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "style", "styleClass", "ariaLabelledBy", "disabled", "dataKey", "autofocus"], outputs: ["onOptionClick", "onChange"] }, { kind: "component", type: i19.HexColorPickerComponent, selector: "hex-color-picker", inputs: ["color"], outputs: ["colorChange"] }] }); }
908
+ toast(severity, summary, detail) {
909
+ this.messageService.add({ key: VIZ_TOAST_KEY, severity, summary, detail });
910
+ }
911
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: RegionEditorComponent, deps: [{ token: REGION_EDITOR_API }, { token: i1.MessageService }, { token: i1.ConfirmationService }, { token: i2.RegionPersistenceService }, { token: i3.MaskExportService }], target: i0.ɵɵFactoryTarget.Component }); }
912
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: RegionEditorComponent, isStandalone: true, selector: "region-editor", providers: [RegionPersistenceService], viewQueries: [{ propertyName: "overlayPanel", first: true, predicate: ["op"], descendants: true }], ngImport: i0, template: "<p-toolbar styleClass=\"region-toolbar\">\n <div class=\"p-toolbar-group-start align-items-center flex-wrap gap-1\">\n <p-button\n icon=\"pi pi-tag\"\n size=\"small\"\n [text]=\"!showShapeLabel\"\n pTooltip=\"Show region labels.\"\n tooltipPosition=\"bottom\"\n (onClick)=\"showShapeLabel = !showShapeLabel; changeShowShapeLabel(showShapeLabel)\"\n >\n </p-button>\n <span class=\"toolbar-separator\"></span>\n <!-- New regions are now created from the diagram toolbar\n (rectangle, polyline, freeform, wand). -->\n <p-button\n icon=\"pi pi-upload\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n pTooltip=\"Upload and display regions from geojson file.\"\n tooltipPosition=\"bottom\"\n (click)=\"roiFileInput.click()\"\n >\n </p-button>\n <input\n #roiFileInput\n type=\"file\"\n accept=\".geojson,.json\"\n hidden\n (change)=\"importRois($event); roiFileInput.value = ''\"\n />\n <p-button\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n pTooltip=\"Download regions as geojson file.\"\n tooltipPosition=\"bottom\"\n icon=\"pi pi-download\"\n (click)=\"exportRois()\"\n >\n </p-button>\n <p-button\n icon=\"pi pi-save\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n pTooltip=\"Save regions to server.\"\n tooltipPosition=\"bottom\"\n (click)=\"persistRegions()\"\n [disabled]=\"!regions || !regions.length\"\n >\n </p-button>\n <p-button\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n pTooltip=\"Download mask of the regions as PNG\"\n tooltipPosition=\"bottom\"\n (click)=\"openSaveMaskDialog()\"\n [disabled]=\"!regions || !regions.length\"\n >\n <!-- References the file so Inkscape edits to mask.svg show up directly\n (the lib's assets/** are served at assets/plotting/). The\n toolbar-icon-dark filter recolours the black SVG to the theme blue,\n same as region-editor.svg and the other SVG toolbar icons. -->\n <img\n src=\"assets/plotting/mask.svg\"\n class=\"toolbar-icon-dark\"\n style=\"width: 1rem; height: 1rem; vertical-align: middle\"\n alt=\"Download mask\"\n />\n </p-button>\n <p-button\n label=\"x512\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n pTooltip=\"Round all rectangle regions\n to width and height that are multiple of 512\"\n tooltipPosition=\"bottom\"\n (click)=\"roundRectangleLengths()\"\n >\n </p-button>\n <p-button\n icon=\"pi pi-check-square\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n pTooltip=\"Select all regions\"\n tooltipPosition=\"bottom\"\n (click)=\"selectAllRegions()\"\n [disabled]=\"!regions || !regions.length\"\n >\n </p-button>\n <p-button\n icon=\"pi pi-tags\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n pTooltip=\"Edit class on selected rows\"\n tooltipPosition=\"bottom\"\n (click)=\"!selectedRegions || !selectedRegions.length ? op.hide() : op.toggle($event)\"\n [disabled]=\"!selectedRegions || !selectedRegions.length\"\n >\n </p-button>\n <p-button\n icon=\"pi pi-palette\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n pTooltip=\"Edit colour of selected regions\"\n tooltipPosition=\"bottom\"\n (click)=\"openColorDialog()\"\n [disabled]=\"!selectedRegions || !selectedRegions.length\"\n >\n </p-button>\n <p-overlayPanel #op (onHide)=\"bulkClass = ''; newBulkClass = ''\">\n <div class=\"flex flex-column gap-2\" style=\"width: 18rem\">\n <span class=\"font-medium text-900\"\n >Set class on {{ selectedRegions?.length || 0 }} selected region(s)</span\n >\n <!-- Selecting a class applies it immediately (choosing an option closes the\n overlay, so there's no separate Apply button). -->\n <p-dropdown\n styleClass=\"w-full\"\n appendTo=\"body\"\n [options]=\"presetSet.classes\"\n optionLabel=\"name\"\n optionValue=\"name\"\n [(ngModel)]=\"bulkClass\"\n placeholder=\"Choose a class\u2026\"\n [filter]=\"presetSet.classes.length > 8\"\n filterBy=\"name\"\n (onChange)=\"applyBulkClass()\"\n >\n <ng-template let-c pTemplate=\"item\">\n <span class=\"dd-value\"\n ><span class=\"chip-swatch\" [style.background]=\"c.color\"></span>{{ c.name }}</span\n >\n </ng-template>\n </p-dropdown>\n <div class=\"p-inputgroup\">\n <input\n type=\"text\"\n pInputText\n placeholder=\"\u2026or a new class\"\n [(ngModel)]=\"newBulkClass\"\n (keydown.enter)=\"addAndApplyBulkClass()\"\n />\n <button\n pButton\n type=\"button\"\n icon=\"pi pi-plus\"\n pTooltip=\"Add class &amp; set it\"\n tooltipPosition=\"bottom\"\n [disabled]=\"!newBulkClass.trim()\"\n (click)=\"addAndApplyBulkClass()\"\n ></button>\n </div>\n </div>\n </p-overlayPanel>\n <p-button\n icon=\"pi pi-trash\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n (click)=\"deleteSelectedRegions()\"\n pTooltip=\"Delete selected regions\"\n tooltipPosition=\"bottom\"\n [disabled]=\"!selectedRegions || !selectedRegions.length\"\n ></p-button>\n <p-button\n icon=\"pi pi-times-circle\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n (click)=\"clearAllRegions()\"\n pTooltip=\"Delete all regions\"\n tooltipPosition=\"bottom\"\n [disabled]=\"!regions || !regions.length\"\n ></p-button>\n <p-button\n icon=\"pi pi-question\"\n size=\"small\"\n [text]=\"true\"\n (click)=\"showHelp()\"\n pTooltip=\"Help\"\n tooltipPosition=\"bottom\"\n ></p-button>\n </div>\n</p-toolbar>\n\n<region-classes-panel\n [classes]=\"displayClasses\"\n [counts]=\"classCounts\"\n [matchMode]=\"presetSet.matchMode\"\n [activeClass]=\"activeClass\"\n [selectedCount]=\"selectedRegions?.length || 0\"\n [defaultClassName]=\"defaultClassName\"\n (pick)=\"selectActiveClass($event)\"\n (recolor)=\"setClassColor($event.name, $event.color)\"\n (remove)=\"deleteClass($event)\"\n (manage)=\"openManageDialog()\"\n>\n</region-classes-panel>\n\n<region-table\n [page]=\"pagedRegions\"\n [total]=\"regions.length\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [rowsPerPageOptions]=\"rowsPerPageOptions\"\n [metaKey]=\"metaKey\"\n [selection]=\"selectedRegions\"\n (selectionChange)=\"selectedRegions = $event\"\n (rowSelect)=\"onSelectionChanged()\"\n [presetSet]=\"presetSet\"\n [defaultColor]=\"shapeColor\"\n [mpp]=\"mpp\"\n [labelDrafts]=\"editingLabelRegions\"\n (labelEditStart)=\"startEditLabel($event)\"\n (labelDraftChange)=\"setLabelDraft($event.region, $event.value)\"\n (labelEditStop)=\"stopEditLabel($event.region, $event.commit)\"\n (classPick)=\"applyPresetToRegion($event.region, $event.name)\"\n (deleteRow)=\"deleteRegion($event)\"\n (pageChange)=\"onPageChange($event)\"\n>\n</region-table>\n\n<!-- Live-edit mode: no Save/Cancel \u2014 every edit commits immediately to the\n active image's region cache. -->\n\n<p-confirmDialog\n #cd\n [style]=\"{ width: '24rem', height: '14rem' }\"\n key=\"positionDialog\"\n [baseZIndex]=\"10000\"\n rejectButtonStyleClass=\"p-button-outlined\"\n>\n <ng-template pTemplate=\"footer\">\n <button type=\"button\" pButton icon=\"pi pi-check\" label=\"Yes\" (click)=\"cd.accept()\"></button>\n <button\n type=\"button\"\n class=\"p-button-outlined\"\n pButton\n icon=\"pi pi-times\"\n label=\"No\"\n (click)=\"cd.reject()\"\n ></button>\n </ng-template>\n</p-confirmDialog>\n\n<region-save-regions-dialog\n [(visible)]=\"showSaveAsDialog\"\n [filename]=\"saveAsFilename\"\n (filenameChange)=\"saveAsFilename = $event; checkSaveAsFileExists()\"\n [fileExists]=\"saveAsFileExists\"\n [busy]=\"saveAsBusy\"\n (confirm)=\"confirmSaveAs()\"\n (cancelSave)=\"cancelSaveAs()\"\n>\n</region-save-regions-dialog>\n\n<region-save-regions-dialog\n header=\"Export Regions\"\n confirmLabel=\"Export\"\n confirmIcon=\"pi pi-download\"\n inputId=\"export-filename\"\n [(visible)]=\"showExportDialog\"\n [(filename)]=\"exportFilename\"\n (confirm)=\"confirmExport()\"\n>\n</region-save-regions-dialog>\n\n<region-save-mask-dialog\n [(visible)]=\"showSaveMaskDialog\"\n [(filename)]=\"saveMaskFilename\"\n [(mode)]=\"maskMode\"\n [busy]=\"maskBusy\"\n [progress]=\"maskProgress\"\n [encoding]=\"maskEncoding\"\n (confirm)=\"confirmSaveMask()\"\n (cancelExport)=\"cancelSaveMask()\"\n>\n</region-save-mask-dialog>\n\n<region-color-dialog\n [(visible)]=\"showColorDialog\"\n [edits]=\"classColorEdits\"\n [selectedCount]=\"selectedRegions?.length || 0\"\n (apply)=\"applyColorToSelected($event)\"\n>\n</region-color-dialog>\n\n<!-- Manage annotation classes (jit-ui#70): edits a working copy, committed on Apply. -->\n<region-manage-classes-dialog\n [(visible)]=\"showManageDialog\"\n [(draft)]=\"presetDraft\"\n (apply)=\"applyManageDialog(true)\"\n (resetDefaults)=\"resetPresetsToDefaults()\"\n (importFile)=\"importPresets($event)\"\n (export)=\"exportPresets()\"\n>\n</region-manage-classes-dialog>\n\n<region-editor-help [(visible)]=\"displayHelpDialog\"></region-editor-help>\n", styles: ["@charset \"UTF-8\";.chip-swatch{display:inline-block;width:.75rem;height:.75rem;border-radius:50%;border:1px solid rgba(0,0,0,.2);flex:0 0 auto}.dd-value{display:inline-flex;align-items:center;gap:.4rem;min-width:0}.dd-value .class-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host{display:flex;flex-direction:column;height:100%;min-height:0;overflow:hidden}::ng-deep region-editor .p-toolbar.region-toolbar{padding:.25rem .5rem;border-radius:0;border-left:none;border-right:none;border-top:none}::ng-deep region-editor .p-toolbar.region-toolbar .p-button.p-button-icon-only{width:auto;padding:.25rem .5rem}::ng-deep region-editor .p-toolbar.region-toolbar .p-button{padding:.25rem .5rem}.toolbar-icon-dark{filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.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: "directive", type: i5.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "label", "icon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain"] }, { kind: "component", type: i5.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "style", "styleClass", "badgeClass", "ariaLabel", "autofocus"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ConfirmDialogModule }, { kind: "component", type: i6.ConfirmDialog, selector: "p-confirmDialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "visible", "position"], outputs: ["onHide"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i7.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: InputTextModule }, { kind: "directive", type: i8.InputText, selector: "[pInputText]", inputs: ["variant"] }, { kind: "ngmodule", type: OverlayPanelModule }, { kind: "component", type: i9.OverlayPanel, selector: "p-overlayPanel", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "showCloseIcon", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: ToolbarModule }, { kind: "component", type: i10.Toolbar, selector: "p-toolbar", inputs: ["style", "styleClass", "ariaLabelledBy"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i11.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: ClassesPanelComponent, selector: "region-classes-panel", inputs: ["classes", "counts", "matchMode", "activeClass", "selectedCount", "defaultClassName"], outputs: ["pick", "recolor", "remove", "manage"] }, { kind: "component", type: RegionTableComponent, selector: "region-table", inputs: ["page", "total", "first", "rows", "rowsPerPageOptions", "metaKey", "selection", "presetSet", "defaultColor", "mpp", "labelDrafts"], outputs: ["selectionChange", "rowSelect", "labelEditStart", "labelDraftChange", "labelEditStop", "classPick", "deleteRow", "pageChange"] }, { kind: "component", type: SaveRegionsDialogComponent, selector: "region-save-regions-dialog", inputs: ["header", "confirmLabel", "confirmIcon", "inputId", "visible", "filename", "fileExists", "busy"], outputs: ["visibleChange", "filenameChange", "confirm", "cancelSave"] }, { kind: "component", type: SaveMaskDialogComponent, selector: "region-save-mask-dialog", inputs: ["visible", "filename", "mode", "busy", "progress", "encoding"], outputs: ["visibleChange", "filenameChange", "modeChange", "confirm", "cancelExport"] }, { kind: "component", type: RegionColorDialogComponent, selector: "region-color-dialog", inputs: ["visible", "selectedCount", "edits"], outputs: ["visibleChange", "apply"] }, { kind: "component", type: ManageClassesDialogComponent, selector: "region-manage-classes-dialog", inputs: ["visible", "draft"], outputs: ["visibleChange", "draftChange", "apply", "resetDefaults", "importFile", "export"] }, { kind: "component", type: RegionEditorHelpComponent, selector: "region-editor-help", inputs: ["visible"], outputs: ["visibleChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1177
913
  }
1178
914
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: RegionEditorComponent, decorators: [{
1179
915
  type: Component,
1180
- args: [{ selector: 'region-editor', template: "\n<p-toolbar styleClass=\"region-toolbar\">\n <div class=\"p-toolbar-group-start align-items-center flex-wrap gap-1\">\n <p-button icon=\"pi pi-tag\" size=\"small\"\n [text]=\"!showShapeLabel\"\n pTooltip=\"Show region labels.\"\n tooltipPosition=\"bottom\"\n (onClick)=\"showShapeLabel = !showShapeLabel; changeShowShapeLabel(showShapeLabel)\">\n </p-button>\n <span class=\"toolbar-separator\"></span>\n <!-- New regions are now created from the diagram toolbar\n (rectangle, polyline, freeform, wand). -->\n <p-button icon=\"pi pi-upload\" size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n pTooltip=\"Upload and display regions from geojson file.\"\n tooltipPosition=\"bottom\"\n (click)=\"roiFileInput.click()\">\n </p-button>\n <input #roiFileInput type=\"file\" accept=\".geojson,.json\" hidden\n (change)=\"importRois($event); roiFileInput.value = ''\"/>\n <p-button size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n pTooltip=\"Download regions as geojson file.\"\n tooltipPosition=\"bottom\" icon=\"pi pi-download\"\n (click)=\"exportRois()\">\n </p-button>\n <p-button icon=\"pi pi-save\" size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n pTooltip=\"Save regions to server.\"\n tooltipPosition=\"bottom\"\n (click)=\"persistRegions()\"\n [disabled]=\"!regions || !regions.length\">\n </p-button>\n <p-button size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n pTooltip=\"Download mask of the regions as PNG\"\n tooltipPosition=\"bottom\"\n (click)=\"openSaveMaskDialog()\"\n [disabled]=\"!regions || !regions.length\">\n <!-- References the file so Inkscape edits to mask.svg show up directly\n (the lib's assets/** are served at assets/plotting/). The\n toolbar-icon-dark filter recolours the black SVG to the theme blue,\n same as region-editor.svg and the other SVG toolbar icons. -->\n <img src=\"assets/plotting/mask.svg\" class=\"toolbar-icon-dark\"\n style=\"width: 1rem; height: 1rem; vertical-align: middle;\"\n alt=\"Download mask\"/>\n </p-button>\n <p-button label=\"x512\" size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n pTooltip=\"Round all rectangle regions\n to width and height that are multiple of 512\" tooltipPosition=\"bottom\"\n (click)=\"roundRectangleLengths()\">\n </p-button>\n <p-button icon=\"pi pi-check-square\" size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n pTooltip=\"Select all regions\" tooltipPosition=\"bottom\"\n (click)=\"selectAllRegions()\"\n [disabled]=\"!regions || !regions.length\">\n </p-button>\n <p-button icon=\"pi pi-tags\" size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n pTooltip=\"Edit class on selected rows\"\n tooltipPosition=\"bottom\"\n (click)=\"(!selectedRegions || !selectedRegions.length)? op.hide() : op.toggle($event)\"\n [disabled]=\"!selectedRegions || !selectedRegions.length\">\n </p-button>\n <p-button icon=\"pi pi-palette\" size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n pTooltip=\"Edit colour of selected regions\"\n tooltipPosition=\"bottom\"\n (click)=\"openColorDialog()\"\n [disabled]=\"!selectedRegions || !selectedRegions.length\">\n </p-button>\n <p-overlayPanel #op (onHide)=\"bulkClass=''; newBulkClass=''\">\n <div class=\"flex flex-column gap-2\" style=\"width: 18rem\">\n <span class=\"font-medium text-900\">Set class on {{ selectedRegions?.length || 0 }} selected region(s)</span>\n <!-- Selecting a class applies it immediately (choosing an option closes the\n overlay, so there's no separate Apply button). -->\n <p-dropdown styleClass=\"w-full\" appendTo=\"body\" [options]=\"presetSet.classes\"\n optionLabel=\"name\" optionValue=\"name\" [(ngModel)]=\"bulkClass\"\n placeholder=\"Choose a class\u2026\"\n [filter]=\"presetSet.classes.length > 8\" filterBy=\"name\"\n (onChange)=\"applyBulkClass()\">\n <ng-template let-c pTemplate=\"item\">\n <span class=\"dd-value\"><span class=\"chip-swatch\" [style.background]=\"c.color\"></span>{{ c.name }}</span>\n </ng-template>\n </p-dropdown>\n <div class=\"p-inputgroup\">\n <input type=\"text\" pInputText placeholder=\"\u2026or a new class\" [(ngModel)]=\"newBulkClass\"\n (keydown.enter)=\"addAndApplyBulkClass()\"/>\n <button pButton type=\"button\" icon=\"pi pi-plus\" pTooltip=\"Add class &amp; set it\"\n tooltipPosition=\"bottom\" [disabled]=\"!newBulkClass.trim()\"\n (click)=\"addAndApplyBulkClass()\"></button>\n </div>\n </div>\n </p-overlayPanel>\n <p-button icon=\"pi pi-trash\" size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n (click)=\"deleteSelectedRegions()\"\n pTooltip=\"Delete selected regions\" tooltipPosition=\"bottom\"\n [disabled]=\"!selectedRegions || !selectedRegions.length\"></p-button>\n <p-button icon=\"pi pi-times-circle\" size=\"small\" [text]=\"true\" styleClass=\"mr-2\"\n (click)=\"clearAllRegions()\"\n pTooltip=\"Delete all regions\" tooltipPosition=\"bottom\"\n [disabled]=\"!regions || !regions.length\"></p-button>\n <p-button icon=\"pi pi-question\" size=\"small\" [text]=\"true\"\n (click)=\"showHelp()\" pTooltip=\"Help\" tooltipPosition=\"bottom\"></p-button>\n </div>\n</p-toolbar>\n\n<!-- Docked \"Classes\" panel (jit-ui#70): the always-visible list of annotation\n classes above the regions table. Click a row to make it the active class\n (new regions inherit it; with rows selected it re-classifies them); recolour\n via the swatch; the badge shows how many regions use the class. \"Manage\"\n opens the full editor (fallback palette, auto-add, matching, import/export). -->\n<div class=\"classes-panel\">\n <div class=\"classes-panel-header\">\n <span class=\"classes-panel-title\">Classes</span>\n <button pButton type=\"button\" icon=\"pi pi-cog\" label=\"Manage\"\n class=\"p-button-sm p-button-text\"\n pTooltip=\"Manage classes \u2014 add / remove, palette, matching, import/export\"\n tooltipPosition=\"bottom\" (click)=\"openManageDialog()\"></button>\n </div>\n <div class=\"classes-panel-body\">\n <div *ngFor=\"let c of displayClasses; trackBy: trackByClassName\"\n class=\"class-row\" [class.active]=\"activeClass === c.name\"\n (click)=\"selectActiveClass(c.name)\"\n [pTooltip]=\"selectedRegions?.length\n ? 'Apply \u201C' + c.name + '\u201D to the selected region(s)'\n : 'Set \u201C' + c.name + '\u201D active \u2014 new regions use it'\"\n tooltipPosition=\"bottom\">\n <span class=\"row-swatch\" (click)=\"$event.stopPropagation()\">\n <hex-color-picker [color]=\"c.color\" (colorChange)=\"setClassColor(c.name, $event)\"></hex-color-picker>\n </span>\n <span class=\"row-name\">{{ c.name }}</span>\n <span class=\"row-count\" [class.zero]=\"classCount(c.name) === 0\">{{ classCount(c.name) }}</span>\n <button pButton type=\"button\" icon=\"pi pi-trash\"\n class=\"p-button-sm p-button-text p-button-warning row-del\"\n [disabled]=\"c.name === defaultClassName && classCount(c.name) > 0\"\n [pTooltip]=\"deleteClassTooltip(c.name)\"\n tooltipPosition=\"bottom\"\n (click)=\"$event.stopPropagation(); deleteClass(c.name)\"></button>\n </div>\n <div *ngIf=\"!displayClasses.length\" class=\"classes-empty\">\n No classes yet \u2014 add one from Manage.\n </div>\n </div>\n</div>\n\n<div class=\"region-editor-content\">\n <p-table #dt [value]=\"regions.slice(paginatorFirst, paginatorFirst + paginatorRows)\"\n [tableStyle]=\"{'width': '100%'}\" [(selection)]=\"selectedRegions\"\n selectionMode=\"multiple\" [metaKeySelection]=\"metaKey\"\n (onRowSelect)=\"onSelectionChanged()\" (onRowUnselect)=\"onSelectionChanged()\"\n [resizableColumns]=\"true\"\n [rowHover]=\"true\" dataKey=\"id\" styleClass=\"p-datatable-sm region-editor-table\"\n [scrollable]=\"true\" scrollHeight=\"100%\">\n <ng-template pTemplate=\"header\">\n <!-- Hide the column header when there are no regions to show. -->\n <tr *ngIf=\"regions.length > 0\">\n <!-- Class column: shrinks to fit \"Region\" by default, grows with longer labels. -->\n <th class=\"class-col-header\" pResizableColumn>Class <p-sortIcon field=\"label\"></p-sortIcon></th>\n <th class=\"surface-col-header\" pResizableColumn>Surface</th>\n <!-- Trash column: just enough for the icon. -->\n <th style=\"width:2rem\"></th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-region let-shapeIdx=\"rowIndex\">\n <tr [pSelectableRow]=\"region\" [pSelectableRowIndex]=\"shapeIdx\">\n <td class=\"class-cell\">\n <ng-container *ngIf=\"!isEditingLabel(region); else editLabelTpl\">\n <span class=\"class-cell-display\">\n <!-- Quick-pick a preset class (its colour follows); the pencil is for\n a free-form name / rename (jit-ui#70). -->\n <p-dropdown class=\"class-dropdown\" appendTo=\"body\"\n [options]=\"presetSet.classes\" optionLabel=\"name\" optionValue=\"name\"\n [ngModel]=\"region.label\" [autoDisplayFirst]=\"false\"\n (onChange)=\"applyPresetToRegion(region, $event.value)\">\n <ng-template pTemplate=\"selectedItem\">\n <span class=\"dd-value\">\n <span class=\"chip-swatch\" [style.background]=\"colorForName(region.label)\"></span>\n <span class=\"class-name\" [pTooltip]=\"region.label\" tooltipPosition=\"bottom\">{{ region.label || '\u2014' }}</span>\n </span>\n </ng-template>\n <ng-template let-c pTemplate=\"item\">\n <span class=\"dd-value\">\n <span class=\"chip-swatch\" [style.background]=\"c.color\"></span>\n <span>{{ c.name }}</span>\n </span>\n </ng-template>\n </p-dropdown>\n <i class=\"pi pi-pencil icon-hover ml-2\"\n pTooltip=\"Rename / free-form label\" tooltipPosition=\"bottom\"\n (click)=\"startEditLabel(region, $event)\"></i>\n </span>\n </ng-container>\n <ng-template #editLabelTpl>\n <span class=\"p-input-icon-right class-cell-edit\">\n <i class=\"pi pi-times icon-hover\"\n pTooltip=\"Close\" tooltipPosition=\"bottom\"\n (click)=\"stopEditLabel(region, false, $event)\"></i>\n <input type=\"text\" pInputText class=\"p-inputtext-sm w-full\"\n [(ngModel)]=\"region.label\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"stopEditLabel(region, true, $event)\"\n (keydown.escape)=\"stopEditLabel(region, false, $event)\"/>\n </span>\n </ng-template>\n </td>\n <!-- Surface area (\u00B5m\u00B2/mm\u00B2 when the image is scaled, else px\u00B2). -->\n <td class=\"surface-cell\">{{ regionArea(region) || '\u2014' }}</td>\n <td>\n <button pButton pRipple icon=\"pi pi-trash\"\n\t\t\t\t\t\t\tclass=\"p-button-sm p-button-rounded p-button-text p-button-warning\"\n (click)=\"deleteRegion(paginatorFirst + shapeIdx)\"></button>\n </td>\n </tr>\n </ng-template>\n </p-table>\n</div>\n\n<p-paginator *ngIf=\"regions.length > 0\"\n [rows]=\"paginatorRows\" [totalRecords]=\"regions.length\"\n [first]=\"paginatorFirst\"\n [rowsPerPageOptions]=\"rowsPerPageOptions\"\n [showCurrentPageReport]=\"true\"\n currentPageReportTemplate=\"{first} to {last} of {totalRecords}\"\n (onPageChange)=\"onPageChange($event)\"\n styleClass=\"region-editor-paginator\">\n</p-paginator>\n\n<!-- Live-edit mode: no Save/Cancel \u2014 every edit commits immediately to the\n active image's region cache. -->\n\n\n<p-confirmDialog #cd [style]=\"{width: '24rem', height: '14rem'}\" key=\"positionDialog\"\n [baseZIndex]=\"10000\" rejectButtonStyleClass=\"p-button-outlined\">\n <ng-template pTemplate=\"footer\">\n <button type=\"button\" pButton icon=\"pi pi-check\" label=\"Yes\" (click)=\"cd.accept()\"></button>\n <button type=\"button\" class=\"p-button-outlined\" pButton icon=\"pi pi-times\" label=\"No\"\n (click)=\"cd.reject()\"></button>\n </ng-template>\n</p-confirmDialog>\n\n<p-dialog header=\"Save Regions As\" [(visible)]=\"showSaveAsDialog\" [modal]=\"true\" appendTo=\"body\"\n [style]=\"{width: '30rem'}\" [draggable]=\"false\" [resizable]=\"false\"\n [closable]=\"!saveAsBusy\" [closeOnEscape]=\"!saveAsBusy\">\n <!-- Idle: filename + overwrite warning. -->\n <div *ngIf=\"!saveAsBusy\" class=\"flex flex-column gap-3 pt-2\">\n <label for=\"geojson-filename\" class=\"font-medium\">GeoJSON filename</label>\n <input id=\"geojson-filename\" type=\"text\" pInputText\n [(ngModel)]=\"saveAsFilename\"\n (ngModelChange)=\"checkSaveAsFileExists()\"\n class=\"w-full\" />\n <small *ngIf=\"saveAsFileExists\" class=\"p-error\">\n <i class=\"pi pi-exclamation-triangle mr-1\"></i>\n This file already exists and will be overwritten.\n </small>\n </div>\n\n <!-- Busy: progress while regions serialize + upload. -->\n <div *ngIf=\"saveAsBusy\" class=\"flex flex-column gap-3 pt-2\">\n <span>Saving regions\u2026</span>\n <p-progressBar mode=\"indeterminate\" [style]=\"{height: '0.75rem'}\"></p-progressBar>\n </div>\n\n <ng-template pTemplate=\"footer\">\n <p-button *ngIf=\"!saveAsBusy\" label=\"Cancel\" icon=\"pi pi-times\" [text]=\"true\"\n (onClick)=\"showSaveAsDialog = false\"></p-button>\n <p-button *ngIf=\"!saveAsBusy\" label=\"Save\" icon=\"pi pi-save\"\n [disabled]=\"!saveAsFilename || !saveAsFilename.trim()\"\n (onClick)=\"confirmSaveAs()\"></p-button>\n <p-button *ngIf=\"saveAsBusy\" label=\"Cancel\" icon=\"pi pi-times\" [text]=\"true\"\n (onClick)=\"cancelSaveAs()\"></p-button>\n </ng-template>\n</p-dialog>\n\n<p-dialog header=\"Export Regions\" [(visible)]=\"showExportDialog\" [modal]=\"true\" appendTo=\"body\"\n [style]=\"{width: '30rem'}\" [draggable]=\"false\" [resizable]=\"false\">\n <div class=\"flex flex-column gap-3 pt-2\">\n <label for=\"export-filename\" class=\"font-medium\">GeoJSON filename</label>\n <input id=\"export-filename\" type=\"text\" pInputText\n [(ngModel)]=\"exportFilename\"\n class=\"w-full\" />\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Cancel\" icon=\"pi pi-times\" [text]=\"true\"\n (onClick)=\"showExportDialog = false\"></p-button>\n <p-button label=\"Export\" icon=\"pi pi-download\"\n [disabled]=\"!exportFilename || !exportFilename.trim()\"\n (onClick)=\"confirmExport()\"></p-button>\n </ng-template>\n</p-dialog>\n\n<p-dialog header=\"Save Mask\" [(visible)]=\"showSaveMaskDialog\" [modal]=\"true\" appendTo=\"body\"\n [style]=\"{width: '30rem'}\" [draggable]=\"false\" [resizable]=\"false\"\n [closable]=\"!maskBusy\" [closeOnEscape]=\"!maskBusy\">\n <!-- Idle: filename + mask-type form. -->\n <div *ngIf=\"!maskBusy\" class=\"flex flex-column gap-3 pt-2\">\n <label for=\"mask-filename\" class=\"font-medium\">Mask filename</label>\n <input id=\"mask-filename\" type=\"text\" pInputText\n [(ngModel)]=\"saveMaskFilename\"\n class=\"w-full\" />\n <span class=\"font-medium\">Mask type</span>\n <div class=\"flex align-items-center gap-2\">\n <p-radioButton inputId=\"mask-binary\" name=\"maskMode\" value=\"binary\"\n [(ngModel)]=\"maskMode\"></p-radioButton>\n <label for=\"mask-binary\">Binary <span class=\"text-color-secondary\">(regions 1, background 0)</span></label>\n </div>\n <div class=\"flex align-items-center gap-2\">\n <p-radioButton inputId=\"mask-multiclass\" name=\"maskMode\" value=\"multiclass\"\n [(ngModel)]=\"maskMode\"></p-radioButton>\n <label for=\"mask-multiclass\">Multi-class <span class=\"text-color-secondary\">(unique id per region, background 0)</span></label>\n </div>\n </div>\n\n <!-- Busy: progress while the worker rasterizes + encodes the PNG. -->\n <div *ngIf=\"maskBusy\" class=\"flex flex-column gap-3 pt-2\">\n <span>{{ maskEncoding ? 'Encoding PNG\u2026' : 'Rasterizing regions\u2026' }}</span>\n <p-progressBar [mode]=\"maskEncoding ? 'indeterminate' : 'determinate'\"\n [value]=\"maskProgress\"\n [style]=\"{height: '0.75rem'}\"></p-progressBar>\n </div>\n\n <ng-template pTemplate=\"footer\">\n <p-button *ngIf=\"!maskBusy\" label=\"Cancel\" icon=\"pi pi-times\" [text]=\"true\"\n (onClick)=\"showSaveMaskDialog = false\"></p-button>\n <p-button *ngIf=\"!maskBusy\" label=\"Download\" icon=\"pi pi-download\"\n [disabled]=\"!saveMaskFilename || !saveMaskFilename.trim()\"\n (onClick)=\"confirmSaveMask()\"></p-button>\n <p-button *ngIf=\"maskBusy\" label=\"Cancel\" icon=\"pi pi-times\" [text]=\"true\"\n (onClick)=\"cancelSaveMask()\"></p-button>\n </ng-template>\n</p-dialog>\n\n<p-dialog header=\"Region colour\" [(visible)]=\"showColorDialog\" [modal]=\"false\" appendTo=\"body\"\n [style]=\"{width: '24rem'}\" [draggable]=\"true\" [resizable]=\"false\">\n <div class=\"flex flex-column gap-3 pt-2\">\n <span>Outline colour per class for the\n <b>{{ selectedRegions?.length || 0 }}</b> selected region(s):</span>\n <div *ngFor=\"let edit of classColorEdits; let i = index\"\n class=\"flex align-items-center gap-3\">\n <span class=\"font-medium flex-1\">{{ edit.label || 'Unclassified' }}</span>\n <hex-color-picker [color]=\"edit.color\"\n (colorChange)=\"classColorEdits[i].color = $event\"></hex-color-picker>\n </div>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Cancel\" icon=\"pi pi-times\" [text]=\"true\" (onClick)=\"showColorDialog = false\">\n </p-button>\n <p-button label=\"Apply\" icon=\"pi pi-check\" (onClick)=\"applyColorToSelected()\">\n </p-button>\n </ng-template>\n</p-dialog>\n\n<!-- Manage annotation classes (jit-ui#70): CRUD + fallback palette + matching/auto-add\n settings + reset/import/export. Edits a working copy, committed on Apply/Done. -->\n<p-dialog header=\"Manage annotation classes\" [(visible)]=\"showManageDialog\" [modal]=\"true\" appendTo=\"body\"\n [style]=\"{width: '34rem'}\" [draggable]=\"true\" [resizable]=\"false\">\n <div *ngIf=\"presetDraft\" class=\"manage-dialog flex flex-column gap-3 pt-2\">\n <div class=\"manage-classes flex flex-column gap-2\">\n <div *ngFor=\"let c of presetDraft.classes; let i = index\"\n class=\"manage-class-row flex align-items-center gap-2\">\n <hex-color-picker [color]=\"c.color\" (colorChange)=\"c.color = $event\"></hex-color-picker>\n <input type=\"text\" pInputText class=\"p-inputtext-sm flex-1\"\n [(ngModel)]=\"c.name\" placeholder=\"Class name\"/>\n <button pButton type=\"button\" icon=\"pi pi-trash\"\n class=\"p-button-sm p-button-text p-button-warning\"\n pTooltip=\"Remove class\" tooltipPosition=\"bottom\"\n (click)=\"removePresetClass(i)\"></button>\n </div>\n <button pButton type=\"button\" icon=\"pi pi-plus\" label=\"Add class\"\n class=\"p-button-sm p-button-text align-self-start\" (click)=\"addPresetClass()\"></button>\n </div>\n\n <div class=\"manage-section flex flex-column gap-2\">\n <span class=\"section-title font-medium\">Fallback palette\n <small class=\"text-color-secondary\">\u2014 colours for classes not listed</small></span>\n <div class=\"fallback-palette flex align-items-center flex-wrap gap-2\">\n <span *ngFor=\"let col of presetDraft.fallbackPalette; let i = index\"\n class=\"fallback-swatch flex align-items-center\">\n <hex-color-picker [color]=\"col\" (colorChange)=\"presetDraft.fallbackPalette[i] = $event\"></hex-color-picker>\n <i class=\"pi pi-times icon-hover ml-1\" pTooltip=\"Remove\" tooltipPosition=\"bottom\"\n (click)=\"removeFallbackColor(i)\"></i>\n </span>\n <button pButton type=\"button\" icon=\"pi pi-plus\"\n class=\"p-button-sm p-button-text\" pTooltip=\"Add fallback colour\"\n tooltipPosition=\"bottom\" (click)=\"addFallbackColor()\"></button>\n </div>\n </div>\n\n <div class=\"flex align-items-center gap-2\">\n <p-checkbox [(ngModel)]=\"presetDraft.autoPromote\" [binary]=\"true\" inputId=\"autoPromote\"></p-checkbox>\n <label for=\"autoPromote\">Auto-add detected classes to this list as they appear</label>\n </div>\n\n <div class=\"flex align-items-center gap-3\">\n <span class=\"font-medium\">Label matching</span>\n <p-selectButton [options]=\"matchModeOptions\" [(ngModel)]=\"presetDraft.matchMode\"\n optionLabel=\"label\" optionValue=\"value\"></p-selectButton>\n </div>\n </div>\n\n <ng-template pTemplate=\"footer\">\n <div class=\"manage-footer flex align-items-center flex-wrap gap-2 w-full\">\n <span class=\"flex align-items-center flex-wrap gap-1\">\n <p-button label=\"Reset\" icon=\"pi pi-refresh\" [text]=\"true\"\n (onClick)=\"resetPresetsToDefaults()\"></p-button>\n <p-button label=\"Import\" icon=\"pi pi-upload\" [text]=\"true\"\n (onClick)=\"presetFileInput.click()\"></p-button>\n <input #presetFileInput type=\"file\" accept=\".json,application/json\" hidden\n (change)=\"importPresets($event); presetFileInput.value = ''\"/>\n <p-button label=\"Export\" icon=\"pi pi-download\" [text]=\"true\"\n (onClick)=\"exportPresets()\"></p-button>\n </span>\n <span class=\"flex-1\"></span>\n <!-- Apply persists to the server and closes; it replaces the old Apply+Done pair. -->\n <p-button label=\"Apply\" icon=\"pi pi-check\" (onClick)=\"applyManageDialog(true)\"></p-button>\n </div>\n </ng-template>\n</p-dialog>\n\n<p-dialog header=\"Region editor help\" [(visible)]=\"displayHelpDialog\" [style]=\"{width: '50vw'}\"\n [maximizable]=\"true\" [draggable]=\"true\" appendTo=\"body\">\n <p>\n The Region Editor lists every region drawn on the currently displayed image.\n Open it with the\n <img src=\"assets/plotting/region-editor.svg\" width=\"14\" height=\"14\" alt=\"Region Editor\"/>\n button in the diagram toolbar: it opens as a floating dialog you can drag,\n resize, and leave open while you draw on the image.\n Regions are tracked <b>per image</b>: switching to another image hides\n these and shows that image's regions; switching back restores them.\n Edits in the table apply <b>live</b> to the diagram \u2014 there is no save or\n cancel.\n </p>\n\n <h3>Creating regions</h3>\n <p>\n Use the diagram toolbar tools to draw new regions:\n <i class=\"pi pi-stop\"></i> rectangle,\n <object data=\"assets/icons/polyline.svg\" width=\"14\" height=\"14\"></object> polyline,\n <i class=\"pi pi-pencil\"></i> freeform path, brush, or\n <object data=\"assets/icons/wand.svg\" width=\"14\" height=\"14\"></object> wand.\n Anything you draw appears immediately in the table, with the class name\n \"<b>Region</b>\" by default.\n </p>\n\n <h3>The table columns</h3>\n <ul>\n <li><b>Class</b> \u2014 the region's class. Pick a class from the dropdown (its\n colour follows), or use the <i class=\"pi pi-pencil\"></i> for a free-form name.</li>\n <li><b>Surface</b> \u2014 the region's area, in \u00B5m\u00B2/mm\u00B2 when the image carries a\n physical pixel size, otherwise in px\u00B2. Holes (donuts) and multi-part\n regions are accounted for.</li>\n <li>The trash icon at the end of a row deletes that one region.</li>\n <li>Use \"<b>x512</b>\" to round all rectangle widths/heights to the\n nearest multiple of 512.</li>\n </ul>\n <p>Region geometry is edited on the diagram itself (drag vertices, the vertex\n tools, brush/wand, and the right-click region actions), not in this table.</p>\n\n <h3>Selecting rows</h3>\n <ul>\n <li>Click a row to select it. The corresponding region is highlighted in the\n diagram (Plotly shows its edit handles; the OpenSeadragon image view\n highlights it and shows its handles in select mode).</li>\n <li><kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+click to add or remove a row from the\n selection. <kbd>Shift</kbd>+click to extend the selection in a range.</li>\n <li>Selecting a region in the diagram highlights the matching row in the\n table.</li>\n <li>Clicks on the colour picker, the trash icon, or the active class-edit\n input do not change the selection \u2014 that's standard PrimeNG behaviour.</li>\n </ul>\n\n <h3>Bulk actions on the selected rows</h3>\n <ul>\n <li><i class=\"pi pi-check-square\"></i> Select every region in the table at once (also a\n right-click action on the image).</li>\n <li><i class=\"pi pi-tags\"></i> Edit the label on every selected row at once.</li>\n <li><i class=\"pi pi-trash\"></i> Delete every selected region \u2014 both from\n the table and the diagram.</li>\n <li><i class=\"pi pi-times-circle\"></i> Delete every region on this image.</li>\n </ul>\n\n <h3>Annotation classes &amp; colours (presets)</h3>\n <p>\n Preset a class once \u2014 a <b>name</b> and a <b>colour</b> \u2014 and every region with\n that class name is drawn in that colour, <b>including regions loaded from a\n GeoJSON later</b>. The preset colour is the source of truth: on load it\n <b>overrides the colour stored in the file</b>, so a given annotation type always\n looks the same across images and sessions.\n </p>\n <p>Set up your classes in the annotation-class manager \u2014\n <i class=\"pi pi-cog\"></i> <b>Manage</b>:</p>\n <ol>\n <li><b>Add class</b>, then type the class <b>name exactly as it appears in your\n data</b> (e.g. <code>Tumor</code>) and pick its <b>colour</b> from the swatch.\n Repeat for each class.</li>\n <li>Optionally: edit the <b>fallback palette</b> (colours given to classes that\n aren't in your list), toggle <b>auto-add</b> (adds unmatched classes found on\n load), and choose <b>exact</b> vs. <b>normalized</b> name matching \u2014 normalized\n ignores case and surrounding spaces, so <code>tumor</code> matches\n <code>Tumor</code>.</li>\n <li><b>Apply.</b> Presets are saved <b>per user</b> on the server, so they follow\n you across images and sessions. You can also <b>reset to defaults</b> or\n <b>import / export</b> the set as JSON to share it.</li>\n </ol>\n <p>\n Next time you open or <i class=\"pi pi-upload\"></i> import a GeoJSON whose regions\n carry those class names, each region is shown in its preset colour automatically \u2014\n no re-colouring needed. A class that isn't in your list instead gets a stable\n colour from the fallback palette.\n </p>\n <p>Day to day:</p>\n <ul>\n <li>The <b>class strip</b> above the table lists your presets with their colours.\n Click a class to make it <b>active</b> (new regions inherit it); with rows\n selected, clicking a class re-classifies them.</li>\n <li>The <b>Class</b> column dropdown re-classifies one region to a preset (its\n colour follows), or use the <i class=\"pi pi-pencil\"></i> for a free-form name.</li>\n <li>Recolour a class from its swatch (class strip or Manage) and every region of\n that class updates at once.</li>\n <li>Deleting a class reassigns its regions to the default <b>Region</b> class.</li>\n <li>A colour you set on a <b>single</b> region (<i class=\"pi pi-palette\"></i>\n Selected regions) is kept and is not overridden by its class.</li>\n </ul>\n\n <h3>Region actions (right-click the diagram)</h3>\n <p>Right-click a region \u2014 or the current multi-selection \u2014 on the diagram for\n the \"Selected region(s)\" actions:</p>\n <ul>\n <li><b>Merge / group</b> \u2014 combine 2+ selected regions into one (it may end\n up multi-part if the pieces don't touch).</li>\n <li><b>Ungroup</b> \u2014 split a multi-part region back into separate regions.</li>\n <li><b>Inverse</b> \u2014 replace the selection with everything outside it (the\n regions become holes in the image rectangle).</li>\n <li><b>Simplify</b> \u2014 reduce the vertex count (Douglas\u2013Peucker) by a pixel\n threshold; pick a preset or a custom value.</li>\n <li><b>Convert to B\u00E9zier / to polygon</b> and <b>Delete</b>.</li>\n </ul>\n\n <h3>Display options</h3>\n <ul>\n <li><i class=\"pi pi-tag\"></i> Toggle showing class labels on every\n region in the diagram (slower with many regions).</li>\n </ul>\n\n <h3>Import / export</h3>\n <ul>\n <li><i class=\"pi pi-upload\"></i> Import regions from a GeoJSON file.</li>\n <li><i class=\"pi pi-download\"></i> Export the current image's regions to\n a GeoJSON file. (The per-image cache is in-memory only \u2014 exporting is\n how you persist regions across sessions.)</li>\n </ul>\n\n <ng-template pTemplate=\"footer\">\n <p-button icon=\"pi pi-check\" (click)=\"displayHelpDialog=false\" label=\"Ok\" styleClass=\"p-button-text\">\n </p-button>\n </ng-template>\n</p-dialog>\n\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;height:100%;min-height:0;overflow:hidden}.region-editor-content{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden}::ng-deep .region-editor-content .p-3{padding:0!important}::ng-deep .region-editor-content>p-table{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden}::ng-deep .region-editor-content .p-datatable.region-editor-table{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden}::ng-deep .region-editor-content .p-datatable.region-editor-table>.p-datatable-header{flex:0 0 auto}::ng-deep .region-editor-content .p-datatable.region-editor-table>.p-datatable-wrapper,::ng-deep .region-editor-content .p-datatable.region-editor-table>.p-datatable-table-container,::ng-deep .region-editor-content .p-datatable.region-editor-table .p-scroller-viewport{flex:1 1 0;min-height:0;height:100%;max-height:100%;overflow:auto}:host>p-paginator{display:block;flex:0 0 auto;border-top:1px solid var(--surface-border);padding:.25rem 0}::ng-deep region-editor .p-toolbar.region-toolbar{padding:.25rem .5rem;border-radius:0;border-left:none;border-right:none;border-top:none}::ng-deep region-editor .p-toolbar.region-toolbar .p-button.p-button-icon-only{width:auto;padding:.25rem .5rem}::ng-deep region-editor .p-toolbar.region-toolbar .p-button{padding:.25rem .5rem}.toolbar-icon-dark{filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.class-col-header,.class-cell{white-space:nowrap;max-width:16rem}.class-cell .class-cell-display{display:flex;align-items:center;min-width:0}.class-cell .class-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.class-cell .icon-hover{flex:0 0 auto}.class-cell .icon-hover{cursor:pointer;color:var(--text-color-secondary);transition:color .15s ease}.class-cell .icon-hover:hover{color:var(--primary-color)}.class-cell .class-cell-edit{display:inline-block;width:100%}::ng-deep .p-datatable-sm .p-datatable-tbody>tr>td .p-button.p-button-rounded,::ng-deep .p-datatable-sm .p-datatable-tbody>tr>td .p-button.p-button-icon-only{width:1.5rem;height:1.5rem;padding:0}::ng-deep .p-datatable-sm .p-datatable-tbody>tr>td .p-button.p-button-rounded .p-button-icon,::ng-deep .p-datatable-sm .p-datatable-tbody>tr>td .p-button.p-button-icon-only .p-button-icon{font-size:.75rem}::ng-deep .p-datatable-sm .p-datatable-tbody>tr>td .p-inputtext{padding:.25rem .5rem;font-size:.875rem}::ng-deep .p-datatable-sm .p-paginator{padding:.15rem .25rem!important;min-height:unset!important}::ng-deep .p-datatable-sm .p-paginator,::ng-deep .p-datatable-sm .p-paginator *{font-size:.75rem!important}::ng-deep .p-datatable-sm .p-paginator .p-paginator-element{min-width:1.5rem!important;height:1.5rem!important}::ng-deep .p-datatable-sm .p-paginator .p-paginator-current{font-size:.75rem!important;height:1.5rem!important;min-width:unset!important}::ng-deep .p-datatable-sm .p-paginator .p-dropdown{height:1.5rem!important}::ng-deep .p-datatable-sm .p-paginator .p-dropdown .p-dropdown-label{padding:.1rem .25rem!important;line-height:1.2!important}::ng-deep .p-datatable-sm .p-paginator .p-dropdown .p-dropdown-trigger{width:1.25rem!important}::ng-deep .p-paginator-rpp-options .p-dropdown-panel .p-dropdown-items .p-dropdown-item{padding:.25rem .5rem!important;font-size:.75rem!important}.region-area{font-size:.8rem;font-weight:600;color:var(--primary-color, #3399ff)}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}.classes-panel{display:flex;flex-direction:column;flex:0 0 auto;border-bottom:1px solid var(--surface-border)}.classes-panel-header{display:flex;align-items:center;gap:.35rem;padding:.35rem .5rem;border-bottom:1px solid var(--surface-border)}.classes-panel-title{font-size:.75rem;font-weight:600;letter-spacing:.03em;text-transform:uppercase;color:var(--text-color-secondary);margin-right:auto}.classes-panel-body{height:7rem;min-height:2.5rem;max-height:60vh;overflow-x:hidden;overflow-y:auto;resize:vertical}::ng-deep .classes-panel-body .hex-picker-swatch{width:1.25rem;height:1.25rem;border-width:1px}.class-row{display:flex;align-items:center;gap:.5rem;padding:.1rem .5rem;cursor:pointer;border-left:3px solid transparent}.class-row:hover{background:var(--surface-hover, rgba(0, 0, 0, .04))}.class-row.active{background:var(--highlight-bg, rgba(51, 153, 255, .12));border-left-color:var(--primary-color)}.class-row .row-swatch{flex:0 0 auto;display:inline-flex}.class-row .row-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.85rem}.class-row.active .row-name{color:var(--primary-color);font-weight:600}.class-row .row-count{flex:0 0 auto;min-width:1.5rem;text-align:center;font-size:.72rem;font-variant-numeric:tabular-nums;color:var(--text-color-secondary);background:var(--surface-200, rgba(0, 0, 0, .06));border-radius:10px;padding:0 .4rem}.class-row .row-count.zero{opacity:.5}.class-row .row-del{flex:0 0 auto}.classes-empty{font-size:.8rem;color:var(--text-color-secondary);padding:.5rem}::ng-deep .classes-panel-body .class-row .p-button.p-button-icon-only{width:1.75rem;height:1.75rem;padding:0}.chip-swatch{display:inline-block;width:.75rem;height:.75rem;border-radius:50%;border:1px solid rgba(0,0,0,.2);flex:0 0 auto}.class-cell .class-dropdown{min-width:0;flex:1 1 auto}::ng-deep .class-cell .class-dropdown.p-dropdown{width:100%}::ng-deep .class-cell .class-dropdown .p-dropdown-label{padding:.15rem .5rem;display:flex;align-items:center}.dd-value{display:inline-flex;align-items:center;gap:.4rem;min-width:0}.dd-value .class-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.manage-dialog .manage-class-row hex-color-picker,.manage-dialog .fallback-palette hex-color-picker{flex:0 0 auto}.manage-dialog .fallback-palette .icon-hover{cursor:pointer;color:var(--text-color-secondary)}.manage-dialog .fallback-palette .icon-hover:hover{color:var(--primary-color)}\n"] }]
916
+ args: [{ selector: 'region-editor', standalone: true, imports: [
917
+ CommonModule,
918
+ FormsModule,
919
+ SharedModule,
920
+ ButtonModule,
921
+ ConfirmDialogModule,
922
+ DropdownModule,
923
+ InputTextModule,
924
+ OverlayPanelModule,
925
+ ToolbarModule,
926
+ TooltipModule,
927
+ ClassesPanelComponent,
928
+ RegionTableComponent,
929
+ SaveRegionsDialogComponent,
930
+ SaveMaskDialogComponent,
931
+ RegionColorDialogComponent,
932
+ ManageClassesDialogComponent,
933
+ RegionEditorHelpComponent,
934
+ ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [RegionPersistenceService], template: "<p-toolbar styleClass=\"region-toolbar\">\n <div class=\"p-toolbar-group-start align-items-center flex-wrap gap-1\">\n <p-button\n icon=\"pi pi-tag\"\n size=\"small\"\n [text]=\"!showShapeLabel\"\n pTooltip=\"Show region labels.\"\n tooltipPosition=\"bottom\"\n (onClick)=\"showShapeLabel = !showShapeLabel; changeShowShapeLabel(showShapeLabel)\"\n >\n </p-button>\n <span class=\"toolbar-separator\"></span>\n <!-- New regions are now created from the diagram toolbar\n (rectangle, polyline, freeform, wand). -->\n <p-button\n icon=\"pi pi-upload\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n pTooltip=\"Upload and display regions from geojson file.\"\n tooltipPosition=\"bottom\"\n (click)=\"roiFileInput.click()\"\n >\n </p-button>\n <input\n #roiFileInput\n type=\"file\"\n accept=\".geojson,.json\"\n hidden\n (change)=\"importRois($event); roiFileInput.value = ''\"\n />\n <p-button\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n pTooltip=\"Download regions as geojson file.\"\n tooltipPosition=\"bottom\"\n icon=\"pi pi-download\"\n (click)=\"exportRois()\"\n >\n </p-button>\n <p-button\n icon=\"pi pi-save\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n pTooltip=\"Save regions to server.\"\n tooltipPosition=\"bottom\"\n (click)=\"persistRegions()\"\n [disabled]=\"!regions || !regions.length\"\n >\n </p-button>\n <p-button\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n pTooltip=\"Download mask of the regions as PNG\"\n tooltipPosition=\"bottom\"\n (click)=\"openSaveMaskDialog()\"\n [disabled]=\"!regions || !regions.length\"\n >\n <!-- References the file so Inkscape edits to mask.svg show up directly\n (the lib's assets/** are served at assets/plotting/). The\n toolbar-icon-dark filter recolours the black SVG to the theme blue,\n same as region-editor.svg and the other SVG toolbar icons. -->\n <img\n src=\"assets/plotting/mask.svg\"\n class=\"toolbar-icon-dark\"\n style=\"width: 1rem; height: 1rem; vertical-align: middle\"\n alt=\"Download mask\"\n />\n </p-button>\n <p-button\n label=\"x512\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n pTooltip=\"Round all rectangle regions\n to width and height that are multiple of 512\"\n tooltipPosition=\"bottom\"\n (click)=\"roundRectangleLengths()\"\n >\n </p-button>\n <p-button\n icon=\"pi pi-check-square\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n pTooltip=\"Select all regions\"\n tooltipPosition=\"bottom\"\n (click)=\"selectAllRegions()\"\n [disabled]=\"!regions || !regions.length\"\n >\n </p-button>\n <p-button\n icon=\"pi pi-tags\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n pTooltip=\"Edit class on selected rows\"\n tooltipPosition=\"bottom\"\n (click)=\"!selectedRegions || !selectedRegions.length ? op.hide() : op.toggle($event)\"\n [disabled]=\"!selectedRegions || !selectedRegions.length\"\n >\n </p-button>\n <p-button\n icon=\"pi pi-palette\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n pTooltip=\"Edit colour of selected regions\"\n tooltipPosition=\"bottom\"\n (click)=\"openColorDialog()\"\n [disabled]=\"!selectedRegions || !selectedRegions.length\"\n >\n </p-button>\n <p-overlayPanel #op (onHide)=\"bulkClass = ''; newBulkClass = ''\">\n <div class=\"flex flex-column gap-2\" style=\"width: 18rem\">\n <span class=\"font-medium text-900\"\n >Set class on {{ selectedRegions?.length || 0 }} selected region(s)</span\n >\n <!-- Selecting a class applies it immediately (choosing an option closes the\n overlay, so there's no separate Apply button). -->\n <p-dropdown\n styleClass=\"w-full\"\n appendTo=\"body\"\n [options]=\"presetSet.classes\"\n optionLabel=\"name\"\n optionValue=\"name\"\n [(ngModel)]=\"bulkClass\"\n placeholder=\"Choose a class\u2026\"\n [filter]=\"presetSet.classes.length > 8\"\n filterBy=\"name\"\n (onChange)=\"applyBulkClass()\"\n >\n <ng-template let-c pTemplate=\"item\">\n <span class=\"dd-value\"\n ><span class=\"chip-swatch\" [style.background]=\"c.color\"></span>{{ c.name }}</span\n >\n </ng-template>\n </p-dropdown>\n <div class=\"p-inputgroup\">\n <input\n type=\"text\"\n pInputText\n placeholder=\"\u2026or a new class\"\n [(ngModel)]=\"newBulkClass\"\n (keydown.enter)=\"addAndApplyBulkClass()\"\n />\n <button\n pButton\n type=\"button\"\n icon=\"pi pi-plus\"\n pTooltip=\"Add class &amp; set it\"\n tooltipPosition=\"bottom\"\n [disabled]=\"!newBulkClass.trim()\"\n (click)=\"addAndApplyBulkClass()\"\n ></button>\n </div>\n </div>\n </p-overlayPanel>\n <p-button\n icon=\"pi pi-trash\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n (click)=\"deleteSelectedRegions()\"\n pTooltip=\"Delete selected regions\"\n tooltipPosition=\"bottom\"\n [disabled]=\"!selectedRegions || !selectedRegions.length\"\n ></p-button>\n <p-button\n icon=\"pi pi-times-circle\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"mr-2\"\n (click)=\"clearAllRegions()\"\n pTooltip=\"Delete all regions\"\n tooltipPosition=\"bottom\"\n [disabled]=\"!regions || !regions.length\"\n ></p-button>\n <p-button\n icon=\"pi pi-question\"\n size=\"small\"\n [text]=\"true\"\n (click)=\"showHelp()\"\n pTooltip=\"Help\"\n tooltipPosition=\"bottom\"\n ></p-button>\n </div>\n</p-toolbar>\n\n<region-classes-panel\n [classes]=\"displayClasses\"\n [counts]=\"classCounts\"\n [matchMode]=\"presetSet.matchMode\"\n [activeClass]=\"activeClass\"\n [selectedCount]=\"selectedRegions?.length || 0\"\n [defaultClassName]=\"defaultClassName\"\n (pick)=\"selectActiveClass($event)\"\n (recolor)=\"setClassColor($event.name, $event.color)\"\n (remove)=\"deleteClass($event)\"\n (manage)=\"openManageDialog()\"\n>\n</region-classes-panel>\n\n<region-table\n [page]=\"pagedRegions\"\n [total]=\"regions.length\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [rowsPerPageOptions]=\"rowsPerPageOptions\"\n [metaKey]=\"metaKey\"\n [selection]=\"selectedRegions\"\n (selectionChange)=\"selectedRegions = $event\"\n (rowSelect)=\"onSelectionChanged()\"\n [presetSet]=\"presetSet\"\n [defaultColor]=\"shapeColor\"\n [mpp]=\"mpp\"\n [labelDrafts]=\"editingLabelRegions\"\n (labelEditStart)=\"startEditLabel($event)\"\n (labelDraftChange)=\"setLabelDraft($event.region, $event.value)\"\n (labelEditStop)=\"stopEditLabel($event.region, $event.commit)\"\n (classPick)=\"applyPresetToRegion($event.region, $event.name)\"\n (deleteRow)=\"deleteRegion($event)\"\n (pageChange)=\"onPageChange($event)\"\n>\n</region-table>\n\n<!-- Live-edit mode: no Save/Cancel \u2014 every edit commits immediately to the\n active image's region cache. -->\n\n<p-confirmDialog\n #cd\n [style]=\"{ width: '24rem', height: '14rem' }\"\n key=\"positionDialog\"\n [baseZIndex]=\"10000\"\n rejectButtonStyleClass=\"p-button-outlined\"\n>\n <ng-template pTemplate=\"footer\">\n <button type=\"button\" pButton icon=\"pi pi-check\" label=\"Yes\" (click)=\"cd.accept()\"></button>\n <button\n type=\"button\"\n class=\"p-button-outlined\"\n pButton\n icon=\"pi pi-times\"\n label=\"No\"\n (click)=\"cd.reject()\"\n ></button>\n </ng-template>\n</p-confirmDialog>\n\n<region-save-regions-dialog\n [(visible)]=\"showSaveAsDialog\"\n [filename]=\"saveAsFilename\"\n (filenameChange)=\"saveAsFilename = $event; checkSaveAsFileExists()\"\n [fileExists]=\"saveAsFileExists\"\n [busy]=\"saveAsBusy\"\n (confirm)=\"confirmSaveAs()\"\n (cancelSave)=\"cancelSaveAs()\"\n>\n</region-save-regions-dialog>\n\n<region-save-regions-dialog\n header=\"Export Regions\"\n confirmLabel=\"Export\"\n confirmIcon=\"pi pi-download\"\n inputId=\"export-filename\"\n [(visible)]=\"showExportDialog\"\n [(filename)]=\"exportFilename\"\n (confirm)=\"confirmExport()\"\n>\n</region-save-regions-dialog>\n\n<region-save-mask-dialog\n [(visible)]=\"showSaveMaskDialog\"\n [(filename)]=\"saveMaskFilename\"\n [(mode)]=\"maskMode\"\n [busy]=\"maskBusy\"\n [progress]=\"maskProgress\"\n [encoding]=\"maskEncoding\"\n (confirm)=\"confirmSaveMask()\"\n (cancelExport)=\"cancelSaveMask()\"\n>\n</region-save-mask-dialog>\n\n<region-color-dialog\n [(visible)]=\"showColorDialog\"\n [edits]=\"classColorEdits\"\n [selectedCount]=\"selectedRegions?.length || 0\"\n (apply)=\"applyColorToSelected($event)\"\n>\n</region-color-dialog>\n\n<!-- Manage annotation classes (jit-ui#70): edits a working copy, committed on Apply. -->\n<region-manage-classes-dialog\n [(visible)]=\"showManageDialog\"\n [(draft)]=\"presetDraft\"\n (apply)=\"applyManageDialog(true)\"\n (resetDefaults)=\"resetPresetsToDefaults()\"\n (importFile)=\"importPresets($event)\"\n (export)=\"exportPresets()\"\n>\n</region-manage-classes-dialog>\n\n<region-editor-help [(visible)]=\"displayHelpDialog\"></region-editor-help>\n", styles: ["@charset \"UTF-8\";.chip-swatch{display:inline-block;width:.75rem;height:.75rem;border-radius:50%;border:1px solid rgba(0,0,0,.2);flex:0 0 auto}.dd-value{display:inline-flex;align-items:center;gap:.4rem;min-width:0}.dd-value .class-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host{display:flex;flex-direction:column;height:100%;min-height:0;overflow:hidden}::ng-deep region-editor .p-toolbar.region-toolbar{padding:.25rem .5rem;border-radius:0;border-left:none;border-right:none;border-top:none}::ng-deep region-editor .p-toolbar.region-toolbar .p-button.p-button-icon-only{width:auto;padding:.25rem .5rem}::ng-deep region-editor .p-toolbar.region-toolbar .p-button{padding:.25rem .5rem}.toolbar-icon-dark{filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}\n"] }]
1181
935
  }], ctorParameters: () => [{ type: undefined, decorators: [{
1182
936
  type: Inject,
1183
937
  args: [REGION_EDITOR_API]
1184
- }] }, { type: i1.MessageService }, { type: i1.ConfirmationService }, { type: undefined, decorators: [{
1185
- type: Inject,
1186
- args: [REGION_IO_PORT]
1187
- }] }], propDecorators: { overlayPanel: [{
938
+ }] }, { type: i1.MessageService }, { type: i1.ConfirmationService }, { type: i2.RegionPersistenceService }, { type: i3.MaskExportService }], propDecorators: { overlayPanel: [{
1188
939
  type: ViewChild,
1189
940
  args: ['op']
1190
941
  }] } });
1191
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVnaW9uLWVkaXRvci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3JlZ2lvbi1lZGl0b3IvcmVnaW9uLWVkaXRvci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9zcmMvbGliL3JlZ2lvbi1lZGl0b3IvcmVnaW9uLWVkaXRvci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBcUIsU0FBUyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ2hGLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUNwRCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzdDLE9BQU8sRUFBRSxZQUFZLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFekQsT0FBTyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLFlBQVksRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzVFLE9BQU8sRUFBMEIsZ0JBQWdCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNsRixPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFMUQsT0FBTyxFQUFFLG1CQUFtQixFQUFFLGNBQWMsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNsRSxPQUFPLEVBQW9CLGlCQUFpQixFQUFFLE1BQU0seUNBQXlDLENBQUM7QUFDOUYsT0FBTyxFQUFnQixjQUFjLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUNqRixPQUFPLEVBQUUsYUFBYSxFQUFFLFVBQVUsRUFBRSxZQUFZLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDeEUsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGtCQUFrQixDQUFDOzs7Ozs7Ozs7Ozs7Ozs7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OzBCQXVHdEIsTUFBTTsyQkFBQyxpQkFBaUI7OzBCQUd4QixNQUFNOzJCQUFDLGNBQWM7eUNBbkdQLFlBQVk7c0JBQTVCLFNBQVM7dUJBQUMsSUFBSSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgSW5qZWN0LCBPbkRlc3Ryb3ksIE9uSW5pdCwgVmlld0NoaWxkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBPdmVybGF5UGFuZWwgfSBmcm9tICdwcmltZW5nL292ZXJsYXlwYW5lbCc7XG5pbXBvcnQgeyBzYXZlQXMgfSBmcm9tICdmaWxlLXNhdmVyJztcbmltcG9ydCB7IFN1YmplY3QsIFN1YnNjcmlwdGlvbiB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgZGVib3VuY2VUaW1lLCBzd2l0Y2hNYXAgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5cbmltcG9ydCB7IFBvbHlnb24sIFJlY3RhbmdsZSwgUmVnaW9uLCBNdWx0aVBvbHlnb24gfSBmcm9tICcuLi9tb2RlbHMvcmVnaW9uJztcbmltcG9ydCB7IFByZXNldFNldCwgQ2xhc3NQcmVzZXQsIGRlZmF1bHRQcmVzZXRTZXQgfSBmcm9tICcuLi9tb2RlbHMvY2xhc3MtcHJlc2V0JztcbmltcG9ydCB7IGNvbG9yRm9yTGFiZWwgfSBmcm9tICcuLi9zdG9yZS9jbGFzcy1jb2xvci51dGlsJztcbmltcG9ydCB7IElJbWFnZU1ldGFkYXRhIH0gZnJvbSAnLi4vY29udHJhY3RzL2ltYWdlLmNvbnRyYWN0JztcbmltcG9ydCB7IENvbmZpcm1hdGlvblNlcnZpY2UsIE1lc3NhZ2VTZXJ2aWNlIH0gZnJvbSAncHJpbWVuZy9hcGknO1xuaW1wb3J0IHsgSVJlZ2lvbkVkaXRvckFwaSwgUkVHSU9OX0VESVRPUl9BUEkgfSBmcm9tICcuLi9jb250cmFjdHMvcmVnaW9uLWVkaXRvci1hcGkuY29udHJhY3QnO1xuaW1wb3J0IHsgUmVnaW9uSW9Qb3J0LCBSRUdJT05fSU9fUE9SVCB9IGZyb20gJy4uL2NvbnRyYWN0cy9wb3J0cy9yZWdpb24taW8ucG9ydCc7XG5pbXBvcnQgeyByZWdpb25Ub1BhcnRzLCBzY2FsZVBhcnRzLCBtYXNrU2NhbGVGb3IgfSBmcm9tICcuL21hc2stcmFzdGVyJztcbmltcG9ydCB7IFZJWl9UT0FTVF9LRVkgfSBmcm9tICcuLi90b2FzdC1vdXRsZXRzJztcblxuQENvbXBvbmVudCh7XG4gIC8vIENhbm9uaWNhbCBwcmVmaXhlZCBzZWxlY3RvciBmaXJzdDsgdGhlIHVucHJlZml4ZWQgb3JpZ2luYWwgaXMga2VwdCBhcyBhblxuICAvLyBhbGlhcyBmb3Igb25lIHJlbGVhc2UgKHByZS1wdWJsaWNhdGlvbiBiYWNrLWNvbXBhdCkuXG4gIHNlbGVjdG9yOiAncmVnaW9uLWVkaXRvcicsXG4gIHRlbXBsYXRlVXJsOiAnLi9yZWdpb24tZWRpdG9yLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vcmVnaW9uLWVkaXRvci5jb21wb25lbnQuc2NzcyddLFxufSlcbmV4cG9ydCBjbGFzcyBSZWdpb25FZGl0b3JDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSB7XG4gIHByb3RlY3RlZCByZWFkb25seSBBcnJheSA9IEFycmF5O1xuXG4gIEBWaWV3Q2hpbGQoJ29wJykgb3ZlcmxheVBhbmVsITogT3ZlcmxheVBhbmVsO1xuXG4gIHJlZ2lvbnM6IFJlZ2lvbltdID0gW107XG4gIHJlZ2lvbnNDb3B5OiBSZWdpb25bXSA9IFtdO1xuICBzZWxlY3RlZFJlZ2lvbnM6IFJlZ2lvbltdID0gW107XG4gIHNob3dTaGFwZUxhYmVsITogYm9vbGVhbjtcbiAgc2hhcGVDb2xvciE6IHN0cmluZztcbiAgZmlsbENvbG9yITogc3RyaW5nO1xuICBkaXNwbGF5SGVscERpYWxvZyA9IGZhbHNlO1xuICBzaG93Q29sb3JEaWFsb2cgPSBmYWxzZTtcbiAgLyoqIE9uZSBjb2xvdXIgZWRpdG9yIHBlciB1bmlxdWUgY2xhc3MgYW1vbmcgdGhlIHNlbGVjdGVkIHJlZ2lvbnMsIHNob3duIGluIHRoZVxuICAgKiAgXCJlZGl0IGNvbG91ciBvZiBzZWxlY3RlZCByZWdpb25zXCIgZGlhbG9nLiBVbmNsYXNzaWZpZWQgcmVnaW9ucyAobm8gbGFiZWwpXG4gICAqICBhcmUgZ3JvdXBlZCB1bmRlciBhIHNpbmdsZSBlbnRyeSB3aXRoIGFuIGVtcHR5IGBsYWJlbGAuICovXG4gIGNsYXNzQ29sb3JFZGl0czogeyBsYWJlbDogc3RyaW5nOyBjb2xvcjogc3RyaW5nIH1bXSA9IFtdO1xuICBsYWJlbENvbG9yczogTWFwPHN0cmluZywgc3RyaW5nPiA9IG5ldyBNYXAoKTtcbiAgc2VsZWN0ZWRMYWJlbENvbG9yID0gJyc7XG4gIC8qKiBcIkVkaXQgY2xhc3Mgb24gc2VsZWN0ZWQgcm93c1wiIHBvcHVwOiBjaG9zZW4gZXhpc3RpbmcgY2xhc3MsIGFuZCBhIG5ldy1jbGFzcyBuYW1lLiAqL1xuICBidWxrQ2xhc3MgPSAnJztcbiAgbmV3QnVsa0NsYXNzID0gJyc7XG5cbiAgLy8g4pSA4pSAIGFubm90YXRpb24tY2xhc3MgcHJlc2V0cyAoaml0LXVpIzcwKSDilIDilIBcbiAgLyoqIExvY2FsIG1pcnJvciBvZiB0aGUgcGVyLXVzZXIgcHJlc2V0IHNldCAoY2hpcCBzdHJpcCwgQ2xhc3MgZHJvcGRvd24sIGRpYWxvZykuICovXG4gIHByZXNldFNldDogUHJlc2V0U2V0ID0gZGVmYXVsdFByZXNldFNldCgpO1xuICAvKiogQ2xhc3MgYXBwbGllZCB0byBuZXdseSBkcmF3bi9hZGRlZCByZWdpb25zIGFuZCB0byBidWxrIGNoaXAtY2xpY2tzLiAqL1xuICBhY3RpdmVDbGFzczogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG4gIHNob3dNYW5hZ2VEaWFsb2cgPSBmYWxzZTtcbiAgLyoqIFdvcmtpbmcgY29weSBlZGl0ZWQgaW5zaWRlIHRoZSBNYW5hZ2UtY2xhc3NlcyBkaWFsb2c7IGNvbW1pdHRlZCBvbiBBcHBseS9Eb25lLiAqL1xuICBwcmVzZXREcmFmdDogUHJlc2V0U2V0IHwgbnVsbCA9IG51bGw7XG4gIC8qKiBjbGFzcyBuYW1lIChtYXRjaC1tb2RlIGtleWVkKSDihpIgbnVtYmVyIG9mIHJlZ2lvbnMgY3VycmVudGx5IHVzaW5nIGl0LiAqL1xuICBjbGFzc0NvdW50cyA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KCk7XG4gIC8qKiBGYWxsYmFjayBjbGFzcyB0aGF0IGEgcmVnaW9uIHJldmVydHMgdG8gd2hlbiBpdHMgY2xhc3MgaXMgZGVsZXRlZCAoaml0LXVpIzcwKS4gKi9cbiAgcmVhZG9ubHkgZGVmYXVsdENsYXNzTmFtZSA9ICdSZWdpb24nO1xuICAvKiogQ2xhc3NlcyBhcyBzaG93biBpbiB0aGUgcGFuZWw6IHNvcnRlZCBieSByZWdpb24gY291bnQgKGRlc2MpLCB0aGVuIG5hbWUuICovXG4gIGRpc3BsYXlDbGFzc2VzOiBDbGFzc1ByZXNldFtdID0gW107XG4gIHJlYWRvbmx5IG1hdGNoTW9kZU9wdGlvbnMgPSBbXG4gICAgeyBsYWJlbDogJ0V4YWN0JywgdmFsdWU6ICdleGFjdCcgfSxcbiAgICB7IGxhYmVsOiAnTm9ybWFsaXplZCcsIHZhbHVlOiAnbm9ybWFsaXplZCcgfSxcbiAgXTtcblxuICBwYWdpbmF0b3JGaXJzdCA9IDA7XG4gIHBhZ2luYXRvclJvd3MgPSAxMDtcbiAgcmVhZG9ubHkgcm93c1BlclBhZ2VPcHRpb25zID0gWzEwLCAyNSwgNTBdO1xuICAvKiogUmVnaW9ucyB3aG9zZSBDbGFzcyBjZWxsIGlzIGN1cnJlbnRseSBpbiBlZGl0IG1vZGUuXG4gICAqICBVc2luZyBvYmplY3QgaWRlbnRpdHkgYXMgdGhlIGtleSBhdm9pZHMgbmVlZGluZyBhIHVuaXF1ZSBpZCBmaWVsZC4gKi9cbiAgZWRpdGluZ0xhYmVsUmVnaW9ucyA9IG5ldyBTZXQ8YW55PigpO1xuXG4gIHNob3dTYXZlQXNEaWFsb2cgPSBmYWxzZTtcbiAgc2F2ZUFzRmlsZW5hbWUgPSAnJztcbiAgc2F2ZUFzRmlsZUV4aXN0cyA9IGZhbHNlO1xuICAvKiogVHJ1ZSB3aGlsZSBhIEdlb0pTT04gcGVyc2lzdCBpcyBzZXJpYWxpemluZy91cGxvYWRpbmcg4oCUIGRyaXZlcyB0aGUgZGlhbG9nJ3NcbiAgICogIHByb2dyZXNzIGJhciBhbmQgQ2FuY2VsIGJ1dHRvbiAocGFyaXR5IHdpdGggdGhlIFNhdmUtbWFzayBkaWFsb2cpLiAqL1xuICBzYXZlQXNCdXN5ID0gZmFsc2U7XG4gIHByaXZhdGUgX3NhdmVBc0NoZWNrJCA9IG5ldyBTdWJqZWN0PHN0cmluZz4oKTtcbiAgcHJpdmF0ZSBfc2F2ZUFzU3ViPzogU3Vic2NyaXB0aW9uO1xuICAvKiogSGFuZGxlIGZvciB0aGUgZGVmZXJyZWQtc2VyaWFsaXplIHRpbWVyIHNvIENhbmNlbC9kZXN0cm95IGNhbiBhYm9ydCBpdFxuICAgKiAgYmVmb3JlIGl0IGZpcmVzIChvdGhlcndpc2UgdGhlIHVwbG9hZCB3b3VsZCBzdGlsbCBzdGFydCkuICovXG4gIHByaXZhdGUgX3NhdmVBc1RpbWVyPzogUmV0dXJuVHlwZTx0eXBlb2Ygc2V0VGltZW91dD47XG5cbiAgc2hvd0V4cG9ydERpYWxvZyA9IGZhbHNlO1xuICBleHBvcnRGaWxlbmFtZSA9ICcnO1xuXG4gIHNob3dTYXZlTWFza0RpYWxvZyA9IGZhbHNlO1xuICBzYXZlTWFza0ZpbGVuYW1lID0gJyc7XG4gIC8qKiBNYXNrIHR5cGUgY2hvc2VuIGluIHRoZSBTYXZlLW1hc2sgZGlhbG9nOiBhIGJpbmFyeSBmb3JlZ3JvdW5kL2JhY2tncm91bmRcbiAgICogIG1hc2ssIG9yIGEgbXVsdGktY2xhc3MgbWFzayB3aXRoIGEgZGlzdGluY3QgaWQgcGVyIHJlZ2lvbi4gKi9cbiAgbWFza01vZGU6ICdiaW5hcnknIHwgJ211bHRpY2xhc3MnID0gJ2JpbmFyeSc7XG4gIC8qKiBUcnVlIHdoaWxlIHRoZSBtYXNrIHdvcmtlciBpcyByYXN0ZXJpemluZy9lbmNvZGluZyDigJQgZHJpdmVzIHRoZSBwcm9ncmVzc1xuICAgKiAgYmFyIGFuZCB0aGUgQ2FuY2VsIGJ1dHRvbiBpbiB0aGUgZGlhbG9nLiAqL1xuICBtYXNrQnVzeSA9IGZhbHNlO1xuICAvKiogMOKAkzEwMCByYXN0ZXJpemF0aW9uIHByb2dyZXNzOyBzd2l0Y2hlcyB0byBpbmRldGVybWluYXRlIGR1cmluZyBlbmNvZGluZy4gKi9cbiAgbWFza1Byb2dyZXNzID0gMDtcbiAgbWFza0VuY29kaW5nID0gZmFsc2U7XG4gIHByaXZhdGUgbWFza1dvcmtlcj86IFdvcmtlcjtcbiAgcHJpdmF0ZSBwZW5kaW5nTWFza0ZpbGVuYW1lID0gJyc7XG5cbiAgLyoqXG4gICAqIFByaW1lTkcgcC10YWJsZSBtdWx0aS1zZWxlY3Rpb24gbW9kZSB0b2dnbGUuIFdoZW4gdHJ1ZSwgc2luZ2xlLWNsaWNrXG4gICAqIHNlbGVjdHMgb25lIHJvdyAocmVwbGFjaW5nIHByaW9yKTsgbWV0YS9jdHJsLWNsaWNrIHRvZ2dsZXMgYSByb3cgaW4vb3V0XG4gICAqIG9mIHRoZSBzZWxlY3Rpb24uIFdoZW4gZmFsc2UsIGV2ZXJ5IGNsaWNrIHRvZ2dsZXMuIERlZmF1bHQgdHJ1ZSB0b1xuICAgKiBtYXRjaCB0aGUgbW9zdCBjb21tb24gc3ByZWFkc2hlZXQtbGlrZSBVWC5cbiAgICovXG4gIG1ldGFLZXkgPSB0cnVlO1xuXG4gIHByaXZhdGUgX3VwZGF0aW5nRnJvbUVkaXRvciA9IGZhbHNlO1xuICBwcml2YXRlIF9zdXBwcmVzc1NlbGVjdGlvblN5bmNUb1Bsb3QgPSBmYWxzZTtcbiAgcHJpdmF0ZSBfcmVnaW9uU3ViID0gbmV3IFN1YnNjcmlwdGlvbigpO1xuICBwcml2YXRlIF9zZWxlY3RlZElkeFN1YiA9IG5ldyBTdWJzY3JpcHRpb24oKTtcbiAgcHJpdmF0ZSBfbWV0YVN1YiA9IG5ldyBTdWJzY3JpcHRpb24oKTtcbiAgcHJpdmF0ZSBfcHJlc2V0U3ViID0gbmV3IFN1YnNjcmlwdGlvbigpO1xuXG4gIC8qKiBQaHlzaWNhbCBwaXhlbCBzaXplICjCtW0vcGl4ZWwpIG9mIHRoZSBhY3RpdmUgaW1hZ2UsIGZvciByZWdpb24gYXJlYXMgaW5cbiAgICogIMK1bcKyLiBVbmRlZmluZWQgd2hlbiB0aGUgZm9ybWF0IHJlcG9ydHMgbm8gcGh5c2ljYWwgc2l6ZS4gKi9cbiAgcHJpdmF0ZSBtcHBYPzogbnVtYmVyO1xuICBwcml2YXRlIG1wcFk/OiBudW1iZXI7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgQEluamVjdChSRUdJT05fRURJVE9SX0FQSSkgcHJpdmF0ZSByZWdpb25BcGk6IElSZWdpb25FZGl0b3JBcGksXG4gICAgcHVibGljIG1lc3NhZ2VTZXJ2aWNlOiBNZXNzYWdlU2VydmljZSxcbiAgICBwcml2YXRlIGNvbmZpcm1hdGlvblNlcnZpY2U6IENvbmZpcm1hdGlvblNlcnZpY2UsXG4gICAgQEluamVjdChSRUdJT05fSU9fUE9SVCkgcHJpdmF0ZSByZWdpb25JbzogUmVnaW9uSW9Qb3J0LFxuICApIHt9XG5cbiAgLyoqXG4gICAqIERlZXAgY29weSBvZiByZWdpb25zIGZvciBkaXJ0eS10cmFja2luZyAoYHJlZ2lvbnNDb3B5YCkuIFJldHVybnMgcGxhaW4tb2JqZWN0XG4gICAqIGNsb25lcyDigJQgb25seSBzdHJ1Y3R1cmFsIGVxdWFsaXR5IG1hdHRlcnMgaGVyZSwgbm90IHByb3RvdHlwZXMuXG4gICAqIFRPRE86IHJlcGxhY2Ugd2l0aCBgc3RydWN0dXJlZENsb25lYCBvbmNlIHZlcmlmaWVkLlxuICAgKi9cbiAgcHJpdmF0ZSBkZWVwQ2xvbmU8VD4oaXRlbXM6IFRbXSk6IFRbXSB7XG4gICAgcmV0dXJuIGl0ZW1zLm1hcCgoaXRlbSkgPT4ge1xuICAgICAgaWYgKEFycmF5LmlzQXJyYXkoaXRlbSkpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZGVlcENsb25lKGl0ZW0pIGFzIHVua25vd24gYXMgVDtcbiAgICAgIH0gZWxzZSBpZiAodHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmIGl0ZW0gIT09IG51bGwpIHtcbiAgICAgICAgY29uc3QgY2xvbmVkOiBhbnkgPSB7fTtcbiAgICAgICAgZm9yIChjb25zdCBrZXkgaW4gaXRlbSkge1xuICAgICAgICAgIGNsb25lZFtrZXldID0gdGhpcy5kZWVwQ2xvbmUoW2l0ZW1ba2V5XV0pWzBdO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBjbG9uZWQ7XG4gICAgICB9XG4gICAgICByZXR1cm4gaXRlbTtcbiAgICB9KTtcbiAgfVxuXG4gIG5nT25Jbml0KCkge1xuICAgIHRoaXMuc2hvd1NoYXBlTGFiZWwgPSB0aGlzLnJlZ2lvbkFwaS5nZXRTaG93U2hhcGVMYWJlbCgpO1xuICAgIHRoaXMuc2hhcGVDb2xvciA9IHRoaXMucmVnaW9uQXBpLmdldFNoYXBlQ29sb3IoKTtcbiAgICB0aGlzLmZpbGxDb2xvciA9IHRoaXMucmVnaW9uQXBpLmdldEZpbGxDb2xvcigpO1xuXG4gICAgLy8gQW5ub3RhdGlvbi1jbGFzcyBwcmVzZXRzIChqaXQtdWkjNzApOiBtaXJyb3IgdGhlIHNldCBmb3IgdGhlIGNoaXAgc3RyaXAsXG4gICAgLy8gdGhlIENsYXNzIGRyb3Bkb3duIGFuZCB0aGUgbWFuYWdlIGRpYWxvZy4gR3VhcmRlZCB3aXRoIG9wdGlvbmFsIGNhbGxzIHNvXG4gICAgLy8gcGFydGlhbCBBUEkgbW9ja3MgaW4gdGVzdHMgKHdoaWNoIG9taXQgdGhlIHByZXNldCBtZXRob2RzKSBkb24ndCB0aHJvdy5cbiAgICBjb25zdCBpbml0aWFsUHJlc2V0cyA9IHRoaXMucmVnaW9uQXBpLmdldFByZXNldFNldD8uKCk7XG4gICAgaWYgKGluaXRpYWxQcmVzZXRzKSB0aGlzLnByZXNldFNldCA9IGluaXRpYWxQcmVzZXRzO1xuICAgIGNvbnN0IHByZXNldHMkID0gdGhpcy5yZWdpb25BcGkuZ2V0UHJlc2V0U2V0JD8uKCk7XG4gICAgaWYgKHByZXNldHMkKSB7XG4gICAgICB0aGlzLl9wcmVzZXRTdWIgPSBwcmVzZXRzJC5zdWJzY3JpYmUoKHNldCkgPT4ge1xuICAgICAgICBpZiAoc2V0KSB0aGlzLnByZXNldFNldCA9IHNldDtcbiAgICAgICAgdGhpcy51cGRhdGVEaXNwbGF5Q2xhc3NlcygpO1xuICAgICAgfSk7XG4gICAgfVxuICAgIC8vIFNlZWQgZnJvbSB0aGUgdmlzdWFsaXplcidzIGN1cnJlbnQgcmVnaW9ucyDigJQgYWxyZWFkeSBzY29wZWQgdG8gdGhlXG4gICAgLy8gYWN0aXZlIGltYWdlIGJ5IHRoZSBwZXItaW1hZ2UgY2FjaGUsIGFuZCBoYW5kZWQgYmFjayBhcyBuZXV0cmFsIFJlZ2lvblxuICAgIC8vIG9iamVjdHMgKG5vIGJhY2tlbmQgc2hhcGUgZm9ybWF0IGludm9sdmVkKS5cbiAgICAvLyBBbm5vdGF0aW9uIHJlZ2lvbnMgb25seSDigJQgaW50ZW5zaXR5LXByb2ZpbGUgbGluZXMgYXJlIG93bmVkIGJ5IHRoZVxuICAgIC8vIGludGVuc2l0eSB0b29sIGFuZCBleGNsdWRlZCBieSB0aGUgY29udHJhY3QsIHNvIHRoZSBlZGl0b3IgbmV2ZXIgc2VlcyB0aGVtLlxuICAgIHRoaXMucmVnaW9ucyA9IHRoaXMuYXBwbHlSZWdpb25Db2xvcnModGhpcy5yZWdpb25BcGkuZ2V0QW5ub3RhdGlvblJlZ2lvbnMoKSk7XG4gICAgdGhpcy5yZWdpb25zQ29weSA9IHRoaXMuZGVlcENsb25lKHRoaXMucmVnaW9ucyk7XG4gICAgdGhpcy5zeW5jQ2xhc3Nlc0Zyb21SZWdpb25zKHRoaXMucmVnaW9ucyk7XG4gICAgdGhpcy5yZWNvbXB1dGVDbGFzc0NvdW50cygpO1xuXG4gICAgLy8gVGhlIHVwZGF0ZSBldmVudCBpcyBqdXN0IGEgY2hhbmdlIHNpZ25hbDsgcmUtcmVhZCB0aGUgcmVnaW9ucyBmcm9tIHRoZVxuICAgIC8vIHZpc3VhbGl6ZXIgcmF0aGVyIHRoYW4gcGFyc2luZyB3aGF0ZXZlciBwYXlsb2FkIGl0IGNhcnJpZXMuXG4gICAgdGhpcy5fcmVnaW9uU3ViID0gdGhpcy5yZWdpb25BcGkuZ2V0UmVnaW9uVXBkYXRlRXZlbnQoKS5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgaWYgKHRoaXMuX3VwZGF0aW5nRnJvbUVkaXRvcikgcmV0dXJuO1xuICAgICAgY29uc3QgdXBkYXRlZCA9IHRoaXMuYXBwbHlSZWdpb25Db2xvcnModGhpcy5yZWdpb25BcGkuZ2V0QW5ub3RhdGlvblJlZ2lvbnMoKSk7XG4gICAgICAvLyBwcmVzZXJ2ZSBzZWxlY3Rpb24gZm9yIHJlZ2lvbnMgdGhhdCBzdGlsbCBleGlzdCAoYnkgaWQsIHNpbmNlIG5hbWVcbiAgICAgIC8vIGlzIGEgdXNlci1lZGl0YWJsZSBkaXNwbGF5IGxhYmVsIGFuZCBtYXkgY29sbGlkZSlcbiAgICAgIGNvbnN0IHNlbGVjdGVkSWRzID0gbmV3IFNldCh0aGlzLnNlbGVjdGVkUmVnaW9ucy5tYXAoKHIpID0+IHIuaWQpKTtcbiAgICAgIHRoaXMucmVnaW9ucyA9IHVwZGF0ZWQ7XG4gICAgICB0aGlzLnJlZ2lvbnNDb3B5ID0gdGhpcy5kZWVwQ2xvbmUodGhpcy5yZWdpb25zKTtcbiAgICAgIHRoaXMuc2VsZWN0ZWRSZWdpb25zID0gdXBkYXRlZC5maWx0ZXIoKHIpID0+IHNlbGVjdGVkSWRzLmhhcyhyLmlkKSk7XG4gICAgICB0aGlzLmNsYW1wUGFnaW5hdG9yRmlyc3QoKTtcbiAgICAgIHRoaXMuc3luY0NsYXNzZXNGcm9tUmVnaW9ucyh1cGRhdGVkKTtcbiAgICAgIHRoaXMucmVjb21wdXRlQ2xhc3NDb3VudHMoKTtcbiAgICB9KTtcblxuICAgIHRoaXMuX3NhdmVBc0NoZWNrJC5waXBlKFxuICAgICAgZGVib3VuY2VUaW1lKDQwMCksXG4gICAgICBzd2l0Y2hNYXAobmFtZSA9PiB0aGlzLnJlZ2lvbklvLnJvaUZpbGVFeGlzdHMobmFtZSkpLFxuICAgICkuc3Vic2NyaWJlKHtcbiAgICAgIG5leHQ6IGV4aXN0cyA9PiB7IHRoaXMuc2F2ZUFzRmlsZUV4aXN0cyA9IGV4aXN0czsgfSxcbiAgICAgIGVycm9yOiAoKSA9PiB7IHRoaXMuc2F2ZUFzRmlsZUV4aXN0cyA9IGZhbHNlOyB9LFxuICAgIH0pO1xuXG4gICAgLy8gUGh5c2ljYWwgcGl4ZWwgc2l6ZSBvZiB0aGUgYWN0aXZlIGltYWdlIChmb3IgcmVnaW9uIGFyZWFzIGluIMK1bcKyL21twrIpLlxuICAgIC8vIE1pcnJvciBQbG90bHlTZXJ2aWNlLmN1cnJlbnRNcHA6IHRoZSBjYWxpYnJhdGlvbiBtYXkgc2l0IG9uIGFueSBjaGFubmVsXG4gICAgLy8gZW50cnksIG5vdCBuZWNlc3NhcmlseSBbMF0sIHNvIHBpY2sgdGhlIGZpcnN0IGVudHJ5IHdpdGggYSBwb3NpdGl2ZSBtcHBYXG4gICAgLy8gcmF0aGVyIHRoYW4gcmVhZGluZyBbMF0gYmxpbmRseS4gRmFsbCBiYWNrIHRvIHNxdWFyZSBwaXhlbHMgKG1wcFkgPSBtcHBYKVxuICAgIC8vIHdoZW4gb25seSBvbmUgYXhpcyBpcyByZXBvcnRlZC5cbiAgICB0aGlzLl9tZXRhU3ViID0gdGhpcy5yZWdpb25BcGkuZ2V0SW1hZ2VNZXRhKCkuc3Vic2NyaWJlKChtZXRhKSA9PiB7XG4gICAgICBjb25zdCB7IG1wcFgsIG1wcFkgfSA9IHRoaXMucGlja01wcChtZXRhKTtcbiAgICAgIHRoaXMubXBwWCA9IG1wcFg7XG4gICAgICB0aGlzLm1wcFkgPSBtcHBZO1xuICAgIH0pO1xuXG4gICAgdGhpcy5fc2VsZWN0ZWRJZHhTdWIgPSB0aGlzLnJlZ2lvbkFwaS5nZXRTZWxlY3RlZFJlZ2lvbnMkKCkuc3Vic2NyaWJlKChzZWxlY3RlZCkgPT4ge1xuICAgICAgaWYgKHRoaXMuX3N1cHByZXNzU2VsZWN0aW9uU3luY1RvUGxvdCkgcmV0dXJuO1xuICAgICAgLy8gTWFwIHRoZSBjb250cmFjdCdzIHNlbGVjdGVkIHJlZ2lvbnMgdG8gdGhlIGVkaXRvcidzIG93biBpbnN0YW5jZXMgYnkgaWQuXG4gICAgICBjb25zdCBuZXh0ID0gc2VsZWN0ZWRcbiAgICAgICAgLm1hcCgocykgPT4gdGhpcy5yZWdpb25zLmZpbmQoKHIpID0+IHIuaWQgPT09IHMuaWQpKVxuICAgICAgICAuZmlsdGVyKChyKTogciBpcyBSZWdpb24gPT4gISFyKTtcbiAgICAgIGNvbnN0IHNhbWUgPVxuICAgICAgICBuZXh0Lmxlbmd0aCA9PT0gdGhpcy5zZWxlY3RlZFJlZ2lvbnMubGVuZ3RoICYmXG4gICAgICAgIG5leHQuZXZlcnkoKHIsIGkpID0+IHIuaWQgPT09IHRoaXMuc2VsZWN0ZWRSZWdpb25zW2ldPy5pZCk7XG4gICAgICBpZiAoIXNhbWUpIHRoaXMuc2VsZWN0ZWRSZWdpb25zID0gbmV4dDtcbiAgICAgIC8vIFNjcm9sbCB0aGUgcGFnaW5hdG9yIHNvIHRoZSBtb3N0LXJlY2VudGx5LXNlbGVjdGVkIHJvdyBpcyB2aXNpYmxlLlxuICAgICAgaWYgKG5leHQubGVuZ3RoID4gMCkge1xuICAgICAgICBjb25zdCBsYXN0SWR4ID0gdGhpcy5yZWdpb25zLmZpbmRJbmRleCgocikgPT4gci5pZCA9PT0gbmV4dFtuZXh0Lmxlbmd0aCAtIDFdLmlkKTtcbiAgICAgICAgaWYgKGxhc3RJZHggPj0gMCkge1xuICAgICAgICAgIGNvbnN0IHBhZ2VTdGFydCA9IE1hdGguZmxvb3IobGFzdElkeCAvIHRoaXMucGFnaW5hdG9yUm93cykgKiB0aGlzLnBhZ2luYXRvclJvd3M7XG4gICAgICAgICAgaWYgKHBhZ2VTdGFydCAhPT0gdGhpcy5wYWdpbmF0b3JGaXJzdCkgdGhpcy5wYWdpbmF0b3JGaXJzdCA9IHBhZ2VTdGFydDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKSB7XG4gICAgdGhpcy5fcmVnaW9uU3ViLnVuc3Vic2NyaWJlKCk7XG4gICAgdGhpcy5fc2VsZWN0ZWRJZHhTdWIudW5zdWJzY3JpYmUoKTtcbiAgICB0aGlzLl9tZXRhU3ViLnVuc3Vic2NyaWJlKCk7XG4gICAgdGhpcy5fcHJlc2V0U3ViLnVuc3Vic2NyaWJlKCk7XG4gICAgaWYgKHRoaXMuX3NhdmVBc1RpbWVyICE9PSB1bmRlZmluZWQpIGNsZWFyVGltZW91dCh0aGlzLl9zYXZlQXNUaW1lcik7XG4gICAgdGhpcy5fc2F2ZUFzU3ViPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMudGVhcmRvd25NYXNrV29ya2VyKCk7XG4gIH1cblxuICAvKipcbiAgICogVGFibGUg4oaSIFBsb3Q6IFByaW1lTkcgZmlyZXMgb25Sb3dTZWxlY3Qvb25Sb3dVbnNlbGVjdCBhZnRlciB1cGRhdGluZ1xuICAgKiBgc2VsZWN0ZWRSZWdpb25zYC4gUHVzaCB0aGUgc2VsZWN0ZWQgcmVnaW9ucyB0byB0aGUgY29udHJhY3QgYnkgaWRlbnRpdHkg4oCUXG4gICAqIHRoZSBwYWNrYWdlIG1hcHMgdGhlbSB0byBpdHMgaW50ZXJuYWwgaW5kZXggc3BhY2UgKHdoaWNoIGluY2x1ZGVzIHRoZVxuICAgKiBpbnRlbnNpdHktcHJvZmlsZSBsaW5lcyB0aGUgZWRpdG9yIG5ldmVyIHNlZXMpLlxuICAgKi9cbiAgb25TZWxlY3Rpb25DaGFuZ2VkKCkge1xuICAgIHRoaXMuX3N1cHByZXNzU2VsZWN0aW9uU3luY1RvUGxvdCA9IHRydWU7XG4gICAgdHJ5IHtcbiAgICAgIHRoaXMucmVnaW9uQXBpLnNldFNlbGVjdGVkUmVnaW9ucyh0aGlzLnNlbGVjdGVkUmVnaW9ucyA/PyBbXSk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIHRoaXMuX3N1cHByZXNzU2VsZWN0aW9uU3luY1RvUGxvdCA9IGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBBcHBseSBjbGFzc2lmaWNhdGlvbiBjb2xvdXJzIGFuZCByZWZyZXNoIHRoZSBsYWJlbOKGkmNvbG91ciBtYXAgZm9yIHJlZ2lvbnNcbiAgICogY29taW5nIGZyb20gdGhlIHZpc3VhbGl6ZXIuIFRoZSB2aXN1YWxpemVyIGhhbmRzIGJhY2sgbmV1dHJhbCBgUmVnaW9uYFxuICAgKiBvYmplY3RzIHdpdGggYm91bmRzLCBjb2xvdXIgYW5kIGxhYmVsIGFscmVhZHkgcG9wdWxhdGVkLCBzbyB0aGUgZWRpdG9yIG5vXG4gICAqIGxvbmdlciBwYXJzZXMgYW55IGJhY2tlbmQtc3BlY2lmaWMgc2hhcGUgZm9ybWF0IOKAlCBpdCBvbmx5IGZpbGxzIGluIGFcbiAgICogZmFsbGJhY2sgY29sb3VyIGFuZCByZWJ1aWxkcyBpdHMgbG9jYWwgbGFiZWzihpJjb2xvdXIgbG9va3VwLlxuICAgKi9cbiAgcHJpdmF0ZSBhcHBseVJlZ2lvbkNvbG9ycyhyZWdpb25zOiBSZWdpb25bXSk6IFJlZ2lvbltdIHtcbiAgICBmb3IgKGNvbnN0IHJlZ2lvbiBvZiByZWdpb25zKSB7XG4gICAgICByZWdpb24uY29sb3IgPVxuICAgICAgICByZWdpb24uY29sb3IgfHxcbiAgICAgICAgdGhpcy5yZWdpb25BcGkuZ2V0Q2xhc3NpZmljYXRpb25Db2xvcnMoKS5nZXQocmVnaW9uLmxhYmVsID8/ICcnKSB8fFxuICAgICAgICB0aGlzLnNoYXBlQ29sb3I7XG4gICAgfVxuICAgIC8vIFJlYnVpbGQgbGFiZWxDb2xvcnM6IHNlZWQgZnJvbSBwZXJzaXN0ZWQgbWFwLCB0aGVuIG92ZXJsYXkgYWN0dWFsIHJlZ2lvbiBjb2xvcnNcbiAgICB0aGlzLmxhYmVsQ29sb3JzLmNsZWFyKCk7XG4gICAgZm9yIChjb25zdCBbbGFiZWwsIGNvbG9yXSBvZiB0aGlzLnJlZ2lvbkFwaS5nZXRDbGFzc2lmaWNhdGlvbkNvbG9ycygpKSB7XG4gICAgICB0aGlzLmxhYmVsQ29sb3JzLnNldChsYWJlbCwgY29sb3IpO1xuICAgIH1cbiAgICBmb3IgKGNvbnN0IHJlZ2lvbiBvZiByZWdpb25zKSB7XG4gICAgICBpZiAocmVnaW9uLmxhYmVsICYmIHJlZ2lvbi5jb2xvcikge1xuICAgICAgICB0aGlzLmxhYmVsQ29sb3JzLnNldChyZWdpb24ubGFiZWwsIHJlZ2lvbi5jb2xvcik7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZWdpb25zO1xuICB9XG5cbiAgLyoqXG4gICAqIFB1c2ggZWRpdHMgZnJvbSB0aGUgZWRpdG9yIHRvIHRoZSBkaWFncmFtIGluIGxpdmUtZWRpdCBtb2RlIChubyBzYXZlIC9cbiAgICogY2FuY2VsIGJ1dHRvbnMpLiBUaGUgYF91cGRhdGluZ0Zyb21FZGl0b3JgIGd1YXJkIGlzIHN0aWxsIHVzZWQgc28gdGhlXG4gICAqIHJlc3VsdGluZyByZWdpb25VcGRhdGVFdmVudCBkb2Vzbid0IGJvdW5jZSBiYWNrIGFzIGFuIGV4dGVybmFsIGNoYW5nZS5cbiAgICovXG4gIHByaXZhdGUgc2V0UmVnaW9uc0Zyb21FZGl0b3IoZmlsbENvbG9yPzogc3RyaW5nKSB7XG4gICAgdGhpcy5fdXBkYXRpbmdGcm9tRWRpdG9yID0gdHJ1ZTtcbiAgICAvLyBpc1JlZ2lvblNhdmVPbj10cnVlIHNvIGNoYW5nZXMgY29tbWl0IHRvIHRoZSByZWdpb24gc3RvcmUgKGFuZCB0aGUgcGVyLWltYWdlXG4gICAgLy8gY2FjaGUpIGltbWVkaWF0ZWx5LiBzZXRBbm5vdGF0aW9uUmVnaW9ucyBwcmVzZXJ2ZXMgdGhlIGludGVuc2l0eS1wcm9maWxlXG4gICAgLy8gbGluZXMgaW50ZXJuYWxseSwgc28gZWRpdG9yIGVkaXRzIG5ldmVyIGRpc3R1cmIgdGhlbS5cbiAgICB0aGlzLnJlZ2lvbkFwaS5zZXRBbm5vdGF0aW9uUmVnaW9ucyh0aGlzLnJlZ2lvbnMsIHRoaXMuc2hvd1NoYXBlTGFiZWwsIHRydWUsIGZpbGxDb2xvciA/PyB0aGlzLmZpbGxDb2xvcik7XG4gICAgdGhpcy5fdXBkYXRpbmdGcm9tRWRpdG9yID0gZmFsc2U7XG4gICAgLy8gVGhlIGVkaXRvcidzIG93biBjb21taXQgaXMgaWdub3JlZCBieSB0aGUgcmVnaW9uLXVwZGF0ZSBzdWJzY3JpcHRpb24gKHRoZVxuICAgIC8vIF91cGRhdGluZ0Zyb21FZGl0b3IgZ3VhcmQpLCBzbyByZWZyZXNoIGhlcmU6IGEgY2xhc3MgbGFiZWwgdHlwZWQgaW4gdGhlXG4gICAgLy8gQ2xhc3MgY29sdW1uIGdldHMgYWRkZWQgdG8gdGhlIGxpc3QsIGFuZCB0aGUgcGFuZWwgY291bnRzL29yZGVyIHN0YXkgaW5cbiAgICAvLyBzeW5jIGFmdGVyIGFueSBlZGl0LiAoaml0LXVpIzcwKVxuICAgIHRoaXMuc3luY0NsYXNzZXNGcm9tUmVnaW9ucyh0aGlzLnJlZ2lvbnMpO1xuICAgIHRoaXMucmVjb21wdXRlQ2xhc3NDb3VudHMoKTtcbiAgfVxuXG4gIC8qKiBTZXQgb25lIHJlZ2lvbidzIG91dGxpbmUgY29sb3VyIGZyb20gdGhlIHBlci1yb3cgcGlja2VyIGFuZCBjb21taXQgbGl2ZS5cbiAgICogIFJlbWVtYmVycyBpdCBhcyB0aGUgbGFiZWwncyBjb2xvdXIgc28gc2FtZS1jbGFzcyByZWdpb25zIGFkZGVkIGxhdGVyIG1hdGNoXG4gICAqICAoaml0LXVpIzg1IOKAlCB0aGUgUmVnaW9uIEVkaXRvcidzIHBlci1yZWdpb24gQ29sb3IgY29sdW1uKS4gKi9cbiAgY2hhbmdlUmVnaW9uQ29sb3IocmVnaW9uOiBSZWdpb24sIGNvbG9yOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBpZiAoIWNvbG9yIHx8IHJlZ2lvbi5jb2xvciA9PT0gY29sb3IpIHJldHVybjtcbiAgICByZWdpb24uY29sb3IgPSBjb2xvcjtcbiAgICByZWdpb24uY29sb3JPdmVycmlkZGVuID0gdHJ1ZTsgLy8gZXhwbGljaXQgcGVyLXJlZ2lvbiBjb2xvdXIg4oCUIHByZXNlcnZlIGl0IGFnYWluc3QgcHJlc2V0IChyZSlhcHBseSAoaml0LXVpIzcwKVxuICAgIGlmIChyZWdpb24ubGFiZWwpIHRoaXMubGFiZWxDb2xvcnMuc2V0KHJlZ2lvbi5sYWJlbCwgY29sb3IpO1xuICAgIHRoaXMuc2V0UmVnaW9uc0Zyb21FZGl0b3IoKTtcbiAgfVxuXG4gIGFkZFJlY3RhbmdsZSgpIHtcbiAgICBjb25zdCByZWdpb24gPSBuZXcgUmVnaW9uKCk7XG4gICAgcmVnaW9uLmJvdW5kcyA9IG5ldyBSZWN0YW5nbGUoKTtcbiAgICByZWdpb24uYm91bmRzLndpZHRoID0gNTEyO1xuICAgIHJlZ2lvbi5ib3VuZHMuaGVpZ2h0ID0gNTEyO1xuICAgIHJlZ2lvbi5sYWJlbCA9IHRoaXMuYWN0aXZlQ2xhc3MgPz8gJ1JlZ2lvbic7XG4gICAgLy8gaWQgYW5kIGEgbm9uLWNvbGxpZGluZyBuYW1lIGFyZSBtaW50ZWQgYnkgdGhlIHZpc3VhbGl6ZXIncyBzZXRSZWdpb25zXG4gICAgdGhpcy5yZWdpb25zID0gWy4uLnRoaXMucmVnaW9ucywgcmVnaW9uXTtcbiAgICB0aGlzLnNldFJlZ2lvbnNGcm9tRWRpdG9yKCk7XG4gICAgdGhpcy5yZWdpb25zQ29weSA9IHRoaXMuZGVlcENsb25lKHRoaXMucmVnaW9ucyk7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlIHRoZSBsYWJlbCBvZiB0aGUgcmVnaW9uIG9uIGVudGVyIGtleSBwcmVzc2VkICh3aGVuIGVkaXRpbmcgYSBsYWJlbCBjZWxsIGluIHRoZSB0YWJsZSlcbiAgICogQHBhcmFtIHJlZ2lvblxuICAgKiBAcGFyYW0gc2V0UmVnaW9uXG4gICAqL1xuICBsYWJlbFJlZ2lvblVwZGF0ZShyZWdpb246IFJlZ2lvbiwgc2V0UmVnaW9uID0gZmFsc2UpIHtcbiAgICBpZiAocmVnaW9uLmxhYmVsKSB7XG4gICAgICAvLyBpZiBsYWJlbCBkb2Vzbid0IGV4aXN0XG4gICAgICBpZiAoIXRoaXMubGFiZWxDb2xvcnMuaGFzKHJlZ2lvbi5sYWJlbCkpIHtcbiAgICAgICAgdGhpcy5sYWJlbENvbG9ycy5zZXQocmVnaW9uLmxhYmVsLCB0aGlzLnNoYXBlQ29sb3IpO1xuICAgICAgfVxuICAgIH1cbiAgICAvLyB1cGRhdGUgY29sb3JzIG9mIHRoZSByZWdpb25zXG4gICAgZm9yIChjb25zdCByZWcgb2YgdGhpcy5yZWdpb25zKSB7XG4gICAgICBpZiAoIXJlZy5jb2xvcikge1xuICAgICAgICBpZiAocmVnLmxhYmVsICYmIHRoaXMubGFiZWxDb2xvcnMuaGFzKHJlZy5sYWJlbCkpIHtcbiAgICAgICAgICByZWcuY29sb3IgPSB0aGlzLmxhYmVsQ29sb3JzLmdldChyZWcubGFiZWwpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIC8vIHVwZGF0ZSBsYWJlbHMgbWFwXG4gICAgdGhpcy5sYWJlbENvbG9ycy5jbGVhcigpO1xuICAgIGZvciAoY29uc3QgcmVnIG9mIHRoaXMucmVnaW9ucykge1xuICAgICAgaWYgKHJlZy5sYWJlbCAmJiByZWcuY29sb3IpIHtcbiAgICAgICAgdGhpcy5sYWJlbENvbG9ycy5zZXQocmVnLmxhYmVsLCByZWcuY29sb3IpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoc2V0UmVnaW9uKSB7XG4gICAgICB0aGlzLnNldFJlZ2lvbnNGcm9tRWRpdG9yKCk7XG4gICAgfVxuICB9XG4gIHhSZWN0VXBkYXRlKHJlZ2lvbjogUmVnaW9uLCBldmVudDogYW55KSB7XG4gICAgY29uc3QgcmVjdGFuZ2xlID0gcmVnaW9uLmJvdW5kcztcbiAgICBpZiAocmVjdGFuZ2xlICYmIHJlY3RhbmdsZSBpbnN0YW5jZW9mIFJlY3RhbmdsZSkge1xuICAgICAgcmVjdGFuZ2xlLnggPSBldmVudC52YWx1ZTtcbiAgICAgIHRoaXMuc2V0UmVnaW9uc0Zyb21FZGl0b3IoKTtcbiAgICB9XG4gIH1cbiAgeVJlY3RVcGRhdGUocmVnaW9uOiBSZWdpb24sIGV2ZW50OiBhbnkpIHtcbiAgICBjb25zdCByZWN0YW5nbGUgPSByZWdpb24uYm91bmRzO1xuICAgIGlmIChyZWN0YW5nbGUgJiYgcmVjdGFuZ2xlIGluc3RhbmNlb2YgUmVjdGFuZ2xlKSB7XG4gICAgICByZWN0YW5nbGUueSA9IGV2ZW50LnZhbHVlO1xuICAgICAgdGhpcy5zZXRSZWdpb25zRnJvbUVkaXRvcigpO1xuICAgIH1cbiAgfVxuICB3aWR0aFJlY3RVcGRhdGUocmVnaW9uOiBSZWdpb24sIGV2ZW50OiBhbnkpIHtcbiAgICBpZiAoZXZlbnQudmFsdWUgPT09IG51bGwgfHwgZXZlbnQudmFsdWUgPT09IHVuZGVmaW5lZCkgcmV0dXJuO1xuICAgIGNvbnN0IHJlY3RhbmdsZSA9IHJlZ2lvbi5ib3VuZHM7XG4gICAgaWYgKHJlY3RhbmdsZSAmJiByZWN0YW5nbGUgaW5zdGFuY2VvZiBSZWN0YW5nbGUpIHtcbiAgICAgIGNvbnN0IHJlZyA9IHRoaXMucmVnaW9uc0NvcHkuZmlsdGVyKChlbGVtZW50OiBSZWdpb24pID0+IGVsZW1lbnQuaWQgPT09IHJlZ2lvbi5pZCk7XG4gICAgICBpZiAocmVnLmxlbmd0aCkge1xuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L2Jhbi10cy1jb21tZW50XG4gICAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgICAgY29uc3Qgb2xkV2lkdGggPSByZWdbMF0uYm91bmRzLndpZHRoO1xuICAgICAgICBjb25zdCBkaWZmV2lkdGggPSBldmVudC52YWx1ZSAtIG9sZFdpZHRoO1xuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L2Jhbi10cy1jb21tZW50XG4gICAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgICAgcmVjdGFuZ2xlLnggPSBNYXRoLnJvdW5kKHJlZ1swXS5ib3VuZHMueCAtIGRpZmZXaWR0aCAvIDIpO1xuICAgICAgfVxuICAgICAgcmVjdGFuZ2xlLndpZHRoID0gZXZlbnQudmFsdWU7XG4gICAgICB0aGlzLnNldFJlZ2lvbnNGcm9tRWRpdG9yKCk7XG4gICAgICB0aGlzLnJlZ2lvbnNDb3B5ID0gdGhpcy5kZWVwQ2xvbmUodGhpcy5yZWdpb25zKTtcbiAgICB9XG4gIH1cbiAgaGVpZ2h0UmVjdFVwZGF0ZShyZWdpb246IFJlZ2lvbiwgZXZlbnQ6IGFueSkge1xuICAgIGlmIChldmVudC52YWx1ZSA9PT0gbnVsbCB8fCBldmVudC52YWx1ZSA9PT0gdW5kZWZpbmVkKSByZXR1cm47XG4gICAgY29uc3QgcmVjdGFuZ2xlID0gcmVnaW9uLmJvdW5kcztcbiAgICBpZiAocmVjdGFuZ2xlICYmIHJlY3RhbmdsZSBpbnN0YW5jZW9mIFJlY3RhbmdsZSkge1xuICAgICAgY29uc3QgcmVnID0gdGhpcy5yZWdpb25zQ29weS5maWx0ZXIoKGVsZW1lbnQ6IFJlZ2lvbikgPT4gZWxlbWVudC5pZCA9PT0gcmVnaW9uLmlkKTtcbiAgICAgIGlmIChyZWcubGVuZ3RoKSB7XG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvYmFuLXRzLWNvbW1lbnRcbiAgICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgICBjb25zdCBvbGRIZWlnaHQgPSByZWdbMF0uYm91bmRzLmhlaWdodDtcbiAgICAgICAgY29uc3QgZGlmZkhlaWdodCA9IGV2ZW50LnZhbHVlIC0gb2xkSGVpZ2h0O1xuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L2Jhbi10cy1jb21tZW50XG4gICAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgICAgcmVjdGFuZ2xlLnkgPSBNYXRoLnJvdW5kKHJlZ1swXS5ib3VuZHMueSAtIGRpZmZIZWlnaHQgLyAyKTtcbiAgICAgIH1cbiAgICAgIHJlY3RhbmdsZS5oZWlnaHQgPSBldmVudC52YWx1ZTtcbiAgICAgIHRoaXMuc2V0UmVnaW9uc0Zyb21FZGl0b3IoKTtcbiAgICAgIHRoaXMucmVnaW9uc0NvcHkgPSB0aGlzLmRlZXBDbG9uZSh0aGlzLnJlZ2lvbnMpO1xuICAgIH1cbiAgfVxuXG4gIG9uUGFnZUNoYW5nZShldmVudDogeyBmaXJzdD86IG51bWJlcjsgcm93cz86IG51bWJlciB9KSB7XG4gICAgdGhpcy5wYWdpbmF0b3JGaXJzdCA9IGV2ZW50LmZpcnN0ID8/IDA7XG4gICAgdGhpcy5wYWdpbmF0b3JSb3dzID0gZXZlbnQucm93cyA/PyB0aGlzLnBhZ2luYXRvclJvd3M7XG4gIH1cblxuICAvKiogU2VsZWN0IGV2ZXJ5IHJlZ2lvbiBpbiB0aGUgdGFibGUgKGFuZCBoaWdobGlnaHQgdGhlbSBvbiB0aGUgZGlhZ3JhbSkuICovXG4gIHNlbGVjdEFsbFJlZ2lvbnMoKSB7XG4gICAgdGhpcy5zZWxlY3RlZFJlZ2lvbnMgPSBbLi4udGhpcy5yZWdpb25zXTtcbiAgICB0aGlzLm9uU2VsZWN0aW9uQ2hhbmdlZCgpO1xuICB9XG5cbiAgZGVsZXRlU2VsZWN0ZWRSZWdpb25zKCkge1xuICAgIGlmICh0aGlzLnNlbGVjdGVkUmVnaW9ucyAmJiB0aGlzLnNlbGVjdGVkUmVnaW9ucy5sZW5ndGggPiAwKSB7XG4gICAgICAvLyBGaWx0ZXIgYnkgaWQg4oCUIG9iamVjdCBpZGVudGl0eSBpc24ndCByZWxpYWJsZSBhY3Jvc3MgcmUtcGFyc2VzLFxuICAgICAgLy8gYW5kIG5hbWUgaXMgYSB1c2VyLWVkaXRhYmxlIGxhYmVsIHRoYXQgbWF5IGNvbGxpZGUuXG4gICAgICBjb25zdCByZW1vdmVkID0gbmV3IFNldCh0aGlzLnNlbGVjdGVkUmVnaW9ucy5tYXAoKHIpID0+IHIuaWQpKTtcbiAgICAgIHRoaXMucmVnaW9ucyA9IHRoaXMucmVnaW9ucy5maWx0ZXIoKHIpID0+ICFyZW1vdmVkLmhhcyhyLmlkKSk7XG4gICAgICB0aGlzLnNlbGVjdGVkUmVnaW9ucyA9IFtdO1xuICAgICAgdGhpcy5jbGFtcFBhZ2luYXRvckZpcnN0KCk7XG4gICAgICB0aGlzLnNldFJlZ2lvbnNGcm9tRWRpdG9yKCk7XG4gICAgICAvLyBTeW5jIHRoZSBjbGVhcmVkIHNlbGVjdGlvbiB3aXRoIHRoZSBwbG90J3MgaGlnaGxpZ2h0IHN0YXRlLlxuICAgICAgdGhpcy5vblNlbGVjdGlvbkNoYW5nZWQoKTtcbiAgICB9XG4gIH1cblxuICBjbGVhckFsbFJlZ2lvbnMoKSB7XG4gICAgdGhpcy5jb25maXJtYXRpb25TZXJ2aWNlLmNvbmZpcm0oe1xuICAgICAga2V5OiAncG9zaXRpb25EaWFsb2cnLFxuICAgICAgbWVzc2FnZTogJ0FyZSB5b3Ugc3VyZSB5b3Ugd2FudCB0byBkZWxldGUgYWxsIHJlZ2lvbnM/JyxcbiAgICAgIGFjY2VwdDogKCkgPT4ge1xuICAgICAgICB0aGlzLnJlZ2lvbnMgPSBbXTtcbiAgICAgICAgdGhpcy5zZWxlY3RlZFJlZ2lvbnMgPSBbXTtcbiAgICAgICAgdGhpcy5wYWdpbmF0b3JGaXJzdCA9IDA7XG4gICAgICAgIHRoaXMuc2V0UmVnaW9uc0Zyb21FZGl0b3IoKTtcbiAgICAgICAgdGhpcy5vblNlbGVjdGlvbkNoYW5nZWQoKTtcbiAgICAgIH0sXG4gICAgfSk7XG4gIH1cblxuICBhZGRQb2x5Z29uKCkge1xuICAgIGNvbnN0IHJlZ2lvbiA9IG5ldyBSZWdpb24oKTtcbiAgICByZWdpb24uYm91bmRzID0gbmV3IFBvbHlnb24oKTtcbiAgICByZWdpb24ubGFiZWwgPSB0aGlzLmFjdGl2ZUNsYXNzID8/ICdSZWdpb24nO1xuICAgIHJlZ2lvbi5ib3VuZHMueXBvaW50cyA9IFswLCAwLCAwXTtcbiAgICByZWdpb24uYm91bmRzLnhwb2ludHMgPSBbMCwgMCwgMF07XG4gICAgcmVnaW9uLmJvdW5kcy5jb29yZGluYXRlcyA9IFtcbiAgICAgIFswLCAwXSxcbiAgICAgIFswLCAwXSxcbiAgICAgIFswLCAwXSxcbiAgICBdO1xuICAgIHJlZ2lvbi5ib3VuZHMubnBvaW50cyA9IDM7XG4gICAgdGhpcy5yZWdpb25zID0gWy4uLnRoaXMucmVnaW9ucywgcmVnaW9uXTtcbiAgICB0aGlzLnNldFJlZ2lvbnNGcm9tRWRpdG9yKCk7XG4gICAgdGhpcy5yZWdpb25zQ29weSA9IHRoaXMuZGVlcENsb25lKHRoaXMucmVnaW9ucyk7XG4gIH1cbiAgZGVsZXRlUmVnaW9uKHNoYXBlSWR4OiBudW1iZXIpIHtcbiAgICBjb25zdCByZW1vdmVkID0gdGhpcy5yZWdpb25zW3NoYXBlSWR4XTtcbiAgICB0aGlzLnJlZ2lvbnMgPSB0aGlzLnJlZ2lvbnMuZmlsdGVyKChfLCBpKSA9PiBpICE9PSBzaGFwZUlkeCk7XG4gICAgaWYgKHJlbW92ZWQpIHtcbiAgICAgIHRoaXMuc2VsZWN0ZWRSZWdpb25zID0gdGhpcy5zZWxlY3RlZFJlZ2lvbnMuZmlsdGVyKChyKSA9PiByLmlkICE9PSByZW1vdmVkLmlkKTtcbiAgICB9XG4gICAgdGhpcy5jbGFtcFBhZ2luYXRvckZpcnN0KCk7XG4gICAgdGhpcy5zZXRSZWdpb25zRnJvbUVkaXRvcigpO1xuICAgIC8vIFJlLWVtaXQgdGhlIChwb3NzaWJseSB0cmltbWVkKSBzZWxlY3Rpb24gc28gdGhlIHBsb3QncyBoaWdobGlnaHQgc3RheXNcbiAgICAvLyBpbiBzeW5jIHdpdGggd2hhdCdzIHN0aWxsIGluIHRoZSB0YWJsZS5cbiAgICB0aGlzLm9uU2VsZWN0aW9uQ2hhbmdlZCgpO1xuICB9XG5cbiAgcHJpdmF0ZSBjbGFtcFBhZ2luYXRvckZpcnN0KCkge1xuICAgIGNvbnN0IG1heEZpcnN0ID0gTWF0aC5tYXgoXG4gICAgICAwLFxuICAgICAgTWF0aC5mbG9vcigodGhpcy5yZWdpb25zLmxlbmd0aCAtIDEpIC8gdGhpcy5wYWdpbmF0b3JSb3dzKSAqIHRoaXMucGFnaW5hdG9yUm93cyxcbiAgICApO1xuICAgIGlmICh0aGlzLnBhZ2luYXRvckZpcnN0ID4gbWF4Rmlyc3QpIHtcbiAgICAgIHRoaXMucGFnaW5hdG9yRmlyc3QgPSBtYXhGaXJzdDtcbiAgICB9XG4gIH1cbiAgY2hhbmdlU2hvd1NoYXBlTGFiZWwoc2hvd0xhYmVsOiBib29sZWFuKSB7XG4gICAgdGhpcy5zZXRSZWdpb25zRnJvbUVkaXRvcigpO1xuICB9XG5cbiAgaXNFZGl0aW5nTGFiZWwocmVnaW9uOiBhbnkpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5lZGl0aW5nTGFiZWxSZWdpb25zLmhhcyhyZWdpb24pO1xuICB9XG5cbiAgc3RhcnRFZGl0TGFiZWwocmVnaW9uOiBhbnksIGV2ZW50PzogRXZlbnQpOiB2b2lkIHtcbiAgICBldmVudD8uc3RvcFByb3BhZ2F0aW9uKCk7IC8vIGRvbid0IHRvZ2dsZSByb3cgc2VsZWN0aW9uXG4gICAgdGhpcy5lZGl0aW5nTGFiZWxSZWdpb25zLmFkZChyZWdpb24pO1xuICB9XG5cbiAgc3RvcEVkaXRMYWJlbChyZWdpb246IGFueSwgY29tbWl0OiBib29sZWFuLCBldmVudD86IEV2ZW50KTogdm9pZCB7XG4gICAgZXZlbnQ/LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgIHRoaXMuZWRpdGluZ0xhYmVsUmVnaW9ucy5kZWxldGUocmVnaW9uKTtcbiAgICBpZiAoY29tbWl0KSB7XG4gICAgICB0aGlzLmxhYmVsUmVnaW9uVXBkYXRlKHJlZ2lvbiwgdHJ1ZSk7XG4gICAgfVxuICB9XG5cbiAgaXNSZWN0YW5nbGUocmVnaW9uOiBSZWdpb24pIHtcbiAgICByZXR1cm4gcmVnaW9uLmJvdW5kcyBpbnN0YW5jZW9mIFJlY3RhbmdsZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWdpb24gYXJlYSBmb3IgZGlzcGxheTogaW4gwrVtwrIgKG9yIG1twrIpIHdoZW4gdGhlIGltYWdlIHJlcG9ydHMgYSBwaHlzaWNhbFxuICAgKiBwaXhlbCBzaXplLCBvdGhlcndpc2UgaW4gcHjCsi4gRW1wdHkgc3RyaW5nIGZvciBkZWdlbmVyYXRlIHJlZ2lvbnMuXG4gICAqL1xuICByZWdpb25BcmVhKHJlZ2lvbjogUmVnaW9uKTogc3RyaW5nIHtcbiAgICBjb25zdCBweCA9IHRoaXMuYXJlYUluUGl4ZWxzKHJlZ2lvbik7XG4gICAgaWYgKHB4IDw9IDApIHJldHVybiAnJztcbiAgICBpZiAodGhpcy5tcHBYICYmIHRoaXMubXBwWSAmJiB0aGlzLm1wcFggPiAwICYmIHRoaXMubXBwWSA+IDApIHtcbiAgICAgIGNvbnN0IHVtMiA9IHB4ICogdGhpcy5tcHBYICogdGhpcy5tcHBZO1xuICAgICAgcmV0dXJuIHVtMiA+PSAxZTYgPyBgJHt0aGlzLmZtdEFyZWEodW0yIC8gMWU2KX0gbW3CsmAgOiBgJHt0aGlzLmZtdEFyZWEodW0yKX0gwrVtwrJgO1xuICAgIH1cbiAgICByZXR1cm4gYCR7dGhpcy5mbXRBcmVhKHB4KX0gcHjCsmA7XG4gIH1cblxuICAvKipcbiAgICogQ2hvb3NlIHRoZSBwaHlzaWNhbCBwaXhlbCBzaXplICjCtW0vcGl4ZWwpIGZvciBhcmVhIGRpc3BsYXkgZnJvbSB0aGUgaW1hZ2VcbiAgICogbWV0YS4gVGhlIGNhbGlicmF0aW9uIG1heSBzaXQgb24gYW55IGNoYW5uZWwgZW50cnkg4oCUIG5vdCBuZWNlc3NhcmlseSBbMF0g4oCUXG4gICAqIHNvIHBpY2sgdGhlIGZpcnN0IGVudHJ5IHdpdGggYSBwb3NpdGl2ZSBtcHBYIChtaXJyb3JzXG4gICAqIFBsb3RseVNlcnZpY2UuY3VycmVudE1wcCkuIEZhbGxzIGJhY2sgdG8gc3F1YXJlIHBpeGVscyAobXBwWSA9IG1wcFgpIHdoZW5cbiAgICogb25seSBvbmUgYXhpcyBpcyByZXBvcnRlZCwgc28gYSBzY2FsZWQgaW1hZ2Ugc2hvd3MgwrVtwrIvbW3CsiByYXRoZXIgdGhhbiBweMKyLlxuICAgKi9cbiAgcHJpdmF0ZSBwaWNrTXBwKG1ldGE6IElJbWFnZU1ldGFkYXRhW10gfCB1bmRlZmluZWQpOiB7IG1wcFg/OiBudW1iZXI7IG1wcFk/OiBudW1iZXIgfSB7XG4gICAgY29uc3QgbSA9IEFycmF5LmlzQXJyYXkobWV0YSlcbiAgICAgID8gKG1ldGEuZmluZCgoZSkgPT4gZSAmJiAoZS5tcHBYID8/IDApID4gMCkgPz8gbWV0YVswXSlcbiAgICAgIDogdW5kZWZpbmVkO1xuICAgIGNvbnN0IG14ID0gbSAmJiAobS5tcHBYID8/IDApID4gMCA/IChtLm1wcFggYXMgbnVtYmVyKSA6IHVuZGVmaW5lZDtcbiAgICBjb25zdCBteSA9IG0gJiYgKG0ubXBwWSA/PyAwKSA+IDAgPyAobS5tcHBZIGFzIG51bWJlcikgOiB1bmRlZmluZWQ7XG4gICAgcmV0dXJuIHsgbXBwWDogbXgsIG1wcFk6IG15ID8/IG14IH07XG4gIH1cblxuICAvKiogUGl4ZWwgYXJlYTogd2lkdGjCt2hlaWdodCBmb3IgcmVjdGFuZ2xlcywgc2hvZWxhY2UgZm9ybXVsYSBmb3IgcG9seWdvbnMuXG4gICAqICBJbnRlcmlvciByaW5ncyAoaG9sZXMpIGFyZSBzdWJ0cmFjdGVkLCBzbyBhIGRvbnV0IHJlcG9ydHMgdGhlIGFubnVsdXMgYXJlYSxcbiAgICogIG5vdCB0aGUgZmlsbGVkIGV4dGVyaW9yIChqaXQtdWkjODUpLiAqL1xuICBwcml2YXRlIGFyZWFJblBpeGVscyhyZWdpb246IFJlZ2lvbik6IG51bWJlciB7XG4gICAgY29uc3QgYiA9IHJlZ2lvbi5ib3VuZHM7XG4gICAgaWYgKGIgaW5zdGFuY2VvZiBSZWN0YW5nbGUpIHJldHVybiBNYXRoLmFicyhiLndpZHRoICogYi5oZWlnaHQpO1xuICAgIGlmIChiIGluc3RhbmNlb2YgUG9seWdvbikgcmV0dXJuIHRoaXMucG9seWdvbkFyZWEoYik7XG4gICAgLy8gTXVsdGktcGFydCByZWdpb246IHN1bSBlYWNoIHBhcnQncyAoZXh0ZXJpb3Ig4oiSIGhvbGVzKSBhcmVhIChqaXQtdWkjODUpLlxuICAgIGlmIChiIGluc3RhbmNlb2YgTXVsdGlQb2x5Z29uKSB7XG4gICAgICByZXR1cm4gYi5wb2x5Z29ucy5yZWR1Y2UoKHN1bSwgcCkgPT4gc3VtICsgdGhpcy5wb2x5Z29uQXJlYShwKSwgMCk7XG4gICAgfVxuICAgIHJldHVybiAwO1xuICB9XG5cbiAgLyoqIFBvbHlnb24gYXJlYTogZXh0ZXJpb3Igc2hvZWxhY2UgbWludXMgZWFjaCBpbnRlcmlvciByaW5nIChob2xlKS4gKi9cbiAgcHJpdmF0ZSBwb2x5Z29uQXJlYShwOiBQb2x5Z29uKTogbnVtYmVyIHtcbiAgICBpZiAoKHAueHBvaW50cz8ubGVuZ3RoID8/IDApIDwgMykgcmV0dXJuIDA7XG4gICAgbGV0IGEgPSB0aGlzLnJpbmdBcmVhKHAueHBvaW50cywgcC55cG9pbnRzKTtcbiAgICBpZiAocC5ob2xlcykge1xuICAgICAgZm9yIChjb25zdCByaW5nIG9mIHAuaG9sZXMpIGEgLT0gdGhpcy5yaW5nQXJlYShyaW5nLm1hcChwdCA9PiBwdFswXSksIHJpbmcubWFwKHB0ID0+IHB0WzFdKSk7XG4gICAgfVxuICAgIHJldHVybiBNYXRoLm1heCgwLCBhKTtcbiAgfVxuXG4gIC8qKiBBYnNvbHV0ZSBzaG9lbGFjZSBhcmVhIG9mIGEgc2luZ2xlIHJpbmcuICovXG4gIHByaXZhdGUgcmluZ0FyZWEoeHM6IG51bWJlcltdLCB5czogbnVtYmVyW10pOiBudW1iZXIge1xuICAgIGNvbnN0IG4gPSB4cz8ubGVuZ3RoID8/IDA7XG4gICAgaWYgKG4gPCAzKSByZXR1cm4gMDtcbiAgICBsZXQgYSA9IDA7XG4gICAgZm9yIChsZXQgaSA9IDAsIGogPSBuIC0gMTsgaSA8IG47IGogPSBpKyspIGEgKz0gKHhzW2pdICsgeHNbaV0pICogKHlzW2pdIC0geXNbaV0pO1xuICAgIHJldHVybiBNYXRoLmFicyhhIC8gMik7XG4gIH1cblxuICBwcml2YXRlIGZtdEFyZWEobjogbnVtYmVyKTogc3RyaW5nIHtcbiAgICByZXR1cm4gKG4gPj0gMTAwMCA/IE1hdGgucm91bmQobikgOiBNYXRoLnJvdW5kKG4gKiAxMDApIC8gMTAwKS50b0xvY2FsZVN0cmluZygpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoaXMgbWV0aG9kIHJvdW5kcyBhbGwgdGhlIHJlY3RhbmdsZSByZWdpb25zIGxlbmd0aHMgdG8gbXVsdGlwbGUgb2YgNTEyXG4gICAqL1xuICByb3VuZFJlY3RhbmdsZUxlbmd0aHMoKSB7XG4gICAgZm9yIChjb25zdCByZWdpb24gb2YgdGhpcy5yZWdpb25zKSB7XG4gICAgICBpZiAocmVnaW9uLmJvdW5kcyBpbnN0YW5jZW9mIFJlY3RhbmdsZSkge1xuICAgICAgICBjb25zdCByZWN0ID0gcmVnaW9uLmJvdW5kcztcbiAgICAgICAgcmVjdC5oZWlnaHQgPSBNYXRoLnJvdW5kKHJlY3QuaGVpZ2h0IC8gNTEyKSAqIDUxMjtcbiAgICAgICAgcmVjdC53aWR0aCA9IE1hdGgucm91bmQocmVjdC53aWR0aCAvIDUxMikgKiA1MTI7XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMuc2V0UmVnaW9uc0Zyb21FZGl0b3IoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdG9wIGtleSBhcnJvdyBldmVudCBwcm9wYWdhdGlvblxuICAgKiBAcGFyYW0gZXZlbnRcbiAgICovXG4gIGRpc2FibGVBcnJvd0tleXMoZXZlbnQ6IGFueSkge1xuICAgIGlmIChcbiAgICAgIGV2ZW50LmtleSA9PT0gJ0Fycm93RG93bicgfHxcbiAgICAgIGV2ZW50LmtleSA9PT0gJ0Rvd24nIHx8XG4gICAgICBldmVudC5rZXkgPT09ICdBcnJvd1VwJyB8fFxuICAgICAgZXZlbnQua2V5ID09PSAnVXAnIHx8XG4gICAgICBldmVudC5rZXkgPT09ICdBcnJvd0xlZnQnIHx8XG4gICAgICBldmVudC5rZXkgPT09ICdMZWZ0JyB8fFxuICAgICAgZXZlbnQua2V5ID09PSAnQXJyb3dSaWdodCcgfHxcbiAgICAgIGV2ZW50LmtleSA9PT0gJ1JpZ2h0J1xuICAgICkge1xuICAgICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgfVxuICB9XG5cbiAgY2hhbmdlU2hhcGVDb2xvcihldmVudDogYW55KSB7XG4gICAgdGhpcy5zaGFwZUNvbG9yID0gZXZlbnQudmFsdWU7XG4gICAgZm9yIChjb25zdCByZWdpb24gb2YgdGhpcy5yZWdpb25zKSB7XG4gICAgICBpZiAocmVnaW9uLmxhYmVsID09PSB0aGlzLnNlbGVjdGVkTGFiZWxDb2xvciAmJiB0aGlzLmxhYmVsQ29sb3JzLmhhcyhyZWdpb24ubGFiZWwpKSB7XG4gICAgICAgIHRoaXMubGFiZWxDb2xvcnMuc2V0KHJlZ2lvbi5sYWJlbCwgdGhpcy5zaGFwZUNvbG9yKTtcbiAgICAgICAgcmVnaW9uLmNvbG9yID0gdGhpcy5zaGFwZUNvbG9yO1xuICAgICAgfVxuICAgIH1cbiAgICB0aGlzLnNldFJlZ2lvbnNGcm9tRWRpdG9yKHRoaXMuZmlsbENvbG9yKTtcbiAgfVxuXG4gIHNob3dIZWxwKCkge1xuICAgIHRoaXMuZGlzcGxheUhlbHBEaWFsb2cgPSB0cnVlO1xuICB9XG5cbiAgLyoqIE9wZW4gdGhlIGNvbG91ciBkaWFsb2cgZm9yIHRoZSBjdXJyZW50bHktc2VsZWN0ZWQgcmVnaW9uKHMpLCBidWlsZGluZyBvbmVcbiAgICogIGNvbG91ciBwaWNrZXIgcGVyIHVuaXF1ZSBjbGFzcyBpbiB0aGUgc2VsZWN0aW9uLiBFYWNoIHBpY2tlciBpcyBzZWVkZWQgd2l0aFxuICAgKiAgdGhlIGNsYXNzJ3MgY3VycmVudCBjb2xvdXIgKHRoZSBsYWJlbOKGkmNvbG91ciBtYXAsIGZhbGxpbmcgYmFjayB0byB0aGUgZmlyc3RcbiAgICogIHNlbGVjdGVkIHJlZ2lvbiBvZiB0aGF0IGNsYXNzKS4gKi9cbiAgb3BlbkNvbG9yRGlhbG9nKCkge1xuICAgIGlmICghdGhpcy5zZWxlY3RlZFJlZ2lvbnM/Lmxlbmd0aCkgcmV0dXJuO1xuICAgIGNvbnN0IHNlZWRCeUxhYmVsID0gbmV3IE1hcDxzdHJpbmcsIHN0cmluZz4oKTtcbiAgICBmb3IgKGNvbnN0IHJlZ2lvbiBvZiB0aGlzLnNlbGVjdGVkUmVnaW9ucykge1xuICAgICAgY29uc3QgbGFiZWwgPSByZWdpb24ubGFiZWw/LnRyaW0oKSA/PyAnJztcbiAgICAgIGlmIChzZWVkQnlMYWJlbC5oYXMobGFiZWwpKSBjb250aW51ZTtcbiAgICAgIHNlZWRCeUxhYmVsLnNldChcbiAgICAgICAgbGFiZWwsXG4gICAgICAgIChsYWJlbCA/IHRoaXMubGFiZWxDb2xvcnMuZ2V0KGxhYmVsKSA6IHVuZGVmaW5lZCkgPz8gcmVnaW9uLmNvbG9yID8/IHRoaXMuc2hhcGVDb2xvcixcbiAgICAgICk7XG4gICAgfVxuICAgIHRoaXMuY2xhc3NDb2xvckVkaXRzID0gWy4uLnNlZWRCeUxhYmVsXS5tYXAoKFtsYWJlbCwgY29sb3JdKSA9PiAoeyBsYWJlbCwgY29sb3IgfSkpO1xuICAgIHRoaXMuc2hvd0NvbG9yRGlhbG9nID0gdHJ1ZTtcbiAgfVxuXG4gIC8qKiBBcHBseSBlYWNoIGNsYXNzJ3MgY2hvc2VuIGNvbG91ciB0byB0aGUgc2VsZWN0ZWQgcmVnaW9ucyBvZiB0aGF0IGNsYXNzIGFuZFxuICAgKiAgY29tbWl0IGxpdmUuIFJlbWVtYmVycyB0aGUgY29sb3VyIHBlciBjbGFzcyBzbyBzYW1lLWNsYXNzIHJlZ2lvbnMgYWRkZWRcbiAgICogIGxhdGVyIG1hdGNoLiAqL1xuICBhcHBseUNvbG9yVG9TZWxlY3RlZCgpIHtcbiAgICBjb25zdCBjb2xvckJ5TGFiZWwgPSBuZXcgTWFwKHRoaXMuY2xhc3NDb2xvckVkaXRzLm1hcCgoZSkgPT4gW2UubGFiZWwsIGUuY29sb3JdKSk7XG4gICAgZm9yIChjb25zdCByZWdpb24gb2YgdGhpcy5zZWxlY3RlZFJlZ2lvbnMgPz8gW10pIHtcbiAgICAgIGNvbnN0IGxhYmVsID0gcmVnaW9uLmxhYmVsPy50cmltKCkgPz8gJyc7XG4gICAgICBjb25zdCBjb2xvciA9IGNvbG9yQnlMYWJlbC5nZXQobGFiZWwpO1xuICAgICAgaWYgKCFjb2xvcikgY29udGludWU7XG4gICAgICByZWdpb24uY29sb3IgPSBjb2xvcjtcbiAgICAgIHJlZ2lvbi5jb2xvck92ZXJyaWRkZW4gPSB0cnVlOyAvLyBleHBsaWNpdCBjb2xvdXIg4oCUIHByZXNlcnZlIGFnYWluc3QgcHJlc2V0IChyZSlhcHBseSAoaml0LXVpIzcwKVxuICAgICAgaWYgKGxhYmVsKSB0aGlzLmxhYmVsQ29sb3JzLnNldChsYWJlbCwgY29sb3IpO1xuICAgIH1cbiAgICB0aGlzLnNldFJlZ2lvbnNGcm9tRWRpdG9yKCk7XG4gICAgdGhpcy5zaG93Q29sb3JEaWFsb2cgPSBmYWxzZTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBhbm5vdGF0aW9uLWNsYXNzIHByZXNldHMgKGppdC11aSM3MCkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLyoqIFJlc29sdmUgdGhlIGRpc3BsYXkgY29sb3VyIGZvciBhIGNsYXNzIG5hbWUgKHByZXNldCBjb2xvdXIgb3IgZGV0ZXJtaW5pc3RpYyBmYWxsYmFjaykuICovXG4gIGNvbG9yRm9yTmFtZShuYW1lPzogc3RyaW5nKTogc3RyaW5nIHtcbiAgICByZXR1cm4gbmFtZSA/IGNvbG9yRm9yTGFiZWwobmFtZSwgdGhpcy5wcmVzZXRTZXQpIDogdGhpcy5zaGFwZUNvbG9yO1xuICB9XG5cbiAgLyoqXG4gICAqIEVuc3VyZSBldmVyeSBjbGFzcyBsYWJlbCBwcmVzZW50IGluIHRoZSBsb2FkZWQgcmVnaW9ucyBleGlzdHMgaW4gdGhlIHByZXNldFxuICAgKiBsaXN0LCBhdXRvLWFkZGluZyBhbnkgdGhhdCBhcmUgbWlzc2luZyAodXNpbmcgdGhlIHJlZ2lvbidzIGFscmVhZHktcmVzb2x2ZWRcbiAgICogY29sb3VyKS4gVGhpcyBtYWtlcyBjbGFzc2VzIGNhcnJpZWQgaW4gYSBsb2FkZWQvaW1wb3J0ZWQgR2VvSlNPTiBzaG93IHVwIGFzXG4gICAqIGNoaXBzICsgQ2xhc3MtZHJvcGRvd24gb3B0aW9ucyAoYSBwLWRyb3Bkb3duIGNhbid0IGRpc3BsYXkgYSB2YWx1ZSB0aGF0IGlzbid0XG4gICAqIGFuIG9wdGlvbikgYW5kIGxldHMgdGhlIHVzZXIgbWFuYWdlIHRoZW0uIFRoZSBwbGFjZWhvbGRlciAnbGVnZW5kJyBhbmQgZW1wdHlcbiAgICogbGFiZWxzIGFyZSBpZ25vcmVkLiAoaml0LXVpIzcwKVxuICAgKi9cbiAgcHJpdmF0ZSBzeW5jQ2xhc3Nlc0Zyb21SZWdpb25zKHJlZ2lvbnM6IFJlZ2lvbltdKTogdm9pZCB7XG4gICAgY29uc3Qgbm9ybWFsaXplZCA9IHRoaXMucHJlc2V0U2V0Lm1hdGNoTW9kZSA9PT0gJ25vcm1hbGl6ZWQnO1xuICAgIGNvbnN0IGtleU9mID0gKGw6IHN0cmluZykgPT4gKG5vcm1hbGl6ZWQgPyBsLnRyaW0oKS50b0xvd2VyQ2FzZSgpIDogbCk7XG4gICAgY29uc3Qga25vd24gPSBuZXcgU2V0KHRoaXMucHJlc2V0U2V0LmNsYXNzZXMubWFwKChjKSA9PiBrZXlPZihjLm5hbWUpKSk7XG4gICAgY29uc3QgYWRkZWQgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgICBmb3IgKGNvbnN0IHIgb2YgcmVnaW9ucykge1xuICAgICAgY29uc3QgbGFiZWwgPSByLmxhYmVsPy50cmltKCk7XG4gICAgICBpZiAoIWxhYmVsIHx8IGxhYmVsID09PSAnbGVnZW5kJykgY29udGludWU7XG4gICAgICBjb25zdCBrID0ga2V5T2YobGFiZWwpO1xuICAgICAgaWYgKGtub3duLmhhcyhrKSB8fCBhZGRlZC5oYXMoaykpIGNvbnRpbnVlO1xuICAgICAgYWRkZWQuYWRkKGspO1xuICAgICAgdGhpcy5yZWdpb25BcGkudXBzZXJ0Q2xhc3Moe1xuICAgICAgICBuYW1lOiBsYWJlbCxcbiAgICAgICAgY29sb3I6IHIuY29sb3IgfHwgY29sb3JGb3JMYWJlbChsYWJlbCwgdGhpcy5wcmVzZXRTZXQpLFxuICAgICAgICBzb3VyY2U6ICdhdXRvJyxcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIC8vIOKUgOKUgCBEb2NrZWQgQ2xhc3NlcyBwYW5lbCAoaml0LXVpIzcwKSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICAvKiogUmVidWlsZCB0aGUgcGVyLWNsYXNzIHJlZ2lvbiBjb3VudHMgc2hvd24gaW4gdGhlIHBhbmVsLiAqL1xuICBwcml2YXRlIHJlY29tcHV0ZUNsYXNzQ291bnRzKCk6IHZvaWQge1xuICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSB0aGlzLnByZXNldFNldC5tYXRjaE1vZGUgPT09ICdub3JtYWxpemVkJztcbiAgICBjb25zdCBjb3VudHMgPSBuZXcgTWFwPHN0cmluZywgbnVtYmVyPigpO1xuICAgIGZvciAoY29uc3QgciBvZiB0aGlzLnJlZ2lvbnMpIHtcbiAgICAgIGlmICghci5sYWJlbCkgY29udGludWU7XG4gICAgICBjb25zdCBrID0gbm9ybWFsaXplZCA/IHIubGFiZWwudHJpbSgpLnRvTG93ZXJDYXNlKCkgOiByLmxhYmVsO1xuICAgICAgY291bnRzLnNldChrLCAoY291bnRzLmdldChrKSA/PyAwKSArIDEpO1xuICAgIH1cbiAgICB0aGlzLmNsYXNzQ291bnRzID0gY291bnRzO1xuICAgIHRoaXMudXBkYXRlRGlzcGxheUNsYXNzZXMoKTtcbiAgfVxuXG4gIC8qKiBSZWJ1aWxkIHRoZSBwYW5lbCdzIGRpc3BsYXkgb3JkZXIg4oCUIG1vc3QtdXNlZCBjbGFzc2VzIGZpcnN0LCB0aGVuIGJ5IG5hbWUuICovXG4gIHByaXZhdGUgdXBkYXRlRGlzcGxheUNsYXNzZXMoKTogdm9pZCB7XG4gICAgdGhpcy5kaXNwbGF5Q2xhc3NlcyA9IFsuLi50aGlzLnByZXNldFNldC5jbGFzc2VzXS5zb3J0KChhLCBiKSA9PiB7XG4gICAgICBjb25zdCBkID0gdGhpcy5jbGFzc0NvdW50KGIubmFtZSkgLSB0aGlzLmNsYXNzQ291bnQoYS5uYW1lKTtcbiAgICAgIHJldHVybiBkICE9PSAwID8gZCA6IGEubmFtZS5sb2NhbGVDb21wYXJlKGIubmFtZSk7XG4gICAgfSk7XG4gIH1cblxuICAvKiogdHJhY2tCeSBmb3IgdGhlIGNsYXNzIHJvd3Mgc28gcmUtc29ydGluZyBkb2Vzbid0IHJlLWNyZWF0ZSB0aGUgcGlja2Vycy4gKi9cbiAgdHJhY2tCeUNsYXNzTmFtZSA9IChfOiBudW1iZXIsIGM6IENsYXNzUHJlc2V0KTogc3RyaW5nID0+IGMubmFtZTtcblxuICAvKiogTnVtYmVyIG9mIHJlZ2lvbnMgY3VycmVudGx5IHVzaW5nIGEgY2xhc3MgKGtleWVkIGJ5IHRoZSBhY3RpdmUgbWF0Y2ggbW9kZSkuICovXG4gIGNsYXNzQ291bnQobmFtZTogc3RyaW5nKTogbnVtYmVyIHtcbiAgICBjb25zdCBrID0gdGhpcy5wcmVzZXRTZXQubWF0Y2hNb2RlID09PSAnbm9ybWFsaXplZCcgPyBuYW1lLnRyaW0oKS50b0xvd2VyQ2FzZSgpIDogbmFtZTtcbiAgICByZXR1cm4gdGhpcy5jbGFzc0NvdW50cy5nZXQoaykgPz8gMDtcbiAgfVxuXG4gIC8qKiBSZWNvbG91ciBhIGNsYXNzIGZyb20gaXRzIHBhbmVsIHN3YXRjaCBhbmQgcmVwYWludCBpdHMgKG5vbi1vdmVycmlkZGVuKSByZWdpb25zLiAqL1xuICBzZXRDbGFzc0NvbG9yKG5hbWU6IHN0cmluZywgY29sb3I6IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICghY29sb3IpIHJldHVybjtcbiAgICB0aGlzLnJlZ2lvbkFwaS5zZXRDbGFzc2lmaWNhdGlvbkNvbG9yKG5hbWUsIGNvbG9yKTtcbiAgICB0aGlzLnNldFJlZ2lvbnNGcm9tRWRpdG9yKCk7XG4gIH1cblxuICAvKiogVG9vbHRpcCBmb3IgYSBjbGFzcydzIGRlbGV0ZSBidXR0b24gaW4gdGhlIHBhbmVsLiAqL1xuICBkZWxldGVDbGFzc1Rvb2x0aXAobmFtZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBjb25zdCBpblVzZSA9IHRoaXMuY2xhc3NDb3VudChuYW1lKSA+IDA7XG4gICAgaWYgKG5hbWUgPT09IHRoaXMuZGVmYXVsdENsYXNzTmFtZSAmJiBpblVzZSkgcmV0dXJuICdUaGUgZGVmYXVsdCBjbGFzcyBjYW5ub3QgYmUgcmVtb3ZlZCB3aGlsZSBpbiB1c2UnO1xuICAgIHJldHVybiBpblVzZSA/ICdSZW1vdmUgY2xhc3Mg4oCUIGl0cyByZWdpb25zIHJldmVydCB0byBSZWdpb24nIDogJ1JlbW92ZSBjbGFzcyc7XG4gIH1cblxuICAvKiogUmVtb3ZlIGEgY2xhc3MuIEFueSByZWdpb25zIHN0aWxsIHVzaW5nIGl0IGZhbGwgYmFjayB0byB0aGUgZGVmYXVsdFxuICAgKiAgXCJSZWdpb25cIiBjbGFzcywgc28gdGhleSBhcmVuJ3Qgb3JwaGFuZWQgKGFuZCB0aGUgY2xhc3MgaXNuJ3QgaW1tZWRpYXRlbHlcbiAgICogIHJlLWFkZGVkIGZyb20gaXRzIHJlZ2lvbnMgYnkgc3luY0NsYXNzZXNGcm9tUmVnaW9ucykuICovXG4gIGRlbGV0ZUNsYXNzKG5hbWU6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMucmVhc3NpZ25SZWdpb25zVG9EZWZhdWx0Q2xhc3MoW25hbWVdKTtcbiAgICB0aGlzLnJlZ2lvbkFwaS5yZW1vdmVDbGFzcyhuYW1lKTtcbiAgICBpZiAodGhpcy5hY3RpdmVDbGFzcyA9PT0gbmFtZSkgdGhpcy5hY3RpdmVDbGFzcyA9IG51bGw7XG4gICAgdGhpcy5zZXRSZWdpb25zRnJvbUVkaXRvcigpO1xuICB9XG5cbiAgLyoqIFJlZ2lvbnMgbGFiZWxsZWQgd2l0aCBhbnkgb2YgYHJlbW92ZWROYW1lc2AgcmV2ZXJ0IHRvIHRoZSBkZWZhdWx0XG4gICAqICB7QGxpbmsgZGVmYXVsdENsYXNzTmFtZX0gKFwiUmVnaW9uXCIpOiBpdHMgY2xhc3MgY29sb3VyLCBvdmVycmlkZSBjbGVhcmVkLlxuICAgKiAgU2tpcHMgdGhlIGRlZmF1bHQgaXRzZWxmIHNvIGRlbGV0aW5nIFwiUmVnaW9uXCIgY2FuJ3Qgc2VsZi1yZWFzc2lnbi4gKGppdC11aSM3MCkgKi9cbiAgcHJpdmF0ZSByZWFzc2lnblJlZ2lvbnNUb0RlZmF1bHRDbGFzcyhyZW1vdmVkTmFtZXM6IHN0cmluZ1tdKTogdm9pZCB7XG4gICAgY29uc3Qgbm9ybSA9IHRoaXMucHJlc2V0U2V0Lm1hdGNoTW9kZSA9PT0gJ25vcm1hbGl6ZWQnO1xuICAgIGNvbnN0IGtleU9mID0gKHM6IHN0cmluZykgPT4gKG5vcm0gPyBzLnRyaW0oKS50b0xvd2VyQ2FzZSgpIDogcyk7XG4gICAgY29uc3QgcmVtb3ZlZCA9IG5ldyBTZXQocmVtb3ZlZE5hbWVzLm1hcChrZXlPZikpO1xuICAgIHJlbW92ZWQuZGVsZXRlKGtleU9mKHRoaXMuZGVmYXVsdENsYXNzTmFtZSkpO1xuICAgIGlmICghcmVtb3ZlZC5zaXplKSByZXR1cm47XG4gICAgY29uc3QgY29sb3IgPSBjb2xvckZvckxhYmVsKHRoaXMuZGVmYXVsdENsYXNzTmFtZSwgdGhpcy5wcmVzZXRTZXQpO1xuICAgIGZvciAoY29uc3QgciBvZiB0aGlzLnJlZ2lvbnMpIHtcbiAgICAgIGlmIChyLmxhYmVsICYmIHJlbW92ZWQuaGFzKGtleU9mKHIubGFiZWwpKSkge1xuICAgICAgICByLmxhYmVsID0gdGhpcy5kZWZhdWx0Q2xhc3NOYW1lO1xuICAgICAgICByLmNvbG9yT3ZlcnJpZGRlbiA9IGZhbHNlO1xuICAgICAgICByLmNvbG9yID0gY29sb3I7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqIFN0YW1wIGEgY2xhc3MgKGFuZCBpdHMgcHJlc2V0L2ZhbGxiYWNrIGNvbG91cikgb250byBvbmUgcmVnaW9uIGZyb20gdGhlIENsYXNzXG4gICAqICBkcm9wZG93bjsgY2hvb3NpbmcgYSBjbGFzcyBvcHRzIHRoZSByZWdpb24gYmFjayBpbnRvIHRoZSBwcmVzZXQgY29sb3VyLiAqL1xuICBhcHBseVByZXNldFRvUmVnaW9uKHJlZ2lvbjogUmVnaW9uLCBjbGFzc05hbWU6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IG5hbWUgPSAoY2xhc3NOYW1lID8/ICcnKS50cmltKCk7XG4gICAgcmVnaW9uLmxhYmVsID0gbmFtZTtcbiAgICByZWdpb24uY29sb3JPdmVycmlkZGVuID0gZmFsc2U7XG4gICAgaWYgKG5hbWUpIHJlZ2lvbi5jb2xvciA9IGNvbG9yRm9yTGFiZWwobmFtZSwgdGhpcy5wcmVzZXRTZXQpO1xuICAgIHRoaXMuc2V0UmVnaW9uc0Zyb21FZGl0b3IoKTtcbiAgfVxuXG4gIC8qKiBNYWtlIGEgY2xhc3MgYWN0aXZlICh1c2VkIGZvciBuZXcgcmVnaW9ucykgYW5kLCBpZiByb3dzIGFyZSBzZWxlY3RlZCwgYXBwbHlcbiAgICogIGl0IHRvIHRoZW0gaW4gb25lIGNvbW1pdC4gKi9cbiAgc2VsZWN0QWN0aXZlQ2xhc3MobmFtZTogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5hY3RpdmVDbGFzcyA9IG5hbWU7XG4gICAgdGhpcy5hcHBseUNsYXNzVG9TZWxlY3RlZChuYW1lKTtcbiAgfVxuXG4gIC8qKiBBcHBseSBhIGNsYXNzIChsYWJlbCArIHByZXNldC9mYWxsYmFjayBjb2xvdXIpIHRvIHRoZSBjdXJyZW50IHNlbGVjdGlvbiBhbmQgY29tbWl0LiAqL1xuICBwcml2YXRlIGFwcGx5Q2xhc3NUb1NlbGVjdGVkKG5hbWU6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IHNlbGVjdGVkID0gdGhpcy5zZWxlY3RlZFJlZ2lvbnMgPz8gW107XG4gICAgaWYgKCFzZWxlY3RlZC5sZW5ndGgpIHJldHVybjtcbiAgICBmb3IgKGNvbnN0IHIgb2Ygc2VsZWN0ZWQpIHtcbiAgICAgIHIubGFiZWwgPSBuYW1lO1xuICAgICAgci5jb2xvck92ZXJyaWRkZW4gPSBmYWxzZTtcbiAgICAgIHIuY29sb3IgPSBjb2xvckZvckxhYmVsKG5hbWUsIHRoaXMucHJlc2V0U2V0KTtcbiAgICB9XG4gICAgdGhpcy5zZXRSZWdpb25zRnJvbUVkaXRvcigpO1xuICB9XG5cbiAgLyoqIFwiRWRpdCBjbGFzcyBvbiBzZWxlY3RlZCByb3dzXCIgcG9wdXAg4oCUIGFwcGx5IHRoZSBjaG9zZW4gZXhpc3RpbmcgY2xhc3MuICovXG4gIGFwcGx5QnVsa0NsYXNzKCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5idWxrQ2xhc3MpIHJldHVybjtcbiAgICB0aGlzLmFwcGx5Q2xhc3NUb1NlbGVjdGVkKHRoaXMuYnVsa0NsYXNzKTtcbiAgICB0aGlzLm92ZXJsYXlQYW5lbD8uaGlkZSgpO1xuICB9XG5cbiAgLyoqIFwiRWRpdCBjbGFzcyBvbiBzZWxlY3RlZCByb3dzXCIgcG9wdXAg4oCUIGFkZCBhIG5ldyBjbGFzcyBhbmQgc2V0IGl0IG9uIHRoZSBzZWxlY3Rpb24uICovXG4gIGFkZEFuZEFwcGx5QnVsa0NsYXNzKCk6IHZvaWQge1xuICAgIGNvbnN0IG5hbWUgPSB0aGlzLm5ld0J1bGtDbGFzcy50cmltKCk7XG4gICAgaWYgKCFuYW1lKSByZXR1cm47XG4gICAgY29uc3Qgbm9ybSA9IHRoaXMucHJlc2V0U2V0Lm1hdGNoTW9kZSA9PT0gJ25vcm1hbGl6ZWQnO1xuICAgIGNvbnN0IGV4aXN0cyA9IHRoaXMucHJlc2V0U2V0LmNsYXNzZXMuc29tZSgoYykgPT5cbiAgICAgIG5vcm0gPyBjLm5hbWUudHJpbSgpLnRvTG93ZXJDYXNlKCkgPT09IG5hbWUudG9Mb3dlckNhc2UoKSA6IGMubmFtZSA9PT0gbmFtZSxcbiAgICApO1xuICAgIGlmICghZXhpc3RzKSB7XG4gICAgICB0aGlzLnJlZ2lvbkFwaS51cHNlcnRDbGFzcyh7IG5hbWUsIGNvbG9yOiBjb2xvckZvckxhYmVsKG5hbWUsIHRoaXMucHJlc2V0U2V0KSwgc291cmNlOiAndXNlcicgfSk7XG4gICAgfVxuICAgIHRoaXMuYXBwbHlDbGFzc1RvU2VsZWN0ZWQobmFtZSk7XG4gICAgdGhpcy5idWxrQ2xhc3MgPSBuYW1lO1xuICAgIHRoaXMubmV3QnVsa0NsYXNzID0gJyc7XG4gICAgdGhpcy5vdmVybGF5UGFuZWw/LmhpZGUoKTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBNYW5hZ2UtY2xhc3NlcyBkaWFsb2cg4pSA4pSAXG4gIG9wZW5NYW5hZ2VEaWFsb2coKTogdm9pZCB7XG4gICAgdGhpcy5wcmVzZXREcmFmdCA9IHRoaXMuY2xvbmVQcmVzZXRTZXQodGhpcy5wcmVzZXRTZXQpO1xuICAgIHRoaXMuc2hvd01hbmFnZURpYWxvZyA9IHRydWU7XG4gIH1cbiAgcHJpdmF0ZSBjbG9uZVByZXNldFNldChzOiBQcmVzZXRTZXQpOiBQcmVzZXRTZXQge1xuICAgIHJldHVybiB7XG4gICAgICBjbGFzc2VzOiAocy5jbGFzc2VzID8/IFtdKS5tYXAoKGMpID0+ICh7IC4uLmMgfSkpLFxuICAgICAgZmFsbGJhY2tQYWxldHRlOiBbLi4uKHMuZmFsbGJhY2tQYWxldHRlID8/IFtdKV0sXG4gICAgICBhdXRvUHJvbW90ZTogISFzLmF1dG9Qcm9tb3RlLFxuICAgICAgbWF0Y2hNb2RlOiBzLm1hdGNoTW9kZSA9PT0gJ25vcm1hbGl6ZWQnID8gJ25vcm1hbGl6ZWQnIDogJ2V4YWN0JyxcbiAgICB9O1xuICB9XG4gIGFkZFByZXNldENsYXNzKCk6IHZvaWQge1xuICAgIHRoaXMucHJlc2V0RHJhZnQ/LmNsYXNzZXMucHVzaCh7IG5hbWU6ICcnLCBjb2xvcjogJyM4ODg4ODgnLCBzb3VyY2U6ICd1c2VyJyB9KTtcbiAgfVxuICByZW1vdmVQcmVzZXRDbGFzcyhpOiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLnByZXNldERyYWZ0Py5jbGFzc2VzLnNwbGljZShpLCAxKTtcbiAgfVxuICBhZGRGYWxsYmFja0NvbG9yKCk6IHZvaWQge1xuICAgIHRoaXMucHJlc2V0RHJhZnQ/LmZhbGxiYWNrUGFsZXR0ZS5wdXNoKCcjODg4ODg4Jyk7XG4gIH1cbiAgcmVtb3ZlRmFsbGJhY2tDb2xvcihpOiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLnByZXNldERyYWZ0Py5mYWxsYmFja1BhbGV0dGUuc3BsaWNlKGksIDEpO1xuICB9XG4gIC8qKiBDb21taXQgdGhlIGRyYWZ0OiBkcm9wIGJsYW5rL2R1cGxpY2F0ZSBuYW1lcywgcGVyc2lzdCwgYW5kIHJlY29sb3VyXG4gICAqICBub24tb3ZlcnJpZGRlbiByZWdpb25zIHdpdGggdGhlIHVwZGF0ZWQgcHJlc2V0cy4gKi9cbiAgYXBwbHlNYW5hZ2VEaWFsb2coY2xvc2U6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMucHJlc2V0RHJhZnQpIHJldHVybjtcbiAgICAvLyBEZS1kdXBsaWNhdGUgdXNpbmcgdGhlIGFjdGl2ZSBtYXRjaCBtb2RlJ3Mga2V5IHNvIG5vcm1hbGl6ZWQgbW9kZSBjYW4ndCBrZWVwXG4gICAgLy8gYm90aCBcIlR1bW9yXCIgYW5kIFwidHVtb3JcIiAod2hpY2ggd291bGQgY29sbGlkZSBhdCBydW50aW1lIGluIGZpbmRQcmVzZXQoKSkuXG4gICAgY29uc3Qgbm9ybSA9IHRoaXMucHJlc2V0RHJhZnQubWF0Y2hNb2RlID09PSAnbm9ybWFsaXplZCc7XG4gICAgY29uc3Qgc2VlbiA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICAgIGNvbnN0IGNsYXNzZXM6IENsYXNzUHJlc2V0W10gPSBbXTtcbiAgICBmb3IgKGNvbnN0IGMgb2YgdGhpcy5wcmVzZXREcmFmdC5jbGFzc2VzKSB7XG4gICAgICBjb25zdCBuYW1lID0gKGMubmFtZSA/PyAnJykudHJpbSgpO1xuICAgICAgY29uc3Qga2V5ID0gbm9ybSA/IG5hbWUudG9Mb3dlckNhc2UoKSA6IG5hbWU7XG4gICAgICBpZiAoIW5hbWUgfHwgc2Vlbi5oYXMoa2V5KSkgY29udGludWU7XG4gICAgICBzZWVuLmFkZChrZXkpO1xuICAgICAgY2xhc3Nlcy5wdXNoKHsgLi4uYywgbmFtZSB9KTtcbiAgICB9XG4gICAgdGhpcy5wcmVzZXREcmFmdC5jbGFzc2VzID0gY2xhc3NlcztcbiAgICAvLyBDbGFzc2VzIGRyb3BwZWQgaW4gdGhlIGRpYWxvZzogdGhlaXIgcmVnaW9ucyByZXZlcnQgdG8gdGhlIGRlZmF1bHQgXCJSZWdpb25cIlxuICAgIC8vIGNsYXNzIChlbHNlIHN5bmNDbGFzc2VzRnJvbVJlZ2lvbnMgd291bGQganVzdCByZS1hZGQgdGhlbSkgKGppdC11aSM3MCkuXG4gICAgY29uc3Qga2V5T2YgPSAoczogc3RyaW5nKSA9PiAobm9ybSA/IHMudHJpbSgpLnRvTG93ZXJDYXNlKCkgOiBzKTtcbiAgICBjb25zdCBrZXB0ID0gbmV3IFNldChjbGFzc2VzLm1hcCgoYykgPT4ga2V5T2YoYy5uYW1lKSkpO1xuICAgIGNvbnN0IHJlbW92ZWQgPSB0aGlzLnByZXNldFNldC5jbGFzc2VzXG4gICAgICAubWFwKChjKSA9PiBjLm5hbWUpXG4gICAgICAuZmlsdGVyKChuKSA9PiAha2VwdC5oYXMoa2V5T2YobikpKTtcbiAgICB0aGlzLnJlYXNzaWduUmVnaW9uc1RvRGVmYXVsdENsYXNzKHJlbW92ZWQpO1xuICAgIHRoaXMucmVnaW9uQXBpLnNldFByZXNldFNldCh0aGlzLnByZXNldERyYWZ0KTtcbiAgICB0aGlzLnNldFJlZ2lvbnNGcm9tRWRpdG9yKCk7IC8vIHJlY29sb3VyIGV4aXN0aW5nIChub24tb3ZlcnJpZGRlbikgcmVnaW9ucyBmcm9tIHRoZSBuZXcgcHJlc2V0c1xuICAgIGlmIChjbG9zZSkgdGhpcy5zaG93TWFuYWdlRGlhbG9nID0gZmFsc2U7XG4gIH1cbiAgcmVzZXRQcmVzZXRzVG9EZWZhdWx0cygpOiB2b2lkIHtcbiAgICB0aGlzLnJlZ2lvbkFwaS5yZXNldFByZXNldHMoKTtcbiAgICB0aGlzLnByZXNldERyYWZ0ID0gdGhpcy5jbG9uZVByZXNldFNldCh0aGlzLnJlZ2lvbkFwaS5nZXRQcmVzZXRTZXQoKSk7XG4gICAgdGhpcy5zZXRSZWdpb25zRnJvbUVkaXRvcigpO1xuICB9XG4gIGV4cG9ydFByZXNldHMoKTogdm9pZCB7XG4gICAgY29uc3QganNvbiA9IEpTT04uc3RyaW5naWZ5KHRoaXMucmVnaW9uQXBpLmdldFByZXNldFNldCgpLCBudWxsLCAyKTtcbiAgICBzYXZlQXMobmV3IEJsb2IoW2pzb25dLCB7IHR5cGU6ICdhcHBsaWNhdGlvbi9qc29uJyB9KSwgJ2Fubm90YXRpb24tY2xhc3Nlcy5qc29uJyk7XG4gIH1cbiAgaW1wb3J0UHJlc2V0cyhldmVudDogRXZlbnQpOiB2b2lkIHtcbiAgICBjb25zdCBpbnB1dCA9IGV2ZW50LnRhcmdldCBhcyBIVE1MSW5wdXRFbGVtZW50O1xuICAgIGNvbnN0IGZpbGUgPSBpbnB1dC5maWxlcz8uWzBdO1xuICAgIGlmICghZmlsZSkgcmV0dXJuO1xuICAgIGNvbnN0IHJlYWRlciA9IG5ldyBGaWxlUmVhZGVyKCk7XG4gICAgcmVhZGVyLm9ubG9hZCA9IChlOiBhbnkpID0+IHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHNldCA9IEpTT04ucGFyc2UoZS50YXJnZXQucmVzdWx0KSBhcyBQcmVzZXRTZXQ7XG4gICAgICAgIHRoaXMucmVnaW9uQXBpLnNldFByZXNldFNldChzZXQpO1xuICAgICAgICB0aGlzLnByZXNldERyYWZ0ID0gdGhpcy5jbG9uZVByZXNldFNldCh0aGlzLnJlZ2lvbkFwaS5nZXRQcmVzZXRTZXQoKSk7XG4gICAgICAgIHRoaXMuc2V0UmVnaW9uc0Zyb21FZGl0b3IoKTtcbiAgICAgICAgdGhpcy5tZXNzYWdlU2VydmljZS5hZGQoeyBrZXk6IFZJWl9UT0FTVF9LRVksIHNldmVyaXR5OiAnc3VjY2VzcycsXG4gICAgICAgICAgc3VtbWFyeTogJ0NsYXNzZXMgaW1wb3J0ZWQnLCBkZXRhaWw6ICdBbm5vdGF0aW9uIGNsYXNzZXMgbG9hZGVkLicgfSk7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgdGhpcy5tZXNzYWdlU2VydmljZS5hZGQoeyBrZXk6IFZJWl9UT0FTVF9LRVksIHNldmVyaXR5OiAnZXJyb3InLFxuICAgICAgICAgIHN1bW1hcnk6ICdJbXBvcnQgZmFpbGVkJywgZGV0YWlsOiBgJHtlcnJ9YCB9KTtcbiAgICAgIH1cbiAgICB9O1xuICAgIHJlYWRlci5yZWFkQXNUZXh0KGZpbGUpO1xuICB9XG5cbiAgaW1wb3J0Um9pcyhldmVudDogRXZlbnQpIHtcbiAgICBjb25zdCBpbnB1dCA9IGV2ZW50LnRhcmdldCBhcyBIVE1MSW5wdXRFbGVtZW50O1xuICAgIGNvbnN0IGZpbGUgPSBpbnB1dC5maWxlcz8uWzBdO1xuICAgIGlmICghZmlsZSkgcmV0dXJuO1xuICAgIGNvbnN0IHJlYWRlciA9IG5ldyBGaWxlUmVhZGVyKCk7XG4gICAgcmVhZGVyLm9ubG9hZCA9IChlOiBhbnkpID0+IHtcbiAgICAgIGNvbnN0IGZpbGVDb250ZW50ID0gZS50YXJnZXQucmVzdWx0O1xuICAgICAgdHJ5IHtcbiAgICAgICAgdGhpcy5yZWdpb25zID0gdGhpcy5yZWdpb25BcGkuaW1wb3J0UmVnaW9ucyhmaWxlQ29udGVudCk7XG4gICAgICAgIC8vIHNldCBsYWJlbCBjb2xvcnNcbiAgICAgICAgZm9yIChjb25zdCByZWdpb24gb2YgdGhpcy5yZWdpb25zKSB7XG4gICAgICAgICAgdGhpcy5sYWJlbFJlZ2lvblVwZGF0ZShyZWdpb24sIGZhbHNlKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnN5bmNDbGFzc2VzRnJvbVJlZ2lvbnModGhpcy5yZWdpb25zKTtcbiAgICAgICAgdGhpcy5zZXRSZWdpb25zRnJvbUVkaXRvcigpO1xuICAgICAgfSBjYXRjaCAoZXZlbnQpIHtcbiAgICAgICAgdGhpcy5tZXNzYWdlU2VydmljZS5hZGQoe1xuICAgICAgICAgIHNldmVyaXR5OiAnZXJyb3InLFxuICAgICAgICAgIHN1bW1hcnk6ICdFcnJvciBpbXBvcnRpbmcgdGhlIGZpbGUnLFxuICAgICAgICAgIGRldGFpbDogYCR7ZXZlbnR9YCxcbiAgICAgICAgfSk7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHJlYWRpbmcgdGhlIGZpbGU6ICcgKyBldmVudCk7XG4gICAgICB9XG4gICAgfTtcbiAgICByZWFkZXIucmVhZEFzVGV4dChmaWxlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWdpb25zIHRvIHNlcmlhbGl6ZSBvbiBzYXZlL2V4cG9ydC4gRm9yIGEgc2luZ2xlLWZpbGUgei1zdGFjayB0aGUgc3RvcmVcbiAgICoga2VlcHMgb25seSB0aGUgY3VycmVudCBzbGljZSBsaXZlLCBzbyBwdWxsIEVWRVJZIHNsaWNlJ3MgYW5ub3RhdGlvbnMgKGVhY2hcbiAgICogdGFnZ2VkIHdpdGggaXRzIHplcm8tYmFzZWQgUmVnaW9uLnopIHRvIHdyaXRlIG9uZSBjb21iaW5lZCB6LWluZGV4ZWRcbiAgICogZ2VvanNvbiAoaml0LXVpIzkzKS4gT3RoZXJ3aXNlIChzaW5nbGUtcGxhbmUgaW1hZ2UsIG9yIGEgZm9sZGVyIHN0YWNrIHdob3NlXG4gICAqIHNsaWNlcyBzYXZlIHRvIHRoZWlyIG93biBwZXItc2xpY2UgZmlsZXMpIHRoZSBjdXJyZW50IHNldC4gKGppdC11aSM5MylcbiAgICovXG4gIHByaXZhdGUgcmVnaW9uc0ZvclNhdmUoKTogUmVnaW9uW10ge1xuICAgIGlmICh0aGlzLnJlZ2lvbkFwaS5pc1N0YWNrTW9kZSgpICYmIHRoaXMucmVnaW9uQXBpLmdldFN0YWNrU2F2ZUxheW91dCgpID09PSAnY29tYmluZWQnKSB7XG4gICAgICByZXR1cm4gdGhpcy5yZWdpb25BcGkuZ2V0U2xpY2VBbm5vdGF0aW9uUmVnaW9ucygpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5yZWdpb25zO1xuICB9XG5cbiAgZXhwb3J0Um9pcygpIHtcbiAgICBpZiAoIXRoaXMucmVnaW9uc0ZvclNhdmUoKS5sZW5ndGgpIHJldHVybjtcbiAgICBjb25zdCBuYW1lID0gdGhpcy5yZWdpb25Jby5nZXRTZWxlY3RlZEZpbGVOYW1lKCk7XG4gICAgY29uc3Qgc3RlbSA9IG5hbWUgPyBuYW1lLnN1YnN0cmluZygwLCBuYW1lLmxhc3RJbmRleE9mKCcuJykpIDogJ3JvaXMnO1xuICAgIHRoaXMuZXhwb3J0RmlsZW5hbWUgPSBgJHtzdGVtfS5nZW9qc29uYDtcbiAgICB0aGlzLnNob3dFeHBvcnREaWFsb2cgPSB0cnVlO1xuICB9XG5cbiAgY29uZmlybUV4cG9ydCgpIHtcbiAgICBjb25zdCBmaWxlbmFtZSA9IHRoaXMuZXhwb3J0RmlsZW5hbWUudHJpbSgpO1xuICAgIGNvbnN0IHJlZ2lvbnMgPSB0aGlzLnJlZ2lvbnNGb3JTYXZlKCk7XG4gICAgaWYgKCFmaWxlbmFtZSB8fCAhcmVnaW9ucy5sZW5ndGgpIHJldHVybjtcbiAgICB0aGlzLnNob3dFeHBvcnREaWFsb2cgPSBmYWxzZTtcbiAgICBjb25zdCBqc29uU3RyaW5nID0gdGhpcy5yZWdpb25BcGkuZ2V0R2VvSnNvblN0cmluZyhyZWdpb25zKTtcbiAgICBjb25zdCBibG9iID0gbmV3IEJsb2IoW2pzb25TdHJpbmddLCB7IHR5cGU6ICdhcHBsaWNhdGlvbi9qc29uJyB9KTtcbiAgICBzYXZlQXMoYmxvYiwgZmlsZW5hbWUpO1xuICB9XG5cbiAgLyoqIE9wZW4gdGhlIFwiU2F2ZSBtYXNrXCIgZGlhbG9nLCBzZWVkZWQgd2l0aCBgPGltYWdlLXN0ZW0+X21hc2sucG5nYC4gKi9cbiAgb3BlblNhdmVNYXNrRGlhbG9nKCkge1xuICAgIGlmICghdGhpcy5yZWdpb25zLmxlbmd0aCkgcmV0dXJuO1xuICAgIGNvbnN0IG5hbWUgPSB0aGlzLnJlZ2lvbklvLmdldFNlbGVjdGVkRmlsZU5hbWUoKTtcbiAgICAvLyBTdHJpcCB0aGUgZXh0ZW5zaW9uIG9ubHkgd2hlbiB0aGVyZSBpcyBvbmU7IGFuIGV4dGVuc2lvbi1sZXNzIG5hbWUga2VlcHNcbiAgICAvLyBpdHMgd2hvbGUgc3RlbSAoYSBsZWFkaW5nLWRvdCBkb3RmaWxlIGlzIHRyZWF0ZWQgYXMgaGF2aW5nIG5vIGV4dGVuc2lvbilcbiAgICAvLyBzbyB3ZSBuZXZlciBwcm9kdWNlIGEgYmFyZSBcIl9tYXNrLnBuZ1wiLlxuICAgIGNvbnN0IGRvdCA9IG5hbWUgPyBuYW1lLmxhc3RJbmRleE9mKCcuJykgOiAtMTtcbiAgICBjb25zdCBzdGVtID0gbmFtZSA/IChkb3QgPiAwID8gbmFtZS5zdWJzdHJpbmcoMCwgZG90KSA6IG5hbWUpIDogJ3JlZ2lvbnMnO1xuICAgIHRoaXMuc2F2ZU1hc2tGaWxlbmFtZSA9IGAke3N0ZW19X21hc2sucG5nYDtcbiAgICB0aGlzLm1hc2tNb2RlID0gJ2JpbmFyeSc7XG4gICAgdGhpcy5zaG93U2F2ZU1hc2tEaWFsb2cgPSB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIFJhc3Rlcml6ZSB0aGUgcmVnaW9ucyB0byB0aGUgY2hvc2VuIG1hc2sgdHlwZSBhbmQgZG93bmxvYWQgYXMgYSBQTkcuIFRoZVxuICAgKiBoZWF2eSB3b3JrIChmdWxsLXJlcyByYXN0ZXJpemUgKyBQTkcgZW5jb2RlKSBydW5zIGluIGEgV2ViIFdvcmtlciBzbyB0aGUgVUlcbiAgICogbmV2ZXIgZnJlZXplcyBvbiBsYXJnZS93aG9sZS1zbGlkZSBpbWFnZXMgYW5kIHRoZSBqb2IgY2FuIGJlIGNhbmNlbGxlZFxuICAgKiAoaml0LXVpIzk1KS4gVGhlIGRpYWxvZyBzdGF5cyBvcGVuIHNob3dpbmcgYSBwcm9ncmVzcyBiYXIgdW50aWwgZG9uZS5cbiAgICovXG4gIGNvbmZpcm1TYXZlTWFzaygpIHtcbiAgICBjb25zdCBmaWxlbmFtZSA9IHRoaXMuc2F2ZU1hc2tGaWxlbmFtZS50cmltKCk7XG4gICAgaWYgKCFmaWxlbmFtZSB8fCAhdGhpcy5yZWdpb25zLmxlbmd0aCB8fCB0aGlzLm1hc2tCdXN5KSByZXR1cm47XG5cbiAgICBjb25zdCBzaXplID0gdGhpcy5yZWdpb25BcGkuZ2V0TWFza0ltYWdlU2l6ZSgpO1xuICAgIGlmICghc2l6ZSkge1xuICAgICAgdGhpcy5tYXNrRXJyb3IoJ05vIGltYWdlIHNpemUgaXMgYXZhaWxhYmxlLCBzbyB0aGUgbWFzayBjYW5ub3QgYmUgc2l6ZWQuJyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gV2hvbGUtc2xpZGUgaW1hZ2VzIGV4Y2VlZCB0aGUgYnJvd3NlcidzIHR5cGVkLWFycmF5L21lbW9yeSBsaW1pdHMsIHNvIGNhcFxuICAgIC8vIHRoZSBtYXNrIHRvIGEgc2FmZSBwaXhlbCBidWRnZXQgYW5kIHNjYWxlIHRoZSBnZW9tZXRyeSB0byBtYXRjaC5cbiAgICBjb25zdCBzY2FsZSA9IG1hc2tTY2FsZUZvcihzaXplLndpZHRoLCBzaXplLmhlaWdodCk7XG4gICAgY29uc3Qgd2lkdGggPSBNYXRoLm1heCgxLCBNYXRoLnJvdW5kKHNpemUud2lkdGggKiBzY2FsZSkpO1xuICAgIGNvbnN0IGhlaWdodCA9IE1hdGgubWF4KDEsIE1hdGgucm91bmQoc2l6ZS5oZWlnaHQgKiBzY2FsZSkpO1xuICAgIGlmIChzY2FsZSA8IDEpIHtcbiAgICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHtcbiAgICAgICAga2V5OiBWSVpfVE9BU1RfS0VZLFxuICAgICAgICBzZXZlcml0eTogJ2luZm8nLFxuICAgICAgICBzdW1tYXJ5OiAnTWFzayBkb3duc2NhbGVkJyxcbiAgICAgICAgZGV0YWlsOiBgSW1hZ2UgdG9vIGxhcmdlIGZvciBhIGZ1bGwtcmVzb2x1dGlvbiBtYXNrOyBzYXZpbmcgYXQgJHt3aWR0aH3DlyR7aGVpZ2h0fS5gLFxuICAgICAgfSk7XG4gICAgfVxuICAgIGNvbnN0IHJlZ2lvbnMgPSB0aGlzLnJlZ2lvbnMubWFwKChyKSA9PiBzY2FsZVBhcnRzKHJlZ2lvblRvUGFydHMociksIHNjYWxlKSk7XG5cbiAgICB0aGlzLnBlbmRpbmdNYXNrRmlsZW5hbWUgPSBmaWxlbmFtZTtcbiAgICB0aGlzLm1hc2tCdXN5ID0gdHJ1ZTtcbiAgICB0aGlzLm1hc2tFbmNvZGluZyA9IGZhbHNlO1xuICAgIHRoaXMubWFza1Byb2dyZXNzID0gMDtcblxuICAgIGNvbnN0IHBheWxvYWQgPSB7XG4gICAgICB3aWR0aCwgaGVpZ2h0LFxuICAgICAgb3JpZ2luYWxXaWR0aDogc2l6ZS53aWR0aCwgb3JpZ2luYWxIZWlnaHQ6IHNpemUuaGVpZ2h0LCBzY2FsZSxcbiAgICAgIG1vZGU6IHRoaXMubWFza01vZGUsXG4gICAgICBzb3VyY2VOYW1lOiB0aGlzLnJlZ2lvbklvLmdldFNlbGVjdGVkRmlsZU5hbWUoKSxcbiAgICAgIHJlZ2lvbnMsXG4gICAgfTtcbiAgICAvLyBjcmVhdGVNYXNrV29ya2VyKCkgaXMgYXN5bmMgKHRoZSB3b3JrZXIgbW9kdWxlIGlzIGltcG9ydGVkIGR5bmFtaWNhbGx5IHNvXG4gICAgLy8gaXRzIGltcG9ydC5tZXRhIG5ldmVyIHJlYWNoZXMgdGhlIENvbW1vbkpTIHRlc3QgY29tcGlsZSkuIFdpcmUgdXAgb25jZSBpdFxuICAgIC8vIHJlc29sdmVzIOKAlCB1bmxlc3MgdGhlIHVzZXIgY2FuY2VsbGVkIHdoaWxlIGl0IHdhcyBsb2FkaW5nLlxuICAgIHRoaXMuY3JlYXRlTWFza1dvcmtlcigpLnRoZW4oKHdvcmtlcikgPT4ge1xuICAgICAgaWYgKCF0aGlzLm1hc2tCdXN5KSB7IHdvcmtlci50ZXJtaW5hdGUoKTsgcmV0dXJuOyB9XG4gICAgICB0aGlzLm1hc2tXb3JrZXIgPSB3b3JrZXI7XG4gICAgICB3b3JrZXIub25tZXNzYWdlID0gKHsgZGF0YSB9OiBNZXNzYWdlRXZlbnQpID0+IHtcbiAgICAgICAgc3dpdGNoIChkYXRhPy50eXBlKSB7XG4gICAgICAgICAgY2FzZSAncHJvZ3Jlc3MnOlxuICAgICAgICAgICAgdGhpcy5tYXNrUHJvZ3Jlc3MgPSBkYXRhLnRvdGFsID8gTWF0aC5yb3VuZCgoZGF0YS5kb25lIC8gZGF0YS50b3RhbCkgKiAxMDApIDogMDtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgJ2VuY29kaW5nJzpcbiAgICAgICAgICAgIHRoaXMubWFza0VuY29kaW5nID0gdHJ1ZTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgJ2RvbmUnOlxuICAgICAgICAgICAgdGhpcy5maW5pc2hNYXNrKG5ldyBCbG9iKFtkYXRhLnBuZ10sIHsgdHlwZTogJ2ltYWdlL3BuZycgfSkpO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSAnZXJyb3InOlxuICAgICAgICAgICAgdGhpcy5tYXNrRXJyb3IoZGF0YS5lcnJvciB8fCAnVGhlIG1hc2sgY291bGQgbm90IGJlIGdlbmVyYXRlZC4nKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgd29ya2VyLm9uZXJyb3IgPSAoKSA9PiB0aGlzLm1hc2tFcnJvcignVGhlIG1hc2sgd29ya2VyIGZhaWxlZC4nKTtcbiAgICAgIHdvcmtlci5wb3N0TWVzc2FnZShwYXlsb2FkKTtcbiAgICB9KS5jYXRjaCgoKSA9PiB0aGlzLm1hc2tFcnJvcignVGhlIG1hc2sgd29ya2VyIGZhaWxlZCB0byBzdGFydC4nKSk7XG4gIH1cblxuICAvKiogQ2FuY2VsIGFuIGluLXByb2dyZXNzIG1hc2sgZXhwb3J0OiB0ZXJtaW5hdGUgdGhlIHdvcmtlciBhbmQgcmVzZXQgc3RhdGUuICovXG4gIGNhbmNlbFNhdmVNYXNrKCkge1xuICAgIHRoaXMudGVhcmRvd25NYXNrV29ya2VyKCk7XG4gICAgdGhpcy5tYXNrQnVzeSA9IGZhbHNlO1xuICAgIHRoaXMubWFza0VuY29kaW5nID0gZmFsc2U7XG4gICAgdGhpcy5tYXNrUHJvZ3Jlc3MgPSAwO1xuICB9XG5cbiAgLyoqIFdvcmtlciBmYWN0b3J5IOKAlCBhc3luYyBzbyB0aGUgd29ya2VyIG1vZHVsZSAoYW5kIGl0cyBgaW1wb3J0Lm1ldGEudXJsYCxcbiAgICogIHdoaWNoIHRoZSBDb21tb25KUyB0ZXN0IGNvbXBpbGUgcmVqZWN0cykgaXMgbG9hZGVkIHZpYSBhIGR5bmFtaWMgaW1wb3J0LFxuICAgKiAgZXhhY3RseSBsaWtlIHRoZSBzZWdtZW50YXRpb24gd29ya2VyLiBPdmVycmlkYWJsZSBpbiB0ZXN0cy4gKi9cbiAgcHJvdGVjdGVkIGFzeW5jIGNyZWF0ZU1hc2tXb3JrZXIoKTogUHJvbWlzZTxXb3JrZXI+IHtcbiAgICBjb25zdCB7IGNyZWF0ZU1hc2tXb3JrZXIgfSA9IGF3YWl0IGltcG9ydCgnLi9tYXNrLXdvcmtlcicpO1xuICAgIHJldHVybiBjcmVhdGVNYXNrV29ya2VyKCk7XG4gIH1cblxuICBwcml2YXRlIGZpbmlzaE1hc2soYmxvYjogQmxvYikge1xuICAgIHRoaXMudGVhcmRvd25NYXNrV29ya2VyKCk7XG4gICAgdGhpcy5tYXNrQnVzeSA9IGZhbHNlO1xuICAgIHRoaXMubWFza0VuY29kaW5nID0gZmFsc2U7XG4gICAgdGhpcy5zaG93U2F2ZU1hc2tEaWFsb2cgPSBmYWxzZTtcbiAgICBzYXZlQXMoYmxvYiwgdGhpcy5wZW5kaW5nTWFza0ZpbGVuYW1lKTtcbiAgfVxuXG4gIHByaXZhdGUgbWFza0Vycm9yKGRldGFpbDogc3RyaW5nKSB7XG4gICAgdGhpcy50ZWFyZG93bk1hc2tXb3JrZXIoKTtcbiAgICB0aGlzLm1hc2tCdXN5ID0gZmFsc2U7XG4gICAgdGhpcy5tYXNrRW5jb2RpbmcgPSBmYWxzZTtcbiAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7XG4gICAgICBrZXk6IFZJWl9UT0FTVF9LRVksXG4gICAgICBzZXZlcml0eTogJ2Vycm9yJyxcbiAgICAgIHN1bW1hcnk6ICdDb3VsZCBub3QgY3JlYXRlIG1hc2snLFxuICAgICAgZGV0YWlsLFxuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSB0ZWFyZG93bk1hc2tXb3JrZXIoKSB7XG4gICAgdGhpcy5tYXNrV29ya2VyPy50ZXJtaW5hdGUoKTtcbiAgICB0aGlzLm1hc2tXb3JrZXIgPSB1bmRlZmluZWQ7XG4gIH1cblxuICBwZXJzaXN0UmVnaW9ucygpIHtcbiAgICAvLyBGb2xkZXIgc3RhY2s6IHdyaXRlIGVhY2ggc2xpY2UncyByZWdpb25zIGJhY2sgdG8gaXRzIG93biBzbGljZS1maWxlJ3NcbiAgICAvLyBzaWJsaW5nIGdlb2pzb24g4oCUIG5vIHNpbmdsZS1maWxlbmFtZSBwcm9tcHQgKHRoZSBmaWxlbmFtZXMgYXJlIHRoZSBzbGljZVxuICAgIC8vIGZpbGVzJykuIChqaXQtdWkjOTMpXG4gICAgaWYgKHRoaXMucmVnaW9uQXBpLmlzU3RhY2tNb2RlKCkgJiYgdGhpcy5yZWdpb25BcGkuZ2V0U3RhY2tTYXZlTGF5b3V0KCkgPT09ICdwZXItc2xpY2UtZmlsZScpIHtcbiAgICAgIHRoaXMuc2F2ZVN0YWNrU2xpY2VzKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IG5hbWUgPSB0aGlzLnJlZ2lvbklvLmdldFNlbGVjdGVkRmlsZU5hbWUoKTtcbiAgICBpZiAoIW5hbWUgfHwgIXRoaXMucmVnaW9uc0ZvclNhdmUoKS5sZW5ndGgpIHJldHVybjtcblxuICAgIHRoaXMuc2F2ZUFzRmlsZW5hbWUgPSBuYW1lLnN1YnN0cmluZygwLCBuYW1lLmxhc3RJbmRleE9mKCcuJykpICsgJy5nZW9qc29uJztcbiAgICB0aGlzLnNhdmVBc0ZpbGVFeGlzdHMgPSBmYWxzZTtcbiAgICB0aGlzLnNob3dTYXZlQXNEaWFsb2cgPSB0cnVlO1xuICAgIHRoaXMuX3NhdmVBc0NoZWNrJC5uZXh0KHRoaXMuc2F2ZUFzRmlsZW5hbWUpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNhdmUgYSBmb2xkZXIgc3RhY2sncyByZWdpb25zIGFzIG9uZSBnZW9qc29uIHBlciBzbGljZS1maWxlIChqaXQtdWkjOTMpLlxuICAgKiBHcm91cHMgdGhlIHN0b3JlJ3MgcGVyLXNsaWNlIHJlZ2lvbnMgYnkgc2xpY2UgaW5kZXggYW5kIHNlcmlhbGl6ZXMgZWFjaCBvblxuICAgKiB0aGUgZGVmYXVsdCBwbGFuZSAoej0wKSDigJQgZWFjaCBzbGljZS1maWxlIGlzIGl0c2VsZiBvbmUgcGxhbmUsIGFuZCB0aGVcbiAgICogbG9hZGVyIHJlLWRlcml2ZXMgdGhlIHNsaWNlIGluZGV4IGZyb20gdGhlIGZpbGUncyBwb3NpdGlvbiBpbiB0aGUgc2VyaWVzLlxuICAgKiBTbGljZXMgY2xlYXJlZCBzaW5jZSBsb2FkIGFyZSBpbmNsdWRlZCAoZW1wdHkpIHNvIHRoZWlyIGZpbGUgaXMgb3ZlcndyaXR0ZW4uXG4gICAqL1xuICBwcml2YXRlIHNhdmVTdGFja1NsaWNlcygpIHtcbiAgICBjb25zdCBieVNsaWNlID0gdGhpcy5yZWdpb25BcGkuZ2V0U3RhY2tTYXZlQW5ub3RhdGlvblNsaWNlcygpO1xuICAgIGlmICghYnlTbGljZS5zaXplKSByZXR1cm47XG4gICAgY29uc3Qgc2xpY2VzOiB7IHo6IG51bWJlcjsgZ2VvSnNvblN0cjogc3RyaW5nIH1bXSA9IFtdO1xuICAgIGZvciAoY29uc3QgW3osIHJlZ3NdIG9mIGJ5U2xpY2UpIHtcbiAgICAgIGNvbnN0IGZsYXQgPSByZWdzLm1hcCgocikgPT4gT2JqZWN0LmFzc2lnbihuZXcgUmVnaW9uKCksIHIsIHsgejogMCB9KSk7XG4gICAgICBzbGljZXMucHVzaCh7IHosIGdlb0pzb25TdHI6IHRoaXMucmVnaW9uQXBpLmdldEdlb0pzb25TdHJpbmcoZmxhdCkgfSk7XG4gICAgfVxuICAgIHRoaXMuc2F2ZUFzQnVzeSA9IHRydWU7XG4gICAgdGhpcy5fc2F2ZUFzU3ViID0gdGhpcy5yZWdpb25Jby5zYXZlU2xpY2VHZW9Kc29ucyhzbGljZXMpLnN1YnNjcmliZSh7XG4gICAgICBuZXh0OiAoKSA9PiB7XG4gICAgICAgIHRoaXMuc2F2ZUFzQnVzeSA9IGZhbHNlO1xuICAgICAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7XG4gICAgICAgICAga2V5OiBWSVpfVE9BU1RfS0VZLFxuICAgICAgICAgIHNldmVyaXR5OiAnc3VjY2VzcycsXG4gICAgICAgICAgc3VtbWFyeTogJ1JlZ2lvbnMgc2F2ZWQnLFxuICAgICAgICAgIGRldGFpbDogYFNhdmVkIFJPSXMgZm9yICR7c2xpY2VzLmxlbmd0aH0gc2xpY2Uke3NsaWNlcy5sZW5ndGggPT09IDEgPyAnJyA6ICdzJ31gLFxuICAgICAgICB9KTtcbiAgICAgIH0sXG4gICAgICBlcnJvcjogKGVycikgPT4ge1xuICAgICAgICB0aGlzLnNhdmVBc0J1c3kgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5tZXNzYWdlU2VydmljZS5hZGQoe1xuICAgICAgICAgIGtleTogVklaX1RPQVNUX0tFWSxcbiAgICAgICAgICBzZXZlcml0eTogJ2Vycm9yJyxcbiAgICAgICAgICBzdW1tYXJ5OiAnRXJyb3Igc2F2aW5nIHJlZ2lvbnMnLFxuICAgICAgICAgIGRldGFpbDogYCR7KGVyciBhcyBFcnJvcik/Lm1lc3NhZ2UgPz8gZXJyfWAsXG4gICAgICAgIH0pO1xuICAgICAgfSxcbiAgICB9KTtcbiAgfVxuXG4gIGNoZWNrU2F2ZUFzRmlsZUV4aXN0cygpIHtcbiAgICB0aGlzLl9zYXZlQXNDaGVjayQubmV4dCh0aGlzLnNhdmVBc0ZpbGVuYW1lKTtcbiAgfVxuXG4gIGNvbmZpcm1TYXZlQXMoKSB7XG4gICAgaWYgKCF0aGlzLnJlZ2lvbklvLmdldFNlbGVjdGVkRmlsZU5hbWUoKSkgcmV0dXJuO1xuXG4gICAgY29uc3QgZmlsZW5hbWUgPSB0aGlzLnNhdmVBc0ZpbGVuYW1lLnRyaW0oKTtcbiAgICBpZiAoIWZpbGVuYW1lKSByZXR1cm47XG5cbiAgICBjb25zdCBkb1NhdmUgPSAoKSA9PiB7XG4gICAgICAvLyBLZWVwIHRoZSBkaWFsb2cgb3BlbiBzaG93aW5nIGFuIChpbmRldGVybWluYXRlKSBwcm9ncmVzcyBiYXIgd2hpbGUgdGhlXG4gICAgICAvLyByZWdpb25zIHNlcmlhbGl6ZSBhbmQgdXBsb2FkLCB0aGVuIGNsb3NlIG9uIHN1Y2Nlc3MuIERlZmVyIG9uZSB0aWNrIHNvXG4gICAgICAvLyB0aGUgYmFyIHBhaW50cyBiZWZvcmUgYSBsYXJnZSBzeW5jaHJvbm91cyBHZW9KU09OIHNlcmlhbGl6ZS5cbiAgICAgIHRoaXMuc2F2ZUFzQnVzeSA9IHRydWU7XG4gICAgICB0aGlzLl9zYXZlQXNUaW1lciA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICB0aGlzLl9zYXZlQXNUaW1lciA9IHVuZGVmaW5lZDtcbiAgICAgICAgbGV0IGdlb0pzb25TdHI6IHN0cmluZztcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBnZW9Kc29uU3RyID0gdGhpcy5yZWdpb25BcGkuZ2V0R2VvSnNvblN0cmluZyh0aGlzLnJlZ2lvbnNGb3JTYXZlKCkpO1xuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICB0aGlzLnNhdmVBc0J1c3kgPSBmYWxzZTtcbiAgICAgICAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7XG4gICAgICAgICAgICBrZXk6IFZJWl9UT0FTVF9LRVksXG4gICAgICAgICAgICBzZXZlcml0eTogJ2Vycm9yJyxcbiAgICAgICAgICAgIHN1bW1hcnk6ICdFcnJvciBzYXZpbmcgcmVnaW9ucycsXG4gICAgICAgICAgICBkZXRhaWw6IGAkeyhlcnIgYXMgRXJyb3IpPy5tZXNzYWdlID8/IGVycn1gLFxuICAgICAgICAgIH0pO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLl9zYXZlQXNTdWIgPSB0aGlzLnJlZ2lvbklvLnNhdmVHZW9Kc29uKGdlb0pzb25TdHIsIGZpbGVuYW1lKS5zdWJzY3JpYmUoe1xuICAgICAgICAgIG5leHQ6ICgpID0+IHtcbiAgICAgICAgICAgIHRoaXMuc2F2ZUFzQnVzeSA9IGZhbHNlO1xuICAgICAgICAgICAgdGhpcy5zaG93U2F2ZUFzRGlhbG9nID0gZmFsc2U7XG4gICAgICAgICAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7XG4gICAgICAgICAgICAgIGtleTogVklaX1RPQVNUX0tFWSxcbiAgICAgICAgICAgICAgc2V2ZXJpdHk6ICdzdWNjZXNzJyxcbiAgICAgICAgICAgICAgc3VtbWFyeTogJ1JlZ2lvbnMgc2F2ZWQnLFxuICAgICAgICAgICAgICBkZXRhaWw6IGBTYXZlZCBhcyAke2ZpbGVuYW1lfWAsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9LFxuICAgICAgICAgIGVycm9yOiAoZXJyKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnNhdmVBc0J1c3kgPSBmYWxzZTtcbiAgICAgICAgICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHtcbiAgICAgICAgICAgICAga2V5OiBWSVpfVE9BU1RfS0VZLFxuICAgICAgICAgICAgICBzZXZlcml0eTogJ2Vycm9yJyxcbiAgICAgICAgICAgICAgc3VtbWFyeTogJ0Vycm9yIHNhdmluZyByZWdpb25zJyxcbiAgICAgICAgICAgICAgZGV0YWlsOiBgJHtlcnIubWVzc2FnZSB8fCBlcnJ9YCxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH0sXG4gICAgICAgIH0pO1xuICAgICAgfSk7XG4gICAgfTtcblxuICAgIGlmICh0aGlzLnNhdmVBc0ZpbGVFeGlzdHMpIHtcbiAgICAgIHRoaXMuY29uZmlybWF0aW9uU2VydmljZS5jb25maXJtKHtcbiAgICAgICAgbWVzc2FnZTogYFwiJHtmaWxlbmFtZX1cIiBhbHJlYWR5IGV4aXN0cy4gRG8geW91IHdhbnQgdG8gb3ZlcndyaXRlIGl0P2AsXG4gICAgICAgIGhlYWRlcjogJ0NvbmZpcm0gT3ZlcndyaXRlJyxcbiAgICAgICAgaWNvbjogJ3BpIHBpLWV4Y2xhbWF0aW9uLXRyaWFuZ2xlJyxcbiAgICAgICAgYWNjZXB0OiBkb1NhdmUsXG4gICAgICB9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgZG9TYXZlKCk7XG4gICAgfVxuICB9XG5cbiAgLyoqIENhbmNlbCBhbiBpbi1wcm9ncmVzcyBHZW9KU09OIHBlcnNpc3Q6IGFib3J0IHRoZSBkZWZlcnJlZCBzZXJpYWxpemUgYW5kL29yXG4gICAqICB0aGUgaW4tZmxpZ2h0IHVwbG9hZCwgYW5kIHJlc2V0IHN0YXRlLiAqL1xuICBjYW5jZWxTYXZlQXMoKSB7XG4gICAgaWYgKHRoaXMuX3NhdmVBc1RpbWVyICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aGlzLl9zYXZlQXNUaW1lcik7XG4gICAgICB0aGlzLl9zYXZlQXNUaW1lciA9IHVuZGVmaW5lZDtcbiAgICB9XG4gICAgdGhpcy5fc2F2ZUFzU3ViPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMuX3NhdmVBc1N1YiA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLnNhdmVBc0J1c3kgPSBmYWxzZTtcbiAgfVxufVxuIiwiXG48cC10b29sYmFyIHN0eWxlQ2xhc3M9XCJyZWdpb24tdG9vbGJhclwiPlxuICA8ZGl2IGNsYXNzPVwicC10b29sYmFyLWdyb3VwLXN0YXJ0IGFsaWduLWl0ZW1zLWNlbnRlciBmbGV4LXdyYXAgZ2FwLTFcIj5cbiAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXRhZ1wiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgIFt0ZXh0XT1cIiFzaG93U2hhcGVMYWJlbFwiXG4gICAgICAgICAgICAgIHBUb29sdGlwPVwiU2hvdyByZWdpb24gbGFiZWxzLlwiXG4gICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgIChvbkNsaWNrKT1cInNob3dTaGFwZUxhYmVsID0gIXNob3dTaGFwZUxhYmVsOyBjaGFuZ2VTaG93U2hhcGVMYWJlbChzaG93U2hhcGVMYWJlbClcIj5cbiAgICA8L3AtYnV0dG9uPlxuICAgIDxzcGFuIGNsYXNzPVwidG9vbGJhci1zZXBhcmF0b3JcIj48L3NwYW4+XG4gICAgPCEtLSBOZXcgcmVnaW9ucyBhcmUgbm93IGNyZWF0ZWQgZnJvbSB0aGUgZGlhZ3JhbSB0b29sYmFyXG4gICAgICAgICAocmVjdGFuZ2xlLCBwb2x5bGluZSwgZnJlZWZvcm0sIHdhbmQpLiAtLT5cbiAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXVwbG9hZFwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIiBzdHlsZUNsYXNzPVwibXItMlwiXG4gICAgICAgICAgICAgIHBUb29sdGlwPVwiVXBsb2FkIGFuZCBkaXNwbGF5IHJlZ2lvbnMgZnJvbSBnZW9qc29uIGZpbGUuXCJcbiAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cInJvaUZpbGVJbnB1dC5jbGljaygpXCI+XG4gICAgPC9wLWJ1dHRvbj5cbiAgICA8aW5wdXQgI3JvaUZpbGVJbnB1dCB0eXBlPVwiZmlsZVwiIGFjY2VwdD1cIi5nZW9qc29uLC5qc29uXCIgaGlkZGVuXG4gICAgICAgICAgIChjaGFuZ2UpPVwiaW1wb3J0Um9pcygkZXZlbnQpOyByb2lGaWxlSW5wdXQudmFsdWUgPSAnJ1wiLz5cbiAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiIHN0eWxlQ2xhc3M9XCJtci0yXCJcbiAgICAgICAgICAgICAgcFRvb2x0aXA9XCJEb3dubG9hZCByZWdpb25zIGFzIGdlb2pzb24gZmlsZS5cIlxuICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIiBpY29uPVwicGkgcGktZG93bmxvYWRcIlxuICAgICAgICAgICAgICAoY2xpY2spPVwiZXhwb3J0Um9pcygpXCI+XG4gICAgPC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXNhdmVcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCIgc3R5bGVDbGFzcz1cIm1yLTJcIlxuICAgICAgICAgICAgICBwVG9vbHRpcD1cIlNhdmUgcmVnaW9ucyB0byBzZXJ2ZXIuXCJcbiAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cInBlcnNpc3RSZWdpb25zKClcIlxuICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwiIXJlZ2lvbnMgfHwgIXJlZ2lvbnMubGVuZ3RoXCI+XG4gICAgPC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiIHN0eWxlQ2xhc3M9XCJtci0yXCJcbiAgICAgICAgICAgICAgcFRvb2x0aXA9XCJEb3dubG9hZCBtYXNrIG9mIHRoZSByZWdpb25zIGFzIFBOR1wiXG4gICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJvcGVuU2F2ZU1hc2tEaWFsb2coKVwiXG4gICAgICAgICAgICAgIFtkaXNhYmxlZF09XCIhcmVnaW9ucyB8fCAhcmVnaW9ucy5sZW5ndGhcIj5cbiAgICAgIDwhLS0gUmVmZXJlbmNlcyB0aGUgZmlsZSBzbyBJbmtzY2FwZSBlZGl0cyB0byBtYXNrLnN2ZyBzaG93IHVwIGRpcmVjdGx5XG4gICAgICAgICAgICh0aGUgbGliJ3MgYXNzZXRzLyoqIGFyZSBzZXJ2ZWQgYXQgYXNzZXRzL3Bsb3R0aW5nLykuIFRoZVxuICAgICAgICAgICB0b29sYmFyLWljb24tZGFyayBmaWx0ZXIgcmVjb2xvdXJzIHRoZSBibGFjayBTVkcgdG8gdGhlIHRoZW1lIGJsdWUsXG4gICAgICAgICAgIHNhbWUgYXMgcmVnaW9uLWVkaXRvci5zdmcgYW5kIHRoZSBvdGhlciBTVkcgdG9vbGJhciBpY29ucy4gLS0+XG4gICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9tYXNrLnN2Z1wiIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIlxuICAgICAgICAgICBzdHlsZT1cIndpZHRoOiAxcmVtOyBoZWlnaHQ6IDFyZW07IHZlcnRpY2FsLWFsaWduOiBtaWRkbGU7XCJcbiAgICAgICAgICAgYWx0PVwiRG93bmxvYWQgbWFza1wiLz5cbiAgICA8L3AtYnV0dG9uPlxuICAgIDxwLWJ1dHRvbiBsYWJlbD1cIng1MTJcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCIgc3R5bGVDbGFzcz1cIm1yLTJcIlxuICAgICAgICAgICAgICBwVG9vbHRpcD1cIlJvdW5kIGFsbCByZWN0YW5nbGUgcmVnaW9uc1xuICAgICAgICAgICAgICAgICAgICB0byB3aWR0aCBhbmQgaGVpZ2h0IHRoYXQgYXJlIG11bHRpcGxlIG9mIDUxMlwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJyb3VuZFJlY3RhbmdsZUxlbmd0aHMoKVwiPlxuICAgIDwvcC1idXR0b24+XG4gICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1jaGVjay1zcXVhcmVcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCIgc3R5bGVDbGFzcz1cIm1yLTJcIlxuICAgICAgICAgICAgICBwVG9vbHRpcD1cIlNlbGVjdCBhbGwgcmVnaW9uc1wiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJzZWxlY3RBbGxSZWdpb25zKClcIlxuICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwiIXJlZ2lvbnMgfHwgIXJlZ2lvbnMubGVuZ3RoXCI+XG4gICAgPC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXRhZ3NcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCIgc3R5bGVDbGFzcz1cIm1yLTJcIlxuICAgICAgICAgICAgICBwVG9vbHRpcD1cIkVkaXQgY2xhc3Mgb24gc2VsZWN0ZWQgcm93c1wiXG4gICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgIChjbGljayk9XCIoIXNlbGVjdGVkUmVnaW9ucyB8fCAhc2VsZWN0ZWRSZWdpb25zLmxlbmd0aCk/IG9wLmhpZGUoKSA6IG9wLnRvZ2dsZSgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgW2Rpc2FibGVkXT1cIiFzZWxlY3RlZFJlZ2lvbnMgfHwgIXNlbGVjdGVkUmVnaW9ucy5sZW5ndGhcIj5cbiAgICA8L3AtYnV0dG9uPlxuICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktcGFsZXR0ZVwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIiBzdHlsZUNsYXNzPVwibXItMlwiXG4gICAgICAgICAgICAgIHBUb29sdGlwPVwiRWRpdCBjb2xvdXIgb2Ygc2VsZWN0ZWQgcmVnaW9uc1wiXG4gICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJvcGVuQ29sb3JEaWFsb2coKVwiXG4gICAgICAgICAgICAgIFtkaXNhYmxlZF09XCIhc2VsZWN0ZWRSZWdpb25zIHx8ICFzZWxlY3RlZFJlZ2lvbnMubGVuZ3RoXCI+XG4gICAgPC9wLWJ1dHRvbj5cbiAgICA8cC1vdmVybGF5UGFuZWwgI29wIChvbkhpZGUpPVwiYnVsa0NsYXNzPScnOyBuZXdCdWxrQ2xhc3M9JydcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJmbGV4IGZsZXgtY29sdW1uIGdhcC0yXCIgc3R5bGU9XCJ3aWR0aDogMThyZW1cIj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmb250LW1lZGl1bSB0ZXh0LTkwMFwiPlNldCBjbGFzcyBvbiB7eyBzZWxlY3RlZFJlZ2lvbnM/Lmxlbmd0aCB8fCAwIH19IHNlbGVjdGVkIHJlZ2lvbihzKTwvc3Bhbj5cbiAgICAgICAgPCEtLSBTZWxlY3RpbmcgYSBjbGFzcyBhcHBsaWVzIGl0IGltbWVkaWF0ZWx5IChjaG9vc2luZyBhbiBvcHRpb24gY2xvc2VzIHRoZVxuICAgICAgICAgICAgIG92ZXJsYXksIHNvIHRoZXJlJ3Mgbm8gc2VwYXJhdGUgQXBwbHkgYnV0dG9uKS4gLS0+XG4gICAgICAgIDxwLWRyb3Bkb3duIHN0eWxlQ2xhc3M9XCJ3LWZ1bGxcIiBhcHBlbmRUbz1cImJvZHlcIiBbb3B0aW9uc109XCJwcmVzZXRTZXQuY2xhc3Nlc1wiXG4gICAgICAgICAgICAgICAgICAgIG9wdGlvbkxhYmVsPVwibmFtZVwiIG9wdGlvblZhbHVlPVwibmFtZVwiIFsobmdNb2RlbCldPVwiYnVsa0NsYXNzXCJcbiAgICAgICAgICAgICAgICAgICAgcGxhY2Vob2xkZXI9XCJDaG9vc2UgYSBjbGFzc+KAplwiXG4gICAgICAgICAgICAgICAgICAgIFtmaWx0ZXJdPVwicHJlc2V0U2V0LmNsYXNzZXMubGVuZ3RoID4gOFwiIGZpbHRlckJ5PVwibmFtZVwiXG4gICAgICAgICAgICAgICAgICAgIChvbkNoYW5nZSk9XCJhcHBseUJ1bGtDbGFzcygpXCI+XG4gICAgICAgICAgPG5nLXRlbXBsYXRlIGxldC1jIHBUZW1wbGF0ZT1cIml0ZW1cIj5cbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiZGQtdmFsdWVcIj48c3BhbiBjbGFzcz1cImNoaXAtc3dhdGNoXCIgW3N0eWxlLmJhY2tncm91bmRdPVwiYy5jb2xvclwiPjwvc3Bhbj57eyBjLm5hbWUgfX08L3NwYW4+XG4gICAgICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgICAgPC9wLWRyb3Bkb3duPlxuICAgICAgICA8ZGl2IGNsYXNzPVwicC1pbnB1dGdyb3VwXCI+XG4gICAgICAgICAgPGlucHV0IHR5cGU9XCJ0ZXh0XCIgcElucHV0VGV4dCBwbGFjZWhvbGRlcj1cIuKApm9yIGEgbmV3IGNsYXNzXCIgWyhuZ01vZGVsKV09XCJuZXdCdWxrQ2xhc3NcIlxuICAgICAgICAgICAgICAgICAoa2V5ZG93bi5lbnRlcik9XCJhZGRBbmRBcHBseUJ1bGtDbGFzcygpXCIvPlxuICAgICAgICAgIDxidXR0b24gcEJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgaWNvbj1cInBpIHBpLXBsdXNcIiBwVG9vbHRpcD1cIkFkZCBjbGFzcyAmYW1wOyBzZXQgaXRcIlxuICAgICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCIgW2Rpc2FibGVkXT1cIiFuZXdCdWxrQ2xhc3MudHJpbSgpXCJcbiAgICAgICAgICAgICAgICAgIChjbGljayk9XCJhZGRBbmRBcHBseUJ1bGtDbGFzcygpXCI+PC9idXR0b24+XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9kaXY+XG4gICAgPC9wLW92ZXJsYXlQYW5lbD5cbiAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXRyYXNoXCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiIHN0eWxlQ2xhc3M9XCJtci0yXCJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cImRlbGV0ZVNlbGVjdGVkUmVnaW9ucygpXCJcbiAgICAgICAgICAgICAgcFRvb2x0aXA9XCJEZWxldGUgc2VsZWN0ZWQgcmVnaW9uc1wiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgIFtkaXNhYmxlZF09XCIhc2VsZWN0ZWRSZWdpb25zIHx8ICFzZWxlY3RlZFJlZ2lvbnMubGVuZ3RoXCI+PC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXRpbWVzLWNpcmNsZVwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIiBzdHlsZUNsYXNzPVwibXItMlwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJjbGVhckFsbFJlZ2lvbnMoKVwiXG4gICAgICAgICAgICAgIHBUb29sdGlwPVwiRGVsZXRlIGFsbCByZWdpb25zXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgW2Rpc2FibGVkXT1cIiFyZWdpb25zIHx8ICFyZWdpb25zLmxlbmd0aFwiPjwvcC1idXR0b24+XG4gICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1xdWVzdGlvblwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAoY2xpY2spPVwic2hvd0hlbHAoKVwiIHBUb29sdGlwPVwiSGVscFwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiPjwvcC1idXR0b24+XG4gIDwvZGl2PlxuPC9wLXRvb2xiYXI+XG5cbjwhLS0gRG9ja2VkIFwiQ2xhc3Nlc1wiIHBhbmVsIChqaXQtdWkjNzApOiB0aGUgYWx3YXlzLXZpc2libGUgbGlzdCBvZiBhbm5vdGF0aW9uXG4gICAgIGNsYXNzZXMgYWJvdmUgdGhlIHJlZ2lvbnMgdGFibGUuIENsaWNrIGEgcm93IHRvIG1ha2UgaXQgdGhlIGFjdGl2ZSBjbGFzc1xuICAgICAobmV3IHJlZ2lvbnMgaW5oZXJpdCBpdDsgd2l0aCByb3dzIHNlbGVjdGVkIGl0IHJlLWNsYXNzaWZpZXMgdGhlbSk7IHJlY29sb3VyXG4gICAgIHZpYSB0aGUgc3dhdGNoOyB0aGUgYmFkZ2Ugc2hvd3MgaG93IG1hbnkgcmVnaW9ucyB1c2UgdGhlIGNsYXNzLiBcIk1hbmFnZVwiXG4gICAgIG9wZW5zIHRoZSBmdWxsIGVkaXRvciAoZmFsbGJhY2sgcGFsZXR0ZSwgYXV0by1hZGQsIG1hdGNoaW5nLCBpbXBvcnQvZXhwb3J0KS4gLS0+XG48ZGl2IGNsYXNzPVwiY2xhc3Nlcy1wYW5lbFwiPlxuICA8ZGl2IGNsYXNzPVwiY2xhc3Nlcy1wYW5lbC1oZWFkZXJcIj5cbiAgICA8c3BhbiBjbGFzcz1cImNsYXNzZXMtcGFuZWwtdGl0bGVcIj5DbGFzc2VzPC9zcGFuPlxuICAgIDxidXR0b24gcEJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgaWNvbj1cInBpIHBpLWNvZ1wiIGxhYmVsPVwiTWFuYWdlXCJcbiAgICAgICAgICAgIGNsYXNzPVwicC1idXR0b24tc20gcC1idXR0b24tdGV4dFwiXG4gICAgICAgICAgICBwVG9vbHRpcD1cIk1hbmFnZSBjbGFzc2VzIOKAlCBhZGQgLyByZW1vdmUsIHBhbGV0dGUsIG1hdGNoaW5nLCBpbXBvcnQvZXhwb3J0XCJcbiAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiIChjbGljayk9XCJvcGVuTWFuYWdlRGlhbG9nKClcIj48L2J1dHRvbj5cbiAgPC9kaXY+XG4gIDxkaXYgY2xhc3M9XCJjbGFzc2VzLXBhbmVsLWJvZHlcIj5cbiAgICA8ZGl2ICpuZ0Zvcj1cImxldCBjIG9mIGRpc3BsYXlDbGFzc2VzOyB0cmFja0J5OiB0cmFja0J5Q2xhc3NOYW1lXCJcbiAgICAgICAgIGNsYXNzPVwiY2xhc3Mtcm93XCIgW2NsYXNzLmFjdGl2ZV09XCJhY3RpdmVDbGFzcyA9PT0gYy5uYW1lXCJcbiAgICAgICAgIChjbGljayk9XCJzZWxlY3RBY3RpdmVDbGFzcyhjLm5hbWUpXCJcbiAgICAgICAgIFtwVG9vbHRpcF09XCJzZWxlY3RlZFJlZ2lvbnM/Lmxlbmd0aFxuICAgICAgICAgICA/ICdBcHBseSDigJwnICsgYy5uYW1lICsgJ+KAnSB0byB0aGUgc2VsZWN0ZWQgcmVnaW9uKHMpJ1xuICAgICAgICAgICA6ICdTZXQg4oCcJyArIGMubmFtZSArICfigJ0gYWN0aXZlIOKAlCBuZXcgcmVnaW9ucyB1c2UgaXQnXCJcbiAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiPlxuICAgICAgPHNwYW4gY2xhc3M9XCJyb3ctc3dhdGNoXCIgKGNsaWNrKT1cIiRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiPlxuICAgICAgICA8aGV4LWNvbG9yLXBpY2tlciBbY29sb3JdPVwiYy5jb2xvclwiIChjb2xvckNoYW5nZSk9XCJzZXRDbGFzc0NvbG9yKGMubmFtZSwgJGV2ZW50KVwiPjwvaGV4LWNvbG9yLXBpY2tlcj5cbiAgICAgIDwvc3Bhbj5cbiAgICAgIDxzcGFuIGNsYXNzPVwicm93LW5hbWVcIj57eyBjLm5hbWUgfX08L3NwYW4+XG4gICAgICA8c3BhbiBjbGFzcz1cInJvdy1jb3VudFwiIFtjbGFzcy56ZXJvXT1cImNsYXNzQ291bnQoYy5uYW1lKSA9PT0gMFwiPnt7IGNsYXNzQ291bnQoYy5uYW1lKSB9fTwvc3Bhbj5cbiAgICAgIDxidXR0b24gcEJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgaWNvbj1cInBpIHBpLXRyYXNoXCJcbiAgICAgICAgICAgICAgY2xhc3M9XCJwLWJ1dHRvbi1zbSBwLWJ1dHRvbi10ZXh0IHAtYnV0dG9uLXdhcm5pbmcgcm93LWRlbFwiXG4gICAgICAgICAgICAgIFtkaXNhYmxlZF09XCJjLm5hbWUgPT09IGRlZmF1bHRDbGFzc05hbWUgJiYgY2xhc3NDb3VudChjLm5hbWUpID4gMFwiXG4gICAgICAgICAgICAgIFtwVG9vbHRpcF09XCJkZWxldGVDbGFzc1Rvb2x0aXAoYy5uYW1lKVwiXG4gICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgIChjbGljayk9XCIkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7IGRlbGV0ZUNsYXNzKGMubmFtZSlcIj48L2J1dHRvbj5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2ICpuZ0lmPVwiIWRpc3BsYXlDbGFzc2VzLmxlbmd0aFwiIGNsYXNzPVwiY2xhc3Nlcy1lbXB0eVwiPlxuICAgICAgTm8gY2xhc3NlcyB5ZXQg4oCUIGFkZCBvbmUgZnJvbSBNYW5hZ2UuXG4gICAgPC9kaXY+XG4gIDwvZGl2PlxuPC9kaXY+XG5cbjxkaXYgY2xhc3M9XCJyZWdpb24tZWRpdG9yLWNvbnRlbnRcIj5cbiAgICA8cC10YWJsZSAjZHQgW3ZhbHVlXT1cInJlZ2lvbnMuc2xpY2UocGFnaW5hdG9yRmlyc3QsIHBhZ2luYXRvckZpcnN0ICsgcGFnaW5hdG9yUm93cylcIlxuICAgICAgICAgICAgIFt0YWJsZVN0eWxlXT1cInsnd2lkdGgnOiAnMTAwJSd9XCIgWyhzZWxlY3Rpb24pXT1cInNlbGVjdGVkUmVnaW9uc1wiXG4gICAgICAgICAgICAgc2VsZWN0aW9uTW9kZT1cIm11bHRpcGxlXCIgW21ldGFLZXlTZWxlY3Rpb25dPVwibWV0YUtleVwiXG4gICAgICAgICAgICAgKG9uUm93U2VsZWN0KT1cIm9uU2VsZWN0aW9uQ2hhbmdlZCgpXCIgKG9uUm93VW5zZWxlY3QpPVwib25TZWxlY3Rpb25DaGFuZ2VkKClcIlxuICAgICAgICAgICAgIFtyZXNpemFibGVDb2x1bW5zXT1cInRydWVcIlxuICAgICAgICAgICAgIFtyb3dIb3Zlcl09XCJ0cnVlXCIgZGF0YUtleT1cImlkXCIgc3R5bGVDbGFzcz1cInAtZGF0YXRhYmxlLXNtIHJlZ2lvbi1lZGl0b3ItdGFibGVcIlxuICAgICAgICAgICAgIFtzY3JvbGxhYmxlXT1cInRydWVcIiBzY3JvbGxIZWlnaHQ9XCIxMDAlXCI+XG4gICAgICA8bmctdGVtcGxhdGUgcFRlbXBsYXRlPVwiaGVhZGVyXCI+XG4gICAgICAgIDwhLS0gSGlkZSB0aGUgY29sdW1uIGhlYWRlciB3aGVuIHRoZXJlIGFyZSBubyByZWdpb25zIHRvIHNob3cuIC0tPlxuICAgICAgICA8dHIgKm5nSWY9XCJyZWdpb25zLmxlbmd0aCA+IDBcIj5cbiAgICAgICAgICA8IS0tIENsYXNzIGNvbHVtbjogc2hyaW5rcyB0byBmaXQgXCJSZWdpb25cIiBieSBkZWZhdWx0LCBncm93cyB3aXRoIGxvbmdlciBsYWJlbHMuIC0tPlxuICAgICAgICAgIDx0aCBjbGFzcz1cImNsYXNzLWNvbC1oZWFkZXJcIiBwUmVzaXphYmxlQ29sdW1uPkNsYXNzIDxwLXNvcnRJY29uIGZpZWxkPVwibGFiZWxcIj48L3Atc29ydEljb24+PC90aD5cbiAgICAgICAgICA8dGggY2xhc3M9XCJzdXJmYWNlLWNvbC1oZWFkZXJcIiBwUmVzaXphYmxlQ29sdW1uPlN1cmZhY2U8L3RoPlxuICAgICAgICAgIDwhLS0gVHJhc2ggY29sdW1uOiBqdXN0IGVub3VnaCBmb3IgdGhlIGljb24uIC0tPlxuICAgICAgICAgIDx0aCBzdHlsZT1cIndpZHRoOjJyZW1cIj48L3RoPlxuICAgICAgICA8L3RyPlxuICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgIDxuZy10ZW1wbGF0ZSBwVGVtcGxhdGU9XCJib2R5XCIgbGV0LXJlZ2lvbiBsZXQtc2hhcGVJZHg9XCJyb3dJbmRleFwiPlxuICAgICAgICA8dHIgW3BTZWxlY3RhYmxlUm93XT1cInJlZ2lvblwiIFtwU2VsZWN0YWJsZVJvd0luZGV4XT1cInNoYXBlSWR4XCI+XG4gICAgICAgICAgPHRkIGNsYXNzPVwiY2xhc3MtY2VsbFwiPlxuICAgICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIiFpc0VkaXRpbmdMYWJlbChyZWdpb24pOyBlbHNlIGVkaXRMYWJlbFRwbFwiPlxuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImNsYXNzLWNlbGwtZGlzcGxheVwiPlxuICAgICAgICAgICAgICAgIDwhLS0gUXVpY2stcGljayBhIHByZXNldCBjbGFzcyAoaXRzIGNvbG91ciBmb2xsb3dzKTsgdGhlIHBlbmNpbCBpcyBmb3JcbiAgICAgICAgICAgICAgICAgICAgIGEgZnJlZS1mb3JtIG5hbWUgLyByZW5hbWUgKGppdC11aSM3MCkuIC0tPlxuICAgICAgICAgICAgICAgIDxwLWRyb3Bkb3duIGNsYXNzPVwiY2xhc3MtZHJvcGRvd25cIiBhcHBlbmRUbz1cImJvZHlcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFtvcHRpb25zXT1cInByZXNldFNldC5jbGFzc2VzXCIgb3B0aW9uTGFiZWw9XCJuYW1lXCIgb3B0aW9uVmFsdWU9XCJuYW1lXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBbbmdNb2RlbF09XCJyZWdpb24ubGFiZWxcIiBbYXV0b0Rpc3BsYXlGaXJzdF09XCJmYWxzZVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgKG9uQ2hhbmdlKT1cImFwcGx5UHJlc2V0VG9SZWdpb24ocmVnaW9uLCAkZXZlbnQudmFsdWUpXCI+XG4gICAgICAgICAgICAgICAgICA8bmctdGVtcGxhdGUgcFRlbXBsYXRlPVwic2VsZWN0ZWRJdGVtXCI+XG4gICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiZGQtdmFsdWVcIj5cbiAgICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImNoaXAtc3dhdGNoXCIgW3N0eWxlLmJhY2tncm91bmRdPVwiY29sb3JGb3JOYW1lKHJlZ2lvbi5sYWJlbClcIj48L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJjbGFzcy1uYW1lXCIgW3BUb29sdGlwXT1cInJlZ2lvbi5sYWJlbFwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiPnt7IHJlZ2lvbi5sYWJlbCB8fCAn4oCUJyB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgPC9zcGFuPlxuICAgICAgICAgICAgICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgICAgICAgICAgICAgIDxuZy10ZW1wbGF0ZSBsZXQtYyBwVGVtcGxhdGU9XCJpdGVtXCI+XG4gICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiZGQtdmFsdWVcIj5cbiAgICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImNoaXAtc3dhdGNoXCIgW3N0eWxlLmJhY2tncm91bmRdPVwiYy5jb2xvclwiPjwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgICA8c3Bhbj57eyBjLm5hbWUgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICAgICAgICAgICAgPC9wLWRyb3Bkb3duPlxuICAgICAgICAgICAgICAgIDxpIGNsYXNzPVwicGkgcGktcGVuY2lsIGljb24taG92ZXIgbWwtMlwiXG4gICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJSZW5hbWUgLyBmcmVlLWZvcm0gbGFiZWxcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgIChjbGljayk9XCJzdGFydEVkaXRMYWJlbChyZWdpb24sICRldmVudClcIj48L2k+XG4gICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgICAgICAgPG5nLXRlbXBsYXRlICNlZGl0TGFiZWxUcGw+XG4gICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwicC1pbnB1dC1pY29uLXJpZ2h0IGNsYXNzLWNlbGwtZWRpdFwiPlxuICAgICAgICAgICAgICAgIDxpIGNsYXNzPVwicGkgcGktdGltZXMgaWNvbi1ob3ZlclwiXG4gICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJDbG9zZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICAgKGNsaWNrKT1cInN0b3BFZGl0TGFiZWwocmVnaW9uLCBmYWxzZSwgJGV2ZW50KVwiPjwvaT5cbiAgICAgICAgICAgICAgICA8aW5wdXQgdHlwZT1cInRleHRcIiBwSW5wdXRUZXh0IGNsYXNzPVwicC1pbnB1dHRleHQtc20gdy1mdWxsXCJcbiAgICAgICAgICAgICAgICAgICAgICAgWyhuZ01vZGVsKV09XCJyZWdpb24ubGFiZWxcIlxuICAgICAgICAgICAgICAgICAgICAgICAoY2xpY2spPVwiJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCJcbiAgICAgICAgICAgICAgICAgICAgICAgKGtleWRvd24uZW50ZXIpPVwic3RvcEVkaXRMYWJlbChyZWdpb24sIHRydWUsICRldmVudClcIlxuICAgICAgICAgICAgICAgICAgICAgICAoa2V5ZG93bi5lc2NhcGUpPVwic3RvcEVkaXRMYWJlbChyZWdpb24sIGZhbHNlLCAkZXZlbnQpXCIvPlxuICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgICAgIDwvdGQ+XG4gICAgICAgICAgPCEtLSBTdXJmYWNlIGFyZWEgKMK1bcKyL21twrIgd2hlbiB0aGUgaW1hZ2UgaXMgc2NhbGVkLCBlbHNlIHB4wrIpLiAtLT5cbiAgICAgICAgICA8dGQgY2xhc3M9XCJzdXJmYWNlLWNlbGxcIj57eyByZWdpb25BcmVhKHJlZ2lvbikgfHwgJ+KAlCcgfX08L3RkPlxuICAgICAgICAgIDx0ZD5cbiAgICAgICAgICAgIDxidXR0b24gcEJ1dHRvbiBwUmlwcGxlIGljb249XCJwaSBwaS10cmFzaFwiXG5cdFx0XHRcdFx0XHRcdGNsYXNzPVwicC1idXR0b24tc20gcC1idXR0b24tcm91bmRlZCBwLWJ1dHRvbi10ZXh0IHAtYnV0dG9uLXdhcm5pbmdcIlxuICAgICAgICAgICAgICAgICAgICAoY2xpY2spPVwiZGVsZXRlUmVnaW9uKHBhZ2luYXRvckZpcnN0ICsgc2hhcGVJZHgpXCI+PC9idXR0b24+XG4gICAgICAgICAgPC90ZD5cbiAgICAgICAgPC90cj5cbiAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgPC9wLXRhYmxlPlxuPC9kaXY+XG5cbjxwLXBhZ2luYXRvciAqbmdJZj1cInJlZ2lvbnMubGVuZ3RoID4gMFwiXG4gICAgICAgICAgICAgW3Jvd3NdPVwicGFnaW5hdG9yUm93c1wiIFt0b3RhbFJlY29yZHNdPVwicmVnaW9ucy5sZW5ndGhcIlxuICAgICAgICAgICAgIFtmaXJzdF09XCJwYWdpbmF0b3JGaXJzdFwiXG4gICAgICAgICAgICAgW3Jvd3NQZXJQYWdlT3B0aW9uc109XCJyb3dzUGVyUGFnZU9wdGlvbnNcIlxuICAgICAgICAgICAgIFtzaG93Q3VycmVudFBhZ2VSZXBvcnRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgY3VycmVudFBhZ2VSZXBvcnRUZW1wbGF0ZT1cIntmaXJzdH0gdG8ge2xhc3R9IG9mIHt0b3RhbFJlY29yZHN9XCJcbiAgICAgICAgICAgICAob25QYWdlQ2hhbmdlKT1cIm9uUGFnZUNoYW5nZSgkZXZlbnQpXCJcbiAgICAgICAgICAgICBzdHlsZUNsYXNzPVwicmVnaW9uLWVkaXRvci1wYWdpbmF0b3JcIj5cbjwvcC1wYWdpbmF0b3I+XG5cbjwhLS0gTGl2ZS1lZGl0IG1vZGU6IG5vIFNhdmUvQ2FuY2VsIOKAlCBldmVyeSBlZGl0IGNvbW1pdHMgaW1tZWRpYXRlbHkgdG8gdGhlXG4gICAgIGFjdGl2ZSBpbWFnZSdzIHJlZ2lvbiBjYWNoZS4gLS0+XG5cblxuPHAtY29uZmlybURpYWxvZyAjY2QgW3N0eWxlXT1cInt3aWR0aDogJzI0cmVtJywgaGVpZ2h0OiAnMTRyZW0nfVwiIGtleT1cInBvc2l0aW9uRGlhbG9nXCJcbiAgICAgICAgICAgICAgICAgW2Jhc2VaSW5kZXhdPVwiMTAwMDBcIiByZWplY3RCdXR0b25TdHlsZUNsYXNzPVwicC1idXR0b24tb3V0bGluZWRcIj5cbiAgPG5nLXRlbXBsYXRlIHBUZW1wbGF0ZT1cImZvb3RlclwiPlxuICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIHBCdXR0b24gaWNvbj1cInBpIHBpLWNoZWNrXCIgbGFiZWw9XCJZZXNcIiAoY2xpY2spPVwiY2QuYWNjZXB0KClcIj48L2J1dHRvbj5cbiAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cInAtYnV0dG9uLW91dGxpbmVkXCIgcEJ1dHRvbiBpY29uPVwicGkgcGktdGltZXNcIiBsYWJlbD1cIk5vXCJcbiAgICAgICAgICAgIChjbGljayk9XCJjZC5yZWplY3QoKVwiPjwvYnV0dG9uPlxuICA8L25nLXRlbXBsYXRlPlxuPC9wLWNvbmZpcm1EaWFsb2c+XG5cbjxwLWRpYWxvZyBoZWFkZXI9XCJTYXZlIFJlZ2lvbnMgQXNcIiBbKHZpc2libGUpXT1cInNob3dTYXZlQXNEaWFsb2dcIiBbbW9kYWxdPVwidHJ1ZVwiIGFwcGVuZFRvPVwiYm9keVwiXG4gICAgICAgICAgW3N0eWxlXT1cInt3aWR0aDogJzMwcmVtJ31cIiBbZHJhZ2dhYmxlXT1cImZhbHNlXCIgW3Jlc2l6YWJsZV09XCJmYWxzZVwiXG4gICAgICAgICAgW2Nsb3NhYmxlXT1cIiFzYXZlQXNCdXN5XCIgW2Nsb3NlT25Fc2NhcGVdPVwiIXNhdmVBc0J1c3lcIj5cbiAgPCEtLSBJZGxlOiBmaWxlbmFtZSArIG92ZXJ3cml0ZSB3YXJuaW5nLiAtLT5cbiAgPGRpdiAqbmdJZj1cIiFzYXZlQXNCdXN5XCIgY2xhc3M9XCJmbGV4IGZsZXgtY29sdW1uIGdhcC0zIHB0LTJcIj5cbiAgICA8bGFiZWwgZm9yPVwiZ2VvanNvbi1maWxlbmFtZVwiIGNsYXNzPVwiZm9udC1tZWRpdW1cIj5HZW9KU09OIGZpbGVuYW1lPC9sYWJlbD5cbiAgICA8aW5wdXQgaWQ9XCJnZW9qc29uLWZpbGVuYW1lXCIgdHlwZT1cInRleHRcIiBwSW5wdXRUZXh0XG4gICAgICAgICAgIFsobmdNb2RlbCldPVwic2F2ZUFzRmlsZW5hbWVcIlxuICAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJjaGVja1NhdmVBc0ZpbGVFeGlzdHMoKVwiXG4gICAgICAgICAgIGNsYXNzPVwidy1mdWxsXCIgLz5cbiAgICA8c21hbGwgKm5nSWY9XCJzYXZlQXNGaWxlRXhpc3RzXCIgY2xhc3M9XCJwLWVycm9yXCI+XG4gICAgICA8aSBjbGFzcz1cInBpIHBpLWV4Y2xhbWF0aW9uLXRyaWFuZ2xlIG1yLTFcIj48L2k+XG4gICAgICBUaGlzIGZpbGUgYWxyZWFkeSBleGlzdHMgYW5kIHdpbGwgYmUgb3ZlcndyaXR0ZW4uXG4gICAgPC9zbWFsbD5cbiAgPC9kaXY+XG5cbiAgPCEtLSBCdXN5OiBwcm9ncmVzcyB3aGlsZSByZWdpb25zIHNlcmlhbGl6ZSArIHVwbG9hZC4gLS0+XG4gIDxkaXYgKm5nSWY9XCJzYXZlQXNCdXN5XCIgY2xhc3M9XCJmbGV4IGZsZXgtY29sdW1uIGdhcC0zIHB0LTJcIj5cbiAgICA8c3Bhbj5TYXZpbmcgcmVnaW9uc+KApjwvc3Bhbj5cbiAgICA8cC1wcm9ncmVzc0JhciBtb2RlPVwiaW5kZXRlcm1pbmF0ZVwiIFtzdHlsZV09XCJ7aGVpZ2h0OiAnMC43NXJlbSd9XCI+PC9wLXByb2dyZXNzQmFyPlxuICA8L2Rpdj5cblxuICA8bmctdGVtcGxhdGUgcFRlbXBsYXRlPVwiZm9vdGVyXCI+XG4gICAgPHAtYnV0dG9uICpuZ0lmPVwiIXNhdmVBc0J1c3lcIiBsYWJlbD1cIkNhbmNlbFwiIGljb249XCJwaSBwaS10aW1lc1wiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAob25DbGljayk9XCJzaG93U2F2ZUFzRGlhbG9nID0gZmFsc2VcIj48L3AtYnV0dG9uPlxuICAgIDxwLWJ1dHRvbiAqbmdJZj1cIiFzYXZlQXNCdXN5XCIgbGFiZWw9XCJTYXZlXCIgaWNvbj1cInBpIHBpLXNhdmVcIlxuICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwiIXNhdmVBc0ZpbGVuYW1lIHx8ICFzYXZlQXNGaWxlbmFtZS50cmltKClcIlxuICAgICAgICAgICAgICAob25DbGljayk9XCJjb25maXJtU2F2ZUFzKClcIj48L3AtYnV0dG9uPlxuICAgIDxwLWJ1dHRvbiAqbmdJZj1cInNhdmVBc0J1c3lcIiBsYWJlbD1cIkNhbmNlbFwiIGljb249XCJwaSBwaS10aW1lc1wiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAob25DbGljayk9XCJjYW5jZWxTYXZlQXMoKVwiPjwvcC1idXR0b24+XG4gIDwvbmctdGVtcGxhdGU+XG48L3AtZGlhbG9nPlxuXG48cC1kaWFsb2cgaGVhZGVyPVwiRXhwb3J0IFJlZ2lvbnNcIiBbKHZpc2libGUpXT1cInNob3dFeHBvcnREaWFsb2dcIiBbbW9kYWxdPVwidHJ1ZVwiIGFwcGVuZFRvPVwiYm9keVwiXG4gICAgICAgICAgW3N0eWxlXT1cInt3aWR0aDogJzMwcmVtJ31cIiBbZHJhZ2dhYmxlXT1cImZhbHNlXCIgW3Jlc2l6YWJsZV09XCJmYWxzZVwiPlxuICA8ZGl2IGNsYXNzPVwiZmxleCBmbGV4LWNvbHVtbiBnYXAtMyBwdC0yXCI+XG4gICAgPGxhYmVsIGZvcj1cImV4cG9ydC1maWxlbmFtZVwiIGNsYXNzPVwiZm9udC1tZWRpdW1cIj5HZW9KU09OIGZpbGVuYW1lPC9sYWJlbD5cbiAgICA8aW5wdXQgaWQ9XCJleHBvcnQtZmlsZW5hbWVcIiB0eXBlPVwidGV4dFwiIHBJbnB1dFRleHRcbiAgICAgICAgICAgWyhuZ01vZGVsKV09XCJleHBvcnRGaWxlbmFtZVwiXG4gICAgICAgICAgIGNsYXNzPVwidy1mdWxsXCIgLz5cbiAgPC9kaXY+XG4gIDxuZy10ZW1wbGF0ZSBwVGVtcGxhdGU9XCJmb290ZXJcIj5cbiAgICA8cC1idXR0b24gbGFiZWw9XCJDYW5jZWxcIiBpY29uPVwicGkgcGktdGltZXNcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgKG9uQ2xpY2spPVwic2hvd0V4cG9ydERpYWxvZyA9IGZhbHNlXCI+PC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b24gbGFiZWw9XCJFeHBvcnRcIiBpY29uPVwicGkgcGktZG93bmxvYWRcIlxuICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwiIWV4cG9ydEZpbGVuYW1lIHx8ICFleHBvcnRGaWxlbmFtZS50cmltKClcIlxuICAgICAgICAgICAgICAob25DbGljayk9XCJjb25maXJtRXhwb3J0KClcIj48L3AtYnV0dG9uPlxuICA8L25nLXRlbXBsYXRlPlxuPC9wLWRpYWxvZz5cblxuPHAtZGlhbG9nIGhlYWRlcj1cIlNhdmUgTWFza1wiIFsodmlzaWJsZSldPVwic2hvd1NhdmVNYXNrRGlhbG9nXCIgW21vZGFsXT1cInRydWVcIiBhcHBlbmRUbz1cImJvZHlcIlxuICAgICAgICAgIFtzdHlsZV09XCJ7d2lkdGg6ICczMHJlbSd9XCIgW2RyYWdnYWJsZV09XCJmYWxzZVwiIFtyZXNpemFibGVdPVwiZmFsc2VcIlxuICAgICAgICAgIFtjbG9zYWJsZV09XCIhbWFza0J1c3lcIiBbY2xvc2VPbkVzY2FwZV09XCIhbWFza0J1c3lcIj5cbiAgPCEtLSBJZGxlOiBmaWxlbmFtZSArIG1hc2stdHlwZSBmb3JtLiAtLT5cbiAgPGRpdiAqbmdJZj1cIiFtYXNrQnVzeVwiIGNsYXNzPVwiZmxleCBmbGV4LWNvbHVtbiBnYXAtMyBwdC0yXCI+XG4gICAgPGxhYmVsIGZvcj1cIm1hc2stZmlsZW5hbWVcIiBjbGFzcz1cImZvbnQtbWVkaXVtXCI+TWFzayBmaWxlbmFtZTwvbGFiZWw+XG4gICAgPGlucHV0IGlkPVwibWFzay1maWxlbmFtZVwiIHR5cGU9XCJ0ZXh0XCIgcElucHV0VGV4dFxuICAgICAgICAgICBbKG5nTW9kZWwpXT1cInNhdmVNYXNrRmlsZW5hbWVcIlxuICAgICAgICAgICBjbGFzcz1cInctZnVsbFwiIC8+XG4gICAgPHNwYW4gY2xhc3M9XCJmb250LW1lZGl1bVwiPk1hc2sgdHlwZTwvc3Bhbj5cbiAgICA8ZGl2IGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgZ2FwLTJcIj5cbiAgICAgIDxwLXJhZGlvQnV0dG9uIGlucHV0SWQ9XCJtYXNrLWJpbmFyeVwiIG5hbWU9XCJtYXNrTW9kZVwiIHZhbHVlPVwiYmluYXJ5XCJcbiAgICAgICAgICAgICAgICAgICAgIFsobmdNb2RlbCldPVwibWFza01vZGVcIj48L3AtcmFkaW9CdXR0b24+XG4gICAgICA8bGFiZWwgZm9yPVwibWFzay1iaW5hcnlcIj5CaW5hcnkgPHNwYW4gY2xhc3M9XCJ0ZXh0LWNvbG9yLXNlY29uZGFyeVwiPihyZWdpb25zIDEsIGJhY2tncm91bmQgMCk8L3NwYW4+PC9sYWJlbD5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2IGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgZ2FwLTJcIj5cbiAgICAgIDxwLXJhZGlvQnV0dG9uIGlucHV0SWQ9XCJtYXNrLW11bHRpY2xhc3NcIiBuYW1lPVwibWFza01vZGVcIiB2YWx1ZT1cIm11bHRpY2xhc3NcIlxuICAgICAgICAgICAgICAgICAgICAgWyhuZ01vZGVsKV09XCJtYXNrTW9kZVwiPjwvcC1yYWRpb0J1dHRvbj5cbiAgICAgIDxsYWJlbCBmb3I9XCJtYXNrLW11bHRpY2xhc3NcIj5NdWx0aS1jbGFzcyA8c3BhbiBjbGFzcz1cInRleHQtY29sb3Itc2Vjb25kYXJ5XCI+KHVuaXF1ZSBpZCBwZXIgcmVnaW9uLCBiYWNrZ3JvdW5kIDApPC9zcGFuPjwvbGFiZWw+XG4gICAgPC9kaXY+XG4gIDwvZGl2PlxuXG4gIDwhLS0gQnVzeTogcHJvZ3Jlc3Mgd2hpbGUgdGhlIHdvcmtlciByYXN0ZXJpemVzICsgZW5jb2RlcyB0aGUgUE5HLiAtLT5cbiAgPGRpdiAqbmdJZj1cIm1hc2tCdXN5XCIgY2xhc3M9XCJmbGV4IGZsZXgtY29sdW1uIGdhcC0zIHB0LTJcIj5cbiAgICA8c3Bhbj57eyBtYXNrRW5jb2RpbmcgPyAnRW5jb2RpbmcgUE5H4oCmJyA6ICdSYXN0ZXJpemluZyByZWdpb25z4oCmJyB9fTwvc3Bhbj5cbiAgICA8cC1wcm9ncmVzc0JhciBbbW9kZV09XCJtYXNrRW5jb2RpbmcgPyAnaW5kZXRlcm1pbmF0ZScgOiAnZGV0ZXJtaW5hdGUnXCJcbiAgICAgICAgICAgICAgICAgICBbdmFsdWVdPVwibWFza1Byb2dyZXNzXCJcbiAgICAgICAgICAgICAgICAgICBbc3R5bGVdPVwie2hlaWdodDogJzAuNzVyZW0nfVwiPjwvcC1wcm9ncmVzc0Jhcj5cbiAgPC9kaXY+XG5cbiAgPG5nLXRlbXBsYXRlIHBUZW1wbGF0ZT1cImZvb3RlclwiPlxuICAgIDxwLWJ1dHRvbiAqbmdJZj1cIiFtYXNrQnVzeVwiIGxhYmVsPVwiQ2FuY2VsXCIgaWNvbj1cInBpIHBpLXRpbWVzXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgIChvbkNsaWNrKT1cInNob3dTYXZlTWFza0RpYWxvZyA9IGZhbHNlXCI+PC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b24gKm5nSWY9XCIhbWFza0J1c3lcIiBsYWJlbD1cIkRvd25sb2FkXCIgaWNvbj1cInBpIHBpLWRvd25sb2FkXCJcbiAgICAgICAgICAgICAgW2Rpc2FibGVkXT1cIiFzYXZlTWFza0ZpbGVuYW1lIHx8ICFzYXZlTWFza0ZpbGVuYW1lLnRyaW0oKVwiXG4gICAgICAgICAgICAgIChvbkNsaWNrKT1cImNvbmZpcm1TYXZlTWFzaygpXCI+PC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b24gKm5nSWY9XCJtYXNrQnVzeVwiIGxhYmVsPVwiQ2FuY2VsXCIgaWNvbj1cInBpIHBpLXRpbWVzXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgIChvbkNsaWNrKT1cImNhbmNlbFNhdmVNYXNrKClcIj48L3AtYnV0dG9uPlxuICA8L25nLXRlbXBsYXRlPlxuPC9wLWRpYWxvZz5cblxuPHAtZGlhbG9nIGhlYWRlcj1cIlJlZ2lvbiBjb2xvdXJcIiBbKHZpc2libGUpXT1cInNob3dDb2xvckRpYWxvZ1wiIFttb2RhbF09XCJmYWxzZVwiIGFwcGVuZFRvPVwiYm9keVwiXG4gICAgICAgICAgW3N0eWxlXT1cInt3aWR0aDogJzI0cmVtJ31cIiBbZHJhZ2dhYmxlXT1cInRydWVcIiBbcmVzaXphYmxlXT1cImZhbHNlXCI+XG4gIDxkaXYgY2xhc3M9XCJmbGV4IGZsZXgtY29sdW1uIGdhcC0zIHB0LTJcIj5cbiAgICA8c3Bhbj5PdXRsaW5lIGNvbG91ciBwZXIgY2xhc3MgZm9yIHRoZVxuICAgICAgPGI+e3sgc2VsZWN0ZWRSZWdpb25zPy5sZW5ndGggfHwgMCB9fTwvYj4gc2VsZWN0ZWQgcmVnaW9uKHMpOjwvc3Bhbj5cbiAgICA8ZGl2ICpuZ0Zvcj1cImxldCBlZGl0IG9mIGNsYXNzQ29sb3JFZGl0czsgbGV0IGkgPSBpbmRleFwiXG4gICAgICAgICBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIGdhcC0zXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cImZvbnQtbWVkaXVtIGZsZXgtMVwiPnt7IGVkaXQubGFiZWwgfHwgJ1VuY2xhc3NpZmllZCcgfX08L3NwYW4+XG4gICAgICA8aGV4LWNvbG9yLXBpY2tlciBbY29sb3JdPVwiZWRpdC5jb2xvclwiXG4gICAgICAgICAgICAgICAgICAgICAgICAoY29sb3JDaGFuZ2UpPVwiY2xhc3NDb2xvckVkaXRzW2ldLmNvbG9yID0gJGV2ZW50XCI+PC9oZXgtY29sb3ItcGlja2VyPlxuICAgIDwvZGl2PlxuICA8L2Rpdj5cbiAgPG5nLXRlbXBsYXRlIHBUZW1wbGF0ZT1cImZvb3RlclwiPlxuICAgIDxwLWJ1dHRvbiBsYWJlbD1cIkNhbmNlbFwiIGljb249XCJwaSBwaS10aW1lc1wiIFt0ZXh0XT1cInRydWVcIiAob25DbGljayk9XCJzaG93Q29sb3JEaWFsb2cgPSBmYWxzZVwiPlxuICAgIDwvcC1idXR0b24+XG4gICAgPHAtYnV0dG9uIGxhYmVsPVwiQXBwbHlcIiBpY29uPVwicGkgcGktY2hlY2tcIiAob25DbGljayk9XCJhcHBseUNvbG9yVG9TZWxlY3RlZCgpXCI+XG4gICAgPC9wLWJ1dHRvbj5cbiAgPC9uZy10ZW1wbGF0ZT5cbjwvcC1kaWFsb2c+XG5cbjwhLS0gTWFuYWdlIGFubm90YXRpb24gY2xhc3NlcyAoaml0LXVpIzcwKTogQ1JVRCArIGZhbGxiYWNrIHBhbGV0dGUgKyBtYXRjaGluZy9hdXRvLWFkZFxuICAgICBzZXR0aW5ncyArIHJlc2V0L2ltcG9ydC9leHBvcnQuIEVkaXRzIGEgd29ya2luZyBjb3B5LCBjb21taXR0ZWQgb24gQXBwbHkvRG9uZS4gLS0+XG48cC1kaWFsb2cgaGVhZGVyPVwiTWFuYWdlIGFubm90YXRpb24gY2xhc3Nlc1wiIFsodmlzaWJsZSldPVwic2hvd01hbmFnZURpYWxvZ1wiIFttb2RhbF09XCJ0cnVlXCIgYXBwZW5kVG89XCJib2R5XCJcbiAgICAgICAgICBbc3R5bGVdPVwie3dpZHRoOiAnMzRyZW0nfVwiIFtkcmFnZ2FibGVdPVwidHJ1ZVwiIFtyZXNpemFibGVdPVwiZmFsc2VcIj5cbiAgPGRpdiAqbmdJZj1cInByZXNldERyYWZ0XCIgY2xhc3M9XCJtYW5hZ2UtZGlhbG9nIGZsZXggZmxleC1jb2x1bW4gZ2FwLTMgcHQtMlwiPlxuICAgIDxkaXYgY2xhc3M9XCJtYW5hZ2UtY2xhc3NlcyBmbGV4IGZsZXgtY29sdW1uIGdhcC0yXCI+XG4gICAgICA8ZGl2ICpuZ0Zvcj1cImxldCBjIG9mIHByZXNldERyYWZ0LmNsYXNzZXM7IGxldCBpID0gaW5kZXhcIlxuICAgICAgICAgICBjbGFzcz1cIm1hbmFnZS1jbGFzcy1yb3cgZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgZ2FwLTJcIj5cbiAgICAgICAgPGhleC1jb2xvci1waWNrZXIgW2NvbG9yXT1cImMuY29sb3JcIiAoY29sb3JDaGFuZ2UpPVwiYy5jb2xvciA9ICRldmVudFwiPjwvaGV4LWNvbG9yLXBpY2tlcj5cbiAgICAgICAgPGlucHV0IHR5cGU9XCJ0ZXh0XCIgcElucHV0VGV4dCBjbGFzcz1cInAtaW5wdXR0ZXh0LXNtIGZsZXgtMVwiXG4gICAgICAgICAgICAgICBbKG5nTW9kZWwpXT1cImMubmFtZVwiIHBsYWNlaG9sZGVyPVwiQ2xhc3MgbmFtZVwiLz5cbiAgICAgICAgPGJ1dHRvbiBwQnV0dG9uIHR5cGU9XCJidXR0b25cIiBpY29uPVwicGkgcGktdHJhc2hcIlxuICAgICAgICAgICAgICAgIGNsYXNzPVwicC1idXR0b24tc20gcC1idXR0b24tdGV4dCBwLWJ1dHRvbi13YXJuaW5nXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlJlbW92ZSBjbGFzc1wiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKGNsaWNrKT1cInJlbW92ZVByZXNldENsYXNzKGkpXCI+PC9idXR0b24+XG4gICAgICA8L2Rpdj5cbiAgICAgIDxidXR0b24gcEJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgaWNvbj1cInBpIHBpLXBsdXNcIiBsYWJlbD1cIkFkZCBjbGFzc1wiXG4gICAgICAgICAgICAgIGNsYXNzPVwicC1idXR0b24tc20gcC1idXR0b24tdGV4dCBhbGlnbi1zZWxmLXN0YXJ0XCIgKGNsaWNrKT1cImFkZFByZXNldENsYXNzKClcIj48L2J1dHRvbj5cbiAgICA8L2Rpdj5cblxuICAgIDxkaXYgY2xhc3M9XCJtYW5hZ2Utc2VjdGlvbiBmbGV4IGZsZXgtY29sdW1uIGdhcC0yXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInNlY3Rpb24tdGl0bGUgZm9udC1tZWRpdW1cIj5GYWxsYmFjayBwYWxldHRlXG4gICAgICAgIDxzbWFsbCBjbGFzcz1cInRleHQtY29sb3Itc2Vjb25kYXJ5XCI+4oCUIGNvbG91cnMgZm9yIGNsYXNzZXMgbm90IGxpc3RlZDwvc21hbGw+PC9zcGFuPlxuICAgICAgPGRpdiBjbGFzcz1cImZhbGxiYWNrLXBhbGV0dGUgZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgZmxleC13cmFwIGdhcC0yXCI+XG4gICAgICAgIDxzcGFuICpuZ0Zvcj1cImxldCBjb2wgb2YgcHJlc2V0RHJhZnQuZmFsbGJhY2tQYWxldHRlOyBsZXQgaSA9IGluZGV4XCJcbiAgICAgICAgICAgICAgY2xhc3M9XCJmYWxsYmFjay1zd2F0Y2ggZmxleCBhbGlnbi1pdGVtcy1jZW50ZXJcIj5cbiAgICAgICAgICA8aGV4LWNvbG9yLXBpY2tlciBbY29sb3JdPVwiY29sXCIgKGNvbG9yQ2hhbmdlKT1cInByZXNldERyYWZ0LmZhbGxiYWNrUGFsZXR0ZVtpXSA9ICRldmVudFwiPjwvaGV4LWNvbG9yLXBpY2tlcj5cbiAgICAgICAgICA8aSBjbGFzcz1cInBpIHBpLXRpbWVzIGljb24taG92ZXIgbWwtMVwiIHBUb29sdGlwPVwiUmVtb3ZlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAoY2xpY2spPVwicmVtb3ZlRmFsbGJhY2tDb2xvcihpKVwiPjwvaT5cbiAgICAgICAgPC9zcGFuPlxuICAgICAgICA8YnV0dG9uIHBCdXR0b24gdHlwZT1cImJ1dHRvblwiIGljb249XCJwaSBwaS1wbHVzXCJcbiAgICAgICAgICAgICAgICBjbGFzcz1cInAtYnV0dG9uLXNtIHAtYnV0dG9uLXRleHRcIiBwVG9vbHRpcD1cIkFkZCBmYWxsYmFjayBjb2xvdXJcIlxuICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiIChjbGljayk9XCJhZGRGYWxsYmFja0NvbG9yKClcIj48L2J1dHRvbj5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuXG4gICAgPGRpdiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIGdhcC0yXCI+XG4gICAgICA8cC1jaGVja2JveCBbKG5nTW9kZWwpXT1cInByZXNldERyYWZ0LmF1dG9Qcm9tb3RlXCIgW2JpbmFyeV09XCJ0cnVlXCIgaW5wdXRJZD1cImF1dG9Qcm9tb3RlXCI+PC9wLWNoZWNrYm94PlxuICAgICAgPGxhYmVsIGZvcj1cImF1dG9Qcm9tb3RlXCI+QXV0by1hZGQgZGV0ZWN0ZWQgY2xhc3NlcyB0byB0aGlzIGxpc3QgYXMgdGhleSBhcHBlYXI8L2xhYmVsPlxuICAgIDwvZGl2PlxuXG4gICAgPGRpdiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIGdhcC0zXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cImZvbnQtbWVkaXVtXCI+TGFiZWwgbWF0Y2hpbmc8L3NwYW4+XG4gICAgICA8cC1zZWxlY3RCdXR0b24gW29wdGlvbnNdPVwibWF0Y2hNb2RlT3B0aW9uc1wiIFsobmdNb2RlbCldPVwicHJlc2V0RHJhZnQubWF0Y2hNb2RlXCJcbiAgICAgICAgICAgICAgICAgICAgICBvcHRpb25MYWJlbD1cImxhYmVsXCIgb3B0aW9uVmFsdWU9XCJ2YWx1ZVwiPjwvcC1zZWxlY3RCdXR0b24+XG4gICAgPC9kaXY+XG4gIDwvZGl2PlxuXG4gIDxuZy10ZW1wbGF0ZSBwVGVtcGxhdGU9XCJmb290ZXJcIj5cbiAgICA8ZGl2IGNsYXNzPVwibWFuYWdlLWZvb3RlciBmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBmbGV4LXdyYXAgZ2FwLTIgdy1mdWxsXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIGZsZXgtd3JhcCBnYXAtMVwiPlxuICAgICAgICA8cC1idXR0b24gbGFiZWw9XCJSZXNldFwiIGljb249XCJwaSBwaS1yZWZyZXNoXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICAob25DbGljayk9XCJyZXNldFByZXNldHNUb0RlZmF1bHRzKClcIj48L3AtYnV0dG9uPlxuICAgICAgICA8cC1idXR0b24gbGFiZWw9XCJJbXBvcnRcIiBpY29uPVwicGkgcGktdXBsb2FkXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICAob25DbGljayk9XCJwcmVzZXRGaWxlSW5wdXQuY2xpY2soKVwiPjwvcC1idXR0b24+XG4gICAgICAgIDxpbnB1dCAjcHJlc2V0RmlsZUlucHV0IHR5cGU9XCJmaWxlXCIgYWNjZXB0PVwiLmpzb24sYXBwbGljYXRpb24vanNvblwiIGhpZGRlblxuICAgICAgICAgICAgICAgKGNoYW5nZSk9XCJpbXBvcnRQcmVzZXRzKCRldmVudCk7IHByZXNldEZpbGVJbnB1dC52YWx1ZSA9ICcnXCIvPlxuICAgICAgICA8cC1idXR0b24gbGFiZWw9XCJFeHBvcnRcIiBpY29uPVwicGkgcGktZG93bmxvYWRcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cImV4cG9ydFByZXNldHMoKVwiPjwvcC1idXR0b24+XG4gICAgICA8L3NwYW4+XG4gICAgICA8c3BhbiBjbGFzcz1cImZsZXgtMVwiPjwvc3Bhbj5cbiAgICAgIDwhLS0gQXBwbHkgcGVyc2lzdHMgdG8gdGhlIHNlcnZlciBhbmQgY2xvc2VzOyBpdCByZXBsYWNlcyB0aGUgb2xkIEFwcGx5K0RvbmUgcGFpci4gLS0+XG4gICAgICA8cC1idXR0b24gbGFiZWw9XCJBcHBseVwiIGljb249XCJwaSBwaS1jaGVja1wiIChvbkNsaWNrKT1cImFwcGx5TWFuYWdlRGlhbG9nKHRydWUpXCI+PC9wLWJ1dHRvbj5cbiAgICA8L2Rpdj5cbiAgPC9uZy10ZW1wbGF0ZT5cbjwvcC1kaWFsb2c+XG5cbjxwLWRpYWxvZyBoZWFkZXI9XCJSZWdpb24gZWRpdG9yIGhlbHBcIiBbKHZpc2libGUpXT1cImRpc3BsYXlIZWxwRGlhbG9nXCIgW3N0eWxlXT1cInt3aWR0aDogJzUwdncnfVwiXG4gICAgICAgICAgW21heGltaXphYmxlXT1cInRydWVcIiBbZHJhZ2dhYmxlXT1cInRydWVcIiBhcHBlbmRUbz1cImJvZHlcIj5cbiAgPHA+XG4gICAgVGhlIFJlZ2lvbiBFZGl0b3IgbGlzdHMgZXZlcnkgcmVnaW9uIGRyYXduIG9uIHRoZSBjdXJyZW50bHkgZGlzcGxheWVkIGltYWdlLlxuICAgIE9wZW4gaXQgd2l0aCB0aGVcbiAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9yZWdpb24tZWRpdG9yLnN2Z1wiIHdpZHRoPVwiMTRcIiBoZWlnaHQ9XCIxNFwiIGFsdD1cIlJlZ2lvbiBFZGl0b3JcIi8+XG4gICAgYnV0dG9uIGluIHRoZSBkaWFncmFtIHRvb2xiYXI6IGl0IG9wZW5zIGFzIGEgZmxvYXRpbmcgZGlhbG9nIHlvdSBjYW4gZHJhZyxcbiAgICByZXNpemUsIGFuZCBsZWF2ZSBvcGVuIHdoaWxlIHlvdSBkcmF3IG9uIHRoZSBpbWFnZS5cbiAgICBSZWdpb25zIGFyZSB0cmFja2VkIDxiPnBlciBpbWFnZTwvYj46IHN3aXRjaGluZyB0byBhbm90aGVyIGltYWdlIGhpZGVzXG4gICAgdGhlc2UgYW5kIHNob3dzIHRoYXQgaW1hZ2UncyByZWdpb25zOyBzd2l0Y2hpbmcgYmFjayByZXN0b3JlcyB0aGVtLlxuICAgIEVkaXRzIGluIHRoZSB0YWJsZSBhcHBseSA8Yj5saXZlPC9iPiB0byB0aGUgZGlhZ3JhbSDigJQgdGhlcmUgaXMgbm8gc2F2ZSBvclxuICAgIGNhbmNlbC5cbiAgPC9wPlxuXG4gIDxoMz5DcmVhdGluZyByZWdpb25zPC9oMz5cbiAgPHA+XG4gICAgVXNlIHRoZSBkaWFncmFtIHRvb2xiYXIgdG9vbHMgdG8gZHJhdyBuZXcgcmVnaW9uczpcbiAgICA8aSBjbGFzcz1cInBpIHBpLXN0b3BcIj48L2k+IHJlY3RhbmdsZSxcbiAgICA8b2JqZWN0IGRhdGE9XCJhc3NldHMvaWNvbnMvcG9seWxpbmUuc3ZnXCIgd2lkdGg9XCIxNFwiIGhlaWdodD1cIjE0XCI+PC9vYmplY3Q+IHBvbHlsaW5lLFxuICAgIDxpIGNsYXNzPVwicGkgcGktcGVuY2lsXCI+PC9pPiBmcmVlZm9ybSBwYXRoLCBicnVzaCwgb3JcbiAgICA8b2JqZWN0IGRhdGE9XCJhc3NldHMvaWNvbnMvd2FuZC5zdmdcIiB3aWR0aD1cIjE0XCIgaGVpZ2h0PVwiMTRcIj48L29iamVjdD4gd2FuZC5cbiAgICBBbnl0aGluZyB5b3UgZHJhdyBhcHBlYXJzIGltbWVkaWF0ZWx5IGluIHRoZSB0YWJsZSwgd2l0aCB0aGUgY2xhc3MgbmFtZVxuICAgIFwiPGI+UmVnaW9uPC9iPlwiIGJ5IGRlZmF1bHQuXG4gIDwvcD5cblxuICA8aDM+VGhlIHRhYmxlIGNvbHVtbnM8L2gzPlxuICA8dWw+XG4gICAgPGxpPjxiPkNsYXNzPC9iPiDigJQgdGhlIHJlZ2lvbidzIGNsYXNzLiBQaWNrIGEgY2xhc3MgZnJvbSB0aGUgZHJvcGRvd24gKGl0c1xuICAgICAgY29sb3VyIGZvbGxvd3MpLCBvciB1c2UgdGhlIDxpIGNsYXNzPVwicGkgcGktcGVuY2lsXCI+PC9pPiBmb3IgYSBmcmVlLWZvcm0gbmFtZS48L2xpPlxuICAgIDxsaT48Yj5TdXJmYWNlPC9iPiDigJQgdGhlIHJlZ2lvbidzIGFyZWEsIGluIMK1bcKyL21twrIgd2hlbiB0aGUgaW1hZ2UgY2FycmllcyBhXG4gICAgICBwaHlzaWNhbCBwaXhlbCBzaXplLCBvdGhlcndpc2UgaW4gcHjCsi4gSG9sZXMgKGRvbnV0cykgYW5kIG11bHRpLXBhcnRcbiAgICAgIHJlZ2lvbnMgYXJlIGFjY291bnRlZCBmb3IuPC9saT5cbiAgICA8bGk+VGhlIHRyYXNoIGljb24gYXQgdGhlIGVuZCBvZiBhIHJvdyBkZWxldGVzIHRoYXQgb25lIHJlZ2lvbi48L2xpPlxuICAgIDxsaT5Vc2UgXCI8Yj54NTEyPC9iPlwiIHRvIHJvdW5kIGFsbCByZWN0YW5nbGUgd2lkdGhzL2hlaWdodHMgdG8gdGhlXG4gICAgICBuZWFyZXN0IG11bHRpcGxlIG9mIDUxMi48L2xpPlxuICA8L3VsPlxuICA8cD5SZWdpb24gZ2VvbWV0cnkgaXMgZWRpdGVkIG9uIHRoZSBkaWFncmFtIGl0c2VsZiAoZHJhZyB2ZXJ0aWNlcywgdGhlIHZlcnRleFxuICAgIHRvb2xzLCBicnVzaC93YW5kLCBhbmQgdGhlIHJpZ2h0LWNsaWNrIHJlZ2lvbiBhY3Rpb25zKSwgbm90IGluIHRoaXMgdGFibGUuPC9wPlxuXG4gIDxoMz5TZWxlY3Rpbmcgcm93czwvaDM+XG4gIDx1bD5cbiAgICA8bGk+Q2xpY2sgYSByb3cgdG8gc2VsZWN0IGl0LiBUaGUgY29ycmVzcG9uZGluZyByZWdpb24gaXMgaGlnaGxpZ2h0ZWQgaW4gdGhlXG4gICAgICBkaWFncmFtIChQbG90bHkgc2hvd3MgaXRzIGVkaXQgaGFuZGxlczsgdGhlIE9wZW5TZWFkcmFnb24gaW1hZ2Ugdmlld1xuICAgICAgaGlnaGxpZ2h0cyBpdCBhbmQgc2hvd3MgaXRzIGhhbmRsZXMgaW4gc2VsZWN0IG1vZGUpLjwvbGk+XG4gICAgPGxpPjxrYmQ+Q3RybDwva2JkPi88a2JkPkNtZDwva2JkPitjbGljayB0byBhZGQgb3IgcmVtb3ZlIGEgcm93IGZyb20gdGhlXG4gICAgICBzZWxlY3Rpb24uIDxrYmQ+U2hpZnQ8L2tiZD4rY2xpY2sgdG8gZXh0ZW5kIHRoZSBzZWxlY3Rpb24gaW4gYSByYW5nZS48L2xpPlxuICAgIDxsaT5TZWxlY3RpbmcgYSByZWdpb24gaW4gdGhlIGRpYWdyYW0gaGlnaGxpZ2h0cyB0aGUgbWF0Y2hpbmcgcm93IGluIHRoZVxuICAgICAgdGFibGUuPC9saT5cbiAgICA8bGk+Q2xpY2tzIG9uIHRoZSBjb2xvdXIgcGlja2VyLCB0aGUgdHJhc2ggaWNvbiwgb3IgdGhlIGFjdGl2ZSBjbGFzcy1lZGl0XG4gICAgICBpbnB1dCBkbyBub3QgY2hhbmdlIHRoZSBzZWxlY3Rpb24g4oCUIHRoYXQncyBzdGFuZGFyZCBQcmltZU5HIGJlaGF2aW91ci48L2xpPlxuICA8L3VsPlxuXG4gIDxoMz5CdWxrIGFjdGlvbnMgb24gdGhlIHNlbGVjdGVkIHJvd3M8L2gzPlxuICA8dWw+XG4gICAgPGxpPjxpIGNsYXNzPVwicGkgcGktY2hlY2stc3F1YXJlXCI+PC9pPiBTZWxlY3QgZXZlcnkgcmVnaW9uIGluIHRoZSB0YWJsZSBhdCBvbmNlIChhbHNvIGFcbiAgICAgIHJpZ2h0LWNsaWNrIGFjdGlvbiBvbiB0aGUgaW1hZ2UpLjwvbGk+XG4gICAgPGxpPjxpIGNsYXNzPVwicGkgcGktdGFnc1wiPjwvaT4gRWRpdCB0aGUgbGFiZWwgb24gZXZlcnkgc2VsZWN0ZWQgcm93IGF0IG9uY2UuPC9saT5cbiAgICA8bGk+PGkgY2xhc3M9XCJwaSBwaS10cmFzaFwiPjwvaT4gRGVsZXRlIGV2ZXJ5IHNlbGVjdGVkIHJlZ2lvbiDigJQgYm90aCBmcm9tXG4gICAgICB0aGUgdGFibGUgYW5kIHRoZSBkaWFncmFtLjwvbGk+XG4gICAgPGxpPjxpIGNsYXNzPVwicGkgcGktdGltZXMtY2lyY2xlXCI+PC9pPiBEZWxldGUgZXZlcnkgcmVnaW9uIG9uIHRoaXMgaW1hZ2UuPC9saT5cbiAgPC91bD5cblxuICA8aDM+QW5ub3RhdGlvbiBjbGFzc2VzICZhbXA7IGNvbG91cnMgKHByZXNldHMpPC9oMz5cbiAgPHA+XG4gICAgUHJlc2V0IGEgY2xhc3Mgb25jZSDigJQgYSA8Yj5uYW1lPC9iPiBhbmQgYSA8Yj5jb2xvdXI8L2I+IOKAlCBhbmQgZXZlcnkgcmVnaW9uIHdpdGhcbiAgICB0aGF0IGNsYXNzIG5hbWUgaXMgZHJhd24gaW4gdGhhdCBjb2xvdXIsIDxiPmluY2x1ZGluZyByZWdpb25zIGxvYWRlZCBmcm9tIGFcbiAgICBHZW9KU09OIGxhdGVyPC9iPi4gVGhlIHByZXNldCBjb2xvdXIgaXMgdGhlIHNvdXJjZSBvZiB0cnV0aDogb24gbG9hZCBpdFxuICAgIDxiPm92ZXJyaWRlcyB0aGUgY29sb3VyIHN0b3JlZCBpbiB0aGUgZmlsZTwvYj4sIHNvIGEgZ2l2ZW4gYW5ub3RhdGlvbiB0eXBlIGFsd2F5c1xuICAgIGxvb2tzIHRoZSBzYW1lIGFjcm9zcyBpbWFnZXMgYW5kIHNlc3Npb25zLlxuICA8L3A+XG4gIDxwPlNldCB1cCB5b3VyIGNsYXNzZXMgaW4gdGhlIGFubm90YXRpb24tY2xhc3MgbWFuYWdlciDigJRcbiAgICA8aSBjbGFzcz1cInBpIHBpLWNvZ1wiPjwvaT4gPGI+TWFuYWdlPC9iPjo8L3A+XG4gIDxvbD5cbiAgICA8bGk+PGI+QWRkIGNsYXNzPC9iPiwgdGhlbiB0eXBlIHRoZSBjbGFzcyA8Yj5uYW1lIGV4YWN0bHkgYXMgaXQgYXBwZWFycyBpbiB5b3VyXG4gICAgICBkYXRhPC9iPiAoZS5nLiA8Y29kZT5UdW1vcjwvY29kZT4pIGFuZCBwaWNrIGl0cyA8Yj5jb2xvdXI8L2I+IGZyb20gdGhlIHN3YXRjaC5cbiAgICAgIFJlcGVhdCBmb3IgZWFjaCBjbGFzcy48L2xpPlxuICAgIDxsaT5PcHRpb25hbGx5OiBlZGl0IHRoZSA8Yj5mYWxsYmFjayBwYWxldHRlPC9iPiAoY29sb3VycyBnaXZlbiB0byBjbGFzc2VzIHRoYXRcbiAgICAgIGFyZW4ndCBpbiB5b3VyIGxpc3QpLCB0b2dnbGUgPGI+YXV0by1hZGQ8L2I+IChhZGRzIHVubWF0Y2hlZCBjbGFzc2VzIGZvdW5kIG9uXG4gICAgICBsb2FkKSwgYW5kIGNob29zZSA8Yj5leGFjdDwvYj4gdnMuIDxiPm5vcm1hbGl6ZWQ8L2I+IG5hbWUgbWF0Y2hpbmcg4oCUIG5vcm1hbGl6ZWRcbiAgICAgIGlnbm9yZXMgY2FzZSBhbmQgc3Vycm91bmRpbmcgc3BhY2VzLCBzbyA8Y29kZT50dW1vcjwvY29kZT4gbWF0Y2hlc1xuICAgICAgPGNvZGU+VHVtb3I8L2NvZGU+LjwvbGk+XG4gICAgPGxpPjxiPkFwcGx5LjwvYj4gUHJlc2V0cyBhcmUgc2F2ZWQgPGI+cGVyIHVzZXI8L2I+IG9uIHRoZSBzZXJ2ZXIsIHNvIHRoZXkgZm9sbG93XG4gICAgICB5b3UgYWNyb3NzIGltYWdlcyBhbmQgc2Vzc2lvbnMuIFlvdSBjYW4gYWxzbyA8Yj5yZXNldCB0byBkZWZhdWx0czwvYj4gb3JcbiAgICAgIDxiPmltcG9ydCAvIGV4cG9ydDwvYj4gdGhlIHNldCBhcyBKU09OIHRvIHNoYXJlIGl0LjwvbGk+XG4gIDwvb2w+XG4gIDxwPlxuICAgIE5leHQgdGltZSB5b3Ugb3BlbiBvciA8aSBjbGFzcz1cInBpIHBpLXVwbG9hZFwiPjwvaT4gaW1wb3J0IGEgR2VvSlNPTiB3aG9zZSByZWdpb25zXG4gICAgY2FycnkgdGhvc2UgY2xhc3MgbmFtZXMsIGVhY2ggcmVnaW9uIGlzIHNob3duIGluIGl0cyBwcmVzZXQgY29sb3VyIGF1dG9tYXRpY2FsbHkg4oCUXG4gICAgbm8gcmUtY29sb3VyaW5nIG5lZWRlZC4gQSBjbGFzcyB0aGF0IGlzbid0IGluIHlvdXIgbGlzdCBpbnN0ZWFkIGdldHMgYSBzdGFibGVcbiAgICBjb2xvdXIgZnJvbSB0aGUgZmFsbGJhY2sgcGFsZXR0ZS5cbiAgPC9wPlxuICA8cD5EYXkgdG8gZGF5OjwvcD5cbiAgPHVsPlxuICAgIDxsaT5UaGUgPGI+Y2xhc3Mgc3RyaXA8L2I+IGFib3ZlIHRoZSB0YWJsZSBsaXN0cyB5b3VyIHByZXNldHMgd2l0aCB0aGVpciBjb2xvdXJzLlxuICAgICAgQ2xpY2sgYSBjbGFzcyB0byBtYWtlIGl0IDxiPmFjdGl2ZTwvYj4gKG5ldyByZWdpb25zIGluaGVyaXQgaXQpOyB3aXRoIHJvd3NcbiAgICAgIHNlbGVjdGVkLCBjbGlja2luZyBhIGNsYXNzIHJlLWNsYXNzaWZpZXMgdGhlbS48L2xpPlxuICAgIDxsaT5UaGUgPGI+Q2xhc3M8L2I+IGNvbHVtbiBkcm9wZG93biByZS1jbGFzc2lmaWVzIG9uZSByZWdpb24gdG8gYSBwcmVzZXQgKGl0c1xuICAgICAgY29sb3VyIGZvbGxvd3MpLCBvciB1c2UgdGhlIDxpIGNsYXNzPVwicGkgcGktcGVuY2lsXCI+PC9pPiBmb3IgYSBmcmVlLWZvcm0gbmFtZS48L2xpPlxuICAgIDxsaT5SZWNvbG91ciBhIGNsYXNzIGZyb20gaXRzIHN3YXRjaCAoY2xhc3Mgc3RyaXAgb3IgTWFuYWdlKSBhbmQgZXZlcnkgcmVnaW9uIG9mXG4gICAgICB0aGF0IGNsYXNzIHVwZGF0ZXMgYXQgb25jZS48L2xpPlxuICAgIDxsaT5EZWxldGluZyBhIGNsYXNzIHJlYXNzaWducyBpdHMgcmVnaW9ucyB0byB0aGUgZGVmYXVsdCA8Yj5SZWdpb248L2I+IGNsYXNzLjwvbGk+XG4gICAgPGxpPkEgY29sb3VyIHlvdSBzZXQgb24gYSA8Yj5zaW5nbGU8L2I+IHJlZ2lvbiAoPGkgY2xhc3M9XCJwaSBwaS1wYWxldHRlXCI+PC9pPlxuICAgICAgU2VsZWN0ZWQgcmVnaW9ucykgaXMga2VwdCBhbmQgaXMgbm90IG92ZXJyaWRkZW4gYnkgaXRzIGNsYXNzLjwvbGk+XG4gIDwvdWw+XG5cbiAgPGgzPlJlZ2lvbiBhY3Rpb25zIChyaWdodC1jbGljayB0aGUgZGlhZ3JhbSk8L2gzPlxuICA8cD5SaWdodC1jbGljayBhIHJlZ2lvbiDigJQgb3IgdGhlIGN1cnJlbnQgbXVsdGktc2VsZWN0aW9uIOKAlCBvbiB0aGUgZGlhZ3JhbSBmb3JcbiAgICB0aGUgXCJTZWxlY3RlZCByZWdpb24ocylcIiBhY3Rpb25zOjwvcD5cbiAgPHVsPlxuICAgIDxsaT48Yj5NZXJnZSAvIGdyb3VwPC9iPiDigJQgY29tYmluZSAyKyBzZWxlY3RlZCByZWdpb25zIGludG8gb25lIChpdCBtYXkgZW5kXG4gICAgICB1cCBtdWx0aS1wYXJ0IGlmIHRoZSBwaWVjZXMgZG9uJ3QgdG91Y2gpLjwvbGk+XG4gICAgPGxpPjxiPlVuZ3JvdXA8L2I+IOKAlCBzcGxpdCBhIG11bHRpLXBhcnQgcmVnaW9uIGJhY2sgaW50byBzZXBhcmF0ZSByZWdpb25zLjwvbGk+XG4gICAgPGxpPjxiPkludmVyc2U8L2I+IOKAlCByZXBsYWNlIHRoZSBzZWxlY3Rpb24gd2l0aCBldmVyeXRoaW5nIG91dHNpZGUgaXQgKHRoZVxuICAgICAgcmVnaW9ucyBiZWNvbWUgaG9sZXMgaW4gdGhlIGltYWdlIHJlY3RhbmdsZSkuPC9saT5cbiAgICA8bGk+PGI+U2ltcGxpZnk8L2I+IOKAlCByZWR1Y2UgdGhlIHZlcnRleCBjb3VudCAoRG91Z2xhc+KAk1BldWNrZXIpIGJ5IGEgcGl4ZWxcbiAgICAgIHRocmVzaG9sZDsgcGljayBhIHByZXNldCBvciBhIGN1c3RvbSB2YWx1ZS48L2xpPlxuICAgIDxsaT48Yj5Db252ZXJ0IHRvIELDqXppZXIgLyB0byBwb2x5Z29uPC9iPiBhbmQgPGI+RGVsZXRlPC9iPi48L2xpPlxuICA8L3VsPlxuXG4gIDxoMz5EaXNwbGF5IG9wdGlvbnM8L2gzPlxuICA8dWw+XG4gICAgPGxpPjxpIGNsYXNzPVwicGkgcGktdGFnXCI+PC9pPiBUb2dnbGUgc2hvd2luZyBjbGFzcyBsYWJlbHMgb24gZXZlcnlcbiAgICAgIHJlZ2lvbiBpbiB0aGUgZGlhZ3JhbSAoc2xvd2VyIHdpdGggbWFueSByZWdpb25zKS48L2xpPlxuICA8L3VsPlxuXG4gIDxoMz5JbXBvcnQgLyBleHBvcnQ8L2gzPlxuICA8dWw+XG4gICAgPGxpPjxpIGNsYXNzPVwicGkgcGktdXBsb2FkXCI+PC9pPiBJbXBvcnQgcmVnaW9ucyBmcm9tIGEgR2VvSlNPTiBmaWxlLjwvbGk+XG4gICAgPGxpPjxpIGNsYXNzPVwicGkgcGktZG93bmxvYWRcIj48L2k+IEV4cG9ydCB0aGUgY3VycmVudCBpbWFnZSdzIHJlZ2lvbnMgdG9cbiAgICAgIGEgR2VvSlNPTiBmaWxlLiAoVGhlIHBlci1pbWFnZSBjYWNoZSBpcyBpbi1tZW1vcnkgb25seSDigJQgZXhwb3J0aW5nIGlzXG4gICAgICBob3cgeW91IHBlcnNpc3QgcmVnaW9ucyBhY3Jvc3Mgc2Vzc2lvbnMuKTwvbGk+XG4gIDwvdWw+XG5cbiAgPG5nLXRlbXBsYXRlIHBUZW1wbGF0ZT1cImZvb3RlclwiPlxuICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktY2hlY2tcIiAoY2xpY2spPVwiZGlzcGxheUhlbHBEaWFsb2c9ZmFsc2VcIiBsYWJlbD1cIk9rXCIgc3R5bGVDbGFzcz1cInAtYnV0dG9uLXRleHRcIj5cbiAgICA8L3AtYnV0dG9uPlxuICA8L25nLXRlbXBsYXRlPlxuPC9wLWRpYWxvZz5cblxuIl19
942
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVnaW9uLWVkaXRvci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3JlZ2lvbi1lZGl0b3IvcmVnaW9uLWVkaXRvci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9zcmMvbGliL3JlZ2lvbi1lZGl0b3IvcmVnaW9uLWVkaXRvci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLGlCQUFpQixFQUNqQixTQUFTLEVBQ1QsVUFBVSxFQUNWLE1BQU0sRUFHTixTQUFTLEVBQ1QsTUFBTSxFQUNOLE1BQU0sR0FDUCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUM1RCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUNoRSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzdDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDbEQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ3BELE9BQU8sRUFBRSxZQUFZLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUN4RSxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDaEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDcEMsT0FBTyxFQUFFLE9BQU8sRUFB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LGtCQUFrQixrV0FDbEIsYUFBYSxrSkFDYixhQUFhLHVYQUNiLHFCQUFxQixtTkFDckIsb0JBQW9CLGdWQUNwQiwwQkFBMEIseVBBQzFCLHVCQUF1QixvT0FDdkIsMEJBQTBCLG9KQUMxQiw0QkFBNEIsb01BQzVCLHlCQUF5Qjs7MkZBT2hCLHFCQUFxQjtrQkEzQmpDLFNBQVM7K0JBQ0UsZUFBZSxjQUNiLElBQUksV0FDUDt3QkFDUCxZQUFZO3dCQUNaLFdBQVc7d0JBQ1gsWUFBWTt3QkFDWixZQUFZO3dCQUNaLG1CQUFtQjt3QkFDbkIsY0FBYzt3QkFDZCxlQUFlO3dCQUNmLGtCQUFrQjt3QkFDbEIsYUFBYTt3QkFDYixhQUFhO3dCQUNiLHFCQUFxQjt3QkFDckIsb0JBQW9CO3dCQUNwQiwwQkFBMEI7d0JBQzFCLHVCQUF1Qjt3QkFDdkIsMEJBQTBCO3dCQUMxQiw0QkFBNEI7d0JBQzVCLHlCQUF5QjtxQkFDMUIsbUJBR2dCLHVCQUF1QixDQUFDLE1BQU0sYUFDcEMsQ0FBQyx3QkFBd0IsQ0FBQzs7MEJBZ0dsQyxNQUFNOzJCQUFDLGlCQUFpQjsrS0E3RkYsWUFBWTtzQkFBcEMsU0FBUzt1QkFBQyxJQUFJIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIENoYW5nZURldGVjdG9yUmVmLFxuICBDb21wb25lbnQsXG4gIERlc3Ryb3lSZWYsXG4gIEluamVjdCxcbiAgT25EZXN0cm95LFxuICBPbkluaXQsXG4gIFZpZXdDaGlsZCxcbiAgaW5qZWN0LFxuICBOZ1pvbmUsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgbWFya0ZvckNoZWNrSW5ab25lIH0gZnJvbSAnLi4vdXRpbC9tYXJrLWZvci1jaGVjayc7XG5pbXBvcnQgeyB0YWtlVW50aWxEZXN0cm95ZWQgfSBmcm9tICdAYW5ndWxhci9jb3JlL3J4anMtaW50ZXJvcCc7XG5pbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgRm9ybXNNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5pbXBvcnQgeyBCdXR0b25Nb2R1bGUgfSBmcm9tICdwcmltZW5nL2J1dHRvbic7XG5pbXBvcnQgeyBDb25maXJtRGlhbG9nTW9kdWxlIH0gZnJvbSAncHJpbWVuZy9jb25maXJtZGlhbG9nJztcbmltcG9ydCB7IERyb3Bkb3duTW9kdWxlIH0gZnJvbSAncHJpbWVuZy9kcm9wZG93bic7XG5pbXBvcnQgeyBJbnB1dFRleHRNb2R1bGUgfSBmcm9tICdwcmltZW5nL2lucHV0dGV4dCc7XG5pbXBvcnQgeyBPdmVybGF5UGFuZWwsIE92ZXJsYXlQYW5lbE1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvb3ZlcmxheXBhbmVsJztcbmltcG9ydCB7IFRvb2xiYXJNb2R1bGUgfSBmcm9tICdwcmltZW5nL3Rvb2xiYXInO1xuaW1wb3J0IHsgVG9vbHRpcE1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvdG9vbHRpcCc7XG5pbXBvcnQgeyBzYXZlQXMgfSBmcm9tICdmaWxlLXNhdmVyJztcbmltcG9ydCB7IFN1YmplY3QsIFN1YnNjcmlwdGlvbiB9IGZyb20gJ3J4anMnO1xuXG5pbXBvcnQgeyBSZWN0YW5nbGUsIFJlZ2lvbiB9IGZyb20gJy4uL21vZGVscy9yZWdpb24nO1xuaW1wb3J0IHsgd2l0aFJlZ2lvblBhdGNoIH0gZnJvbSAnLi4vbW9kZWxzL3JlZ2lvbi1jbG9uZSc7XG5pbXBvcnQgeyBQcmVzZXRTZXQsIENsYXNzUHJlc2V0LCBkZWZhdWx0UHJlc2V0U2V0LCBwYXJzZVByZXNldFNldCB9IGZyb20gJy4uL21vZGVscy9jbGFzcy1wcmVzZXQnO1xuaW1wb3J0IHsgY29sb3JGb3JMYWJlbCwgcHJlc2V0S2V5IH0gZnJvbSAnLi4vc3RvcmUvY2xhc3MtY29sb3IudXRpbCc7XG5pbXBvcnQgeyBDb25maXJtYXRpb25TZXJ2aWNlLCBNZXNzYWdlU2VydmljZSwgU2hhcmVkTW9kdWxlIH0gZnJvbSAncHJpbWVuZy9hcGknO1xuaW1wb3J0IHsgSVJlZ2lvbkVkaXRvckFwaSwgUkVHSU9OX0VESVRPUl9BUEkgfSBmcm9tICcuLi9jb250cmFjdHMvcmVnaW9uLWVkaXRvci1hcGkuY29udHJhY3QnO1xuaW1wb3J0IHsgVklaX1RPQVNUX0tFWSB9IGZyb20gJy4uL3RvYXN0LW91dGxldHMnO1xuaW1wb3J0IHsgUmVnaW9uUGVyc2lzdGVuY2VTZXJ2aWNlIH0gZnJvbSAnLi9yZWdpb24tcGVyc2lzdGVuY2Uuc2VydmljZSc7XG5pbXBvcnQgeyBDbGFzc0NvbG9yRWRpdCwgUmVnaW9uQ29sb3JEaWFsb2dDb21wb25lbnQgfSBmcm9tICcuL3JlZ2lvbi1jb2xvci1kaWFsb2cvcmVnaW9uLWNvbG9yLWRpYWxvZy5jb21wb25lbnQnO1xuaW1wb3J0IHsgQ2xhc3Nlc1BhbmVsQ29tcG9uZW50IH0gZnJvbSAnLi9jbGFzc2VzLXBhbmVsL2NsYXNzZXMtcGFuZWwuY29tcG9uZW50JztcbmltcG9ydCB7IFJlZ2lvblRhYmxlQ29tcG9uZW50IH0gZnJvbSAnLi9yZWdpb24tdGFibGUvcmVnaW9uLXRhYmxlLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBTYXZlUmVnaW9uc0RpYWxvZ0NvbXBvbmVudCB9IGZyb20gJy4vc2F2ZS1yZWdpb25zLWRpYWxvZy9zYXZlLXJlZ2lvbnMtZGlhbG9nLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBTYXZlTWFza0RpYWxvZ0NvbXBvbmVudCB9IGZyb20gJy4vc2F2ZS1tYXNrLWRpYWxvZy9zYXZlLW1hc2stZGlhbG9nLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBNYW5hZ2VDbGFzc2VzRGlhbG9nQ29tcG9uZW50IH0gZnJvbSAnLi9tYW5hZ2UtY2xhc3Nlcy1kaWFsb2cvbWFuYWdlLWNsYXNzZXMtZGlhbG9nLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBSZWdpb25FZGl0b3JIZWxwQ29tcG9uZW50IH0gZnJvbSAnLi9yZWdpb24tZWRpdG9yLWhlbHAvcmVnaW9uLWVkaXRvci1oZWxwLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBNYXNrRXhwb3J0U2VydmljZSwgTWFza01vZGUgfSBmcm9tICcuL21hc2stZXhwb3J0LnNlcnZpY2UnO1xuaW1wb3J0IHsgUGl4ZWxTaXplLCBmb3JtYXRBcmVhLCBwaWNrTXBwLCByZWdpb25BcmVhUHggfSBmcm9tICcuL3JlZ2lvbi1tZXRyaWNzJztcblxuLyoqXG4gKiBUaGUgUmVnaW9uIEVkaXRvciAoYDxyZWdpb24tZWRpdG9yPmApOiB0aGUgdGFibGUgb2YgdGhlIGFjdGl2ZSBpbWFnZSdzXG4gKiBhbm5vdGF0aW9uIHJlZ2lvbnMgd2l0aCBjbGFzcyBwcmVzZXRzLCBjb2xvdXJzLCBsYWJlbHMsIEdlb0pTT04gaW1wb3J0IC9cbiAqIGV4cG9ydCAvIHNlcnZlciBzYXZlIGFuZCBtYXNrIGV4cG9ydC4gU2VsZi1jb250YWluZWQ6IGl0IHRhbGtzIHRvIHRoZSB2aWV3ZXJcbiAqIG9ubHkgdGhyb3VnaCB7QGxpbmsgUkVHSU9OX0VESVRPUl9BUEl9IChhbmQgdGhlIGhvc3QncyBSRUdJT05fSU9fUE9SVCksIHNvIGl0XG4gKiBoYXMgbm8gaW5wdXRzIG9yIG91dHB1dHMuXG4gKlxuICogT25QdXNoOiB0ZW1wbGF0ZSBldmVudHMgcmUtcmVuZGVyIGl0OyBzdGF0ZSB0aGF0IGxhbmRzIGFzeW5jaHJvbm91c2x5ICh0aGVcbiAqIEFQSSdzIHJlZ2lvbiAvIHNlbGVjdGlvbiAvIHByZXNldCAvIGltYWdlLW1ldGEgc3RyZWFtcywgc2F2ZSBhbmQgbWFzay1leHBvcnRcbiAqIHByb2dyZXNzLCBmaWxlIHJlYWRzKSBtYXJrcyBpdCBmb3IgY2hlY2suXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ3JlZ2lvbi1lZGl0b3InLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbXG4gICAgQ29tbW9uTW9kdWxlLFxuICAgIEZvcm1zTW9kdWxlLFxuICAgIFNoYXJlZE1vZHVsZSxcbiAgICBCdXR0b25Nb2R1bGUsXG4gICAgQ29uZmlybURpYWxvZ01vZHVsZSxcbiAgICBEcm9wZG93bk1vZHVsZSxcbiAgICBJbnB1dFRleHRNb2R1bGUsXG4gICAgT3ZlcmxheVBhbmVsTW9kdWxlLFxuICAgIFRvb2xiYXJNb2R1bGUsXG4gICAgVG9vbHRpcE1vZHVsZSxcbiAgICBDbGFzc2VzUGFuZWxDb21wb25lbnQsXG4gICAgUmVnaW9uVGFibGVDb21wb25lbnQsXG4gICAgU2F2ZVJlZ2lvbnNEaWFsb2dDb21wb25lbnQsXG4gICAgU2F2ZU1hc2tEaWFsb2dDb21wb25lbnQsXG4gICAgUmVnaW9uQ29sb3JEaWFsb2dDb21wb25lbnQsXG4gICAgTWFuYWdlQ2xhc3Nlc0RpYWxvZ0NvbXBvbmVudCxcbiAgICBSZWdpb25FZGl0b3JIZWxwQ29tcG9uZW50LFxuICBdLFxuICB0ZW1wbGF0ZVVybDogJy4vcmVnaW9uLWVkaXRvci5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL3JlZ2lvbi1lZGl0b3IuY29tcG9uZW50LnNjc3MnXSxcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG4gIHByb3ZpZGVyczogW1JlZ2lvblBlcnNpc3RlbmNlU2VydmljZV0sXG59KVxuZXhwb3J0IGNsYXNzIFJlZ2lvbkVkaXRvckNvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25EZXN0cm95IHtcbiAgQFZpZXdDaGlsZCgnb3AnKSBwcml2YXRlIG92ZXJsYXlQYW5lbCE6IE92ZXJsYXlQYW5lbDtcblxuICBwcm90ZWN0ZWQgcmVnaW9uczogUmVnaW9uW10gPSBbXTtcbiAgcHJvdGVjdGVkIHNlbGVjdGVkUmVnaW9uczogUmVnaW9uW10gPSBbXTtcbiAgcHJvdGVjdGVkIHNob3dTaGFwZUxhYmVsITogYm9vbGVhbjtcbiAgcHJvdGVjdGVkIHNoYXBlQ29sb3IhOiBzdHJpbmc7XG4gIHByb3RlY3RlZCBmaWxsQ29sb3IhOiBzdHJpbmc7XG4gIHByb3RlY3RlZCBkaXNwbGF5SGVscERpYWxvZyA9IGZhbHNlO1xuICBwcm90ZWN0ZWQgc2hvd0NvbG9yRGlhbG9nID0gZmFsc2U7XG4gIC8qKiBPbmUgY29sb3VyIGVkaXRvciBwZXIgdW5pcXVlIGNsYXNzIGFtb25nIHRoZSBzZWxlY3RlZCByZWdpb25zLCBzaG93biBpbiB0aGVcbiAgICogIFwiZWRpdCBjb2xvdXIgb2Ygc2VsZWN0ZWQgcmVnaW9uc1wiIGRpYWxvZy4gVW5jbGFzc2lmaWVkIHJlZ2lvbnMgKG5vIGxhYmVsKVxuICAgKiAgYXJlIGdyb3VwZWQgdW5kZXIgYSBzaW5nbGUgZW50cnkgd2l0aCBhbiBlbXB0eSBgbGFiZWxgLiAqL1xuICBwcm90ZWN0ZWQgY2xhc3NDb2xvckVkaXRzOiBDbGFzc0NvbG9yRWRpdFtdID0gW107XG4gIC8qKiBcIkVkaXQgY2xhc3Mgb24gc2VsZWN0ZWQgcm93c1wiIHBvcHVwOiBjaG9zZW4gZXhpc3RpbmcgY2xhc3MsIGFuZCBhIG5ldy1jbGFzcyBuYW1lLiAqL1xuICBwcm90ZWN0ZWQgYnVsa0NsYXNzID0gJyc7XG4gIHByb3RlY3RlZCBuZXdCdWxrQ2xhc3MgPSAnJztcblxuICAvLyDilIDilIAgYW5ub3RhdGlvbi1jbGFzcyBwcmVzZXRzIChqaXQtdWkjNzApIOKUgOKUgFxuICAvKiogTG9jYWwgbWlycm9yIG9mIHRoZSBwZXItdXNlciBwcmVzZXQgc2V0IChjaGlwIHN0cmlwLCBDbGFzcyBkcm9wZG93biwgZGlhbG9nKS4gKi9cbiAgcHJvdGVjdGVkIHByZXNldFNldDogUHJlc2V0U2V0ID0gZGVmYXVsdFByZXNldFNldCgpO1xuICAvKiogVGhlIGNsYXNzIGxhc3QgcGlja2VkIGluIHRoZSBwYW5lbDsgYSBjaGlwLWNsaWNrIGFwcGxpZXMgaXQgdG8gdGhlIHNlbGVjdGVkIHJvd3MuICovXG4gIHByb3RlY3RlZCBhY3RpdmVDbGFzczogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG4gIHByb3RlY3RlZCBzaG93TWFuYWdlRGlhbG9nID0gZmFsc2U7XG4gIC8qKiBXb3JraW5nIGNvcHkgZWRpdGVkIGluc2lkZSB0aGUgTWFuYWdlLWNsYXNzZXMgZGlhbG9nOyBjb21taXR0ZWQgb24gQXBwbHkvRG9uZS4gKi9cbiAgcHJvdGVjdGVkIHByZXNldERyYWZ0OiBQcmVzZXRTZXQgfCBudWxsID0gbnVsbDtcbiAgLyoqIGNsYXNzIG5hbWUgKG1hdGNoLW1vZGUga2V5ZWQpIOKGkiBudW1iZXIgb2YgcmVnaW9ucyBjdXJyZW50bHkgdXNpbmcgaXQuICovXG4gIHByb3RlY3RlZCBjbGFzc0NvdW50cyA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KCk7XG4gIC8qKiBGYWxsYmFjayBjbGFzcyB0aGF0IGEgcmVnaW9uIHJldmVydHMgdG8gd2hlbiBpdHMgY2xhc3MgaXMgZGVsZXRlZCAoaml0LXVpIzcwKS4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGRlZmF1bHRDbGFzc05hbWUgPSAnUmVnaW9uJztcbiAgLyoqIENsYXNzZXMgYXMgc2hvd24gaW4gdGhlIHBhbmVsOiBzb3J0ZWQgYnkgcmVnaW9uIGNvdW50IChkZXNjKSwgdGhlbiBuYW1lLiAqL1xuICBwcm90ZWN0ZWQgZGlzcGxheUNsYXNzZXM6IENsYXNzUHJlc2V0W10gPSBbXTtcblxuICBwcm90ZWN0ZWQgcGFnaW5hdG9yRmlyc3QgPSAwO1xuICBwcm90ZWN0ZWQgcGFnaW5hdG9yUm93cyA9IDEwO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgcm93c1BlclBhZ2VPcHRpb25zID0gWzEwLCAyNSwgNTBdO1xuICAvKiogUmVnaW9ucyB3aG9zZSBDbGFzcyBjZWxsIGlzIGN1cnJlbnRseSBpbiBlZGl0IG1vZGUg4oaSIHRoZSBsYWJlbCBiZWluZyB0eXBlZC5cbiAgICogIFRoZSBkcmFmdCBpcyBjb21taXR0ZWQgb24gRW50ZXIgYW5kIGRpc2NhcmRlZCBvbiBFc2NhcGUv4pyVLCBzbyB0eXBpbmcgbmV2ZXJcbiAgICogIHdyaXRlcyBpbnRvIHRoZSBzdG9yZSdzIHJlZ2lvbiAoUlQtMTgpLiBLZXllZCBieSBvYmplY3QgaWRlbnRpdHksIHdoaWNoXG4gICAqICBhdm9pZHMgbmVlZGluZyBhIHVuaXF1ZSBpZCBmaWVsZC4gKi9cbiAgcHJvdGVjdGVkIGVkaXRpbmdMYWJlbFJlZ2lvbnMgPSBuZXcgTWFwPFJlZ2lvbiwgc3RyaW5nPigpO1xuXG4gIHByb3RlY3RlZCBzaG93U2F2ZUFzRGlhbG9nID0gZmFsc2U7XG4gIHByb3RlY3RlZCBzYXZlQXNGaWxlbmFtZSA9ICcnO1xuICBwcm90ZWN0ZWQgc2F2ZUFzRmlsZUV4aXN0cyA9IGZhbHNlO1xuICAvKiogVHJ1ZSB3aGlsZSBhIEdlb0pTT04gcGVyc2lzdCBpcyBzZXJpYWxpemluZy91cGxvYWRpbmcg4oCUIGRyaXZlcyB0aGUgZGlhbG9nJ3NcbiAgICogIHByb2dyZXNzIGJhciBhbmQgQ2FuY2VsIGJ1dHRvbiAocGFyaXR5IHdpdGggdGhlIFNhdmUtbWFzayBkaWFsb2cpLiAqL1xuICBwcm90ZWN0ZWQgc2F2ZUFzQnVzeSA9IGZhbHNlO1xuICAvKiogVGhlIHJ1bm5pbmcgR2VvSlNPTiBzYXZlOyB1bnN1YnNjcmliaW5nIGNhbmNlbHMgaXQgKENhbmNlbCAvIGRlc3Ryb3kpLiAqL1xuICBwcml2YXRlIF9zYXZlQXNTdWI/OiBTdWJzY3JpcHRpb247XG4gIC8qKiBTYXZlLWFzIGZpbGUgbmFtZXMgdG8gY2hlY2sgZm9yIGFuIGV4aXN0aW5nIGZpbGUgKGRlYm91bmNlZCkuICovXG4gIHByaXZhdGUgcmVhZG9ubHkgc2F2ZUFzQ2hlY2skID0gbmV3IFN1YmplY3Q8c3RyaW5nPigpO1xuXG4gIHByb3RlY3RlZCBzaG93RXhwb3J0RGlhbG9nID0gZmFsc2U7XG4gIHByb3RlY3RlZCBleHBvcnRGaWxlbmFtZSA9ICcnO1xuXG4gIHByb3RlY3RlZCBzaG93U2F2ZU1hc2tEaWFsb2cgPSBmYWxzZTtcbiAgcHJvdGVjdGVkIHNhdmVNYXNrRmlsZW5hbWUgPSAnJztcbiAgLyoqIE1hc2sgdHlwZSBjaG9zZW4gaW4gdGhlIFNhdmUtbWFzayBkaWFsb2c6IGEgYmluYXJ5IGZvcmVncm91bmQvYmFja2dyb3VuZFxuICAgKiAgbWFzaywgb3IgYSBtdWx0aS1jbGFzcyBtYXNrIHdpdGggYSBkaXN0aW5jdCBpZCBwZXIgcmVnaW9uLiAqL1xuICBwcm90ZWN0ZWQgbWFza01vZGU6IE1hc2tNb2RlID0gJ2JpbmFyeSc7XG4gIC8qKiBUcnVlIHdoaWxlIHRoZSBtYXNrIHdvcmtlciBpcyByYXN0ZXJpemluZy9lbmNvZGluZyDigJQgZHJpdmVzIHRoZSBwcm9ncmVzc1xuICAgKiAgYmFyIGFuZCB0aGUgQ2FuY2VsIGJ1dHRvbiBpbiB0aGUgZGlhbG9nLiAqL1xuICBwcm90ZWN0ZWQgbWFza0J1c3kgPSBmYWxzZTtcbiAgLyoqIDDigJMxMDAgcmFzdGVyaXphdGlvbiBwcm9ncmVzczsgc3dpdGNoZXMgdG8gaW5kZXRlcm1pbmF0ZSBkdXJpbmcgZW5jb2RpbmcuICovXG4gIHByb3RlY3RlZCBtYXNrUHJvZ3Jlc3MgPSAwO1xuICBwcm90ZWN0ZWQgbWFza0VuY29kaW5nID0gZmFsc2U7XG4gIC8qKiBUaGUgcnVubmluZyBtYXNrIGV4cG9ydDsgdW5zdWJzY3JpYmluZyBjYW5jZWxzIGl0ICh0ZXJtaW5hdGVzIGl0cyB3b3JrZXIpLiAqL1xuICBwcml2YXRlIG1hc2tKb2I/OiBTdWJzY3JpcHRpb247XG5cbiAgLyoqXG4gICAqIFByaW1lTkcgcC10YWJsZSBtdWx0aS1zZWxlY3Rpb24gbW9kZSB0b2dnbGUuIFdoZW4gdHJ1ZSwgc2luZ2xlLWNsaWNrXG4gICAqIHNlbGVjdHMgb25lIHJvdyAocmVwbGFjaW5nIHByaW9yKTsgbWV0YS9jdHJsLWNsaWNrIHRvZ2dsZXMgYSByb3cgaW4vb3V0XG4gICAqIG9mIHRoZSBzZWxlY3Rpb24uIFdoZW4gZmFsc2UsIGV2ZXJ5IGNsaWNrIHRvZ2dsZXMuIERlZmF1bHQgdHJ1ZSB0b1xuICAgKiBtYXRjaCB0aGUgbW9zdCBjb21tb24gc3ByZWFkc2hlZXQtbGlrZSBVWC5cbiAgICovXG4gIHByb3RlY3RlZCBtZXRhS2V5ID0gdHJ1ZTtcblxuICBwcml2YXRlIF91cGRhdGluZ0Zyb21FZGl0b3IgPSBmYWxzZTtcbiAgcHJpdmF0ZSBfc3VwcHJlc3NTZWxlY3Rpb25TeW5jVG9QbG90ID0gZmFsc2U7XG4gIHByaXZhdGUgcmVhZG9ubHkgZGVzdHJveVJlZiA9IGluamVjdChEZXN0cm95UmVmKTtcbiAgcHJpdmF0ZSByZWFkb25seSBjZHIgPSBpbmplY3QoQ2hhbmdlRGV0ZWN0b3JSZWYpO1xuICBwcml2YXRlIHJlYWRvbmx5IHpvbmUgPSBpbmplY3QoTmdab25lKTtcblxuICAvKiogUmUtcmVuZGVyIHRoaXMgT25QdXNoIHZpZXcsIHJlLWVudGVyaW5nIHRoZSB6b25lIHdoZW4gdGhlIGNoYW5nZSBjYW1lIGZyb20gb3V0c2lkZSBpdC4gKi9cbiAgcHJpdmF0ZSBjaGFuZ2VkKCk6IHZvaWQge1xuICAgIG1hcmtGb3JDaGVja0luWm9uZSh0aGlzLmNkciwgdGhpcy56b25lKTtcbiAgfVxuXG4gIC8qKiBQaHlzaWNhbCBwaXhlbCBzaXplICjCtW0vcGl4ZWwpIG9mIHRoZSBhY3RpdmUgaW1hZ2UsIGZvciByZWdpb24gYXJlYXMgaW5cbiAgICogIMK1bcKyOyBlbXB0eSB3aGVuIHRoZSBmb3JtYXQgcmVwb3J0cyBubyBwaHlzaWNhbCBzaXplLiAqL1xuICBwcm90ZWN0ZWQgbXBwOiBQaXhlbFNpemUgPSB7fTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBASW5qZWN0KFJFR0lPTl9FRElUT1JfQVBJKSBwcml2YXRlIHJlZ2lvbkFwaTogSVJlZ2lvbkVkaXRvckFwaSxcbiAgICBwcml2YXRlIG1lc3NhZ2VTZXJ2aWNlOiBNZXNzYWdlU2VydmljZSxcbiAgICBwcml2YXRlIGNvbmZpcm1hdGlvblNlcnZpY2U6IENvbmZpcm1hdGlvblNlcnZpY2UsXG4gICAgcHJpdmF0ZSBwZXJzaXN0ZW5jZTogUmVnaW9uUGVyc2lzdGVuY2VTZXJ2aWNlLFxuICAgIHByaXZhdGUgbWFza0V4cG9ydDogTWFza0V4cG9ydFNlcnZpY2UsXG4gICkge31cblxuICBuZ09uSW5pdCgpIHtcbiAgICB0aGlzLnNob3dTaGFwZUxhYmVsID0gdGhpcy5yZWdpb25BcGkuZ2V0U2hvd1NoYXBlTGFiZWwoKTtcbiAgICB0aGlzLnNoYXBlQ29sb3IgPSB0aGlzLnJlZ2lvbkFwaS5nZXRTaGFwZUNvbG9yKCk7XG4gICAgdGhpcy5maWxsQ29sb3IgPSB0aGlzLnJlZ2lvbkFwaS5nZXRGaWxsQ29sb3IoKTtcblxuICAgIC8vIEFubm90YXRpb24tY2xhc3MgcHJlc2V0cyAoaml0LXVpIzcwKTogbWlycm9yIHRoZSBzZXQgZm9yIHRoZSBjaGlwIHN0cmlwLFxuICAgIC8vIHRoZSBDbGFzcyBkcm9wZG93biBhbmQgdGhlIG1hbmFnZSBkaWFsb2cuIEd1YXJkZWQgd2l0aCBvcHRpb25hbCBjYWxscyBzb1xuICAgIC8vIHBhcnRpYWwgQVBJIG1vY2tzIGluIHRlc3RzICh3aGljaCBvbWl0IHRoZSBwcmVzZXQgbWV0aG9kcykgZG9uJ3QgdGhyb3cuXG4gICAgY29uc3QgaW5pdGlhbFByZXNldHMgPSB0aGlzLnJlZ2lvbkFwaS5nZXRQcmVzZXRTZXQ/LigpO1xuICAgIGlmIChpbml0aWFsUHJlc2V0cykgdGhpcy5wcmVzZXRTZXQgPSBpbml0aWFsUHJlc2V0cztcbiAgICBjb25zdCBwcmVzZXRzJCA9IHRoaXMucmVnaW9uQXBpLmdldFByZXNldFNldCQ/LigpO1xuICAgIGlmIChwcmVzZXRzJCkge1xuICAgICAgcHJlc2V0cyQucGlwZSh0YWtlVW50aWxEZXN0cm95ZWQodGhpcy5kZXN0cm95UmVmKSkuc3Vic2NyaWJlKChzZXQpID0+IHtcbiAgICAgICAgaWYgKHNldCkgdGhpcy5wcmVzZXRTZXQgPSBzZXQ7XG4gICAgICAgIHRoaXMudXBkYXRlRGlzcGxheUNsYXNzZXMoKTtcbiAgICAgICAgdGhpcy5jaGFuZ2VkKCk7XG4gICAgICB9KTtcbiAgICB9XG4gICAgLy8gU2VlZCBmcm9tIHRoZSB2aXN1YWxpemVyJ3MgY3VycmVudCByZWdpb25zIOKAlCBhbHJlYWR5IHNjb3BlZCB0byB0aGVcbiAgICAvLyBhY3RpdmUgaW1hZ2UgYnkgdGhlIHBlci1pbWFnZSBjYWNoZSwgYW5kIGhhbmRlZCBiYWNrIGFzIG5ldXRyYWwgUmVnaW9uXG4gICAgLy8gb2JqZWN0cyAobm8gYmFja2VuZCBzaGFwZSBmb3JtYXQgaW52b2x2ZWQpLlxuICAgIC8vIEFubm90YXRpb24gcmVnaW9ucyBvbmx5IOKAlCBpbnRlbnNpdHktcHJvZmlsZSBsaW5lcyBhcmUgb3duZWQgYnkgdGhlXG4gICAgLy8gaW50ZW5zaXR5IHRvb2wgYW5kIGV4Y2x1ZGVkIGJ5IHRoZSBjb250cmFjdCwgc28gdGhlIGVkaXRvciBuZXZlciBzZWVzIHRoZW0uXG4gICAgdGhpcy5yZWdpb25zID0gdGhpcy5hcHBseVJlZ2lvbkNvbG9ycyh0aGlzLnJlZ2lvbkFwaS5nZXRBbm5vdGF0aW9uUmVnaW9ucygpKTtcbiAgICB0aGlzLnN5bmNDbGFzc2VzRnJvbVJlZ2lvbnModGhpcy5yZWdpb25zKTtcbiAgICB0aGlzLnJlY29tcHV0ZUNsYXNzQ291bnRzKCk7XG5cbiAgICAvLyBUaGUgdXBkYXRlIGV2ZW50IGlzIGp1c3QgYSBjaGFuZ2Ugc2lnbmFsOyByZS1yZWFkIHRoZSByZWdpb25zIGZyb20gdGhlXG4gICAgLy8gdmlzdWFsaXplciByYXRoZXIgdGhhbiBwYXJzaW5nIHdoYXRldmVyIHBheWxvYWQgaXQgY2Fycmllcy5cbiAgICB0aGlzLnJlZ2lvbkFwaVxuICAgICAgLmdldFJlZ2lvblVwZGF0ZUV2ZW50KClcbiAgICAgIC5waXBlKHRha2VVbnRpbERlc3Ryb3llZCh0aGlzLmRlc3Ryb3lSZWYpKVxuICAgICAgLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICAgIGlmICh0aGlzLl91cGRhdGluZ0Zyb21FZGl0b3IpIHJldHVybjtcbiAgICAgICAgY29uc3QgdXBkYXRlZCA9IHRoaXMuYXBwbHlSZWdpb25Db2xvcnModGhpcy5yZWdpb25BcGkuZ2V0QW5ub3RhdGlvblJlZ2lvbnMoKSk7XG4gICAgICAgIC8vIHByZXNlcnZlIHNlbGVjdGlvbiBmb3IgcmVnaW9ucyB0aGF0IHN0aWxsIGV4aXN0IChieSBpZCwgc2luY2UgbmFtZVxuICAgICAgICAvLyBpcyBhIHVzZXItZWRpdGFibGUgZGlzcGxheSBsYWJlbCBhbmQgbWF5IGNvbGxpZGUpXG4gICAgICAgIGNvbnN0IHNlbGVjdGVkSWRzID0gbmV3IFNldCh0aGlzLnNlbGVjdGVkUmVnaW9ucy5tYXAoKHIpID0+IHIuaWQpKTtcbiAgICAgICAgdGhpcy5yZWdpb25zID0gdXBkYXRlZDtcbiAgICAgICAgdGhpcy5zZWxlY3RlZFJlZ2lvbnMgPSB1cGRhdGVkLmZpbHRlcigocikgPT4gc2VsZWN0ZWRJZHMuaGFzKHIuaWQpKTtcbiAgICAgICAgdGhpcy5jbGFtcFBhZ2luYXRvckZpcnN0KCk7XG4gICAgICAgIHRoaXMuc3luY0NsYXNzZXNGcm9tUmVnaW9ucyh1cGRhdGVkKTtcbiAgICAgICAgdGhpcy5yZWNvbXB1dGVDbGFzc0NvdW50cygpO1xuICAgICAgICB0aGlzLmNoYW5nZWQoKTtcbiAgICAgIH0pO1xuXG4gICAgdGhpcy5wZXJzaXN0ZW5jZVxuICAgICAgLmZpbGVFeGlzdHModGhpcy5zYXZlQXNDaGVjayQpXG4gICAgICAucGlwZSh0YWtlVW50aWxEZXN0cm95ZWQodGhpcy5kZXN0cm95UmVmKSlcbiAgICAgIC5zdWJzY3JpYmUoKGV4aXN0cykgPT4ge1xuICAgICAgICB0aGlzLnNhdmVBc0ZpbGVFeGlzdHMgPSBleGlzdHM7XG4gICAgICAgIHRoaXMuY2hhbmdlZCgpO1xuICAgICAgfSk7XG5cbiAgICAvLyBQaHlzaWNhbCBwaXhlbCBzaXplIG9mIHRoZSBhY3RpdmUgaW1hZ2UgKGZvciByZWdpb24gYXJlYXMgaW4gwrVtwrIvbW3CsikuXG4gICAgdGhpcy5yZWdpb25BcGlcbiAgICAgIC5nZXRJbWFnZU1ldGEoKVxuICAgICAgLnBpcGUodGFrZVVudGlsRGVzdHJveWVkKHRoaXMuZGVzdHJveVJlZikpXG4gICAgICAuc3Vic2NyaWJlKChtZXRhKSA9PiB7XG4gICAgICAgIHRoaXMubXBwID0gcGlja01wcChtZXRhKTtcbiAgICAgICAgdGhpcy5jaGFuZ2VkKCk7XG4gICAgICB9KTtcblxuICAgIHRoaXMucmVnaW9uQXBpXG4gICAgICAuZ2V0U2VsZWN0ZWRSZWdpb25zJCgpXG4gICAgICAucGlwZSh0YWtlVW50aWxEZXN0cm95ZWQodGhpcy5kZXN0cm95UmVmKSlcbiAgICAgIC5zdWJzY3JpYmUoKHNlbGVjdGVkKSA9PiB7XG4gICAgICAgIGlmICh0aGlzLl9zdXBwcmVzc1NlbGVjdGlvblN5bmNUb1Bsb3QpIHJldHVybjtcbiAgICAgICAgLy8gTWFwIHRoZSBjb250cmFjdCdzIHNlbGVjdGVkIHJlZ2lvbnMgdG8gdGhlIGVkaXRvcidzIG93biBpbnN0YW5jZXMgYnkgaWQuXG4gICAgICAgIGNvbnN0IG5leHQgPSBzZWxlY3RlZC5tYXAoKHMpID0+IHRoaXMucmVnaW9ucy5maW5kKChyKSA9PiByLmlkID09PSBzLmlkKSkuZmlsdGVyKChyKTogciBpcyBSZWdpb24gPT4gISFyKTtcbiAgICAgICAgY29uc3Qgc2FtZSA9XG4gICAgICAgICAgbmV4dC5sZW5ndGggPT09IHRoaXMuc2VsZWN0ZWRSZWdpb25zLmxlbmd0aCAmJlxuICAgICAgICAgIG5leHQuZXZlcnkoKHIsIGkpID0+IHIuaWQgPT09IHRoaXMuc2VsZWN0ZWRSZWdpb25zW2ldPy5pZCk7XG4gICAgICAgIGlmICghc2FtZSkgdGhpcy5zZWxlY3RlZFJlZ2lvbnMgPSBuZXh0O1xuICAgICAgICAvLyBTY3JvbGwgdGhlIHBhZ2luYXRvciBzbyB0aGUgbW9zdC1yZWNlbnRseS1zZWxlY3RlZCByb3cgaXMgdmlzaWJsZS5cbiAgICAgICAgaWYgKG5leHQubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGNvbnN0IGxhc3RJZHggPSB0aGlzLnJlZ2lvbnMuZmluZEluZGV4KChyKSA9PiByLmlkID09PSBuZXh0W25leHQubGVuZ3RoIC0gMV0uaWQpO1xuICAgICAgICAgIGlmIChsYXN0SWR4ID49IDApIHtcbiAgICAgICAgICAgIGNvbnN0IHBhZ2VTdGFydCA9IE1hdGguZmxvb3IobGFzdElkeCAvIHRoaXMucGFnaW5hdG9yUm93cykgKiB0aGlzLnBhZ2luYXRvclJvd3M7XG4gICAgICAgICAgICBpZiAocGFnZVN0YXJ0ICE9PSB0aGlzLnBhZ2luYXRvckZpcnN0KSB0aGlzLnBhZ2luYXRvckZpcnN0ID0gcGFnZVN0YXJ0O1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB0aGlzLmNoYW5nZWQoKTtcbiAgICAgIH0pO1xuICB9XG5cbiAgLyoqIENhbmNlbHMgYSBydW5uaW5nIEdlb0pTT04gc2F2ZSAoYWJvcnRzIGl0cyB1cGxvYWQpIGFuZCBtYXNrIGV4cG9ydCAodGVybWluYXRlc1xuICAgKiAgaXRzIHdvcmtlcik7IHRoZSBBUEkgc3Vic2NyaXB0aW9ucyBlbmQgdGhyb3VnaCBgdGFrZVVudGlsRGVzdHJveWVkYC4gKi9cbiAgbmdPbkRlc3Ryb3koKSB7XG4gICAgdGhpcy5fc2F2ZUFzU3ViPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMubWFza0pvYj8udW5zdWJzY3JpYmUoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUYWJsZSDihpIgUGxvdDogUHJpbWVORyBmaXJlcyBvblJvd1NlbGVjdC9vblJvd1Vuc2VsZWN0IGFmdGVyIHVwZGF0aW5nXG4gICAqIGBzZWxlY3RlZFJlZ2lvbnNgLiBQdXNoIHRoZSBzZWxlY3RlZCByZWdpb25zIHRvIHRoZSBjb250cmFjdCBieSBpZGVudGl0eSDigJRcbiAgICogdGhlIHBhY2thZ2UgbWFwcyB0aGVtIHRvIGl0cyBpbnRlcm5hbCBpbmRleCBzcGFjZSAod2hpY2ggaW5jbHVkZXMgdGhlXG4gICAqIGludGVuc2l0eS1wcm9maWxlIGxpbmVzIHRoZSBlZGl0b3IgbmV2ZXIgc2VlcykuXG4gICAqL1xuICBwcm90ZWN0ZWQgb25TZWxlY3Rpb25DaGFuZ2VkKCkge1xuICAgIHRoaXMuX3N1cHByZXNzU2VsZWN0aW9uU3luY1RvUGxvdCA9IHRydWU7XG4gICAgdHJ5IHtcbiAgICAgIHRoaXMucmVnaW9uQXBpLnNldFNlbGVjdGVkUmVnaW9ucyh0aGlzLnNlbGVjdGVkUmVnaW9ucyA/PyBbXSk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIHRoaXMuX3N1cHByZXNzU2VsZWN0aW9uU3luY1RvUGxvdCA9IGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSb3dzIGZvciByZWdpb25zIGNvbWluZyBmcm9tIHRoZSB2aXN1YWxpemVyOiBuZXV0cmFsIGBSZWdpb25gcyB3aXRoIGJvdW5kcyxcbiAgICogY29sb3VyIGFuZCBsYWJlbCBhbHJlYWR5IHBvcHVsYXRlZC4gQSByZWdpb24gd2l0aG91dCBhIGNvbG91ciBpcyBzaG93biB3aXRoXG4gICAqIGl0cyBjbGFzc2lmaWNhdGlvbiBjb2xvdXIgKG9yIHRoZSBkZWZhdWx0IHNoYXBlIGNvbG91cikg4oCUIGFzIGEgY29weTogdGhlXG4gICAqIHN0b3JlJ3MgaW5zdGFuY2VzIGFyZSBzaGFyZWQgd2l0aCBpdHMgdW5kbyBoaXN0b3J5IGFuZCBtdXN0IG5vdCBiZSBjaGFuZ2VkXG4gICAqIGluIHBsYWNlIChSVC0xKS5cbiAgICovXG4gIHByaXZhdGUgYXBwbHlSZWdpb25Db2xvcnMocmVnaW9uczogUmVnaW9uW10pOiBSZWdpb25bXSB7XG4gICAgY29uc3QgY2xhc3NDb2xvcnMgPSB0aGlzLnJlZ2lvbkFwaS5nZXRDbGFzc2lmaWNhdGlvbkNvbG9ycygpO1xuICAgIHJldHVybiByZWdpb25zLm1hcCgocmVnaW9uKSA9PlxuICAgICAgcmVnaW9uLmNvbG9yXG4gICAgICAgID8gcmVnaW9uXG4gICAgICAgIDogd2l0aFJlZ2lvblBhdGNoKHJlZ2lvbiwgeyBjb2xvcjogY2xhc3NDb2xvcnMuZ2V0KHJlZ2lvbi5sYWJlbCA/PyAnJykgfHwgdGhpcy5zaGFwZUNvbG9yIH0pLFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogVGhlIGVkaXRvcidzIG9uZSB3cml0ZSBwYXRoIChSVC0xKTogbWFrZSBgbmV4dGAgdGhlIHRhYmxlJ3Mgcm93cyBhbmQgY29tbWl0XG4gICAqIHRoZW0gdG8gdGhlIHN0b3JlIOKAlCBsaXZlLCBhcyBvbmUgdW5kb2FibGUgc3RlcCAoaXNSZWdpb25TYXZlT249dHJ1ZTsgdGhlXG4gICAqIHJvdXRlciBrZWVwcyB0aGUgaW50ZW5zaXR5LXByb2ZpbGUgbGluZXMpLiBSb3dzIGFyZSB0aGUgc3RvcmUncyBpbnN0YW5jZXMsXG4gICAqIHNvIGFuIGVkaXQgcGFzc2VzIGNvcGllcyAoe0BsaW5rIHBhdGNoUmVnaW9uc30pLCBuZXZlciBjaGFuZ2VkIHJvd3MuIFRoZVxuICAgKiByZXN1bHRpbmcgcmVnaW9uLXVwZGF0ZSBldmVudCBpcyBpZ25vcmVkIChgX3VwZGF0aW5nRnJvbUVkaXRvcmApLCBzbyB0aGVcbiAgICogY2xhc3MgbGlzdCBhbmQgY291bnRzIGFyZSByZWZyZXNoZWQgaGVyZSAoYSB0eXBlZCBjbGFzcyBpcyBhZGRlZDsgaml0LXVpIzcwKS5cbiAgICovXG4gIHByaXZhdGUgY29tbWl0KG5leHQ6IFJlZ2lvbltdID0gdGhpcy5yZWdpb25zKTogdm9pZCB7XG4gICAgdGhpcy5yZWdpb25zID0gbmV4dDtcbiAgICB0aGlzLl91cGRhdGluZ0Zyb21FZGl0b3IgPSB0cnVlO1xuICAgIHRyeSB7XG4gICAgICB0aGlzLnJlZ2lvbkFwaS5zZXRBbm5vdGF0aW9uUmVnaW9ucyhuZXh0LCB0aGlzLnNob3dTaGFwZUxhYmVsLCB0cnVlLCB0aGlzLmZpbGxDb2xvcik7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIHRoaXMuX3VwZGF0aW5nRnJvbUVkaXRvciA9IGZhbHNlO1xuICAgIH1cbiAgICB0aGlzLnN5bmNDbGFzc2VzRnJvbVJlZ2lvbnMobmV4dCk7XG4gICAgdGhpcy5yZWNvbXB1dGVDbGFzc0NvdW50cygpO1xuICAgIHRoaXMuY2hhbmdlZCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvcHktb24td3JpdGUgZWRpdCAocmV2aWV3IFJULTEpLiBUaGUgZWRpdG9yJ3Mgcm93cyBhcmUgdGhlIHJlZ2lvbiBzdG9yZSdzXG4gICAqIGxpdmUgaW5zdGFuY2VzLCBhbmQgdGhlIHN0b3JlIHRha2VzIGl0cyB1bmRvIHNuYXBzaG90IG9ubHkgd2hlbiB0aGUgZWRpdCBpc1xuICAgKiBjb21taXR0ZWQg4oCUIHNvIGFuIGVkaXQgbXVzdCByZXBsYWNlIGEgcmVnaW9uIHdpdGggYSBwYXRjaGVkIGNvcHkgKGFuZCBhbnlcbiAgICogYm91bmRzIGl0IGNoYW5nZXMgd2l0aCBuZXcgYm91bmRzKSwgbmV2ZXIgbXV0YXRlIHRoZSBpbnN0YW5jZSB0aGUgc3RvcmVcbiAgICogaG9sZHMuIFJldHVybnMgdGhlIGNvcGllcyBieSBvcmlnaW5hbDsgdGhlIHNlbGVjdGlvbiBhbmQgYW4gb3BlbiBsYWJlbCBlZGl0XG4gICAqIGZvbGxvdyB0aGVtLlxuICAgKi9cbiAgcHJpdmF0ZSBwYXRjaFJlZ2lvbnMocGF0Y2hGb3I6IChyOiBSZWdpb24pID0+IFBhcnRpYWw8UmVnaW9uPiB8IG51bGwpOiBNYXA8UmVnaW9uLCBSZWdpb24+IHtcbiAgICBjb25zdCBjb3BpZXMgPSBuZXcgTWFwPFJlZ2lvbiwgUmVnaW9uPigpO1xuICAgIHRoaXMucmVnaW9ucyA9IHRoaXMucmVnaW9ucy5tYXAoKHIpID0+IHtcbiAgICAgIGNvbnN0IHBhdGNoID0gcGF0Y2hGb3Iocik7XG4gICAgICBpZiAoIXBhdGNoKSByZXR1cm4gcjtcbiAgICAgIGNvbnN0IGNvcHkgPSB3aXRoUmVnaW9uUGF0Y2gociwgcGF0Y2gpO1xuICAgICAgY29waWVzLnNldChyLCBjb3B5KTtcbiAgICAgIHJldHVybiBjb3B5O1xuICAgIH0pO1xuICAgIGlmIChjb3BpZXMuc2l6ZSkge1xuICAgICAgdGhpcy5zZWxlY3RlZFJlZ2lvbnMgPSAodGhpcy5zZWxlY3RlZFJlZ2lvbnMgPz8gW10pLm1hcCgocikgPT4gY29waWVzLmdldChyKSA/PyByKTtcbiAgICAgIGlmIChbLi4uY29waWVzLmtleXMoKV0uc29tZSgocikgPT4gdGhpcy5lZGl0aW5nTGFiZWxSZWdpb25zLmhhcyhyKSkpIHtcbiAgICAgICAgdGhpcy5lZGl0aW5nTGFiZWxSZWdpb25zID0gbmV3IE1hcChbLi4udGhpcy5lZGl0aW5nTGFiZWxSZWdpb25zXS5tYXAoKFtyLCBkXSkgPT4gW2NvcGllcy5nZXQocikgPz8gciwgZF0pKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGNvcGllcztcbiAgfVxuXG4gIC8qKiBTZXQgb25lIHJlZ2lvbidzIG91dGxpbmUgY29sb3VyIChhbiBleHBsaWNpdCBvdmVycmlkZSkgYW5kIGNvbW1pdCBsaXZlLiAqL1xuICBwcm90ZWN0ZWQgY2hhbmdlUmVnaW9uQ29sb3IocmVnaW9uOiBSZWdpb24sIGNvbG9yOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBpZiAoIWNvbG9yIHx8IHJlZ2lvbi5jb2xvciA9PT0gY29sb3IpIHJldHVybjtcbiAgICAvLyBjb2xvck92ZXJyaWRkZW46IGV4cGxpY2l0IHBlci1yZWdpb24gY29sb3VyIOKAlCBwcmVzZXJ2ZSBpdCBhZ2FpbnN0IHByZXNldCAocmUpYXBwbHkgKGppdC11aSM3MClcbiAgICB0aGlzLnBhdGNoUmVnaW9ucygocikgPT4gKHIgPT09IHJlZ2lvbiA/IHsgY29sb3IsIGNvbG9yT3ZlcnJpZGRlbjogdHJ1ZSB9IDogbnVsbCkpO1xuICAgIHRoaXMuY29tbWl0KCk7XG4gIH1cblxuICAvKipcbiAgICogQWZ0ZXIgYSBsYWJlbCBlZGl0IG9uIGByZWdpb25gOiBnaXZlIGV2ZXJ5IGxhYmVsbGVkIHJvdyB3aXRob3V0IGEgY29sb3VyIGl0c1xuICAgKiBjbGFzcyBjb2xvdXIgKHByZXNldCBvciBmYWxsYmFjaywgYXMgdGhlIHN0b3JlIHdpbGwpLCBhbmQgY29tbWl0IHdoZW5cbiAgICogYGNvbW1pdGAgaXMgc2V0LlxuICAgKi9cbiAgcHJvdGVjdGVkIGxhYmVsUmVnaW9uVXBkYXRlKHJlZ2lvbjogUmVnaW9uLCBjb21taXQgPSBmYWxzZSkge1xuICAgIHRoaXMuZmlsbENsYXNzQ29sb3JzKCk7XG4gICAgaWYgKGNvbW1pdCkgdGhpcy5jb21taXQoKTtcbiAgfVxuXG4gIC8qKiBMYWJlbGxlZCByb3dzIHdpdGhvdXQgYSBjb2xvdXIgZ2V0IHRoZWlyIGNsYXNzIGNvbG91ciAoY29waWVzKS4gKi9cbiAgcHJpdmF0ZSBmaWxsQ2xhc3NDb2xvcnMoKTogdm9pZCB7XG4gICAgdGhpcy5wYXRjaFJlZ2lvbnMoKHIpID0+ICghci5jb2xvciAmJiByLmxhYmVsID8geyBjb2xvcjogdGhpcy5jb2xvckZvck5hbWUoci5sYWJlbCkgfSA6IG51bGwpKTtcbiAgfVxuXG4gIC8qKiBUaGUgY3VycmVudCBwYWdlIG9mIHRoZSB0YWJsZS4gTWVtb2l6ZWQgb24gKHJlZ2lvbnMsIGZpcnN0LCByb3dzKSwgc29cbiAgICogIGNoYW5nZSBkZXRlY3Rpb24gZG9lc24ndCBoYW5kIHAtdGFibGUgYSBuZXcgYXJyYXkgZXZlcnkgdGljayAoUlQtMjApLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IHBhZ2VkUmVnaW9ucygpOiBSZWdpb25bXSB7XG4gICAgY29uc3Qga2V5ID0gdGhpcy5fcGFnZUtleTtcbiAgICBpZiAoa2V5LnJlZ2lvbnMgIT09IHRoaXMucmVnaW9ucyB8fCBrZXkuZmlyc3QgIT09IHRoaXMucGFnaW5hdG9yRmlyc3QgfHwga2V5LnJvd3MgIT09IHRoaXMucGFnaW5hdG9yUm93cykge1xuICAgICAgdGhpcy5fcGFnZUtleSA9IHsgcmVnaW9uczogdGhpcy5yZWdpb25zLCBmaXJzdDogdGhpcy5wYWdpbmF0b3JGaXJzdCwgcm93czogdGhpcy5wYWdpbmF0b3JSb3dzIH07XG4gICAgICB0aGlzLl9wYWdlID0gdGhpcy5yZWdpb25zLnNsaWNlKHRoaXMucGFnaW5hdG9yRmlyc3QsIHRoaXMucGFnaW5hdG9yRmlyc3QgKyB0aGlzLnBhZ2luYXRvclJvd3MpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fcGFnZTtcbiAgfVxuICBwcml2YXRlIF9wYWdlS2V5OiB7IHJlZ2lvbnM/OiBSZWdpb25bXTsgZmlyc3Q/OiBudW1iZXI7IHJvd3M/OiBudW1iZXIgfSA9IHt9O1xuICBwcml2YXRlIF9wYWdlOiBSZWdpb25bXSA9IFtdO1xuXG4gIHByb3RlY3RlZCBvblBhZ2VDaGFuZ2UoZXZlbnQ6IHsgZmlyc3Q/OiBudW1iZXI7IHJvd3M/OiBudW1iZXIgfSkge1xuICAgIHRoaXMucGFnaW5hdG9yRmlyc3QgPSBldmVudC5maXJzdCA/PyAwO1xuICAgIHRoaXMucGFnaW5hdG9yUm93cyA9IGV2ZW50LnJvd3MgPz8gdGhpcy5wYWdpbmF0b3JSb3dzO1xuICB9XG5cbiAgLyoqIFNlbGVjdCBldmVyeSByZWdpb24gaW4gdGhlIHRhYmxlIChhbmQgaGlnaGxpZ2h0IHRoZW0gb24gdGhlIGRpYWdyYW0pLiAqL1xuICBwcm90ZWN0ZWQgc2VsZWN0QWxsUmVnaW9ucygpIHtcbiAgICB0aGlzLnNlbGVjdGVkUmVnaW9ucyA9IFsuLi50aGlzLnJlZ2lvbnNdO1xuICAgIHRoaXMub25TZWxlY3Rpb25DaGFuZ2VkKCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgZGVsZXRlU2VsZWN0ZWRSZWdpb25zKCkge1xuICAgIGlmICh0aGlzLnNlbGVjdGVkUmVnaW9ucyAmJiB0aGlzLnNlbGVjdGVkUmVnaW9ucy5sZW5ndGggPiAwKSB7XG4gICAgICAvLyBGaWx0ZXIgYnkgaWQg4oCUIG9iamVjdCBpZGVudGl0eSBpc24ndCByZWxpYWJsZSBhY3Jvc3MgcmUtcGFyc2VzLFxuICAgICAgLy8gYW5kIG5hbWUgaXMgYSB1c2VyLWVkaXRhYmxlIGxhYmVsIHRoYXQgbWF5IGNvbGxpZGUuXG4gICAgICBjb25zdCByZW1vdmVkID0gbmV3IFNldCh0aGlzLnNlbGVjdGVkUmVnaW9ucy5tYXAoKHIpID0+IHIuaWQpKTtcbiAgICAgIHRoaXMuc2VsZWN0ZWRSZWdpb25zID0gW107XG4gICAgICB0aGlzLmNvbW1pdCh0aGlzLnJlZ2lvbnMuZmlsdGVyKChyKSA9PiAhcmVtb3ZlZC5oYXMoci5pZCkpKTtcbiAgICAgIHRoaXMuY2xhbXBQYWdpbmF0b3JGaXJzdCgpO1xuICAgICAgLy8gU3luYyB0aGUgY2xlYXJlZCBzZWxlY3Rpb24gd2l0aCB0aGUgcGxvdCdzIGhpZ2hsaWdodCBzdGF0ZS5cbiAgICAgIHRoaXMub25TZWxlY3Rpb25DaGFuZ2VkKCk7XG4gICAgfVxuICB9XG5cbiAgcHJvdGVjdGVkIGNsZWFyQWxsUmVnaW9ucygpIHtcbiAgICB0aGlzLmNvbmZpcm1hdGlvblNlcnZpY2UuY29uZmlybSh7XG4gICAgICBrZXk6ICdwb3NpdGlvbkRpYWxvZycsXG4gICAgICBtZXNzYWdlOiAnQXJlIHlvdSBzdXJlIHlvdSB3YW50IHRvIGRlbGV0ZSBhbGwgcmVnaW9ucz8nLFxuICAgICAgYWNjZXB0OiAoKSA9PiB7XG4gICAgICAgIHRoaXMuc2VsZWN0ZWRSZWdpb25zID0gW107XG4gICAgICAgIHRoaXMucGFnaW5hdG9yRmlyc3QgPSAwO1xuICAgICAgICB0aGlzLmNvbW1pdChbXSk7XG4gICAgICAgIHRoaXMub25TZWxlY3Rpb25DaGFuZ2VkKCk7XG4gICAgICB9LFxuICAgIH0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGRlbGV0ZVJlZ2lvbihzaGFwZUlkeDogbnVtYmVyKSB7XG4gICAgY29uc3QgcmVtb3ZlZCA9IHRoaXMucmVnaW9uc1tzaGFwZUlkeF07XG4gICAgaWYgKHJlbW92ZWQpIHtcbiAgICAgIHRoaXMuc2VsZWN0ZWRSZWdpb25zID0gdGhpcy5zZWxlY3RlZFJlZ2lvbnMuZmlsdGVyKChyKSA9PiByLmlkICE9PSByZW1vdmVkLmlkKTtcbiAgICB9XG4gICAgdGhpcy5jb21taXQodGhpcy5yZWdpb25zLmZpbHRlcigoXywgaSkgPT4gaSAhPT0gc2hhcGVJZHgpKTtcbiAgICB0aGlzLmNsYW1wUGFnaW5hdG9yRmlyc3QoKTtcbiAgICAvLyBSZS1lbWl0IHRoZSAocG9zc2libHkgdHJpbW1lZCkgc2VsZWN0aW9uIHNvIHRoZSBwbG90J3MgaGlnaGxpZ2h0IHN0YXlzXG4gICAgLy8gaW4gc3luYyB3aXRoIHdoYXQncyBzdGlsbCBpbiB0aGUgdGFibGUuXG4gICAgdGhpcy5vblNlbGVjdGlvbkNoYW5nZWQoKTtcbiAgfVxuXG4gIHByaXZhdGUgY2xhbXBQYWdpbmF0b3JGaXJzdCgpIHtcbiAgICBjb25zdCBtYXhGaXJzdCA9IE1hdGgubWF4KDAsIE1hdGguZmxvb3IoKHRoaXMucmVnaW9ucy5sZW5ndGggLSAxKSAvIHRoaXMucGFnaW5hdG9yUm93cykgKiB0aGlzLnBhZ2luYXRvclJvd3MpO1xuICAgIGlmICh0aGlzLnBhZ2luYXRvckZpcnN0ID4gbWF4Rmlyc3QpIHtcbiAgICAgIHRoaXMucGFnaW5hdG9yRmlyc3QgPSBtYXhGaXJzdDtcbiAgICB9XG4gIH1cbiAgcHJvdGVjdGVkIGNoYW5nZVNob3dTaGFwZUxhYmVsKHNob3dMYWJlbDogYm9vbGVhbikge1xuICAgIHRoaXMuY29tbWl0KCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaXNFZGl0aW5nTGFiZWwocmVnaW9uOiBSZWdpb24pOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5lZGl0aW5nTGFiZWxSZWdpb25zLmhhcyhyZWdpb24pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHN0YXJ0RWRpdExhYmVsKHJlZ2lvbjogUmVnaW9uLCBldmVudD86IEV2ZW50KTogdm9pZCB7XG4gICAgZXZlbnQ/LnN0b3BQcm9wYWdhdGlvbigpOyAvLyBkb24ndCB0b2dnbGUgcm93IHNlbGVjdGlvblxuICAgIC8vIEEgbmV3IG1hcCBwZXIgc3RhcnQvc3RvcCwgc28gdGhlIE9uUHVzaCB0YWJsZSBzZWVzIHRoZSBjaGFuZ2UuXG4gICAgdGhpcy5lZGl0aW5nTGFiZWxSZWdpb25zID0gbmV3IE1hcCh0aGlzLmVkaXRpbmdMYWJlbFJlZ2lvbnMpLnNldChyZWdpb24sIHJlZ2lvbi5sYWJlbCA/PyAnJyk7XG4gIH1cblxuICAvKiogVGhlIGxhYmVsIHR5cGVkIHNvIGZhciBmb3IgYSByb3cgaW4gZWRpdCBtb2RlIChubyByZS1yZW5kZXIgbmVlZGVkKS4gKi9cbiAgcHJvdGVjdGVkIHNldExhYmVsRHJhZnQocmVnaW9uOiBSZWdpb24sIHZhbHVlOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5lZGl0aW5nTGFiZWxSZWdpb25zLmhhcyhyZWdpb24pKSB0aGlzLmVkaXRpbmdMYWJlbFJlZ2lvbnMuc2V0KHJlZ2lvbiwgdmFsdWUpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHN0b3BFZGl0TGFiZWwocmVnaW9uOiBSZWdpb24sIGNvbW1pdDogYm9vbGVhbiwgZXZlbnQ/OiBFdmVudCk6IHZvaWQge1xuICAgIGV2ZW50Py5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICBjb25zdCBkcmFmdCA9IHRoaXMuZWRpdGluZ0xhYmVsUmVnaW9ucy5nZXQocmVnaW9uKTtcbiAgICBpZiAodGhpcy5lZGl0aW5nTGFiZWxSZWdpb25zLmhhcyhyZWdpb24pKSB7XG4gICAgICBjb25zdCBuZXh0ID0gbmV3IE1hcCh0aGlzLmVkaXRpbmdMYWJlbFJlZ2lvbnMpO1xuICAgICAgbmV4dC5kZWxldGUocmVnaW9uKTtcbiAgICAgIHRoaXMuZWRpdGluZ0xhYmVsUmVnaW9ucyA9IG5leHQ7XG4gICAgfVxuICAgIGlmIChjb21taXQpIHtcbiAgICAgIC8vIEEgY2hhbmdlZCBsYWJlbCBjb21taXRzIGFzIGEgcmVwbGFjZW1lbnQgcmVnaW9uLCBzbyBpdCBpcyB1bmRvYWJsZSAoUlQtMSkuXG4gICAgICBjb25zdCBlZGl0ZWQgPVxuICAgICAgICBkcmFmdCAhPT0gdW5kZWZpbmVkICYmIGRyYWZ0ICE9PSByZWdpb24ubGFiZWxcbiAgICAgICAgICA/ICh0aGlzLnBhdGNoUmVnaW9ucygocikgPT4gKHIgPT09IHJlZ2lvbiA/IHsgbGFiZWw6IGRyYWZ0IH0gOiBudWxsKSkuZ2V0KHJlZ2lvbikgPz8gcmVnaW9uKVxuICAgICAgICAgIDogcmVnaW9uO1xuICAgICAgdGhpcy5sYWJlbFJlZ2lvblVwZGF0ZShlZGl0ZWQsIHRydWUpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBSZWdpb24gYXJlYSBmb3IgZGlzcGxheSAowrVtwrIvbW3CsiB3aGVuIHRoZSBpbWFnZSBpcyBzY2FsZWQsIGVsc2UgcHjCsik7XG4gICAqICBlbXB0eSBmb3IgYSBkZWdlbmVyYXRlIHJlZ2lvbi4gU2VlIGByZWdpb24tbWV0cmljc2AuICovXG4gIHByb3RlY3RlZCByZWdpb25BcmVhKHJlZ2lvbjogUmVnaW9uKTogc3RyaW5nIHtcbiAgICByZXR1cm4gZm9ybWF0QXJlYShyZWdpb25BcmVhUHgocmVnaW9uKSwgdGhpcy5tcHApO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoaXMgbWV0aG9kIHJvdW5kcyBhbGwgdGhlIHJlY3RhbmdsZSByZWdpb25zIGxlbmd0aHMgdG8gbXVsdGlwbGUgb2YgNTEyXG4gICAqL1xuICBwcm90ZWN0ZWQgcm91bmRSZWN0YW5nbGVMZW5ndGhzKCkge1xuICAgIHRoaXMucGF0Y2hSZWdpb25zKChyZWdpb24pID0+IHtcbiAgICAgIGNvbnN0IHJlY3QgPSByZWdpb24uYm91bmRzO1xuICAgICAgaWYgKCEocmVjdCBpbnN0YW5jZW9mIFJlY3RhbmdsZSkpIHJldHVybiBudWxsO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgYm91bmRzOiBPYmplY3QuYXNzaWduKG5ldyBSZWN0YW5nbGUoKSwgcmVjdCwge1xuICAgICAgICAgIGhlaWdodDogTWF0aC5yb3VuZChyZWN0LmhlaWdodCAvIDUxMikgKiA1MTIsXG4gICAgICAgICAgd2lkdGg6IE1hdGgucm91bmQocmVjdC53aWR0aCAvIDUxMikgKiA1MTIsXG4gICAgICAgIH0pLFxuICAgICAgfTtcbiAgICB9KTtcbiAgICB0aGlzLmNvbW1pdCgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNob3dIZWxwKCkge1xuICAgIHRoaXMuZGlzcGxheUhlbHBEaWFsb2cgPSB0cnVlO1xuICB9XG5cbiAgLyoqIE9wZW4gdGhlIGNvbG91ciBkaWFsb2cgZm9yIHRoZSBjdXJyZW50bHktc2VsZWN0ZWQgcmVnaW9uKHMpLCBidWlsZGluZyBvbmVcbiAgICogIGNvbG91ciBwaWNrZXIgcGVyIHVuaXF1ZSBjbGFzcyBpbiB0aGUgc2VsZWN0aW9uLiBFYWNoIHBpY2tlciBpcyBzZWVkZWQgd2l0aFxuICAgKiAgdGhlIGZpcnN0IHNlbGVjdGVkIHJlZ2lvbiBvZiB0aGF0IGNsYXNzJ3MgY29sb3VyIChlbHNlIHRoZSBjbGFzcyBjb2xvdXIpLiAqL1xuICBwcm90ZWN0ZWQgb3BlbkNvbG9yRGlhbG9nKCkge1xuICAgIGlmICghdGhpcy5zZWxlY3RlZFJlZ2lvbnM/Lmxlbmd0aCkgcmV0dXJuO1xuICAgIGNvbnN0IHNlZWRCeUxhYmVsID0gbmV3IE1hcDxzdHJpbmcsIHN0cmluZz4oKTtcbiAgICBmb3IgKGNvbnN0IHJlZ2lvbiBvZiB0aGlzLnNlbGVjdGVkUmVnaW9ucykge1xuICAgICAgY29uc3QgbGFiZWwgPSByZWdpb24ubGFiZWw/LnRyaW0oKSA/PyAnJztcbiAgICAgIGlmIChzZWVkQnlMYWJlbC5oYXMobGFiZWwpKSBjb250aW51ZTtcbiAgICAgIHNlZWRCeUxhYmVsLnNldChsYWJlbCwgcmVnaW9uLmNvbG9yID8/IChsYWJlbCA/IHRoaXMuY29sb3JGb3JOYW1lKGxhYmVsKSA6IHRoaXMuc2hhcGVDb2xvcikpO1xuICAgIH1cbiAgICB0aGlzLmNsYXNzQ29sb3JFZGl0cyA9IFsuLi5zZWVkQnlMYWJlbF0ubWFwKChbbGFiZWwsIGNvbG9yXSkgPT4gKHsgbGFiZWwsIGNvbG9yIH0pKTtcbiAgICB0aGlzLnNob3dDb2xvckRpYWxvZyA9IHRydWU7XG4gIH1cblxuICAvKiogQXBwbHkgZWFjaCBjbGFzcydzIGNob3NlbiBjb2xvdXIgdG8gdGhlIHNlbGVjdGVkIHJlZ2lvbnMgb2YgdGhhdCBjbGFzcyAoYW5cbiAgICogIGV4cGxpY2l0IG92ZXJyaWRlKSBhbmQgY29tbWl0IGxpdmUuICovXG4gIHByb3RlY3RlZCBhcHBseUNvbG9yVG9TZWxlY3RlZChlZGl0czogQ2xhc3NDb2xvckVkaXRbXSA9IHRoaXMuY2xhc3NDb2xvckVkaXRzKSB7XG4gICAgY29uc3QgY29sb3JCeUxhYmVsID0gbmV3IE1hcChlZGl0cy5tYXAoKGUpID0+IFtlLmxhYmVsLCBlLmNvbG9yXSkpO1xuICAgIGNvbnN0IHNlbGVjdGVkID0gbmV3IFNldCh0aGlzLnNlbGVjdGVkUmVnaW9ucyA/PyBbXSk7XG4gICAgdGhpcy5wYXRjaFJlZ2lvbnMoKHJlZ2lvbikgPT4ge1xuICAgICAgaWYgKCFzZWxlY3RlZC5oYXMocmVnaW9uKSkgcmV0dXJuIG51bGw7XG4gICAgICBjb25zdCBsYWJlbCA9IHJlZ2lvbi5sYWJlbD8udHJpbSgpID8/ICcnO1xuICAgICAgY29uc3QgY29sb3IgPSBjb2xvckJ5TGFiZWwuZ2V0KGxhYmVsKTtcbiAgICAgIGlmICghY29sb3IpIHJldHVybiBudWxsO1xuICAgICAgLy8gZXhwbGljaXQgY29sb3VyIOKAlCBwcmVzZXJ2ZSBhZ2FpbnN0IHByZXNldCAocmUpYXBwbHkgKGppdC11aSM3MClcbiAgICAgIHJldHVybiB7IGNvbG9yLCBjb2xvck92ZXJyaWRkZW46IHRydWUgfTtcbiAgICB9KTtcbiAgICB0aGlzLmNvbW1pdCgpO1xuICAgIHRoaXMuc2hvd0NvbG9yRGlhbG9nID0gZmFsc2U7XG4gIH1cblxuICAvLyDilIDilIAgYW5ub3RhdGlvbi1jbGFzcyBwcmVzZXRzIChqaXQtdWkjNzApIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBSZXNvbHZlIHRoZSBkaXNwbGF5IGNvbG91ciBmb3IgYSBjbGFzcyBuYW1lIChwcmVzZXQgY29sb3VyIG9yIGRldGVybWluaXN0aWMgZmFsbGJhY2spLiAqL1xuICBwcm90ZWN0ZWQgY29sb3JGb3JOYW1lKG5hbWU/OiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHJldHVybiBuYW1lID8gY29sb3JGb3JMYWJlbChuYW1lLCB0aGlzLnByZXNldFNldCkgOiB0aGlzLnNoYXBlQ29sb3I7XG4gIH1cblxuICAvKipcbiAgICogRW5zdXJlIGV2ZXJ5IGNsYXNzIGxhYmVsIHByZXNlbnQgaW4gdGhlIGxvYWRlZCByZWdpb25zIGV4aXN0cyBpbiB0aGUgcHJlc2V0XG4gICAqIGxpc3QsIGF1dG8tYWRkaW5nIGFueSB0aGF0IGFyZSBtaXNzaW5nICh1c2luZyB0aGUgcmVnaW9uJ3MgYWxyZWFkeS1yZXNvbHZlZFxuICAgKiBjb2xvdXIpLiBUaGlzIG1ha2VzIGNsYXNzZXMgY2FycmllZCBpbiBhIGxvYWRlZC9pbXBvcnRlZCBHZW9KU09OIHNob3cgdXAgYXNcbiAgICogY2hpcHMgKyBDbGFzcy1kcm9wZG93biBvcHRpb25zIChhIHAtZHJvcGRvd24gY2FuJ3QgZGlzcGxheSBhIHZhbHVlIHRoYXQgaXNuJ3RcbiAgICogYW4gb3B0aW9uKSBhbmQgbGV0cyB0aGUgdXNlciBtYW5hZ2UgdGhlbS4gVGhlIHBsYWNlaG9sZGVyICdsZWdlbmQnIGFuZCBlbXB0eVxuICAgKiBsYWJlbHMgYXJlIGlnbm9yZWQuIChqaXQtdWkjNzApXG4gICAqL1xuICBwcml2YXRlIHN5bmNDbGFzc2VzRnJvbVJlZ2lvbnMocmVnaW9uczogUmVnaW9uW10pOiB2b2lkIHtcbiAgICBjb25zdCBrZXlPZiA9IChsOiBzdHJpbmcpID0+IHByZXNldEtleSh0aGlzLnByZXNldFNldCwgbCk7XG4gICAgY29uc3Qga25vd24gPSBuZXcgU2V0KHRoaXMucHJlc2V0U2V0LmNsYXNzZXMubWFwKChjKSA9PiBrZXlPZihjLm5hbWUpKSk7XG4gICAgY29uc3QgYWRkZWQgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgICBmb3IgKGNvbnN0IHIgb2YgcmVnaW9ucykge1xuICAgICAgY29uc3QgbGFiZWwgPSByLmxhYmVsPy50cmltKCk7XG4gICAgICBpZiAoIWxhYmVsIHx8IGxhYmVsID09PSAnbGVnZW5kJykgY29udGludWU7XG4gICAgICBjb25zdCBrID0ga2V5T2YobGFiZWwpO1xuICAgICAgaWYgKGtub3duLmhhcyhrKSB8fCBhZGRlZC5oYXMoaykpIGNvbnRpbnVlO1xuICAgICAgYWRkZWQuYWRkKGspO1xuICAgICAgdGhpcy5yZWdpb25BcGkudXBzZXJ0Q2xhc3Moe1xuICAgICAgICBuYW1lOiBsYWJlbCxcbiAgICAgICAgY29sb3I6IHIuY29sb3IgfHwgY29sb3JGb3JMYWJlbChsYWJlbCwgdGhpcy5wcmVzZXRTZXQpLFxuICAgICAgICBzb3VyY2U6ICdhdXRvJyxcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIC8vIOKUgOKUgCBEb2NrZWQgQ2xhc3NlcyBwYW5lbCAoaml0LXVpIzcwKSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICAvKiogUmVidWlsZCB0aGUgcGVyLWNsYXNzIHJlZ2lvbiBjb3VudHMgc2hvd24gaW4gdGhlIHBhbmVsLiAqL1xuICBwcml2YXRlIHJlY29tcHV0ZUNsYXNzQ291bnRzKCk6IHZvaWQge1xuICAgIGNvbnN0IGNvdW50cyA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KCk7XG4gICAgZm9yIChjb25zdCByIG9mIHRoaXMucmVnaW9ucykge1xuICAgICAgaWYgKCFyLmxhYmVsKSBjb250aW51ZTtcbiAgICAgIGNvbnN0IGsgPSBwcmVzZXRLZXkodGhpcy5wcmVzZXRTZXQsIHIubGFiZWwpO1xuICAgICAgY291bnRzLnNldChrLCAoY291bnRzLmdldChrKSA/PyAwKSArIDEpO1xuICAgIH1cbiAgICB0aGlzLmNsYXNzQ291bnRzID0gY291bnRzO1xuICAgIHRoaXMudXBkYXRlRGlzcGxheUNsYXNzZXMoKTtcbiAgfVxuXG4gIC8qKiBSZWJ1aWxkIHRoZSBwYW5lbCdzIGRpc3BsYXkgb3JkZXIg4oCUIG1vc3QtdXNlZCBjbGFzc2VzIGZpcnN0LCB0aGVuIGJ5IG5hbWUuICovXG4gIHByaXZhdGUgdXBkYXRlRGlzcGxheUNsYXNzZXMoKTogdm9pZCB7XG4gICAgdGhpcy5kaXNwbGF5Q2xhc3NlcyA9IFsuLi50aGlzLnByZXNldFNldC5jbGFzc2VzXS5zb3J0KChhLCBiKSA9PiB7XG4gICAgICBjb25zdCBkID0gdGhpcy5jbGFzc0NvdW50KGIubmFtZSkgLSB0aGlzLmNsYXNzQ291bnQoYS5uYW1lKTtcbiAgICAgIHJldHVybiBkICE9PSAwID8gZCA6IGEubmFtZS5sb2NhbGVDb21wYXJlKGIubmFtZSk7XG4gICAgfSk7XG4gIH1cblxuICAvKiogTnVtYmVyIG9mIHJlZ2lvbnMgY3VycmVudGx5IHVzaW5nIGEgY2xhc3MgKGtleWVkIGJ5IHRoZSBhY3RpdmUgbWF0Y2ggbW9kZSkuICovXG4gIHByb3RlY3RlZCBjbGFzc0NvdW50KG5hbWU6IHN0cmluZyk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuY2xhc3NDb3VudHMuZ2V0KHByZXNldEtleSh0aGlzLnByZXNldFNldCwgbmFtZSkpID8/IDA7XG4gIH1cblxuICAvKiogUmVjb2xvdXIgYSBjbGFzcyBmcm9tIGl0cyBwYW5lbCBzd2F0Y2ggYW5kIHJlcGFpbnQgaXRzIChub24tb3ZlcnJpZGRlbikgcmVnaW9ucy4gKi9cbiAgcHJvdGVjdGVkIHNldENsYXNzQ29sb3IobmFtZTogc3RyaW5nLCBjb2xvcjogc3RyaW5nKTogdm9pZCB7XG4gICAgaWYgKCFjb2xvcikgcmV0dXJuO1xuICAgIHRoaXMucmVnaW9uQXBpLnNldENsYXNzaWZpY2F0aW9uQ29sb3IobmFtZSwgY29sb3IpO1xuICAgIHRoaXMuY29tbWl0KCk7XG4gIH1cblxuICAvKiogUmVtb3ZlIGEgY2xhc3MuIEFueSByZWdpb25zIHN0aWxsIHVzaW5nIGl0IGZhbGwgYmFjayB0byB0aGUgZGVmYXVsdFxuICAgKiAgXCJSZWdpb25cIiBjbGFzcywgc28gdGhleSBhcmVuJ3Qgb3JwaGFuZWQgKGFuZCB0aGUgY2xhc3MgaXNuJ3QgaW1tZWRpYXRlbHlcbiAgICogIHJlLWFkZGVkIGZyb20gaXRzIHJlZ2lvbnMgYnkgc3luY0NsYXNzZXNGcm9tUmVnaW9ucykuICovXG4gIHByb3RlY3RlZCBkZWxldGVDbGFzcyhuYW1lOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLnJlYXNzaWduUmVnaW9uc1RvRGVmYXVsdENsYXNzKFtuYW1lXSk7XG4gICAgdGhpcy5yZWdpb25BcGkucmVtb3ZlQ2xhc3MobmFtZSk7XG4gICAgaWYgKHRoaXMuYWN0aXZlQ2xhc3MgPT09IG5hbWUpIHRoaXMuYWN0aXZlQ2xhc3MgPSBudWxsO1xuICAgIHRoaXMuY29tbWl0KCk7XG4gIH1cblxuICAvKiogUmVnaW9ucyBsYWJlbGxlZCB3aXRoIGFueSBvZiBgcmVtb3ZlZE5hbWVzYCByZXZlcnQgdG8gdGhlIGRlZmF1bHRcbiAgICogIHtAbGluayBkZWZhdWx0Q2xhc3NOYW1lfSAoXCJSZWdpb25cIik6IGl0cyBjbGFzcyBjb2xvdXIsIG92ZXJyaWRlIGNsZWFyZWQuXG4gICAqICBTa2lwcyB0aGUgZGVmYXVsdCBpdHNlbGYgc28gZGVsZXRpbmcgXCJSZWdpb25cIiBjYW4ndCBzZWxmLXJlYXNzaWduLiAoaml0LXVpIzcwKSAqL1xuICBwcml2YXRlIHJlYXNzaWduUmVnaW9uc1RvRGVmYXVsdENsYXNzKHJlbW92ZWROYW1lczogc3RyaW5nW10pOiB2b2lkIHtcbiAgICBjb25zdCBrZXlPZiA9IChzOiBzdHJpbmcpID0+IHByZXNldEtleSh0aGlzLnByZXNldFNldCwgcyk7XG4gICAgY29uc3QgcmVtb3ZlZCA9IG5ldyBTZXQocmVtb3ZlZE5hbWVzLm1hcChrZXlPZikpO1xuICAgIHJlbW92ZWQuZGVsZXRlKGtleU9mKHRoaXMuZGVmYXVsdENsYXNzTmFtZSkpO1xuICAgIGlmICghcmVtb3ZlZC5zaXplKSByZXR1cm47XG4gICAgY29uc3QgY29sb3IgPSBjb2xvckZvckxhYmVsKHRoaXMuZGVmYXVsdENsYXNzTmFtZSwgdGhpcy5wcmVzZXRTZXQpO1xuICAgIHRoaXMucGF0Y2hSZWdpb25zKChyKSA9PlxuICAgICAgci5sYWJlbCAmJiByZW1vdmVkLmhhcyhrZXlPZihyLmxhYmVsKSlcbiAgICAgICAgPyB7IGxhYmVsOiB0aGlzLmRlZmF1bHRDbGFzc05hbWUsIGNvbG9yT3ZlcnJpZGRlbjogZmFsc2UsIGNvbG9yIH1cbiAgICAgICAgOiBudWxsLFxuICAgICk7XG4gIH1cblxuICAvKiogU3RhbXAgYSBjbGFzcyAoYW5kIGl0cyBwcmVzZXQvZmFsbGJhY2sgY29sb3VyKSBvbnRvIG9uZSByZWdpb24gZnJvbSB0aGUgQ2xhc3NcbiAgICogIGRyb3Bkb3duOyBjaG9vc2luZyBhIGNsYXNzIG9wdHMgdGhlIHJlZ2lvbiBiYWNrIGludG8gdGhlIHByZXNldCBjb2xvdXIuICovXG4gIHByb3RlY3RlZCBhcHBseVByZXNldFRvUmVnaW9uKHJlZ2lvbjogUmVnaW9uLCBjbGFzc05hbWU6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IG5hbWUgPSAoY2xhc3NOYW1lID8/ICcnKS50cmltKCk7XG4gICAgY29uc3QgcGF0Y2g6IFBhcnRpYWw8UmVnaW9uPiA9IHsgbGFiZWw6IG5hbWUsIGNvbG9yT3ZlcnJpZGRlbjogZmFsc2UgfTtcbiAgICBpZiAobmFtZSkgcGF0Y2guY29sb3IgPSBjb2xvckZvckxhYmVsKG5hbWUsIHRoaXMucHJlc2V0U2V0KTtcbiAgICB0aGlzLnBhdGNoUmVnaW9ucygocikgPT4gKHIgPT09IHJlZ2lvbiA/IHBhdGNoIDogbnVsbCkpO1xuICAgIHRoaXMuY29tbWl0KCk7XG4gIH1cblxuICAvKiogTWFrZSBhIGNsYXNzIGFjdGl2ZSAodXNlZCBmb3IgbmV3IHJlZ2lvbnMpIGFuZCwgaWYgcm93cyBhcmUgc2VsZWN0ZWQsIGFwcGx5XG4gICAqICBpdCB0byB0aGVtIGluIG9uZSBjb21taXQuICovXG4gIHByb3RlY3RlZCBzZWxlY3RBY3RpdmVDbGFzcyhuYW1lOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLmFjdGl2ZUNsYXNzID0gbmFtZTtcbiAgICB0aGlzLmFwcGx5Q2xhc3NUb1NlbGVjdGVkKG5hbWUpO1xuICB9XG5cbiAgLyoqIEFwcGx5IGEgY2xhc3MgKGxhYmVsICsgcHJlc2V0L2ZhbGxiYWNrIGNvbG91cikgdG8gdGhlIGN1cnJlbnQgc2VsZWN0aW9uIGFuZCBjb21taXQuICovXG4gIHByaXZhdGUgYXBwbHlDbGFzc1RvU2VsZWN0ZWQobmFtZTogc3RyaW5nKTogdm9pZCB7XG4gICAgY29uc3Qgc2VsZWN0ZWQgPSBuZXcgU2V0KHRoaXMuc2VsZWN0ZWRSZWdpb25zID8/IFtdKTtcbiAgICBpZiAoIXNlbGVjdGVkLnNpemUpIHJldHVybjtcbiAgICBjb25zdCBjb2xvciA9IGNvbG9yRm9yTGFiZWwobmFtZSwgdGhpcy5wcmVzZXRTZXQpO1xuICAgIHRoaXMucGF0Y2hSZWdpb25zKChyKSA9PiAoc2VsZWN0ZWQuaGFzKHIpID8geyBsYWJlbDogbmFtZSwgY29sb3JPdmVycmlkZGVuOiBmYWxzZSwgY29sb3IgfSA6IG51bGwpKTtcbiAgICB0aGlzLmNvbW1pdCgpO1xuICB9XG5cbiAgLyoqIFwiRWRpdCBjbGFzcyBvbiBzZWxlY3RlZCByb3dzXCIgcG9wdXAg4oCUIGFwcGx5IHRoZSBjaG9zZW4gZXhpc3RpbmcgY2xhc3MuICovXG4gIHByb3RlY3RlZCBhcHBseUJ1bGtDbGFzcygpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuYnVsa0NsYXNzKSByZXR1cm47XG4gICAgdGhpcy5hcHBseUNsYXNzVG9TZWxlY3RlZCh0aGlzLmJ1bGtDbGFzcyk7XG4gICAgdGhpcy5vdmVybGF5UGFuZWw/LmhpZGUoKTtcbiAgfVxuXG4gIC8qKiBcIkVkaXQgY2xhc3Mgb24gc2VsZWN0ZWQgcm93c1wiIHBvcHVwIOKAlCBhZGQgYSBuZXcgY2xhc3MgYW5kIHNldCBpdCBvbiB0aGUgc2VsZWN0aW9uLiAqL1xuICBwcm90ZWN0ZWQgYWRkQW5kQXBwbHlCdWxrQ2xhc3MoKTogdm9pZCB7XG4gICAgY29uc3QgbmFtZSA9IHRoaXMubmV3QnVsa0NsYXNzLnRyaW0oKTtcbiAgICBpZiAoIW5hbWUpIHJldHVybjtcbiAgICBjb25zdCBrZXkgPSBwcmVzZXRLZXkodGhpcy5wcmVzZXRTZXQsIG5hbWUpO1xuICAgIGNvbnN0IGV4aXN0cyA9IHRoaXMucHJlc2V0U2V0LmNsYXNzZXMuc29tZSgoYykgPT4gcHJlc2V0S2V5KHRoaXMucHJlc2V0U2V0LCBjLm5hbWUpID09PSBrZXkpO1xuICAgIGlmICghZXhpc3RzKSB7XG4gICAgICB0aGlzLnJlZ2lvbkFwaS51cHNlcnRDbGFzcyh7IG5hbWUsIGNvbG9yOiBjb2xvckZvckxhYmVsKG5hbWUsIHRoaXMucHJlc2V0U2V0KSwgc291cmNlOiAndXNlcicgfSk7XG4gICAgfVxuICAgIHRoaXMuYXBwbHlDbGFzc1RvU2VsZWN0ZWQobmFtZSk7XG4gICAgdGhpcy5idWxrQ2xhc3MgPSBuYW1lO1xuICAgIHRoaXMubmV3QnVsa0NsYXNzID0gJyc7XG4gICAgdGhpcy5vdmVybGF5UGFuZWw/LmhpZGUoKTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBNYW5hZ2UtY2xhc3NlcyBkaWFsb2cg4pSA4pSAXG4gIHByb3RlY3RlZCBvcGVuTWFuYWdlRGlhbG9nKCk6IHZvaWQge1xuICAgIHRoaXMucHJlc2V0RHJhZnQgPSB0aGlzLmNsb25lUHJlc2V0U2V0KHRoaXMucHJlc2V0U2V0KTtcbiAgICB0aGlzLnNob3dNYW5hZ2VEaWFsb2cgPSB0cnVlO1xuICB9XG4gIHByaXZhdGUgY2xvbmVQcmVzZXRTZXQoczogUHJlc2V0U2V0KTogUHJlc2V0U2V0IHtcbiAgICByZXR1cm4ge1xuICAgICAgY2xhc3NlczogKHMuY2xhc3NlcyA/PyBbXSkubWFwKChjKSA9PiAoeyAuLi5jIH0pKSxcbiAgICAgIGZhbGxiYWNrUGFsZXR0ZTogWy4uLihzLmZhbGxiYWNrUGFsZXR0ZSA/PyBbXSldLFxuICAgICAgYXV0b1Byb21vdGU6ICEhcy5hdXRvUHJvbW90ZSxcbiAgICAgIG1hdGNoTW9kZTogcy5tYXRjaE1vZGUgPT09ICdub3JtYWxpemVkJyA/ICdub3JtYWxpemVkJyA6ICdleGFjdCcsXG4gICAgfTtcbiAgfVxuICAvKiogQ29tbWl0IHRoZSBkcmFmdDogZHJvcCBibGFuay9kdXBsaWNhdGUgbmFtZXMsIHBlcnNpc3QsIGFuZCByZWNvbG91clxuICAgKiAgbm9uLW92ZXJyaWRkZW4gcmVnaW9ucyB3aXRoIHRoZSB1cGRhdGVkIHByZXNldHMuICovXG4gIHByb3RlY3RlZCBhcHBseU1hbmFnZURpYWxvZyhjbG9zZTogYm9vbGVhbik6IHZvaWQge1xuICAgIGlmICghdGhpcy5wcmVzZXREcmFmdCkgcmV0dXJuO1xuICAgIC8vIERlLWR1cGxpY2F0ZSB1c2luZyB0aGUgYWN0aXZlIG1hdGNoIG1vZGUncyBrZXkgc28gbm9ybWFsaXplZCBtb2RlIGNhbid0IGtlZXBcbiAgICAvLyBib3RoIFwiVHVtb3JcIiBhbmQgXCJ0dW1vclwiICh3aGljaCB3b3VsZCBjb2xsaWRlIGF0IHJ1bnRpbWUgaW4gZmluZFByZXNldCgpKS5cbiAgICBjb25zdCBkcmFmdCA9IHRoaXMucHJlc2V0RHJhZnQ7XG4gICAgY29uc3Qga2V5T2YgPSAoczogc3RyaW5nKSA9PiBwcmVzZXRLZXkoZHJhZnQsIHMpO1xuICAgIGNvbnN0IHNlZW4gPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgICBjb25zdCBjbGFzc2VzOiBDbGFzc1ByZXNldFtdID0gW107XG4gICAgZm9yIChjb25zdCBjIG9mIGRyYWZ0LmNsYXNzZXMpIHtcbiAgICAgIGNvbnN0IG5hbWUgPSAoYy5uYW1lID8/ICcnKS50cmltKCk7XG4gICAgICBjb25zdCBrZXkgPSBrZXlPZihuYW1lKTtcbiAgICAgIGlmICghbmFtZSB8fCBzZWVuLmhhcyhrZXkpKSBjb250aW51ZTtcbiAgICAgIHNlZW4uYWRkKGtleSk7XG4gICAgICBjbGFzc2VzLnB1c2goeyAuLi5jLCBuYW1lIH0pO1xuICAgIH1cbiAgICB0aGlzLnByZXNldERyYWZ0ID0geyAuLi5kcmFmdCwgY2xhc3NlcyB9O1xuICAgIC8vIENsYXNzZXMgZHJvcHBlZCBpbiB0aGUgZGlhbG9nOiB0aGVpciByZWdpb25zIHJldmVydCB0byB0aGUgZGVmYXVsdCBcIlJlZ2lvblwiXG4gICAgLy8gY2xhc3MgKGVsc2Ugc3luY0NsYXNzZXNGcm9tUmVnaW9ucyB3b3VsZCBqdXN0IHJlLWFkZCB0aGVtKSAoaml0LXVpIzcwKS5cbiAgICBjb25zdCBrZXB0ID0gbmV3IFNldChjbGFzc2VzLm1hcCgoYykgPT4ga2V5T2YoYy5uYW1lKSkpO1xuICAgIGNvbnN0IHJlbW92ZWQgPSB0aGlzLnByZXNldFNldC5jbGFzc2VzLm1hcCgoYykgPT4gYy5uYW1lKS5maWx0ZXIoKG4pID0+ICFrZXB0LmhhcyhrZXlPZihuKSkpO1xuICAgIHRoaXMucmVhc3NpZ25SZWdpb25zVG9EZWZhdWx0Q2xhc3MocmVtb3ZlZCk7XG4gICAgdGhpcy5yZWdpb25BcGkuc2V0UHJlc2V0U2V0KHRoaXMucHJlc2V0RHJhZnQpO1xuICAgIHRoaXMuY29tbWl0KCk7IC8vIHJlY29sb3VyIGV4aXN0aW5nIChub24tb3ZlcnJpZGRlbikgcmVnaW9ucyBmcm9tIHRoZSBuZXcgcHJlc2V0c1xuICAgIGlmIChjbG9zZSkgdGhpcy5zaG93TWFuYWdlRGlhbG9nID0gZmFsc2U7XG4gIH1cbiAgcHJvdGVjdGVkIHJlc2V0UHJlc2V0c1RvRGVmYXVsdHMoKTogdm9pZCB7XG4gICAgdGhpcy5yZWdpb25BcGkucmVzZXRQcmVzZXRzKCk7XG4gICAgdGhpcy5wcmVzZXREcmFmdCA9IHRoaXMuY2xvbmVQcmVzZXRTZXQodGhpcy5yZWdpb25BcGkuZ2V0UHJlc2V0U2V0KCkpO1xuICAgIHRoaXMuY29tbWl0KCk7XG4gIH1cbiAgcHJvdGVjdGVkIGV4cG9ydFByZXNldHMoKTogdm9pZCB7XG4gICAgY29uc3QganNvbiA9IEpTT04uc3RyaW5naWZ5KHRoaXMucmVnaW9uQXBpLmdldFByZXNldFNldCgpLCBudWxsLCAyKTtcbiAgICBzYXZlQXMobmV3IEJsb2IoW2pzb25dLCB7IHR5cGU6ICdhcHBsaWNhdGlvbi9qc29uJyB9KSwgJ2Fubm90YXRpb24tY2xhc3Nlcy5qc29uJyk7XG4gIH1cbiAgcHJvdGVjdGVkIGltcG9ydFByZXNldHMoZXZlbnQ6IEV2ZW50KTogdm9pZCB7XG4gICAgdGhpcy5wZXJzaXN0ZW5jZS5yZWFkQ2hvc2VuRmlsZShldmVudCwgKHRleHQpID0+IHRoaXMuYXBwbHlJbXBvcnRlZFByZXNldHModGV4dCkpO1xuICB9XG5cbiAgLyoqIFZhbGlkYXRlIGFuZCBhcHBseSBhbiBpbXBvcnRlZCBhbm5vdGF0aW9uLWNsYXNzZXMgSlNPTiBmaWxlLiAqL1xuICBwcml2YXRlIGFwcGx5SW1wb3J0ZWRQcmVzZXRzKHRleHQ6IHN0cmluZyk6IHZvaWQge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBzZXQgPSBwYXJzZVByZXNldFNldChKU09OLnBhcnNlKHRleHQpKTtcbiAgICAgIGlmICghc2V0KSB0aHJvdyBuZXcgRXJyb3IoJ1RoZSBmaWxlIGlzIG5vdCBhbiBhbm5vdGF0aW9uLWNsYXNzIGxpc3QgKG5vIHZhbGlkIGNsYXNzZXMpLicpO1xuICAgICAgdGhpcy5yZWdpb25BcGkuc2V0UHJlc2V0U2V0KHNldCk7XG4gICAgICB0aGlzLnByZXNldERyYWZ0ID0gdGhpcy5jbG9uZVByZXNldFNldCh0aGlzLnJlZ2lvbkFwaS5nZXRQcmVzZXRTZXQoKSk7XG4gICAgICB0aGlzLmNvbW1pdCgpO1xuICAgICAgdGhpcy5tZXNzYWdlU2VydmljZS5hZGQoe1xuICAgICAgICBrZXk6IFZJWl9UT0FTVF9LRVksXG4gICAgICAgIHNldmVyaXR5OiAnc3VjY2VzcycsXG4gICAgICAgIHN1bW1hcnk6ICdDbGFzc2VzIGltcG9ydGVkJyxcbiAgICAgICAgZGV0YWlsOiAnQW5ub3RhdGlvbiBjbGFzc2VzIGxvYWRlZC4nLFxuICAgICAgfSk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7XG4gICAgICAgIGtleTogVklaX1RPQVNUX0tFWSxcbiAgICAgICAgc2V2ZXJpdHk6ICdlcnJvcicsXG4gICAgICAgIHN1bW1hcnk6ICdJbXBvcnQgZmFpbGVkJyxcbiAgICAgICAgZGV0YWlsOiBgJHsoZXJyIGFzIEVycm9yKT8ubWVzc2FnZSA/PyBlcnJ9YCxcbiAgICAgIH0pO1xuICAgIH1cbiAgICB0aGlzLmNoYW5nZWQoKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBpbXBvcnRSb2lzKGV2ZW50OiBFdmVudCkge1xuICAgIHRoaXMucGVyc2lzdGVuY2UucmVhZENob3NlbkZpbGUoZXZlbnQsICh0ZXh0KSA9PiB0aGlzLmFwcGx5SW1wb3J0ZWRSb2lzKHRleHQpKTtcbiAgfVxuXG4gIC8qKiBSZXBsYWNlIHRoZSByZWdpb25zIHdpdGggYW4gaW1wb3J0ZWQgR2VvSlNPTiBmaWxlJ3MuICovXG4gIHByaXZhdGUgYXBwbHlJbXBvcnRlZFJvaXModGV4dDogc3RyaW5nKTogdm9pZCB7XG4gICAgdHJ5IHtcbiAgICAgIHRoaXMucmVnaW9ucyA9IHRoaXMucmVnaW9uQXBpLmltcG9ydFJlZ2lvbnModGV4dCk7XG4gICAgICB0aGlzLmZpbGxDbGFzc0NvbG9ycygpO1xuICAgICAgdGhpcy5jb21taXQoKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHtcbiAgICAgICAga2V5OiBWSVpfVE9BU1RfS0VZLFxuICAgICAgICBzZXZlcml0eTogJ2Vycm9yJyxcbiAgICAgICAgc3VtbWFyeTogJ0Vycm9yIGltcG9ydGluZyB0aGUgZmlsZScsXG4gICAgICAgIGRldGFpbDogYCR7KGVyciBhcyBFcnJvcik/Lm1lc3NhZ2UgPz8gZXJyfWAsXG4gICAgICB9KTtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHJlYWRpbmcgdGhlIGZpbGU6JywgZXJyKTtcbiAgICB9XG4gIH1cblxuICAvKiogUmVnaW9ucyB0byBzZXJpYWxpemUgb24gc2F2ZS9leHBvcnQgKGV2ZXJ5IHNsaWNlIGZvciBhIGNvbWJpbmVkIHotc3RhY2spLiAqL1xuICBwcml2YXRlIHJlZ2lvbnNGb3JTYXZlKCk6IFJlZ2lvbltdIHtcbiAgICByZXR1cm4gdGhpcy5wZXJzaXN0ZW5jZS5yZWdpb25zRm9yU2F2ZSh0aGlzLnJlZ2lvbnMpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGV4cG9ydFJvaXMoKSB7XG4gICAgaWYgKCF0aGlzLnJlZ2lvbnNGb3JTYXZlKCkubGVuZ3RoKSByZXR1cm47XG4gICAgdGhpcy5leHBvcnRGaWxlbmFtZSA9IHRoaXMucGVyc2lzdGVuY2UuZGVmYXVsdEV4cG9ydE5hbWUoKTtcbiAgICB0aGlzLnNob3dFeHBvcnREaWFsb2cgPSB0cnVlO1xuICB9XG5cbiAgcHJvdGVjdGVkIGNvbmZpcm1FeHBvcnQoKSB7XG4gICAgY29uc3QgZmlsZW5hbWUgPSB0aGlzLmV4cG9ydEZpbGVuYW1lLnRyaW0oKTtcbiAgICBjb25zdCByZWdpb25zID0gdGhpcy5yZWdpb25zRm9yU2F2ZSgpO1xuICAgIGlmICghZmlsZW5hbWUgfHwgIXJlZ2lvbnMubGVuZ3RoKSByZXR1cm47XG4gICAgdGhpcy5zaG93RXhwb3J0RGlhbG9nID0gZmFsc2U7XG4gICAgdGhpcy5wZXJzaXN0ZW5jZS5kb3dubG9hZChyZWdpb25zLCBmaWxlbmFtZSk7XG4gIH1cblxuICAvKiogT3BlbiB0aGUgXCJTYXZlIG1hc2tcIiBkaWFsb2csIHNlZWRlZCB3aXRoIGA8aW1hZ2Utc3RlbT5fbWFzay5wbmdgLiAqL1xuICBwcm90ZWN0ZWQgb3BlblNhdmVNYXNrRGlhbG9nKCkge1xuICAgIGlmICghdGhpcy5yZWdpb25zLmxlbmd0aCkgcmV0dXJuO1xuICAgIHRoaXMuc2F2ZU1hc2tGaWxlbmFtZSA9IHRoaXMucGVyc2lzdGVuY2UuZGVmYXVsdE1hc2tOYW1lKCk7XG4gICAgdGhpcy5tYXNrTW9kZSA9ICdiaW5hcnknO1xuICAgIHRoaXMuc2hvd1NhdmVNYXNrRGlhbG9nID0gdHJ1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSYXN0ZXJpemUgdGhlIHJlZ2lvbnMgdG8gdGhlIGNob3NlbiBtYXNrIHR5cGUgYW5kIGRvd25sb2FkIGFzIGEgUE5HLiBUaGVcbiAgICogaGVhdnkgd29yayBydW5zIGluIGEgV2ViIFdvcmtlciAoe0BsaW5rIE1hc2tFeHBvcnRTZXJ2aWNlfSkgc28gdGhlIFVJIG5ldmVyXG4gICAqIGZyZWV6ZXMgb24gd2hvbGUtc2xpZGUgaW1hZ2VzIGFuZCB0aGUgam9iIGNhbiBiZSBjYW5jZWxsZWQgKGppdC11aSM5NSk7XG4gICAqIHRoZSBkaWFsb2cgc3RheXMgb3BlbiB3aXRoIGEgcHJvZ3Jlc3MgYmFyIHVudGlsIGRvbmUuXG4gICAqL1xuICBwcm90ZWN0ZWQgY29uZmlybVNhdmVNYXNrKCkge1xuICAgIGNvbnN0IGZpbGVuYW1lID0gdGhpcy5zYXZlTWFza0ZpbGVuYW1lLnRyaW0oKTtcbiAgICBpZiAoIWZpbGVuYW1lIHx8ICF0aGlzLnJlZ2lvbnMubGVuZ3RoIHx8IHRoaXMubWFza0J1c3kpIHJldHVybjtcblxuICAgIGNvbnN0IHNpemUgPSB0aGlzLnJlZ2lvbkFwaS5nZXRNYXNrSW1hZ2VTaXplKCk7XG4gICAgaWYgKCFzaXplKSB7XG4gICAgICB0aGlzLm1hc2tFcnJvcignTm8gaW1hZ2Ugc2l6ZSBpcyBhdmFpbGFibGUsIHNvIHRoZSBtYXNrIGNhbm5vdCBiZSBzaXplZC4nKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5tYXNrQnVzeSA9IHRydWU7XG4gICAgdGhpcy5tYXNrRW5jb2RpbmcgPSBmYWxzZTtcbiAgICB0aGlzLm1hc2tQcm9ncmVzcyA9IDA7XG4gICAgdGhpcy5tYXNrSm9iID0gdGhpcy5tYXNrRXhwb3J0XG4gICAgICAuZXhwb3J0KHtcbiAgICAgICAgcmVnaW9uczogdGhpcy5yZWdpb25zLFxuICAgICAgICBpbWFnZVNpemU6IHNpemUsXG4gICAgICAgIG1vZGU6IHRoaXMubWFza01vZGUsXG4gICAgICAgIHNvdXJjZU5hbWU6IHRoaXMucGVyc2lzdGVuY2Uuc2VsZWN0ZWRGaWxlTmFtZSgpLFxuICAgICAgfSlcbiAgICAgIC5zdWJzY3JpYmUoe1xuICAgICAgICBuZXh0OiAoZSkgPT4ge1xuICAgICAgICAgIHRoaXMuY2hhbmdlZCgpO1xuICAgICAgICAgIHN3aXRjaCAoZS50eXBlKSB7XG4gICAgICAgICAgICBjYXNlICdwbGFubmVkJzpcbiAgICAgICAgICAgICAgaWYgKGUuc2NhbGUgPCAxKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5tZXNzYWdlU2VydmljZS5hZGQoe1xuICAgICAgICAgICAgICAgICAga2V5OiBWSVpfVE9BU1RfS0VZLFxuICAgICAgICAgICAgICAgICAgc2V2ZXJpdHk6ICdpbmZvJyxcbiAgICAgICAgICAgICAgICAgIHN1bW1hcnk6ICdNYXNrIGRvd25zY2FsZWQnLFxuICAgICAgICAgICAgICAgICAgZGV0YWlsOiBgSW1hZ2UgdG9vIGxhcmdlIGZvciBhIGZ1bGwtcmVzb2x1dGlvbiBtYXNrOyBzYXZpbmcgYXQgJHtlLndpZHRofcOXJHtlLmhlaWdodH0uYCxcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ3Byb2dyZXNzJzpcbiAgICAgICAgICAgICAgdGhpcy5tYXNrUHJvZ3Jlc3MgPSBlLnBlcmNlbnQ7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSAnZW5jb2RpbmcnOlxuICAgICAgICAgICAgICB0aGlzLm1hc2tFbmNvZGluZyA9IHRydWU7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSAnZG9uZSc6XG4gICAgICAgICAgICAgIHRoaXMuZmluaXNoTWFzayhlLmJsb2IsIGZpbGVuYW1lKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICBlcnJvcjogKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICB0aGlzLm1hc2tFcnJvcihlcnIubWVzc2FnZSk7XG4gICAgICAgICAgdGhpcy5jaGFuZ2VkKCk7XG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgfVxuXG4gIC8qKiBDYW5jZWwgYW4gaW4tcHJvZ3Jlc3MgbWFzayBleHBvcnQgKHRlcm1pbmF0ZXMgaXRzIHdvcmtlcikgYW5kIHJlc2V0IHN0YXRlLiAqL1xuICBwcm90ZWN0ZWQgY2FuY2VsU2F2ZU1hc2soKSB7XG4gICAgdGhpcy5tYXNrSm9iPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMubWFza0pvYiA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLm1hc2tCdXN5ID0gZmFsc2U7XG4gICAgdGhpcy5tYXNrRW5jb2RpbmcgPSBmYWxzZTtcbiAgICB0aGlzLm1hc2tQcm9ncmVzcyA9IDA7XG4gIH1cblxuICBwcml2YXRlIGZpbmlzaE1hc2soYmxvYjogQmxvYiwgZmlsZW5hbWU6IHN0cmluZykge1xuICAgIHRoaXMubWFza0pvYiA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLm1hc2tCdXN5ID0gZmFsc2U7XG4gICAgdGhpcy5tYXNrRW5jb2RpbmcgPSBmYWxzZTtcbiAgICB0aGlzLnNob3dTYXZlTWFza0RpYWxvZyA9IGZhbHNlO1xuICAgIHNhdmVBcyhibG9iLCBmaWxlbmFtZSk7XG4gIH1cblxuICBwcml2YXRlIG1hc2tFcnJvcihkZXRhaWw6IHN0cmluZykge1xuICAgIHRoaXMubWFza0pvYiA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLm1hc2tCdXN5ID0gZmFsc2U7XG4gICAgdGhpcy5tYXNrRW5jb2RpbmcgPSBmYWxzZTtcbiAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7XG4gICAgICBrZXk6IFZJWl9UT0FTVF9LRVksXG4gICAgICBzZXZlcml0eTogJ2Vycm9yJyxcbiAgICAgIHN1bW1hcnk6ICdDb3VsZCBub3QgY3JlYXRlIG1hc2snLFxuICAgICAgZGV0YWlsLFxuICAgIH0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHBlcnNpc3RSZWdpb25zKCkge1xuICAgIC8vIEZvbGRlciBzdGFjazogd3JpdGUgZWFjaCBzbGljZSdzIHJlZ2lvbnMgYmFjayB0byBpdHMgb3duIHNsaWNlLWZpbGUnc1xuICAgIC8vIHNpYmxpbmcgZ2VvanNvbiDigJQgbm8gc2luZ2xlLWZpbGVuYW1lIHByb21wdCAoaml0LXVpIzkzKS5cbiAgICBpZiAodGhpcy5wZXJzaXN0ZW5jZS5zYXZlc1BlclNsaWNlKCkpIHtcbiAgICAgIHRoaXMuc2F2ZVN0YWNrU2xpY2VzKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGZpbGVuYW1lID0gdGhpcy5wZXJzaXN0ZW5jZS5kZWZhdWx0U2F2ZU5hbWUoKTtcbiAgICBpZiAoIWZpbGVuYW1lIHx8ICF0aGlzLnJlZ2lvbnNGb3JTYXZlKCkubGVuZ3RoKSByZXR1cm47XG5cbiAgICB0aGlzLnNhdmVBc0ZpbGVuYW1lID0gZmlsZW5hbWU7XG4gICAgdGhpcy5zYXZlQXNGaWxlRXhpc3RzID0gZmFsc2U7XG4gICAgdGhpcy5zaG93U2F2ZUFzRGlhbG9nID0gdHJ1ZTtcbiAgICB0aGlzLnNhdmVBc0NoZWNrJC5uZXh0KHRoaXMuc2F2ZUFzRmlsZW5hbWUpO1xuICB9XG5cbiAgLyoqIFNhdmUgYSBmb2xkZXIgc3RhY2sncyByZWdpb25zIGFzIG9uZSBnZW9qc29uIHBlciBzbGljZS1maWxlIChqaXQtdWkjOTMpLlxuICAgKiAgTm8gZGlhbG9nIGNvdmVycyB0aGUgdG9vbGJhciBtZWFud2hpbGUsIHNvIGEgU2F2ZSB3aGlsZSBvbmUgaXMgdXBsb2FkaW5nXG4gICAqICBpcyBpZ25vcmVkIHJhdGhlciB0aGFuIHN0YXJ0aW5nIGEgc2Vjb25kIHdyaXRlIChhbmQgb3JwaGFuaW5nIHRoZSBmaXJzdCkuICovXG4gIHByaXZhdGUgc2F2ZVN0YWNrU2xpY2VzKCkge1xuICAgIGlmICh0aGlzLnNhdmVBc0J1c3kpIHJldHVybjtcbiAgICBjb25zdCBzbGljZXMgPSB0aGlzLnBlcnNpc3RlbmNlLnNsaWNlR2VvSnNvbnMoKTtcbiAgICBpZiAoIXNsaWNlcy5sZW5ndGgpIHJldHVybjtcbiAgICB0aGlzLnNhdmVBc0J1c3kgPSB0cnVlO1xuICAgIHRoaXMuX3NhdmVBc1N1YiA9IHRoaXMucGVyc2lzdGVuY2Uuc2F2ZVNsaWNlcyhzbGljZXMpLnN1YnNjcmliZSh7XG4gICAgICBuZXh0OiAoKSA9PiB7XG4gICAgICAgIHRoaXMuc2F2ZUFzQnVzeSA9IGZhbHNlO1xuICAgICAgICB0aGlzLmNoYW5nZWQoKTtcbiAgICAgICAgdGhpcy50b2FzdChcbiAgICAgICAgICAnc3VjY2VzcycsXG4gICAgICAgICAgJ1JlZ2lvbnMgc2F2ZWQnLFxuICAgICAgICAgIGBTYXZlZCBST0lzIGZvciAke3NsaWNlcy5sZW5ndGh9IHNsaWNlJHtzbGljZXMubGVuZ3RoID09PSAxID8gJycgOiAncyd9YCxcbiAgICAgICAgKTtcbiAgICAgIH0sXG4gICAgICBlcnJvcjogKGVycikgPT4ge1xuICAgICAgICB0aGlzLnNhdmVBc0J1c3kgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5jaGFuZ2VkKCk7XG4gICAgICAgIHRoaXMudG9hc3QoJ2Vycm9yJywgJ0Vycm9yIHNhdmluZyByZWdpb25zJywgYCR7KGVyciBhcyBFcnJvcik/Lm1lc3NhZ2UgPz8gZXJyfWApO1xuICAgICAgfSxcbiAgICB9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBjaGVja1NhdmVBc0ZpbGVFeGlzdHMoKSB7XG4gICAgdGhpcy5zYXZlQXNDaGVjayQubmV4dCh0aGlzLnNhdmVBc0ZpbGVuYW1lKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBjb25maXJtU2F2ZUFzKCkge1xuICAgIGlmICghdGhpcy5wZXJzaXN0ZW5jZS5zZWxlY3RlZEZpbGVOYW1lKCkpIHJldHVybjtcblxuICAgIGNvbnN0IGZpbGVuYW1lID0gdGhpcy5zYXZlQXNGaWxlbmFtZS50cmltKCk7XG4gICAgaWYgKCFmaWxlbmFtZSkgcmV0dXJuO1xuXG4gICAgLy8gS2VlcCB0aGUgZGlhbG9nIG9wZW4gc2hvd2luZyBhbiAoaW5kZXRlcm1pbmF0ZSkgcHJvZ3Jlc3MgYmFyIHdoaWxlIHRoZVxuICAgIC8vIHJlZ2lvbnMgc2VyaWFsaXplIGFuZCB1cGxvYWQsIHRoZW4gY2xvc2Ugb24gc3VjY2Vzcy5cbiAgICBjb25zdCBkb1NhdmUgPSAoKSA9PiB7XG4gICAgICB0aGlzLnNhdmVBc0J1c3kgPSB0cnVlO1xuICAgICAgLy8gQWxzbyBydW4gZnJvbSB0aGUgb3ZlcndyaXRlIGNvbmZpcm1hdGlvbiwgd2hpY2ggdGhlIGhvc3QncyBkaWFsb2cgbWF5IGFuc3dlci5cbiAgICAgIHRoaXMuY2hhbmdlZCgpO1xuICAgICAgdGhpcy5fc2F2ZUFzU3ViID0gdGhpcy5wZXJzaXN0ZW5jZVxuICAgICAgICAuc2F2ZSgoKSA9PiB0aGlzLnJlZ2lvbnNGb3JTYXZlKCksIGZpbGVuYW1lKVxuICAgICAgICAuc3Vic2NyaWJlKHtcbiAgICAgICAgICBuZXh0OiAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnNhdmVBc0J1c3kgPSBmYWxzZTtcbiAgICAgICAgICAgIHRoaXMuc2hvd1NhdmVBc0RpYWxvZyA9IGZhbHNlO1xuICAgICAgICAgICAgdGhpcy5jaGFuZ2VkKCk7XG4gICAgICAgICAgICB0aGlzLnRvYXN0KCdzdWNjZXNzJywgJ1JlZ2lvbnMgc2F2ZWQnLCBgU2F2ZWQgYXMgJHtmaWxlbmFtZX1gKTtcbiAgICAgICAgICB9LFxuICAgICAgICAgIGVycm9yOiAoZXJyKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnNhdmVBc0J1c3kgPSBmYWxzZTtcbiAgICAgICAgICAgIHRoaXMuY2hhbmdlZCgpO1xuICAgICAgICAgICAgdGhpcy50b2FzdCgnZXJyb3InLCAnRXJyb3Igc2F2aW5nIHJlZ2lvbnMnLCBgJHsoZXJyIGFzIEVycm9yKT8ubWVzc2FnZSB8fCBlcnJ9YCk7XG4gICAgICAgICAgfSxcbiAgICAgICAgfSk7XG4gICAgfTtcblxuICAgIGlmICh0aGlzLnNhdmVBc0ZpbGVFeGlzdHMpIHtcbiAgICAgIHRoaXMuY29uZmlybWF0aW9uU2VydmljZS5jb25maXJtKHtcbiAgICAgICAgbWVzc2FnZTogYFwiJHtmaWxlbmFtZX1cIiBhbHJlYWR5IGV4aXN0cy4gRG8geW91IHdhbnQgdG8gb3ZlcndyaXRlIGl0P2AsXG4gICAgICAgIGhlYWRlcjogJ0NvbmZpcm0gT3ZlcndyaXRlJyxcbiAgICAgICAgaWNvbjogJ3BpIHBpLWV4Y2xhbWF0aW9uLXRyaWFuZ2xlJyxcbiAgICAgICAgYWNjZXB0OiBkb1NhdmUsXG4gICAgICB9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgZG9TYXZlKCk7XG4gICAgfVxuICB9XG5cbiAgLyoqIENhbmNlbCBhbiBpbi1wcm9ncmVzcyBHZW9KU09OIHBlcnNpc3Q6IGFib3J0IHRoZSBkZWZlcnJlZCBzZXJpYWxpemUgYW5kL29yXG4gICAqICB0aGUgaW4tZmxpZ2h0IHVwbG9hZCwgYW5kIHJlc2V0IHN0YXRlLiAqL1xuICBwcm90ZWN0ZWQgY2FuY2VsU2F2ZUFzKCkge1xuICAgIHRoaXMuX3NhdmVBc1N1Yj8udW5zdWJzY3JpYmUoKTtcbiAgICB0aGlzLl9zYXZlQXNTdWIgPSB1bmRlZmluZWQ7XG4gICAgdGhpcy5zYXZlQXNCdXN5ID0gZmFsc2U7XG4gIH1cblxuICBwcml2YXRlIHRvYXN0KHNldmVyaXR5OiAnc3VjY2VzcycgfCAnZXJyb3InIHwgJ2luZm8nLCBzdW1tYXJ5OiBzdHJpbmcsIGRldGFpbDogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5tZXNzYWdlU2VydmljZS5hZGQoeyBrZXk6IFZJWl9UT0FTVF9LRVksIHNldmVyaXR5LCBzdW1tYXJ5LCBkZXRhaWwgfSk7XG4gIH1cbn1cbiIsIjxwLXRvb2xiYXIgc3R5bGVDbGFzcz1cInJlZ2lvbi10b29sYmFyXCI+XG4gIDxkaXYgY2xhc3M9XCJwLXRvb2xiYXItZ3JvdXAtc3RhcnQgYWxpZ24taXRlbXMtY2VudGVyIGZsZXgtd3JhcCBnYXAtMVwiPlxuICAgIDxwLWJ1dHRvblxuICAgICAgaWNvbj1cInBpIHBpLXRhZ1wiXG4gICAgICBzaXplPVwic21hbGxcIlxuICAgICAgW3RleHRdPVwiIXNob3dTaGFwZUxhYmVsXCJcbiAgICAgIHBUb29sdGlwPVwiU2hvdyByZWdpb24gbGFiZWxzLlwiXG4gICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgKG9uQ2xpY2spPVwic2hvd1NoYXBlTGFiZWwgPSAhc2hvd1NoYXBlTGFiZWw7IGNoYW5nZVNob3dTaGFwZUxhYmVsKHNob3dTaGFwZUxhYmVsKVwiXG4gICAgPlxuICAgIDwvcC1idXR0b24+XG4gICAgPHNwYW4gY2xhc3M9XCJ0b29sYmFyLXNlcGFyYXRvclwiPjwvc3Bhbj5cbiAgICA8IS0tIE5ldyByZWdpb25zIGFyZSBub3cgY3JlYXRlZCBmcm9tIHRoZSBkaWFncmFtIHRvb2xiYXJcbiAgICAgICAgIChyZWN0YW5nbGUsIHBvbHlsaW5lLCBmcmVlZm9ybSwgd2FuZCkuIC0tPlxuICAgIDxwLWJ1dHRvblxuICAgICAgaWNvbj1cInBpIHBpLXVwbG9hZFwiXG4gICAgICBzaXplPVwic21hbGxcIlxuICAgICAgW3RleHRdPVwidHJ1ZVwiXG4gICAgICBzdHlsZUNsYXNzPVwibXItMlwiXG4gICAgICBwVG9vbHRpcD1cIlVwbG9hZCBhbmQgZGlzcGxheSByZWdpb25zIGZyb20gZ2VvanNvbiBmaWxlLlwiXG4gICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgKGNsaWNrKT1cInJvaUZpbGVJbnB1dC5jbGljaygpXCJcbiAgICA+XG4gICAgPC9wLWJ1dHRvbj5cbiAgICA8aW5wdXRcbiAgICAgICNyb2lGaWxlSW5wdXRcbiAgICAgIHR5cGU9XCJmaWxlXCJcbiAgICAgIGFjY2VwdD1cIi5nZW9qc29uLC5qc29uXCJcbiAgICAgIGhpZGRlblxuICAgICAgKGNoYW5nZSk9XCJpbXBvcnRSb2lzKCRldmVudCk7IHJvaUZpbGVJbnB1dC52YWx1ZSA9ICcnXCJcbiAgICAvPlxuICAgIDxwLWJ1dHRvblxuICAgICAgc2l6ZT1cInNtYWxsXCJcbiAgICAgIFt0ZXh0XT1cInRydWVcIlxuICAgICAgc3R5bGVDbGFzcz1cIm1yLTJcIlxuICAgICAgcFRvb2x0aXA9XCJEb3dubG9hZCByZWdpb25zIGFzIGdlb2pzb24gZmlsZS5cIlxuICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgIGljb249XCJwaSBwaS1kb3dubG9hZFwiXG4gICAgICAoY2xpY2spPVwiZXhwb3J0Um9pcygpXCJcbiAgICA+XG4gICAgPC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b25cbiAgICAgIGljb249XCJwaSBwaS1zYXZlXCJcbiAgICAgIHNpemU9XCJzbWFsbFwiXG4gICAgICBbdGV4dF09XCJ0cnVlXCJcbiAgICAgIHN0eWxlQ2xhc3M9XCJtci0yXCJcbiAgICAgIHBUb29sdGlwPVwiU2F2ZSByZWdpb25zIHRvIHNlcnZlci5cIlxuICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgIChjbGljayk9XCJwZXJzaXN0UmVnaW9ucygpXCJcbiAgICAgIFtkaXNhYmxlZF09XCIhcmVnaW9ucyB8fCAhcmVnaW9ucy5sZW5ndGhcIlxuICAgID5cbiAgICA8L3AtYnV0dG9uPlxuICAgIDxwLWJ1dHRvblxuICAgICAgc2l6ZT1cInNtYWxsXCJcbiAgICAgIFt0ZXh0XT1cInRydWVcIlxuICAgICAgc3R5bGVDbGFzcz1cIm1yLTJcIlxuICAgICAgcFRvb2x0aXA9XCJEb3dubG9hZCBtYXNrIG9mIHRoZSByZWdpb25zIGFzIFBOR1wiXG4gICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgKGNsaWNrKT1cIm9wZW5TYXZlTWFza0RpYWxvZygpXCJcbiAgICAgIFtkaXNhYmxlZF09XCIhcmVnaW9ucyB8fCAhcmVnaW9ucy5sZW5ndGhcIlxuICAgID5cbiAgICAgIDwhLS0gUmVmZXJlbmNlcyB0aGUgZmlsZSBzbyBJbmtzY2FwZSBlZGl0cyB0byBtYXNrLnN2ZyBzaG93IHVwIGRpcmVjdGx5XG4gICAgICAgICAgICh0aGUgbGliJ3MgYXNzZXRzLyoqIGFyZSBzZXJ2ZWQgYXQgYXNzZXRzL3Bsb3R0aW5nLykuIFRoZVxuICAgICAgICAgICB0b29sYmFyLWljb24tZGFyayBmaWx0ZXIgcmVjb2xvdXJzIHRoZSBibGFjayBTVkcgdG8gdGhlIHRoZW1lIGJsdWUsXG4gICAgICAgICAgIHNhbWUgYXMgcmVnaW9uLWVkaXRvci5zdmcgYW5kIHRoZSBvdGhlciBTVkcgdG9vbGJhciBpY29ucy4gLS0+XG4gICAgICA8aW1nXG4gICAgICAgIHNyYz1cImFzc2V0cy9wbG90dGluZy9tYXNrLnN2Z1wiXG4gICAgICAgIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIlxuICAgICAgICBzdHlsZT1cIndpZHRoOiAxcmVtOyBoZWlnaHQ6IDFyZW07IHZlcnRpY2FsLWFsaWduOiBtaWRkbGVcIlxuICAgICAgICBhbHQ9XCJEb3dubG9hZCBtYXNrXCJcbiAgICAgIC8+XG4gICAgPC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b25cbiAgICAgIGxhYmVsPVwieDUxMlwiXG4gICAgICBzaXplPVwic21hbGxcIlxuICAgICAgW3RleHRdPVwidHJ1ZVwiXG4gICAgICBzdHlsZUNsYXNzPVwibXItMlwiXG4gICAgICBwVG9vbHRpcD1cIlJvdW5kIGFsbCByZWN0YW5nbGUgcmVnaW9uc1xuICAgICAgICAgICAgICAgICAgICB0byB3aWR0aCBhbmQgaGVpZ2h0IHRoYXQgYXJlIG11bHRpcGxlIG9mIDUxMlwiXG4gICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgKGNsaWNrKT1cInJvdW5kUmVjdGFuZ2xlTGVuZ3RocygpXCJcbiAgICA+XG4gICAgPC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b25cbiAgICAgIGljb249XCJwaSBwaS1jaGVjay1zcXVhcmVcIlxuICAgICAgc2l6ZT1cInNtYWxsXCJcbiAgICAgIFt0ZXh0XT1cInRydWVcIlxuICAgICAgc3R5bGVDbGFzcz1cIm1yLTJcIlxuICAgICAgcFRvb2x0aXA9XCJTZWxlY3QgYWxsIHJlZ2lvbnNcIlxuICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgIChjbGljayk9XCJzZWxlY3RBbGxSZWdpb25zKClcIlxuICAgICAgW2Rpc2FibGVkXT1cIiFyZWdpb25zIHx8ICFyZWdpb25zLmxlbmd0aFwiXG4gICAgPlxuICAgIDwvcC1idXR0b24+XG4gICAgPHAtYnV0dG9uXG4gICAgICBpY29uPVwicGkgcGktdGFnc1wiXG4gICAgICBzaXplPVwic21hbGxcIlxuICAgICAgW3RleHRdPVwidHJ1ZVwiXG4gICAgICBzdHlsZUNsYXNzPVwibXItMlwiXG4gICAgICBwVG9vbHRpcD1cIkVkaXQgY2xhc3Mgb24gc2VsZWN0ZWQgcm93c1wiXG4gICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgKGNsaWNrKT1cIiFzZWxlY3RlZFJlZ2lvbnMgfHwgIXNlbGVjdGVkUmVnaW9ucy5sZW5ndGggPyBvcC5oaWRlKCkgOiBvcC50b2dnbGUoJGV2ZW50KVwiXG4gICAgICBbZGlzYWJsZWRdPVwiIXNlbGVjdGVkUmVnaW9ucyB8fCAhc2VsZWN0ZWRSZWdpb25zLmxlbmd0aFwiXG4gICAgPlxuICAgIDwvcC1idXR0b24+XG4gICAgPHAtYnV0dG9uXG4gICAgICBpY29uPVwicGkgcGktcGFsZXR0ZVwiXG4gICAgICBzaXplPVwic21hbGxcIlxuICAgICAgW3RleHRdPVwidHJ1ZVwiXG4gICAgICBzdHlsZUNsYXNzPVwibXItMlwiXG4gICAgICBwVG9vbHRpcD1cIkVkaXQgY29sb3VyIG9mIHNlbGVjdGVkIHJlZ2lvbnNcIlxuICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgIChjbGljayk9XCJvcGVuQ29sb3JEaWFsb2coKVwiXG4gICAgICBbZGlzYWJsZWRdPVwiIXNlbGVjdGVkUmVnaW9ucyB8fCAhc2VsZWN0ZWRSZWdpb25zLmxlbmd0aFwiXG4gICAgPlxuICAgIDwvcC1idXR0b24+XG4gICAgPHAtb3ZlcmxheVBhbmVsICNvcCAob25IaWRlKT1cImJ1bGtDbGFzcyA9ICcnOyBuZXdCdWxrQ2xhc3MgPSAnJ1wiPlxuICAgICAgPGRpdiBjbGFzcz1cImZsZXggZmxleC1jb2x1bW4gZ2FwLTJcIiBzdHlsZT1cIndpZHRoOiAxOHJlbVwiPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZvbnQtbWVkaXVtIHRleHQtOTAwXCJcbiAgICAgICAgICA+U2V0IGNsYXNzIG9uIHt7IHNlbGVjdGVkUmVnaW9ucz8ubGVuZ3RoIHx8IDAgfX0gc2VsZWN0ZWQgcmVnaW9uKHMpPC9zcGFuXG4gICAgICAgID5cbiAgICAgICAgPCEtLSBTZWxlY3RpbmcgYSBjbGFzcyBhcHBsaWVzIGl0IGltbWVkaWF0ZWx5IChjaG9vc2luZyBhbiBvcHRpb24gY2xvc2VzIHRoZVxuICAgICAgICAgICAgIG92ZXJsYXksIHNvIHRoZXJlJ3Mgbm8gc2VwYXJhdGUgQXBwbHkgYnV0dG9uKS4gLS0+XG4gICAgICAgIDxwLWRyb3Bkb3duXG4gICAgICAgICAgc3R5bGVDbGFzcz1cInctZnVsbFwiXG4gICAgICAgICAgYXBwZW5kVG89XCJib2R5XCJcbiAgICAgICAgICBbb3B0aW9uc109XCJwcmVzZXRTZXQuY2xhc3Nlc1wiXG4gICAgICAgICAgb3B0aW9uTGFiZWw9XCJuYW1lXCJcbiAgICAgICAgICBvcHRpb25WYWx1ZT1cIm5hbWVcIlxuICAgICAgICAgIFsobmdNb2RlbCldPVwiYnVsa0NsYXNzXCJcbiAgICAgICAgICBwbGFjZWhvbGRlcj1cIkNob29zZSBhIGNsYXNz4oCmXCJcbiAgICAgICAgICBbZmlsdGVyXT1cInByZXNldFNldC5jbGFzc2VzLmxlbmd0aCA+IDhcIlxuICAgICAgICAgIGZpbHRlckJ5PVwibmFtZVwiXG4gICAgICAgICAgKG9uQ2hhbmdlKT1cImFwcGx5QnVsa0NsYXNzKClcIlxuICAgICAgICA+XG4gICAgICAgICAgPG5nLXRlbXBsYXRlIGxldC1jIHBUZW1wbGF0ZT1cIml0ZW1cIj5cbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiZGQtdmFsdWVcIlxuICAgICAgICAgICAgICA+PHNwYW4gY2xhc3M9XCJjaGlwLXN3YXRjaFwiIFtzdHlsZS5iYWNrZ3JvdW5kXT1cImMuY29sb3JcIj48L3NwYW4+e3sgYy5uYW1lIH19PC9zcGFuXG4gICAgICAgICAgICA+XG4gICAgICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgICAgPC9wLWRyb3Bkb3duPlxuICAgICAgICA8ZGl2IGNsYXNzPVwicC1pbnB1dGdyb3VwXCI+XG4gICAgICAgICAgPGlucHV0XG4gICAgICAgICAgICB0eXBlPVwidGV4dFwiXG4gICAgICAgICAgICBwSW5wdXRUZXh0XG4gICAgICAgICAgICBwbGFjZWhvbGRlcj1cIuKApm9yIGEgbmV3IGNsYXNzXCJcbiAgICAgICAgICAgIFsobmdNb2RlbCldPVwibmV3QnVsa0NsYXNzXCJcbiAgICAgICAgICAgIChrZXlkb3duLmVudGVyKT1cImFkZEFuZEFwcGx5QnVsa0NsYXNzKClcIlxuICAgICAgICAgIC8+XG4gICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgcEJ1dHRvblxuICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICBpY29uPVwicGkgcGktcGx1c1wiXG4gICAgICAgICAgICBwVG9vbHRpcD1cIkFkZCBjbGFzcyAmYW1wOyBzZXQgaXRcIlxuICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgIFtkaXNhYmxlZF09XCIhbmV3QnVsa0NsYXNzLnRyaW0oKVwiXG4gICAgICAgICAgICAoY2xpY2spPVwiYWRkQW5kQXBwbHlCdWxrQ2xhc3MoKVwiXG4gICAgICAgICAgPjwvYnV0dG9uPlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuICAgIDwvcC1vdmVybGF5UGFuZWw+XG4gICAgPHAtYnV0dG9uXG4gICAgICBpY29uPVwicGkgcGktdHJhc2hcIlxuICAgICAgc2l6ZT1cInNtYWxsXCJcbiAgICAgIFt0ZXh0XT1cInRydWVcIlxuICAgICAgc3R5bGVDbGFzcz1cIm1yLTJcIlxuICAgICAgKGNsaWNrKT1cImRlbGV0ZVNlbGVjdGVkUmVnaW9ucygpXCJcbiAgICAgIHBUb29sdGlwPVwiRGVsZXRlIHNlbGVjdGVkIHJlZ2lvbnNcIlxuICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgIFtkaXNhYmxlZF09XCIhc2VsZWN0ZWRSZWdpb25zIHx8ICFzZWxlY3RlZFJlZ2lvbnMubGVuZ3RoXCJcbiAgICA+PC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b25cbiAgICAgIGljb249XCJwaSBwaS10aW1lcy1jaXJjbGVcIlxuICAgICAgc2l6ZT1cInNtYWxsXCJcbiAgICAgIFt0ZXh0XT1cInRydWVcIlxuICAgICAgc3R5bGVDbGFzcz1cIm1yLTJcIlxuICAgICAgKGNsaWNrKT1cImNsZWFyQWxsUmVnaW9ucygpXCJcbiAgICAgIHBUb29sdGlwPVwiRGVsZXRlIGFsbCByZWdpb25zXCJcbiAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICBbZGlzYWJsZWRdPVwiIXJlZ2lvbnMgfHwgIXJlZ2lvbnMubGVuZ3RoXCJcbiAgICA+PC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b25cbiAgICAgIGljb249XCJwaSBwaS1xdWVzdGlvblwiXG4gICAgICBzaXplPVwic21hbGxcIlxuICAgICAgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAoY2xpY2spPVwic2hvd0hlbHAoKVwiXG4gICAgICBwVG9vbHRpcD1cIkhlbHBcIlxuICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICA+PC9wLWJ1dHRvbj5cbiAgPC9kaXY+XG48L3AtdG9vbGJhcj5cblxuPHJlZ2lvbi1jbGFzc2VzLXBhbmVsXG4gIFtjbGFzc2VzXT1cImRpc3BsYXlDbGFzc2VzXCJcbiAgW2NvdW50c109XCJjbGFzc0NvdW50c1wiXG4gIFttYXRjaE1vZGVdPVwicHJlc2V0U2V0Lm1hdGNoTW9kZVwiXG4gIFthY3RpdmVDbGFzc109XCJhY3RpdmVDbGFzc1wiXG4gIFtzZWxlY3RlZENvdW50XT1cInNlbGVjdGVkUmVnaW9ucz8ubGVuZ3RoIHx8IDBcIlxuICBbZGVmYXVsdENsYXNzTmFtZV09XCJkZWZhdWx0Q2xhc3NOYW1lXCJcbiAgKHBpY2spPVwic2VsZWN0QWN0aXZlQ2xhc3MoJGV2ZW50KVwiXG4gIChyZWNvbG9yKT1cInNldENsYXNzQ29sb3IoJGV2ZW50Lm5hbWUsICRldmVudC5jb2xvcilcIlxuICAocmVtb3ZlKT1cImRlbGV0ZUNsYXNzKCRldmVudClcIlxuICAobWFuYWdlKT1cIm9wZW5NYW5hZ2VEaWFsb2coKVwiXG4+XG48L3JlZ2lvbi1jbGFzc2VzLXBhbmVsPlxuXG48cmVnaW9uLXRhYmxlXG4gIFtwYWdlXT1cInBhZ2VkUmVnaW9uc1wiXG4gIFt0b3RhbF09XCJyZWdpb25zLmxlbmd0aFwiXG4gIFtmaXJzdF09XCJwYWdpbmF0b3JGaXJzdFwiXG4gIFtyb3dzXT1cInBhZ2luYXRvclJvd3NcIlxuICBbcm93c1BlclBhZ2VPcHRpb25zXT1cInJvd3NQZXJQYWdlT3B0aW9uc1wiXG4gIFttZXRhS2V5XT1cIm1ldGFLZXlcIlxuICBbc2VsZWN0aW9uXT1cInNlbGVjdGVkUmVnaW9uc1wiXG4gIChzZWxlY3Rpb25DaGFuZ2UpPVwic2VsZWN0ZWRSZWdpb25zID0gJGV2ZW50XCJcbiAgKHJvd1NlbGVjdCk9XCJvblNlbGVjdGlvbkNoYW5nZWQoKVwiXG4gIFtwcmVzZXRTZXRdPVwicHJlc2V0U2V0XCJcbiAgW2RlZmF1bHRDb2xvcl09XCJzaGFwZUNvbG9yXCJcbiAgW21wcF09XCJtcHBcIlxuICBbbGFiZWxEcmFmdHNdPVwiZWRpdGluZ0xhYmVsUmVnaW9uc1wiXG4gIChsYWJlbEVkaXRTdGFydCk9XCJzdGFydEVkaXRMYWJlbCgkZXZlbnQpXCJcbiAgKGxhYmVsRHJhZnRDaGFuZ2UpPVwic2V0TGFiZWxEcmFmdCgkZXZlbnQucmVnaW9uLCAkZXZlbnQudmFsdWUpXCJcbiAgKGxhYmVsRWRpdFN0b3ApPVwic3RvcEVkaXRMYWJlbCgkZXZlbnQucmVnaW9uLCAkZXZlbnQuY29tbWl0KVwiXG4gIChjbGFzc1BpY2spPVwiYXBwbHlQcmVzZXRUb1JlZ2lvbigkZXZlbnQucmVnaW9uLCAkZXZlbnQubmFtZSlcIlxuICAoZGVsZXRlUm93KT1cImRlbGV0ZVJlZ2lvbigkZXZlbnQpXCJcbiAgKHBhZ2VDaGFuZ2UpPVwib25QYWdlQ2hhbmdlKCRldmVudClcIlxuPlxuPC9yZWdpb24tdGFibGU+XG5cbjwhLS0gTGl2ZS1lZGl0IG1vZGU6IG5vIFNhdmUvQ2FuY2VsIOKAlCBldmVyeSBlZGl0IGNvbW1pdHMgaW1tZWRpYXRlbHkgdG8gdGhlXG4gICAgIGFjdGl2ZSBpbWFnZSdzIHJlZ2lvbiBjYWNoZS4gLS0+XG5cbjxwLWNvbmZpcm1EaWFsb2dcbiAgI2NkXG4gIFtzdHlsZV09XCJ7IHdpZHRoOiAnMjRyZW0nLCBoZWlnaHQ6ICcxNHJlbScgfVwiXG4gIGtleT1cInBvc2l0aW9uRGlhbG9nXCJcbiAgW2Jhc2VaSW5kZXhdPVwiMTAwMDBcIlxuICByZWplY3RCdXR0b25TdHlsZUNsYXNzPVwicC1idXR0b24tb3V0bGluZWRcIlxuPlxuICA8bmctdGVtcGxhdGUgcFRlbXBsYXRlPVwiZm9vdGVyXCI+XG4gICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgcEJ1dHRvbiBpY29uPVwicGkgcGktY2hlY2tcIiBsYWJlbD1cIlllc1wiIChjbGljayk9XCJjZC5hY2NlcHQoKVwiPjwvYnV0dG9uPlxuICAgIDxidXR0b25cbiAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgY2xhc3M9XCJwLWJ1dHRvbi1vdXRsaW5lZFwiXG4gICAgICBwQnV0dG9uXG4gICAgICBpY29uPVwicGkgcGktdGltZXNcIlxuICAgICAgbGFiZWw9XCJOb1wiXG4gICAgICAoY2xpY2spPVwiY2QucmVqZWN0KClcIlxuICAgID48L2J1dHRvbj5cbiAgPC9uZy10ZW1wbGF0ZT5cbjwvcC1jb25maXJtRGlhbG9nPlxuXG48cmVnaW9uLXNhdmUtcmVnaW9ucy1kaWFsb2dcbiAgWyh2aXNpYmxlKV09XCJzaG93U2F2ZUFzRGlhbG9nXCJcbiAgW2ZpbGVuYW1lXT1cInNhdmVBc0ZpbGVuYW1lXCJcbiAgKGZpbGVuYW1lQ2hhbmdlKT1cInNhdmVBc0ZpbGVuYW1lID0gJGV2ZW50OyBjaGVja1NhdmVBc0ZpbGVFeGlzdHMoKVwiXG4gIFtmaWxlRXhpc3RzXT1cInNhdmVBc0ZpbGVFeGlzdHNcIlxuICBbYnVzeV09XCJzYXZlQXNCdXN5XCJcbiAgKGNvbmZpcm0pPVwiY29uZmlybVNhdmVBcygpXCJcbiAgKGNhbmNlbFNhdmUpPVwiY2FuY2VsU2F2ZUFzKClcIlxuPlxuPC9yZWdpb24tc2F2ZS1yZWdpb25zLWRpYWxvZz5cblxuPHJlZ2lvbi1zYXZlLXJlZ2lvbnMtZGlhbG9nXG4gIGhlYWRlcj1cIkV4cG9ydCBSZWdpb25zXCJcbiAgY29uZmlybUxhYmVsPVwiRXhwb3J0XCJcbiAgY29uZmlybUljb249XCJwaSBwaS1kb3dubG9hZFwiXG4gIGlucHV0SWQ9XCJleHBvcnQtZmlsZW5hbWVcIlxuICBbKHZpc2libGUpXT1cInNob3dFeHBvcnREaWFsb2dcIlxuICBbKGZpbGVuYW1lKV09XCJleHBvcnRGaWxlbmFtZVwiXG4gIChjb25maXJtKT1cImNvbmZpcm1FeHBvcnQoKVwiXG4+XG48L3JlZ2lvbi1zYXZlLXJlZ2lvbnMtZGlhbG9nPlxuXG48cmVnaW9uLXNhdmUtbWFzay1kaWFsb2dcbiAgWyh2aXNpYmxlKV09XCJzaG93U2F2ZU1hc2tEaWFsb2dcIlxuICBbKGZpbGVuYW1lKV09XCJzYXZlTWFza0ZpbGVuYW1lXCJcbiAgWyhtb2RlKV09XCJtYXNrTW9kZVwiXG4gIFtidXN5XT1cIm1hc2tCdXN5XCJcbiAgW3Byb2dyZXNzXT1cIm1hc2tQcm9ncmVzc1wiXG4gIFtlbmNvZGluZ109XCJtYXNrRW5jb2RpbmdcIlxuICAoY29uZmlybSk9XCJjb25maXJtU2F2ZU1hc2soKVwiXG4gIChjYW5jZWxFeHBvcnQpPVwiY2FuY2VsU2F2ZU1hc2soKVwiXG4+XG48L3JlZ2lvbi1zYXZlLW1hc2stZGlhbG9nPlxuXG48cmVnaW9uLWNvbG9yLWRpYWxvZ1xuICBbKHZpc2libGUpXT1cInNob3dDb2xvckRpYWxvZ1wiXG4gIFtlZGl0c109XCJjbGFzc0NvbG9yRWRpdHNcIlxuICBbc2VsZWN0ZWRDb3VudF09XCJzZWxlY3RlZFJlZ2lvbnM/Lmxlbmd0aCB8fCAwXCJcbiAgKGFwcGx5KT1cImFwcGx5Q29sb3JUb1NlbGVjdGVkKCRldmVudClcIlxuPlxuPC9yZWdpb24tY29sb3ItZGlhbG9nPlxuXG48IS0tIE1hbmFnZSBhbm5vdGF0aW9uIGNsYXNzZXMgKGppdC11aSM3MCk6IGVkaXRzIGEgd29ya2luZyBjb3B5LCBjb21taXR0ZWQgb24gQXBwbHkuIC0tPlxuPHJlZ2lvbi1tYW5hZ2UtY2xhc3Nlcy1kaWFsb2dcbiAgWyh2aXNpYmxlKV09XCJzaG93TWFuYWdlRGlhbG9nXCJcbiAgWyhkcmFmdCldPVwicHJlc2V0RHJhZnRcIlxuICAoYXBwbHkpPVwiYXBwbHlNYW5hZ2VEaWFsb2codHJ1ZSlcIlxuICAocmVzZXREZWZhdWx0cyk9XCJyZXNldFByZXNldHNUb0RlZmF1bHRzKClcIlxuICAoaW1wb3J0RmlsZSk9XCJpbXBvcnRQcmVzZXRzKCRldmVudClcIlxuICAoZXhwb3J0KT1cImV4cG9ydFByZXNldHMoKVwiXG4+XG48L3JlZ2lvbi1tYW5hZ2UtY2xhc3Nlcy1kaWFsb2c+XG5cbjxyZWdpb24tZWRpdG9yLWhlbHAgWyh2aXNpYmxlKV09XCJkaXNwbGF5SGVscERpYWxvZ1wiPjwvcmVnaW9uLWVkaXRvci1oZWxwPlxuIl19