@jax-data-science/sci-image-visualizer 0.1.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 (311) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +404 -0
  3. package/esm2022/index.mjs +52 -0
  4. package/esm2022/jax-data-science-sci-image-visualizer.mjs +5 -0
  5. package/esm2022/lib/channel-histogram/channel-histogram.component.mjs +398 -0
  6. package/esm2022/lib/contracts/capabilities.contract.mjs +32 -0
  7. package/esm2022/lib/contracts/cell-segmenter.contract.mjs +12 -0
  8. package/esm2022/lib/contracts/channel-histogram-api.contract.mjs +14 -0
  9. package/esm2022/lib/contracts/colormap-lut.mjs +95 -0
  10. package/esm2022/lib/contracts/coordinate-transform.contract.mjs +2 -0
  11. package/esm2022/lib/contracts/display-types.mjs +8 -0
  12. package/esm2022/lib/contracts/image.contract.mjs +11 -0
  13. package/esm2022/lib/contracts/intensity.mjs +28 -0
  14. package/esm2022/lib/contracts/plot-type.mjs +112 -0
  15. package/esm2022/lib/contracts/ports/image-state.port.mjs +3 -0
  16. package/esm2022/lib/contracts/ports/preferences.port.mjs +3 -0
  17. package/esm2022/lib/contracts/ports/region-io.port.mjs +3 -0
  18. package/esm2022/lib/contracts/ports/tile-access.port.mjs +3 -0
  19. package/esm2022/lib/contracts/region-editor-api.contract.mjs +4 -0
  20. package/esm2022/lib/contracts/region-overlay.contract.mjs +2 -0
  21. package/esm2022/lib/contracts/region-store.contract.mjs +2 -0
  22. package/esm2022/lib/contracts/sam.contract.mjs +12 -0
  23. package/esm2022/lib/contracts/toolbar-config.mjs +8 -0
  24. package/esm2022/lib/contracts/visualizer.contract.mjs +10 -0
  25. package/esm2022/lib/contracts/viz-config.mjs +3 -0
  26. package/esm2022/lib/hex-color-picker/hex-color-picker.component.mjs +260 -0
  27. package/esm2022/lib/implementations/base-store-visualizer.mjs +76 -0
  28. package/esm2022/lib/implementations/napari-js/napari-axes-labels.mjs +106 -0
  29. package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +847 -0
  30. package/esm2022/lib/implementations/napari-js/napari-scale-bar.mjs +92 -0
  31. package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +2283 -0
  32. package/esm2022/lib/implementations/napari-js/napari-volume-z-handle.mjs +126 -0
  33. package/esm2022/lib/implementations/osd/display-pipeline.mjs +143 -0
  34. package/esm2022/lib/implementations/osd/histogram-sampler.mjs +217 -0
  35. package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +1780 -0
  36. package/esm2022/lib/implementations/osd/osd-coordinate-transform.mjs +25 -0
  37. package/esm2022/lib/implementations/osd/osd-coords.mjs +53 -0
  38. package/esm2022/lib/implementations/osd/osd-lib.mjs +20 -0
  39. package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +1237 -0
  40. package/esm2022/lib/implementations/osd/osd-scale-bar.mjs +88 -0
  41. package/esm2022/lib/implementations/osd/slice-cache.mjs +488 -0
  42. package/esm2022/lib/implementations/osd/tile-client.mjs +29 -0
  43. package/esm2022/lib/implementations/plotly/plotly-coordinate-transform.mjs +37 -0
  44. package/esm2022/lib/implementations/plotly/plotly-region-overlay.mjs +31 -0
  45. package/esm2022/lib/implementations/plotly/plotly-trace-builders.mjs +183 -0
  46. package/esm2022/lib/implementations/plotly/plotly.service.mjs +2137 -0
  47. package/esm2022/lib/implementations/simple-slice-access.service.mjs +99 -0
  48. package/esm2022/lib/models/bezier.mjs +120 -0
  49. package/esm2022/lib/models/class-preset.mjs +46 -0
  50. package/esm2022/lib/models/geometry.mjs +198 -0
  51. package/esm2022/lib/models/region.mjs +166 -0
  52. package/esm2022/lib/models/shape.mjs +53 -0
  53. package/esm2022/lib/plot.utilities.mjs +693 -0
  54. package/esm2022/lib/processing/image-converter.service.mjs +90 -0
  55. package/esm2022/lib/processing/image-crop.mjs +62 -0
  56. package/esm2022/lib/processing/processing-image.mjs +50 -0
  57. package/esm2022/lib/provide-visualization.mjs +63 -0
  58. package/esm2022/lib/region-editor/mask-raster.mjs +95 -0
  59. package/esm2022/lib/region-editor/mask-worker.mjs +10 -0
  60. package/esm2022/lib/region-editor/region-editor.component.mjs +1190 -0
  61. package/esm2022/lib/region-ops.service.mjs +301 -0
  62. package/esm2022/lib/render-orchestrator.mjs +87 -0
  63. package/esm2022/lib/routing-visualizer.service.mjs +533 -0
  64. package/esm2022/lib/store/class-color.util.mjs +52 -0
  65. package/esm2022/lib/store/region-store.service.mjs +1029 -0
  66. package/esm2022/lib/store/visualizer-store.service.mjs +352 -0
  67. package/esm2022/lib/toolbar/brush/brush-tool.service.mjs +546 -0
  68. package/esm2022/lib/toolbar/crop/slide-crop.mjs +57 -0
  69. package/esm2022/lib/toolbar/segmentation/cell-segment-tool.service.mjs +115 -0
  70. package/esm2022/lib/toolbar/segmentation/cellpose-segmenter.service.mjs +84 -0
  71. package/esm2022/lib/toolbar/segmentation/onnx-sam-session.mjs +133 -0
  72. package/esm2022/lib/toolbar/segmentation/ort-runtime-config.mjs +31 -0
  73. package/esm2022/lib/toolbar/segmentation/sam-model-registry.mjs +108 -0
  74. package/esm2022/lib/toolbar/segmentation/sam-onnx-core.mjs +159 -0
  75. package/esm2022/lib/toolbar/segmentation/sam-point-tool.service.mjs +255 -0
  76. package/esm2022/lib/toolbar/segmentation/sam-prompt.mjs +94 -0
  77. package/esm2022/lib/toolbar/segmentation/sam-tool.service.mjs +188 -0
  78. package/esm2022/lib/toolbar/toolbar.component.mjs +311 -0
  79. package/esm2022/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.mjs +234 -0
  80. package/esm2022/lib/toolbar/wand/wand-tool.service.mjs +499 -0
  81. package/esm2022/lib/toolbar/wand/wand.service.mjs +921 -0
  82. package/esm2022/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.mjs +157 -0
  83. package/esm2022/lib/visualization.module.mjs +162 -0
  84. package/esm2022/lib/visualizer.component.mjs +1597 -0
  85. package/fesm2022/jax-data-science-sci-image-visualizer-mask-worker-uhepXUhM.mjs +12 -0
  86. package/fesm2022/jax-data-science-sci-image-visualizer-mask-worker-uhepXUhM.mjs.map +1 -0
  87. package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs +327 -0
  88. package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs.map +1 -0
  89. package/fesm2022/jax-data-science-sci-image-visualizer.mjs +21314 -0
  90. package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -0
  91. package/fesm2022/mask.worker.js +989 -0
  92. package/fesm2022/onnx-sam.worker.js +202 -0
  93. package/index.d.ts +39 -0
  94. package/lib/channel-histogram/channel-histogram.component.d.ts +112 -0
  95. package/lib/contracts/capabilities.contract.d.ts +30 -0
  96. package/lib/contracts/cell-segmenter.contract.d.ts +40 -0
  97. package/lib/contracts/channel-histogram-api.contract.d.ts +98 -0
  98. package/lib/contracts/colormap-lut.d.ts +21 -0
  99. package/lib/contracts/coordinate-transform.contract.d.ts +43 -0
  100. package/lib/contracts/display-types.d.ts +41 -0
  101. package/lib/contracts/image.contract.d.ts +72 -0
  102. package/lib/contracts/intensity.d.ts +18 -0
  103. package/lib/contracts/plot-type.d.ts +108 -0
  104. package/lib/contracts/ports/image-state.port.d.ts +29 -0
  105. package/lib/contracts/ports/preferences.port.d.ts +19 -0
  106. package/lib/contracts/ports/region-io.port.d.ts +29 -0
  107. package/lib/contracts/ports/tile-access.port.d.ts +27 -0
  108. package/lib/contracts/region-editor-api.contract.d.ts +83 -0
  109. package/lib/contracts/region-overlay.contract.d.ts +47 -0
  110. package/lib/contracts/region-store.contract.d.ts +74 -0
  111. package/lib/contracts/sam.contract.d.ts +98 -0
  112. package/lib/contracts/toolbar-config.d.ts +24 -0
  113. package/lib/contracts/visualizer.contract.d.ts +346 -0
  114. package/lib/contracts/viz-config.d.ts +25 -0
  115. package/lib/hex-color-picker/hex-color-picker.component.d.ts +47 -0
  116. package/lib/implementations/base-store-visualizer.d.ts +71 -0
  117. package/lib/implementations/napari-js/napari-axes-labels.d.ts +47 -0
  118. package/lib/implementations/napari-js/napari-region-overlay.d.ts +115 -0
  119. package/lib/implementations/napari-js/napari-scale-bar.d.ts +31 -0
  120. package/lib/implementations/napari-js/napari-visualizer.service.d.ts +455 -0
  121. package/lib/implementations/napari-js/napari-volume-z-handle.d.ts +46 -0
  122. package/lib/implementations/osd/display-pipeline.d.ts +47 -0
  123. package/lib/implementations/osd/histogram-sampler.d.ts +85 -0
  124. package/lib/implementations/osd/openseadragon-visualizer.service.d.ts +476 -0
  125. package/lib/implementations/osd/osd-coordinate-transform.d.ts +17 -0
  126. package/lib/implementations/osd/osd-coords.d.ts +16 -0
  127. package/lib/implementations/osd/osd-lib.d.ts +19 -0
  128. package/lib/implementations/osd/osd-region-overlay.d.ts +191 -0
  129. package/lib/implementations/osd/osd-scale-bar.d.ts +17 -0
  130. package/lib/implementations/osd/slice-cache.d.ts +161 -0
  131. package/lib/implementations/osd/tile-client.d.ts +32 -0
  132. package/lib/implementations/plotly/plotly-coordinate-transform.d.ts +19 -0
  133. package/lib/implementations/plotly/plotly-region-overlay.d.ts +23 -0
  134. package/lib/implementations/plotly/plotly-trace-builders.d.ts +60 -0
  135. package/lib/implementations/plotly/plotly.service.d.ts +594 -0
  136. package/lib/implementations/simple-slice-access.service.d.ts +64 -0
  137. package/lib/models/bezier.d.ts +59 -0
  138. package/lib/models/class-preset.d.ts +39 -0
  139. package/lib/models/geometry.d.ts +66 -0
  140. package/lib/models/region.d.ts +132 -0
  141. package/lib/models/shape.d.ts +44 -0
  142. package/lib/plot.utilities.d.ts +107 -0
  143. package/lib/processing/image-converter.service.d.ts +28 -0
  144. package/lib/processing/image-crop.d.ts +18 -0
  145. package/lib/processing/processing-image.d.ts +17 -0
  146. package/lib/provide-visualization.d.ts +34 -0
  147. package/lib/region-editor/mask-raster.d.ts +61 -0
  148. package/lib/region-editor/mask-worker.d.ts +7 -0
  149. package/lib/region-editor/region-editor.component.d.ts +305 -0
  150. package/lib/region-ops.service.d.ts +92 -0
  151. package/lib/render-orchestrator.d.ts +59 -0
  152. package/lib/routing-visualizer.service.d.ts +227 -0
  153. package/lib/store/class-color.util.d.ts +26 -0
  154. package/lib/store/region-store.service.d.ts +315 -0
  155. package/lib/store/visualizer-store.service.d.ts +123 -0
  156. package/lib/toolbar/brush/brush-tool.service.d.ts +143 -0
  157. package/lib/toolbar/crop/slide-crop.d.ts +30 -0
  158. package/lib/toolbar/segmentation/cell-segment-tool.service.d.ts +32 -0
  159. package/lib/toolbar/segmentation/cellpose-segmenter.service.d.ts +39 -0
  160. package/lib/toolbar/segmentation/onnx-sam-session.d.ts +25 -0
  161. package/lib/toolbar/segmentation/ort-runtime-config.d.ts +4 -0
  162. package/lib/toolbar/segmentation/sam-model-registry.d.ts +36 -0
  163. package/lib/toolbar/segmentation/sam-onnx-core.d.ts +48 -0
  164. package/lib/toolbar/segmentation/sam-point-tool.service.d.ts +59 -0
  165. package/lib/toolbar/segmentation/sam-prompt.d.ts +34 -0
  166. package/lib/toolbar/segmentation/sam-tool.service.d.ts +50 -0
  167. package/lib/toolbar/toolbar.component.d.ts +167 -0
  168. package/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.d.ts +64 -0
  169. package/lib/toolbar/wand/wand-tool.service.d.ts +149 -0
  170. package/lib/toolbar/wand/wand.service.d.ts +133 -0
  171. package/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.d.ts +49 -0
  172. package/lib/visualization.module.d.ts +45 -0
  173. package/lib/visualizer.component.d.ts +378 -0
  174. package/package.json +73 -0
  175. package/src/lib/assets/3d-coordinates.svg +49 -0
  176. package/src/lib/assets/cells.svg +1 -0
  177. package/src/lib/assets/click.svg +1 -0
  178. package/src/lib/assets/colormap-luts.json +1 -0
  179. package/src/lib/assets/contour.svg +48 -0
  180. package/src/lib/assets/cube-3d.svg +1 -0
  181. package/src/lib/assets/edit-selection-white.svg +47 -0
  182. package/src/lib/assets/heatmap.svg +1 -0
  183. package/src/lib/assets/icons/colormap-Pastel1.png +0 -0
  184. package/src/lib/assets/icons/colormap-accent.png +0 -0
  185. package/src/lib/assets/icons/colormap-afmhot.png +0 -0
  186. package/src/lib/assets/icons/colormap-autumn.png +0 -0
  187. package/src/lib/assets/icons/colormap-berlin.png +0 -0
  188. package/src/lib/assets/icons/colormap-binary.png +0 -0
  189. package/src/lib/assets/icons/colormap-blackbody.png +0 -0
  190. package/src/lib/assets/icons/colormap-bluered.png +0 -0
  191. package/src/lib/assets/icons/colormap-blues.png +0 -0
  192. package/src/lib/assets/icons/colormap-bone.png +0 -0
  193. package/src/lib/assets/icons/colormap-brbg.png +0 -0
  194. package/src/lib/assets/icons/colormap-brg.png +0 -0
  195. package/src/lib/assets/icons/colormap-bugn.png +0 -0
  196. package/src/lib/assets/icons/colormap-bupu.png +0 -0
  197. package/src/lib/assets/icons/colormap-bwr.png +0 -0
  198. package/src/lib/assets/icons/colormap-cividis.png +0 -0
  199. package/src/lib/assets/icons/colormap-cmrmap.png +0 -0
  200. package/src/lib/assets/icons/colormap-cool.png +0 -0
  201. package/src/lib/assets/icons/colormap-coolwarm.png +0 -0
  202. package/src/lib/assets/icons/colormap-copper.png +0 -0
  203. package/src/lib/assets/icons/colormap-cubehelix.png +0 -0
  204. package/src/lib/assets/icons/colormap-dark2.png +0 -0
  205. package/src/lib/assets/icons/colormap-earth.png +0 -0
  206. package/src/lib/assets/icons/colormap-electric.png +0 -0
  207. package/src/lib/assets/icons/colormap-flag.png +0 -0
  208. package/src/lib/assets/icons/colormap-gist_earth.png +0 -0
  209. package/src/lib/assets/icons/colormap-gist_gray.png +0 -0
  210. package/src/lib/assets/icons/colormap-gist_heat.png +0 -0
  211. package/src/lib/assets/icons/colormap-gist_ncar.png +0 -0
  212. package/src/lib/assets/icons/colormap-gist_rainbow.png +0 -0
  213. package/src/lib/assets/icons/colormap-gist_stern.png +0 -0
  214. package/src/lib/assets/icons/colormap-gist_yarg.png +0 -0
  215. package/src/lib/assets/icons/colormap-gnbu.png +0 -0
  216. package/src/lib/assets/icons/colormap-gnuplot.png +0 -0
  217. package/src/lib/assets/icons/colormap-gnuplot2.png +0 -0
  218. package/src/lib/assets/icons/colormap-gray.png +0 -0
  219. package/src/lib/assets/icons/colormap-greens-inv.png +0 -0
  220. package/src/lib/assets/icons/colormap-greens.png +0 -0
  221. package/src/lib/assets/icons/colormap-greys-inv.png +0 -0
  222. package/src/lib/assets/icons/colormap-greys.png +0 -0
  223. package/src/lib/assets/icons/colormap-hot.png +0 -0
  224. package/src/lib/assets/icons/colormap-hsv.png +0 -0
  225. package/src/lib/assets/icons/colormap-inferno.png +0 -0
  226. package/src/lib/assets/icons/colormap-jet.png +0 -0
  227. package/src/lib/assets/icons/colormap-magma.png +0 -0
  228. package/src/lib/assets/icons/colormap-managua.png +0 -0
  229. package/src/lib/assets/icons/colormap-nipy_spectral.png +0 -0
  230. package/src/lib/assets/icons/colormap-ocean.png +0 -0
  231. package/src/lib/assets/icons/colormap-oranges.png +0 -0
  232. package/src/lib/assets/icons/colormap-orrd.png +0 -0
  233. package/src/lib/assets/icons/colormap-pastel2.png +0 -0
  234. package/src/lib/assets/icons/colormap-picnic.png +0 -0
  235. package/src/lib/assets/icons/colormap-pink.png +0 -0
  236. package/src/lib/assets/icons/colormap-piyg.png +0 -0
  237. package/src/lib/assets/icons/colormap-placeholder.png +0 -0
  238. package/src/lib/assets/icons/colormap-plasma.png +0 -0
  239. package/src/lib/assets/icons/colormap-portland.png +0 -0
  240. package/src/lib/assets/icons/colormap-prgn.png +0 -0
  241. package/src/lib/assets/icons/colormap-prism.png +0 -0
  242. package/src/lib/assets/icons/colormap-pubu.png +0 -0
  243. package/src/lib/assets/icons/colormap-pubugn.png +0 -0
  244. package/src/lib/assets/icons/colormap-puor.png +0 -0
  245. package/src/lib/assets/icons/colormap-purd.png +0 -0
  246. package/src/lib/assets/icons/colormap-purples.png +0 -0
  247. package/src/lib/assets/icons/colormap-rainbow.png +0 -0
  248. package/src/lib/assets/icons/colormap-rdbu.png +0 -0
  249. package/src/lib/assets/icons/colormap-rdgy.png +0 -0
  250. package/src/lib/assets/icons/colormap-rdpu.png +0 -0
  251. package/src/lib/assets/icons/colormap-rdylbu.png +0 -0
  252. package/src/lib/assets/icons/colormap-rdylgn.png +0 -0
  253. package/src/lib/assets/icons/colormap-reds.png +0 -0
  254. package/src/lib/assets/icons/colormap-seismic.png +0 -0
  255. package/src/lib/assets/icons/colormap-set1.png +0 -0
  256. package/src/lib/assets/icons/colormap-set2.png +0 -0
  257. package/src/lib/assets/icons/colormap-set3.png +0 -0
  258. package/src/lib/assets/icons/colormap-spectral.png +0 -0
  259. package/src/lib/assets/icons/colormap-spring.png +0 -0
  260. package/src/lib/assets/icons/colormap-summer.png +0 -0
  261. package/src/lib/assets/icons/colormap-tab10.png +0 -0
  262. package/src/lib/assets/icons/colormap-tab20.png +0 -0
  263. package/src/lib/assets/icons/colormap-tab20b.png +0 -0
  264. package/src/lib/assets/icons/colormap-tab20c.png +0 -0
  265. package/src/lib/assets/icons/colormap-terrain.png +0 -0
  266. package/src/lib/assets/icons/colormap-turbo.png +0 -0
  267. package/src/lib/assets/icons/colormap-twilight.png +0 -0
  268. package/src/lib/assets/icons/colormap-twilight_shifted.png +0 -0
  269. package/src/lib/assets/icons/colormap-vanimo.png +0 -0
  270. package/src/lib/assets/icons/colormap-viridis.png +0 -0
  271. package/src/lib/assets/icons/colormap-winter.png +0 -0
  272. package/src/lib/assets/icons/colormap-wistia.png +0 -0
  273. package/src/lib/assets/icons/colormap-ylgn.png +0 -0
  274. package/src/lib/assets/icons/colormap-ylgnbu-inv.png +0 -0
  275. package/src/lib/assets/icons/colormap-ylgnbu.png +0 -0
  276. package/src/lib/assets/icons/colormap-ylorbr.png +0 -0
  277. package/src/lib/assets/icons/colormap-ylorrd-inv.png +0 -0
  278. package/src/lib/assets/icons/colormap-ylorrd.png +0 -0
  279. package/src/lib/assets/interactive-segmentation-cursor.svg +1 -0
  280. package/src/lib/assets/invert.svg +1 -0
  281. package/src/lib/assets/isosurface.svg +7 -0
  282. package/src/lib/assets/mask.svg +1 -0
  283. package/src/lib/assets/paintbrush.svg +1 -0
  284. package/src/lib/assets/picture-segmentation.svg +43 -0
  285. package/src/lib/assets/polygon-vertices.svg +13 -0
  286. package/src/lib/assets/polyline.svg +3 -0
  287. package/src/lib/assets/redo.svg +39 -0
  288. package/src/lib/assets/region-editor.svg +37 -0
  289. package/src/lib/assets/region-move.svg +7 -0
  290. package/src/lib/assets/smooth.svg +56 -0
  291. package/src/lib/assets/spline-chart.svg +7 -0
  292. package/src/lib/assets/surface.svg +37 -0
  293. package/src/lib/assets/test/download-379.svg +1 -0
  294. package/src/lib/assets/test/download-98.svg +1 -0
  295. package/src/lib/assets/test/json-1.svg +1 -0
  296. package/src/lib/assets/test/load-53.svg +1 -0
  297. package/src/lib/assets/test/mask-44.svg +1 -0
  298. package/src/lib/assets/test/mask-download-44.svg +15 -0
  299. package/src/lib/assets/test/mask-save-44.svg +11 -0
  300. package/src/lib/assets/test/overlay-1.svg +1 -0
  301. package/src/lib/assets/test/overlay-4.svg +1 -0
  302. package/src/lib/assets/test/region-26.svg +1 -0
  303. package/src/lib/assets/test/save-56.svg +1 -0
  304. package/src/lib/assets/test/upload-43.svg +1 -0
  305. package/src/lib/assets/to-bezier.svg +15 -0
  306. package/src/lib/assets/to-polygon.svg +11 -0
  307. package/src/lib/assets/undo.svg +38 -0
  308. package/src/lib/assets/vertex-add.svg +12 -0
  309. package/src/lib/assets/vertex-delete.svg +12 -0
  310. package/src/lib/assets/wand.svg +54 -0
  311. package/src/lib/assets/zoom-box.svg +10 -0
@@ -0,0 +1,2137 @@
1
+ import { Injectable, Inject, Optional } from '@angular/core';
2
+ import * as Plotly from 'plotly.js-dist-min';
3
+ import { Image } from 'image-js';
4
+ import { HttpClient } from '@angular/common/http';
5
+ import { firstValueFrom } from 'rxjs';
6
+ import { Polygon, Rectangle, Region } from '../../models/region';
7
+ import { Buffer } from 'buffer';
8
+ import { TILE_ACCESS_PORT } from '../../contracts/ports/tile-access.port';
9
+ import { IMAGE_STATE_PORT } from '../../contracts/ports/image-state.port';
10
+ import { BehaviorSubject, EMPTY, Subject, combineLatest, of } from 'rxjs';
11
+ import { MessageService } from 'primeng/api';
12
+ import { ShapeSelection } from '../../models/shape';
13
+ import { CONFIG, CONFIG_SURFACE, PlotUtilities } from '../../plot.utilities';
14
+ import { WandService } from '../../toolbar/wand/wand.service';
15
+ import { WandToolService } from '../../toolbar/wand/wand-tool.service';
16
+ import { BrushToolService } from '../../toolbar/brush/brush-tool.service';
17
+ import { SamToolService } from '../../toolbar/segmentation/sam-tool.service';
18
+ import { SamPointToolService } from '../../toolbar/segmentation/sam-point-tool.service';
19
+ import { CellSegmentToolService } from '../../toolbar/segmentation/cell-segment-tool.service';
20
+ import { CELL_SEGMENTER } from '../../contracts/cell-segmenter.contract';
21
+ import { VertexEraserToolService } from '../../toolbar/vertex-eraser/vertex-eraser-tool.service';
22
+ import { ZoomToBoxToolService } from '../../toolbar/zoom-to-box/zoom-to-box-tool.service';
23
+ import { PlotType, PLOT_TYPE_DESCRIPTORS } from '../../contracts/plot-type';
24
+ import { PLOTLY_PLOT_TYPE_IMPLS, } from './plotly-trace-builders';
25
+ import { bt601Luminance, histogram256 } from '../../contracts/intensity';
26
+ import { ViewerFeature, capabilitiesOf } from '../../contracts/capabilities.contract';
27
+ import { PlotlyRegionOverlay } from './plotly-region-overlay';
28
+ import { PlotlyCoordinateTransform } from './plotly-coordinate-transform';
29
+ import { VisualizerStore } from '../../store/visualizer-store.service';
30
+ import { RegionStore } from '../../store/region-store.service';
31
+ import * as i0 from "@angular/core";
32
+ import * as i1 from "primeng/api";
33
+ import * as i2 from "@angular/common/http";
34
+ import * as i3 from "../../toolbar/wand/wand.service";
35
+ import * as i4 from "../../toolbar/wand/wand-tool.service";
36
+ import * as i5 from "../../toolbar/brush/brush-tool.service";
37
+ import * as i6 from "../../toolbar/segmentation/sam-tool.service";
38
+ import * as i7 from "../../toolbar/segmentation/sam-point-tool.service";
39
+ import * as i8 from "../../toolbar/segmentation/cell-segment-tool.service";
40
+ import * as i9 from "../../toolbar/vertex-eraser/vertex-eraser-tool.service";
41
+ import * as i10 from "../../toolbar/zoom-to-box/zoom-to-box-tool.service";
42
+ import * as i11 from "../../store/visualizer-store.service";
43
+ import * as i12 from "../../store/region-store.service";
44
+ // Re-exported so existing consumers can keep importing PlotType from this
45
+ // module while it physically lives in the backend-neutral contracts/ dir.
46
+ // TODO(plotting-abstraction): once all consumers import from
47
+ // './contracts/plot-type' directly, drop this re-export.
48
+ export { PlotType } from '../../contracts/plot-type';
49
+ window.Buffer = Buffer;
50
+ export class PlotlyService {
51
+ constructor(tiles, state, messageService, http, wandService, wandTool, brushTool, samTool, samPointTool, cellSegmentTool, cellSegmenter, vertexEraserTool, zoomToBoxTool, store, regionStore) {
52
+ this.tiles = tiles;
53
+ this.state = state;
54
+ this.messageService = messageService;
55
+ this.http = http;
56
+ this.wandService = wandService;
57
+ this.wandTool = wandTool;
58
+ this.brushTool = brushTool;
59
+ this.samTool = samTool;
60
+ this.samPointTool = samPointTool;
61
+ this.cellSegmentTool = cellSegmentTool;
62
+ this.cellSegmenter = cellSegmenter;
63
+ this.vertexEraserTool = vertexEraserTool;
64
+ this.zoomToBoxTool = zoomToBoxTool;
65
+ this.store = store;
66
+ this.regionStore = regionStore;
67
+ /**
68
+ * Plotly is the full-featured data backend: it supports every feature,
69
+ * including 3D scenes, live scalar colormaps, pixel readback and the
70
+ * server-side high-def zoom re-fetch. (OpenSeadragon's stub advertises only
71
+ * image display.)
72
+ */
73
+ this.capabilities = capabilitiesOf([
74
+ ViewerFeature.ImageDisplay,
75
+ ViewerFeature.Surface3D,
76
+ ViewerFeature.ScalarColormap,
77
+ ViewerFeature.PixelReadback,
78
+ ViewerFeature.HighDefZoom,
79
+ ViewerFeature.StackSlider,
80
+ ViewerFeature.Isosurface,
81
+ ]);
82
+ this.shapes = [];
83
+ this.previousShapes = [];
84
+ this.isRegionSavedOn = true;
85
+ this.isRealZoom = true;
86
+ this.scaleratio = true;
87
+ this.zoomCoordinates = [];
88
+ this.plotUtilities = new PlotUtilities();
89
+ this.onPlotMouseDown = null;
90
+ this.cachedImageWidth = 0;
91
+ this.cachedImageHeight = 0;
92
+ this.cachedImageRatios = [1, 1];
93
+ this.cachedIsGrayscale = false;
94
+ /** Data-space coordinate of the cached frame's pixel (0,0). [0,0] for the full
95
+ * image; a crop's top-left when zoomed (so the intensity profile samples the
96
+ * zoom-level pixels at the right offset/resolution). */
97
+ this.cachedFrameOrigin = [0, 0];
98
+ /** Last visible image-pixel rectangle from the active backend's viewport (set by
99
+ * the OSD viewport-change hook via refreshIntensitySamplingForRoi). New profile lines are
100
+ * placed within it so they land on-screen when zoomed in; null → use full image. */
101
+ this.lastVisibleRoi = null;
102
+ this.stackLoading$ = new BehaviorSubject(false);
103
+ this.stackLoadingProgress$ = new BehaviorSubject(0);
104
+ // current index of image in stack (if stack), 0 if single image
105
+ this.zIndex = new BehaviorSubject(0);
106
+ this.autoscaleEvent = new Subject();
107
+ // The region update event and selection stream are owned by the shared
108
+ // RegionStore; getRegionUpdateEvent()/getSelectedShapeIndices$() delegate to
109
+ // it so every consumer (and the OSD backend) sees one stream.
110
+ /** Bright, well-separated colours cycled as the user adds profile-line ROIs.
111
+ * The matching inset trace is drawn in the same colour. */
112
+ this.PROFILE_PALETTE = [
113
+ '#FFD400', // yellow
114
+ '#00E5FF', // cyan
115
+ '#FF2D95', // magenta
116
+ '#39FF14', // green
117
+ '#FF9500', // orange
118
+ '#7C4DFF', // violet
119
+ '#FF3B30', // red
120
+ '#18FFFF', // aqua
121
+ ];
122
+ /** Palette cursor for profile lines (never reset on delete, so colours keep
123
+ * cycling across the session). */
124
+ this.profileColorSeq = 0;
125
+ this.intensityProfile$ = new Subject();
126
+ /** ISOSURFACE band as a 0–255 slider position, mapped onto the volume's real
127
+ * intensity range at render time (see mapIsoBand). Defaults to the full range
128
+ * so the first render shows the whole structure regardless of how bright or
129
+ * dark the volume is; the user then narrows the band to isolate intensities. */
130
+ this.isoMin = 0;
131
+ this.isoMax = 255;
132
+ /** Actual intensity range of the volume on screen ([min,max] over the loaded
133
+ * frames). The slider spans 0–255, but a given stack often occupies only a
134
+ * sub-range, so we clamp the iso band into this range (with a small margin)
135
+ * to guarantee non-degenerate surfaces — both on first render and on live
136
+ * slider moves. Null until an isosurface volume has been measured. */
137
+ this.isoDataRange = null;
138
+ // Id minting and the selection index stream are owned by the shared
139
+ // RegionStore. Selection still drives Plotly's `_activeShapeIndex` (so a
140
+ // single shape gets the edit handles) — see setSelectedShapeIndices().
141
+ this.imageCached = false;
142
+ this.coordinateTransform = new PlotlyCoordinateTransform(() => document.getElementById(this.plotDiv), () => this.getOverlayContainer());
143
+ // relayout event router
144
+ this.onRelayoutEvent = (event) => { this.relayoutEventHandler(event); };
145
+ // Tool services read/mutate our state via small *Host interfaces. Stored
146
+ // so we can re-bind them to ourselves whenever a Plotly tool is activated
147
+ // (the OpenSeadragon backend re-binds the same singleton tools to itself).
148
+ this.wandHost = {
149
+ getRegions: () => this.regionStore.getRegions(),
150
+ setRegions: (regions) => this.setRegions(regions),
151
+ getCachedImageData: () => this.getCachedImageData(),
152
+ getActiveFrameIndex: () => this.activeFrameIndex(),
153
+ getOverlayContainer: () => this.getOverlayContainer(),
154
+ getCoordinateTransform: () => this.getCoordinateTransform(),
155
+ getFileName: () => this.fileName,
156
+ getShapeColor: () => this.regionStore.getShapeColor(),
157
+ };
158
+ this.eraserHost = {
159
+ getRegions: () => this.regionStore.getRegions(),
160
+ setRegions: (regions) => this.setRegions(regions),
161
+ invalidateWandRegion: () => this.wandTool.clearActiveRegion(),
162
+ getOverlayContainer: () => this.getOverlayContainer(),
163
+ getCoordinateTransform: () => this.getCoordinateTransform(),
164
+ getCachedImageRatio: () => this.cachedImageRatios[0] || 1,
165
+ };
166
+ this.wandTool.bindHost(this.wandHost);
167
+ // The brush reuses the wand host (same coordinate frame + region access).
168
+ this.brushTool.bindHost(this.wandHost);
169
+ this.vertexEraserTool.bindHost(this.eraserHost);
170
+ this.bindZoomToBoxHost();
171
+ this.imageCachedSubscription = this.state.isImageCached$().subscribe(imageCached => {
172
+ this.imageCached = imageCached;
173
+ });
174
+ this.filenameSubscription = this.state.getFilename$().subscribe(filename => {
175
+ this.fileName = filename;
176
+ });
177
+ // Live recolor from the Channels & Histogram pane: restyle the display window (zmin/zmax for
178
+ // a heatmap, cmin/cmax for isosurface/surface), the colorscale (colormap), and reverse/invert
179
+ // when the channel state OR the colormap changes. (Gamma is applied by the OSD image view; the
180
+ // Plotly colorscale window covers the common contrast case.)
181
+ this.channelSub = combineLatest([
182
+ this.store.getChannelStates(),
183
+ this.store.getReverseScale(),
184
+ this.store.getInvert(),
185
+ this.store.getColormap(),
186
+ ]).subscribe(([channels, rev, inv]) => this.applyChannelDisplay(channels, rev, inv));
187
+ // Profile lines are store regions; any region change (add/drag/delete, on
188
+ // either backend) should refresh the inset traces. The live-edit stream
189
+ // fires per frame during a drag (OSD coalesces regionUpdate$ until release),
190
+ // so the inset tracks an OSD line ROI live as it's moved.
191
+ this.regionUpdateSubscription = this.regionStore.getRegionUpdateEvent().subscribe(() => {
192
+ this.emitProfiles();
193
+ });
194
+ this.regionLiveEditSubscription = this.regionStore.getRegionLiveEdit$().subscribe(() => {
195
+ this.emitProfiles();
196
+ });
197
+ }
198
+ getTrueImageSize() {
199
+ if (!this.trueImgSize)
200
+ return null;
201
+ return {
202
+ width: this.trueImgSize[1] - this.trueImgSize[0],
203
+ height: this.trueImgSize[3] - this.trueImgSize[2],
204
+ };
205
+ }
206
+ /**
207
+ * Load image
208
+ * @param imageInfo
209
+ * @param zIndex index of the image to load
210
+ * @return an object with data and ratio keys
211
+ */
212
+ async load(imageInfo, zIndex) {
213
+ const urls = imageInfo.urls;
214
+ // use zIndex provided if any, 0 otherwise
215
+ let imageUrl;
216
+ if (zIndex) {
217
+ imageUrl = urls[zIndex];
218
+ }
219
+ else {
220
+ imageUrl = urls[0];
221
+ }
222
+ const isGrayscale = imageInfo.isGrayscale;
223
+ const image = await this.loadImage(imageUrl);
224
+ const xRatio = imageInfo.trueImageSize[0] / image.width;
225
+ const yRatio = imageInfo.trueImageSize[1] / image.height;
226
+ const trueImageSize = [];
227
+ // [x0, x1, y0, y1]
228
+ trueImageSize[0] = 0;
229
+ trueImageSize[1] = imageInfo.trueImageSize[0];
230
+ trueImageSize[2] = 0;
231
+ trueImageSize[3] = imageInfo.trueImageSize[1];
232
+ this.trueImgSize = trueImageSize;
233
+ this.fileName = imageInfo.fileName;
234
+ if (imageInfo.isStack && imageInfo.showStack) {
235
+ const images = [];
236
+ this.stackLoadingProgress$.next(0);
237
+ // One URL per slice — load them all (earlier `length-1` dropped the last).
238
+ for (let i = 0; i < urls.length; i++) {
239
+ console.log(`image stack ${i}`);
240
+ // do not keep with loading if filename is different (a new file has been selected)
241
+ // or if the stackLoading value is set to false
242
+ if (this.fileName === imageInfo.fileName && this.stackLoading$.value) {
243
+ const image = await this.loadImage(urls[i]);
244
+ if (isGrayscale) {
245
+ const grey = image.grey();
246
+ const imgData = this.plotUtilities.arrayToMatrix(Array.from(grey.data), image.width);
247
+ images.push(imgData);
248
+ }
249
+ else {
250
+ const rgbData = image.getPixelsArray();
251
+ const rgbMatrix = this.plotUtilities.arrayToMatrix(rgbData, image.width);
252
+ images.push(rgbMatrix);
253
+ }
254
+ this.stackLoadingProgress$.next(Math.round((i * 100) / urls.length));
255
+ }
256
+ else {
257
+ // break stack loading
258
+ break;
259
+ }
260
+ }
261
+ // reset stackLoading progress to 0
262
+ this.stackLoadingProgress$.next(0);
263
+ return { data: images, ratios: [xRatio, yRatio],
264
+ sizes: [image.width, image.height],
265
+ filename: imageInfo.fileName };
266
+ }
267
+ else {
268
+ let imageData;
269
+ if (isGrayscale) {
270
+ const grey = image.grey();
271
+ imageData = this.plotUtilities.arrayToMatrix(Array.from(grey.data), image.width);
272
+ }
273
+ else {
274
+ const rgbData = image.getPixelsArray();
275
+ imageData = this.plotUtilities.arrayToMatrix(rgbData, image.width);
276
+ }
277
+ return { data: [imageData],
278
+ ratios: [xRatio, yRatio],
279
+ sizes: [image.width, image.height],
280
+ filename: imageInfo.fileName };
281
+ }
282
+ }
283
+ /**
284
+ *
285
+ * @param plotDiv
286
+ * @param imageLoaded object with data, ratios and sizes key
287
+ * @param imageInfo ImageInfo object
288
+ * @param screenHeight size of the available screen height
289
+ * @param plotType type of plotting
290
+ */
291
+ /**
292
+ * @param inPlace when true, updates the existing plot via Plotly.react
293
+ * instead of Plotly.purge + Plotly.newPlot. Used by the multi-tier
294
+ * diagram swap (small → large) so the canvas doesn't briefly blank
295
+ * between phases.
296
+ */
297
+ plot(plotDiv, imageLoaded, imageInfo, screenHeight, plotType, inPlace = false) {
298
+ const trueImageSize = [];
299
+ this.zoomCoordinates = [];
300
+ // [x0, x1, y0, y1]
301
+ trueImageSize[0] = 0;
302
+ trueImageSize[1] = imageInfo.trueImageSize[0];
303
+ trueImageSize[2] = 0;
304
+ trueImageSize[3] = imageInfo.trueImageSize[1];
305
+ // Save the current image's regions and pull in any cached regions for the
306
+ // image we're about to display, before Plotly.newPlot reads `this.shapes`.
307
+ this.setActiveImage(imageInfo);
308
+ this.imageInfo = imageInfo;
309
+ this.plotType = plotType;
310
+ // Cache pixel matrices so the wand tool can sample them.
311
+ this.cachedImageFrames = imageLoaded.data;
312
+ this.cachedImageWidth = imageLoaded.sizes[0];
313
+ this.cachedImageHeight = imageLoaded.sizes[1];
314
+ this.cachedImageRatios = imageLoaded.ratios;
315
+ this.cachedFrameOrigin = [0, 0]; // full image — reset any prior zoom-crop origin
316
+ this.cachedIsGrayscale = !!imageInfo.isGrayscale;
317
+ // Pluggable plot types (contour, scatter, scatter3d, isosurface) render
318
+ // through the trace-builder registry. The original HEATMAP/SURFACE/RGB-image
319
+ // renderers keep their dedicated paths below (also reused by the high-def
320
+ // zoom re-fetch). Intensity profiles are no longer a plot type — they're
321
+ // Region-based line ROIs, available in HEATMAP/IMAGE mode.
322
+ const impl = PLOTLY_PLOT_TYPE_IMPLS[plotType];
323
+ if (impl) {
324
+ this.imageInfo.isGrayscale = !!imageInfo.isGrayscale;
325
+ return this.plotViaRegistry(plotDiv, impl, this.buildTraceInput(imageInfo, imageLoaded, trueImageSize), screenHeight, inPlace);
326
+ }
327
+ if (imageInfo.isGrayscale) {
328
+ if (plotType === PlotType.SURFACE) {
329
+ return this.plotSurface(plotDiv, imageInfo.urls, imageLoaded.data, trueImageSize, imageLoaded.ratios, screenHeight, inPlace);
330
+ }
331
+ return this.plotHeatmap(plotDiv, imageInfo.urls, imageLoaded.data, trueImageSize, imageLoaded.ratios, screenHeight, inPlace);
332
+ }
333
+ return this.plotRGBHeatmap(plotDiv, imageInfo.urls, imageLoaded.data, trueImageSize, imageLoaded.ratios, imageLoaded.sizes[0], imageLoaded.sizes[1], screenHeight, inPlace);
334
+ }
335
+ /**
336
+ * Function used to plot a grayscale heatmap
337
+ * @param plotDiv
338
+ * @param urls
339
+ * @param images
340
+ * @param trueImgSize array containing the following : [x0, x1, y0, y1]
341
+ * @param ratios
342
+ * @param screenHeight
343
+ * @return Promise true when plotting is finished
344
+ */
345
+ plotHeatmap(plotDiv, urls, images, trueImgSize, ratios, screenHeight, inPlace = false) {
346
+ if (!inPlace)
347
+ Plotly.purge(plotDiv);
348
+ this.plotDiv = plotDiv;
349
+ this.imageLength = images.length;
350
+ this.urls = urls;
351
+ this.screenHeight = screenHeight;
352
+ // plot
353
+ const traces = [];
354
+ images.forEach((dataset, index) => {
355
+ const trace = {
356
+ x0: trueImgSize[0],
357
+ dx: ratios[0],
358
+ y0: trueImgSize[2],
359
+ dy: ratios[0],
360
+ z: dataset,
361
+ type: 'heatmap',
362
+ // We remove the tooltip annotation for performances reasons
363
+ // text: dataset.map((row: any[], i: any) => row.map((item, j) => {
364
+ // return `x: ${+(j * ratios[0]).toFixed(2)}<br>y: ${+(i * ratios[0]).toFixed(2)}<br>value: ${item}`})),
365
+ hoverinfo: 'none',
366
+ colorscale: this.store.currentColormap().data.value,
367
+ reversescale: this.store.currentReverseScale(),
368
+ name: `Slice ${index + 1}`,
369
+ visible: index === 0
370
+ };
371
+ traces.push(trace);
372
+ });
373
+ const render = inPlace ? Plotly.react : Plotly.newPlot;
374
+ return render(plotDiv, traces, this.getHeatmapLayout([trueImgSize[0], trueImgSize[1]], [trueImgSize[3],
375
+ trueImgSize[2]]), CONFIG).then(() => {
376
+ // handle the relayout event for zoom / rois and the click event
377
+ this.setEvents(plotDiv, true, screenHeight);
378
+ return true;
379
+ });
380
+ }
381
+ /**
382
+ * Function used to plot a grayscale surface
383
+ * @param plotDiv
384
+ * @param urls
385
+ * @param images
386
+ * @param trueImgSize array containing the following : [x0, x1, y0, y1]
387
+ * @param ratios
388
+ * @param screenHeight
389
+ * @return Promise true when plotting is finished
390
+ */
391
+ plotSurface(plotDiv, urls, images, trueImgSize, ratios, screenHeight, inPlace = false) {
392
+ if (!inPlace)
393
+ Plotly.purge(plotDiv);
394
+ this.plotDiv = plotDiv;
395
+ this.imageLength = images.length;
396
+ this.urls = urls;
397
+ this.screenHeight = screenHeight;
398
+ // plot
399
+ const traces = [];
400
+ images.forEach((dataset, index) => {
401
+ const trace = {
402
+ z: dataset,
403
+ type: 'surface',
404
+ // hoverinfo: 'none',
405
+ colorscale: this.store.currentColormap().data.value,
406
+ reversescale: this.store.currentReverseScale(),
407
+ // name: `Slice ${index + 1}`,
408
+ // visible: index === 0
409
+ };
410
+ traces.push(trace);
411
+ });
412
+ const xrange = [trueImgSize[0], trueImgSize[1]];
413
+ const yrange = [trueImgSize[2], trueImgSize[3]];
414
+ const render = inPlace ? Plotly.react : Plotly.newPlot;
415
+ return render(plotDiv, traces, this.getSurfaceLayout(xrange, yrange, 0.4), CONFIG_SURFACE).then(() => {
416
+ // handle the relayout event for zoom / rois and the click event
417
+ this.setEvents(plotDiv, true, screenHeight);
418
+ return true;
419
+ });
420
+ }
421
+ /**
422
+ * Function used to plot an RGB heatmap
423
+ * @param plotDiv
424
+ * @param urls
425
+ * @param images
426
+ * @param trueImgSize
427
+ * @param width
428
+ * @param height
429
+ * @param ratios
430
+ * @param screenHeight
431
+ * @return Promise
432
+ */
433
+ plotRGBHeatmap(plotDiv, urls, images, trueImgSize, ratios, width, height, screenHeight, inPlace = false) {
434
+ // Plotly.react updates in place; Plotly.purge + Plotly.newPlot blanks
435
+ // the canvas for ~100ms which makes the multi-tier small→large swap
436
+ // look like a regression to "loading" before the sharper version
437
+ // appears.
438
+ if (!inPlace)
439
+ Plotly.purge(plotDiv);
440
+ this.plotDiv = plotDiv;
441
+ this.imageLength = images.length;
442
+ this.urls = urls;
443
+ this.screenHeight = screenHeight;
444
+ // as autorange is set to false, and not reversed we need to set the yrange correcly here
445
+ const layout = this.getHeatmapLayout([trueImgSize[0], trueImgSize[1]], [trueImgSize[3], trueImgSize[2]]);
446
+ const traces = [];
447
+ images.forEach((dataset, index) => {
448
+ const trace = {
449
+ x0: trueImgSize[0],
450
+ dx: ratios[0],
451
+ y0: trueImgSize[2],
452
+ dy: ratios[0],
453
+ x: Array.from(Array(width).keys()),
454
+ y: Array.from(Array(height).keys()),
455
+ z: dataset,
456
+ // text: dataset.map((row: any[], i: any) => row.map((item, j) => {
457
+ // return `x: ${+(j * ratios[0]).toFixed(2)}<br>y: ${+(i * ratios[0]).toFixed(2)}<br>value: ${item}`})),
458
+ hoverinfo: 'none',
459
+ type: 'image',
460
+ name: `Slice ${index + 1}`,
461
+ visible: index === 0
462
+ };
463
+ traces.push(trace);
464
+ });
465
+ const render = inPlace ? Plotly.react : Plotly.newPlot;
466
+ return render(plotDiv, traces, layout, CONFIG).then(() => {
467
+ // handle the relayout event
468
+ this.setEvents(plotDiv, false, screenHeight);
469
+ return true;
470
+ });
471
+ }
472
+ /**
473
+ * Assemble the normalised input the pluggable trace builders consume.
474
+ * Pure data only — no Plotly handles — so the builders stay backend-neutral.
475
+ */
476
+ buildTraceInput(imageInfo, imageLoaded, trueImageSize) {
477
+ // Measure the volume's real intensity range so the iso band can be clamped
478
+ // into it (the slider is a fixed 0–255 but a stack may occupy only part of
479
+ // that, which would otherwise leave the surfaces with nothing to cross).
480
+ if (this.plotType === PlotType.ISOSURFACE) {
481
+ this.isoDataRange = this.measureIntensityRange(imageLoaded.data, !!imageInfo.isGrayscale);
482
+ }
483
+ const [isoMin, isoMax] = this.mapIsoBand(this.isoMin, this.isoMax);
484
+ return {
485
+ frames: imageLoaded.data,
486
+ width: imageLoaded.sizes[0],
487
+ height: imageLoaded.sizes[1],
488
+ ratios: imageLoaded.ratios,
489
+ trueImageSize,
490
+ isGrayscale: !!imageInfo.isGrayscale,
491
+ colorscale: this.store.currentColormap().data.value,
492
+ reversescale: this.store.currentReverseScale(),
493
+ regions: this.getRegionPolygons(),
494
+ shapeColor: this.regionStore.getShapeColor(),
495
+ isoMin,
496
+ isoMax,
497
+ };
498
+ }
499
+ /** Min/max intensity over every voxel of the loaded frames (RGB → luminance),
500
+ * used to keep the iso band inside the data. Returns null for empty input. */
501
+ measureIntensityRange(frames, isGrayscale) {
502
+ let min = Infinity, max = -Infinity;
503
+ for (const frame of frames || []) {
504
+ for (const row of frame || []) {
505
+ for (const cell of row || []) {
506
+ const v = isGrayscale ? cell
507
+ : 0.299 * cell[0] + 0.587 * cell[1] + 0.114 * cell[2];
508
+ if (!Number.isFinite(v))
509
+ continue;
510
+ if (v < min)
511
+ min = v;
512
+ if (v > max)
513
+ max = v;
514
+ }
515
+ }
516
+ }
517
+ return (Number.isFinite(min) && Number.isFinite(max) && max > min) ? [min, max] : null;
518
+ }
519
+ /** Map the 0–255 slider band onto the volume's actual intensity range. The
520
+ * slider is fixed 0–255, but a stack often occupies only a narrow sub-range
521
+ * (e.g. this EDOF volume is ~2–50 with structure near 6–10). Mapping spreads
522
+ * the data across the whole slider so every intensity is easy to reach and
523
+ * the band always lands inside the data — a small inset keeps the extreme
524
+ * surfaces off the exact data edges, which are degenerate and draw nothing. */
525
+ mapIsoBand(isoMin, isoMax) {
526
+ let lo = Math.min(isoMin, isoMax);
527
+ let hi = Math.max(isoMin, isoMax);
528
+ if (this.isoDataRange) {
529
+ const [vMin, vMax] = this.isoDataRange;
530
+ const span = vMax - vMin;
531
+ const pad = 0.03 * span;
532
+ const usable = span - 2 * pad;
533
+ const SLIDER_MAX = 255; // toolbar slider domain (isoValueMax)
534
+ lo = vMin + pad + (lo / SLIDER_MAX) * usable;
535
+ hi = vMin + pad + (hi / SLIDER_MAX) * usable;
536
+ if (lo > hi) {
537
+ const t = lo;
538
+ lo = hi;
539
+ hi = t;
540
+ }
541
+ }
542
+ return [lo, hi];
543
+ }
544
+ /**
545
+ * Update the isosurface intensity bounds. When isosurface is on screen this
546
+ * restyles `isomin`/`isomax` in place for a live update (no volume rebuild).
547
+ * Slider values are mapped onto the volume's real range so the surfaces always
548
+ * have data to cross (matching the initial render).
549
+ */
550
+ setIsoRange(isoMin, isoMax) {
551
+ this.isoMin = isoMin;
552
+ this.isoMax = isoMax;
553
+ if (this.plotType === PlotType.ISOSURFACE && this.plotDiv) {
554
+ const [lo, hi] = this.mapIsoBand(isoMin, isoMax);
555
+ try {
556
+ Plotly.restyle(this.plotDiv, { isomin: [lo], isomax: [hi] });
557
+ }
558
+ catch { /* plot not ready */ }
559
+ }
560
+ }
561
+ /** Plotly renders isosurfaces, so it exposes the isosurface controls (itself). */
562
+ getIsosurfaceControls() { return this; }
563
+ /**
564
+ * Render a registry-backed plot type: build its traces from the input, pick
565
+ * the matching layout, render, and wire the usual relayout/click events.
566
+ * Mirrors the structure of the dedicated heatmap/surface renderers.
567
+ */
568
+ plotViaRegistry(plotDiv, impl, input, screenHeight, inPlace = false) {
569
+ if (!inPlace)
570
+ Plotly.purge(plotDiv);
571
+ this.plotDiv = plotDiv;
572
+ this.imageLength = input.frames.length;
573
+ this.urls = this.imageInfo?.urls ?? this.urls;
574
+ this.screenHeight = screenHeight;
575
+ const traces = impl.buildTraces(input);
576
+ const [x0, x1, y0, y1] = input.trueImageSize;
577
+ let layout;
578
+ switch (impl.layoutKind) {
579
+ case '3d-volume':
580
+ layout = this.getVolumeLayout();
581
+ break;
582
+ case '2d-chart':
583
+ layout = this.getChartLayout();
584
+ break;
585
+ case '2d-overlay':
586
+ layout = this.getOverlayLayout([x0, x1], [y1, y0]); // reversed y like the image
587
+ break;
588
+ default: // '2d-image'
589
+ layout = this.getHeatmapLayout([x0, x1], [y1, y0]);
590
+ }
591
+ const config = impl.threeD ? CONFIG_SURFACE : CONFIG;
592
+ const render = inPlace ? Plotly.react : Plotly.newPlot;
593
+ return render(plotDiv, traces, layout, config).then(() => {
594
+ this.setEvents(plotDiv, input.isGrayscale, screenHeight);
595
+ return true;
596
+ });
597
+ }
598
+ /** Plain 2D chart axes (intensity profile / line plots). */
599
+ getChartLayout() {
600
+ return {
601
+ margin: { t: 30, b: 45, l: 60, r: 20 },
602
+ height: this.screenHeight,
603
+ autosize: true,
604
+ xaxis: { title: 'Position (px)' },
605
+ yaxis: { title: 'Intensity' },
606
+ dragmode: false,
607
+ };
608
+ }
609
+ /** Image-aligned 2D axes without the z-plane slider (region scatter). */
610
+ getOverlayLayout(xRange, yRange) {
611
+ return {
612
+ xaxis: { constrain: 'range', constraintoward: 'center', side: 'top', ticks: '', range: xRange },
613
+ yaxis: {
614
+ constrain: 'range', constraintoward: 'center', range: yRange, ticks: '',
615
+ ticksuffix: ' ', autorange: false, scaleanchor: this.scaleratio ? 'x' : false,
616
+ },
617
+ margin: { t: 30, b: 5, l: 55, r: 5 },
618
+ height: this.screenHeight,
619
+ autosize: true,
620
+ shapes: this.currentRenderShapes(this.regionStore.getShowShapeLabel()),
621
+ dragmode: this.dragMode ? this.dragMode : false,
622
+ };
623
+ }
624
+ /** 3D scene for volumetric plot types (scatter3d, isosurface). */
625
+ getVolumeLayout() {
626
+ return {
627
+ margin: { t: 0, b: 0, l: 0, r: 0 },
628
+ height: this.screenHeight,
629
+ autosize: true,
630
+ scene: {
631
+ xaxis: { title: 'X' },
632
+ yaxis: { title: 'Y' },
633
+ zaxis: { title: 'Z-plane' },
634
+ // 'cube' (not 'data'): a z-stack has far fewer planes than X/Y pixels,
635
+ // so 'data' squashes the volume into a near-flat slab that reads as
636
+ // empty edge-on. A cube gives the z dimension real height so the
637
+ // isosurface/voxels are actually visible.
638
+ aspectmode: 'cube',
639
+ },
640
+ };
641
+ }
642
+ /** Plot types this backend advertises (drives the UI selector). */
643
+ getPlotTypeDescriptors() {
644
+ return Object.values(PLOT_TYPE_DESCRIPTORS).filter((d) => !!d);
645
+ }
646
+ /** This backend's region renderer. Plotly draws shapes natively, so the
647
+ * overlay is a thin adapter over this service's drag modes. */
648
+ getRegionOverlay() {
649
+ if (!this.regionOverlay)
650
+ this.regionOverlay = new PlotlyRegionOverlay(this);
651
+ return this.regionOverlay;
652
+ }
653
+ // ── Intensity profile (Region-based line ROIs) ──────────────────────
654
+ /** Emits the full set of intensity profiles (one per profile-line region)
655
+ * whenever a profile line is added, moved, or removed. */
656
+ getIntensityProfile$() {
657
+ return this.intensityProfile$.asObservable();
658
+ }
659
+ /** Plotly renders the line ROIs + inset, so it exposes the intensity controls. */
660
+ getIntensityControls() { return this; }
661
+ /**
662
+ * Render the floating intensity-profile inset chart into `divId` — one line
663
+ * trace per ROI, each drawn in its ROI's colour. Kept here (not in the diagram
664
+ * component) so the consumer never touches Plotly directly.
665
+ */
666
+ renderIntensityInset(divId, profiles) {
667
+ const el = document.getElementById(divId);
668
+ if (!el)
669
+ return;
670
+ const traces = (profiles ?? []).map((p, i) => ({
671
+ x: p.positions,
672
+ y: p.values,
673
+ type: 'scatter',
674
+ mode: 'lines',
675
+ line: { color: p.color ?? '#FFD400', width: 2 },
676
+ name: `Line ${i + 1}`,
677
+ hoverinfo: 'x+y',
678
+ }));
679
+ // X axis is in microns when the image carries a physical pixel size (mpp),
680
+ // otherwise in pixels — driven by the unit the sampler tagged on the profile.
681
+ const unit = (profiles ?? []).find(p => p.unit)?.unit ?? 'px';
682
+ const xTitle = unit === 'µm' ? 'Position (µm)' : 'Position (px)';
683
+ Plotly.react(el, traces, {
684
+ margin: { t: 6, r: 8, b: 38, l: 40 },
685
+ xaxis: { title: xTitle, zeroline: false, color: '#ddd' },
686
+ yaxis: { title: 'Intensity', zeroline: false, color: '#ddd' },
687
+ showlegend: false,
688
+ paper_bgcolor: 'rgba(25,25,25,0.9)',
689
+ plot_bgcolor: 'rgba(25,25,25,0.9)',
690
+ font: { color: '#eee', size: 10 },
691
+ }, { displayModeBar: false, responsive: true });
692
+ }
693
+ /**
694
+ * Region shapes (filtered/redrawn for the current file). The profile-line ROIs
695
+ * are ordinary store regions (tagged `kind: 'profile'`), so they are already
696
+ * part of `this.shapes` — no separate append is needed.
697
+ */
698
+ currentRenderShapes(showLabel = this.regionStore.getShowShapeLabel()) {
699
+ return this.shapesToRedraw(showLabel);
700
+ }
701
+ /**
702
+ * IIntensityControls: add another intensity-profile line as a neutral Region
703
+ * (tagged `kind: 'profile'`). It is a horizontal open 2-point polyline spanning
704
+ * the image width, staggered vertically so successive lines stay distinct, in
705
+ * the next bright palette colour. Added to the shared store so both backends
706
+ * (Plotly + OpenSeadragon) render and drag it; excluded from the Regions tab
707
+ * and exports by its kind.
708
+ */
709
+ addProfileLine() {
710
+ if (!this.trueImgSize)
711
+ return null;
712
+ const count = this.getProfileRegions().length;
713
+ const imgX0 = this.trueImgSize[0], imgX1 = this.trueImgSize[1];
714
+ const imgTop = this.trueImgSize[2], imgBottom = this.trueImgSize[3];
715
+ // Place the line within the currently VISIBLE image area, so when zoomed in it
716
+ // lands on-screen instead of spanning the whole (mostly off-screen) image, at
717
+ // 2/3 of the visible width and horizontally centred. Falls back to the full
718
+ // image when no viewport ROI is known (Plotly heatmap) or a stale ROI no longer
719
+ // overlaps the image.
720
+ const roi = this.lastVisibleRoi;
721
+ const overlaps = !!roi && roi.width > 0 && roi.height > 0
722
+ && roi.x < imgX1 && roi.x + roi.width > imgX0
723
+ && roi.y < imgBottom && roi.y + roi.height > imgTop;
724
+ const x0v = overlaps ? Math.max(imgX0, roi.x) : imgX0;
725
+ const x1v = overlaps ? Math.min(imgX1, roi.x + roi.width) : imgX1;
726
+ const topV = overlaps ? Math.max(imgTop, roi.y) : imgTop;
727
+ const bottomV = overlaps ? Math.min(imgBottom, roi.y + roi.height) : imgBottom;
728
+ const cx = (x0v + x1v) / 2;
729
+ const half = ((x1v - x0v) * (2 / 3)) / 2; // line spans 2/3 of the visible width
730
+ const x0 = cx - half, x1 = cx + half;
731
+ const bandH = bottomV - topV;
732
+ const midY = (topV + bottomV) / 2;
733
+ // Stagger successive lines across the visible band so they stay distinct.
734
+ const y = Math.min(bottomV, Math.max(topV, midY + count * bandH * 0.12));
735
+ const poly = new Polygon();
736
+ poly.npoints = 2;
737
+ poly.xpoints = [x0, x1];
738
+ poly.ypoints = [y, y];
739
+ poly.coordinates = [[x0, y], [x1, y]];
740
+ poly.closed = false;
741
+ const region = new Region();
742
+ region.bounds = poly;
743
+ region.kind = 'profile';
744
+ region.color = this.PROFILE_PALETTE[this.profileColorSeq++ % this.PROFILE_PALETTE.length];
745
+ // Leave region.label undefined so applyClassificationColors won't recolor it.
746
+ this.regionStore.addRegion(region);
747
+ // If a Plotly plot is on screen, re-render shapes so the new line appears.
748
+ // (The OSD overlay renders automatically via its region-update subscription.)
749
+ if (this.plotDiv) {
750
+ this.syncShapesFromStore();
751
+ try {
752
+ Plotly.relayout(this.plotDiv, { shapes: this.currentRenderShapes() });
753
+ }
754
+ catch { /* div not a Plotly plot (OSD owns it) */ }
755
+ }
756
+ return region;
757
+ }
758
+ /** The profile-line ROIs currently in the store (kind === 'profile'). */
759
+ getProfileRegions() {
760
+ return this.regionStore.getRegions().filter(r => r.kind === 'profile');
761
+ }
762
+ /** Recompute and broadcast a profile for every profile-line region (id + colour
763
+ * tagged so the inset trace matches its line). */
764
+ emitProfiles() {
765
+ this.intensityProfile$.next(this.getProfileRegions().map(r => ({
766
+ ...this.computeIntensityProfileForRegion(r),
767
+ id: r.id,
768
+ color: r.color,
769
+ })));
770
+ }
771
+ /**
772
+ * Sample the intensity profile for a profile-line region: extract its two
773
+ * polygon endpoints and reuse the shared `computeIntensityProfile` sampling.
774
+ */
775
+ computeIntensityProfileForRegion(region) {
776
+ // Duck-typed (not `instanceof Polygon`): after a drag round-trips through the
777
+ // store the bounds can be a plain object, so match on the point arrays.
778
+ const poly = region.bounds;
779
+ if (!poly || !Array.isArray(poly.xpoints) || !Array.isArray(poly.ypoints) ||
780
+ poly.xpoints.length < 2 || poly.ypoints.length < 2) {
781
+ return { positions: [], values: [] };
782
+ }
783
+ return this.computeIntensityProfile({
784
+ x0: poly.xpoints[0], y0: poly.ypoints[0],
785
+ x1: poly.xpoints[1], y1: poly.ypoints[1],
786
+ });
787
+ }
788
+ /**
789
+ * Supply pixel frames for intensity sampling (used by the OSD/image backend,
790
+ * whose cached frames may be empty). Optionally also sets the data/pixel ratios.
791
+ */
792
+ setSamplingFrames(frames, ratios, origin = [0, 0]) {
793
+ this.cachedImageFrames = frames;
794
+ if (ratios)
795
+ this.cachedImageRatios = ratios;
796
+ this.cachedFrameOrigin = origin;
797
+ }
798
+ /**
799
+ * Populate the sampling cache for the intensity profiles when OpenSeadragon is
800
+ * the active backend (the Image plot type). In OSD mode Plotly never rendered,
801
+ * so `cachedImageFrames`/`trueImgSize` are unset — fetch the current slice's
802
+ * preview (the same pixels the heatmap would sample) so the line ROIs have
803
+ * pixel data and a placement extent. Re-emits the profiles once loaded.
804
+ */
805
+ async ensureIntensitySampling(imageInfo, zIndex) {
806
+ if (!imageInfo?.urls?.length)
807
+ return;
808
+ // Sample just the displayed slice (showStack off → single frame); `load`
809
+ // also sets `this.trueImgSize`, which addProfileLine needs for placement.
810
+ const single = { ...imageInfo, showStack: false };
811
+ const loaded = await this.load(single, zIndex || 0);
812
+ this.cachedImageFrames = loaded.data;
813
+ this.cachedImageRatios = loaded.ratios;
814
+ this.cachedImageWidth = loaded.sizes?.[0] ?? this.cachedImageWidth;
815
+ this.cachedImageHeight = loaded.sizes?.[1] ?? this.cachedImageHeight;
816
+ this.cachedFrameOrigin = [0, 0]; // full preview
817
+ this.cachedIsGrayscale = !!imageInfo.isGrayscale;
818
+ this.emitProfiles();
819
+ }
820
+ /**
821
+ * {@link IIntensitySampling} stub — the viewport-change signal is OpenSeadragon's
822
+ * (it re-samples on OSD zoom/pan). Plotly's own high-def zoom updates the
823
+ * sampling cache inline, so it never emits here; returns EMPTY so a uniform
824
+ * `IVisualizer` consumer can subscribe regardless of the active backend.
825
+ */
826
+ getViewportChange$() {
827
+ return EMPTY;
828
+ }
829
+ /**
830
+ * Re-fetch the given image-pixel ROI at the display resolution and use it as
831
+ * the intensity-sampling source, so the profile reflects the data at the
832
+ * current zoom level. Shared by both backends: the OSD viewport-change hook
833
+ * calls it on zoom/pan; Plotly's own high-def zoom (triggerZoom) updates the
834
+ * same cache inline. `roi` is in full-image pixel coordinates.
835
+ */
836
+ refreshIntensitySamplingForRoi(x, y, width, height, zIndex) {
837
+ if (width <= 0 || height <= 0)
838
+ return;
839
+ // Remember the visible region so a new profile line is placed inside it.
840
+ this.lastVisibleRoi = { x, y, width, height };
841
+ const roi = new Rectangle();
842
+ roi.x = Math.round(x);
843
+ roi.y = Math.round(y);
844
+ roi.width = Math.round(width);
845
+ roi.height = Math.round(height);
846
+ const screen = this.plotUtilities.getDomRectangle('diagram');
847
+ // Snapshot the filename so a response that arrives after the user switched
848
+ // files is dropped (the request carried the file selected at call time).
849
+ const reqName = this.fileName;
850
+ this.tiles.zoomOnRegion(roi, screen, zIndex || 0).subscribe({
851
+ next: (zoomData) => {
852
+ Image.load(Buffer.from(new Uint8Array(zoomData))).then((image) => {
853
+ if (this.fileName !== reqName)
854
+ return;
855
+ const isGray = !!this.imageInfo?.isGrayscale;
856
+ const frame = isGray
857
+ ? this.plotUtilities.arrayToMatrix(image.grey().data, image.width)
858
+ : this.plotUtilities.arrayToMatrix(image.getPixelsArray(), image.width);
859
+ this.setSamplingFrames([frame], [roi.width / image.width, roi.height / image.height], [roi.x, roi.y]);
860
+ this.emitProfiles();
861
+ });
862
+ },
863
+ error: () => { },
864
+ });
865
+ }
866
+ /**
867
+ * Sample the active frame's intensity (grayscale value or RGB luminance)
868
+ * along the line ROI. Returns distance-along-line positions and values.
869
+ */
870
+ computeIntensityProfile(line) {
871
+ const empty = { positions: [], values: [] };
872
+ if (!this.cachedImageFrames?.length || !line)
873
+ return empty;
874
+ const frame = this.cachedImageFrames[this.activeFrameIndex()] ?? this.cachedImageFrames[0];
875
+ if (!frame?.length)
876
+ return empty;
877
+ const rx = this.cachedImageRatios[0] || 1;
878
+ const ry = this.cachedImageRatios[1] || rx;
879
+ const [ox, oy] = this.cachedFrameOrigin;
880
+ const x0 = +line.x0, y0 = +line.y0, x1 = +line.x1, y1 = +line.y1;
881
+ const dxData = x1 - x0, dyData = y1 - y0;
882
+ // Scale the along-line distance by the physical pixel size (microns/pixel)
883
+ // when the image carries one — anisotropic mppX/mppY are applied per axis so
884
+ // diagonal lines measure the true physical length. Falls back to pixels.
885
+ const { mppX, mppY } = this.currentMpp();
886
+ const useMicrons = mppX != null;
887
+ const lenData = useMicrons
888
+ ? Math.hypot(dxData * mppX, dyData * (mppY ?? mppX))
889
+ : Math.hypot(dxData, dyData);
890
+ const lenPx = Math.hypot(dxData / rx, dyData / ry);
891
+ const n = Math.max(2, Math.round(lenPx));
892
+ const h = frame.length;
893
+ const w = frame[0].length;
894
+ const positions = [];
895
+ const values = [];
896
+ for (let i = 0; i < n; i++) {
897
+ const t = i / (n - 1);
898
+ // Map data coords to the cached frame's pixel grid, offset by the frame's
899
+ // origin (non-zero when the frame is a zoomed crop).
900
+ const px = Math.round((x0 + t * dxData - ox) / rx);
901
+ const py = Math.round((y0 + t * dyData - oy) / ry);
902
+ let v = 0;
903
+ if (px >= 0 && px < w && py >= 0 && py < h) {
904
+ const cell = frame[py][px];
905
+ v = Array.isArray(cell) ? (0.299 * cell[0] + 0.587 * cell[1] + 0.114 * cell[2]) : cell;
906
+ }
907
+ positions.push(t * lenData);
908
+ values.push(v);
909
+ }
910
+ return { positions, values, unit: useMicrons ? 'µm' : 'px' };
911
+ }
912
+ /** Physical pixel size (microns/pixel) for the current image, if known.
913
+ * mpp is constant across frames/channels, so the first metadata entry that
914
+ * carries a positive mppX wins. Returns nulls when the image is unscaled. */
915
+ currentMpp() {
916
+ const meta = this.imageInfo?.imageMeta;
917
+ const m = Array.isArray(meta)
918
+ ? (meta.find(e => e && (e.mppX ?? 0) > 0) ?? meta[0])
919
+ : undefined;
920
+ const mppX = m && (m.mppX ?? 0) > 0 ? m.mppX : null;
921
+ const mppY = m && (m.mppY ?? 0) > 0 ? m.mppY : null;
922
+ return { mppX, mppY };
923
+ }
924
+ /**
925
+ * autoscale the plot
926
+ */
927
+ autoscale() {
928
+ if (this.plotDiv) {
929
+ this.zoomCoordinates = [];
930
+ Plotly.relayout(this.plotDiv, {
931
+ 'xaxis.autorange': true,
932
+ 'yaxis.autorange': false
933
+ });
934
+ }
935
+ }
936
+ /**
937
+ * relayout the plot
938
+ */
939
+ relayout(trueImageSize) {
940
+ let imgSize;
941
+ if (trueImageSize) {
942
+ imgSize = trueImageSize;
943
+ }
944
+ else {
945
+ imgSize = this.trueImgSize;
946
+ }
947
+ if (this.plotDiv) {
948
+ // Refresh height from current DOM so panel resizes are reflected
949
+ const plotEl = document.getElementById(this.plotDiv);
950
+ if (plotEl?.offsetHeight) {
951
+ this.screenHeight = plotEl.offsetHeight;
952
+ }
953
+ try {
954
+ if (this.zoomCoordinates.length > 0) {
955
+ Plotly.relayout(this.plotDiv, this.getHeatmapLayout([this.zoomCoordinates[0], this.zoomCoordinates[1]], [this.zoomCoordinates[2], this.zoomCoordinates[3]])); // reverse the y range
956
+ }
957
+ else {
958
+ // as autorange is set to false, and not reversed we need to set the yrange correcly here
959
+ Plotly.relayout(this.plotDiv, this.getHeatmapLayout([imgSize[0], imgSize[1]], [imgSize[3], imgSize[2]]));
960
+ }
961
+ }
962
+ catch (err) {
963
+ const msg = err?.error?.message || err?.message || err?.statusText || String(err);
964
+ console.error('Error occured', err);
965
+ this.messageService.add({ key: 'center-toast', sticky: true, severity: 'error', summary: 'An error occured', detail: `The following
966
+ error occured: ${msg}. Please try to open the image again through the
967
+ file navigator.` });
968
+ // TODO correctly clear the plot
969
+ this.reset();
970
+ }
971
+ }
972
+ }
973
+ setEvents(plotDiv, isGrayscale, screenHeight) {
974
+ this.imageInfo.isGrayscale = isGrayscale;
975
+ this.screenHeight = screenHeight;
976
+ const plot = document.getElementById(plotDiv);
977
+ if (plot) {
978
+ // unbind previous event
979
+ plot.removeEventListener('plotly_relayout', this.onRelayoutEvent);
980
+ // bind new event
981
+ plot.on('plotly_relayout', this.onRelayoutEvent);
982
+ // Clicking on a shape activates it but Plotly doesn't fire a dedicated
983
+ // event — sample _activeShapeIndex on the next microtask so subscribers
984
+ // (Region Editor) can mirror the selection.
985
+ plot.removeEventListener('mousedown', this.onPlotMouseDown);
986
+ this.onPlotMouseDown = () => setTimeout(() => this.maybeEmitActiveShapeIndex(), 0);
987
+ plot.addEventListener('mousedown', this.onPlotMouseDown);
988
+ }
989
+ }
990
+ relayoutEventHandler(event) {
991
+ if (Object.keys(event).includes('dragmode')) {
992
+ this.dragMode = event.dragmode;
993
+ }
994
+ // when relayout event occurs
995
+ // update the region selection udpdates
996
+ const keys = Object.keys(event);
997
+ let shapesModified = false;
998
+ keys.forEach((key) => {
999
+ if (typeof key === 'string' && key.startsWith('shapes[')) {
1000
+ const shapeNumber = +key.split('[')[1].split(']')[0];
1001
+ const shapeChange = key.split('.')[1];
1002
+ if (this.shapes && this.shapes[shapeNumber]) {
1003
+ if (shapeChange === 'path') {
1004
+ this.shapes[shapeNumber][shapeChange] = this.plotUtilities.roundPathCoordinates(event[key]);
1005
+ }
1006
+ else {
1007
+ this.shapes[shapeNumber][shapeChange] = Math.round(+event[key]);
1008
+ }
1009
+ shapesModified = true;
1010
+ }
1011
+ }
1012
+ });
1013
+ if (shapesModified) {
1014
+ // Mirror the in-place edit into the shared store (syncs its cache + emits).
1015
+ this.commitShapesToStore();
1016
+ }
1017
+ // manage high def zoom (not if we are showing a stack)
1018
+ if (keys.length === 4 && this.isRealZoom) {
1019
+ const coordinates = [];
1020
+ keys.forEach(key => {
1021
+ if (key.startsWith('xaxis.range[')) {
1022
+ coordinates.push(event[key]);
1023
+ }
1024
+ if (key.startsWith('yaxis.range[')) {
1025
+ coordinates.push(event[key]);
1026
+ }
1027
+ });
1028
+ this.zoomCoordinates = coordinates;
1029
+ if (!this.imageInfo.showStack) {
1030
+ this.triggerZoom(coordinates);
1031
+ }
1032
+ }
1033
+ // when shape is created
1034
+ if (keys.length === 1 && 'shapes' in event) {
1035
+ this.shapes = event.shapes;
1036
+ for (let i = 0; i < this.shapes.length; i++) {
1037
+ // A freshly Plotly-drawn shape has no id yet — give it a label object
1038
+ // (when labels are on) before the store mints id + default name.
1039
+ const isNew = this.shapes[i].id == null;
1040
+ this.shapes[i].fileName = this.fileName;
1041
+ this.shapes[i] = this.plotUtilities.snapRegion(this.shapes[i]);
1042
+ if (isNew && this.regionStore.getShowShapeLabel()) {
1043
+ this.shapes[i].label = { text: this.shapes[i].label,
1044
+ texttemplate: this.shapes[i].label,
1045
+ font: { color: this.regionStore.getShapeColor() },
1046
+ textposition: 'top left'
1047
+ };
1048
+ }
1049
+ }
1050
+ // Mint ids/names in the store, write them back onto the dicts, and emit.
1051
+ this.commitShapesToStore();
1052
+ if (this.isRegionSavedOn) {
1053
+ this.previousShapes = this.shapes.slice();
1054
+ }
1055
+ }
1056
+ // if autoscale
1057
+ if (keys.includes('xaxis.autorange') && keys.includes('yaxis.autorange')) {
1058
+ // trigger an autoscale event to reset the selected image mode dropdown
1059
+ this.autoscaleEvent.next('an autoscale has happened');
1060
+ // select display type to trigger a new plotting update with all the necessary plotting parameters set
1061
+ this.tiles.selectDiagramDisplay();
1062
+ }
1063
+ // if showstack or aspectratio event
1064
+ if (event.showstack !== undefined || event.aspectratio !== undefined) {
1065
+ // Set new image info to trigger a plot update
1066
+ this.setImageInfo(event.showstack, event.aspectratio);
1067
+ }
1068
+ // Plotly may have updated the active shape index as part of the relayout
1069
+ // (e.g. clicking a shape's edit handle, or finishing a draw) — surface
1070
+ // that change to the Region Editor.
1071
+ this.maybeEmitActiveShapeIndex();
1072
+ }
1073
+ reloadAndPlot() {
1074
+ this.setImageInfo();
1075
+ }
1076
+ // ── Tool delegations ────────────────────────────────────────────────
1077
+ setWandMode(active, options = {}) {
1078
+ // Re-bind to ourselves in case the OSD backend last used these singletons.
1079
+ if (active)
1080
+ this.wandTool.bindHost(this.wandHost);
1081
+ this.wandTool.setMode(active, options);
1082
+ }
1083
+ setWandOptions(options) {
1084
+ this.wandTool.setOptions(options);
1085
+ }
1086
+ clearActiveWandRegion() {
1087
+ this.wandTool.clearActiveRegion();
1088
+ }
1089
+ setBrushMode(active, options = {}) {
1090
+ // Re-bind to ourselves in case the OSD backend last used this singleton.
1091
+ if (active)
1092
+ this.brushTool.bindHost(this.wandHost);
1093
+ this.brushTool.setMode(active, options);
1094
+ }
1095
+ setBrushOptions(options) {
1096
+ if (options.size != null)
1097
+ this.brushTool.setSize(options.size);
1098
+ }
1099
+ setVertexEraserMode(active) {
1100
+ if (active)
1101
+ this.vertexEraserTool.bindHost(this.eraserHost);
1102
+ this.vertexEraserTool.setMode(active);
1103
+ }
1104
+ setVertexEraserRadius(radius) {
1105
+ this.vertexEraserTool.setRadius(radius);
1106
+ }
1107
+ setZoomToBoxMode(active) {
1108
+ // The zoom-to-box tool is a root singleton shared with the OSD backend,
1109
+ // which rebinds it to its own host on activation. Rebind to ours when we
1110
+ // activate so a prior OSD zoom-to-box doesn't leave the singleton pointing
1111
+ // at OSD's coordinate/zoom handlers (which would break box-zoom on heatmaps).
1112
+ if (active)
1113
+ this.bindZoomToBoxHost();
1114
+ this.zoomToBoxTool.setMode(active);
1115
+ }
1116
+ /** Bind the shared zoom-to-box tool to this (Plotly) backend's host. */
1117
+ bindZoomToBoxHost() {
1118
+ this.zoomToBoxTool.bindHost({
1119
+ getPlotDiv: () => this.plotDiv,
1120
+ pixelToData: (px, py) => this.zoomBoxPixelToData(px, py),
1121
+ applyZoomToBox: (coords) => this.applyZoomToBox(coords),
1122
+ });
1123
+ }
1124
+ /** Box-prompted SAM: segment the drawn rectangles. The SAM tool reuses the
1125
+ * wand host (cached frame + coordinate transform + region store). */
1126
+ segmentRectangles() {
1127
+ this.samTool.bindHost(this.wandHost);
1128
+ return this.samTool.segmentBoxes();
1129
+ }
1130
+ segmentRectanglesCellpose() {
1131
+ if (!this.cellSegmenter)
1132
+ return Promise.resolve(0);
1133
+ this.cellSegmentTool.bindHost(this.wandHost);
1134
+ return this.cellSegmentTool.segmentBoxes(this.cellSegmenter);
1135
+ }
1136
+ setSamModel(id) {
1137
+ this.samTool.setModel(id);
1138
+ this.samPointTool.setModel(id);
1139
+ }
1140
+ setSamPointMode(active) {
1141
+ if (active)
1142
+ this.samPointTool.bindHost(this.wandHost);
1143
+ this.samPointTool.setMode(active);
1144
+ }
1145
+ commitSamPoints() { this.samPointTool.commit(); }
1146
+ clearSamPoints() { this.samPointTool.clear(); }
1147
+ /**
1148
+ * Tool-host callback: apply the zoom-to-box selection. Stack mode does a
1149
+ * pure axis-range relayout; non-stack mode goes through the high-def
1150
+ * triggerZoom pipeline so the image is re-fetched at the new resolution.
1151
+ */
1152
+ /** Overlay-pixel -> Plotly data coords via the axis objects (subtracting the
1153
+ * plot margin offset). The zoom-to-box tool calls this through its host. */
1154
+ zoomBoxPixelToData(px, py) {
1155
+ const gd = document.getElementById(this.plotDiv);
1156
+ const xaxis = gd._fullLayout.xaxis;
1157
+ const yaxis = gd._fullLayout.yaxis;
1158
+ return { x: xaxis.p2d(px - xaxis._offset), y: yaxis.p2d(py - yaxis._offset) };
1159
+ }
1160
+ applyZoomToBox(coordinates) {
1161
+ this.zoomCoordinates = coordinates;
1162
+ if (this.imageInfo?.showStack) {
1163
+ Plotly.relayout(this.plotDiv, {
1164
+ 'xaxis.range[0]': coordinates[0],
1165
+ 'xaxis.range[1]': coordinates[1],
1166
+ 'yaxis.range[0]': coordinates[2],
1167
+ 'yaxis.range[1]': coordinates[3],
1168
+ });
1169
+ }
1170
+ else {
1171
+ this.triggerZoom(coordinates);
1172
+ }
1173
+ }
1174
+ // ── IViewportHost implementation (for the on-canvas tools) ──────────
1175
+ /** The plot element the tool canvas overlays attach to. */
1176
+ getOverlayContainer() {
1177
+ return this.plotDiv ? document.getElementById(this.plotDiv) : null;
1178
+ }
1179
+ /** Plotly coordinate transform (screen <-> data via the axis objects). */
1180
+ getCoordinateTransform() {
1181
+ return this.coordinateTransform;
1182
+ }
1183
+ // ── WandToolHost implementation ─────────────────────────────────────
1184
+ /** Pixel data the wand needs for sampling. null = no image loaded. */
1185
+ getCachedImageData() {
1186
+ if (!this.cachedImageFrames || this.cachedImageFrames.length === 0)
1187
+ return null;
1188
+ return {
1189
+ frames: this.cachedImageFrames,
1190
+ width: this.cachedImageWidth,
1191
+ height: this.cachedImageHeight,
1192
+ ratios: this.cachedImageRatios,
1193
+ isGrayscale: this.cachedIsGrayscale,
1194
+ };
1195
+ }
1196
+ // ── shared-store sync helpers ──────────────────────────────────────────
1197
+ //
1198
+ // `this.shapes` is Plotly's own representation (the dict array Plotly.relayout
1199
+ // consumes). The shared RegionStore is the single source of truth, holding the
1200
+ // neutral Region model. These helpers bridge the two: writes mirror
1201
+ // `this.shapes` INTO the store; image switches/external changes project the
1202
+ // store back OUT to `this.shapes`.
1203
+ /** Build Plotly shape dicts from the store's regions (the render projection). */
1204
+ regionsToShapeDicts() {
1205
+ const showLabel = this.regionStore.getShowShapeLabel();
1206
+ return this.regionStore.getRegions().map(r => {
1207
+ r.filename = this.fileName;
1208
+ return { ...r.getShape(showLabel) };
1209
+ });
1210
+ }
1211
+ /** Rebuild the dict working-set from the store (after an image switch, or when
1212
+ * regions changed while another backend was rendering). */
1213
+ syncShapesFromStore() {
1214
+ this.shapes = this.regionsToShapeDicts();
1215
+ }
1216
+ /**
1217
+ * Make the shared store authoritative for the current dict working-set: convert
1218
+ * `this.shapes` to neutral regions and replace the store's list (the store
1219
+ * mints ids, applies class colours, syncs its per-image cache and emits the
1220
+ * region-update event). Store-minted ids are written back onto the dicts so the
1221
+ * two representations stay aligned. Profile-line ROIs are ordinary regions
1222
+ * (tagged `kind: 'profile'`) and round-trip through here too.
1223
+ */
1224
+ commitShapesToStore() {
1225
+ const regions = this.shapes
1226
+ .map(s => Object.assign(new ShapeSelection(), s).getRegion());
1227
+ this.regionStore.setRegions(regions, this.regionStore.getShowShapeLabel(), true, this.regionStore.getFillColor(), false);
1228
+ const stored = this.regionStore.getRegions();
1229
+ for (let i = 0; i < this.shapes.length && i < stored.length; i++) {
1230
+ if (this.shapes[i].id == null)
1231
+ this.shapes[i].id = stored[i].id;
1232
+ if (this.shapes[i].name == null)
1233
+ this.shapes[i].name = stored[i].name;
1234
+ }
1235
+ }
1236
+ /**
1237
+ * Push the current `this.shapes` to Plotly, mirror them into the shared store
1238
+ * (which syncs its cache + emits the region-update event), and refresh the
1239
+ * local previous-shapes buffer. The wand/eraser tools call this after mutating
1240
+ * the shapes array directly.
1241
+ */
1242
+ applyShapesChange() {
1243
+ if (this.isRegionSavedOn) {
1244
+ this.previousShapes = this.shapes.slice();
1245
+ }
1246
+ this.commitShapesToStore();
1247
+ if (this.plotDiv) {
1248
+ const dictArray = this.shapes.map(s => ({ ...s }));
1249
+ try {
1250
+ Plotly.relayout(this.plotDiv, this.shapesRelayout(dictArray));
1251
+ }
1252
+ catch { /* div owned by another backend (OSD) — its overlay renders shapes */ }
1253
+ }
1254
+ }
1255
+ /** Active frame index in the cached image stack. */
1256
+ activeFrameIndex() {
1257
+ if (!this.cachedImageFrames || this.cachedImageFrames.length <= 1)
1258
+ return 0;
1259
+ const gd = document.getElementById(this.plotDiv);
1260
+ const sliderActive = gd?._fullLayout?.sliders?.[0]?.active;
1261
+ if (typeof sliderActive === 'number')
1262
+ return sliderActive;
1263
+ return this.zIndex.value || 0;
1264
+ }
1265
+ /** Capability-gated 3D scene controls (Plotly renders the 3D plot types). */
1266
+ getSurface3dControls() {
1267
+ return {
1268
+ setSurfaceDragMode: (mode) => this.setSurfaceDragMode(mode),
1269
+ resetSurfaceCamera: () => this.resetSurfaceCamera(),
1270
+ };
1271
+ }
1272
+ setSurfaceDragMode(mode) {
1273
+ if (this.plotDiv) {
1274
+ Plotly.relayout(this.plotDiv, { 'scene.dragmode': mode });
1275
+ }
1276
+ }
1277
+ resetSurfaceCamera() {
1278
+ if (this.plotDiv) {
1279
+ Plotly.relayout(this.plotDiv, { 'scene.camera': {} });
1280
+ }
1281
+ }
1282
+ triggerZoom(coordinates) {
1283
+ if (coordinates.length === 0 || !this.trueImgSize)
1284
+ return;
1285
+ this.zoomCoordinates = coordinates;
1286
+ const rect = this.plotUtilities.getRectangle(coordinates, this.trueImgSize);
1287
+ if (this.plotUtilities.isZoomSameAsImgSize(rect, this.trueImgSize)) {
1288
+ this.autoscale();
1289
+ return;
1290
+ }
1291
+ // A brief "Caching image..." message for uncached files (large files take a
1292
+ // moment to cache); just a spinner otherwise.
1293
+ this.state.setImageLoadingMessage(this.imageCached ? '' : 'Caching image...');
1294
+ const screen = this.plotUtilities.getDomRectangle('diagram');
1295
+ const imageSize = [];
1296
+ imageSize[0] = rect.x;
1297
+ imageSize[1] = rect.x + rect.width;
1298
+ imageSize[2] = rect.y;
1299
+ imageSize[3] = rect.y + rect.height;
1300
+ // Snapshot the filename so a response that arrives after the user switched
1301
+ // files is dropped (the request carried the file selected at call time).
1302
+ const reqName = this.fileName;
1303
+ this.state.setImageLoading(true);
1304
+ this.state.setZoom(true);
1305
+ this.tiles.zoomOnRegion(rect, screen, this.zIndex.value).subscribe({ next: zoomData => {
1306
+ const uint8Array = new Uint8Array(zoomData);
1307
+ const buffer = Buffer.from(uint8Array);
1308
+ Image.load(buffer).then((image) => {
1309
+ const xRatio = rect.width / image.width;
1310
+ const yRatio = rect.height / image.height;
1311
+ if (this.fileName === reqName) {
1312
+ const isGray = this.imageInfo.isGrayscale;
1313
+ const frame = isGray
1314
+ ? this.plotUtilities.arrayToMatrix(image.grey().data, image.width)
1315
+ : this.plotUtilities.arrayToMatrix(image.getPixelsArray(), image.width);
1316
+ // Also sample the intensity profiles from this high-def crop so the
1317
+ // inset reflects the zoom-level resolution (origin = crop top-left).
1318
+ this.setSamplingFrames([frame], [xRatio, yRatio], [imageSize[0], imageSize[2]]);
1319
+ this.emitProfiles();
1320
+ // Re-render the high-def crop in the SAME plot type the user is
1321
+ // viewing. Without this the zoom re-fetch always fell back to a
1322
+ // heatmap, so zooming in contour (or any registry type) reverted
1323
+ // to heatmap.
1324
+ const impl = PLOTLY_PLOT_TYPE_IMPLS[this.plotType];
1325
+ const renderPromise = impl
1326
+ ? this.plotViaRegistry(this.plotDiv, impl, this.buildTraceInput(this.imageInfo, {
1327
+ data: [frame], ratios: [xRatio, yRatio],
1328
+ sizes: [image.width, image.height], filename: reqName,
1329
+ }, imageSize), this.screenHeight)
1330
+ : (isGray
1331
+ ? this.plotHeatmap(this.plotDiv, this.urls, [frame], imageSize, [xRatio, yRatio], this.screenHeight)
1332
+ : this.plotRGBHeatmap(this.plotDiv, this.urls, [frame], imageSize, [xRatio, yRatio], image.width, image.height, this.screenHeight));
1333
+ // plotViaRegistry already applies the type's own layout/range for
1334
+ // non-image layouts (chart/overlay); only re-apply the heatmap
1335
+ // range for the image-aligned paths.
1336
+ const reapplyRange = !impl || impl.layoutKind === '2d-image';
1337
+ renderPromise.then(() => {
1338
+ this.state.setImageCached(true);
1339
+ this.state.setImageLoading(false);
1340
+ image = null;
1341
+ if (reapplyRange)
1342
+ this.relayout(imageSize);
1343
+ });
1344
+ }
1345
+ });
1346
+ }, error: err => {
1347
+ const msg = err?.error?.message || err?.message || err?.statusText || String(err);
1348
+ console.error('Error occured when zooming', err);
1349
+ this.messageService.add({ key: 'center-toast', sticky: true, severity: 'error', summary: 'An error occured',
1350
+ detail: `The following error occured while zooming: ${msg}.
1351
+ Please try to open the image again through the file navigator and
1352
+ zoom on the selected area once more.` });
1353
+ this.state.setLoadingError(true);
1354
+ this.state.setImageLoading(false);
1355
+ }, complete: () => {
1356
+ console.log('zooming complete');
1357
+ } });
1358
+ }
1359
+ setPlotType(plotType) {
1360
+ this.plotType = plotType;
1361
+ }
1362
+ setImageInfo(showStack, scaleratio) {
1363
+ // Build a partial image descriptor and push it to the host via the port.
1364
+ const imgInfo = {
1365
+ isGrayscale: this.imageInfo.isGrayscale,
1366
+ trueImageSize: [this.trueImgSize[1], this.trueImgSize[3]],
1367
+ urls: this.urls,
1368
+ isStack: this.urls.length > 1,
1369
+ scaleRatio: scaleratio !== undefined ? scaleratio : this.scaleratio,
1370
+ };
1371
+ if (showStack !== undefined)
1372
+ imgInfo.showStack = showStack;
1373
+ if (this.fileName)
1374
+ imgInfo.fileName = this.fileName;
1375
+ this.state.setImageInfo(imgInfo);
1376
+ }
1377
+ getShapes() {
1378
+ return this.shapes;
1379
+ }
1380
+ /**
1381
+ * Framework-neutral view of the current regions — delegates to the shared
1382
+ * RegionStore (the single source of truth). The canonical accessor on the
1383
+ * `IVisualizer` contract; consumers should prefer this over `getShapes()`
1384
+ * (which exposes Plotly-shaped dicts).
1385
+ */
1386
+ getRegions() {
1387
+ return this.regionStore.getRegions();
1388
+ }
1389
+ /**
1390
+ * Switch the active image. The shared RegionStore owns the per-image region
1391
+ * cache (snapshot outgoing / restore incoming / clear selection / emit); we
1392
+ * delegate to it, then re-project the store's regions into Plotly's dict
1393
+ * working-set. Re-projecting unconditionally (even when the store no-ops for
1394
+ * the same image) means Plotly also picks up regions another backend (OSD)
1395
+ * added while it was off-screen. Called from `plot()` and from the router
1396
+ * before any backend renders.
1397
+ */
1398
+ setActiveImage(imageInfo) {
1399
+ this.regionStore.setActiveImage(imageInfo);
1400
+ this.syncShapesFromStore();
1401
+ this.previousShapes = this.shapes.slice();
1402
+ }
1403
+ /** Test/dev hook — drop the entire per-image cache (logout / project switch). */
1404
+ clearRegionsByImageKey() {
1405
+ this.regionStore.clearRegionsByImageKey();
1406
+ }
1407
+ /**
1408
+ * Get all the selected regions as polygons. Open polylines (closed === false)
1409
+ * are excluded — they're annotation-only and cannot be used as filled regions
1410
+ * for image processing. Delegates to the shared store.
1411
+ */
1412
+ getRegionPolygons() {
1413
+ return this.regionStore.getRegionPolygons();
1414
+ }
1415
+ isRectangle(bnds) {
1416
+ return 'x' in bnds && 'y' in bnds && 'width' in bnds && 'height' in bnds;
1417
+ }
1418
+ isPolygon(bnds) {
1419
+ return 'npoints' in bnds && 'xpoints' in bnds && 'ypoints' in bnds;
1420
+ }
1421
+ /**
1422
+ * Set plot regions. Delegates the state change to the shared RegionStore
1423
+ * (id/name minting, classification colours, append de-duplication, per-image
1424
+ * cache and the region-update event all live there), then re-projects the
1425
+ * store's regions into Plotly's dict working-set and renders.
1426
+ *
1427
+ * When `isRegionSaveOn` is false the regions are shown transiently — rendered
1428
+ * without altering the stored working-set (preserves the prior behaviour).
1429
+ */
1430
+ setRegions(regions, showRegionLabel, isRegionSaveOn, fillColor, append = false) {
1431
+ const showLabel = showRegionLabel === undefined ? this.regionStore.getShowShapeLabel() : showRegionLabel;
1432
+ const save = isRegionSaveOn === undefined ? this.isRegionSavedOn : isRegionSaveOn;
1433
+ this.regionStore.setRegions(regions, showRegionLabel, isRegionSaveOn, fillColor, append);
1434
+ this.isRegionSavedOn = save;
1435
+ if (save) {
1436
+ // Re-project the authoritative regions into Plotly's working-set and render.
1437
+ this.syncShapesFromStore();
1438
+ this.renderShapes();
1439
+ }
1440
+ else {
1441
+ // Transient display only — render the passed regions, don't touch the
1442
+ // working-set (and the store didn't store them either).
1443
+ const dicts = regions.map(r => { r.filename = this.fileName; return { ...r.getShape(showLabel) }; });
1444
+ try {
1445
+ Plotly.relayout(this.plotDiv, this.shapesRelayout(dicts));
1446
+ }
1447
+ catch { /* div owned by another backend (OSD) — its overlay renders shapes */ }
1448
+ }
1449
+ }
1450
+ /** The shapes-relayout payload: region shapes (profile-line ROIs included, as
1451
+ * they are ordinary regions) plus the active-shape fill colour. */
1452
+ shapesRelayout(shapeDicts) {
1453
+ return {
1454
+ shapes: shapeDicts,
1455
+ activeshape: { fillcolor: this.regionStore.getFillColor() },
1456
+ };
1457
+ }
1458
+ /** Push the current dict working-set to Plotly (no-op when another backend
1459
+ * owns the div). */
1460
+ renderShapes() {
1461
+ if (!this.plotDiv)
1462
+ return;
1463
+ const dictArray = this.shapes.map(s => ({ ...s }));
1464
+ try {
1465
+ Plotly.relayout(this.plotDiv, this.shapesRelayout(dictArray));
1466
+ }
1467
+ catch { /* div owned by another backend (OSD) — its overlay renders shapes */ }
1468
+ }
1469
+ getShowShapeLabel() {
1470
+ return this.regionStore.getShowShapeLabel();
1471
+ }
1472
+ /**
1473
+ * Set previous saved shapes
1474
+ */
1475
+ plotPreviousShapes() {
1476
+ // convert to dict so that plotly recognises the shapes
1477
+ const dictArray = this.previousShapes.map(s => ({ ...s }));
1478
+ try {
1479
+ Plotly.relayout(this.plotDiv, { shapes: dictArray });
1480
+ }
1481
+ catch { /* div owned by another backend (OSD) */ }
1482
+ }
1483
+ setPreviousShapes(shapes) {
1484
+ this.previousShapes = shapes;
1485
+ }
1486
+ getPreviousShapes() {
1487
+ return this.previousShapes;
1488
+ }
1489
+ // Undo (jit-ui#85): region history lives in the shared RegionStore, so the
1490
+ // active backend's overlay re-renders off its regionUpdate$ on restore.
1491
+ undo() { this.regionStore.undo(); }
1492
+ redo() { this.regionStore.redo(); }
1493
+ canUndo() { return this.regionStore.canUndo(); }
1494
+ canRedo() { return this.regionStore.canRedo(); }
1495
+ getCanUndo$() { return this.regionStore.getCanUndo$(); }
1496
+ getCanRedo$() { return this.regionStore.getCanRedo$(); }
1497
+ resetUndoHistory() { this.regionStore.resetUndoHistory(); }
1498
+ // ── Per-slice z-stack regions → RegionStore (jit-ui#93) ──────────────────
1499
+ enterStackMode(slices, initialZ, saveLayout) {
1500
+ this.regionStore.enterStackMode(slices, initialZ, saveLayout);
1501
+ }
1502
+ exitStackMode() { this.regionStore.exitStackMode(); }
1503
+ isStackMode() { return this.regionStore.isStackMode(); }
1504
+ getStackSaveLayout() {
1505
+ return this.regionStore.getStackSaveLayout();
1506
+ }
1507
+ setDisplaySlice(z) { this.regionStore.setDisplaySlice(z); }
1508
+ getSliceRegions() { return this.regionStore.getSliceRegions(); }
1509
+ getStackSaveSlices() { return this.regionStore.getStackSaveSlices(); }
1510
+ getHeatmapLayout(xRange, yRange) {
1511
+ return {
1512
+ xaxis: {
1513
+ constrain: 'range',
1514
+ constraintoward: 'center',
1515
+ side: 'top',
1516
+ ticks: '',
1517
+ range: xRange
1518
+ },
1519
+ yaxis: {
1520
+ constrain: 'range',
1521
+ constraintoward: 'center',
1522
+ range: yRange,
1523
+ ticks: '',
1524
+ ticksuffix: ' ',
1525
+ // set autorange to false so that plotly does not overwrite the range for the y axis
1526
+ autorange: false,
1527
+ scaleanchor: this.scaleratio ? 'x' : false
1528
+ },
1529
+ margin: { t: 30, b: 5, l: 55, r: 5 },
1530
+ height: this.screenHeight,
1531
+ sliders: [{
1532
+ pad: { t: 50 },
1533
+ currentvalue: {
1534
+ visible: true,
1535
+ prefix: 'Z-plane:',
1536
+ xanchor: 'right',
1537
+ },
1538
+ steps: this.getSteps()
1539
+ }],
1540
+ autosize: true,
1541
+ shapes: this.currentRenderShapes(this.regionStore.getShowShapeLabel()),
1542
+ activeshape: { fillcolor: this.regionStore.getFillColor() },
1543
+ dragmode: this.dragMode ? this.dragMode : false,
1544
+ newshape: { line: { color: this.regionStore.getShapeColor(), width: 3 } }
1545
+ };
1546
+ }
1547
+ shapesToRedraw(showLabel) {
1548
+ const shapesToRedraw = [];
1549
+ for (const shape of this.shapes) {
1550
+ if (JSON.stringify(shape.fileName) === JSON.stringify(this.fileName)) {
1551
+ // Set label
1552
+ if (showLabel) {
1553
+ shape.label = {
1554
+ text: `${shape.legend}`,
1555
+ texttemplate: `${shape.legend}`,
1556
+ textposition: 'top left',
1557
+ font: { color: `${shape.line.color}` }
1558
+ };
1559
+ }
1560
+ else {
1561
+ shape.label = {};
1562
+ }
1563
+ shapesToRedraw.push(shape);
1564
+ }
1565
+ }
1566
+ // convert to dict so that plotly recognises the shapes
1567
+ return shapesToRedraw.map(s => ({ ...s }));
1568
+ }
1569
+ /**
1570
+ * Unused (will be used when surface plot is added)
1571
+ * @param xRange
1572
+ * @param yRange
1573
+ * @param zRatio
1574
+ * @private
1575
+ */
1576
+ getSurfaceLayout(xRange, yRange, zRatio) {
1577
+ return {
1578
+ margin: { t: 0, b: 0, l: 0, r: 0 },
1579
+ scene: {
1580
+ xaxis: {
1581
+ gridcolor: 'rgb(255, 255, 255)',
1582
+ zerolinecolor: 'rgb(255, 255, 255)',
1583
+ showbackground: true,
1584
+ backgroundcolor: 'rgb(230, 230,230)',
1585
+ // range: xRange
1586
+ },
1587
+ yaxis: {
1588
+ gridcolor: 'rgb(255, 255, 255)',
1589
+ zerolinecolor: 'rgb(255, 255, 255)',
1590
+ showbackground: true,
1591
+ backgroundcolor: 'rgb(230, 230, 230)',
1592
+ // autorange: 'reversed',
1593
+ // range: yRange,
1594
+ // scaleanchor: 'x'
1595
+ },
1596
+ zaxis: {
1597
+ gridcolor: 'rgb(255, 255, 255)',
1598
+ zerolinecolor: 'rgb(255, 255, 255)',
1599
+ showbackground: true,
1600
+ backgroundcolor: 'rgb(230, 230,230)'
1601
+ },
1602
+ aspectratio: { x: 1, y: 1, z: zRatio },
1603
+ aspectmode: 'manual',
1604
+ sliders: [{
1605
+ pad: { t: 50 },
1606
+ currentvalue: {
1607
+ visible: true,
1608
+ prefix: 'Z-plane:',
1609
+ xanchor: 'right',
1610
+ },
1611
+ steps: this.getSteps()
1612
+ }],
1613
+ }
1614
+ };
1615
+ }
1616
+ getSteps() {
1617
+ const steps = [];
1618
+ for (let i = 0; i < this.imageLength; i++) {
1619
+ steps.push({
1620
+ label: i + 1,
1621
+ method: 'restyle',
1622
+ args: ['visible', Array(this.imageLength).fill(false).fill(true, i, i + 1)],
1623
+ });
1624
+ }
1625
+ return steps;
1626
+ }
1627
+ /**
1628
+ * Fetch an image via Angular HttpClient so that auth interceptors (Bearer token)
1629
+ * are applied, then decode it with image-js. This avoids raw browser fetch()
1630
+ * calls that bypass the interceptor chain and fail behind an OAuth2 proxy.
1631
+ */
1632
+ async loadImage(url) {
1633
+ const buffer = await firstValueFrom(this.http.get(url, { responseType: 'arraybuffer' }));
1634
+ return Image.load(Buffer.from(buffer));
1635
+ }
1636
+ /**
1637
+ * Get the currently displayed image as an image-js Image instance.
1638
+ * Used by the processing pipeline to obtain the input image.
1639
+ */
1640
+ async getCurrentImage() {
1641
+ if (!this.imageInfo || !this.imageInfo.urls || this.imageInfo.urls.length === 0) {
1642
+ return null;
1643
+ }
1644
+ const zIdx = this.zIndex.value || 0;
1645
+ const url = this.imageInfo.urls[zIdx] || this.imageInfo.urls[0];
1646
+ return this.loadImage(url);
1647
+ }
1648
+ /**
1649
+ * Get the currently displayed plot pixel data as a flat Uint8ClampedArray.
1650
+ * This captures whatever is actually rendered — including zoomed regions.
1651
+ * Returns { width, height, channels, data } or null if no plot is displayed.
1652
+ */
1653
+ getDisplayedPixelData() {
1654
+ if (!this.plotDiv)
1655
+ return null;
1656
+ const gd = document.getElementById(this.plotDiv);
1657
+ if (!gd?.data || gd.data.length === 0)
1658
+ return null;
1659
+ // Find the visible trace
1660
+ const frameIdx = this.activeFrameIndex();
1661
+ const trace = gd.data[frameIdx] || gd.data[0];
1662
+ if (!trace || !trace.z)
1663
+ return null;
1664
+ const zData = trace.z;
1665
+ const height = zData.length;
1666
+ if (height === 0)
1667
+ return null;
1668
+ const width = zData[0].length;
1669
+ if (trace.type === 'image') {
1670
+ // RGB image: z[row][col] = [r, g, b] or [r, g, b, a]
1671
+ const sample = zData[0][0];
1672
+ const channels = Array.isArray(sample) ? sample.length : 3;
1673
+ const data = new Uint8ClampedArray(width * height * channels);
1674
+ for (let y = 0; y < height; y++) {
1675
+ for (let x = 0; x < width; x++) {
1676
+ const pixel = zData[y][x];
1677
+ const offset = (y * width + x) * channels;
1678
+ for (let c = 0; c < channels; c++) {
1679
+ data[offset + c] = pixel[c];
1680
+ }
1681
+ }
1682
+ }
1683
+ return { width, height, channels, data };
1684
+ }
1685
+ else {
1686
+ // Heatmap (grayscale): z[row][col] = scalar value
1687
+ const data = new Uint8ClampedArray(width * height);
1688
+ for (let y = 0; y < height; y++) {
1689
+ for (let x = 0; x < width; x++) {
1690
+ data[y * width + x] = Math.round(zData[y][x]);
1691
+ }
1692
+ }
1693
+ return { width, height, channels: 1, data };
1694
+ }
1695
+ }
1696
+ /**
1697
+ * Region of the original image that the displayed `z` grid covers, in
1698
+ * full-image pixel coords. On a server zoom (`triggerZoom`) the trace `z` is
1699
+ * replaced with the high-def crop and the axes are relaid out to the crop's
1700
+ * original-image bounds (`imageSize`), so the live axis ranges ARE that
1701
+ * rectangle; when the whole image is in view the ranges span `trueImgSize`.
1702
+ * Reading the ranges therefore yields the crop origin + extent without
1703
+ * tracking zoom state separately. Falls back to the full image, then `null`.
1704
+ */
1705
+ getDisplayedSourceRect() {
1706
+ const gd = this.plotDiv ? document.getElementById(this.plotDiv) : null;
1707
+ const xr = gd?._fullLayout?.xaxis?.range;
1708
+ const yr = gd?._fullLayout?.yaxis?.range;
1709
+ if (xr && yr) {
1710
+ const x0 = Math.min(xr[0], xr[1]);
1711
+ const y0 = Math.min(yr[0], yr[1]); // y axis is reversed for image layouts
1712
+ return { x: x0, y: y0, width: Math.abs(xr[1] - xr[0]), height: Math.abs(yr[1] - yr[0]) };
1713
+ }
1714
+ if (!this.trueImgSize)
1715
+ return null;
1716
+ return {
1717
+ x: this.trueImgSize[0],
1718
+ y: this.trueImgSize[2],
1719
+ width: this.trueImgSize[1] - this.trueImgSize[0],
1720
+ height: this.trueImgSize[3] - this.trueImgSize[2],
1721
+ };
1722
+ }
1723
+ reset() {
1724
+ if (this.plotDiv) {
1725
+ Plotly.newPlot(this.plotDiv, [], this.getHeatmapLayout([0, 100], [100, 0]), CONFIG);
1726
+ }
1727
+ }
1728
+ /**
1729
+ * Fully remove Plotly's DOM from the plot div (axes included). Used when
1730
+ * handing the div to another backend (OpenSeadragon) — unlike `reset()`,
1731
+ * which re-renders an empty plot and would leave the Plotly axes showing.
1732
+ */
1733
+ purgePlot() {
1734
+ if (this.plotDiv) {
1735
+ Plotly.purge(this.plotDiv);
1736
+ }
1737
+ }
1738
+ /** Export the heatmap as a PNG (the rendered plot already reflects the active
1739
+ * colormap/window). The full-res tile composite is OSD's job. */
1740
+ exportComposite() {
1741
+ this.downloadImage();
1742
+ }
1743
+ downloadImage() {
1744
+ if (this.plotDiv) {
1745
+ Plotly.downloadImage(this.plotDiv, { format: 'png', filename: this.fileName || 'image' });
1746
+ }
1747
+ }
1748
+ resetAxes() {
1749
+ this.zoomCoordinates = [];
1750
+ this.relayout();
1751
+ }
1752
+ setDragMode(mode) {
1753
+ this.dragMode = mode ? mode : '';
1754
+ if (!this.plotDiv)
1755
+ return;
1756
+ // Only relayout a LIVE Plotly graph. When another backend (OSD / napari-js) owns the view the
1757
+ // div id is still set but has no Plotly plot attached, and Plotly.relayout throws
1758
+ // ("can't access property _guiEditing, n is undefined"). That throw aborted the caller
1759
+ // (deactivateActiveTool → onSelectPlotType) before the re-plot ran, wedging the plot type
1760
+ // (e.g. stuck switching napari volume ↔ isosurface). Guard on `_fullLayout` like the zoom
1761
+ // helpers do so it safely no-ops when Plotly isn't the active renderer.
1762
+ const gd = document.getElementById(this.plotDiv);
1763
+ if (!gd?._fullLayout)
1764
+ return;
1765
+ Plotly.relayout(this.plotDiv, { dragmode: mode });
1766
+ }
1767
+ /** {@link IDataRenderer} stub — Plotly has no overview navigator (it's an
1768
+ * OpenSeadragon feature), so toggling it is a no-op here. */
1769
+ setNavigatorVisible(_visible) {
1770
+ /* no-op: navigator is OpenSeadragon-only */
1771
+ }
1772
+ /** {@link IDataRenderer} stub — image smoothing is an OpenSeadragon canvas-drawer
1773
+ * setting; the Plotly image/heatmap traces don't expose it, so it's a no-op. */
1774
+ setImageSmoothingEnabled(_enabled) {
1775
+ /* no-op: smoothing toggle is OpenSeadragon-only */
1776
+ }
1777
+ zoomIn() {
1778
+ if (!this.plotDiv)
1779
+ return;
1780
+ const gd = document.getElementById(this.plotDiv);
1781
+ if (!gd?._fullLayout)
1782
+ return;
1783
+ const xl = gd._fullLayout.xaxis;
1784
+ const yl = gd._fullLayout.yaxis;
1785
+ // 3D scenes have no 2D xaxis/yaxis (they live under `scene`); the step-zoom
1786
+ // is meaningless there and would throw on `xl.range`. Plotly's scene handles
1787
+ // scroll-zoom natively, so just bail.
1788
+ if (!xl?.range || !yl?.range)
1789
+ return;
1790
+ const xc = (xl.range[0] + xl.range[1]) / 2;
1791
+ const yc = (yl.range[0] + yl.range[1]) / 2;
1792
+ const dx = (xl.range[1] - xl.range[0]) / 1.3;
1793
+ const dy = (yl.range[1] - yl.range[0]) / 1.3;
1794
+ const x0 = xc - dx / 2, x1 = xc + dx / 2;
1795
+ const y0 = yc - dy / 2, y1 = yc + dy / 2;
1796
+ this.zoomCoordinates = [x0, x1, y0, y1];
1797
+ Plotly.relayout(this.plotDiv, {
1798
+ 'xaxis.range[0]': x0, 'xaxis.range[1]': x1,
1799
+ 'yaxis.range[0]': y0, 'yaxis.range[1]': y1
1800
+ });
1801
+ }
1802
+ /**
1803
+ * Delete every currently selected shape. Falls back to the single
1804
+ * `_activeShapeIndex` when no multi-selection is active (covers the case
1805
+ * where Plotly clicked a shape without going through the table).
1806
+ */
1807
+ deleteActiveShape() {
1808
+ // Note: no `plotDiv` guard — when OpenSeadragon is the renderer, Plotly
1809
+ // never plotted (plotDiv is unset), but deletion only needs the shape list
1810
+ // + the current selection; the Plotly relayout below is guarded.
1811
+ const gd = this.plotDiv ? document.getElementById(this.plotDiv) : null;
1812
+ // Resolve what to delete: the store's selection, else the shape Plotly
1813
+ // tracks as clicked (when the user clicked a shape without going through the
1814
+ // Region Editor table).
1815
+ if (this.regionStore.getSelectedShapeIndices().length === 0) {
1816
+ const activeIndex = gd?._fullLayout?._activeShapeIndex;
1817
+ if (activeIndex === undefined || activeIndex < 0)
1818
+ return;
1819
+ this.regionStore.setSelectedShapeIndices([activeIndex]);
1820
+ }
1821
+ // The store removes the selected regions, clears the selection, syncs its
1822
+ // cache and emits the region-update event.
1823
+ this.regionStore.deleteActiveShape();
1824
+ // Re-project the remaining regions and render on the Plotly plot (a no-op,
1825
+ // caught, when another backend owns the div — its overlay re-renders via
1826
+ // the store's update event).
1827
+ if (gd?._fullLayout)
1828
+ gd._fullLayout._activeShapeIndex = -1;
1829
+ this.syncShapesFromStore();
1830
+ const dictArray = this.shapes.map(s => ({ ...s }));
1831
+ try {
1832
+ Plotly.relayout(this.plotDiv, { shapes: dictArray }).then(() => {
1833
+ if (gd?._fullLayout)
1834
+ Plotly.redraw(gd);
1835
+ }, () => { });
1836
+ }
1837
+ catch { /* div owned by another backend */ }
1838
+ }
1839
+ zoomOut() {
1840
+ if (!this.plotDiv)
1841
+ return;
1842
+ const gd = document.getElementById(this.plotDiv);
1843
+ if (!gd?._fullLayout)
1844
+ return;
1845
+ const xl = gd._fullLayout.xaxis;
1846
+ const yl = gd._fullLayout.yaxis;
1847
+ // 3D scenes have no 2D xaxis/yaxis — see zoomIn().
1848
+ if (!xl?.range || !yl?.range)
1849
+ return;
1850
+ const xc = (xl.range[0] + xl.range[1]) / 2;
1851
+ const yc = (yl.range[0] + yl.range[1]) / 2;
1852
+ const dx = (xl.range[1] - xl.range[0]) * 1.3;
1853
+ const dy = (yl.range[1] - yl.range[0]) * 1.3;
1854
+ const x0 = xc - dx / 2, x1 = xc + dx / 2;
1855
+ const y0 = yc - dy / 2, y1 = yc + dy / 2;
1856
+ this.zoomCoordinates = [x0, x1, y0, y1];
1857
+ Plotly.relayout(this.plotDiv, {
1858
+ 'xaxis.range[0]': x0, 'xaxis.range[1]': x1,
1859
+ 'yaxis.range[0]': y0, 'yaxis.range[1]': y1
1860
+ });
1861
+ }
1862
+ isStackLoading$() {
1863
+ return this.stackLoading$.asObservable();
1864
+ }
1865
+ setStackLoading(stackLoading) {
1866
+ this.stackLoading$.next(stackLoading);
1867
+ }
1868
+ getStackLoadingProgress$() {
1869
+ return this.stackLoadingProgress$.asObservable();
1870
+ }
1871
+ // Colormap / reverse-scale / image metadata state lives in the shared
1872
+ // VisualizerStore; these delegate, keeping only the Plotly-specific live
1873
+ // restyle as render glue.
1874
+ getColormapOptions() {
1875
+ return this.store.getColormapOptions();
1876
+ }
1877
+ setColormap(colormap) {
1878
+ this.store.setColormap(colormap);
1879
+ try {
1880
+ Plotly.restyle(this.plotDiv, { 'colorscale': [colormap.data.value] });
1881
+ }
1882
+ catch { /* div owned by another backend (OSD) — it recolors via the LUT */ }
1883
+ }
1884
+ getColormap() {
1885
+ return this.store.getColormap();
1886
+ }
1887
+ setReverseScale(reverscale) {
1888
+ this.store.setReverseScale(reverscale);
1889
+ try {
1890
+ Plotly.restyle(this.plotDiv, { 'reversescale': reverscale });
1891
+ }
1892
+ catch { /* div owned by another backend (OSD) — it recolors via the LUT */ }
1893
+ }
1894
+ getReverseScale() {
1895
+ return this.store.getReverseScale();
1896
+ }
1897
+ setImageMeta(imageMeta) {
1898
+ this.store.setImageMeta(imageMeta);
1899
+ }
1900
+ getImageMeta() {
1901
+ return this.store.getImageMeta();
1902
+ }
1903
+ setShowStack(showstack) {
1904
+ if (!showstack) {
1905
+ this.zIndex.next(0);
1906
+ }
1907
+ this.imageInfo.showStack = showstack;
1908
+ this.stackLoading$.next(showstack);
1909
+ Plotly.relayout(this.plotDiv, { 'showstack': showstack });
1910
+ }
1911
+ getAutoscaleEvent() {
1912
+ return this.autoscaleEvent.asObservable();
1913
+ }
1914
+ getRegionUpdateEvent() {
1915
+ return this.regionStore.getRegionUpdateEvent();
1916
+ }
1917
+ /**
1918
+ * Stream of the indices of currently selected regions — owned by the shared
1919
+ * RegionStore so the selection is consistent across backends and the Region
1920
+ * Editor table. Empty = nothing selected.
1921
+ */
1922
+ getSelectedShapeIndices$() {
1923
+ return this.regionStore.getSelectedShapeIndices$();
1924
+ }
1925
+ /**
1926
+ * Programmatically select regions (or clear with []). The shared store owns
1927
+ * the selection state (validates/dedupes/emits); here we additionally point
1928
+ * Plotly's `_activeShapeIndex` at the last selected shape so it gets the edit
1929
+ * handles. No visual change to non-active shapes — Plotly's own active-shape
1930
+ * rendering is the only highlight.
1931
+ */
1932
+ setSelectedShapeIndices(indices) {
1933
+ const valid = (indices || [])
1934
+ .filter(i => Number.isFinite(i) && i >= 0 && i < this.shapes.length);
1935
+ const seen = new Set();
1936
+ const cleaned = [];
1937
+ for (const i of valid) {
1938
+ if (!seen.has(i)) {
1939
+ seen.add(i);
1940
+ cleaned.push(i);
1941
+ }
1942
+ }
1943
+ if (this.plotDiv) {
1944
+ const gd = document.getElementById(this.plotDiv);
1945
+ if (gd?._fullLayout) {
1946
+ gd._fullLayout._activeShapeIndex = cleaned.length > 0 ? cleaned[cleaned.length - 1] : -1;
1947
+ try {
1948
+ Plotly.redraw(gd);
1949
+ }
1950
+ catch { /* noop in tests */ }
1951
+ }
1952
+ }
1953
+ this.regionStore.setSelectedShapeIndices(cleaned);
1954
+ }
1955
+ /**
1956
+ * Select a specific region (IRegionStore.selectRegion). Sets the shared store
1957
+ * selection by identity and points Plotly's active-shape at the matching
1958
+ * rendered shape so its edit handles appear.
1959
+ */
1960
+ selectRegion(region) {
1961
+ this.regionStore.selectRegion(region);
1962
+ if (this.plotDiv) {
1963
+ const idx = this.shapes.findIndex(s => s.id === region?.id);
1964
+ const gd = document.getElementById(this.plotDiv);
1965
+ if (gd?._fullLayout && idx >= 0) {
1966
+ gd._fullLayout._activeShapeIndex = idx;
1967
+ try {
1968
+ Plotly.redraw(gd);
1969
+ }
1970
+ catch { /* noop in tests */ }
1971
+ }
1972
+ }
1973
+ }
1974
+ /**
1975
+ * Read Plotly's `_activeShapeIndex` and push it as the selection. Called from
1976
+ * the relayout handler and the post-click tick — Plotly doesn't fire a
1977
+ * dedicated active-shape event for clicks, so we sample. The store no-ops if
1978
+ * the selection is unchanged.
1979
+ */
1980
+ maybeEmitActiveShapeIndex() {
1981
+ if (!this.plotDiv)
1982
+ return;
1983
+ const gd = document.getElementById(this.plotDiv);
1984
+ const raw = gd?._fullLayout?._activeShapeIndex;
1985
+ const idx = (typeof raw === 'number' && raw >= 0 && raw < this.shapes.length) ? raw : -1;
1986
+ this.regionStore.setSelectedShapeIndices(idx >= 0 ? [idx] : []);
1987
+ }
1988
+ setZIndex(zIndex) {
1989
+ this.zIndex.next(zIndex);
1990
+ }
1991
+ getShapeColor() {
1992
+ return this.regionStore.getShapeColor();
1993
+ }
1994
+ getFillColor() {
1995
+ return this.regionStore.getFillColor();
1996
+ }
1997
+ getClassificationColors() {
1998
+ return this.store.getClassificationColors();
1999
+ }
2000
+ setClassificationColor(label, color) {
2001
+ this.store.setClassificationColor(label, color);
2002
+ }
2003
+ /**
2004
+ * Unsubscribe Subscriptions
2005
+ */
2006
+ unsubscribe() {
2007
+ if (this.imageCachedSubscription) {
2008
+ this.imageCachedSubscription.unsubscribe();
2009
+ }
2010
+ if (this.filenameSubscription) {
2011
+ this.filenameSubscription.unsubscribe();
2012
+ }
2013
+ if (this.regionUpdateSubscription) {
2014
+ this.regionUpdateSubscription.unsubscribe();
2015
+ }
2016
+ if (this.regionLiveEditSubscription) {
2017
+ this.regionLiveEditSubscription.unsubscribe();
2018
+ }
2019
+ if (this.channelSub) {
2020
+ this.channelSub.unsubscribe();
2021
+ }
2022
+ }
2023
+ /** Live-apply the channel display window (zmin/zmax) + reverse/invert to the
2024
+ * heatmap. No-op when no Plotly plot is mounted (OSD owns the div). */
2025
+ applyChannelDisplay(channels, rev, inv) {
2026
+ if (!this.plotDiv)
2027
+ return;
2028
+ const ch = channels?.[0];
2029
+ const colorscale = this.store.currentColormap()?.data?.value;
2030
+ // reverse-scale and invert each flip the ramp; both together cancel.
2031
+ const update = { reversescale: rev !== inv };
2032
+ if (colorscale != null)
2033
+ update.colorscale = colorscale; // live colormap recolor
2034
+ if (ch) {
2035
+ // Surface/isosurface/scatter3d colour-map via cmin/cmax; heatmap/contour via zmin/zmax.
2036
+ const threeD = this.plotType === PlotType.ISOSURFACE ||
2037
+ this.plotType === PlotType.SURFACE ||
2038
+ this.plotType === PlotType.SCATTER3D;
2039
+ if (threeD) {
2040
+ update.cmin = ch.min;
2041
+ update.cmax = ch.max;
2042
+ update.cauto = false;
2043
+ }
2044
+ else {
2045
+ update.zmin = ch.min;
2046
+ update.zmax = ch.max;
2047
+ update.zauto = false;
2048
+ }
2049
+ }
2050
+ try {
2051
+ Plotly.restyle(this.plotDiv, update);
2052
+ }
2053
+ catch {
2054
+ /* not a colour-mapped trace, or OSD owns the div */
2055
+ }
2056
+ }
2057
+ /** Binned intensity histogram for a channel from the cached source frames
2058
+ * (raw, pre-LUT). Grayscale cells are numbers; RGB cells are [r,g,b]. */
2059
+ getHistogram(channelIndex, _bins) {
2060
+ const frames = this.cachedImageFrames;
2061
+ if (!frames?.length)
2062
+ return null;
2063
+ const frame = frames[this.activeFrameIndex()] ?? frames[0];
2064
+ if (!frame?.length)
2065
+ return null;
2066
+ const counts = new Array(256).fill(0);
2067
+ for (const row of frame) {
2068
+ if (!row)
2069
+ continue;
2070
+ for (const cell of row) {
2071
+ let v;
2072
+ if (Array.isArray(cell)) {
2073
+ v = channelIndex >= 0 && channelIndex < cell.length
2074
+ ? cell[channelIndex]
2075
+ : Math.round(bt601Luminance(cell[0], cell[1], cell[2]));
2076
+ }
2077
+ else {
2078
+ v = cell;
2079
+ }
2080
+ v = v | 0;
2081
+ if (v < 0)
2082
+ v = 0;
2083
+ else if (v > 255)
2084
+ v = 255;
2085
+ counts[v]++;
2086
+ }
2087
+ }
2088
+ return histogram256(counts);
2089
+ }
2090
+ /** Async histogram stream — Plotly renders heatmap frame data (already in
2091
+ * memory, 8-bit luminance), so this just wraps the synchronous 8-bit
2092
+ * histogram. Native 16-bit histograms only apply to the OSD tile path. */
2093
+ getHistogram$(channelIndex, bins) {
2094
+ return of(this.getHistogram(channelIndex, bins));
2095
+ }
2096
+ /** Data export (16-bit TIFF) is an OSD-tile-path feature backed by the server.
2097
+ * Plotly renders heatmaps from frame data, so there's nothing to export here. */
2098
+ exportData() {
2099
+ console.warn('[plotly] 16-bit data export is not available for the heatmap backend.');
2100
+ }
2101
+ importRegions(geoJsonStr) {
2102
+ return this.plotUtilities.importROIsFromGeoJson(geoJsonStr);
2103
+ }
2104
+ exportRegions(regions) {
2105
+ const jsonString = this.plotUtilities.exportROIsToGeoJson(regions);
2106
+ this.plotUtilities.saveToFile(jsonString, this.fileName);
2107
+ }
2108
+ getGeoJsonString(regions) {
2109
+ return this.plotUtilities.exportROIsToGeoJson(regions);
2110
+ }
2111
+ getStackLoadingProgress() {
2112
+ return this.getStackLoadingProgress$();
2113
+ }
2114
+ isStackLoading() {
2115
+ return this.isStackLoading$();
2116
+ }
2117
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: PlotlyService, deps: [{ token: TILE_ACCESS_PORT }, { token: IMAGE_STATE_PORT }, { token: i1.MessageService }, { token: i2.HttpClient }, { token: i3.WandService }, { token: i4.WandToolService }, { token: i5.BrushToolService }, { token: i6.SamToolService }, { token: i7.SamPointToolService }, { token: i8.CellSegmentToolService }, { token: CELL_SEGMENTER, optional: true }, { token: i9.VertexEraserToolService }, { token: i10.ZoomToBoxToolService }, { token: i11.VisualizerStore }, { token: i12.RegionStore }], target: i0.ɵɵFactoryTarget.Injectable }); }
2118
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: PlotlyService, providedIn: 'root' }); }
2119
+ }
2120
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: PlotlyService, decorators: [{
2121
+ type: Injectable,
2122
+ args: [{
2123
+ providedIn: 'root'
2124
+ }]
2125
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
2126
+ type: Inject,
2127
+ args: [TILE_ACCESS_PORT]
2128
+ }] }, { type: undefined, decorators: [{
2129
+ type: Inject,
2130
+ args: [IMAGE_STATE_PORT]
2131
+ }] }, { type: i1.MessageService }, { type: i2.HttpClient }, { type: i3.WandService }, { type: i4.WandToolService }, { type: i5.BrushToolService }, { type: i6.SamToolService }, { type: i7.SamPointToolService }, { type: i8.CellSegmentToolService }, { type: undefined, decorators: [{
2132
+ type: Optional
2133
+ }, {
2134
+ type: Inject,
2135
+ args: [CELL_SEGMENTER]
2136
+ }] }, { type: i9.VertexEraserToolService }, { type: i10.ZoomToBoxToolService }, { type: i11.VisualizerStore }, { type: i12.RegionStore }] });
2137
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGxvdGx5LnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL2ltcGxlbWVudGF0aW9ucy9wbG90bHkvcGxvdGx5LnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRTdELE9BQU8sS0FBSyxNQUFNLE1BQU0sb0JBQW9CLENBQUM7QUFDN0MsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUNqQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDbEQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUV0QyxPQUFPLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUNqRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBRWhDLE9BQU8sRUFBa0IsZ0JBQWdCLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQztBQUMxRixPQUFPLEVBQWtCLGdCQUFnQixFQUFFLE1BQU0sd0NBQXdDLENBQUM7QUFDMUYsT0FBTyxFQUFFLGVBQWUsRUFBRSxLQUFLLEVBQWMsT0FBTyxFQUFnQixhQUFhLEVBQUUsRUFBRSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3BHLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDN0MsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ3BELE9BQU8sRUFBRSxNQUFNLEVBQUUsY0FBYyxFQUFFLGFBQWEsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQzdFLE9BQU8sRUFBRSxXQUFXLEVBQWUsTUFBTSxpQ0FBaUMsQ0FBQztBQUMzRSxPQUFPLEVBQW1CLGVBQWUsRUFBZ0IsTUFBTSxzQ0FBc0MsQ0FBQztBQUN0RyxP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Q0FBQyxLQUFLLEdBQUcsS0FBSztlQUMxQyxHQUFHLENBQUMsQ0FBQyxHQUFHLFNBQVMsSUFBSSxHQUFHLENBQUMsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO1FBQ3RELE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsR0FBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7UUFDdkQsTUFBTSxHQUFHLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxHQUFJLENBQUMsQ0FBQyxHQUFHLEdBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDO1FBQ3BFLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsR0FBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUM7UUFDMUQsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLFNBQVMsRUFBRSxHQUFJLENBQUMsQ0FBQyxHQUFHLEdBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO1FBRWpGLE1BQU0sRUFBRSxHQUFHLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUMzQixNQUFNLElBQUksR0FBRyxDQUFDLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsc0NBQXNDO1FBQ2hGLE1BQU0sRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLEVBQUUsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7UUFFckMsTUFBTSxLQUFLLEdBQUcsT0FBTyxHQUFHLElBQUksQ0FBQztRQUM3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RUQsYUFBYSxDQUFDLE9BQWlCO1FBQzdCLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsbUJBQW1CLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDbkUsSUFBSSxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsVUFBVSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUMzRCxDQUFDO0lBRUQsZ0JBQWdCLENBQUMsT0FBaUI7UUFDaEMsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLG1CQUFtQixDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3pELENBQUM7SUFFRCx1QkFBdUI7UUFDckIsT0FBTyxJQUFJLENBQUMsd0JBQXdCLEVBQUUsQ0FBQztJQUN6QyxDQUFDO0lBRUQsY0FBYztRQUNaLE9BQU8sSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO0lBQ2hDLENBQUM7OEdBcnBFVSxhQUFhLGtCQThISixnQkFBZ0IsYUFDaEIsZ0JBQWdCLHNRQVFKLGNBQWM7a0hBdkluQyxhQUFhLGNBRlosTUFBTTs7MkZBRVAsYUFBYTtrQkFIekIsVUFBVTttQkFBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkI7OzBCQStIYyxNQUFNOzJCQUFDLGdCQUFnQjs7MEJBQ3ZCLE1BQU07MkJBQUMsZ0JBQWdCOzswQkFRdkIsUUFBUTs7MEJBQUksTUFBTTsyQkFBQyxjQUFjIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSwgSW5qZWN0LCBPcHRpb25hbCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5pbXBvcnQgKiBhcyBQbG90bHkgZnJvbSAncGxvdGx5LmpzLWRpc3QtbWluJztcbmltcG9ydCB7IEltYWdlIH0gZnJvbSAnaW1hZ2UtanMnO1xuaW1wb3J0IHsgSHR0cENsaWVudCB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbi9odHRwJztcbmltcG9ydCB7IGZpcnN0VmFsdWVGcm9tIH0gZnJvbSAncnhqcyc7XG5cbmltcG9ydCB7IFBvbHlnb24sIFJlY3RhbmdsZSwgUmVnaW9uIH0gZnJvbSAnLi4vLi4vbW9kZWxzL3JlZ2lvbic7XG5pbXBvcnQgeyBCdWZmZXIgfSBmcm9tICdidWZmZXInO1xuaW1wb3J0IHsgSUltYWdlSW5mbywgSUltYWdlTWV0YWRhdGEgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvaW1hZ2UuY29udHJhY3QnO1xuaW1wb3J0IHsgVGlsZUFjY2Vzc1BvcnQsIFRJTEVfQUNDRVNTX1BPUlQgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvcG9ydHMvdGlsZS1hY2Nlc3MucG9ydCc7XG5pbXBvcnQgeyBJbWFnZVN0YXRlUG9ydCwgSU1BR0VfU1RBVEVfUE9SVCB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9wb3J0cy9pbWFnZS1zdGF0ZS5wb3J0JztcbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCwgRU1QVFksIE9ic2VydmFibGUsIFN1YmplY3QsIFN1YnNjcmlwdGlvbiwgY29tYmluZUxhdGVzdCwgb2YgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IE1lc3NhZ2VTZXJ2aWNlIH0gZnJvbSAncHJpbWVuZy9hcGknO1xuaW1wb3J0IHsgU2hhcGVTZWxlY3Rpb24gfSBmcm9tICcuLi8uLi9tb2RlbHMvc2hhcGUnO1xuaW1wb3J0IHsgQ09ORklHLCBDT05GSUdfU1VSRkFDRSwgUGxvdFV0aWxpdGllcyB9IGZyb20gJy4uLy4uL3Bsb3QudXRpbGl0aWVzJztcbmltcG9ydCB7IFdhbmRTZXJ2aWNlLCBXYW5kT3B0aW9ucyB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvd2FuZC93YW5kLnNlcnZpY2UnO1xuaW1wb3J0IHsgQ2FjaGVkSW1hZ2VEYXRhLCBXYW5kVG9vbFNlcnZpY2UsIFdhbmRUb29sSG9zdCB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvd2FuZC93YW5kLXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBCcnVzaFRvb2xTZXJ2aWNlLCBCcnVzaE9wdGlvbnMgfSBmcm9tICcuLi8uLi90b29sYmFyL2JydXNoL2JydXNoLXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBTYW1Ub29sU2VydmljZSB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3NhbS10b29sLnNlcnZpY2UnO1xuaW1wb3J0IHsgU2FtUG9pbnRUb29sU2VydmljZSB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3NhbS1wb2ludC10b29sLnNlcnZpY2UnO1xuaW1wb3J0IHsgQ2VsbFNlZ21lbnRUb29sU2VydmljZSB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvc2VnbWVudGF0aW9uL2NlbGwtc2VnbWVudC10b29sLnNlcnZpY2UnO1xuaW1wb3J0IHsgSUNlbGxTZWdtZW50ZXIsIENFTExfU0VHTUVOVEVSIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL2NlbGwtc2VnbWVudGVyLmNvbnRyYWN0JztcbmltcG9ydCB7IFZlcnRleEVyYXNlclRvb2xTZXJ2aWNlLCBWZXJ0ZXhFcmFzZXJUb29sSG9zdCB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvdmVydGV4LWVyYXNlci92ZXJ0ZXgtZXJhc2VyLXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBab29tVG9Cb3hUb29sU2VydmljZSB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvem9vbS10by1ib3gvem9vbS10by1ib3gtdG9vbC5zZXJ2aWNlJztcbmltcG9ydCB7XG4gIHBhcnNlU3ZnUGF0aFBvbHlnb24sXG4gIHZlcnRpY2VzVG9TdmdQYXRoLFxufSBmcm9tICcuLi8uLi9tb2RlbHMvZ2VvbWV0cnknO1xuaW1wb3J0IHsgUGxvdFR5cGUsIFBMT1RfVFlQRV9ERVNDUklQVE9SUywgUGxvdFR5cGVEZXNjcmlwdG9yIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL3Bsb3QtdHlwZSc7XG5pbXBvcnQge1xuICBQTE9UTFlfUExPVF9UWVBFX0lNUExTLFxuICBQbG90bHlQbG90VHlwZUltcGwsXG4gIFRyYWNlQnVpbGRJbnB1dCxcbn0gZnJvbSAnLi9wbG90bHktdHJhY2UtYnVpbGRlcnMnO1xuaW1wb3J0IHsgSVZpc3VhbGl6ZXIsIEludGVuc2l0eVByb2ZpbGUsIElJc29zdXJmYWNlQ29udHJvbHMsIElJbnRlbnNpdHlDb250cm9scyB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy92aXN1YWxpemVyLmNvbnRyYWN0JztcbmltcG9ydCB7IElIaXN0b2dyYW0gfSBmcm9tICcuLi8uLi9jb250cmFjdHMvY2hhbm5lbC1oaXN0b2dyYW0tYXBpLmNvbnRyYWN0JztcbmltcG9ydCB7IGJ0NjAxTHVtaW5hbmNlLCBoaXN0b2dyYW0yNTYgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvaW50ZW5zaXR5JztcbmltcG9ydCB7IFZpZXdlckNhcGFiaWxpdGllcywgVmlld2VyRmVhdHVyZSwgY2FwYWJpbGl0aWVzT2YgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvY2FwYWJpbGl0aWVzLmNvbnRyYWN0JztcbmltcG9ydCB7IElSZWdpb25PdmVybGF5IH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL3JlZ2lvbi1vdmVybGF5LmNvbnRyYWN0JztcbmltcG9ydCB7IFBsb3RseVJlZ2lvbk92ZXJsYXkgfSBmcm9tICcuL3Bsb3RseS1yZWdpb24tb3ZlcmxheSc7XG5pbXBvcnQgeyBJQ29vcmRpbmF0ZVRyYW5zZm9ybSB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9jb29yZGluYXRlLXRyYW5zZm9ybS5jb250cmFjdCc7XG5pbXBvcnQgeyBQbG90bHlDb29yZGluYXRlVHJhbnNmb3JtIH0gZnJvbSAnLi9wbG90bHktY29vcmRpbmF0ZS10cmFuc2Zvcm0nO1xuaW1wb3J0IHsgVmlzdWFsaXplclN0b3JlIH0gZnJvbSAnLi4vLi4vc3RvcmUvdmlzdWFsaXplci1zdG9yZS5zZXJ2aWNlJztcbmltcG9ydCB7IFJlZ2lvblN0b3JlIH0gZnJvbSAnLi4vLi4vc3RvcmUvcmVnaW9uLXN0b3JlLnNlcnZpY2UnO1xuXG4vLyBSZS1leHBvcnRlZCBzbyBleGlzdGluZyBjb25zdW1lcnMgY2FuIGtlZXAgaW1wb3J0aW5nIFBsb3RUeXBlIGZyb20gdGhpc1xuLy8gbW9kdWxlIHdoaWxlIGl0IHBoeXNpY2FsbHkgbGl2ZXMgaW4gdGhlIGJhY2tlbmQtbmV1dHJhbCBjb250cmFjdHMvIGRpci5cbi8vIFRPRE8ocGxvdHRpbmctYWJzdHJhY3Rpb24pOiBvbmNlIGFsbCBjb25zdW1lcnMgaW1wb3J0IGZyb21cbi8vICcuL2NvbnRyYWN0cy9wbG90LXR5cGUnIGRpcmVjdGx5LCBkcm9wIHRoaXMgcmUtZXhwb3J0LlxuZXhwb3J0IHsgUGxvdFR5cGUgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvcGxvdC10eXBlJztcblxuXG4od2luZG93IGFzIGFueSkuQnVmZmVyID0gQnVmZmVyO1xuXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBQbG90bHlTZXJ2aWNlIGltcGxlbWVudHMgSVZpc3VhbGl6ZXIge1xuXG4gIC8qKlxuICAgKiBQbG90bHkgaXMgdGhlIGZ1bGwtZmVhdHVyZWQgZGF0YSBiYWNrZW5kOiBpdCBzdXBwb3J0cyBldmVyeSBmZWF0dXJlLFxuICAgKiBpbmNsdWRpbmcgM0Qgc2NlbmVzLCBsaXZlIHNjYWxhciBjb2xvcm1hcHMsIHBpeGVsIHJlYWRiYWNrIGFuZCB0aGVcbiAgICogc2VydmVyLXNpZGUgaGlnaC1kZWYgem9vbSByZS1mZXRjaC4gKE9wZW5TZWFkcmFnb24ncyBzdHViIGFkdmVydGlzZXMgb25seVxuICAgKiBpbWFnZSBkaXNwbGF5LilcbiAgICovXG4gIHJlYWRvbmx5IGNhcGFiaWxpdGllczogVmlld2VyQ2FwYWJpbGl0aWVzID0gY2FwYWJpbGl0aWVzT2YoW1xuICAgIFZpZXdlckZlYXR1cmUuSW1hZ2VEaXNwbGF5LFxuICAgIFZpZXdlckZlYXR1cmUuU3VyZmFjZTNELFxuICAgIFZpZXdlckZlYXR1cmUuU2NhbGFyQ29sb3JtYXAsXG4gICAgVmlld2VyRmVhdHVyZS5QaXhlbFJlYWRiYWNrLFxuICAgIFZpZXdlckZlYXR1cmUuSGlnaERlZlpvb20sXG4gICAgVmlld2VyRmVhdHVyZS5TdGFja1NsaWRlcixcbiAgICBWaWV3ZXJGZWF0dXJlLklzb3N1cmZhY2UsXG4gIF0pO1xuXG4gIHByaXZhdGUgc2hhcGVzOiBhbnlbXSA9IFtdO1xuICBwcml2YXRlIHByZXZpb3VzU2hhcGVzOiBTaGFwZVNlbGVjdGlvbltdID0gW107XG4gIHByaXZhdGUgaXNSZWdpb25TYXZlZE9uID0gdHJ1ZTtcbiAgcHJpdmF0ZSBpbWFnZUxlbmd0aCE6IG51bWJlcjtcbiAgcHJpdmF0ZSBzY3JlZW5IZWlnaHQhOiBudW1iZXI7XG4gIHByaXZhdGUgcGxvdERpdiE6IHN0cmluZztcbiAgcHJpdmF0ZSBpc1JlYWxab29tID0gdHJ1ZTtcbiAgcHJpdmF0ZSBzY2FsZXJhdGlvID0gdHJ1ZTtcbiAgcHJpdmF0ZSB0cnVlSW1nU2l6ZSE6IG51bWJlcltdO1xuICBwcml2YXRlIHpvb21Db29yZGluYXRlczogbnVtYmVyW10gPSBbXTtcbiAgcHJpdmF0ZSBmaWxlTmFtZSE6IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICBwcml2YXRlIGRyYWdNb2RlITogc3RyaW5nO1xuICAvLyBSZWdpb24gZGF0YSAodGhlIGxpc3QgKyBwZXItaW1hZ2UgY2FjaGUpLCBzZWxlY3Rpb24sIGlkIG1pbnRpbmcsIHRoZSByZWdpb25cbiAgLy8gdXBkYXRlIGV2ZW50IGFuZCB0aGUgc2hhcGUgY29sb3VyL2xhYmVsIHByZWZzIG5vdyBsaXZlIGluIHRoZSBzaGFyZWRcbiAgLy8gUmVnaW9uU3RvcmUgKGluamVjdGVkIGFzIGB0aGlzLnJlZ2lvblN0b3JlYCkg4oCUIHRoZSBzaW5nbGUgc291cmNlIG9mIHRydXRoXG4gIC8vIGJvdGggYmFja2VuZHMgZGVsZWdhdGUgdG8uIGB0aGlzLnNoYXBlc2AgYmVsb3cgaXMga2VwdCBhcyBQbG90bHkncyAqcmVuZGVyXG4gIC8vIHByb2plY3Rpb24qIG9mIHRob3NlIHJlZ2lvbnMgKHRoZSBkaWN0IGFycmF5IFBsb3RseS5yZWxheW91dCBjb25zdW1lcykgYW5kXG4gIC8vIGlzIG1pcnJvcmVkIGludG8gdGhlIHN0b3JlIG9uIGV2ZXJ5IHdyaXRlLiBDb2xvcm1hcCwgcmV2ZXJzZS1zY2FsZSwgaW1hZ2VcbiAgLy8gbWV0YWRhdGEgYW5kIGNsYXNzaWZpY2F0aW9uIGNvbG91cnMgbGl2ZSBpbiB0aGUgc2hhcmVkIFZpc3VhbGl6ZXJTdG9yZVxuICAvLyAoaW5qZWN0ZWQgYXMgYHRoaXMuc3RvcmVgKS5cbiAgcHJpdmF0ZSB1cmxzITogc3RyaW5nW107XG4gIGltYWdlSW5mbyE6IElJbWFnZUluZm87XG4gIHByaXZhdGUgcGxvdFV0aWxpdGllcyA9IG5ldyBQbG90VXRpbGl0aWVzKCk7XG4gIHByaXZhdGUgcGxvdFR5cGUhOiBQbG90VHlwZTtcblxuICBwcml2YXRlIG9uUGxvdE1vdXNlRG93bjogKCgpID0+IHZvaWQpIHwgbnVsbCA9IG51bGw7XG5cbiAgLy8gVGhlIHBlci1pbWFnZSByZWdpb24gY2FjaGUgKHJlZ2lvbnNCeUltYWdlS2V5L2N1cnJlbnRJbWFnZUtleSkgbm93IGxpdmVzIGluXG4gIC8vIHRoZSBzaGFyZWQgUmVnaW9uU3RvcmU7IHNldEFjdGl2ZUltYWdlKCkgZGVsZWdhdGVzIHRvIGl0IGFuZCByZS1wcm9qZWN0c1xuICAvLyBgdGhpcy5zaGFwZXNgIGZyb20gdGhlIHN0b3JlIGFmdGVyd2FyZHMuXG5cbiAgLyoqIFBpeGVsIGRhdGEgY2FjaGVkIGZvciB3YW5kIHNhbXBsaW5nLiBkYXRhW3pJbmRleF0gaXMgYSAyLUQgbWF0cml4LiAqL1xuICBwcml2YXRlIGNhY2hlZEltYWdlRnJhbWVzPzogYW55W107XG4gIHByaXZhdGUgY2FjaGVkSW1hZ2VXaWR0aCA9IDA7XG4gIHByaXZhdGUgY2FjaGVkSW1hZ2VIZWlnaHQgPSAwO1xuICBwcml2YXRlIGNhY2hlZEltYWdlUmF0aW9zOiBudW1iZXJbXSA9IFsxLCAxXTtcbiAgcHJpdmF0ZSBjYWNoZWRJc0dyYXlzY2FsZSA9IGZhbHNlO1xuICAvKiogRGF0YS1zcGFjZSBjb29yZGluYXRlIG9mIHRoZSBjYWNoZWQgZnJhbWUncyBwaXhlbCAoMCwwKS4gWzAsMF0gZm9yIHRoZSBmdWxsXG4gICAqICBpbWFnZTsgYSBjcm9wJ3MgdG9wLWxlZnQgd2hlbiB6b29tZWQgKHNvIHRoZSBpbnRlbnNpdHkgcHJvZmlsZSBzYW1wbGVzIHRoZVxuICAgKiAgem9vbS1sZXZlbCBwaXhlbHMgYXQgdGhlIHJpZ2h0IG9mZnNldC9yZXNvbHV0aW9uKS4gKi9cbiAgcHJpdmF0ZSBjYWNoZWRGcmFtZU9yaWdpbjogW251bWJlciwgbnVtYmVyXSA9IFswLCAwXTtcbiAgLyoqIExhc3QgdmlzaWJsZSBpbWFnZS1waXhlbCByZWN0YW5nbGUgZnJvbSB0aGUgYWN0aXZlIGJhY2tlbmQncyB2aWV3cG9ydCAoc2V0IGJ5XG4gICAqICB0aGUgT1NEIHZpZXdwb3J0LWNoYW5nZSBob29rIHZpYSByZWZyZXNoSW50ZW5zaXR5U2FtcGxpbmdGb3JSb2kpLiBOZXcgcHJvZmlsZSBsaW5lcyBhcmVcbiAgICogIHBsYWNlZCB3aXRoaW4gaXQgc28gdGhleSBsYW5kIG9uLXNjcmVlbiB3aGVuIHpvb21lZCBpbjsgbnVsbCDihpIgdXNlIGZ1bGwgaW1hZ2UuICovXG4gIHByaXZhdGUgbGFzdFZpc2libGVSb2k6IHsgeDogbnVtYmVyOyB5OiBudW1iZXI7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyIH0gfCBudWxsID0gbnVsbDtcblxuICAvKiogZXZlbnRzICovXG4gIHByaXZhdGUgb25SZWxheW91dEV2ZW50OiBhbnk7XG5cbiAgcHJpdmF0ZSBzdGFja0xvYWRpbmckID0gbmV3IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPihmYWxzZSk7XG4gIHByaXZhdGUgc3RhY2tMb2FkaW5nUHJvZ3Jlc3MkID0gbmV3IEJlaGF2aW9yU3ViamVjdDxudW1iZXI+KDApO1xuICAvLyBjdXJyZW50IGluZGV4IG9mIGltYWdlIGluIHN0YWNrIChpZiBzdGFjayksIDAgaWYgc2luZ2xlIGltYWdlXG4gIHByaXZhdGUgekluZGV4ID0gbmV3IEJlaGF2aW9yU3ViamVjdDxudW1iZXI+KDApO1xuICBwcml2YXRlIGF1dG9zY2FsZUV2ZW50ID0gbmV3IFN1YmplY3Q8YW55PigpO1xuICAvLyBUaGUgcmVnaW9uIHVwZGF0ZSBldmVudCBhbmQgc2VsZWN0aW9uIHN0cmVhbSBhcmUgb3duZWQgYnkgdGhlIHNoYXJlZFxuICAvLyBSZWdpb25TdG9yZTsgZ2V0UmVnaW9uVXBkYXRlRXZlbnQoKS9nZXRTZWxlY3RlZFNoYXBlSW5kaWNlcyQoKSBkZWxlZ2F0ZSB0b1xuICAvLyBpdCBzbyBldmVyeSBjb25zdW1lciAoYW5kIHRoZSBPU0QgYmFja2VuZCkgc2VlcyBvbmUgc3RyZWFtLlxuICAvKiogQnJpZ2h0LCB3ZWxsLXNlcGFyYXRlZCBjb2xvdXJzIGN5Y2xlZCBhcyB0aGUgdXNlciBhZGRzIHByb2ZpbGUtbGluZSBST0lzLlxuICAgKiAgVGhlIG1hdGNoaW5nIGluc2V0IHRyYWNlIGlzIGRyYXduIGluIHRoZSBzYW1lIGNvbG91ci4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBQUk9GSUxFX1BBTEVUVEUgPSBbXG4gICAgJyNGRkQ0MDAnLCAvLyB5ZWxsb3dcbiAgICAnIzAwRTVGRicsIC8vIGN5YW5cbiAgICAnI0ZGMkQ5NScsIC8vIG1hZ2VudGFcbiAgICAnIzM5RkYxNCcsIC8vIGdyZWVuXG4gICAgJyNGRjk1MDAnLCAvLyBvcmFuZ2VcbiAgICAnIzdDNERGRicsIC8vIHZpb2xldFxuICAgICcjRkYzQjMwJywgLy8gcmVkXG4gICAgJyMxOEZGRkYnLCAvLyBhcXVhXG4gIF07XG4gIC8qKiBQYWxldHRlIGN1cnNvciBmb3IgcHJvZmlsZSBsaW5lcyAobmV2ZXIgcmVzZXQgb24gZGVsZXRlLCBzbyBjb2xvdXJzIGtlZXBcbiAgICogIGN5Y2xpbmcgYWNyb3NzIHRoZSBzZXNzaW9uKS4gKi9cbiAgcHJpdmF0ZSBwcm9maWxlQ29sb3JTZXEgPSAwO1xuICBwcml2YXRlIGludGVuc2l0eVByb2ZpbGUkID0gbmV3IFN1YmplY3Q8SW50ZW5zaXR5UHJvZmlsZVtdPigpO1xuICAvKiogSVNPU1VSRkFDRSBiYW5kIGFzIGEgMOKAkzI1NSBzbGlkZXIgcG9zaXRpb24sIG1hcHBlZCBvbnRvIHRoZSB2b2x1bWUncyByZWFsXG4gICAqICBpbnRlbnNpdHkgcmFuZ2UgYXQgcmVuZGVyIHRpbWUgKHNlZSBtYXBJc29CYW5kKS4gRGVmYXVsdHMgdG8gdGhlIGZ1bGwgcmFuZ2VcbiAgICogIHNvIHRoZSBmaXJzdCByZW5kZXIgc2hvd3MgdGhlIHdob2xlIHN0cnVjdHVyZSByZWdhcmRsZXNzIG9mIGhvdyBicmlnaHQgb3JcbiAgICogIGRhcmsgdGhlIHZvbHVtZSBpczsgdGhlIHVzZXIgdGhlbiBuYXJyb3dzIHRoZSBiYW5kIHRvIGlzb2xhdGUgaW50ZW5zaXRpZXMuICovXG4gIHByaXZhdGUgaXNvTWluID0gMDtcbiAgcHJpdmF0ZSBpc29NYXggPSAyNTU7XG4gIC8qKiBBY3R1YWwgaW50ZW5zaXR5IHJhbmdlIG9mIHRoZSB2b2x1bWUgb24gc2NyZWVuIChbbWluLG1heF0gb3ZlciB0aGUgbG9hZGVkXG4gICAqICBmcmFtZXMpLiBUaGUgc2xpZGVyIHNwYW5zIDDigJMyNTUsIGJ1dCBhIGdpdmVuIHN0YWNrIG9mdGVuIG9jY3VwaWVzIG9ubHkgYVxuICAgKiAgc3ViLXJhbmdlLCBzbyB3ZSBjbGFtcCB0aGUgaXNvIGJhbmQgaW50byB0aGlzIHJhbmdlICh3aXRoIGEgc21hbGwgbWFyZ2luKVxuICAgKiAgdG8gZ3VhcmFudGVlIG5vbi1kZWdlbmVyYXRlIHN1cmZhY2VzIOKAlCBib3RoIG9uIGZpcnN0IHJlbmRlciBhbmQgb24gbGl2ZVxuICAgKiAgc2xpZGVyIG1vdmVzLiBOdWxsIHVudGlsIGFuIGlzb3N1cmZhY2Ugdm9sdW1lIGhhcyBiZWVuIG1lYXN1cmVkLiAqL1xuICBwcml2YXRlIGlzb0RhdGFSYW5nZTogW251bWJlciwgbnVtYmVyXSB8IG51bGwgPSBudWxsO1xuICAvLyBJZCBtaW50aW5nIGFuZCB0aGUgc2VsZWN0aW9uIGluZGV4IHN0cmVhbSBhcmUgb3duZWQgYnkgdGhlIHNoYXJlZFxuICAvLyBSZWdpb25TdG9yZS4gU2VsZWN0aW9uIHN0aWxsIGRyaXZlcyBQbG90bHkncyBgX2FjdGl2ZVNoYXBlSW5kZXhgIChzbyBhXG4gIC8vIHNpbmdsZSBzaGFwZSBnZXRzIHRoZSBlZGl0IGhhbmRsZXMpIOKAlCBzZWUgc2V0U2VsZWN0ZWRTaGFwZUluZGljZXMoKS5cbiAgcHJpdmF0ZSBpbWFnZUNhY2hlZCA9IGZhbHNlO1xuICBwcml2YXRlIGltYWdlQ2FjaGVkU3Vic2NyaXB0aW9uOiBTdWJzY3JpcHRpb247XG4gIHByaXZhdGUgZmlsZW5hbWVTdWJzY3JpcHRpb246IFN1YnNjcmlwdGlvbjtcbiAgLyoqIERyaXZlcyB0aGUgaW50ZW5zaXR5IGluc2V0IG9mZiByZWdpb24gYWRkcy9kcmFncy9kZWxldGVzIChib3RoIGJhY2tlbmRzKS4gKi9cbiAgcHJpdmF0ZSByZWdpb25VcGRhdGVTdWJzY3JpcHRpb24/OiBTdWJzY3JpcHRpb247XG4gIC8qKiBMaXZlIChwZXItZnJhbWUsIG5vbi1jb2FsZXNjZWQpIHJlZ2lvbiBlZGl0cyDigJQgc28gdGhlIGluc2V0IHRyYWNrcyBhbiBPU0RcbiAgICogIGxpbmUgUk9JIGxpdmUgZHVyaW5nIGEgZHJhZyAoT1NEIGJhdGNoZXMgcmVnaW9uVXBkYXRlJCB1bnRpbCByZWxlYXNlKS4gKi9cbiAgcHJpdmF0ZSByZWdpb25MaXZlRWRpdFN1YnNjcmlwdGlvbj86IFN1YnNjcmlwdGlvbjtcbiAgcHJpdmF0ZSBjaGFubmVsU3ViPzogU3Vic2NyaXB0aW9uO1xuXG4gIHByaXZhdGUgd2FuZEhvc3QhOiBXYW5kVG9vbEhvc3Q7XG4gIHByaXZhdGUgZXJhc2VySG9zdCE6IFZlcnRleEVyYXNlclRvb2xIb3N0O1xuICAvKiogVGhpcyBiYWNrZW5kJ3MgcmVnaW9uIHJlbmRlcmVyIChsYXppbHkgY3JlYXRlZCBpbiBnZXRSZWdpb25PdmVybGF5KS4gKi9cbiAgcHJpdmF0ZSByZWdpb25PdmVybGF5PzogSVJlZ2lvbk92ZXJsYXk7XG4gIHByaXZhdGUgcmVhZG9ubHkgY29vcmRpbmF0ZVRyYW5zZm9ybTogSUNvb3JkaW5hdGVUcmFuc2Zvcm0gPVxuICAgIG5ldyBQbG90bHlDb29yZGluYXRlVHJhbnNmb3JtKFxuICAgICAgKCkgPT4gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2KSxcbiAgICAgICgpID0+IHRoaXMuZ2V0T3ZlcmxheUNvbnRhaW5lcigpKTtcblxuICBjb25zdHJ1Y3RvcihASW5qZWN0KFRJTEVfQUNDRVNTX1BPUlQpIHByaXZhdGUgdGlsZXM6IFRpbGVBY2Nlc3NQb3J0LFxuICAgICAgICAgICAgICBASW5qZWN0KElNQUdFX1NUQVRFX1BPUlQpIHByaXZhdGUgc3RhdGU6IEltYWdlU3RhdGVQb3J0LFxuICAgICAgICAgICAgICBwdWJsaWMgbWVzc2FnZVNlcnZpY2U6IE1lc3NhZ2VTZXJ2aWNlLCBwcml2YXRlIGh0dHA6IEh0dHBDbGllbnQsXG4gICAgICAgICAgICAgIHByaXZhdGUgd2FuZFNlcnZpY2U6IFdhbmRTZXJ2aWNlLFxuICAgICAgICAgICAgICBwcml2YXRlIHdhbmRUb29sOiBXYW5kVG9vbFNlcnZpY2UsXG4gICAgICAgICAgICAgIHByaXZhdGUgYnJ1c2hUb29sOiBCcnVzaFRvb2xTZXJ2aWNlLFxuICAgICAgICAgICAgICBwcml2YXRlIHNhbVRvb2w6IFNhbVRvb2xTZXJ2aWNlLFxuICAgICAgICAgICAgICBwcml2YXRlIHNhbVBvaW50VG9vbDogU2FtUG9pbnRUb29sU2VydmljZSxcbiAgICAgICAgICAgICAgcHJpdmF0ZSBjZWxsU2VnbWVudFRvb2w6IENlbGxTZWdtZW50VG9vbFNlcnZpY2UsXG4gICAgICAgICAgICAgIEBPcHRpb25hbCgpIEBJbmplY3QoQ0VMTF9TRUdNRU5URVIpIHByaXZhdGUgY2VsbFNlZ21lbnRlcjogSUNlbGxTZWdtZW50ZXIgfCBudWxsLFxuICAgICAgICAgICAgICBwcml2YXRlIHZlcnRleEVyYXNlclRvb2w6IFZlcnRleEVyYXNlclRvb2xTZXJ2aWNlLFxuICAgICAgICAgICAgICBwcml2YXRlIHpvb21Ub0JveFRvb2w6IFpvb21Ub0JveFRvb2xTZXJ2aWNlLFxuICAgICAgICAgICAgICBwcml2YXRlIHN0b3JlOiBWaXN1YWxpemVyU3RvcmUsXG4gICAgICAgICAgICAgIHByaXZhdGUgcmVnaW9uU3RvcmU6IFJlZ2lvblN0b3JlKSB7XG4gICAgLy8gcmVsYXlvdXQgZXZlbnQgcm91dGVyXG4gICAgdGhpcy5vblJlbGF5b3V0RXZlbnQgPSAoZXZlbnQ6IGFueSkgPT4geyB0aGlzLnJlbGF5b3V0RXZlbnRIYW5kbGVyKGV2ZW50KTsgfTtcblxuICAgIC8vIFRvb2wgc2VydmljZXMgcmVhZC9tdXRhdGUgb3VyIHN0YXRlIHZpYSBzbWFsbCAqSG9zdCBpbnRlcmZhY2VzLiBTdG9yZWRcbiAgICAvLyBzbyB3ZSBjYW4gcmUtYmluZCB0aGVtIHRvIG91cnNlbHZlcyB3aGVuZXZlciBhIFBsb3RseSB0b29sIGlzIGFjdGl2YXRlZFxuICAgIC8vICh0aGUgT3BlblNlYWRyYWdvbiBiYWNrZW5kIHJlLWJpbmRzIHRoZSBzYW1lIHNpbmdsZXRvbiB0b29scyB0byBpdHNlbGYpLlxuICAgIHRoaXMud2FuZEhvc3QgPSB7XG4gICAgICBnZXRSZWdpb25zOiAoKSA9PiB0aGlzLnJlZ2lvblN0b3JlLmdldFJlZ2lvbnMoKSxcbiAgICAgIHNldFJlZ2lvbnM6IChyZWdpb25zKSA9PiB0aGlzLnNldFJlZ2lvbnMocmVnaW9ucyksXG4gICAgICBnZXRDYWNoZWRJbWFnZURhdGE6ICgpID0+IHRoaXMuZ2V0Q2FjaGVkSW1hZ2VEYXRhKCksXG4gICAgICBnZXRBY3RpdmVGcmFtZUluZGV4OiAoKSA9PiB0aGlzLmFjdGl2ZUZyYW1lSW5kZXgoKSxcbiAgICAgIGdldE92ZXJsYXlDb250YWluZXI6ICgpID0+IHRoaXMuZ2V0T3ZlcmxheUNvbnRhaW5lcigpLFxuICAgICAgZ2V0Q29vcmRpbmF0ZVRyYW5zZm9ybTogKCkgPT4gdGhpcy5nZXRDb29yZGluYXRlVHJhbnNmb3JtKCksXG4gICAgICBnZXRGaWxlTmFtZTogKCkgPT4gdGhpcy5maWxlTmFtZSxcbiAgICAgIGdldFNoYXBlQ29sb3I6ICgpID0+IHRoaXMucmVnaW9uU3RvcmUuZ2V0U2hhcGVDb2xvcigpLFxuICAgIH07XG4gICAgdGhpcy5lcmFzZXJIb3N0ID0ge1xuICAgICAgZ2V0UmVnaW9uczogKCkgPT4gdGhpcy5yZWdpb25TdG9yZS5nZXRSZWdpb25zKCksXG4gICAgICBzZXRSZWdpb25zOiAocmVnaW9ucykgPT4gdGhpcy5zZXRSZWdpb25zKHJlZ2lvbnMpLFxuICAgICAgaW52YWxpZGF0ZVdhbmRSZWdpb246ICgpID0+IHRoaXMud2FuZFRvb2wuY2xlYXJBY3RpdmVSZWdpb24oKSxcbiAgICAgIGdldE92ZXJsYXlDb250YWluZXI6ICgpID0+IHRoaXMuZ2V0T3ZlcmxheUNvbnRhaW5lcigpLFxuICAgICAgZ2V0Q29vcmRpbmF0ZVRyYW5zZm9ybTogKCkgPT4gdGhpcy5nZXRDb29yZGluYXRlVHJhbnNmb3JtKCksXG4gICAgICBnZXRDYWNoZWRJbWFnZVJhdGlvOiAoKSA9PiB0aGlzLmNhY2hlZEltYWdlUmF0aW9zWzBdIHx8IDEsXG4gICAgfTtcbiAgICB0aGlzLndhbmRUb29sLmJpbmRIb3N0KHRoaXMud2FuZEhvc3QpO1xuICAgIC8vIFRoZSBicnVzaCByZXVzZXMgdGhlIHdhbmQgaG9zdCAoc2FtZSBjb29yZGluYXRlIGZyYW1lICsgcmVnaW9uIGFjY2VzcykuXG4gICAgdGhpcy5icnVzaFRvb2wuYmluZEhvc3QodGhpcy53YW5kSG9zdCk7XG4gICAgdGhpcy52ZXJ0ZXhFcmFzZXJUb29sLmJpbmRIb3N0KHRoaXMuZXJhc2VySG9zdCk7XG4gICAgdGhpcy5iaW5kWm9vbVRvQm94SG9zdCgpO1xuXG4gICAgdGhpcy5pbWFnZUNhY2hlZFN1YnNjcmlwdGlvbiA9IHRoaXMuc3RhdGUuaXNJbWFnZUNhY2hlZCQoKS5zdWJzY3JpYmUoaW1hZ2VDYWNoZWQgPT4ge1xuICAgICAgdGhpcy5pbWFnZUNhY2hlZCA9IGltYWdlQ2FjaGVkO1xuICAgIH0pO1xuICAgIHRoaXMuZmlsZW5hbWVTdWJzY3JpcHRpb24gPSB0aGlzLnN0YXRlLmdldEZpbGVuYW1lJCgpLnN1YnNjcmliZShmaWxlbmFtZSA9PiB7XG4gICAgICB0aGlzLmZpbGVOYW1lID0gZmlsZW5hbWU7XG4gICAgfSk7XG4gICAgLy8gTGl2ZSByZWNvbG9yIGZyb20gdGhlIENoYW5uZWxzICYgSGlzdG9ncmFtIHBhbmU6IHJlc3R5bGUgdGhlIGRpc3BsYXkgd2luZG93ICh6bWluL3ptYXggZm9yXG4gICAgLy8gYSBoZWF0bWFwLCBjbWluL2NtYXggZm9yIGlzb3N1cmZhY2Uvc3VyZmFjZSksIHRoZSBjb2xvcnNjYWxlIChjb2xvcm1hcCksIGFuZCByZXZlcnNlL2ludmVydFxuICAgIC8vIHdoZW4gdGhlIGNoYW5uZWwgc3RhdGUgT1IgdGhlIGNvbG9ybWFwIGNoYW5nZXMuIChHYW1tYSBpcyBhcHBsaWVkIGJ5IHRoZSBPU0QgaW1hZ2UgdmlldzsgdGhlXG4gICAgLy8gUGxvdGx5IGNvbG9yc2NhbGUgd2luZG93IGNvdmVycyB0aGUgY29tbW9uIGNvbnRyYXN0IGNhc2UuKVxuICAgIHRoaXMuY2hhbm5lbFN1YiA9IGNvbWJpbmVMYXRlc3QoW1xuICAgICAgdGhpcy5zdG9yZS5nZXRDaGFubmVsU3RhdGVzKCksXG4gICAgICB0aGlzLnN0b3JlLmdldFJldmVyc2VTY2FsZSgpLFxuICAgICAgdGhpcy5zdG9yZS5nZXRJbnZlcnQoKSxcbiAgICAgIHRoaXMuc3RvcmUuZ2V0Q29sb3JtYXAoKSxcbiAgICBdKS5zdWJzY3JpYmUoKFtjaGFubmVscywgcmV2LCBpbnZdKSA9PiB0aGlzLmFwcGx5Q2hhbm5lbERpc3BsYXkoY2hhbm5lbHMsIHJldiwgaW52KSk7XG4gICAgLy8gUHJvZmlsZSBsaW5lcyBhcmUgc3RvcmUgcmVnaW9uczsgYW55IHJlZ2lvbiBjaGFuZ2UgKGFkZC9kcmFnL2RlbGV0ZSwgb25cbiAgICAvLyBlaXRoZXIgYmFja2VuZCkgc2hvdWxkIHJlZnJlc2ggdGhlIGluc2V0IHRyYWNlcy4gVGhlIGxpdmUtZWRpdCBzdHJlYW1cbiAgICAvLyBmaXJlcyBwZXIgZnJhbWUgZHVyaW5nIGEgZHJhZyAoT1NEIGNvYWxlc2NlcyByZWdpb25VcGRhdGUkIHVudGlsIHJlbGVhc2UpLFxuICAgIC8vIHNvIHRoZSBpbnNldCB0cmFja3MgYW4gT1NEIGxpbmUgUk9JIGxpdmUgYXMgaXQncyBtb3ZlZC5cbiAgICB0aGlzLnJlZ2lvblVwZGF0ZVN1YnNjcmlwdGlvbiA9IHRoaXMucmVnaW9uU3RvcmUuZ2V0UmVnaW9uVXBkYXRlRXZlbnQoKS5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgdGhpcy5lbWl0UHJvZmlsZXMoKTtcbiAgICB9KTtcbiAgICB0aGlzLnJlZ2lvbkxpdmVFZGl0U3Vic2NyaXB0aW9uID0gdGhpcy5yZWdpb25TdG9yZS5nZXRSZWdpb25MaXZlRWRpdCQoKS5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgdGhpcy5lbWl0UHJvZmlsZXMoKTtcbiAgICB9KTtcbiAgfVxuXG4gIGdldFRydWVJbWFnZVNpemUoKTogeyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlciB9IHwgbnVsbCB7XG4gICAgIGlmICghdGhpcy50cnVlSW1nU2l6ZSkgcmV0dXJuIG51bGw7XG4gICAgIHJldHVybiB7XG4gICAgICAgd2lkdGg6IHRoaXMudHJ1ZUltZ1NpemVbMV0gLSB0aGlzLnRydWVJbWdTaXplWzBdLFxuICAgICAgIGhlaWdodDogdGhpcy50cnVlSW1nU2l6ZVszXSAtIHRoaXMudHJ1ZUltZ1NpemVbMl0sXG4gICAgIH07XG4gIH1cblxuICAvKipcbiAgICogTG9hZCBpbWFnZVxuICAgKiBAcGFyYW0gaW1hZ2VJbmZvXG4gICAqIEBwYXJhbSB6SW5kZXggaW5kZXggb2YgdGhlIGltYWdlIHRvIGxvYWRcbiAgICogQHJldHVybiBhbiBvYmplY3Qgd2l0aCBkYXRhIGFuZCByYXRpbyBrZXlzXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgbG9hZChpbWFnZUluZm86IElJbWFnZUluZm8sIHpJbmRleDogbnVtYmVyKSB7XG4gICAgY29uc3QgdXJscyA9IGltYWdlSW5mby51cmxzO1xuICAgIC8vIHVzZSB6SW5kZXggcHJvdmlkZWQgaWYgYW55LCAwIG90aGVyd2lzZVxuICAgIGxldCBpbWFnZVVybDtcbiAgICBpZiAoekluZGV4KSB7XG4gICAgICBpbWFnZVVybCA9IHVybHNbekluZGV4XTtcbiAgICB9IGVsc2Uge1xuICAgICAgaW1hZ2VVcmwgPSB1cmxzWzBdO1xuICAgIH1cbiAgICBjb25zdCBpc0dyYXlzY2FsZSA9IGltYWdlSW5mby5pc0dyYXlzY2FsZTtcbiAgICBjb25zdCBpbWFnZSA9IGF3YWl0IHRoaXMubG9hZEltYWdlKGltYWdlVXJsKTtcbiAgICBjb25zdCB4UmF0aW8gPSBpbWFnZUluZm8udHJ1ZUltYWdlU2l6ZVswXSAvIGltYWdlLndpZHRoO1xuICAgIGNvbnN0IHlSYXRpbyA9IGltYWdlSW5mby50cnVlSW1hZ2VTaXplWzFdIC8gaW1hZ2UuaGVpZ2h0O1xuXG4gICAgY29uc3QgdHJ1ZUltYWdlU2l6ZSA9IFtdO1xuICAgIC8vIFt4MCwgeDEsIHkwLCB5MV1cbiAgICB0cnVlSW1hZ2VTaXplWzBdID0gMDtcbiAgICB0cnVlSW1hZ2VTaXplWzFdID0gaW1hZ2VJbmZvLnRydWVJbWFnZVNpemVbMF07XG4gICAgdHJ1ZUltYWdlU2l6ZVsyXSA9IDA7XG4gICAgdHJ1ZUltYWdlU2l6ZVszXSA9IGltYWdlSW5mby50cnVlSW1hZ2VTaXplWzFdO1xuICAgIHRoaXMudHJ1ZUltZ1NpemUgPSB0cnVlSW1hZ2VTaXplO1xuICAgIHRoaXMuZmlsZU5hbWUgPSBpbWFnZUluZm8uZmlsZU5hbWU7XG4gICAgaWYgKGltYWdlSW5mby5pc1N0YWNrICYmIGltYWdlSW5mby5zaG93U3RhY2spIHtcbiAgICAgIGNvbnN0IGltYWdlczogYW55W10gPSBbXTtcbiAgICAgIHRoaXMuc3RhY2tMb2FkaW5nUHJvZ3Jlc3MkLm5leHQoMCk7XG4gICAgICAvLyBPbmUgVVJMIHBlciBzbGljZSDigJQgbG9hZCB0aGVtIGFsbCAoZWFybGllciBgbGVuZ3RoLTFgIGRyb3BwZWQgdGhlIGxhc3QpLlxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB1cmxzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGBpbWFnZSBzdGFjayAke2l9YCk7XG4gICAgICAgIC8vIGRvIG5vdCBrZWVwIHdpdGggbG9hZGluZyBpZiBmaWxlbmFtZSBpcyBkaWZmZXJlbnQgKGEgbmV3IGZpbGUgaGFzIGJlZW4gc2VsZWN0ZWQpXG4gICAgICAgIC8vIG9yIGlmIHRoZSBzdGFja0xvYWRpbmcgdmFsdWUgaXMgc2V0IHRvIGZhbHNlXG4gICAgICAgIGlmICh0aGlzLmZpbGVOYW1lID09PSBpbWFnZUluZm8uZmlsZU5hbWUgJiYgdGhpcy5zdGFja0xvYWRpbmckLnZhbHVlKSB7XG4gICAgICAgICAgY29uc3QgaW1hZ2UgPSBhd2FpdCB0aGlzLmxvYWRJbWFnZSh1cmxzW2ldKTtcbiAgICAgICAgICBpZiAoaXNHcmF5c2NhbGUpIHtcbiAgICAgICAgICAgIGNvbnN0IGdyZXkgPSBpbWFnZS5ncmV5KCk7XG4gICAgICAgICAgICBjb25zdCBpbWdEYXRhID0gdGhpcy5wbG90VXRpbGl0aWVzLmFycmF5VG9NYXRyaXgoQXJyYXkuZnJvbShncmV5LmRhdGEpLCBpbWFnZS53aWR0aCk7XG4gICAgICAgICAgICBpbWFnZXMucHVzaChpbWdEYXRhKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc3QgcmdiRGF0YSA9IGltYWdlLmdldFBpeGVsc0FycmF5KCk7XG4gICAgICAgICAgICBjb25zdCByZ2JNYXRyaXggPSB0aGlzLnBsb3RVdGlsaXRpZXMuYXJyYXlUb01hdHJpeChyZ2JEYXRhLCBpbWFnZS53aWR0aCk7XG4gICAgICAgICAgICBpbWFnZXMucHVzaChyZ2JNYXRyaXgpO1xuICAgICAgICAgIH1cbiAgICAgICAgICB0aGlzLnN0YWNrTG9hZGluZ1Byb2dyZXNzJC5uZXh0KE1hdGgucm91bmQoKGkgKiAxMDApIC8gdXJscy5sZW5ndGgpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBicmVhayBzdGFjayBsb2FkaW5nXG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIC8vIHJlc2V0IHN0YWNrTG9hZGluZyBwcm9ncmVzcyB0byAwXG4gICAgICB0aGlzLnN0YWNrTG9hZGluZ1Byb2dyZXNzJC5uZXh0KDApO1xuICAgICAgcmV0dXJuIHsgZGF0YTogaW1hZ2VzLCByYXRpb3M6IFt4UmF0aW8sIHlSYXRpb10sXG4gICAgICAgICAgICAgICBzaXplczogW2ltYWdlLndpZHRoLCBpbWFnZS5oZWlnaHRdLFxuICAgICAgICAgICAgICAgZmlsZW5hbWU6IGltYWdlSW5mby5maWxlTmFtZSB9O1xuICAgIH0gZWxzZSB7XG4gICAgICBsZXQgaW1hZ2VEYXRhO1xuICAgICAgaWYgKGlzR3JheXNjYWxlKSB7XG4gICAgICAgIGNvbnN0IGdyZXkgPSBpbWFnZS5ncmV5KCk7XG4gICAgICAgIGltYWdlRGF0YSA9IHRoaXMucGxvdFV0aWxpdGllcy5hcnJheVRvTWF0cml4KEFycmF5LmZyb20oZ3JleS5kYXRhKSwgaW1hZ2Uud2lkdGgpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3QgcmdiRGF0YSA9IGltYWdlLmdldFBpeGVsc0FycmF5KCk7XG4gICAgICAgIGltYWdlRGF0YSA9IHRoaXMucGxvdFV0aWxpdGllcy5hcnJheVRvTWF0cml4KHJnYkRhdGEsIGltYWdlLndpZHRoKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB7IGRhdGE6IFtpbWFnZURhdGFdLFxuICAgICAgICByYXRpb3M6IFt4UmF0aW8sIHlSYXRpb10sXG4gICAgICAgIHNpemVzOiBbaW1hZ2Uud2lkdGgsIGltYWdlLmhlaWdodF0sXG4gICAgICAgIGZpbGVuYW1lOiBpbWFnZUluZm8uZmlsZU5hbWUgfTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICpcbiAgICogQHBhcmFtIHBsb3REaXZcbiAgICogQHBhcmFtIGltYWdlTG9hZGVkIG9iamVjdCB3aXRoIGRhdGEsIHJhdGlvcyBhbmQgc2l6ZXMga2V5XG4gICAqIEBwYXJhbSBpbWFnZUluZm8gSW1hZ2VJbmZvIG9iamVjdFxuICAgKiBAcGFyYW0gc2NyZWVuSGVpZ2h0IHNpemUgb2YgdGhlIGF2YWlsYWJsZSBzY3JlZW4gaGVpZ2h0XG4gICAqIEBwYXJhbSBwbG90VHlwZSB0eXBlIG9mIHBsb3R0aW5nXG4gICAqL1xuICAvKipcbiAgICogQHBhcmFtIGluUGxhY2Ugd2hlbiB0cnVlLCB1cGRhdGVzIHRoZSBleGlzdGluZyBwbG90IHZpYSBQbG90bHkucmVhY3RcbiAgICogICBpbnN0ZWFkIG9mIFBsb3RseS5wdXJnZSArIFBsb3RseS5uZXdQbG90LiBVc2VkIGJ5IHRoZSBtdWx0aS10aWVyXG4gICAqICAgZGlhZ3JhbSBzd2FwIChzbWFsbCDihpIgbGFyZ2UpIHNvIHRoZSBjYW52YXMgZG9lc24ndCBicmllZmx5IGJsYW5rXG4gICAqICAgYmV0d2VlbiBwaGFzZXMuXG4gICAqL1xuICBwdWJsaWMgcGxvdChwbG90RGl2OiBzdHJpbmcsIGltYWdlTG9hZGVkOiBhbnksIGltYWdlSW5mbzogSUltYWdlSW5mbywgc2NyZWVuSGVpZ2h0OiBudW1iZXIsXG4gICAgICAgICAgICAgIHBsb3RUeXBlOiBQbG90VHlwZSwgaW5QbGFjZTogYm9vbGVhbiA9IGZhbHNlKSB7XG4gICAgY29uc3QgdHJ1ZUltYWdlU2l6ZTogbnVtYmVyW10gPSBbXTtcbiAgICB0aGlzLnpvb21Db29yZGluYXRlcyA9IFtdO1xuICAgIC8vIFt4MCwgeDEsIHkwLCB5MV1cbiAgICB0cnVlSW1hZ2VTaXplWzBdID0gMDtcbiAgICB0cnVlSW1hZ2VTaXplWzFdID0gaW1hZ2VJbmZvLnRydWVJbWFnZVNpemVbMF07XG4gICAgdHJ1ZUltYWdlU2l6ZVsyXSA9IDA7XG4gICAgdHJ1ZUltYWdlU2l6ZVszXSA9IGltYWdlSW5mby50cnVlSW1hZ2VTaXplWzFdO1xuICAgIC8vIFNhdmUgdGhlIGN1cnJlbnQgaW1hZ2UncyByZWdpb25zIGFuZCBwdWxsIGluIGFueSBjYWNoZWQgcmVnaW9ucyBmb3IgdGhlXG4gICAgLy8gaW1hZ2Ugd2UncmUgYWJvdXQgdG8gZGlzcGxheSwgYmVmb3JlIFBsb3RseS5uZXdQbG90IHJlYWRzIGB0aGlzLnNoYXBlc2AuXG4gICAgdGhpcy5zZXRBY3RpdmVJbWFnZShpbWFnZUluZm8pO1xuICAgIHRoaXMuaW1hZ2VJbmZvID0gaW1hZ2VJbmZvO1xuICAgIHRoaXMucGxvdFR5cGUgPSBwbG90VHlwZTtcbiAgICAvLyBDYWNoZSBwaXhlbCBtYXRyaWNlcyBzbyB0aGUgd2FuZCB0b29sIGNhbiBzYW1wbGUgdGhlbS5cbiAgICB0aGlzLmNhY2hlZEltYWdlRnJhbWVzID0gaW1hZ2VMb2FkZWQuZGF0YTtcbiAgICB0aGlzLmNhY2hlZEltYWdlV2lkdGggPSBpbWFnZUxvYWRlZC5zaXplc1swXTtcbiAgICB0aGlzLmNhY2hlZEltYWdlSGVpZ2h0ID0gaW1hZ2VMb2FkZWQuc2l6ZXNbMV07XG4gICAgdGhpcy5jYWNoZWRJbWFnZVJhdGlvcyA9IGltYWdlTG9hZGVkLnJhdGlvcztcbiAgICB0aGlzLmNhY2hlZEZyYW1lT3JpZ2luID0gWzAsIDBdOyAvLyBmdWxsIGltYWdlIOKAlCByZXNldCBhbnkgcHJpb3Igem9vbS1jcm9wIG9yaWdpblxuICAgIHRoaXMuY2FjaGVkSXNHcmF5c2NhbGUgPSAhIWltYWdlSW5mby5pc0dyYXlzY2FsZTtcblxuICAgIC8vIFBsdWdnYWJsZSBwbG90IHR5cGVzIChjb250b3VyLCBzY2F0dGVyLCBzY2F0dGVyM2QsIGlzb3N1cmZhY2UpIHJlbmRlclxuICAgIC8vIHRocm91Z2ggdGhlIHRyYWNlLWJ1aWxkZXIgcmVnaXN0cnkuIFRoZSBvcmlnaW5hbCBIRUFUTUFQL1NVUkZBQ0UvUkdCLWltYWdlXG4gICAgLy8gcmVuZGVyZXJzIGtlZXAgdGhlaXIgZGVkaWNhdGVkIHBhdGhzIGJlbG93IChhbHNvIHJldXNlZCBieSB0aGUgaGlnaC1kZWZcbiAgICAvLyB6b29tIHJlLWZldGNoKS4gSW50ZW5zaXR5IHByb2ZpbGVzIGFyZSBubyBsb25nZXIgYSBwbG90IHR5cGUg4oCUIHRoZXkncmVcbiAgICAvLyBSZWdpb24tYmFzZWQgbGluZSBST0lzLCBhdmFpbGFibGUgaW4gSEVBVE1BUC9JTUFHRSBtb2RlLlxuICAgIGNvbnN0IGltcGwgPSBQTE9UTFlfUExPVF9UWVBFX0lNUExTW3Bsb3RUeXBlXTtcbiAgICBpZiAoaW1wbCkge1xuICAgICAgdGhpcy5pbWFnZUluZm8uaXNHcmF5c2NhbGUgPSAhIWltYWdlSW5mby5pc0dyYXlzY2FsZTtcbiAgICAgIHJldHVybiB0aGlzLnBsb3RWaWFSZWdpc3RyeShcbiAgICAgICAgcGxvdERpdiwgaW1wbCwgdGhpcy5idWlsZFRyYWNlSW5wdXQoaW1hZ2VJbmZvLCBpbWFnZUxvYWRlZCwgdHJ1ZUltYWdlU2l6ZSksXG4gICAgICAgIHNjcmVlbkhlaWdodCwgaW5QbGFjZSk7XG4gICAgfVxuXG4gICAgaWYgKGltYWdlSW5mby5pc0dyYXlzY2FsZSkge1xuICAgICAgaWYgKHBsb3RUeXBlID09PSBQbG90VHlwZS5TVVJGQUNFKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnBsb3RTdXJmYWNlKHBsb3REaXYsIGltYWdlSW5mby51cmxzLCBpbWFnZUxvYWRlZC5kYXRhLCB0cnVlSW1hZ2VTaXplLFxuICAgICAgICAgIGltYWdlTG9hZGVkLnJhdGlvcywgc2NyZWVuSGVpZ2h0LCBpblBsYWNlKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLnBsb3RIZWF0bWFwKHBsb3REaXYsIGltYWdlSW5mby51cmxzLCBpbWFnZUxvYWRlZC5kYXRhLCB0cnVlSW1hZ2VTaXplLFxuICAgICAgICBpbWFnZUxvYWRlZC5yYXRpb3MsIHNjcmVlbkhlaWdodCwgaW5QbGFjZSk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLnBsb3RSR0JIZWF0bWFwKHBsb3REaXYsIGltYWdlSW5mby51cmxzLCBpbWFnZUxvYWRlZC5kYXRhLCB0cnVlSW1hZ2VTaXplLFxuICAgICAgaW1hZ2VMb2FkZWQucmF0aW9zLCBpbWFnZUxvYWRlZC5zaXplc1swXSwgaW1hZ2VMb2FkZWQuc2l6ZXNbMV0sIHNjcmVlbkhlaWdodCwgaW5QbGFjZSk7XG4gIH1cblxuICAvKipcbiAgICogRnVuY3Rpb24gdXNlZCB0byBwbG90IGEgZ3JheXNjYWxlIGhlYXRtYXBcbiAgICogQHBhcmFtIHBsb3REaXZcbiAgICogQHBhcmFtIHVybHNcbiAgICogQHBhcmFtIGltYWdlc1xuICAgKiBAcGFyYW0gdHJ1ZUltZ1NpemUgYXJyYXkgY29udGFpbmluZyB0aGUgZm9sbG93aW5nIDogW3gwLCB4MSwgeTAsIHkxXVxuICAgKiBAcGFyYW0gcmF0aW9zXG4gICAqIEBwYXJhbSBzY3JlZW5IZWlnaHRcbiAgICogQHJldHVybiBQcm9taXNlIHRydWUgd2hlbiBwbG90dGluZyBpcyBmaW5pc2hlZFxuICAgKi9cbiAgcHJpdmF0ZSBwbG90SGVhdG1hcChwbG90RGl2OiBzdHJpbmcsIHVybHM6IHN0cmluZ1tdLCBpbWFnZXM6IGFueVtdLCB0cnVlSW1nU2l6ZTogbnVtYmVyW10sXG4gICAgICAgICAgICAgICAgICAgICByYXRpb3M6IG51bWJlcltdLCBzY3JlZW5IZWlnaHQ6IG51bWJlciwgaW5QbGFjZTogYm9vbGVhbiA9IGZhbHNlKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgaWYgKCFpblBsYWNlKSBQbG90bHkucHVyZ2UocGxvdERpdik7XG4gICAgdGhpcy5wbG90RGl2ID0gcGxvdERpdjtcbiAgICB0aGlzLmltYWdlTGVuZ3RoID0gaW1hZ2VzLmxlbmd0aDtcbiAgICB0aGlzLnVybHMgPSB1cmxzO1xuICAgIHRoaXMuc2NyZWVuSGVpZ2h0ID0gc2NyZWVuSGVpZ2h0O1xuICAgIC8vIHBsb3RcbiAgICBjb25zdCB0cmFjZXM6IHsgejogYW55OyB0eXBlOiBzdHJpbmc7IG5hbWU6IHN0cmluZzsgdmlzaWJsZTogYm9vbGVhbjsgfVtdID0gW107XG4gICAgaW1hZ2VzLmZvckVhY2goKGRhdGFzZXQsIGluZGV4KSA9PiB7XG4gICAgICBjb25zdCB0cmFjZSA9IHtcbiAgICAgICAgeDA6IHRydWVJbWdTaXplWzBdLFxuICAgICAgICBkeDogcmF0aW9zWzBdLFxuICAgICAgICB5MDogdHJ1ZUltZ1NpemVbMl0sXG4gICAgICAgIGR5OiByYXRpb3NbMF0sXG4gICAgICAgIHo6IGRhdGFzZXQsXG4gICAgICAgIHR5cGU6ICdoZWF0bWFwJyxcbiAgICAgICAgLy8gV2UgcmVtb3ZlIHRoZSB0b29sdGlwIGFubm90YXRpb24gZm9yIHBlcmZvcm1hbmNlcyByZWFzb25zXG4gICAgICAgIC8vIHRleHQ6IGRhdGFzZXQubWFwKChyb3c6IGFueVtdLCBpOiBhbnkpID0+IHJvdy5tYXAoKGl0ZW0sIGopID0+IHtcbiAgICAgICAgLy8gICByZXR1cm4gYHg6ICR7KyhqICogcmF0aW9zWzBdKS50b0ZpeGVkKDIpfTxicj55OiAkeysoaSAqIHJhdGlvc1swXSkudG9GaXhlZCgyKX08YnI+dmFsdWU6ICR7aXRlbX1gfSkpLFxuICAgICAgICBob3ZlcmluZm86ICdub25lJyxcbiAgICAgICAgY29sb3JzY2FsZTogdGhpcy5zdG9yZS5jdXJyZW50Q29sb3JtYXAoKS5kYXRhLnZhbHVlLFxuICAgICAgICByZXZlcnNlc2NhbGU6IHRoaXMuc3RvcmUuY3VycmVudFJldmVyc2VTY2FsZSgpLFxuICAgICAgICBuYW1lOiBgU2xpY2UgJHtpbmRleCArIDF9YCxcbiAgICAgICAgdmlzaWJsZTogaW5kZXggPT09IDBcbiAgICAgIH07XG4gICAgICB0cmFjZXMucHVzaCh0cmFjZSk7XG4gICAgfSk7XG4gICAgY29uc3QgcmVuZGVyID0gaW5QbGFjZSA/IFBsb3RseS5yZWFjdCA6IFBsb3RseS5uZXdQbG90O1xuICAgIHJldHVybiAocmVuZGVyIGFzIGFueSkocGxvdERpdiwgdHJhY2VzIGFzIGFueSxcbiAgICAgIHRoaXMuZ2V0SGVhdG1hcExheW91dChbdHJ1ZUltZ1NpemVbMF0sIHRydWVJbWdTaXplWzFdXSwgW3RydWVJbWdTaXplWzNdLFxuICAgICAgICB0cnVlSW1nU2l6ZVsyXV0pLCBDT05GSUcgYXMgYW55KS50aGVuKCgpID0+IHtcbiAgICAgIC8vIGhhbmRsZSB0aGUgcmVsYXlvdXQgZXZlbnQgZm9yIHpvb20gLyByb2lzIGFuZCB0aGUgY2xpY2sgZXZlbnRcbiAgICAgIHRoaXMuc2V0RXZlbnRzKHBsb3REaXYsIHRydWUsIHNjcmVlbkhlaWdodCk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGdW5jdGlvbiB1c2VkIHRvIHBsb3QgYSBncmF5c2NhbGUgc3VyZmFjZVxuICAgKiBAcGFyYW0gcGxvdERpdlxuICAgKiBAcGFyYW0gdXJsc1xuICAgKiBAcGFyYW0gaW1hZ2VzXG4gICAqIEBwYXJhbSB0cnVlSW1nU2l6ZSBhcnJheSBjb250YWluaW5nIHRoZSBmb2xsb3dpbmcgOiBbeDAsIHgxLCB5MCwgeTFdXG4gICAqIEBwYXJhbSByYXRpb3NcbiAgICogQHBhcmFtIHNjcmVlbkhlaWdodFxuICAgKiBAcmV0dXJuIFByb21pc2UgdHJ1ZSB3aGVuIHBsb3R0aW5nIGlzIGZpbmlzaGVkXG4gICAqL1xuICBwcml2YXRlIHBsb3RTdXJmYWNlKHBsb3REaXY6IHN0cmluZywgdXJsczogc3RyaW5nW10sIGltYWdlczogYW55W10sIHRydWVJbWdTaXplOiBudW1iZXJbXSxcbiAgICAgICAgICAgICAgICAgICAgICByYXRpb3M6IG51bWJlcltdLCBzY3JlZW5IZWlnaHQ6IG51bWJlciwgaW5QbGFjZTogYm9vbGVhbiA9IGZhbHNlKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgaWYgKCFpblBsYWNlKSBQbG90bHkucHVyZ2UocGxvdERpdik7XG4gICAgdGhpcy5wbG90RGl2ID0gcGxvdERpdjtcbiAgICB0aGlzLmltYWdlTGVuZ3RoID0gaW1hZ2VzLmxlbmd0aDtcbiAgICB0aGlzLnVybHMgPSB1cmxzO1xuICAgIHRoaXMuc2NyZWVuSGVpZ2h0ID0gc2NyZWVuSGVpZ2h0O1xuICAgIC8vIHBsb3RcbiAgICBjb25zdCB0cmFjZXM6IHsgejogYW55OyB0eXBlOiBzdHJpbmc7ICB9W10gPSBbXTtcbiAgICBpbWFnZXMuZm9yRWFjaCgoZGF0YXNldCwgaW5kZXgpID0+IHtcbiAgICAgIGNvbnN0IHRyYWNlID0ge1xuICAgICAgICB6OiBkYXRhc2V0LFxuICAgICAgICB0eXBlOiAnc3VyZmFjZScsXG4gICAgICAgIC8vIGhvdmVyaW5mbzogJ25vbmUnLFxuICAgICAgICBjb2xvcnNjYWxlOiB0aGlzLnN0b3JlLmN1cnJlbnRDb2xvcm1hcCgpLmRhdGEudmFsdWUsXG4gICAgICAgIHJldmVyc2VzY2FsZTogdGhpcy5zdG9yZS5jdXJyZW50UmV2ZXJzZVNjYWxlKCksXG4gICAgICAgIC8vIG5hbWU6IGBTbGljZSAke2luZGV4ICsgMX1gLFxuICAgICAgICAvLyB2aXNpYmxlOiBpbmRleCA9PT0gMFxuICAgICAgfTtcbiAgICAgIHRyYWNlcy5wdXNoKHRyYWNlKTtcbiAgICB9KTtcbiAgICBjb25zdCB4cmFuZ2UgPSBbdHJ1ZUltZ1NpemVbMF0sIHRydWVJbWdTaXplWzFdXTtcbiAgICBjb25zdCB5cmFuZ2UgPSBbdHJ1ZUltZ1NpemVbMl0sIHRydWVJbWdTaXplWzNdXTtcbiAgICBjb25zdCByZW5kZXIgPSBpblBsYWNlID8gUGxvdGx5LnJlYWN0IDogUGxvdGx5Lm5ld1Bsb3Q7XG4gICAgcmV0dXJuIChyZW5kZXIgYXMgYW55KShwbG90RGl2LCB0cmFjZXMgYXMgYW55LFxuICAgICAgdGhpcy5nZXRTdXJmYWNlTGF5b3V0KHhyYW5nZSwgeXJhbmdlLCAwLjQpLCBDT05GSUdfU1VSRkFDRSBhcyBhbnkpLnRoZW4oKCkgPT4ge1xuICAgICAgLy8gaGFuZGxlIHRoZSByZWxheW91dCBldmVudCBmb3Igem9vbSAvIHJvaXMgYW5kIHRoZSBjbGljayBldmVudFxuICAgICAgdGhpcy5zZXRFdmVudHMocGxvdERpdiwgdHJ1ZSwgc2NyZWVuSGVpZ2h0KTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEZ1bmN0aW9uIHVzZWQgdG8gcGxvdCBhbiBSR0IgaGVhdG1hcFxuICAgKiBAcGFyYW0gcGxvdERpdlxuICAgKiBAcGFyYW0gdXJsc1xuICAgKiBAcGFyYW0gaW1hZ2VzXG4gICAqIEBwYXJhbSB0cnVlSW1nU2l6ZVxuICAgKiBAcGFyYW0gd2lkdGhcbiAgICogQHBhcmFtIGhlaWdodFxuICAgKiBAcGFyYW0gcmF0aW9zXG4gICAqIEBwYXJhbSBzY3JlZW5IZWlnaHRcbiAgICogQHJldHVybiBQcm9taXNlXG4gICAqL1xuICBwcml2YXRlIHBsb3RSR0JIZWF0bWFwKHBsb3REaXY6IHN0cmluZywgdXJsczogc3RyaW5nW10sIGltYWdlczogYW55W10sIHRydWVJbWdTaXplOiBudW1iZXJbXSxcbiAgICAgICAgICAgICAgICAgICAgICAgICByYXRpb3M6IG51bWJlcltdLCB3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlcixcbiAgICAgICAgICAgICAgICAgICAgICAgICBzY3JlZW5IZWlnaHQ6IG51bWJlciwgaW5QbGFjZTogYm9vbGVhbiA9IGZhbHNlKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgLy8gUGxvdGx5LnJlYWN0IHVwZGF0ZXMgaW4gcGxhY2U7IFBsb3RseS5wdXJnZSArIFBsb3RseS5uZXdQbG90IGJsYW5rc1xuICAgIC8vIHRoZSBjYW52YXMgZm9yIH4xMDBtcyB3aGljaCBtYWtlcyB0aGUgbXVsdGktdGllciBzbWFsbOKGkmxhcmdlIHN3YXBcbiAgICAvLyBsb29rIGxpa2UgYSByZWdyZXNzaW9uIHRvIFwibG9hZGluZ1wiIGJlZm9yZSB0aGUgc2hhcnBlciB2ZXJzaW9uXG4gICAgLy8gYXBwZWFycy5cbiAgICBpZiAoIWluUGxhY2UpIFBsb3RseS5wdXJnZShwbG90RGl2KTtcbiAgICB0aGlzLnBsb3REaXYgPSBwbG90RGl2O1xuICAgIHRoaXMuaW1hZ2VMZW5ndGggPSBpbWFnZXMubGVuZ3RoO1xuICAgIHRoaXMudXJscyA9IHVybHM7XG4gICAgdGhpcy5zY3JlZW5IZWlnaHQgPSBzY3JlZW5IZWlnaHQ7XG5cbiAgICAvLyBhcyBhdXRvcmFuZ2UgaXMgc2V0IHRvIGZhbHNlLCBhbmQgbm90IHJldmVyc2VkIHdlIG5lZWQgdG8gc2V0IHRoZSB5cmFuZ2UgY29ycmVjbHkgaGVyZVxuICAgIGNvbnN0IGxheW91dCA9IHRoaXMuZ2V0SGVhdG1hcExheW91dChbdHJ1ZUltZ1NpemVbMF0sIHRydWVJbWdTaXplWzFdXSxcbiAgICAgIFt0cnVlSW1nU2l6ZVszXSwgdHJ1ZUltZ1NpemVbMl1dKTtcbiAgICBjb25zdCB0cmFjZXM6IHsgejogYW55OyB0eXBlOiBzdHJpbmc7IG5hbWU6IHN0cmluZzsgdmlzaWJsZTogYm9vbGVhbjsgfVtdID0gW107XG4gICAgaW1hZ2VzLmZvckVhY2goKGRhdGFzZXQsIGluZGV4KSA9PiB7XG4gICAgICBjb25zdCB0cmFjZSA9IHtcbiAgICAgICAgeDA6IHRydWVJbWdTaXplWzBdLFxuICAgICAgICBkeDogcmF0aW9zWzBdLFxuICAgICAgICB5MDogdHJ1ZUltZ1NpemVbMl0sXG4gICAgICAgIGR5OiByYXRpb3NbMF0sXG4gICAgICAgIHg6IEFycmF5LmZyb20oQXJyYXkod2lkdGgpLmtleXMoKSksXG4gICAgICAgIHk6IEFycmF5LmZyb20oQXJyYXkoaGVpZ2h0KS5rZXlzKCkpLFxuICAgICAgICB6OiBkYXRhc2V0LFxuICAgICAgICAvLyB0ZXh0OiBkYXRhc2V0Lm1hcCgocm93OiBhbnlbXSwgaTogYW55KSA9PiByb3cubWFwKChpdGVtLCBqKSA9PiB7XG4gICAgICAgIC8vICAgcmV0dXJuIGB4OiAkeysoaiAqIHJhdGlvc1swXSkudG9GaXhlZCgyKX08YnI+eTogJHsrKGkgKiByYXRpb3NbMF0pLnRvRml4ZWQoMil9PGJyPnZhbHVlOiAke2l0ZW19YH0pKSxcbiAgICAgICAgaG92ZXJpbmZvOiAnbm9uZScsXG4gICAgICAgIHR5cGU6ICdpbWFnZScsXG4gICAgICAgIG5hbWU6IGBTbGljZSAke2luZGV4ICsgMX1gLFxuICAgICAgICB2aXNpYmxlOiBpbmRleCA9PT0gMFxuICAgICAgfTtcbiAgICAgIHRyYWNlcy5wdXNoKHRyYWNlKTtcbiAgICB9KTtcbiAgICBjb25zdCByZW5kZXIgPSBpblBsYWNlID8gUGxvdGx5LnJlYWN0IDogUGxvdGx5Lm5ld1Bsb3Q7XG4gICAgcmV0dXJuIChyZW5kZXIgYXMgYW55KShwbG90RGl2LCB0cmFjZXMgYXMgYW55LCBsYXlvdXQsIENPTkZJRyBhcyBhbnkpLnRoZW4oKCkgPT4ge1xuICAgICAgLy8gaGFuZGxlIHRoZSByZWxheW91dCBldmVudFxuICAgICAgdGhpcy5zZXRFdmVudHMocGxvdERpdiwgZmFsc2UsIHNjcmVlbkhlaWdodCk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBc3NlbWJsZSB0aGUgbm9ybWFsaXNlZCBpbnB1dCB0aGUgcGx1Z2dhYmxlIHRyYWNlIGJ1aWxkZXJzIGNvbnN1bWUuXG4gICAqIFB1cmUgZGF0YSBvbmx5IOKAlCBubyBQbG90bHkgaGFuZGxlcyDigJQgc28gdGhlIGJ1aWxkZXJzIHN0YXkgYmFja2VuZC1uZXV0cmFsLlxuICAgKi9cbiAgcHJpdmF0ZSBidWlsZFRyYWNlSW5wdXQoaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvLCBpbWFnZUxvYWRlZDogYW55LFxuICAgICAgICAgICAgICAgICAgICAgICAgICB0cnVlSW1hZ2VTaXplOiBudW1iZXJbXSk6IFRyYWNlQnVpbGRJbnB1dCB7XG4gICAgLy8gTWVhc3VyZSB0aGUgdm9sdW1lJ3MgcmVhbCBpbnRlbnNpdHkgcmFuZ2Ugc28gdGhlIGlzbyBiYW5kIGNhbiBiZSBjbGFtcGVkXG4gICAgLy8gaW50byBpdCAodGhlIHNsaWRlciBpcyBhIGZpeGVkIDDigJMyNTUgYnV0IGEgc3RhY2sgbWF5IG9jY3VweSBvbmx5IHBhcnQgb2ZcbiAgICAvLyB0aGF0LCB3aGljaCB3b3VsZCBvdGhlcndpc2UgbGVhdmUgdGhlIHN1cmZhY2VzIHdpdGggbm90aGluZyB0byBjcm9zcykuXG4gICAgaWYgKHRoaXMucGxvdFR5cGUgPT09IFBsb3RUeXBlLklTT1NVUkZBQ0UpIHtcbiAgICAgIHRoaXMuaXNvRGF0YVJhbmdlID0gdGhpcy5tZWFzdXJlSW50ZW5zaXR5UmFuZ2UoaW1hZ2VMb2FkZWQuZGF0YSwgISFpbWFnZUluZm8uaXNHcmF5c2NhbGUpO1xuICAgIH1cbiAgICBjb25zdCBbaXNvTWluLCBpc29NYXhdID0gdGhpcy5tYXBJc29CYW5kKHRoaXMuaXNvTWluLCB0aGlzLmlzb01heCk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGZyYW1lczogaW1hZ2VMb2FkZWQuZGF0YSxcbiAgICAgIHdpZHRoOiBpbWFnZUxvYWRlZC5zaXplc1swXSxcbiAgICAgIGhlaWdodDogaW1hZ2VMb2FkZWQuc2l6ZXNbMV0sXG4gICAgICByYXRpb3M6IGltYWdlTG9hZGVkLnJhdGlvcyxcbiAgICAgIHRydWVJbWFnZVNpemUsXG4gICAgICBpc0dyYXlzY2FsZTogISFpbWFnZUluZm8uaXNHcmF5c2NhbGUsXG4gICAgICBjb2xvcnNjYWxlOiB0aGlzLnN0b3JlLmN1cnJlbnRDb2xvcm1hcCgpLmRhdGEudmFsdWUsXG4gICAgICByZXZlcnNlc2NhbGU6IHRoaXMuc3RvcmUuY3VycmVudFJldmVyc2VTY2FsZSgpLFxuICAgICAgcmVnaW9uczogdGhpcy5nZXRSZWdpb25Qb2x5Z29ucygpLFxuICAgICAgc2hhcGVDb2xvcjogdGhpcy5yZWdpb25TdG9yZS5nZXRTaGFwZUNvbG9yKCksXG4gICAgICBpc29NaW4sXG4gICAgICBpc29NYXgsXG4gICAgfTtcbiAgfVxuXG4gIC8qKiBNaW4vbWF4IGludGVuc2l0eSBvdmVyIGV2ZXJ5IHZveGVsIG9mIHRoZSBsb2FkZWQgZnJhbWVzIChSR0Ig4oaSIGx1bWluYW5jZSksXG4gICAqICB1c2VkIHRvIGtlZXAgdGhlIGlzbyBiYW5kIGluc2lkZSB0aGUgZGF0YS4gUmV0dXJucyBudWxsIGZvciBlbXB0eSBpbnB1dC4gKi9cbiAgcHJpdmF0ZSBtZWFzdXJlSW50ZW5zaXR5UmFuZ2UoZnJhbWVzOiBhbnlbXSwgaXNHcmF5c2NhbGU6IGJvb2xlYW4pOiBbbnVtYmVyLCBudW1iZXJdIHwgbnVsbCB7XG4gICAgbGV0IG1pbiA9IEluZmluaXR5LCBtYXggPSAtSW5maW5pdHk7XG4gICAgZm9yIChjb25zdCBmcmFtZSBvZiBmcmFtZXMgfHwgW10pIHtcbiAgICAgIGZvciAoY29uc3Qgcm93IG9mIGZyYW1lIHx8IFtdKSB7XG4gICAgICAgIGZvciAoY29uc3QgY2VsbCBvZiByb3cgfHwgW10pIHtcbiAgICAgICAgICBjb25zdCB2ID0gaXNHcmF5c2NhbGUgPyBjZWxsXG4gICAgICAgICAgICA6IDAuMjk5ICogY2VsbFswXSArIDAuNTg3ICogY2VsbFsxXSArIDAuMTE0ICogY2VsbFsyXTtcbiAgICAgICAgICBpZiAoIU51bWJlci5pc0Zpbml0ZSh2KSkgY29udGludWU7XG4gICAgICAgICAgaWYgKHYgPCBtaW4pIG1pbiA9IHY7XG4gICAgICAgICAgaWYgKHYgPiBtYXgpIG1heCA9IHY7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIChOdW1iZXIuaXNGaW5pdGUobWluKSAmJiBOdW1iZXIuaXNGaW5pdGUobWF4KSAmJiBtYXggPiBtaW4pID8gW21pbiwgbWF4XSA6IG51bGw7XG4gIH1cblxuICAvKiogTWFwIHRoZSAw4oCTMjU1IHNsaWRlciBiYW5kIG9udG8gdGhlIHZvbHVtZSdzIGFjdHVhbCBpbnRlbnNpdHkgcmFuZ2UuIFRoZVxuICAgKiAgc2xpZGVyIGlzIGZpeGVkIDDigJMyNTUsIGJ1dCBhIHN0YWNrIG9mdGVuIG9jY3VwaWVzIG9ubHkgYSBuYXJyb3cgc3ViLXJhbmdlXG4gICAqICAoZS5nLiB0aGlzIEVET0Ygdm9sdW1lIGlzIH4y4oCTNTAgd2l0aCBzdHJ1Y3R1cmUgbmVhciA24oCTMTApLiBNYXBwaW5nIHNwcmVhZHNcbiAgICogIHRoZSBkYXRhIGFjcm9zcyB0aGUgd2hvbGUgc2xpZGVyIHNvIGV2ZXJ5IGludGVuc2l0eSBpcyBlYXN5IHRvIHJlYWNoIGFuZFxuICAgKiAgdGhlIGJhbmQgYWx3YXlzIGxhbmRzIGluc2lkZSB0aGUgZGF0YSDigJQgYSBzbWFsbCBpbnNldCBrZWVwcyB0aGUgZXh0cmVtZVxuICAgKiAgc3VyZmFjZXMgb2ZmIHRoZSBleGFjdCBkYXRhIGVkZ2VzLCB3aGljaCBhcmUgZGVnZW5lcmF0ZSBhbmQgZHJhdyBub3RoaW5nLiAqL1xuICBwcml2YXRlIG1hcElzb0JhbmQoaXNvTWluOiBudW1iZXIsIGlzb01heDogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gICAgbGV0IGxvID0gTWF0aC5taW4oaXNvTWluLCBpc29NYXgpO1xuICAgIGxldCBoaSA9IE1hdGgubWF4KGlzb01pbiwgaXNvTWF4KTtcbiAgICBpZiAodGhpcy5pc29EYXRhUmFuZ2UpIHtcbiAgICAgIGNvbnN0IFt2TWluLCB2TWF4XSA9IHRoaXMuaXNvRGF0YVJhbmdlO1xuICAgICAgY29uc3Qgc3BhbiA9IHZNYXggLSB2TWluO1xuICAgICAgY29uc3QgcGFkID0gMC4wMyAqIHNwYW47XG4gICAgICBjb25zdCB1c2FibGUgPSBzcGFuIC0gMiAqIHBhZDtcbiAgICAgIGNvbnN0IFNMSURFUl9NQVggPSAyNTU7IC8vIHRvb2xiYXIgc2xpZGVyIGRvbWFpbiAoaXNvVmFsdWVNYXgpXG4gICAgICBsbyA9IHZNaW4gKyBwYWQgKyAobG8gLyBTTElERVJfTUFYKSAqIHVzYWJsZTtcbiAgICAgIGhpID0gdk1pbiArIHBhZCArIChoaSAvIFNMSURFUl9NQVgpICogdXNhYmxlO1xuICAgICAgaWYgKGxvID4gaGkpIHsgY29uc3QgdCA9IGxvOyBsbyA9IGhpOyBoaSA9IHQ7IH1cbiAgICB9XG4gICAgcmV0dXJuIFtsbywgaGldO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZSB0aGUgaXNvc3VyZmFjZSBpbnRlbnNpdHkgYm91bmRzLiBXaGVuIGlzb3N1cmZhY2UgaXMgb24gc2NyZWVuIHRoaXNcbiAgICogcmVzdHlsZXMgYGlzb21pbmAvYGlzb21heGAgaW4gcGxhY2UgZm9yIGEgbGl2ZSB1cGRhdGUgKG5vIHZvbHVtZSByZWJ1aWxkKS5cbiAgICogU2xpZGVyIHZhbHVlcyBhcmUgbWFwcGVkIG9udG8gdGhlIHZvbHVtZSdzIHJlYWwgcmFuZ2Ugc28gdGhlIHN1cmZhY2VzIGFsd2F5c1xuICAgKiBoYXZlIGRhdGEgdG8gY3Jvc3MgKG1hdGNoaW5nIHRoZSBpbml0aWFsIHJlbmRlcikuXG4gICAqL1xuICBwdWJsaWMgc2V0SXNvUmFuZ2UoaXNvTWluOiBudW1iZXIsIGlzb01heDogbnVtYmVyKTogdm9pZCB7XG4gICAgdGhpcy5pc29NaW4gPSBpc29NaW47XG4gICAgdGhpcy5pc29NYXggPSBpc29NYXg7XG4gICAgaWYgKHRoaXMucGxvdFR5cGUgPT09IFBsb3RUeXBlLklTT1NVUkZBQ0UgJiYgdGhpcy5wbG90RGl2KSB7XG4gICAgICBjb25zdCBbbG8sIGhpXSA9IHRoaXMubWFwSXNvQmFuZChpc29NaW4sIGlzb01heCk7XG4gICAgICB0cnkge1xuICAgICAgICBQbG90bHkucmVzdHlsZSh0aGlzLnBsb3REaXYsIHsgaXNvbWluOiBbbG9dLCBpc29tYXg6IFtoaV0gfSBhcyBhbnkpO1xuICAgICAgfSBjYXRjaCB7IC8qIHBsb3Qgbm90IHJlYWR5ICovIH1cbiAgICB9XG4gIH1cblxuICAvKiogUGxvdGx5IHJlbmRlcnMgaXNvc3VyZmFjZXMsIHNvIGl0IGV4cG9zZXMgdGhlIGlzb3N1cmZhY2UgY29udHJvbHMgKGl0c2VsZikuICovXG4gIHB1YmxpYyBnZXRJc29zdXJmYWNlQ29udHJvbHMoKTogSUlzb3N1cmZhY2VDb250cm9scyB8IG51bGwgeyByZXR1cm4gdGhpczsgfVxuXG4gIC8qKlxuICAgKiBSZW5kZXIgYSByZWdpc3RyeS1iYWNrZWQgcGxvdCB0eXBlOiBidWlsZCBpdHMgdHJhY2VzIGZyb20gdGhlIGlucHV0LCBwaWNrXG4gICAqIHRoZSBtYXRjaGluZyBsYXlvdXQsIHJlbmRlciwgYW5kIHdpcmUgdGhlIHVzdWFsIHJlbGF5b3V0L2NsaWNrIGV2ZW50cy5cbiAgICogTWlycm9ycyB0aGUgc3RydWN0dXJlIG9mIHRoZSBkZWRpY2F0ZWQgaGVhdG1hcC9zdXJmYWNlIHJlbmRlcmVycy5cbiAgICovXG4gIHByaXZhdGUgcGxvdFZpYVJlZ2lzdHJ5KHBsb3REaXY6IHN0cmluZywgaW1wbDogUGxvdGx5UGxvdFR5cGVJbXBsLCBpbnB1dDogVHJhY2VCdWlsZElucHV0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JlZW5IZWlnaHQ6IG51bWJlciwgaW5QbGFjZTogYm9vbGVhbiA9IGZhbHNlKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgaWYgKCFpblBsYWNlKSBQbG90bHkucHVyZ2UocGxvdERpdik7XG4gICAgdGhpcy5wbG90RGl2ID0gcGxvdERpdjtcbiAgICB0aGlzLmltYWdlTGVuZ3RoID0gaW5wdXQuZnJhbWVzLmxlbmd0aDtcbiAgICB0aGlzLnVybHMgPSB0aGlzLmltYWdlSW5mbz8udXJscyA/PyB0aGlzLnVybHM7XG4gICAgdGhpcy5zY3JlZW5IZWlnaHQgPSBzY3JlZW5IZWlnaHQ7XG5cbiAgICBjb25zdCB0cmFjZXMgPSBpbXBsLmJ1aWxkVHJhY2VzKGlucHV0KTtcbiAgICBjb25zdCBbeDAsIHgxLCB5MCwgeTFdID0gaW5wdXQudHJ1ZUltYWdlU2l6ZTtcbiAgICBsZXQgbGF5b3V0OiBhbnk7XG4gICAgc3dpdGNoIChpbXBsLmxheW91dEtpbmQpIHtcbiAgICAgIGNhc2UgJzNkLXZvbHVtZSc6XG4gICAgICAgIGxheW91dCA9IHRoaXMuZ2V0Vm9sdW1lTGF5b3V0KCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnMmQtY2hhcnQnOlxuICAgICAgICBsYXlvdXQgPSB0aGlzLmdldENoYXJ0TGF5b3V0KCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnMmQtb3ZlcmxheSc6XG4gICAgICAgIGxheW91dCA9IHRoaXMuZ2V0T3ZlcmxheUxheW91dChbeDAsIHgxXSwgW3kxLCB5MF0pOyAvLyByZXZlcnNlZCB5IGxpa2UgdGhlIGltYWdlXG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDogLy8gJzJkLWltYWdlJ1xuICAgICAgICBsYXlvdXQgPSB0aGlzLmdldEhlYXRtYXBMYXlvdXQoW3gwLCB4MV0sIFt5MSwgeTBdKTtcbiAgICB9XG4gICAgY29uc3QgY29uZmlnID0gaW1wbC50aHJlZUQgPyBDT05GSUdfU1VSRkFDRSA6IENPTkZJRztcbiAgICBjb25zdCByZW5kZXIgPSBpblBsYWNlID8gUGxvdGx5LnJlYWN0IDogUGxvdGx5Lm5ld1Bsb3Q7XG4gICAgcmV0dXJuIChyZW5kZXIgYXMgYW55KShwbG90RGl2LCB0cmFjZXMgYXMgYW55LCBsYXlvdXQsIGNvbmZpZyBhcyBhbnkpLnRoZW4oKCkgPT4ge1xuICAgICAgdGhpcy5zZXRFdmVudHMocGxvdERpdiwgaW5wdXQuaXNHcmF5c2NhbGUsIHNjcmVlbkhlaWdodCk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBQbGFpbiAyRCBjaGFydCBheGVzIChpbnRlbnNpdHkgcHJvZmlsZSAvIGxpbmUgcGxvdHMpLiAqL1xuICBwcml2YXRlIGdldENoYXJ0TGF5b3V0KCk6IGFueSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIG1hcmdpbjogeyB0OiAzMCwgYjogNDUsIGw6IDYwLCByOiAyMCB9LFxuICAgICAgaGVpZ2h0OiB0aGlzLnNjcmVlbkhlaWdodCxcbiAgICAgIGF1dG9zaXplOiB0cnVlLFxuICAgICAgeGF4aXM6IHsgdGl0bGU6ICdQb3NpdGlvbiAocHgpJyB9LFxuICAgICAgeWF4aXM6IHsgdGl0bGU6ICdJbnRlbnNpdHknIH0sXG4gICAgICBkcmFnbW9kZTogZmFsc2UsXG4gICAgfTtcbiAgfVxuXG4gIC8qKiBJbWFnZS1hbGlnbmVkIDJEIGF4ZXMgd2l0aG91dCB0aGUgei1wbGFuZSBzbGlkZXIgKHJlZ2lvbiBzY2F0dGVyKS4gKi9cbiAgcHJpdmF0ZSBnZXRPdmVybGF5TGF5b3V0KHhSYW5nZTogbnVtYmVyW10sIHlSYW5nZTogbnVtYmVyW10pOiBhbnkge1xuICAgIHJldHVybiB7XG4gICAgICB4YXhpczogeyBjb25zdHJhaW46ICdyYW5nZScsIGNvbnN0cmFpbnRvd2FyZDogJ2NlbnRlcicsIHNpZGU6ICd0b3AnLCB0aWNrczogJycsIHJhbmdlOiB4UmFuZ2UgfSxcbiAgICAgIHlheGlzOiB7XG4gICAgICAgIGNvbnN0cmFpbjogJ3JhbmdlJywgY29uc3RyYWludG93YXJkOiAnY2VudGVyJywgcmFuZ2U6IHlSYW5nZSwgdGlja3M6ICcnLFxuICAgICAgICB0aWNrc3VmZml4OiAnICAnLCBhdXRvcmFuZ2U6IGZhbHNlLCBzY2FsZWFuY2hvcjogdGhpcy5zY2FsZXJhdGlvID8gJ3gnIDogZmFsc2UsXG4gICAgICB9LFxuICAgICAgbWFyZ2luOiB7IHQ6IDMwLCBiOiA1LCBsOiA1NSwgcjogNSB9LFxuICAgICAgaGVpZ2h0OiB0aGlzLnNjcmVlbkhlaWdodCxcbiAgICAgIGF1dG9zaXplOiB0cnVlLFxuICAgICAgc2hhcGVzOiB0aGlzLmN1cnJlbnRSZW5kZXJTaGFwZXModGhpcy5yZWdpb25TdG9yZS5nZXRTaG93U2hhcGVMYWJlbCgpKSxcbiAgICAgIGRyYWdtb2RlOiB0aGlzLmRyYWdNb2RlID8gdGhpcy5kcmFnTW9kZSA6IGZhbHNlLFxuICAgIH07XG4gIH1cblxuICAvKiogM0Qgc2NlbmUgZm9yIHZvbHVtZXRyaWMgcGxvdCB0eXBlcyAoc2NhdHRlcjNkLCBpc29zdXJmYWNlKS4gKi9cbiAgcHJpdmF0ZSBnZXRWb2x1bWVMYXlvdXQoKTogYW55IHtcbiAgICByZXR1cm4ge1xuICAgICAgbWFyZ2luOiB7IHQ6IDAsIGI6IDAsIGw6IDAsIHI6IDAgfSxcbiAgICAgIGhlaWdodDogdGhpcy5zY3JlZW5IZWlnaHQsXG4gICAgICBhdXRvc2l6ZTogdHJ1ZSxcbiAgICAgIHNjZW5lOiB7XG4gICAgICAgIHhheGlzOiB7IHRpdGxlOiAnWCcgfSxcbiAgICAgICAgeWF4aXM6IHsgdGl0bGU6ICdZJyB9LFxuICAgICAgICB6YXhpczogeyB0aXRsZTogJ1otcGxhbmUnIH0sXG4gICAgICAgIC8vICdjdWJlJyAobm90ICdkYXRhJyk6IGEgei1zdGFjayBoYXMgZmFyIGZld2VyIHBsYW5lcyB0aGFuIFgvWSBwaXhlbHMsXG4gICAgICAgIC8vIHNvICdkYXRhJyBzcXVhc2hlcyB0aGUgdm9sdW1lIGludG8gYSBuZWFyLWZsYXQgc2xhYiB0aGF0IHJlYWRzIGFzXG4gICAgICAgIC8vIGVtcHR5IGVkZ2Utb24uIEEgY3ViZSBnaXZlcyB0aGUgeiBkaW1lbnNpb24gcmVhbCBoZWlnaHQgc28gdGhlXG4gICAgICAgIC8vIGlzb3N1cmZhY2Uvdm94ZWxzIGFyZSBhY3R1YWxseSB2aXNpYmxlLlxuICAgICAgICBhc3BlY3Rtb2RlOiAnY3ViZScsXG4gICAgICB9LFxuICAgIH07XG4gIH1cblxuICAvKiogUGxvdCB0eXBlcyB0aGlzIGJhY2tlbmQgYWR2ZXJ0aXNlcyAoZHJpdmVzIHRoZSBVSSBzZWxlY3RvcikuICovXG4gIHB1YmxpYyBnZXRQbG90VHlwZURlc2NyaXB0b3JzKCk6IFBsb3RUeXBlRGVzY3JpcHRvcltdIHtcbiAgICByZXR1cm4gT2JqZWN0LnZhbHVlcyhQTE9UX1RZUEVfREVTQ1JJUFRPUlMpLmZpbHRlcigoZCk6IGQgaXMgUGxvdFR5cGVEZXNjcmlwdG9yID0+ICEhZCk7XG4gIH1cblxuICAvKiogVGhpcyBiYWNrZW5kJ3MgcmVnaW9uIHJlbmRlcmVyLiBQbG90bHkgZHJhd3Mgc2hhcGVzIG5hdGl2ZWx5LCBzbyB0aGVcbiAgICogIG92ZXJsYXkgaXMgYSB0aGluIGFkYXB0ZXIgb3ZlciB0aGlzIHNlcnZpY2UncyBkcmFnIG1vZGVzLiAqL1xuICBwdWJsaWMgZ2V0UmVnaW9uT3ZlcmxheSgpOiBJUmVnaW9uT3ZlcmxheSB7XG4gICAgaWYgKCF0aGlzLnJlZ2lvbk92ZXJsYXkpIHRoaXMucmVnaW9uT3ZlcmxheSA9IG5ldyBQbG90bHlSZWdpb25PdmVybGF5KHRoaXMpO1xuICAgIHJldHVybiB0aGlzLnJlZ2lvbk92ZXJsYXk7XG4gIH1cblxuICAvLyDilIDilIAgSW50ZW5zaXR5IHByb2ZpbGUgKFJlZ2lvbi1iYXNlZCBsaW5lIFJPSXMpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBFbWl0cyB0aGUgZnVsbCBzZXQgb2YgaW50ZW5zaXR5IHByb2ZpbGVzIChvbmUgcGVyIHByb2ZpbGUtbGluZSByZWdpb24pXG4gICAqICB3aGVuZXZlciBhIHByb2ZpbGUgbGluZSBpcyBhZGRlZCwgbW92ZWQsIG9yIHJlbW92ZWQuICovXG4gIHB1YmxpYyBnZXRJbnRlbnNpdHlQcm9maWxlJCgpOiBPYnNlcnZhYmxlPEludGVuc2l0eVByb2ZpbGVbXT4ge1xuICAgIHJldHVybiB0aGlzLmludGVuc2l0eVByb2ZpbGUkLmFzT2JzZXJ2YWJsZSgpO1xuICB9XG5cbiAgLyoqIFBsb3RseSByZW5kZXJzIHRoZSBsaW5lIFJPSXMgKyBpbnNldCwgc28gaXQgZXhwb3NlcyB0aGUgaW50ZW5zaXR5IGNvbnRyb2xzLiAqL1xuICBwdWJsaWMgZ2V0SW50ZW5zaXR5Q29udHJvbHMoKTogSUludGVuc2l0eUNvbnRyb2xzIHwgbnVsbCB7IHJldHVybiB0aGlzOyB9XG5cbiAgLyoqXG4gICAqIFJlbmRlciB0aGUgZmxvYXRpbmcgaW50ZW5zaXR5LXByb2ZpbGUgaW5zZXQgY2hhcnQgaW50byBgZGl2SWRgIOKAlCBvbmUgbGluZVxuICAgKiB0cmFjZSBwZXIgUk9JLCBlYWNoIGRyYXduIGluIGl0cyBST0kncyBjb2xvdXIuIEtlcHQgaGVyZSAobm90IGluIHRoZSBkaWFncmFtXG4gICAqIGNvbXBvbmVudCkgc28gdGhlIGNvbnN1bWVyIG5ldmVyIHRvdWNoZXMgUGxvdGx5IGRpcmVjdGx5LlxuICAgKi9cbiAgcHVibGljIHJlbmRlckludGVuc2l0eUluc2V0KGRpdklkOiBzdHJpbmcsIHByb2ZpbGVzOiBJbnRlbnNpdHlQcm9maWxlW10pOiB2b2lkIHtcbiAgICBjb25zdCBlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGRpdklkKTtcbiAgICBpZiAoIWVsKSByZXR1cm47XG4gICAgY29uc3QgdHJhY2VzID0gKHByb2ZpbGVzID8/IFtdKS5tYXAoKHAsIGkpID0+ICh7XG4gICAgICB4OiBwLnBvc2l0aW9ucyxcbiAgICAgIHk6IHAudmFsdWVzLFxuICAgICAgdHlwZTogJ3NjYXR0ZXInLFxuICAgICAgbW9kZTogJ2xpbmVzJyxcbiAgICAgIGxpbmU6IHsgY29sb3I6IHAuY29sb3IgPz8gJyNGRkQ0MDAnLCB3aWR0aDogMiB9LFxuICAgICAgbmFtZTogYExpbmUgJHtpICsgMX1gLFxuICAgICAgaG92ZXJpbmZvOiAneCt5JyxcbiAgICB9KSk7XG4gICAgLy8gWCBheGlzIGlzIGluIG1pY3JvbnMgd2hlbiB0aGUgaW1hZ2UgY2FycmllcyBhIHBoeXNpY2FsIHBpeGVsIHNpemUgKG1wcCksXG4gICAgLy8gb3RoZXJ3aXNlIGluIHBpeGVscyDigJQgZHJpdmVuIGJ5IHRoZSB1bml0IHRoZSBzYW1wbGVyIHRhZ2dlZCBvbiB0aGUgcHJvZmlsZS5cbiAgICBjb25zdCB1bml0ID0gKHByb2ZpbGVzID8/IFtdKS5maW5kKHAgPT4gcC51bml0KT8udW5pdCA/PyAncHgnO1xuICAgIGNvbnN0IHhUaXRsZSA9IHVuaXQgPT09ICfCtW0nID8gJ1Bvc2l0aW9uICjCtW0pJyA6ICdQb3NpdGlvbiAocHgpJztcbiAgICBQbG90bHkucmVhY3QoZWwsIHRyYWNlcyBhcyBhbnksIHtcbiAgICAgIG1hcmdpbjogeyB0OiA2LCByOiA4LCBiOiAzOCwgbDogNDAgfSxcbiAgICAgIHhheGlzOiB7IHRpdGxlOiB4VGl0bGUsIHplcm9saW5lOiBmYWxzZSwgY29sb3I6ICcjZGRkJyB9LFxuICAgICAgeWF4aXM6IHsgdGl0bGU6ICdJbnRlbnNpdHknLCB6ZXJvbGluZTogZmFsc2UsIGNvbG9yOiAnI2RkZCcgfSxcbiAgICAgIHNob3dsZWdlbmQ6IGZhbHNlLFxuICAgICAgcGFwZXJfYmdjb2xvcjogJ3JnYmEoMjUsMjUsMjUsMC45KScsXG4gICAgICBwbG90X2JnY29sb3I6ICdyZ2JhKDI1LDI1LDI1LDAuOSknLFxuICAgICAgZm9udDogeyBjb2xvcjogJyNlZWUnLCBzaXplOiAxMCB9LFxuICAgIH0gYXMgYW55LCB7IGRpc3BsYXlNb2RlQmFyOiBmYWxzZSwgcmVzcG9uc2l2ZTogdHJ1ZSB9IGFzIGFueSk7XG4gIH1cblxuICAvKipcbiAgICogUmVnaW9uIHNoYXBlcyAoZmlsdGVyZWQvcmVkcmF3biBmb3IgdGhlIGN1cnJlbnQgZmlsZSkuIFRoZSBwcm9maWxlLWxpbmUgUk9Jc1xuICAgKiBhcmUgb3JkaW5hcnkgc3RvcmUgcmVnaW9ucyAodGFnZ2VkIGBraW5kOiAncHJvZmlsZSdgKSwgc28gdGhleSBhcmUgYWxyZWFkeVxuICAgKiBwYXJ0IG9mIGB0aGlzLnNoYXBlc2Ag4oCUIG5vIHNlcGFyYXRlIGFwcGVuZCBpcyBuZWVkZWQuXG4gICAqL1xuICBwcml2YXRlIGN1cnJlbnRSZW5kZXJTaGFwZXMoc2hvd0xhYmVsID0gdGhpcy5yZWdpb25TdG9yZS5nZXRTaG93U2hhcGVMYWJlbCgpKTogYW55W10ge1xuICAgIHJldHVybiB0aGlzLnNoYXBlc1RvUmVkcmF3KHNob3dMYWJlbCk7XG4gIH1cblxuICAvKipcbiAgICogSUludGVuc2l0eUNvbnRyb2xzOiBhZGQgYW5vdGhlciBpbnRlbnNpdHktcHJvZmlsZSBsaW5lIGFzIGEgbmV1dHJhbCBSZWdpb25cbiAgICogKHRhZ2dlZCBga2luZDogJ3Byb2ZpbGUnYCkuIEl0IGlzIGEgaG9yaXpvbnRhbCBvcGVuIDItcG9pbnQgcG9seWxpbmUgc3Bhbm5pbmdcbiAgICogdGhlIGltYWdlIHdpZHRoLCBzdGFnZ2VyZWQgdmVydGljYWxseSBzbyBzdWNjZXNzaXZlIGxpbmVzIHN0YXkgZGlzdGluY3QsIGluXG4gICAqIHRoZSBuZXh0IGJyaWdodCBwYWxldHRlIGNvbG91ci4gQWRkZWQgdG8gdGhlIHNoYXJlZCBzdG9yZSBzbyBib3RoIGJhY2tlbmRzXG4gICAqIChQbG90bHkgKyBPcGVuU2VhZHJhZ29uKSByZW5kZXIgYW5kIGRyYWcgaXQ7IGV4Y2x1ZGVkIGZyb20gdGhlIFJlZ2lvbnMgdGFiXG4gICAqIGFuZCBleHBvcnRzIGJ5IGl0cyBraW5kLlxuICAgKi9cbiAgcHVibGljIGFkZFByb2ZpbGVMaW5lKCk6IFJlZ2lvbiB8IG51bGwge1xuICAgIGlmICghdGhpcy50cnVlSW1nU2l6ZSkgcmV0dXJuIG51bGw7XG4gICAgY29uc3QgY291bnQgPSB0aGlzLmdldFByb2ZpbGVSZWdpb25zKCkubGVuZ3RoO1xuICAgIGNvbnN0IGltZ1gwID0gdGhpcy50cnVlSW1nU2l6ZVswXSwgaW1nWDEgPSB0aGlzLnRydWVJbWdTaXplWzFdO1xuICAgIGNvbnN0IGltZ1RvcCA9IHRoaXMudHJ1ZUltZ1NpemVbMl0sIGltZ0JvdHRvbSA9IHRoaXMudHJ1ZUltZ1NpemVbM107XG5cbiAgICAvLyBQbGFjZSB0aGUgbGluZSB3aXRoaW4gdGhlIGN1cnJlbnRseSBWSVNJQkxFIGltYWdlIGFyZWEsIHNvIHdoZW4gem9vbWVkIGluIGl0XG4gICAgLy8gbGFuZHMgb24tc2NyZWVuIGluc3RlYWQgb2Ygc3Bhbm5pbmcgdGhlIHdob2xlIChtb3N0bHkgb2ZmLXNjcmVlbikgaW1hZ2UsIGF0XG4gICAgLy8gMi8zIG9mIHRoZSB2aXNpYmxlIHdpZHRoIGFuZCBob3Jpem9udGFsbHkgY2VudHJlZC4gRmFsbHMgYmFjayB0byB0aGUgZnVsbFxuICAgIC8vIGltYWdlIHdoZW4gbm8gdmlld3BvcnQgUk9JIGlzIGtub3duIChQbG90bHkgaGVhdG1hcCkgb3IgYSBzdGFsZSBST0kgbm8gbG9uZ2VyXG4gICAgLy8gb3ZlcmxhcHMgdGhlIGltYWdlLlxuICAgIGNvbnN0IHJvaSA9IHRoaXMubGFzdFZpc2libGVSb2k7XG4gICAgY29uc3Qgb3ZlcmxhcHMgPSAhIXJvaSAmJiByb2kud2lkdGggPiAwICYmIHJvaS5oZWlnaHQgPiAwXG4gICAgICAmJiByb2kueCA8IGltZ1gxICYmIHJvaS54ICsgcm9pLndpZHRoID4gaW1nWDBcbiAgICAgICYmIHJvaS55IDwgaW1nQm90dG9tICYmIHJvaS55ICsgcm9pLmhlaWdodCA+IGltZ1RvcDtcbiAgICBjb25zdCB4MHYgPSBvdmVybGFwcyA/IE1hdGgubWF4KGltZ1gwLCByb2khLngpIDogaW1nWDA7XG4gICAgY29uc3QgeDF2ID0gb3ZlcmxhcHMgPyBNYXRoLm1pbihpbWdYMSwgcm9pIS54ICsgcm9pIS53aWR0aCkgOiBpbWdYMTtcbiAgICBjb25zdCB0b3BWID0gb3ZlcmxhcHMgPyBNYXRoLm1heChpbWdUb3AsIHJvaSEueSkgOiBpbWdUb3A7XG4gICAgY29uc3QgYm90dG9tViA9IG92ZXJsYXBzID8gTWF0aC5taW4oaW1nQm90dG9tLCByb2khLnkgKyByb2khLmhlaWdodCkgOiBpbWdCb3R0b207XG5cbiAgICBjb25zdCBjeCA9ICh4MHYgKyB4MXYpIC8gMjtcbiAgICBjb25zdCBoYWxmID0gKCh4MXYgLSB4MHYpICogKDIgLyAzKSkgLyAyOyAvLyBsaW5lIHNwYW5zIDIvMyBvZiB0aGUgdmlzaWJsZSB3aWR0aFxuICAgIGNvbnN0IHgwID0gY3ggLSBoYWxmLCB4MSA9IGN4ICsgaGFsZjtcblxuICAgIGNvbnN0IGJhbmRIID0gYm90dG9tViAtIHRvcFY7XG4gICAgY29uc3QgbWlkWSA9ICh0b3BWICsgYm90dG9tVikgLyAyO1xuICAgIC8vIFN0YWdnZXIgc3VjY2Vzc2l2ZSBsaW5lcyBhY3Jvc3MgdGhlIHZpc2libGUgYmFuZCBzbyB0aGV5IHN0YXkgZGlzdGluY3QuXG4gICAgY29uc3QgeSA9IE1hdGgubWluKGJvdHRvbVYsIE1hdGgubWF4KHRvcFYsIG1pZFkgKyBjb3VudCAqIGJhbmRIICogMC4xMikpO1xuXG4gICAgY29uc3QgcG9seSA9IG5ldyBQb2x5Z29uKCk7XG4gICAgcG9seS5ucG9pbnRzID0gMjtcbiAgICBwb2x5Lnhwb2ludHMgPSBbeDAsIHgxXTtcbiAgICBwb2x5Lnlwb2ludHMgPSBbeSwgeV07XG4gICAgcG9seS5jb29yZGluYXRlcyA9IFtbeDAsIHldLCBbeDEsIHldXTtcbiAgICBwb2x5LmNsb3NlZCA9IGZhbHNlO1xuXG4gICAgY29uc3QgcmVnaW9uID0gbmV3IFJlZ2lvbigpO1xuICAgIHJlZ2lvbi5ib3VuZHMgPSBwb2x5O1xuICAgIHJlZ2lvbi5raW5kID0gJ3Byb2ZpbGUnO1xuICAgIHJlZ2lvbi5jb2xvciA9IHRoaXMuUFJPRklMRV9QQUxFVFRFW3RoaXMucHJvZmlsZUNvbG9yU2VxKysgJSB0aGlzLlBST0ZJTEVfUEFMRVRURS5sZW5ndGhdO1xuICAgIC8vIExlYXZlIHJlZ2lvbi5sYWJlbCB1bmRlZmluZWQgc28gYXBwbHlDbGFzc2lmaWNhdGlvbkNvbG9ycyB3b24ndCByZWNvbG9yIGl0LlxuXG4gICAgdGhpcy5yZWdpb25TdG9yZS5hZGRSZWdpb24ocmVnaW9uKTtcblxuICAgIC8vIElmIGEgUGxvdGx5IHBsb3QgaXMgb24gc2NyZWVuLCByZS1yZW5kZXIgc2hhcGVzIHNvIHRoZSBuZXcgbGluZSBhcHBlYXJzLlxuICAgIC8vIChUaGUgT1NEIG92ZXJsYXkgcmVuZGVycyBhdXRvbWF0aWNhbGx5IHZpYSBpdHMgcmVnaW9uLXVwZGF0ZSBzdWJzY3JpcHRpb24uKVxuICAgIGlmICh0aGlzLnBsb3REaXYpIHtcbiAgICAgIHRoaXMuc3luY1NoYXBlc0Zyb21TdG9yZSgpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgUGxvdGx5LnJlbGF5b3V0KHRoaXMucGxvdERpdiwgeyBzaGFwZXM6IHRoaXMuY3VycmVudFJlbmRlclNoYXBlcygpIH0gYXMgYW55KTtcbiAgICAgIH0gY2F0Y2ggeyAvKiBkaXYgbm90IGEgUGxvdGx5IHBsb3QgKE9TRCBvd25zIGl0KSAqLyB9XG4gICAgfVxuICAgIHJldHVybiByZWdpb247XG4gIH1cblxuICAvKiogVGhlIHByb2ZpbGUtbGluZSBST0lzIGN1cnJlbnRseSBpbiB0aGUgc3RvcmUgKGtpbmQgPT09ICdwcm9maWxlJykuICovXG4gIHByaXZhdGUgZ2V0UHJvZmlsZVJlZ2lvbnMoKTogUmVnaW9uW10ge1xuICAgIHJldHVybiB0aGlzLnJlZ2lvblN0b3JlLmdldFJlZ2lvbnMoKS5maWx0ZXIociA9PiAociBhcyBhbnkpLmtpbmQgPT09ICdwcm9maWxlJyk7XG4gIH1cblxuICAvKiogUmVjb21wdXRlIGFuZCBicm9hZGNhc3QgYSBwcm9maWxlIGZvciBldmVyeSBwcm9maWxlLWxpbmUgcmVnaW9uIChpZCArIGNvbG91clxuICAgKiAgdGFnZ2VkIHNvIHRoZSBpbnNldCB0cmFjZSBtYXRjaGVzIGl0cyBsaW5lKS4gKi9cbiAgcHJpdmF0ZSBlbWl0UHJvZmlsZXMoKTogdm9pZCB7XG4gICAgdGhpcy5pbnRlbnNpdHlQcm9maWxlJC5uZXh0KHRoaXMuZ2V0UHJvZmlsZVJlZ2lvbnMoKS5tYXAociA9PiAoe1xuICAgICAgLi4udGhpcy5jb21wdXRlSW50ZW5zaXR5UHJvZmlsZUZvclJlZ2lvbihyKSxcbiAgICAgIGlkOiByLmlkLFxuICAgICAgY29sb3I6IHIuY29sb3IsXG4gICAgfSkpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTYW1wbGUgdGhlIGludGVuc2l0eSBwcm9maWxlIGZvciBhIHByb2ZpbGUtbGluZSByZWdpb246IGV4dHJhY3QgaXRzIHR3b1xuICAgKiBwb2x5Z29uIGVuZHBvaW50cyBhbmQgcmV1c2UgdGhlIHNoYXJlZCBgY29tcHV0ZUludGVuc2l0eVByb2ZpbGVgIHNhbXBsaW5nLlxuICAgKi9cbiAgcHJpdmF0ZSBjb21wdXRlSW50ZW5zaXR5UHJvZmlsZUZvclJlZ2lvbihyZWdpb246IFJlZ2lvbik6IEludGVuc2l0eVByb2ZpbGUge1xuICAgIC8vIER1Y2stdHlwZWQgKG5vdCBgaW5zdGFuY2VvZiBQb2x5Z29uYCk6IGFmdGVyIGEgZHJhZyByb3VuZC10cmlwcyB0aHJvdWdoIHRoZVxuICAgIC8vIHN0b3JlIHRoZSBib3VuZHMgY2FuIGJlIGEgcGxhaW4gb2JqZWN0LCBzbyBtYXRjaCBvbiB0aGUgcG9pbnQgYXJyYXlzLlxuICAgIGNvbnN0IHBvbHk6IGFueSA9IHJlZ2lvbi5ib3VuZHM7XG4gICAgaWYgKCFwb2x5IHx8ICFBcnJheS5pc0FycmF5KHBvbHkueHBvaW50cykgfHwgIUFycmF5LmlzQXJyYXkocG9seS55cG9pbnRzKSB8fFxuICAgICAgICBwb2x5Lnhwb2ludHMubGVuZ3RoIDwgMiB8fCBwb2x5Lnlwb2ludHMubGVuZ3RoIDwgMikge1xuICAgICAgcmV0dXJuIHsgcG9zaXRpb25zOiBbXSwgdmFsdWVzOiBbXSB9O1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5jb21wdXRlSW50ZW5zaXR5UHJvZmlsZSh7XG4gICAgICB4MDogcG9seS54cG9pbnRzWzBdLCB5MDogcG9seS55cG9pbnRzWzBdLFxuICAgICAgeDE6IHBvbHkueHBvaW50c1sxXSwgeTE6IHBvbHkueXBvaW50c1sxXSxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdXBwbHkgcGl4ZWwgZnJhbWVzIGZvciBpbnRlbnNpdHkgc2FtcGxpbmcgKHVzZWQgYnkgdGhlIE9TRC9pbWFnZSBiYWNrZW5kLFxuICAgKiB3aG9zZSBjYWNoZWQgZnJhbWVzIG1heSBiZSBlbXB0eSkuIE9wdGlvbmFsbHkgYWxzbyBzZXRzIHRoZSBkYXRhL3BpeGVsIHJhdGlvcy5cbiAgICovXG4gIHB1YmxpYyBzZXRTYW1wbGluZ0ZyYW1lcyhmcmFtZXM6IGFueVtdLCByYXRpb3M6IG51bWJlcltdLCBvcmlnaW46IFtudW1iZXIsIG51bWJlcl0gPSBbMCwgMF0pOiB2b2lkIHtcbiAgICB0aGlzLmNhY2hlZEltYWdlRnJhbWVzID0gZnJhbWVzO1xuICAgIGlmIChyYXRpb3MpIHRoaXMuY2FjaGVkSW1hZ2VSYXRpb3MgPSByYXRpb3M7XG4gICAgdGhpcy5jYWNoZWRGcmFtZU9yaWdpbiA9IG9yaWdpbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBQb3B1bGF0ZSB0aGUgc2FtcGxpbmcgY2FjaGUgZm9yIHRoZSBpbnRlbnNpdHkgcHJvZmlsZXMgd2hlbiBPcGVuU2VhZHJhZ29uIGlzXG4gICAqIHRoZSBhY3RpdmUgYmFja2VuZCAodGhlIEltYWdlIHBsb3QgdHlwZSkuIEluIE9TRCBtb2RlIFBsb3RseSBuZXZlciByZW5kZXJlZCxcbiAgICogc28gYGNhY2hlZEltYWdlRnJhbWVzYC9gdHJ1ZUltZ1NpemVgIGFyZSB1bnNldCDigJQgZmV0Y2ggdGhlIGN1cnJlbnQgc2xpY2Unc1xuICAgKiBwcmV2aWV3ICh0aGUgc2FtZSBwaXhlbHMgdGhlIGhlYXRtYXAgd291bGQgc2FtcGxlKSBzbyB0aGUgbGluZSBST0lzIGhhdmVcbiAgICogcGl4ZWwgZGF0YSBhbmQgYSBwbGFjZW1lbnQgZXh0ZW50LiBSZS1lbWl0cyB0aGUgcHJvZmlsZXMgb25jZSBsb2FkZWQuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgZW5zdXJlSW50ZW5zaXR5U2FtcGxpbmcoaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvLCB6SW5kZXg6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICghaW1hZ2VJbmZvPy51cmxzPy5sZW5ndGgpIHJldHVybjtcbiAgICAvLyBTYW1wbGUganVzdCB0aGUgZGlzcGxheWVkIHNsaWNlIChzaG93U3RhY2sgb2ZmIOKGkiBzaW5nbGUgZnJhbWUpOyBgbG9hZGBcbiAgICAvLyBhbHNvIHNldHMgYHRoaXMudHJ1ZUltZ1NpemVgLCB3aGljaCBhZGRQcm9maWxlTGluZSBuZWVkcyBmb3IgcGxhY2VtZW50LlxuICAgIGNvbnN0IHNpbmdsZSA9IHsgLi4uaW1hZ2VJbmZvLCBzaG93U3RhY2s6IGZhbHNlIH0gYXMgSUltYWdlSW5mbztcbiAgICBjb25zdCBsb2FkZWQgPSBhd2FpdCB0aGlzLmxvYWQoc2luZ2xlLCB6SW5kZXggfHwgMCk7XG4gICAgdGhpcy5jYWNoZWRJbWFnZUZyYW1lcyA9IGxvYWRlZC5kYXRhO1xuICAgIHRoaXMuY2FjaGVkSW1hZ2VSYXRpb3MgPSBsb2FkZWQucmF0aW9zO1xuICAgIHRoaXMuY2FjaGVkSW1hZ2VXaWR0aCA9IGxvYWRlZC5zaXplcz8uWzBdID8/IHRoaXMuY2FjaGVkSW1hZ2VXaWR0aDtcbiAgICB0aGlzLmNhY2hlZEltYWdlSGVpZ2h0ID0gbG9hZGVkLnNpemVzPy5bMV0gPz8gdGhpcy5jYWNoZWRJbWFnZUhlaWdodDtcbiAgICB0aGlzLmNhY2hlZEZyYW1lT3JpZ2luID0gWzAsIDBdOyAvLyBmdWxsIHByZXZpZXdcbiAgICB0aGlzLmNhY2hlZElzR3JheXNjYWxlID0gISFpbWFnZUluZm8uaXNHcmF5c2NhbGU7XG4gICAgdGhpcy5lbWl0UHJvZmlsZXMoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiB7QGxpbmsgSUludGVuc2l0eVNhbXBsaW5nfSBzdHViIOKAlCB0aGUgdmlld3BvcnQtY2hhbmdlIHNpZ25hbCBpcyBPcGVuU2VhZHJhZ29uJ3NcbiAgICogKGl0IHJlLXNhbXBsZXMgb24gT1NEIHpvb20vcGFuKS4gUGxvdGx5J3Mgb3duIGhpZ2gtZGVmIHpvb20gdXBkYXRlcyB0aGVcbiAgICogc2FtcGxpbmcgY2FjaGUgaW5saW5lLCBzbyBpdCBuZXZlciBlbWl0cyBoZXJlOyByZXR1cm5zIEVNUFRZIHNvIGEgdW5pZm9ybVxuICAgKiBgSVZpc3VhbGl6ZXJgIGNvbnN1bWVyIGNhbiBzdWJzY3JpYmUgcmVnYXJkbGVzcyBvZiB0aGUgYWN0aXZlIGJhY2tlbmQuXG4gICAqL1xuICBwdWJsaWMgZ2V0Vmlld3BvcnRDaGFuZ2UkKCk6IE9ic2VydmFibGU8eyB4OiBudW1iZXI7IHk6IG51bWJlcjsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfT4ge1xuICAgIHJldHVybiBFTVBUWTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZS1mZXRjaCB0aGUgZ2l2ZW4gaW1hZ2UtcGl4ZWwgUk9JIGF0IHRoZSBkaXNwbGF5IHJlc29sdXRpb24gYW5kIHVzZSBpdCBhc1xuICAgKiB0aGUgaW50ZW5zaXR5LXNhbXBsaW5nIHNvdXJjZSwgc28gdGhlIHByb2ZpbGUgcmVmbGVjdHMgdGhlIGRhdGEgYXQgdGhlXG4gICAqIGN1cnJlbnQgem9vbSBsZXZlbC4gU2hhcmVkIGJ5IGJvdGggYmFja2VuZHM6IHRoZSBPU0Qgdmlld3BvcnQtY2hhbmdlIGhvb2tcbiAgICogY2FsbHMgaXQgb24gem9vbS9wYW47IFBsb3RseSdzIG93biBoaWdoLWRlZiB6b29tICh0cmlnZ2VyWm9vbSkgdXBkYXRlcyB0aGVcbiAgICogc2FtZSBjYWNoZSBpbmxpbmUuIGByb2lgIGlzIGluIGZ1bGwtaW1hZ2UgcGl4ZWwgY29vcmRpbmF0ZXMuXG4gICAqL1xuICBwdWJsaWMgcmVmcmVzaEludGVuc2l0eVNhbXBsaW5nRm9yUm9pKHg6IG51bWJlciwgeTogbnVtYmVyLCB3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlcixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6SW5kZXg6IG51bWJlcik6IHZvaWQge1xuICAgIGlmICh3aWR0aCA8PSAwIHx8IGhlaWdodCA8PSAwKSByZXR1cm47XG4gICAgLy8gUmVtZW1iZXIgdGhlIHZpc2libGUgcmVnaW9uIHNvIGEgbmV3IHByb2ZpbGUgbGluZSBpcyBwbGFjZWQgaW5zaWRlIGl0LlxuICAgIHRoaXMubGFzdFZpc2libGVSb2kgPSB7IHgsIHksIHdpZHRoLCBoZWlnaHQgfTtcbiAgICBjb25zdCByb2kgPSBuZXcgUmVjdGFuZ2xlKCk7XG4gICAgcm9pLnggPSBNYXRoLnJvdW5kKHgpOyByb2kueSA9IE1hdGgucm91bmQoeSk7XG4gICAgcm9pLndpZHRoID0gTWF0aC5yb3VuZCh3aWR0aCk7IHJvaS5oZWlnaHQgPSBNYXRoLnJvdW5kKGhlaWdodCk7XG4gICAgY29uc3Qgc2NyZWVuID0gdGhpcy5wbG90VXRpbGl0aWVzLmdldERvbVJlY3RhbmdsZSgnZGlhZ3JhbScpO1xuICAgIC8vIFNuYXBzaG90IHRoZSBmaWxlbmFtZSBzbyBhIHJlc3BvbnNlIHRoYXQgYXJyaXZlcyBhZnRlciB0aGUgdXNlciBzd2l0Y2hlZFxuICAgIC8vIGZpbGVzIGlzIGRyb3BwZWQgKHRoZSByZXF1ZXN0IGNhcnJpZWQgdGhlIGZpbGUgc2VsZWN0ZWQgYXQgY2FsbCB0aW1lKS5cbiAgICBjb25zdCByZXFOYW1lID0gdGhpcy5maWxlTmFtZTtcbiAgICB0aGlzLnRpbGVzLnpvb21PblJlZ2lvbihyb2ksIHNjcmVlbiwgekluZGV4IHx8IDApLnN1YnNjcmliZSh7XG4gICAgICBuZXh0OiAoem9vbURhdGEpID0+IHtcbiAgICAgICAgSW1hZ2UubG9hZChCdWZmZXIuZnJvbShuZXcgVWludDhBcnJheSh6b29tRGF0YSkpKS50aGVuKChpbWFnZTogYW55KSA9PiB7XG4gICAgICAgICAgaWYgKHRoaXMuZmlsZU5hbWUgIT09IHJlcU5hbWUpIHJldHVybjtcbiAgICAgICAgICBjb25zdCBpc0dyYXkgPSAhIXRoaXMuaW1hZ2VJbmZvPy5pc0dyYXlzY2FsZTtcbiAgICAgICAgICBjb25zdCBmcmFtZSA9IGlzR3JheVxuICAgICAgICAgICAgPyB0aGlzLnBsb3RVdGlsaXRpZXMuYXJyYXlUb01hdHJpeChpbWFnZS5ncmV5KCkuZGF0YSwgaW1hZ2Uud2lkdGgpXG4gICAgICAgICAgICA6IHRoaXMucGxvdFV0aWxpdGllcy5hcnJheVRvTWF0cml4KGltYWdlLmdldFBpeGVsc0FycmF5KCksIGltYWdlLndpZHRoKTtcbiAgICAgICAgICB0aGlzLnNldFNhbXBsaW5nRnJhbWVzKFtmcmFtZV0sIFtyb2kud2lkdGggLyBpbWFnZS53aWR0aCwgcm9pLmhlaWdodCAvIGltYWdlLmhlaWdodF0sXG4gICAgICAgICAgICBbcm9pLngsIHJvaS55XSk7XG4gICAgICAgICAgdGhpcy5lbWl0UHJvZmlsZXMoKTtcbiAgICAgICAgfSk7XG4gICAgICB9LFxuICAgICAgZXJyb3I6ICgpID0+IHsgLyoga2VlcCB0aGUgcHJldmlvdXMgc2FtcGxpbmcgZnJhbWUgb24gYSBmYWlsZWQgY3JvcCBmZXRjaCAqLyB9LFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFNhbXBsZSB0aGUgYWN0aXZlIGZyYW1lJ3MgaW50ZW5zaXR5IChncmF5c2NhbGUgdmFsdWUgb3IgUkdCIGx1bWluYW5jZSlcbiAgICogYWxvbmcgdGhlIGxpbmUgUk9JLiBSZXR1cm5zIGRpc3RhbmNlLWFsb25nLWxpbmUgcG9zaXRpb25zIGFuZCB2YWx1ZXMuXG4gICAqL1xuICBwcml2YXRlIGNvbXB1dGVJbnRlbnNpdHlQcm9maWxlKGxpbmU6IGFueSk6IEludGVuc2l0eVByb2ZpbGUge1xuICAgIGNvbnN0IGVtcHR5OiBJbnRlbnNpdHlQcm9maWxlID0geyBwb3NpdGlvbnM6IFtdLCB2YWx1ZXM6IFtdIH07XG4gICAgaWYgKCF0aGlzLmNhY2hlZEltYWdlRnJhbWVzPy5sZW5ndGggfHwgIWxpbmUpIHJldHVybiBlbXB0eTtcbiAgICBjb25zdCBmcmFtZSA9IHRoaXMuY2FjaGVkSW1hZ2VGcmFtZXNbdGhpcy5hY3RpdmVGcmFtZUluZGV4KCldID8/IHRoaXMuY2FjaGVkSW1hZ2VGcmFtZXNbMF07XG4gICAgaWYgKCFmcmFtZT8ubGVuZ3RoKSByZXR1cm4gZW1wdHk7XG4gICAgY29uc3QgcnggPSB0aGlzLmNhY2hlZEltYWdlUmF0aW9zWzBdIHx8IDE7XG4gICAgY29uc3QgcnkgPSB0aGlzLmNhY2hlZEltYWdlUmF0aW9zWzFdIHx8IHJ4O1xuICAgIGNvbnN0IFtveCwgb3ldID0gdGhpcy5jYWNoZWRGcmFtZU9yaWdpbjtcbiAgICBjb25zdCB4MCA9ICtsaW5lLngwLCB5MCA9ICtsaW5lLnkwLCB4MSA9ICtsaW5lLngxLCB5MSA9ICtsaW5lLnkxO1xuICAgIGNvbnN0IGR4RGF0YSA9IHgxIC0geDAsIGR5RGF0YSA9IHkxIC0geTA7XG4gICAgLy8gU2NhbGUgdGhlIGFsb25nLWxpbmUgZGlzdGFuY2UgYnkgdGhlIHBoeXNpY2FsIHBpeGVsIHNpemUgKG1pY3JvbnMvcGl4ZWwpXG4gICAgLy8gd2hlbiB0aGUgaW1hZ2UgY2FycmllcyBvbmUg4oCUIGFuaXNvdHJvcGljIG1wcFgvbXBwWSBhcmUgYXBwbGllZCBwZXIgYXhpcyBzb1xuICAgIC8vIGRpYWdvbmFsIGxpbmVzIG1lYXN1cmUgdGhlIHRydWUgcGh5c2ljYWwgbGVuZ3RoLiBGYWxscyBiYWNrIHRvIHBpeGVscy5cbiAgICBjb25zdCB7IG1wcFgsIG1wcFkgfSA9IHRoaXMuY3VycmVudE1wcCgpO1xuICAgIGNvbnN0IHVzZU1pY3JvbnMgPSBtcHBYICE9IG51bGw7XG4gICAgY29uc3QgbGVuRGF0YSA9IHVzZU1pY3JvbnNcbiAgICAgID8gTWF0aC5oeXBvdChkeERhdGEgKiBtcHBYLCBkeURhdGEgKiAobXBwWSA/PyBtcHBYKSlcbiAgICAgIDogTWF0aC5oeXBvdChkeERhdGEsIGR5RGF0YSk7XG4gICAgY29uc3QgbGVuUHggPSBNYXRoLmh5cG90KGR4RGF0YSAvIHJ4LCBkeURhdGEgLyByeSk7XG4gICAgY29uc3QgbiA9IE1hdGgubWF4KDIsIE1hdGgucm91bmQobGVuUHgpKTtcbiAgICBjb25zdCBoID0gZnJhbWUubGVuZ3RoO1xuICAgIGNvbnN0IHcgPSBmcmFtZVswXS5sZW5ndGg7XG4gICAgY29uc3QgcG9zaXRpb25zOiBudW1iZXJbXSA9IFtdO1xuICAgIGNvbnN0IHZhbHVlczogbnVtYmVyW10gPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG47IGkrKykge1xuICAgICAgY29uc3QgdCA9IGkgLyAobiAtIDEpO1xuICAgICAgLy8gTWFwIGRhdGEgY29vcmRzIHRvIHRoZSBjYWNoZWQgZnJhbWUncyBwaXhlbCBncmlkLCBvZmZzZXQgYnkgdGhlIGZyYW1lJ3NcbiAgICAgIC8vIG9yaWdpbiAobm9uLXplcm8gd2hlbiB0aGUgZnJhbWUgaXMgYSB6b29tZWQgY3JvcCkuXG4gICAgICBjb25zdCBweCA9IE1hdGgucm91bmQoKHgwICsgdCAqIGR4RGF0YSAtIG94KSAvIHJ4KTtcbiAgICAgIGNvbnN0IHB5ID0gTWF0aC5yb3VuZCgoeTAgKyB0ICogZHlEYXRhIC0gb3kpIC8gcnkpO1xuICAgICAgbGV0IHYgPSAwO1xuICAgICAgaWYgKHB4ID49IDAgJiYgcHggPCB3ICYmIHB5ID49IDAgJiYgcHkgPCBoKSB7XG4gICAgICAgIGNvbnN0IGNlbGwgPSBmcmFtZVtweV1bcHhdO1xuICAgICAgICB2ID0gQXJyYXkuaXNBcnJheShjZWxsKSA/ICgwLjI5OSAqIGNlbGxbMF0gKyAwLjU4NyAqIGNlbGxbMV0gKyAwLjExNCAqIGNlbGxbMl0pIDogY2VsbDtcbiAgICAgIH1cbiAgICAgIHBvc2l0aW9ucy5wdXNoKHQgKiBsZW5EYXRhKTtcbiAgICAgIHZhbHVlcy5wdXNoKHYpO1xuICAgIH1cbiAgICByZXR1cm4geyBwb3NpdGlvbnMsIHZhbHVlcywgdW5pdDogdXNlTWljcm9ucyA/ICfCtW0nIDogJ3B4JyB9O1xuICB9XG5cbiAgLyoqIFBoeXNpY2FsIHBpeGVsIHNpemUgKG1pY3JvbnMvcGl4ZWwpIGZvciB0aGUgY3VycmVudCBpbWFnZSwgaWYga25vd24uXG4gICAqICBtcHAgaXMgY29uc3RhbnQgYWNyb3NzIGZyYW1lcy9jaGFubmVscywgc28gdGhlIGZpcnN0IG1ldGFkYXRhIGVudHJ5IHRoYXRcbiAgICogIGNhcnJpZXMgYSBwb3NpdGl2ZSBtcHBYIHdpbnMuIFJldHVybnMgbnVsbHMgd2hlbiB0aGUgaW1hZ2UgaXMgdW5zY2FsZWQuICovXG4gIHByaXZhdGUgY3VycmVudE1wcCgpOiB7IG1wcFg6IG51bWJlciB8IG51bGw7IG1wcFk6IG51bWJlciB8IG51bGwgfSB7XG4gICAgY29uc3QgbWV0YSA9IHRoaXMuaW1hZ2VJbmZvPy5pbWFnZU1ldGE7XG4gICAgY29uc3QgbSA9IEFycmF5LmlzQXJyYXkobWV0YSlcbiAgICAgID8gKG1ldGEuZmluZChlID0+IGUgJiYgKGUubXBwWCA/PyAwKSA+IDApID8/IG1ldGFbMF0pXG4gICAgICA6IHVuZGVmaW5lZDtcbiAgICBjb25zdCBtcHBYID0gbSAmJiAobS5tcHBYID8/IDApID4gMCA/IChtLm1wcFggYXMgbnVtYmVyKSA6IG51bGw7XG4gICAgY29uc3QgbXBwWSA9IG0gJiYgKG0ubXBwWSA/PyAwKSA+IDAgPyAobS5tcHBZIGFzIG51bWJlcikgOiBudWxsO1xuICAgIHJldHVybiB7IG1wcFgsIG1wcFkgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBhdXRvc2NhbGUgdGhlIHBsb3RcbiAgICovXG4gIHB1YmxpYyBhdXRvc2NhbGUoKSB7XG4gICAgaWYgKHRoaXMucGxvdERpdikge1xuICAgICAgdGhpcy56b29tQ29vcmRpbmF0ZXMgPSBbXTtcbiAgICAgIFBsb3RseS5yZWxheW91dCh0aGlzLnBsb3REaXYsIHtcbiAgICAgICAgJ3hheGlzLmF1dG9yYW5nZSc6IHRydWUsXG4gICAgICAgICd5YXhpcy5hdXRvcmFuZ2UnOiBmYWxzZSB9XG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiByZWxheW91dCB0aGUgcGxvdFxuICAgKi9cbiAgcHVibGljIHJlbGF5b3V0KHRydWVJbWFnZVNpemU/OiBudW1iZXJbXSkge1xuICAgIGxldCBpbWdTaXplO1xuICAgIGlmICh0cnVlSW1hZ2VTaXplKSB7XG4gICAgICBpbWdTaXplID0gdHJ1ZUltYWdlU2l6ZTtcbiAgICB9IGVsc2Uge1xuICAgICAgaW1nU2l6ZSA9IHRoaXMudHJ1ZUltZ1NpemU7XG4gICAgfVxuICAgIGlmICh0aGlzLnBsb3REaXYpIHtcbiAgICAgIC8vIFJlZnJlc2ggaGVpZ2h0IGZyb20gY3VycmVudCBET00gc28gcGFuZWwgcmVzaXplcyBhcmUgcmVmbGVjdGVkXG4gICAgICBjb25zdCBwbG90RWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0aGlzLnBsb3REaXYpO1xuICAgICAgaWYgKHBsb3RFbD8ub2Zmc2V0SGVpZ2h0KSB7XG4gICAgICAgIHRoaXMuc2NyZWVuSGVpZ2h0ID0gcGxvdEVsLm9mZnNldEhlaWdodDtcbiAgICAgIH1cbiAgICAgIHRyeSB7XG4gICAgICAgIGlmICh0aGlzLnpvb21Db29yZGluYXRlcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgUGxvdGx5LnJlbGF5b3V0KHRoaXMucGxvdERpdiwgdGhpcy5nZXRIZWF0bWFwTGF5b3V0KFxuICAgICAgICAgICAgW3RoaXMuem9vbUNvb3JkaW5hdGVzWzBdLCB0aGlzLnpvb21Db29yZGluYXRlc1sxXV0sXG4gICAgICAgICAgICBbdGhpcy56b29tQ29vcmRpbmF0ZXNbMl0sIHRoaXMuem9vbUNvb3JkaW5hdGVzWzNdXSkpOyAgICAvLyByZXZlcnNlIHRoZSB5IHJhbmdlXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gYXMgYXV0b3JhbmdlIGlzIHNldCB0byBmYWxzZSwgYW5kIG5vdCByZXZlcnNlZCB3ZSBuZWVkIHRvIHNldCB0aGUgeXJhbmdlIGNvcnJlY2x5IGhlcmVcbiAgICAgICAgICBQbG90bHkucmVsYXlvdXQodGhpcy5wbG90RGl2LCB0aGlzLmdldEhlYXRtYXBMYXlvdXQoXG4gICAgICAgICAgICBbaW1nU2l6ZVswXSwgaW1nU2l6ZVsxXV0sIFtpbWdTaXplWzNdLCBpbWdTaXplWzJdXSkpO1xuICAgICAgICB9XG4gICAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICBjb25zdCBtc2cgPSBlcnI/LmVycm9yPy5tZXNzYWdlIHx8IGVycj8ubWVzc2FnZSB8fCBlcnI/LnN0YXR1c1RleHQgfHwgU3RyaW5nKGVycik7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIG9jY3VyZWQnLCBlcnIpO1xuICAgICAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7IGtleTogJ2NlbnRlci10b2FzdCcsIHN0aWNreTogdHJ1ZSwgc2V2ZXJpdHk6J2Vycm9yJywgc3VtbWFyeTonQW4gZXJyb3Igb2NjdXJlZCcsIGRldGFpbDpgVGhlIGZvbGxvd2luZ1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yIG9jY3VyZWQ6ICR7bXNnfS4gUGxlYXNlIHRyeSB0byBvcGVuIHRoZSBpbWFnZSBhZ2FpbiB0aHJvdWdoIHRoZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGUgbmF2aWdhdG9yLmAgfSk7XG4gICAgICAgIC8vIFRPRE8gY29ycmVjdGx5IGNsZWFyIHRoZSBwbG90XG4gICAgICAgIHRoaXMucmVzZXQoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHNldEV2ZW50cyhwbG90RGl2OiBzdHJpbmcsIGlzR3JheXNjYWxlOiBib29sZWFuLCBzY3JlZW5IZWlnaHQ6IG51bWJlcikge1xuICAgIHRoaXMuaW1hZ2VJbmZvLmlzR3JheXNjYWxlID0gaXNHcmF5c2NhbGU7XG4gICAgdGhpcy5zY3JlZW5IZWlnaHQgPSBzY3JlZW5IZWlnaHQ7XG4gICAgY29uc3QgcGxvdDogYW55ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQocGxvdERpdik7XG4gICAgaWYgKHBsb3QpIHtcbiAgICAgIC8vIHVuYmluZCBwcmV2aW91cyBldmVudFxuICAgICAgcGxvdC5yZW1vdmVFdmVudExpc3RlbmVyKCdwbG90bHlfcmVsYXlvdXQnLCB0aGlzLm9uUmVsYXlvdXRFdmVudCk7XG4gICAgICAvLyBiaW5kIG5ldyBldmVudFxuICAgICAgcGxvdC5vbigncGxvdGx5X3JlbGF5b3V0JywgdGhpcy5vblJlbGF5b3V0RXZlbnQpO1xuXG4gICAgICAvLyBDbGlja2luZyBvbiBhIHNoYXBlIGFjdGl2YXRlcyBpdCBidXQgUGxvdGx5IGRvZXNuJ3QgZmlyZSBhIGRlZGljYXRlZFxuICAgICAgLy8gZXZlbnQg4oCUIHNhbXBsZSBfYWN0aXZlU2hhcGVJbmRleCBvbiB0aGUgbmV4dCBtaWNyb3Rhc2sgc28gc3Vic2NyaWJlcnNcbiAgICAgIC8vIChSZWdpb24gRWRpdG9yKSBjYW4gbWlycm9yIHRoZSBzZWxlY3Rpb24uXG4gICAgICBwbG90LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ21vdXNlZG93bicsIHRoaXMub25QbG90TW91c2VEb3duKTtcbiAgICAgIHRoaXMub25QbG90TW91c2VEb3duID0gKCkgPT4gc2V0VGltZW91dCgoKSA9PiB0aGlzLm1heWJlRW1pdEFjdGl2ZVNoYXBlSW5kZXgoKSwgMCk7XG4gICAgICBwbG90LmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlZG93bicsIHRoaXMub25QbG90TW91c2VEb3duKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHJlbGF5b3V0RXZlbnRIYW5kbGVyKGV2ZW50OiBhbnkpIHtcbiAgICBpZiAoT2JqZWN0LmtleXMoZXZlbnQpLmluY2x1ZGVzKCdkcmFnbW9kZScpKSB7XG4gICAgICB0aGlzLmRyYWdNb2RlID0gZXZlbnQuZHJhZ21vZGU7XG4gICAgfVxuICAgIC8vIHdoZW4gcmVsYXlvdXQgZXZlbnQgb2NjdXJzXG4gICAgLy8gdXBkYXRlIHRoZSByZWdpb24gc2VsZWN0aW9uIHVkcGRhdGVzXG4gICAgY29uc3Qga2V5cyA9IE9iamVjdC5rZXlzKGV2ZW50KTtcbiAgICBsZXQgc2hhcGVzTW9kaWZpZWQgPSBmYWxzZTtcbiAgICBrZXlzLmZvckVhY2goKGtleTogYW55KSA9PiB7XG4gICAgICBpZiAodHlwZW9mIGtleSA9PT0gJ3N0cmluZycgJiYga2V5LnN0YXJ0c1dpdGgoJ3NoYXBlc1snKSkge1xuICAgICAgICBjb25zdCBzaGFwZU51bWJlciA9ICtrZXkuc3BsaXQoJ1snKVsxXS5zcGxpdCgnXScpWzBdO1xuICAgICAgICBjb25zdCBzaGFwZUNoYW5nZSA9IGtleS5zcGxpdCgnLicpWzFdO1xuICAgICAgICBpZiAodGhpcy5zaGFwZXMgJiYgdGhpcy5zaGFwZXNbc2hhcGVOdW1iZXJdKSB7XG4gICAgICAgICAgaWYgKHNoYXBlQ2hhbmdlID09PSAncGF0aCcpIHtcbiAgICAgICAgICAgIHRoaXMuc2hhcGVzW3NoYXBlTnVtYmVyXVtzaGFwZUNoYW5nZV0gPSB0aGlzLnBsb3RVdGlsaXRpZXMucm91bmRQYXRoQ29vcmRpbmF0ZXMoZXZlbnRba2V5XSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuc2hhcGVzW3NoYXBlTnVtYmVyXVtzaGFwZUNoYW5nZV0gPSBNYXRoLnJvdW5kKCtldmVudFtrZXldKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgc2hhcGVzTW9kaWZpZWQgPSB0cnVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gICAgaWYgKHNoYXBlc01vZGlmaWVkKSB7XG4gICAgICAvLyBNaXJyb3IgdGhlIGluLXBsYWNlIGVkaXQgaW50byB0aGUgc2hhcmVkIHN0b3JlIChzeW5jcyBpdHMgY2FjaGUgKyBlbWl0cykuXG4gICAgICB0aGlzLmNvbW1pdFNoYXBlc1RvU3RvcmUoKTtcbiAgICB9XG4gICAgLy8gbWFuYWdlIGhpZ2ggZGVmIHpvb20gKG5vdCBpZiB3ZSBhcmUgc2hvd2luZyBhIHN0YWNrKVxuICAgIGlmIChrZXlzLmxlbmd0aCA9PT0gNCAmJiB0aGlzLmlzUmVhbFpvb20pIHtcbiAgICAgIGNvbnN0IGNvb3JkaW5hdGVzOiBhbnlbXSA9IFtdO1xuICAgICAga2V5cy5mb3JFYWNoKGtleSA9PiB7XG4gICAgICAgIGlmIChrZXkuc3RhcnRzV2l0aCgneGF4aXMucmFuZ2VbJykpIHtcbiAgICAgICAgICBjb29yZGluYXRlcy5wdXNoKGV2ZW50W2tleV0pO1xuICAgICAgICB9XG4gICAgICAgIGlmIChrZXkuc3RhcnRzV2l0aCgneWF4aXMucmFuZ2VbJykpIHtcbiAgICAgICAgICBjb29yZGluYXRlcy5wdXNoKGV2ZW50W2tleV0pO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIHRoaXMuem9vbUNvb3JkaW5hdGVzID0gY29vcmRpbmF0ZXM7XG4gICAgICBpZiAoIXRoaXMuaW1hZ2VJbmZvLnNob3dTdGFjaykge1xuICAgICAgICB0aGlzLnRyaWdnZXJab29tKGNvb3JkaW5hdGVzKTtcbiAgICAgIH1cbiAgICB9XG4gICAgLy8gd2hlbiBzaGFwZSBpcyBjcmVhdGVkXG4gICAgaWYgKGtleXMubGVuZ3RoID09PSAxICYmICdzaGFwZXMnIGluIGV2ZW50KSB7XG4gICAgICB0aGlzLnNoYXBlcyA9IGV2ZW50LnNoYXBlcyBhcyBhbnlbXTtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5zaGFwZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgLy8gQSBmcmVzaGx5IFBsb3RseS1kcmF3biBzaGFwZSBoYXMgbm8gaWQgeWV0IOKAlCBnaXZlIGl0IGEgbGFiZWwgb2JqZWN0XG4gICAgICAgIC8vICh3aGVuIGxhYmVscyBhcmUgb24pIGJlZm9yZSB0aGUgc3RvcmUgbWludHMgaWQgKyBkZWZhdWx0IG5hbWUuXG4gICAgICAgIGNvbnN0IGlzTmV3ID0gdGhpcy5zaGFwZXNbaV0uaWQgPT0gbnVsbDtcbiAgICAgICAgdGhpcy5zaGFwZXNbaV0uZmlsZU5hbWUgPSB0aGlzLmZpbGVOYW1lO1xuICAgICAgICB0aGlzLnNoYXBlc1tpXSA9IHRoaXMucGxvdFV0aWxpdGllcy5zbmFwUmVnaW9uKHRoaXMuc2hhcGVzW2ldKTtcbiAgICAgICAgaWYgKGlzTmV3ICYmIHRoaXMucmVnaW9uU3RvcmUuZ2V0U2hvd1NoYXBlTGFiZWwoKSkge1xuICAgICAgICAgIHRoaXMuc2hhcGVzW2ldLmxhYmVsID0geyB0ZXh0OiB0aGlzLnNoYXBlc1tpXS5sYWJlbCxcbiAgICAgICAgICAgIHRleHR0ZW1wbGF0ZTogdGhpcy5zaGFwZXNbaV0ubGFiZWwsXG4gICAgICAgICAgICBmb250OiB7IGNvbG9yOiB0aGlzLnJlZ2lvblN0b3JlLmdldFNoYXBlQ29sb3IoKSB9LFxuICAgICAgICAgICAgdGV4dHBvc2l0aW9uOiAndG9wIGxlZnQnXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgLy8gTWludCBpZHMvbmFtZXMgaW4gdGhlIHN0b3JlLCB3cml0ZSB0aGVtIGJhY2sgb250byB0aGUgZGljdHMsIGFuZCBlbWl0LlxuICAgICAgdGhpcy5jb21taXRTaGFwZXNUb1N0b3JlKCk7XG4gICAgICBpZiAodGhpcy5pc1JlZ2lvblNhdmVkT24pIHtcbiAgICAgICAgdGhpcy5wcmV2aW91c1NoYXBlcyA9IHRoaXMuc2hhcGVzLnNsaWNlKCk7XG4gICAgICB9XG4gICAgfVxuICAgIC8vIGlmIGF1dG9zY2FsZVxuICAgIGlmIChrZXlzLmluY2x1ZGVzKCd4YXhpcy5hdXRvcmFuZ2UnKSAmJiBrZXlzLmluY2x1ZGVzKCd5YXhpcy5hdXRvcmFuZ2UnKSkge1xuICAgICAgLy8gdHJpZ2dlciBhbiBhdXRvc2NhbGUgZXZlbnQgdG8gcmVzZXQgdGhlIHNlbGVjdGVkIGltYWdlIG1vZGUgZHJvcGRvd25cbiAgICAgIHRoaXMuYXV0b3NjYWxlRXZlbnQubmV4dCgnYW4gYXV0b3NjYWxlIGhhcyBoYXBwZW5lZCcpO1xuICAgICAgLy8gc2VsZWN0IGRpc3BsYXkgdHlwZSB0byB0cmlnZ2VyIGEgbmV3IHBsb3R0aW5nIHVwZGF0ZSB3aXRoIGFsbCB0aGUgbmVjZXNzYXJ5IHBsb3R0aW5nIHBhcmFtZXRlcnMgc2V0XG4gICAgICB0aGlzLnRpbGVzLnNlbGVjdERpYWdyYW1EaXNwbGF5KCk7XG4gICAgfVxuICAgIC8vIGlmIHNob3dzdGFjayBvciBhc3BlY3RyYXRpbyBldmVudFxuICAgIGlmIChldmVudC5zaG93c3RhY2sgIT09IHVuZGVmaW5lZCB8fCBldmVudC5hc3BlY3RyYXRpbyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAvLyBTZXQgbmV3IGltYWdlIGluZm8gdG8gdHJpZ2dlciBhIHBsb3QgdXBkYXRlXG4gICAgICB0aGlzLnNldEltYWdlSW5mbyhldmVudC5zaG93c3RhY2ssIGV2ZW50LmFzcGVjdHJhdGlvKTtcbiAgICB9XG5cbiAgICAvLyBQbG90bHkgbWF5IGhhdmUgdXBkYXRlZCB0aGUgYWN0aXZlIHNoYXBlIGluZGV4IGFzIHBhcnQgb2YgdGhlIHJlbGF5b3V0XG4gICAgLy8gKGUuZy4gY2xpY2tpbmcgYSBzaGFwZSdzIGVkaXQgaGFuZGxlLCBvciBmaW5pc2hpbmcgYSBkcmF3KSDigJQgc3VyZmFjZVxuICAgIC8vIHRoYXQgY2hhbmdlIHRvIHRoZSBSZWdpb24gRWRpdG9yLlxuICAgIHRoaXMubWF5YmVFbWl0QWN0aXZlU2hhcGVJbmRleCgpO1xuICB9XG5cbiAgcHVibGljIHJlbG9hZEFuZFBsb3QoKSB7XG4gICAgdGhpcy5zZXRJbWFnZUluZm8oKTtcbiAgfVxuXG5cbiAgLy8g4pSA4pSAIFRvb2wgZGVsZWdhdGlvbnMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgcHVibGljIHNldFdhbmRNb2RlKGFjdGl2ZTogYm9vbGVhbiwgb3B0aW9uczogV2FuZE9wdGlvbnMgPSB7fSkge1xuICAgIC8vIFJlLWJpbmQgdG8gb3Vyc2VsdmVzIGluIGNhc2UgdGhlIE9TRCBiYWNrZW5kIGxhc3QgdXNlZCB0aGVzZSBzaW5nbGV0b25zLlxuICAgIGlmIChhY3RpdmUpIHRoaXMud2FuZFRvb2wuYmluZEhvc3QodGhpcy53YW5kSG9zdCk7XG4gICAgdGhpcy53YW5kVG9vbC5zZXRNb2RlKGFjdGl2ZSwgb3B0aW9ucyk7XG4gIH1cblxuICBwdWJsaWMgc2V0V2FuZE9wdGlvbnMob3B0aW9uczogV2FuZE9wdGlvbnMpIHtcbiAgICB0aGlzLndhbmRUb29sLnNldE9wdGlvbnMob3B0aW9ucyk7XG4gIH1cblxuICBwdWJsaWMgY2xlYXJBY3RpdmVXYW5kUmVnaW9uKCkge1xuICAgIHRoaXMud2FuZFRvb2wuY2xlYXJBY3RpdmVSZWdpb24oKTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRCcnVzaE1vZGUoYWN0aXZlOiBib29sZWFuLCBvcHRpb25zOiBCcnVzaE9wdGlvbnMgPSB7fSkge1xuICAgIC8vIFJlLWJpbmQgdG8gb3Vyc2VsdmVzIGluIGNhc2UgdGhlIE9TRCBiYWNrZW5kIGxhc3QgdXNlZCB0aGlzIHNpbmdsZXRvbi5cbiAgICBpZiAoYWN0aXZlKSB0aGlzLmJydXNoVG9vbC5iaW5kSG9zdCh0aGlzLndhbmRIb3N0KTtcbiAgICB0aGlzLmJydXNoVG9vbC5zZXRNb2RlKGFjdGl2ZSwgb3B0aW9ucyk7XG4gIH1cblxuICBwdWJsaWMgc2V0QnJ1c2hPcHRpb25zKG9wdGlvbnM6IEJydXNoT3B0aW9ucykge1xuICAgIGlmIChvcHRpb25zLnNpemUgIT0gbnVsbCkgdGhpcy5icnVzaFRvb2wuc2V0U2l6ZShvcHRpb25zLnNpemUpO1xuICB9XG5cbiAgcHVibGljIHNldFZlcnRleEVyYXNlck1vZGUoYWN0aXZlOiBib29sZWFuKSB7XG4gICAgaWYgKGFjdGl2ZSkgdGhpcy52ZXJ0ZXhFcmFzZXJUb29sLmJpbmRIb3N0KHRoaXMuZXJhc2VySG9zdCk7XG4gICAgdGhpcy52ZXJ0ZXhFcmFzZXJUb29sLnNldE1vZGUoYWN0aXZlKTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRWZXJ0ZXhFcmFzZXJSYWRpdXMocmFkaXVzOiBudW1iZXIpIHtcbiAgICB0aGlzLnZlcnRleEVyYXNlclRvb2wuc2V0UmFkaXVzKHJhZGl1cyk7XG4gIH1cblxuICBwdWJsaWMgc2V0Wm9vbVRvQm94TW9kZShhY3RpdmU6IGJvb2xlYW4pIHtcbiAgICAvLyBUaGUgem9vbS10by1ib3ggdG9vbCBpcyBhIHJvb3Qgc2luZ2xldG9uIHNoYXJlZCB3aXRoIHRoZSBPU0QgYmFja2VuZCxcbiAgICAvLyB3aGljaCByZWJpbmRzIGl0IHRvIGl0cyBvd24gaG9zdCBvbiBhY3RpdmF0aW9uLiBSZWJpbmQgdG8gb3VycyB3aGVuIHdlXG4gICAgLy8gYWN0aXZhdGUgc28gYSBwcmlvciBPU0Qgem9vbS10by1ib3ggZG9lc24ndCBsZWF2ZSB0aGUgc2luZ2xldG9uIHBvaW50aW5nXG4gICAgLy8gYXQgT1NEJ3MgY29vcmRpbmF0ZS96b29tIGhhbmRsZXJzICh3aGljaCB3b3VsZCBicmVhayBib3gtem9vbSBvbiBoZWF0bWFwcykuXG4gICAgaWYgKGFjdGl2ZSkgdGhpcy5iaW5kWm9vbVRvQm94SG9zdCgpO1xuICAgIHRoaXMuem9vbVRvQm94VG9vbC5zZXRNb2RlKGFjdGl2ZSk7XG4gIH1cblxuICAvKiogQmluZCB0aGUgc2hhcmVkIHpvb20tdG8tYm94IHRvb2wgdG8gdGhpcyAoUGxvdGx5KSBiYWNrZW5kJ3MgaG9zdC4gKi9cbiAgcHJpdmF0ZSBiaW5kWm9vbVRvQm94SG9zdCgpIHtcbiAgICB0aGlzLnpvb21Ub0JveFRvb2wuYmluZEhvc3Qoe1xuICAgICAgZ2V0UGxvdERpdjogKCkgPT4gdGhpcy5wbG90RGl2LFxuICAgICAgcGl4ZWxUb0RhdGE6IChweCwgcHkpID0+IHRoaXMuem9vbUJveFBpeGVsVG9EYXRhKHB4LCBweSksXG4gICAgICBhcHBseVpvb21Ub0JveDogKGNvb3JkcykgPT4gdGhpcy5hcHBseVpvb21Ub0JveChjb29yZHMpLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqIEJveC1wcm9tcHRlZCBTQU06IHNlZ21lbnQgdGhlIGRyYXduIHJlY3RhbmdsZXMuIFRoZSBTQU0gdG9vbCByZXVzZXMgdGhlXG4gICAqICB3YW5kIGhvc3QgKGNhY2hlZCBmcmFtZSArIGNvb3JkaW5hdGUgdHJhbnNmb3JtICsgcmVnaW9uIHN0b3JlKS4gKi9cbiAgcHVibGljIHNlZ21lbnRSZWN0YW5nbGVzKCk6IFByb21pc2U8bnVtYmVyPiB7XG4gICAgdGhpcy5zYW1Ub29sLmJpbmRIb3N0KHRoaXMud2FuZEhvc3QpO1xuICAgIHJldHVybiB0aGlzLnNhbVRvb2wuc2VnbWVudEJveGVzKCk7XG4gIH1cbiAgcHVibGljIHNlZ21lbnRSZWN0YW5nbGVzQ2VsbHBvc2UoKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICBpZiAoIXRoaXMuY2VsbFNlZ21lbnRlcikgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgwKTtcbiAgICB0aGlzLmNlbGxTZWdtZW50VG9vbC5iaW5kSG9zdCh0aGlzLndhbmRIb3N0KTtcbiAgICByZXR1cm4gdGhpcy5jZWxsU2VnbWVudFRvb2wuc2VnbWVudEJveGVzKHRoaXMuY2VsbFNlZ21lbnRlcik7XG4gIH1cbiAgcHVibGljIHNldFNhbU1vZGVsKGlkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLnNhbVRvb2wuc2V0TW9kZWwoaWQpO1xuICAgIHRoaXMuc2FtUG9pbnRUb29sLnNldE1vZGVsKGlkKTtcbiAgfVxuICBwdWJsaWMgc2V0U2FtUG9pbnRNb2RlKGFjdGl2ZTogYm9vbGVhbik6IHZvaWQge1xuICAgIGlmIChhY3RpdmUpIHRoaXMuc2FtUG9pbnRUb29sLmJpbmRIb3N0KHRoaXMud2FuZEhvc3QpO1xuICAgIHRoaXMuc2FtUG9pbnRUb29sLnNldE1vZGUoYWN0aXZlKTtcbiAgfVxuICBwdWJsaWMgY29tbWl0U2FtUG9pbnRzKCk6IHZvaWQgeyB0aGlzLnNhbVBvaW50VG9vbC5jb21taXQoKTsgfVxuICBwdWJsaWMgY2xlYXJTYW1Qb2ludHMoKTogdm9pZCB7IHRoaXMuc2FtUG9pbnRUb29sLmNsZWFyKCk7IH1cblxuICAvKipcbiAgICogVG9vbC1ob3N0IGNhbGxiYWNrOiBhcHBseSB0aGUgem9vbS10by1ib3ggc2VsZWN0aW9uLiBTdGFjayBtb2RlIGRvZXMgYVxuICAgKiBwdXJlIGF4aXMtcmFuZ2UgcmVsYXlvdXQ7IG5vbi1zdGFjayBtb2RlIGdvZXMgdGhyb3VnaCB0aGUgaGlnaC1kZWZcbiAgICogdHJpZ2dlclpvb20gcGlwZWxpbmUgc28gdGhlIGltYWdlIGlzIHJlLWZldGNoZWQgYXQgdGhlIG5ldyByZXNvbHV0aW9uLlxuICAgKi9cbiAgLyoqIE92ZXJsYXktcGl4ZWwgLT4gUGxvdGx5IGRhdGEgY29vcmRzIHZpYSB0aGUgYXhpcyBvYmplY3RzIChzdWJ0cmFjdGluZyB0aGVcbiAgICogIHBsb3QgbWFyZ2luIG9mZnNldCkuIFRoZSB6b29tLXRvLWJveCB0b29sIGNhbGxzIHRoaXMgdGhyb3VnaCBpdHMgaG9zdC4gKi9cbiAgcHJpdmF0ZSB6b29tQm94UGl4ZWxUb0RhdGEocHg6IG51bWJlciwgcHk6IG51bWJlcik6IHsgeDogbnVtYmVyOyB5OiBudW1iZXIgfSB7XG4gICAgY29uc3QgZ2Q6IGFueSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRoaXMucGxvdERpdik7XG4gICAgY29uc3QgeGF4aXMgPSBnZC5fZnVsbExheW91dC54YXhpcztcbiAgICBjb25zdCB5YXhpcyA9IGdkLl9mdWxsTGF5b3V0LnlheGlzO1xuICAgIHJldHVybiB7IHg6IHhheGlzLnAyZChweCAtIHhheGlzLl9vZmZzZXQpLCB5OiB5YXhpcy5wMmQocHkgLSB5YXhpcy5fb2Zmc2V0KSB9O1xuICB9XG5cbiAgcHJpdmF0ZSBhcHBseVpvb21Ub0JveChjb29yZGluYXRlczogbnVtYmVyW10pIHtcbiAgICB0aGlzLnpvb21Db29yZGluYXRlcyA9IGNvb3JkaW5hdGVzO1xuICAgIGlmICh0aGlzLmltYWdlSW5mbz8uc2hvd1N0YWNrKSB7XG4gICAgICBQbG90bHkucmVsYXlvdXQodGhpcy5wbG90RGl2LCB7XG4gICAgICAgICd4YXhpcy5yYW5nZVswXSc6IGNvb3JkaW5hdGVzWzBdLFxuICAgICAgICAneGF4aXMucmFuZ2VbMV0nOiBjb29yZGluYXRlc1sxXSxcbiAgICAgICAgJ3lheGlzLnJhbmdlWzBdJzogY29vcmRpbmF0ZXNbMl0sXG4gICAgICAgICd5YXhpcy5yYW5nZVsxXSc6IGNvb3JkaW5hdGVzWzNdLFxuICAgICAgfSBhcyBhbnkpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnRyaWdnZXJab29tKGNvb3JkaW5hdGVzKTtcbiAgICB9XG4gIH1cblxuICAvLyDilIDilIAgSVZpZXdwb3J0SG9zdCBpbXBsZW1lbnRhdGlvbiAoZm9yIHRoZSBvbi1jYW52YXMgdG9vbHMpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBUaGUgcGxvdCBlbGVtZW50IHRoZSB0b29sIGNhbnZhcyBvdmVybGF5cyBhdHRhY2ggdG8uICovXG4gIHB1YmxpYyBnZXRPdmVybGF5Q29udGFpbmVyKCk6IEhUTUxFbGVtZW50IHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMucGxvdERpdiA/IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRoaXMucGxvdERpdikgOiBudWxsO1xuICB9XG5cbiAgLyoqIFBsb3RseSBjb29yZGluYXRlIHRyYW5zZm9ybSAoc2NyZWVuIDwtPiBkYXRhIHZpYSB0aGUgYXhpcyBvYmplY3RzKS4gKi9cbiAgcHVibGljIGdldENvb3JkaW5hdGVUcmFuc2Zvcm0oKTogSUNvb3JkaW5hdGVUcmFuc2Zvcm0ge1xuICAgIHJldHVybiB0aGlzLmNvb3JkaW5hdGVUcmFuc2Zvcm07XG4gIH1cblxuICAvLyDilIDilIAgV2FuZFRvb2xIb3N0IGltcGxlbWVudGF0aW9uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBQaXhlbCBkYXRhIHRoZSB3YW5kIG5lZWRzIGZvciBzYW1wbGluZy4gbnVsbCA9IG5vIGltYWdlIGxvYWRlZC4gKi9cbiAgcHJpdmF0ZSBnZXRDYWNoZWRJbWFnZURhdGEoKTogQ2FjaGVkSW1hZ2VEYXRhIHwgbnVsbCB7XG4gICAgaWYgKCF0aGlzLmNhY2hlZEltYWdlRnJhbWVzIHx8IHRoaXMuY2FjaGVkSW1hZ2VGcmFtZXMubGVuZ3RoID09PSAwKSByZXR1cm4gbnVsbDtcbiAgICByZXR1cm4ge1xuICAgICAgZnJhbWVzOiB0aGlzLmNhY2hlZEltYWdlRnJhbWVzLFxuICAgICAgd2lkdGg6IHRoaXMuY2FjaGVkSW1hZ2VXaWR0aCxcbiAgICAgIGhlaWdodDogdGhpcy5jYWNoZWRJbWFnZUhlaWdodCxcbiAgICAgIHJhdGlvczogdGhpcy5jYWNoZWRJbWFnZVJhdGlvcyxcbiAgICAgIGlzR3JheXNjYWxlOiB0aGlzLmNhY2hlZElzR3JheXNjYWxlLFxuICAgIH07XG4gIH1cblxuICAvLyDilIDilIAgc2hhcmVkLXN0b3JlIHN5bmMgaGVscGVycyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgLy9cbiAgLy8gYHRoaXMuc2hhcGVzYCBpcyBQbG90bHkncyBvd24gcmVwcmVzZW50YXRpb24gKHRoZSBkaWN0IGFycmF5IFBsb3RseS5yZWxheW91dFxuICAvLyBjb25zdW1lcykuIFRoZSBzaGFyZWQgUmVnaW9uU3RvcmUgaXMgdGhlIHNpbmdsZSBzb3VyY2Ugb2YgdHJ1dGgsIGhvbGRpbmcgdGhlXG4gIC8vIG5ldXRyYWwgUmVnaW9uIG1vZGVsLiBUaGVzZSBoZWxwZXJzIGJyaWRnZSB0aGUgdHdvOiB3cml0ZXMgbWlycm9yXG4gIC8vIGB0aGlzLnNoYXBlc2AgSU5UTyB0aGUgc3RvcmU7IGltYWdlIHN3aXRjaGVzL2V4dGVybmFsIGNoYW5nZXMgcHJvamVjdCB0aGVcbiAgLy8gc3RvcmUgYmFjayBPVVQgdG8gYHRoaXMuc2hhcGVzYC5cblxuICAvKiogQnVpbGQgUGxvdGx5IHNoYXBlIGRpY3RzIGZyb20gdGhlIHN0b3JlJ3MgcmVnaW9ucyAodGhlIHJlbmRlciBwcm9qZWN0aW9uKS4gKi9cbiAgcHJpdmF0ZSByZWdpb25zVG9TaGFwZURpY3RzKCk6IGFueVtdIHtcbiAgICBjb25zdCBzaG93TGFiZWwgPSB0aGlzLnJlZ2lvblN0b3JlLmdldFNob3dTaGFwZUxhYmVsKCk7XG4gICAgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0UmVnaW9ucygpLm1hcChyID0+IHtcbiAgICAgIHIuZmlsZW5hbWUgPSB0aGlzLmZpbGVOYW1lO1xuICAgICAgcmV0dXJuIHsgLi4uci5nZXRTaGFwZShzaG93TGFiZWwpIH07XG4gICAgfSk7XG4gIH1cblxuICAvKiogUmVidWlsZCB0aGUgZGljdCB3b3JraW5nLXNldCBmcm9tIHRoZSBzdG9yZSAoYWZ0ZXIgYW4gaW1hZ2Ugc3dpdGNoLCBvciB3aGVuXG4gICAqICByZWdpb25zIGNoYW5nZWQgd2hpbGUgYW5vdGhlciBiYWNrZW5kIHdhcyByZW5kZXJpbmcpLiAqL1xuICBwcml2YXRlIHN5bmNTaGFwZXNGcm9tU3RvcmUoKTogdm9pZCB7XG4gICAgdGhpcy5zaGFwZXMgPSB0aGlzLnJlZ2lvbnNUb1NoYXBlRGljdHMoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNYWtlIHRoZSBzaGFyZWQgc3RvcmUgYXV0aG9yaXRhdGl2ZSBmb3IgdGhlIGN1cnJlbnQgZGljdCB3b3JraW5nLXNldDogY29udmVydFxuICAgKiBgdGhpcy5zaGFwZXNgIHRvIG5ldXRyYWwgcmVnaW9ucyBhbmQgcmVwbGFjZSB0aGUgc3RvcmUncyBsaXN0ICh0aGUgc3RvcmVcbiAgICogbWludHMgaWRzLCBhcHBsaWVzIGNsYXNzIGNvbG91cnMsIHN5bmNzIGl0cyBwZXItaW1hZ2UgY2FjaGUgYW5kIGVtaXRzIHRoZVxuICAgKiByZWdpb24tdXBkYXRlIGV2ZW50KS4gU3RvcmUtbWludGVkIGlkcyBhcmUgd3JpdHRlbiBiYWNrIG9udG8gdGhlIGRpY3RzIHNvIHRoZVxuICAgKiB0d28gcmVwcmVzZW50YXRpb25zIHN0YXkgYWxpZ25lZC4gUHJvZmlsZS1saW5lIFJPSXMgYXJlIG9yZGluYXJ5IHJlZ2lvbnNcbiAgICogKHRhZ2dlZCBga2luZDogJ3Byb2ZpbGUnYCkgYW5kIHJvdW5kLXRyaXAgdGhyb3VnaCBoZXJlIHRvby5cbiAgICovXG4gIHByaXZhdGUgY29tbWl0U2hhcGVzVG9TdG9yZSgpOiB2b2lkIHtcbiAgICBjb25zdCByZWdpb25zID0gdGhpcy5zaGFwZXNcbiAgICAgIC5tYXAocyA9PiBPYmplY3QuYXNzaWduKG5ldyBTaGFwZVNlbGVjdGlvbigpLCBzKS5nZXRSZWdpb24oKSk7XG4gICAgdGhpcy5yZWdpb25TdG9yZS5zZXRSZWdpb25zKHJlZ2lvbnMsIHRoaXMucmVnaW9uU3RvcmUuZ2V0U2hvd1NoYXBlTGFiZWwoKSwgdHJ1ZSxcbiAgICAgIHRoaXMucmVnaW9uU3RvcmUuZ2V0RmlsbENvbG9yKCksIGZhbHNlKTtcbiAgICBjb25zdCBzdG9yZWQgPSB0aGlzLnJlZ2lvblN0b3JlLmdldFJlZ2lvbnMoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuc2hhcGVzLmxlbmd0aCAmJiBpIDwgc3RvcmVkLmxlbmd0aDsgaSsrKSB7XG4gICAgICBpZiAodGhpcy5zaGFwZXNbaV0uaWQgPT0gbnVsbCkgdGhpcy5zaGFwZXNbaV0uaWQgPSBzdG9yZWRbaV0uaWQ7XG4gICAgICBpZiAodGhpcy5zaGFwZXNbaV0ubmFtZSA9PSBudWxsKSB0aGlzLnNoYXBlc1tpXS5uYW1lID0gc3RvcmVkW2ldLm5hbWU7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFB1c2ggdGhlIGN1cnJlbnQgYHRoaXMuc2hhcGVzYCB0byBQbG90bHksIG1pcnJvciB0aGVtIGludG8gdGhlIHNoYXJlZCBzdG9yZVxuICAgKiAod2hpY2ggc3luY3MgaXRzIGNhY2hlICsgZW1pdHMgdGhlIHJlZ2lvbi11cGRhdGUgZXZlbnQpLCBhbmQgcmVmcmVzaCB0aGVcbiAgICogbG9jYWwgcHJldmlvdXMtc2hhcGVzIGJ1ZmZlci4gVGhlIHdhbmQvZXJhc2VyIHRvb2xzIGNhbGwgdGhpcyBhZnRlciBtdXRhdGluZ1xuICAgKiB0aGUgc2hhcGVzIGFycmF5IGRpcmVjdGx5LlxuICAgKi9cbiAgcHVibGljIGFwcGx5U2hhcGVzQ2hhbmdlKCkge1xuICAgIGlmICh0aGlzLmlzUmVnaW9uU2F2ZWRPbikge1xuICAgICAgdGhpcy5wcmV2aW91c1NoYXBlcyA9IHRoaXMuc2hhcGVzLnNsaWNlKCk7XG4gICAgfVxuICAgIHRoaXMuY29tbWl0U2hhcGVzVG9TdG9yZSgpO1xuICAgIGlmICh0aGlzLnBsb3REaXYpIHtcbiAgICAgIGNvbnN0IGRpY3RBcnJheSA9IHRoaXMuc2hhcGVzLm1hcChzID0+ICh7IC4uLnMgfSkpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgUGxvdGx5LnJlbGF5b3V0KHRoaXMucGxvdERpdiwgdGhpcy5zaGFwZXNSZWxheW91dChkaWN0QXJyYXkpIGFzIGFueSk7XG4gICAgICB9IGNhdGNoIHsgLyogZGl2IG93bmVkIGJ5IGFub3RoZXIgYmFja2VuZCAoT1NEKSDigJQgaXRzIG92ZXJsYXkgcmVuZGVycyBzaGFwZXMgKi8gfVxuICAgIH1cbiAgfVxuXG4gIC8qKiBBY3RpdmUgZnJhbWUgaW5kZXggaW4gdGhlIGNhY2hlZCBpbWFnZSBzdGFjay4gKi9cbiAgcHJpdmF0ZSBhY3RpdmVGcmFtZUluZGV4KCk6IG51bWJlciB7XG4gICAgaWYgKCF0aGlzLmNhY2hlZEltYWdlRnJhbWVzIHx8IHRoaXMuY2FjaGVkSW1hZ2VGcmFtZXMubGVuZ3RoIDw9IDEpIHJldHVybiAwO1xuICAgIGNvbnN0IGdkOiBhbnkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0aGlzLnBsb3REaXYpO1xuICAgIGNvbnN0IHNsaWRlckFjdGl2ZSA9IGdkPy5fZnVsbExheW91dD8uc2xpZGVycz8uWzBdPy5hY3RpdmU7XG4gICAgaWYgKHR5cGVvZiBzbGlkZXJBY3RpdmUgPT09ICdudW1iZXInKSByZXR1cm4gc2xpZGVyQWN0aXZlO1xuICAgIHJldHVybiB0aGlzLnpJbmRleC52YWx1ZSB8fCAwO1xuICB9XG5cbiAgLyoqIENhcGFiaWxpdHktZ2F0ZWQgM0Qgc2NlbmUgY29udHJvbHMgKFBsb3RseSByZW5kZXJzIHRoZSAzRCBwbG90IHR5cGVzKS4gKi9cbiAgZ2V0U3VyZmFjZTNkQ29udHJvbHMoKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHNldFN1cmZhY2VEcmFnTW9kZTogKG1vZGU6IHN0cmluZykgPT4gdGhpcy5zZXRTdXJmYWNlRHJhZ01vZGUobW9kZSksXG4gICAgICByZXNldFN1cmZhY2VDYW1lcmE6ICgpID0+IHRoaXMucmVzZXRTdXJmYWNlQ2FtZXJhKCksXG4gICAgfTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRTdXJmYWNlRHJhZ01vZGUobW9kZTogc3RyaW5nKSB7XG4gICAgaWYgKHRoaXMucGxvdERpdikge1xuICAgICAgUGxvdGx5LnJlbGF5b3V0KHRoaXMucGxvdERpdiwgeyAnc2NlbmUuZHJhZ21vZGUnOiBtb2RlIH0gYXMgYW55KTtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgcmVzZXRTdXJmYWNlQ2FtZXJhKCkge1xuICAgIGlmICh0aGlzLnBsb3REaXYpIHtcbiAgICAgIFBsb3RseS5yZWxheW91dCh0aGlzLnBsb3REaXYsIHsgJ3NjZW5lLmNhbWVyYSc6IHt9IH0gYXMgYW55KTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHRyaWdnZXJab29tKGNvb3JkaW5hdGVzOiBudW1iZXJbXSkge1xuICAgIGlmIChjb29yZGluYXRlcy5sZW5ndGggPT09IDAgfHwgIXRoaXMudHJ1ZUltZ1NpemUpIHJldHVybjtcbiAgICB0aGlzLnpvb21Db29yZGluYXRlcyA9IGNvb3JkaW5hdGVzO1xuICAgIGNvbnN0IHJlY3QgPSB0aGlzLnBsb3RVdGlsaXRpZXMuZ2V0UmVjdGFuZ2xlKGNvb3JkaW5hdGVzLCB0aGlzLnRydWVJbWdTaXplKTtcbiAgICBpZiAodGhpcy5wbG90VXRpbGl0aWVzLmlzWm9vbVNhbWVBc0ltZ1NpemUocmVjdCwgdGhpcy50cnVlSW1nU2l6ZSkpIHtcbiAgICAgIHRoaXMuYXV0b3NjYWxlKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIEEgYnJpZWYgXCJDYWNoaW5nIGltYWdlLi4uXCIgbWVzc2FnZSBmb3IgdW5jYWNoZWQgZmlsZXMgKGxhcmdlIGZpbGVzIHRha2UgYVxuICAgIC8vIG1vbWVudCB0byBjYWNoZSk7IGp1c3QgYSBzcGlubmVyIG90aGVyd2lzZS5cbiAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZ01lc3NhZ2UodGhpcy5pbWFnZUNhY2hlZCA/ICcnIDogJ0NhY2hpbmcgaW1hZ2UuLi4nKTtcbiAgICBjb25zdCBzY3JlZW4gPSB0aGlzLnBsb3RVdGlsaXRpZXMuZ2V0RG9tUmVjdGFuZ2xlKCdkaWFncmFtJyk7XG4gICAgY29uc3QgaW1hZ2VTaXplOiBhbnlbXSA9IFtdO1xuICAgIGltYWdlU2l6ZVswXSA9IHJlY3QueDtcbiAgICBpbWFnZVNpemVbMV0gPSByZWN0LnggKyByZWN0LndpZHRoO1xuICAgIGltYWdlU2l6ZVsyXSA9IHJlY3QueTtcbiAgICBpbWFnZVNpemVbM10gPSByZWN0LnkgKyByZWN0LmhlaWdodDtcbiAgICAvLyBTbmFwc2hvdCB0aGUgZmlsZW5hbWUgc28gYSByZXNwb25zZSB0aGF0IGFycml2ZXMgYWZ0ZXIgdGhlIHVzZXIgc3dpdGNoZWRcbiAgICAvLyBmaWxlcyBpcyBkcm9wcGVkICh0aGUgcmVxdWVzdCBjYXJyaWVkIHRoZSBmaWxlIHNlbGVjdGVkIGF0IGNhbGwgdGltZSkuXG4gICAgY29uc3QgcmVxTmFtZSA9IHRoaXMuZmlsZU5hbWU7XG4gICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcodHJ1ZSk7XG4gICAgdGhpcy5zdGF0ZS5zZXRab29tKHRydWUpO1xuICAgIHRoaXMudGlsZXMuem9vbU9uUmVnaW9uKHJlY3QsIHNjcmVlbiwgdGhpcy56SW5kZXgudmFsdWUpLnN1YnNjcmliZSh7IG5leHQ6IHpvb21EYXRhID0+IHtcbiAgICAgIGNvbnN0IHVpbnQ4QXJyYXkgPSBuZXcgVWludDhBcnJheSh6b29tRGF0YSk7XG4gICAgICBjb25zdCBidWZmZXIgPSBCdWZmZXIuZnJvbSh1aW50OEFycmF5KTtcbiAgICAgIEltYWdlLmxvYWQoYnVmZmVyKS50aGVuKChpbWFnZTogYW55KSA9PiB7XG4gICAgICAgIGNvbnN0IHhSYXRpbyA9IHJlY3Qud2lkdGggLyBpbWFnZS53aWR0aDtcbiAgICAgICAgY29uc3QgeVJhdGlvID0gcmVjdC5oZWlnaHQgLyBpbWFnZS5oZWlnaHQ7XG4gICAgICAgIGlmICh0aGlzLmZpbGVOYW1lID09PSByZXFOYW1lKSB7XG4gICAgICAgICAgY29uc3QgaXNHcmF5ID0gdGhpcy5pbWFnZUluZm8uaXNHcmF5c2NhbGU7XG4gICAgICAgICAgY29uc3QgZnJhbWUgPSBpc0dyYXlcbiAgICAgICAgICAgID8gdGhpcy5wbG90VXRpbGl0aWVzLmFycmF5VG9NYXRyaXgoaW1hZ2UuZ3JleSgpLmRhdGEsIGltYWdlLndpZHRoKVxuICAgICAgICAgICAgOiB0aGlzLnBsb3RVdGlsaXRpZXMuYXJyYXlUb01hdHJpeChpbWFnZS5nZXRQaXhlbHNBcnJheSgpLCBpbWFnZS53aWR0aCk7XG4gICAgICAgICAgLy8gQWxzbyBzYW1wbGUgdGhlIGludGVuc2l0eSBwcm9maWxlcyBmcm9tIHRoaXMgaGlnaC1kZWYgY3JvcCBzbyB0aGVcbiAgICAgICAgICAvLyBpbnNldCByZWZsZWN0cyB0aGUgem9vbS1sZXZlbCByZXNvbHV0aW9uIChvcmlnaW4gPSBjcm9wIHRvcC1sZWZ0KS5cbiAgICAgICAgICB0aGlzLnNldFNhbXBsaW5nRnJhbWVzKFtmcmFtZV0sIFt4UmF0aW8sIHlSYXRpb10sIFtpbWFnZVNpemVbMF0sIGltYWdlU2l6ZVsyXV0pO1xuICAgICAgICAgIHRoaXMuZW1pdFByb2ZpbGVzKCk7XG4gICAgICAgICAgLy8gUmUtcmVuZGVyIHRoZSBoaWdoLWRlZiBjcm9wIGluIHRoZSBTQU1FIHBsb3QgdHlwZSB0aGUgdXNlciBpc1xuICAgICAgICAgIC8vIHZpZXdpbmcuIFdpdGhvdXQgdGhpcyB0aGUgem9vbSByZS1mZXRjaCBhbHdheXMgZmVsbCBiYWNrIHRvIGFcbiAgICAgICAgICAvLyBoZWF0bWFwLCBzbyB6b29taW5nIGluIGNvbnRvdXIgKG9yIGFueSByZWdpc3RyeSB0eXBlKSByZXZlcnRlZFxuICAgICAgICAgIC8vIHRvIGhlYXRtYXAuXG4gICAgICAgICAgY29uc3QgaW1wbCA9IFBMT1RMWV9QTE9UX1RZUEVfSU1QTFNbdGhpcy5wbG90VHlwZV07XG4gICAgICAgICAgY29uc3QgcmVuZGVyUHJvbWlzZSA9IGltcGxcbiAgICAgICAgICAgID8gdGhpcy5wbG90VmlhUmVnaXN0cnkodGhpcy5wbG90RGl2LCBpbXBsLFxuICAgICAgICAgICAgICAgIHRoaXMuYnVpbGRUcmFjZUlucHV0KHRoaXMuaW1hZ2VJbmZvLCB7XG4gICAgICAgICAgICAgICAgICBkYXRhOiBbZnJhbWVdLCByYXRpb3M6IFt4UmF0aW8sIHlSYXRpb10sXG4gICAgICAgICAgICAgICAgICBzaXplczogW2ltYWdlLndpZHRoLCBpbWFnZS5oZWlnaHRdLCBmaWxlbmFtZTogcmVxTmFtZSxcbiAgICAgICAgICAgICAgICB9LCBpbWFnZVNpemUpLCB0aGlzLnNjcmVlbkhlaWdodClcbiAgICAgICAgICAgIDogKGlzR3JheVxuICAgICAgICAgICAgICAgID8gdGhpcy5wbG90SGVhdG1hcCh0aGlzLnBsb3REaXYsIHRoaXMudXJscywgW2ZyYW1lXSwgaW1hZ2VTaXplLCBbeFJhdGlvLCB5UmF0aW9dLFxuICAgICAgICAgICAgICAgICAgICB0aGlzLnNjcmVlbkhlaWdodClcbiAgICAgICAgICAgICAgICA6IHRoaXMucGxvdFJHQkhlYXRtYXAodGhpcy5wbG90RGl2LCB0aGlzLnVybHMsIFtmcmFtZV0sIGltYWdlU2l6ZSwgW3hSYXRpbywgeVJhdGlvXSxcbiAgICAgICAgICAgICAgICAgICAgaW1hZ2Uud2lkdGgsIGltYWdlLmhlaWdodCwgdGhpcy5zY3JlZW5IZWlnaHQpKTtcbiAgICAgICAgICAvLyBwbG90VmlhUmVnaXN0cnkgYWxyZWFkeSBhcHBsaWVzIHRoZSB0eXBlJ3Mgb3duIGxheW91dC9yYW5nZSBmb3JcbiAgICAgICAgICAvLyBub24taW1hZ2UgbGF5b3V0cyAoY2hhcnQvb3ZlcmxheSk7IG9ubHkgcmUtYXBwbHkgdGhlIGhlYXRtYXBcbiAgICAgICAgICAvLyByYW5nZSBmb3IgdGhlIGltYWdlLWFsaWduZWQgcGF0aHMuXG4gICAgICAgICAgY29uc3QgcmVhcHBseVJhbmdlID0gIWltcGwgfHwgaW1wbC5sYXlvdXRLaW5kID09PSAnMmQtaW1hZ2UnO1xuICAgICAgICAgIHJlbmRlclByb21pc2UudGhlbigoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnN0YXRlLnNldEltYWdlQ2FjaGVkKHRydWUpO1xuICAgICAgICAgICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcoZmFsc2UpO1xuICAgICAgICAgICAgaW1hZ2UgPSBudWxsO1xuICAgICAgICAgICAgaWYgKHJlYXBwbHlSYW5nZSkgdGhpcy5yZWxheW91dChpbWFnZVNpemUpO1xuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9LCBlcnJvcjogZXJyID0+IHtcbiAgICAgIGNvbnN0IG1zZyA9IGVycj8uZXJyb3I/Lm1lc3NhZ2UgfHwgZXJyPy5tZXNzYWdlIHx8IGVycj8uc3RhdHVzVGV4dCB8fCBTdHJpbmcoZXJyKTtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIG9jY3VyZWQgd2hlbiB6b29taW5nJywgZXJyKTtcbiAgICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHsga2V5OiAnY2VudGVyLXRvYXN0Jywgc3RpY2t5OiB0cnVlLCBzZXZlcml0eTonZXJyb3InLCBzdW1tYXJ5OidBbiBlcnJvciBvY2N1cmVkJyxcbiAgICAgICAgZGV0YWlsOmBUaGUgZm9sbG93aW5nIGVycm9yIG9jY3VyZWQgd2hpbGUgem9vbWluZzogJHttc2d9LlxuICAgICAgICAgICAgICAgICAgICAgICAgICBQbGVhc2UgdHJ5IHRvIG9wZW4gdGhlIGltYWdlIGFnYWluIHRocm91Z2ggdGhlIGZpbGUgbmF2aWdhdG9yIGFuZFxuICAgICAgICAgICAgICAgICAgICAgICAgICB6b29tIG9uIHRoZSBzZWxlY3RlZCBhcmVhIG9uY2UgbW9yZS5gIH0pO1xuICAgICAgdGhpcy5zdGF0ZS5zZXRMb2FkaW5nRXJyb3IodHJ1ZSk7XG4gICAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyhmYWxzZSk7XG4gICAgfSwgY29tcGxldGU6ICgpID0+IHtcbiAgICAgIGNvbnNvbGUubG9nKCd6b29taW5nIGNvbXBsZXRlJyk7XG4gICAgfSB9KTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRQbG90VHlwZShwbG90VHlwZTogUGxvdFR5cGUpIHtcbiAgICB0aGlzLnBsb3RUeXBlID0gcGxvdFR5cGU7XG4gIH1cblxuICBwcml2YXRlIHNldEltYWdlSW5mbyhzaG93U3RhY2s/OiBib29sZWFuLCBzY2FsZXJhdGlvPzogYm9vbGVhbikge1xuICAgIC8vIEJ1aWxkIGEgcGFydGlhbCBpbWFnZSBkZXNjcmlwdG9yIGFuZCBwdXNoIGl0IHRvIHRoZSBob3N0IHZpYSB0aGUgcG9ydC5cbiAgICBjb25zdCBpbWdJbmZvOiBQYXJ0aWFsPElJbWFnZUluZm8+ID0ge1xuICAgICAgaXNHcmF5c2NhbGU6IHRoaXMuaW1hZ2VJbmZvLmlzR3JheXNjYWxlLFxuICAgICAgdHJ1ZUltYWdlU2l6ZTogW3RoaXMudHJ1ZUltZ1NpemVbMV0sIHRoaXMudHJ1ZUltZ1NpemVbM11dLFxuICAgICAgdXJsczogdGhpcy51cmxzLFxuICAgICAgaXNTdGFjazogdGhpcy51cmxzLmxlbmd0aCA+IDEsXG4gICAgICBzY2FsZVJhdGlvOiBzY2FsZXJhdGlvICE9PSB1bmRlZmluZWQgPyBzY2FsZXJhdGlvIDogdGhpcy5zY2FsZXJhdGlvLFxuICAgIH07XG4gICAgaWYgKHNob3dTdGFjayAhPT0gdW5kZWZpbmVkKSBpbWdJbmZvLnNob3dTdGFjayA9IHNob3dTdGFjaztcbiAgICBpZiAodGhpcy5maWxlTmFtZSkgaW1nSW5mby5maWxlTmFtZSA9IHRoaXMuZmlsZU5hbWU7XG4gICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUluZm8oaW1nSW5mbyk7XG4gIH1cblxuICBwdWJsaWMgZ2V0U2hhcGVzKCkge1xuICAgIHJldHVybiB0aGlzLnNoYXBlcztcbiAgfVxuXG4gIC8qKlxuICAgKiBGcmFtZXdvcmstbmV1dHJhbCB2aWV3IG9mIHRoZSBjdXJyZW50IHJlZ2lvbnMg4oCUIGRlbGVnYXRlcyB0byB0aGUgc2hhcmVkXG4gICAqIFJlZ2lvblN0b3JlICh0aGUgc2luZ2xlIHNvdXJjZSBvZiB0cnV0aCkuIFRoZSBjYW5vbmljYWwgYWNjZXNzb3Igb24gdGhlXG4gICAqIGBJVmlzdWFsaXplcmAgY29udHJhY3Q7IGNvbnN1bWVycyBzaG91bGQgcHJlZmVyIHRoaXMgb3ZlciBgZ2V0U2hhcGVzKClgXG4gICAqICh3aGljaCBleHBvc2VzIFBsb3RseS1zaGFwZWQgZGljdHMpLlxuICAgKi9cbiAgcHVibGljIGdldFJlZ2lvbnMoKTogUmVnaW9uW10ge1xuICAgIHJldHVybiB0aGlzLnJlZ2lvblN0b3JlLmdldFJlZ2lvbnMoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTd2l0Y2ggdGhlIGFjdGl2ZSBpbWFnZS4gVGhlIHNoYXJlZCBSZWdpb25TdG9yZSBvd25zIHRoZSBwZXItaW1hZ2UgcmVnaW9uXG4gICAqIGNhY2hlIChzbmFwc2hvdCBvdXRnb2luZyAvIHJlc3RvcmUgaW5jb21pbmcgLyBjbGVhciBzZWxlY3Rpb24gLyBlbWl0KTsgd2VcbiAgICogZGVsZWdhdGUgdG8gaXQsIHRoZW4gcmUtcHJvamVjdCB0aGUgc3RvcmUncyByZWdpb25zIGludG8gUGxvdGx5J3MgZGljdFxuICAgKiB3b3JraW5nLXNldC4gUmUtcHJvamVjdGluZyB1bmNvbmRpdGlvbmFsbHkgKGV2ZW4gd2hlbiB0aGUgc3RvcmUgbm8tb3BzIGZvclxuICAgKiB0aGUgc2FtZSBpbWFnZSkgbWVhbnMgUGxvdGx5IGFsc28gcGlja3MgdXAgcmVnaW9ucyBhbm90aGVyIGJhY2tlbmQgKE9TRClcbiAgICogYWRkZWQgd2hpbGUgaXQgd2FzIG9mZi1zY3JlZW4uIENhbGxlZCBmcm9tIGBwbG90KClgIGFuZCBmcm9tIHRoZSByb3V0ZXJcbiAgICogYmVmb3JlIGFueSBiYWNrZW5kIHJlbmRlcnMuXG4gICAqL1xuICBwdWJsaWMgc2V0QWN0aXZlSW1hZ2UoaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvKSB7XG4gICAgdGhpcy5yZWdpb25TdG9yZS5zZXRBY3RpdmVJbWFnZShpbWFnZUluZm8pO1xuICAgIHRoaXMuc3luY1NoYXBlc0Zyb21TdG9yZSgpO1xuICAgIHRoaXMucHJldmlvdXNTaGFwZXMgPSB0aGlzLnNoYXBlcy5zbGljZSgpO1xuICB9XG5cbiAgLyoqIFRlc3QvZGV2IGhvb2sg4oCUIGRyb3AgdGhlIGVudGlyZSBwZXItaW1hZ2UgY2FjaGUgKGxvZ291dCAvIHByb2plY3Qgc3dpdGNoKS4gKi9cbiAgcHVibGljIGNsZWFyUmVnaW9uc0J5SW1hZ2VLZXkoKSB7XG4gICAgdGhpcy5yZWdpb25TdG9yZS5jbGVhclJlZ2lvbnNCeUltYWdlS2V5KCk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGFsbCB0aGUgc2VsZWN0ZWQgcmVnaW9ucyBhcyBwb2x5Z29ucy4gT3BlbiBwb2x5bGluZXMgKGNsb3NlZCA9PT0gZmFsc2UpXG4gICAqIGFyZSBleGNsdWRlZCDigJQgdGhleSdyZSBhbm5vdGF0aW9uLW9ubHkgYW5kIGNhbm5vdCBiZSB1c2VkIGFzIGZpbGxlZCByZWdpb25zXG4gICAqIGZvciBpbWFnZSBwcm9jZXNzaW5nLiBEZWxlZ2F0ZXMgdG8gdGhlIHNoYXJlZCBzdG9yZS5cbiAgICovXG4gIHB1YmxpYyBnZXRSZWdpb25Qb2x5Z29ucygpOiBhbnlbXSB7XG4gICAgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0UmVnaW9uUG9seWdvbnMoKTtcbiAgfVxuXG4gIGlzUmVjdGFuZ2xlKGJuZHM6IGFueSk6IGJuZHMgaXMgUmVjdGFuZ2xlIHtcbiAgICByZXR1cm4gJ3gnIGluIGJuZHMgJiYgJ3knIGluIGJuZHMgJiYgJ3dpZHRoJyBpbiBibmRzICYmICdoZWlnaHQnIGluIGJuZHM7XG4gIH1cblxuICBpc1BvbHlnb24oYm5kczogYW55KTogYm5kcyBpcyBQb2x5Z29uIHtcbiAgICByZXR1cm4gJ25wb2ludHMnIGluIGJuZHMgJiYgJ3hwb2ludHMnIGluIGJuZHMgJiYgJ3lwb2ludHMnIGluIGJuZHM7XG4gIH1cblxuICAvKipcbiAgICogU2V0IHBsb3QgcmVnaW9ucy4gRGVsZWdhdGVzIHRoZSBzdGF0ZSBjaGFuZ2UgdG8gdGhlIHNoYXJlZCBSZWdpb25TdG9yZVxuICAgKiAoaWQvbmFtZSBtaW50aW5nLCBjbGFzc2lmaWNhdGlvbiBjb2xvdXJzLCBhcHBlbmQgZGUtZHVwbGljYXRpb24sIHBlci1pbWFnZVxuICAgKiBjYWNoZSBhbmQgdGhlIHJlZ2lvbi11cGRhdGUgZXZlbnQgYWxsIGxpdmUgdGhlcmUpLCB0aGVuIHJlLXByb2plY3RzIHRoZVxuICAgKiBzdG9yZSdzIHJlZ2lvbnMgaW50byBQbG90bHkncyBkaWN0IHdvcmtpbmctc2V0IGFuZCByZW5kZXJzLlxuICAgKlxuICAgKiBXaGVuIGBpc1JlZ2lvblNhdmVPbmAgaXMgZmFsc2UgdGhlIHJlZ2lvbnMgYXJlIHNob3duIHRyYW5zaWVudGx5IOKAlCByZW5kZXJlZFxuICAgKiB3aXRob3V0IGFsdGVyaW5nIHRoZSBzdG9yZWQgd29ya2luZy1zZXQgKHByZXNlcnZlcyB0aGUgcHJpb3IgYmVoYXZpb3VyKS5cbiAgICovXG4gIHB1YmxpYyBzZXRSZWdpb25zKHJlZ2lvbnM6IFJlZ2lvbltdLCBzaG93UmVnaW9uTGFiZWw/OiBib29sZWFuLFxuICAgICAgICAgICAgICAgICAgICBpc1JlZ2lvblNhdmVPbj86IGJvb2xlYW4sIGZpbGxDb2xvcj86IHN0cmluZyxcbiAgICAgICAgICAgICAgICAgICAgYXBwZW5kOiBib29sZWFuID0gZmFsc2UpIHtcbiAgICBjb25zdCBzaG93TGFiZWwgPSBzaG93UmVnaW9uTGFiZWwgPT09IHVuZGVmaW5lZCA/IHRoaXMucmVnaW9uU3RvcmUuZ2V0U2hvd1NoYXBlTGFiZWwoKSA6IHNob3dSZWdpb25MYWJlbDtcbiAgICBjb25zdCBzYXZlID0gaXNSZWdpb25TYXZlT24gPT09IHVuZGVmaW5lZCA/IHRoaXMuaXNSZWdpb25TYXZlZE9uIDogaXNSZWdpb25TYXZlT247XG5cbiAgICB0aGlzLnJlZ2lvblN0b3JlLnNldFJlZ2lvbnMocmVnaW9ucywgc2hvd1JlZ2lvbkxhYmVsLCBpc1JlZ2lvblNhdmVPbiwgZmlsbENvbG9yLCBhcHBlbmQpO1xuICAgIHRoaXMuaXNSZWdpb25TYXZlZE9uID0gc2F2ZTtcblxuICAgIGlmIChzYXZlKSB7XG4gICAgICAvLyBSZS1wcm9qZWN0IHRoZSBhdXRob3JpdGF0aXZlIHJlZ2lvbnMgaW50byBQbG90bHkncyB3b3JraW5nLXNldCBhbmQgcmVuZGVyLlxuICAgICAgdGhpcy5zeW5jU2hhcGVzRnJvbVN0b3JlKCk7XG4gICAgICB0aGlzLnJlbmRlclNoYXBlcygpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBUcmFuc2llbnQgZGlzcGxheSBvbmx5IOKAlCByZW5kZXIgdGhlIHBhc3NlZCByZWdpb25zLCBkb24ndCB0b3VjaCB0aGVcbiAgICAgIC8vIHdvcmtpbmctc2V0IChhbmQgdGhlIHN0b3JlIGRpZG4ndCBzdG9yZSB0aGVtIGVpdGhlcikuXG4gICAgICBjb25zdCBkaWN0cyA9IHJlZ2lvbnMubWFwKHIgPT4geyByLmZpbGVuYW1lID0gdGhpcy5maWxlTmFtZTsgcmV0dXJuIHsgLi4uci5nZXRTaGFwZShzaG93TGFiZWwpIH07IH0pO1xuICAgICAgdHJ5IHtcbiAgICAgICAgUGxvdGx5LnJlbGF5b3V0KHRoaXMucGxvdERpdiwgdGhpcy5zaGFwZXNSZWxheW91dChkaWN0cykgYXMgYW55KTtcbiAgICAgIH0gY2F0Y2ggeyAvKiBkaXYgb3duZWQgYnkgYW5vdGhlciBiYWNrZW5kIChPU0QpIOKAlCBpdHMgb3ZlcmxheSByZW5kZXJzIHNoYXBlcyAqLyB9XG4gICAgfVxuICB9XG5cbiAgLyoqIFRoZSBzaGFwZXMtcmVsYXlvdXQgcGF5bG9hZDogcmVnaW9uIHNoYXBlcyAocHJvZmlsZS1saW5lIFJPSXMgaW5jbHVkZWQsIGFzXG4gICAqICB0aGV5IGFyZSBvcmRpbmFyeSByZWdpb25zKSBwbHVzIHRoZSBhY3RpdmUtc2hhcGUgZmlsbCBjb2xvdXIuICovXG4gIHByaXZhdGUgc2hhcGVzUmVsYXlvdXQoc2hhcGVEaWN0czogYW55W10pOiBhbnkge1xuICAgIHJldHVybiB7XG4gICAgICBzaGFwZXM6IHNoYXBlRGljdHMsXG4gICAgICBhY3RpdmVzaGFwZTogeyBmaWxsY29sb3I6IHRoaXMucmVnaW9uU3RvcmUuZ2V0RmlsbENvbG9yKCkgfSxcbiAgICB9O1xuICB9XG5cbiAgLyoqIFB1c2ggdGhlIGN1cnJlbnQgZGljdCB3b3JraW5nLXNldCB0byBQbG90bHkgKG5vLW9wIHdoZW4gYW5vdGhlciBiYWNrZW5kXG4gICAqICBvd25zIHRoZSBkaXYpLiAqL1xuICBwcml2YXRlIHJlbmRlclNoYXBlcygpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMucGxvdERpdikgcmV0dXJuO1xuICAgIGNvbnN0IGRpY3RBcnJheSA9IHRoaXMuc2hhcGVzLm1hcChzID0+ICh7IC4uLnMgfSkpO1xuICAgIHRyeSB7XG4gICAgICBQbG90bHkucmVsYXlvdXQodGhpcy5wbG90RGl2LCB0aGlzLnNoYXBlc1JlbGF5b3V0KGRpY3RBcnJheSkgYXMgYW55KTtcbiAgICB9IGNhdGNoIHsgLyogZGl2IG93bmVkIGJ5IGFub3RoZXIgYmFja2VuZCAoT1NEKSDigJQgaXRzIG92ZXJsYXkgcmVuZGVycyBzaGFwZXMgKi8gfVxuICB9XG5cbiAgZ2V0U2hvd1NoYXBlTGFiZWwoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0U2hvd1NoYXBlTGFiZWwoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXQgcHJldmlvdXMgc2F2ZWQgc2hhcGVzXG4gICAqL1xuICBwdWJsaWMgcGxvdFByZXZpb3VzU2hhcGVzKCkge1xuICAgIC8vIGNvbnZlcnQgdG8gZGljdCBzbyB0aGF0IHBsb3RseSByZWNvZ25pc2VzIHRoZSBzaGFwZXNcbiAgICBjb25zdCBkaWN0QXJyYXkgPSB0aGlzLnByZXZpb3VzU2hhcGVzLm1hcChzID0+ICh7IC4uLnMgfSkpO1xuICAgIHRyeSB7XG4gICAgICBQbG90bHkucmVsYXlvdXQodGhpcy5wbG90RGl2LCB7IHNoYXBlczogZGljdEFycmF5IH0pO1xuICAgIH0gY2F0Y2ggeyAvKiBkaXYgb3duZWQgYnkgYW5vdGhlciBiYWNrZW5kIChPU0QpICovIH1cbiAgfVxuXG4gIHNldFByZXZpb3VzU2hhcGVzKHNoYXBlczogU2hhcGVTZWxlY3Rpb25bXSkge1xuICAgIHRoaXMucHJldmlvdXNTaGFwZXMgPSBzaGFwZXM7XG4gIH1cblxuICBnZXRQcmV2aW91c1NoYXBlcygpIHtcbiAgICByZXR1cm4gdGhpcy5wcmV2aW91c1NoYXBlcztcbiAgfVxuXG4gIC8vIFVuZG8gKGppdC11aSM4NSk6IHJlZ2lvbiBoaXN0b3J5IGxpdmVzIGluIHRoZSBzaGFyZWQgUmVnaW9uU3RvcmUsIHNvIHRoZVxuICAvLyBhY3RpdmUgYmFja2VuZCdzIG92ZXJsYXkgcmUtcmVuZGVycyBvZmYgaXRzIHJlZ2lvblVwZGF0ZSQgb24gcmVzdG9yZS5cbiAgcHVibGljIHVuZG8oKTogdm9pZCB7IHRoaXMucmVnaW9uU3RvcmUudW5kbygpOyB9XG4gIHB1YmxpYyByZWRvKCk6IHZvaWQgeyB0aGlzLnJlZ2lvblN0b3JlLnJlZG8oKTsgfVxuICBwdWJsaWMgY2FuVW5kbygpOiBib29sZWFuIHsgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuY2FuVW5kbygpOyB9XG4gIHB1YmxpYyBjYW5SZWRvKCk6IGJvb2xlYW4geyByZXR1cm4gdGhpcy5yZWdpb25TdG9yZS5jYW5SZWRvKCk7IH1cbiAgcHVibGljIGdldENhblVuZG8kKCk6IE9ic2VydmFibGU8Ym9vbGVhbj4geyByZXR1cm4gdGhpcy5yZWdpb25TdG9yZS5nZXRDYW5VbmRvJCgpOyB9XG4gIHB1YmxpYyBnZXRDYW5SZWRvJCgpOiBPYnNlcnZhYmxlPGJvb2xlYW4+IHsgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0Q2FuUmVkbyQoKTsgfVxuICBwdWJsaWMgcmVzZXRVbmRvSGlzdG9yeSgpOiB2b2lkIHsgdGhpcy5yZWdpb25TdG9yZS5yZXNldFVuZG9IaXN0b3J5KCk7IH1cblxuICAvLyDilIDilIAgUGVyLXNsaWNlIHotc3RhY2sgcmVnaW9ucyDihpIgUmVnaW9uU3RvcmUgKGppdC11aSM5Mykg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIHB1YmxpYyBlbnRlclN0YWNrTW9kZShzbGljZXM6IE1hcDxudW1iZXIsIFJlZ2lvbltdPiwgaW5pdGlhbFo/OiBudW1iZXIsXG4gICAgICAgICAgICAgICAgICAgICAgICBzYXZlTGF5b3V0PzogJ2NvbWJpbmVkJyB8ICdwZXItc2xpY2UtZmlsZScpOiB2b2lkIHtcbiAgICB0aGlzLnJlZ2lvblN0b3JlLmVudGVyU3RhY2tNb2RlKHNsaWNlcywgaW5pdGlhbFosIHNhdmVMYXlvdXQpO1xuICB9XG4gIHB1YmxpYyBleGl0U3RhY2tNb2RlKCk6IHZvaWQgeyB0aGlzLnJlZ2lvblN0b3JlLmV4aXRTdGFja01vZGUoKTsgfVxuICBwdWJsaWMgaXNTdGFja01vZGUoKTogYm9vbGVhbiB7IHJldHVybiB0aGlzLnJlZ2lvblN0b3JlLmlzU3RhY2tNb2RlKCk7IH1cbiAgcHVibGljIGdldFN0YWNrU2F2ZUxheW91dCgpOiAnY29tYmluZWQnIHwgJ3Blci1zbGljZS1maWxlJyB7XG4gICAgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0U3RhY2tTYXZlTGF5b3V0KCk7XG4gIH1cbiAgcHVibGljIHNldERpc3BsYXlTbGljZSh6OiBudW1iZXIpOiB2b2lkIHsgdGhpcy5yZWdpb25TdG9yZS5zZXREaXNwbGF5U2xpY2Uoeik7IH1cbiAgcHVibGljIGdldFNsaWNlUmVnaW9ucygpOiBSZWdpb25bXSB7IHJldHVybiB0aGlzLnJlZ2lvblN0b3JlLmdldFNsaWNlUmVnaW9ucygpOyB9XG4gIHB1YmxpYyBnZXRTdGFja1NhdmVTbGljZXMoKTogTWFwPG51bWJlciwgUmVnaW9uW10+IHsgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0U3RhY2tTYXZlU2xpY2VzKCk7IH1cblxuICBwcml2YXRlIGdldEhlYXRtYXBMYXlvdXQoeFJhbmdlOiBudW1iZXJbXSwgeVJhbmdlOiBudW1iZXJbXSk6IGFueSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHhheGlzOiB7XG4gICAgICAgIGNvbnN0cmFpbjogJ3JhbmdlJyxcbiAgICAgICAgY29uc3RyYWludG93YXJkOiAnY2VudGVyJyxcbiAgICAgICAgc2lkZTogJ3RvcCcsXG4gICAgICAgIHRpY2tzOiAnJyxcbiAgICAgICAgcmFuZ2U6IHhSYW5nZVxuICAgICAgfSxcbiAgICAgIHlheGlzOiB7XG4gICAgICAgIGNvbnN0cmFpbjogJ3JhbmdlJyxcbiAgICAgICAgY29uc3RyYWludG93YXJkOiAnY2VudGVyJyxcbiAgICAgICAgcmFuZ2U6IHlSYW5nZSxcbiAgICAgICAgdGlja3M6ICcnLFxuICAgICAgICB0aWNrc3VmZml4OiAnICAnLFxuXHRcdFx0XHQvLyBzZXQgYXV0b3JhbmdlIHRvIGZhbHNlIHNvIHRoYXQgcGxvdGx5IGRvZXMgbm90IG92ZXJ3cml0ZSB0aGUgcmFuZ2UgZm9yIHRoZSB5IGF4aXNcblx0XHRcdFx0YXV0b3JhbmdlOiBmYWxzZSxcbiAgICAgICAgc2NhbGVhbmNob3I6IHRoaXMuc2NhbGVyYXRpbyA/ICd4JyA6IGZhbHNlXG4gICAgIH0sXG4gICAgICBtYXJnaW46IHsgdDogMzAsIGI6IDUsIGw6IDU1LCByOiA1IH0sXG4gICAgICBoZWlnaHQ6IHRoaXMuc2NyZWVuSGVpZ2h0LFxuICAgICAgc2xpZGVyczogW3tcbiAgICAgICAgcGFkOiB7IHQ6IDUwIH0sXG4gICAgICAgIGN1cnJlbnR2YWx1ZToge1xuICAgICAgICAgIHZpc2libGU6IHRydWUsXG4gICAgICAgICAgcHJlZml4OiAnWi1wbGFuZTonLFxuICAgICAgICAgIHhhbmNob3I6ICdyaWdodCcsXG4gICAgICAgIH0sXG4gICAgICAgIHN0ZXBzOiB0aGlzLmdldFN0ZXBzKClcbiAgICAgIH1dLFxuICAgICAgYXV0b3NpemU6IHRydWUsXG4gICAgICBzaGFwZXM6IHRoaXMuY3VycmVudFJlbmRlclNoYXBlcyh0aGlzLnJlZ2lvblN0b3JlLmdldFNob3dTaGFwZUxhYmVsKCkpLFxuICAgICAgYWN0aXZlc2hhcGU6IHsgZmlsbGNvbG9yOiB0aGlzLnJlZ2lvblN0b3JlLmdldEZpbGxDb2xvcigpIH0sXG4gICAgICBkcmFnbW9kZTogdGhpcy5kcmFnTW9kZSA/IHRoaXMuZHJhZ01vZGUgOiBmYWxzZSxcbiAgICAgIG5ld3NoYXBlOiB7IGxpbmU6IHsgY29sb3I6IHRoaXMucmVnaW9uU3RvcmUuZ2V0U2hhcGVDb2xvcigpLCB3aWR0aDogMyB9IH1cbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBzaGFwZXNUb1JlZHJhdyhzaG93TGFiZWw6IGJvb2xlYW4pIHtcbiAgICBjb25zdCBzaGFwZXNUb1JlZHJhdzogU2hhcGVTZWxlY3Rpb25bXSA9IFtdO1xuICAgIGZvciAoY29uc3Qgc2hhcGUgb2YgdGhpcy5zaGFwZXMpIHtcbiAgICAgIGlmIChKU09OLnN0cmluZ2lmeShzaGFwZS5maWxlTmFtZSkgPT09IEpTT04uc3RyaW5naWZ5KHRoaXMuZmlsZU5hbWUpKSB7XG4gICAgICAgIC8vIFNldCBsYWJlbFxuICAgICAgICBpZiAoc2hvd0xhYmVsKSB7XG4gICAgICAgICAgc2hhcGUubGFiZWwgPSB7XG4gICAgICAgICAgICB0ZXh0OiBgJHtzaGFwZS5sZWdlbmR9YCxcbiAgICAgICAgICAgIHRleHR0ZW1wbGF0ZTogYCR7c2hhcGUubGVnZW5kfWAsXG4gICAgICAgICAgICB0ZXh0cG9zaXRpb246ICd0b3AgbGVmdCcsXG4gICAgICAgICAgICBmb250OiB7IGNvbG9yOiBgJHtzaGFwZS5saW5lLmNvbG9yfWAgfVxuICAgICAgICAgIH07XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgc2hhcGUubGFiZWwgPSB7fTtcbiAgICAgICAgfVxuICAgICAgICBzaGFwZXNUb1JlZHJhdy5wdXNoKHNoYXBlKTtcbiAgICAgIH1cbiAgICB9XG4gICAgLy8gY29udmVydCB0byBkaWN0IHNvIHRoYXQgcGxvdGx5IHJlY29nbmlzZXMgdGhlIHNoYXBlc1xuICAgIHJldHVybiBzaGFwZXNUb1JlZHJhdy5tYXAocyA9PiAoeyAuLi5zIH0pKTtcbiAgfVxuICAvKipcbiAgICogVW51c2VkICh3aWxsIGJlIHVzZWQgd2hlbiBzdXJmYWNlIHBsb3QgaXMgYWRkZWQpXG4gICAqIEBwYXJhbSB4UmFuZ2VcbiAgICogQHBhcmFtIHlSYW5nZVxuICAgKiBAcGFyYW0gelJhdGlvXG4gICAqIEBwcml2YXRlXG4gICAqL1xuICBwcml2YXRlIGdldFN1cmZhY2VMYXlvdXQoeFJhbmdlOiBudW1iZXJbXSwgeVJhbmdlOiBudW1iZXJbXSwgelJhdGlvOiBudW1iZXIpOiBhbnkge1xuICAgIHJldHVybiB7XG4gICAgICBtYXJnaW46IHsgdDogMCwgYjogMCwgbDogMCwgcjogMCB9LFxuICAgICAgc2NlbmU6IHtcbiAgICAgICAgeGF4aXM6IHtcbiAgICAgICAgICBncmlkY29sb3I6ICdyZ2IoMjU1LCAyNTUsIDI1NSknLFxuICAgICAgICAgIHplcm9saW5lY29sb3I6ICdyZ2IoMjU1LCAyNTUsIDI1NSknLFxuICAgICAgICAgIHNob3diYWNrZ3JvdW5kOiB0cnVlLFxuICAgICAgICAgIGJhY2tncm91bmRjb2xvcjogJ3JnYigyMzAsIDIzMCwyMzApJyxcbiAgICAgICAgICAvLyByYW5nZTogeFJhbmdlXG4gICAgICAgIH0sXG4gICAgICAgIHlheGlzOiB7XG4gICAgICAgICAgZ3JpZGNvbG9yOiAncmdiKDI1NSwgMjU1LCAyNTUpJyxcbiAgICAgICAgICB6ZXJvbGluZWNvbG9yOiAncmdiKDI1NSwgMjU1LCAyNTUpJyxcbiAgICAgICAgICBzaG93YmFja2dyb3VuZDogdHJ1ZSxcbiAgICAgICAgICBiYWNrZ3JvdW5kY29sb3I6ICdyZ2IoMjMwLCAyMzAsIDIzMCknLFxuICAgICAgICAgIC8vIGF1dG9yYW5nZTogJ3JldmVyc2VkJyxcbiAgICAgICAgICAvLyByYW5nZTogeVJhbmdlLFxuICAgICAgICAgIC8vIHNjYWxlYW5jaG9yOiAneCdcbiAgICAgICAgfSxcbiAgICAgICAgemF4aXM6IHtcbiAgICAgICAgICBncmlkY29sb3I6ICdyZ2IoMjU1LCAyNTUsIDI1NSknLFxuICAgICAgICAgIHplcm9saW5lY29sb3I6ICdyZ2IoMjU1LCAyNTUsIDI1NSknLFxuICAgICAgICAgIHNob3diYWNrZ3JvdW5kOiB0cnVlLFxuICAgICAgICAgIGJhY2tncm91bmRjb2xvcjogJ3JnYigyMzAsIDIzMCwyMzApJ1xuICAgICAgICB9LFxuICAgICAgICBhc3BlY3RyYXRpbzogeyB4OiAxLCB5OiAxLCB6OiB6UmF0aW8gfSxcbiAgICAgICAgYXNwZWN0bW9kZTogJ21hbnVhbCcsXG4gICAgICAgIHNsaWRlcnM6IFt7XG4gICAgICAgICAgcGFkOiB7IHQ6IDUwIH0sXG4gICAgICAgICAgY3VycmVudHZhbHVlOiB7XG4gICAgICAgICAgICB2aXNpYmxlOiB0cnVlLFxuICAgICAgICAgICAgcHJlZml4OiAnWi1wbGFuZTonLFxuICAgICAgICAgICAgeGFuY2hvcjogJ3JpZ2h0JyxcbiAgICAgICAgICB9LFxuICAgICAgICAgIHN0ZXBzOiB0aGlzLmdldFN0ZXBzKClcbiAgICAgICAgfV0sXG4gICAgICB9XG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0U3RlcHMoKSB7XG4gICAgY29uc3Qgc3RlcHMgPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuaW1hZ2VMZW5ndGg7IGkrKykge1xuICAgICAgc3RlcHMucHVzaCh7XG4gICAgICAgIGxhYmVsOiBpICsgMSxcbiAgICAgICAgbWV0aG9kOiAncmVzdHlsZScsXG4gICAgICAgIGFyZ3M6IFsndmlzaWJsZScsIEFycmF5KHRoaXMuaW1hZ2VMZW5ndGgpLmZpbGwoZmFsc2UpLmZpbGwodHJ1ZSwgaSwgaSArIDEpXSxcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gc3RlcHM7XG4gIH1cblxuICAvKipcbiAgICogRmV0Y2ggYW4gaW1hZ2UgdmlhIEFuZ3VsYXIgSHR0cENsaWVudCBzbyB0aGF0IGF1dGggaW50ZXJjZXB0b3JzIChCZWFyZXIgdG9rZW4pXG4gICAqIGFyZSBhcHBsaWVkLCB0aGVuIGRlY29kZSBpdCB3aXRoIGltYWdlLWpzLiBUaGlzIGF2b2lkcyByYXcgYnJvd3NlciBmZXRjaCgpXG4gICAqIGNhbGxzIHRoYXQgYnlwYXNzIHRoZSBpbnRlcmNlcHRvciBjaGFpbiBhbmQgZmFpbCBiZWhpbmQgYW4gT0F1dGgyIHByb3h5LlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBsb2FkSW1hZ2UodXJsOiBzdHJpbmcpOiBQcm9taXNlPEltYWdlPiB7XG4gICAgY29uc3QgYnVmZmVyID0gYXdhaXQgZmlyc3RWYWx1ZUZyb20oXG4gICAgICB0aGlzLmh0dHAuZ2V0KHVybCwgeyByZXNwb25zZVR5cGU6ICdhcnJheWJ1ZmZlcicgfSlcbiAgICApO1xuICAgIHJldHVybiBJbWFnZS5sb2FkKEJ1ZmZlci5mcm9tKGJ1ZmZlcikpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgY3VycmVudGx5IGRpc3BsYXllZCBpbWFnZSBhcyBhbiBpbWFnZS1qcyBJbWFnZSBpbnN0YW5jZS5cbiAgICogVXNlZCBieSB0aGUgcHJvY2Vzc2luZyBwaXBlbGluZSB0byBvYnRhaW4gdGhlIGlucHV0IGltYWdlLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIGdldEN1cnJlbnRJbWFnZSgpOiBQcm9taXNlPEltYWdlIHwgbnVsbD4ge1xuICAgIGlmICghdGhpcy5pbWFnZUluZm8gfHwgIXRoaXMuaW1hZ2VJbmZvLnVybHMgfHwgdGhpcy5pbWFnZUluZm8udXJscy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICBjb25zdCB6SWR4ID0gdGhpcy56SW5kZXgudmFsdWUgfHwgMDtcbiAgICBjb25zdCB1cmwgPSB0aGlzLmltYWdlSW5mby51cmxzW3pJZHhdIHx8IHRoaXMuaW1hZ2VJbmZvLnVybHNbMF07XG4gICAgcmV0dXJuIHRoaXMubG9hZEltYWdlKHVybCk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBjdXJyZW50bHkgZGlzcGxheWVkIHBsb3QgcGl4ZWwgZGF0YSBhcyBhIGZsYXQgVWludDhDbGFtcGVkQXJyYXkuXG4gICAqIFRoaXMgY2FwdHVyZXMgd2hhdGV2ZXIgaXMgYWN0dWFsbHkgcmVuZGVyZWQg4oCUIGluY2x1ZGluZyB6b29tZWQgcmVnaW9ucy5cbiAgICogUmV0dXJucyB7IHdpZHRoLCBoZWlnaHQsIGNoYW5uZWxzLCBkYXRhIH0gb3IgbnVsbCBpZiBubyBwbG90IGlzIGRpc3BsYXllZC5cbiAgICovXG4gIHB1YmxpYyBnZXREaXNwbGF5ZWRQaXhlbERhdGEoKTogeyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlcjsgY2hhbm5lbHM6IG51bWJlcjtcbiAgICBkYXRhOiBVaW50OENsYW1wZWRBcnJheSB9IHwgbnVsbCB7XG4gICAgaWYgKCF0aGlzLnBsb3REaXYpIHJldHVybiBudWxsO1xuICAgIGNvbnN0IGdkOiBhbnkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0aGlzLnBsb3REaXYpO1xuICAgIGlmICghZ2Q/LmRhdGEgfHwgZ2QuZGF0YS5sZW5ndGggPT09IDApIHJldHVybiBudWxsO1xuXG4gICAgLy8gRmluZCB0aGUgdmlzaWJsZSB0cmFjZVxuICAgIGNvbnN0IGZyYW1lSWR4ID0gdGhpcy5hY3RpdmVGcmFtZUluZGV4KCk7XG4gICAgY29uc3QgdHJhY2UgPSBnZC5kYXRhW2ZyYW1lSWR4XSB8fCBnZC5kYXRhWzBdO1xuICAgIGlmICghdHJhY2UgfHwgIXRyYWNlLnopIHJldHVybiBudWxsO1xuXG4gICAgY29uc3QgekRhdGE6IGFueVtdW10gPSB0cmFjZS56O1xuICAgIGNvbnN0IGhlaWdodCA9IHpEYXRhLmxlbmd0aDtcbiAgICBpZiAoaGVpZ2h0ID09PSAwKSByZXR1cm4gbnVsbDtcbiAgICBjb25zdCB3aWR0aCA9IHpEYXRhWzBdLmxlbmd0aDtcblxuICAgIGlmICh0cmFjZS50eXBlID09PSAnaW1hZ2UnKSB7XG4gICAgICAvLyBSR0IgaW1hZ2U6IHpbcm93XVtjb2xdID0gW3IsIGcsIGJdIG9yIFtyLCBnLCBiLCBhXVxuICAgICAgY29uc3Qgc2FtcGxlID0gekRhdGFbMF1bMF07XG4gICAgICBjb25zdCBjaGFubmVscyA9IEFycmF5LmlzQXJyYXkoc2FtcGxlKSA/IHNhbXBsZS5sZW5ndGggOiAzO1xuICAgICAgY29uc3QgZGF0YSA9IG5ldyBVaW50OENsYW1wZWRBcnJheSh3aWR0aCAqIGhlaWdodCAqIGNoYW5uZWxzKTtcbiAgICAgIGZvciAobGV0IHkgPSAwOyB5IDwgaGVpZ2h0OyB5KyspIHtcbiAgICAgICAgZm9yIChsZXQgeCA9IDA7IHggPCB3aWR0aDsgeCsrKSB7XG4gICAgICAgICAgY29uc3QgcGl4ZWwgPSB6RGF0YVt5XVt4XTtcbiAgICAgICAgICBjb25zdCBvZmZzZXQgPSAoeSAqIHdpZHRoICsgeCkgKiBjaGFubmVscztcbiAgICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IGNoYW5uZWxzOyBjKyspIHtcbiAgICAgICAgICAgIGRhdGFbb2Zmc2V0ICsgY10gPSBwaXhlbFtjXTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiB7IHdpZHRoLCBoZWlnaHQsIGNoYW5uZWxzLCBkYXRhIH07XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIEhlYXRtYXAgKGdyYXlzY2FsZSk6IHpbcm93XVtjb2xdID0gc2NhbGFyIHZhbHVlXG4gICAgICBjb25zdCBkYXRhID0gbmV3IFVpbnQ4Q2xhbXBlZEFycmF5KHdpZHRoICogaGVpZ2h0KTtcbiAgICAgIGZvciAobGV0IHkgPSAwOyB5IDwgaGVpZ2h0OyB5KyspIHtcbiAgICAgICAgZm9yIChsZXQgeCA9IDA7IHggPCB3aWR0aDsgeCsrKSB7XG4gICAgICAgICAgZGF0YVt5ICogd2lkdGggKyB4XSA9IE1hdGgucm91bmQoekRhdGFbeV1beF0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4geyB3aWR0aCwgaGVpZ2h0LCBjaGFubmVsczogMSwgZGF0YSB9O1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZWdpb24gb2YgdGhlIG9yaWdpbmFsIGltYWdlIHRoYXQgdGhlIGRpc3BsYXllZCBgemAgZ3JpZCBjb3ZlcnMsIGluXG4gICAqIGZ1bGwtaW1hZ2UgcGl4ZWwgY29vcmRzLiBPbiBhIHNlcnZlciB6b29tIChgdHJpZ2dlclpvb21gKSB0aGUgdHJhY2UgYHpgIGlzXG4gICAqIHJlcGxhY2VkIHdpdGggdGhlIGhpZ2gtZGVmIGNyb3AgYW5kIHRoZSBheGVzIGFyZSByZWxhaWQgb3V0IHRvIHRoZSBjcm9wJ3NcbiAgICogb3JpZ2luYWwtaW1hZ2UgYm91bmRzIChgaW1hZ2VTaXplYCksIHNvIHRoZSBsaXZlIGF4aXMgcmFuZ2VzIEFSRSB0aGF0XG4gICAqIHJlY3RhbmdsZTsgd2hlbiB0aGUgd2hvbGUgaW1hZ2UgaXMgaW4gdmlldyB0aGUgcmFuZ2VzIHNwYW4gYHRydWVJbWdTaXplYC5cbiAgICogUmVhZGluZyB0aGUgcmFuZ2VzIHRoZXJlZm9yZSB5aWVsZHMgdGhlIGNyb3Agb3JpZ2luICsgZXh0ZW50IHdpdGhvdXRcbiAgICogdHJhY2tpbmcgem9vbSBzdGF0ZSBzZXBhcmF0ZWx5LiBGYWxscyBiYWNrIHRvIHRoZSBmdWxsIGltYWdlLCB0aGVuIGBudWxsYC5cbiAgICovXG4gIHB1YmxpYyBnZXREaXNwbGF5ZWRTb3VyY2VSZWN0KCk6IHsgeDogbnVtYmVyOyB5OiBudW1iZXI7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyIH0gfCBudWxsIHtcbiAgICBjb25zdCBnZDogYW55ID0gdGhpcy5wbG90RGl2ID8gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2KSA6IG51bGw7XG4gICAgY29uc3QgeHI6IG51bWJlcltdIHwgdW5kZWZpbmVkID0gZ2Q/Ll9mdWxsTGF5b3V0Py54YXhpcz8ucmFuZ2U7XG4gICAgY29uc3QgeXI6IG51bWJlcltdIHwgdW5kZWZpbmVkID0gZ2Q/Ll9mdWxsTGF5b3V0Py55YXhpcz8ucmFuZ2U7XG4gICAgaWYgKHhyICYmIHlyKSB7XG4gICAgICBjb25zdCB4MCA9IE1hdGgubWluKHhyWzBdLCB4clsxXSk7XG4gICAgICBjb25zdCB5MCA9IE1hdGgubWluKHlyWzBdLCB5clsxXSk7IC8vIHkgYXhpcyBpcyByZXZlcnNlZCBmb3IgaW1hZ2UgbGF5b3V0c1xuICAgICAgcmV0dXJuIHsgeDogeDAsIHk6IHkwLCB3aWR0aDogTWF0aC5hYnMoeHJbMV0gLSB4clswXSksIGhlaWdodDogTWF0aC5hYnMoeXJbMV0gLSB5clswXSkgfTtcbiAgICB9XG4gICAgaWYgKCF0aGlzLnRydWVJbWdTaXplKSByZXR1cm4gbnVsbDtcbiAgICByZXR1cm4ge1xuICAgICAgeDogdGhpcy50cnVlSW1nU2l6ZVswXSxcbiAgICAgIHk6IHRoaXMudHJ1ZUltZ1NpemVbMl0sXG4gICAgICB3aWR0aDogdGhpcy50cnVlSW1nU2l6ZVsxXSAtIHRoaXMudHJ1ZUltZ1NpemVbMF0sXG4gICAgICBoZWlnaHQ6IHRoaXMudHJ1ZUltZ1NpemVbM10gLSB0aGlzLnRydWVJbWdTaXplWzJdLFxuICAgIH07XG4gIH1cblxuICBwdWJsaWMgcmVzZXQoKSB7XG4gICAgaWYgKHRoaXMucGxvdERpdikge1xuICAgICAgUGxvdGx5Lm5ld1Bsb3QodGhpcy5wbG90RGl2LCBbXSxcbiAgICAgICAgdGhpcy5nZXRIZWF0bWFwTGF5b3V0KFswLCAxMDBdLCBbMTAwLCAwXSksIENPTkZJRyBhcyBhbnkpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBGdWxseSByZW1vdmUgUGxvdGx5J3MgRE9NIGZyb20gdGhlIHBsb3QgZGl2IChheGVzIGluY2x1ZGVkKS4gVXNlZCB3aGVuXG4gICAqIGhhbmRpbmcgdGhlIGRpdiB0byBhbm90aGVyIGJhY2tlbmQgKE9wZW5TZWFkcmFnb24pIOKAlCB1bmxpa2UgYHJlc2V0KClgLFxuICAgKiB3aGljaCByZS1yZW5kZXJzIGFuIGVtcHR5IHBsb3QgYW5kIHdvdWxkIGxlYXZlIHRoZSBQbG90bHkgYXhlcyBzaG93aW5nLlxuICAgKi9cbiAgcHVibGljIHB1cmdlUGxvdCgpIHtcbiAgICBpZiAodGhpcy5wbG90RGl2KSB7XG4gICAgICBQbG90bHkucHVyZ2UodGhpcy5wbG90RGl2KTtcbiAgICB9XG4gIH1cblxuICAvKiogRXhwb3J0IHRoZSBoZWF0bWFwIGFzIGEgUE5HICh0aGUgcmVuZGVyZWQgcGxvdCBhbHJlYWR5IHJlZmxlY3RzIHRoZSBhY3RpdmVcbiAgICogIGNvbG9ybWFwL3dpbmRvdykuIFRoZSBmdWxsLXJlcyB0aWxlIGNvbXBvc2l0ZSBpcyBPU0QncyBqb2IuICovXG4gIHB1YmxpYyBleHBvcnRDb21wb3NpdGUoKTogdm9pZCB7XG4gICAgdGhpcy5kb3dubG9hZEltYWdlKCk7XG4gIH1cblxuICBwdWJsaWMgZG93bmxvYWRJbWFnZSgpIHtcbiAgICBpZiAodGhpcy5wbG90RGl2KSB7XG4gICAgICAoUGxvdGx5IGFzIGFueSkuZG93bmxvYWRJbWFnZSh0aGlzLnBsb3REaXYsIHsgZm9ybWF0OiAncG5nJywgZmlsZW5hbWU6IHRoaXMuZmlsZU5hbWUgfHwgJ2ltYWdlJyB9KTtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgcmVzZXRBeGVzKCkge1xuICAgIHRoaXMuem9vbUNvb3JkaW5hdGVzID0gW107XG4gICAgdGhpcy5yZWxheW91dCgpO1xuICB9XG5cbiAgcHVibGljIHNldERyYWdNb2RlKG1vZGU6IHN0cmluZyB8IGZhbHNlKSB7XG4gICAgdGhpcy5kcmFnTW9kZSA9IG1vZGUgPyAobW9kZSBhcyBzdHJpbmcpIDogJyc7XG4gICAgaWYgKCF0aGlzLnBsb3REaXYpIHJldHVybjtcbiAgICAvLyBPbmx5IHJlbGF5b3V0IGEgTElWRSBQbG90bHkgZ3JhcGguIFdoZW4gYW5vdGhlciBiYWNrZW5kIChPU0QgLyBuYXBhcmktanMpIG93bnMgdGhlIHZpZXcgdGhlXG4gICAgLy8gZGl2IGlkIGlzIHN0aWxsIHNldCBidXQgaGFzIG5vIFBsb3RseSBwbG90IGF0dGFjaGVkLCBhbmQgUGxvdGx5LnJlbGF5b3V0IHRocm93c1xuICAgIC8vIChcImNhbid0IGFjY2VzcyBwcm9wZXJ0eSBfZ3VpRWRpdGluZywgbiBpcyB1bmRlZmluZWRcIikuIFRoYXQgdGhyb3cgYWJvcnRlZCB0aGUgY2FsbGVyXG4gICAgLy8gKGRlYWN0aXZhdGVBY3RpdmVUb29sIOKGkiBvblNlbGVjdFBsb3RUeXBlKSBiZWZvcmUgdGhlIHJlLXBsb3QgcmFuLCB3ZWRnaW5nIHRoZSBwbG90IHR5cGVcbiAgICAvLyAoZS5nLiBzdHVjayBzd2l0Y2hpbmcgbmFwYXJpIHZvbHVtZSDihpQgaXNvc3VyZmFjZSkuIEd1YXJkIG9uIGBfZnVsbExheW91dGAgbGlrZSB0aGUgem9vbVxuICAgIC8vIGhlbHBlcnMgZG8gc28gaXQgc2FmZWx5IG5vLW9wcyB3aGVuIFBsb3RseSBpc24ndCB0aGUgYWN0aXZlIHJlbmRlcmVyLlxuICAgIGNvbnN0IGdkID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2KSBhcyB1bmtub3duIGFzIHsgX2Z1bGxMYXlvdXQ/OiB1bmtub3duIH0gfCBudWxsO1xuICAgIGlmICghZ2Q/Ll9mdWxsTGF5b3V0KSByZXR1cm47XG4gICAgUGxvdGx5LnJlbGF5b3V0KHRoaXMucGxvdERpdiwgeyBkcmFnbW9kZTogbW9kZSB9IGFzIGFueSk7XG4gIH1cblxuICAvKioge0BsaW5rIElEYXRhUmVuZGVyZXJ9IHN0dWIg4oCUIFBsb3RseSBoYXMgbm8gb3ZlcnZpZXcgbmF2aWdhdG9yIChpdCdzIGFuXG4gICAqICBPcGVuU2VhZHJhZ29uIGZlYXR1cmUpLCBzbyB0b2dnbGluZyBpdCBpcyBhIG5vLW9wIGhlcmUuICovXG4gIHB1YmxpYyBzZXROYXZpZ2F0b3JWaXNpYmxlKF92aXNpYmxlOiBib29sZWFuKTogdm9pZCB7XG4gICAgLyogbm8tb3A6IG5hdmlnYXRvciBpcyBPcGVuU2VhZHJhZ29uLW9ubHkgKi9cbiAgfVxuXG4gIC8qKiB7QGxpbmsgSURhdGFSZW5kZXJlcn0gc3R1YiDigJQgaW1hZ2Ugc21vb3RoaW5nIGlzIGFuIE9wZW5TZWFkcmFnb24gY2FudmFzLWRyYXdlclxuICAgKiAgc2V0dGluZzsgdGhlIFBsb3RseSBpbWFnZS9oZWF0bWFwIHRyYWNlcyBkb24ndCBleHBvc2UgaXQsIHNvIGl0J3MgYSBuby1vcC4gKi9cbiAgcHVibGljIHNldEltYWdlU21vb3RoaW5nRW5hYmxlZChfZW5hYmxlZDogYm9vbGVhbik6IHZvaWQge1xuICAgIC8qIG5vLW9wOiBzbW9vdGhpbmcgdG9nZ2xlIGlzIE9wZW5TZWFkcmFnb24tb25seSAqL1xuICB9XG5cbiAgcHVibGljIHpvb21JbigpIHtcbiAgICBpZiAoIXRoaXMucGxvdERpdikgcmV0dXJuO1xuICAgIGNvbnN0IGdkOiBhbnkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0aGlzLnBsb3REaXYpO1xuICAgIGlmICghZ2Q/Ll9mdWxsTGF5b3V0KSByZXR1cm47XG4gICAgY29uc3QgeGwgPSBnZC5fZnVsbExheW91dC54YXhpcztcbiAgICBjb25zdCB5bCA9IGdkLl9mdWxsTGF5b3V0LnlheGlzO1xuICAgIC8vIDNEIHNjZW5lcyBoYXZlIG5vIDJEIHhheGlzL3lheGlzICh0aGV5IGxpdmUgdW5kZXIgYHNjZW5lYCk7IHRoZSBzdGVwLXpvb21cbiAgICAvLyBpcyBtZWFuaW5nbGVzcyB0aGVyZSBhbmQgd291bGQgdGhyb3cgb24gYHhsLnJhbmdlYC4gUGxvdGx5J3Mgc2NlbmUgaGFuZGxlc1xuICAgIC8vIHNjcm9sbC16b29tIG5hdGl2ZWx5LCBzbyBqdXN0IGJhaWwuXG4gICAgaWYgKCF4bD8ucmFuZ2UgfHwgIXlsPy5yYW5nZSkgcmV0dXJuO1xuICAgIGNvbnN0IHhjID0gKHhsLnJhbmdlWzBdICsgeGwucmFuZ2VbMV0pIC8gMjtcbiAgICBjb25zdCB5YyA9ICh5bC5yYW5nZVswXSArIHlsLnJhbmdlWzFdKSAvIDI7XG4gICAgY29uc3QgZHggPSAoeGwucmFuZ2VbMV0gLSB4bC5yYW5nZVswXSkgLyAxLjM7XG4gICAgY29uc3QgZHkgPSAoeWwucmFuZ2VbMV0gLSB5bC5yYW5nZVswXSkgLyAxLjM7XG4gICAgY29uc3QgeDAgPSB4YyAtIGR4IC8gMiwgeDEgPSB4YyArIGR4IC8gMjtcbiAgICBjb25zdCB5MCA9IHljIC0gZHkgLyAyLCB5MSA9IHljICsgZHkgLyAyO1xuICAgIHRoaXMuem9vbUNvb3JkaW5hdGVzID0gW3gwLCB4MSwgeTAsIHkxXTtcbiAgICBQbG90bHkucmVsYXlvdXQodGhpcy5wbG90RGl2LCB7XG4gICAgICAneGF4aXMucmFuZ2VbMF0nOiB4MCwgJ3hheGlzLnJhbmdlWzFdJzogeDEsXG4gICAgICAneWF4aXMucmFuZ2VbMF0nOiB5MCwgJ3lheGlzLnJhbmdlWzFdJzogeTFcbiAgICB9IGFzIGFueSk7XG4gIH1cblxuICAvKipcbiAgICogRGVsZXRlIGV2ZXJ5IGN1cnJlbnRseSBzZWxlY3RlZCBzaGFwZS4gRmFsbHMgYmFjayB0byB0aGUgc2luZ2xlXG4gICAqIGBfYWN0aXZlU2hhcGVJbmRleGAgd2hlbiBubyBtdWx0aS1zZWxlY3Rpb24gaXMgYWN0aXZlIChjb3ZlcnMgdGhlIGNhc2VcbiAgICogd2hlcmUgUGxvdGx5IGNsaWNrZWQgYSBzaGFwZSB3aXRob3V0IGdvaW5nIHRocm91Z2ggdGhlIHRhYmxlKS5cbiAgICovXG4gIHB1YmxpYyBkZWxldGVBY3RpdmVTaGFwZSgpIHtcbiAgICAvLyBOb3RlOiBubyBgcGxvdERpdmAgZ3VhcmQg4oCUIHdoZW4gT3BlblNlYWRyYWdvbiBpcyB0aGUgcmVuZGVyZXIsIFBsb3RseVxuICAgIC8vIG5ldmVyIHBsb3R0ZWQgKHBsb3REaXYgaXMgdW5zZXQpLCBidXQgZGVsZXRpb24gb25seSBuZWVkcyB0aGUgc2hhcGUgbGlzdFxuICAgIC8vICsgdGhlIGN1cnJlbnQgc2VsZWN0aW9uOyB0aGUgUGxvdGx5IHJlbGF5b3V0IGJlbG93IGlzIGd1YXJkZWQuXG4gICAgY29uc3QgZ2Q6IGFueSA9IHRoaXMucGxvdERpdiA/IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRoaXMucGxvdERpdikgOiBudWxsO1xuXG4gICAgLy8gUmVzb2x2ZSB3aGF0IHRvIGRlbGV0ZTogdGhlIHN0b3JlJ3Mgc2VsZWN0aW9uLCBlbHNlIHRoZSBzaGFwZSBQbG90bHlcbiAgICAvLyB0cmFja3MgYXMgY2xpY2tlZCAod2hlbiB0aGUgdXNlciBjbGlja2VkIGEgc2hhcGUgd2l0aG91dCBnb2luZyB0aHJvdWdoIHRoZVxuICAgIC8vIFJlZ2lvbiBFZGl0b3IgdGFibGUpLlxuICAgIGlmICh0aGlzLnJlZ2lvblN0b3JlLmdldFNlbGVjdGVkU2hhcGVJbmRpY2VzKCkubGVuZ3RoID09PSAwKSB7XG4gICAgICBjb25zdCBhY3RpdmVJbmRleDogbnVtYmVyID0gZ2Q/Ll9mdWxsTGF5b3V0Py5fYWN0aXZlU2hhcGVJbmRleDtcbiAgICAgIGlmIChhY3RpdmVJbmRleCA9PT0gdW5kZWZpbmVkIHx8IGFjdGl2ZUluZGV4IDwgMCkgcmV0dXJuO1xuICAgICAgdGhpcy5yZWdpb25TdG9yZS5zZXRTZWxlY3RlZFNoYXBlSW5kaWNlcyhbYWN0aXZlSW5kZXhdKTtcbiAgICB9XG4gICAgLy8gVGhlIHN0b3JlIHJlbW92ZXMgdGhlIHNlbGVjdGVkIHJlZ2lvbnMsIGNsZWFycyB0aGUgc2VsZWN0aW9uLCBzeW5jcyBpdHNcbiAgICAvLyBjYWNoZSBhbmQgZW1pdHMgdGhlIHJlZ2lvbi11cGRhdGUgZXZlbnQuXG4gICAgdGhpcy5yZWdpb25TdG9yZS5kZWxldGVBY3RpdmVTaGFwZSgpO1xuXG4gICAgLy8gUmUtcHJvamVjdCB0aGUgcmVtYWluaW5nIHJlZ2lvbnMgYW5kIHJlbmRlciBvbiB0aGUgUGxvdGx5IHBsb3QgKGEgbm8tb3AsXG4gICAgLy8gY2F1Z2h0LCB3aGVuIGFub3RoZXIgYmFja2VuZCBvd25zIHRoZSBkaXYg4oCUIGl0cyBvdmVybGF5IHJlLXJlbmRlcnMgdmlhXG4gICAgLy8gdGhlIHN0b3JlJ3MgdXBkYXRlIGV2ZW50KS5cbiAgICBpZiAoZ2Q/Ll9mdWxsTGF5b3V0KSBnZC5fZnVsbExheW91dC5fYWN0aXZlU2hhcGVJbmRleCA9IC0xO1xuICAgIHRoaXMuc3luY1NoYXBlc0Zyb21TdG9yZSgpO1xuICAgIGNvbnN0IGRpY3RBcnJheSA9IHRoaXMuc2hhcGVzLm1hcChzID0+ICh7IC4uLnMgfSkpO1xuICAgIHRyeSB7XG4gICAgICBQbG90bHkucmVsYXlvdXQodGhpcy5wbG90RGl2LCB7IHNoYXBlczogZGljdEFycmF5IH0gYXMgYW55KS50aGVuKCgpID0+IHtcbiAgICAgICAgaWYgKGdkPy5fZnVsbExheW91dCkgKFBsb3RseSBhcyBhbnkpLnJlZHJhdyhnZCk7XG4gICAgICB9LCAoKSA9PiB7IC8qIGRpdiBub3QgYSBQbG90bHkgcGxvdCAqLyB9KTtcbiAgICB9IGNhdGNoIHsgLyogZGl2IG93bmVkIGJ5IGFub3RoZXIgYmFja2VuZCAqLyB9XG4gIH1cblxuICBwdWJsaWMgem9vbU91dCgpIHtcbiAgICBpZiAoIXRoaXMucGxvdERpdikgcmV0dXJuO1xuICAgIGNvbnN0IGdkOiBhbnkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0aGlzLnBsb3REaXYpO1xuICAgIGlmICghZ2Q/Ll9mdWxsTGF5b3V0KSByZXR1cm47XG4gICAgY29uc3QgeGwgPSBnZC5fZnVsbExheW91dC54YXhpcztcbiAgICBjb25zdCB5bCA9IGdkLl9mdWxsTGF5b3V0LnlheGlzO1xuICAgIC8vIDNEIHNjZW5lcyBoYXZlIG5vIDJEIHhheGlzL3lheGlzIOKAlCBzZWUgem9vbUluKCkuXG4gICAgaWYgKCF4bD8ucmFuZ2UgfHwgIXlsPy5yYW5nZSkgcmV0dXJuO1xuICAgIGNvbnN0IHhjID0gKHhsLnJhbmdlWzBdICsgeGwucmFuZ2VbMV0pIC8gMjtcbiAgICBjb25zdCB5YyA9ICh5bC5yYW5nZVswXSArIHlsLnJhbmdlWzFdKSAvIDI7XG4gICAgY29uc3QgZHggPSAoeGwucmFuZ2VbMV0gLSB4bC5yYW5nZVswXSkgKiAxLjM7XG4gICAgY29uc3QgZHkgPSAoeWwucmFuZ2VbMV0gLSB5bC5yYW5nZVswXSkgKiAxLjM7XG4gICAgY29uc3QgeDAgPSB4YyAtIGR4IC8gMiwgeDEgPSB4YyArIGR4IC8gMjtcbiAgICBjb25zdCB5MCA9IHljIC0gZHkgLyAyLCB5MSA9IHljICsgZHkgLyAyO1xuICAgIHRoaXMuem9vbUNvb3JkaW5hdGVzID0gW3gwLCB4MSwgeTAsIHkxXTtcbiAgICBQbG90bHkucmVsYXlvdXQodGhpcy5wbG90RGl2LCB7XG4gICAgICAneGF4aXMucmFuZ2VbMF0nOiB4MCwgJ3hheGlzLnJhbmdlWzFdJzogeDEsXG4gICAgICAneWF4aXMucmFuZ2VbMF0nOiB5MCwgJ3lheGlzLnJhbmdlWzFdJzogeTFcbiAgICB9IGFzIGFueSk7XG4gIH1cblxuICBwdWJsaWMgaXNTdGFja0xvYWRpbmckKCk6IE9ic2VydmFibGU8Ym9vbGVhbj4ge1xuICAgIHJldHVybiB0aGlzLnN0YWNrTG9hZGluZyQuYXNPYnNlcnZhYmxlKCk7XG4gIH1cbiAgcHVibGljIHNldFN0YWNrTG9hZGluZyhzdGFja0xvYWRpbmc6IGJvb2xlYW4pIHtcbiAgICB0aGlzLnN0YWNrTG9hZGluZyQubmV4dChzdGFja0xvYWRpbmcpO1xuICB9XG4gIHB1YmxpYyBnZXRTdGFja0xvYWRpbmdQcm9ncmVzcyQoKTogT2JzZXJ2YWJsZTxudW1iZXI+IHtcbiAgICByZXR1cm4gdGhpcy5zdGFja0xvYWRpbmdQcm9ncmVzcyQuYXNPYnNlcnZhYmxlKCk7XG4gIH1cblxuICAvLyBDb2xvcm1hcCAvIHJldmVyc2Utc2NhbGUgLyBpbWFnZSBtZXRhZGF0YSBzdGF0ZSBsaXZlcyBpbiB0aGUgc2hhcmVkXG4gIC8vIFZpc3VhbGl6ZXJTdG9yZTsgdGhlc2UgZGVsZWdhdGUsIGtlZXBpbmcgb25seSB0aGUgUGxvdGx5LXNwZWNpZmljIGxpdmVcbiAgLy8gcmVzdHlsZSBhcyByZW5kZXIgZ2x1ZS5cbiAgZ2V0Q29sb3JtYXBPcHRpb25zKCkge1xuICAgIHJldHVybiB0aGlzLnN0b3JlLmdldENvbG9ybWFwT3B0aW9ucygpO1xuICB9XG4gIHNldENvbG9ybWFwKGNvbG9ybWFwOiBhbnkpIHtcbiAgICB0aGlzLnN0b3JlLnNldENvbG9ybWFwKGNvbG9ybWFwKTtcbiAgICB0cnkge1xuICAgICAgUGxvdGx5LnJlc3R5bGUodGhpcy5wbG90RGl2LCB7ICdjb2xvcnNjYWxlJzogW2NvbG9ybWFwLmRhdGEudmFsdWVdIH0pO1xuICAgIH0gY2F0Y2ggeyAvKiBkaXYgb3duZWQgYnkgYW5vdGhlciBiYWNrZW5kIChPU0QpIOKAlCBpdCByZWNvbG9ycyB2aWEgdGhlIExVVCAqLyB9XG4gIH1cbiAgZ2V0Q29sb3JtYXAoKSB7XG4gICAgcmV0dXJuIHRoaXMuc3RvcmUuZ2V0Q29sb3JtYXAoKTtcbiAgfVxuXG4gIHNldFJldmVyc2VTY2FsZShyZXZlcnNjYWxlOiBhbnkpIHtcbiAgICB0aGlzLnN0b3JlLnNldFJldmVyc2VTY2FsZShyZXZlcnNjYWxlKTtcbiAgICB0cnkge1xuICAgICAgUGxvdGx5LnJlc3R5bGUodGhpcy5wbG90RGl2LCB7ICdyZXZlcnNlc2NhbGUnOiByZXZlcnNjYWxlIH0pO1xuICAgIH0gY2F0Y2ggeyAvKiBkaXYgb3duZWQgYnkgYW5vdGhlciBiYWNrZW5kIChPU0QpIOKAlCBpdCByZWNvbG9ycyB2aWEgdGhlIExVVCAqLyB9XG4gIH1cblxuICBnZXRSZXZlcnNlU2NhbGUoKSB7XG4gICAgcmV0dXJuIHRoaXMuc3RvcmUuZ2V0UmV2ZXJzZVNjYWxlKCk7XG4gIH1cblxuICBzZXRJbWFnZU1ldGEoaW1hZ2VNZXRhOiBJSW1hZ2VNZXRhZGF0YVtdKSB7XG4gICAgdGhpcy5zdG9yZS5zZXRJbWFnZU1ldGEoaW1hZ2VNZXRhKTtcbiAgfVxuXG4gIGdldEltYWdlTWV0YSgpIHtcbiAgICByZXR1cm4gdGhpcy5zdG9yZS5nZXRJbWFnZU1ldGEoKTtcbiAgfVxuXG4gIHNldFNob3dTdGFjayhzaG93c3RhY2s6IGJvb2xlYW4pIHtcbiAgICBpZiAoIXNob3dzdGFjaykge1xuICAgICAgdGhpcy56SW5kZXgubmV4dCgwKTtcbiAgICB9XG4gICAgdGhpcy5pbWFnZUluZm8uc2hvd1N0YWNrID0gc2hvd3N0YWNrO1xuICAgIHRoaXMuc3RhY2tMb2FkaW5nJC5uZXh0KHNob3dzdGFjayk7XG4gICAgUGxvdGx5LnJlbGF5b3V0KHRoaXMucGxvdERpdiwgeyAnc2hvd3N0YWNrJzogc2hvd3N0YWNrIH0gYXMgYW55KTtcbiAgfVxuXG4gIGdldEF1dG9zY2FsZUV2ZW50KCkge1xuICAgIHJldHVybiB0aGlzLmF1dG9zY2FsZUV2ZW50LmFzT2JzZXJ2YWJsZSgpO1xuICB9XG5cbiAgZ2V0UmVnaW9uVXBkYXRlRXZlbnQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0UmVnaW9uVXBkYXRlRXZlbnQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdHJlYW0gb2YgdGhlIGluZGljZXMgb2YgY3VycmVudGx5IHNlbGVjdGVkIHJlZ2lvbnMg4oCUIG93bmVkIGJ5IHRoZSBzaGFyZWRcbiAgICogUmVnaW9uU3RvcmUgc28gdGhlIHNlbGVjdGlvbiBpcyBjb25zaXN0ZW50IGFjcm9zcyBiYWNrZW5kcyBhbmQgdGhlIFJlZ2lvblxuICAgKiBFZGl0b3IgdGFibGUuIEVtcHR5ID0gbm90aGluZyBzZWxlY3RlZC5cbiAgICovXG4gIGdldFNlbGVjdGVkU2hhcGVJbmRpY2VzJCgpOiBPYnNlcnZhYmxlPG51bWJlcltdPiB7XG4gICAgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0U2VsZWN0ZWRTaGFwZUluZGljZXMkKCk7XG4gIH1cblxuICAvKipcbiAgICogUHJvZ3JhbW1hdGljYWxseSBzZWxlY3QgcmVnaW9ucyAob3IgY2xlYXIgd2l0aCBbXSkuIFRoZSBzaGFyZWQgc3RvcmUgb3duc1xuICAgKiB0aGUgc2VsZWN0aW9uIHN0YXRlICh2YWxpZGF0ZXMvZGVkdXBlcy9lbWl0cyk7IGhlcmUgd2UgYWRkaXRpb25hbGx5IHBvaW50XG4gICAqIFBsb3RseSdzIGBfYWN0aXZlU2hhcGVJbmRleGAgYXQgdGhlIGxhc3Qgc2VsZWN0ZWQgc2hhcGUgc28gaXQgZ2V0cyB0aGUgZWRpdFxuICAgKiBoYW5kbGVzLiBObyB2aXN1YWwgY2hhbmdlIHRvIG5vbi1hY3RpdmUgc2hhcGVzIOKAlCBQbG90bHkncyBvd24gYWN0aXZlLXNoYXBlXG4gICAqIHJlbmRlcmluZyBpcyB0aGUgb25seSBoaWdobGlnaHQuXG4gICAqL1xuICBwdWJsaWMgc2V0U2VsZWN0ZWRTaGFwZUluZGljZXMoaW5kaWNlczogbnVtYmVyW10pIHtcbiAgICBjb25zdCB2YWxpZCA9IChpbmRpY2VzIHx8IFtdKVxuICAgICAgLmZpbHRlcihpID0+IE51bWJlci5pc0Zpbml0ZShpKSAmJiBpID49IDAgJiYgaSA8IHRoaXMuc2hhcGVzLmxlbmd0aCk7XG4gICAgY29uc3Qgc2VlbiA9IG5ldyBTZXQ8bnVtYmVyPigpO1xuICAgIGNvbnN0IGNsZWFuZWQ6IG51bWJlcltdID0gW107XG4gICAgZm9yIChjb25zdCBpIG9mIHZhbGlkKSB7XG4gICAgICBpZiAoIXNlZW4uaGFzKGkpKSB7IHNlZW4uYWRkKGkpOyBjbGVhbmVkLnB1c2goaSk7IH1cbiAgICB9XG4gICAgaWYgKHRoaXMucGxvdERpdikge1xuICAgICAgY29uc3QgZ2Q6IGFueSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRoaXMucGxvdERpdik7XG4gICAgICBpZiAoZ2Q/Ll9mdWxsTGF5b3V0KSB7XG4gICAgICAgIGdkLl9mdWxsTGF5b3V0Ll9hY3RpdmVTaGFwZUluZGV4ID0gY2xlYW5lZC5sZW5ndGggPiAwID8gY2xlYW5lZFtjbGVhbmVkLmxlbmd0aCAtIDFdIDogLTE7XG4gICAgICAgIHRyeSB7IChQbG90bHkgYXMgYW55KS5yZWRyYXcoZ2QpOyB9IGNhdGNoIHsgLyogbm9vcCBpbiB0ZXN0cyAqLyB9XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMucmVnaW9uU3RvcmUuc2V0U2VsZWN0ZWRTaGFwZUluZGljZXMoY2xlYW5lZCk7XG4gIH1cblxuICAvKipcbiAgICogU2VsZWN0IGEgc3BlY2lmaWMgcmVnaW9uIChJUmVnaW9uU3RvcmUuc2VsZWN0UmVnaW9uKS4gU2V0cyB0aGUgc2hhcmVkIHN0b3JlXG4gICAqIHNlbGVjdGlvbiBieSBpZGVudGl0eSBhbmQgcG9pbnRzIFBsb3RseSdzIGFjdGl2ZS1zaGFwZSBhdCB0aGUgbWF0Y2hpbmdcbiAgICogcmVuZGVyZWQgc2hhcGUgc28gaXRzIGVkaXQgaGFuZGxlcyBhcHBlYXIuXG4gICAqL1xuICBwdWJsaWMgc2VsZWN0UmVnaW9uKHJlZ2lvbjogUmVnaW9uKTogdm9pZCB7XG4gICAgdGhpcy5yZWdpb25TdG9yZS5zZWxlY3RSZWdpb24ocmVnaW9uKTtcbiAgICBpZiAodGhpcy5wbG90RGl2KSB7XG4gICAgICBjb25zdCBpZHggPSB0aGlzLnNoYXBlcy5maW5kSW5kZXgocyA9PiBzLmlkID09PSByZWdpb24/LmlkKTtcbiAgICAgIGNvbnN0IGdkOiBhbnkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0aGlzLnBsb3REaXYpO1xuICAgICAgaWYgKGdkPy5fZnVsbExheW91dCAmJiBpZHggPj0gMCkge1xuICAgICAgICBnZC5fZnVsbExheW91dC5fYWN0aXZlU2hhcGVJbmRleCA9IGlkeDtcbiAgICAgICAgdHJ5IHsgKFBsb3RseSBhcyBhbnkpLnJlZHJhdyhnZCk7IH0gY2F0Y2ggeyAvKiBub29wIGluIHRlc3RzICovIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVhZCBQbG90bHkncyBgX2FjdGl2ZVNoYXBlSW5kZXhgIGFuZCBwdXNoIGl0IGFzIHRoZSBzZWxlY3Rpb24uIENhbGxlZCBmcm9tXG4gICAqIHRoZSByZWxheW91dCBoYW5kbGVyIGFuZCB0aGUgcG9zdC1jbGljayB0aWNrIOKAlCBQbG90bHkgZG9lc24ndCBmaXJlIGFcbiAgICogZGVkaWNhdGVkIGFjdGl2ZS1zaGFwZSBldmVudCBmb3IgY2xpY2tzLCBzbyB3ZSBzYW1wbGUuIFRoZSBzdG9yZSBuby1vcHMgaWZcbiAgICogdGhlIHNlbGVjdGlvbiBpcyB1bmNoYW5nZWQuXG4gICAqL1xuICBwcml2YXRlIG1heWJlRW1pdEFjdGl2ZVNoYXBlSW5kZXgoKSB7XG4gICAgaWYgKCF0aGlzLnBsb3REaXYpIHJldHVybjtcbiAgICBjb25zdCBnZDogYW55ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2KTtcbiAgICBjb25zdCByYXcgPSBnZD8uX2Z1bGxMYXlvdXQ/Ll9hY3RpdmVTaGFwZUluZGV4O1xuICAgIGNvbnN0IGlkeCA9ICh0eXBlb2YgcmF3ID09PSAnbnVtYmVyJyAmJiByYXcgPj0gMCAmJiByYXcgPCB0aGlzLnNoYXBlcy5sZW5ndGgpID8gcmF3IDogLTE7XG4gICAgdGhpcy5yZWdpb25TdG9yZS5zZXRTZWxlY3RlZFNoYXBlSW5kaWNlcyhpZHggPj0gMCA/IFtpZHhdIDogW10pO1xuICB9XG4gIHNldFpJbmRleCh6SW5kZXg6IG51bWJlcikge1xuICAgIHRoaXMuekluZGV4Lm5leHQoekluZGV4KTtcbiAgfVxuXG4gIGdldFNoYXBlQ29sb3IoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0U2hhcGVDb2xvcigpO1xuICB9XG4gIGdldEZpbGxDb2xvcigpIHtcbiAgICByZXR1cm4gdGhpcy5yZWdpb25TdG9yZS5nZXRGaWxsQ29sb3IoKTtcbiAgfVxuXG4gIGdldENsYXNzaWZpY2F0aW9uQ29sb3JzKCk6IE1hcDxzdHJpbmcsIHN0cmluZz4ge1xuICAgIHJldHVybiB0aGlzLnN0b3JlLmdldENsYXNzaWZpY2F0aW9uQ29sb3JzKCk7XG4gIH1cblxuICBzZXRDbGFzc2lmaWNhdGlvbkNvbG9yKGxhYmVsOiBzdHJpbmcsIGNvbG9yOiBzdHJpbmcpIHtcbiAgICB0aGlzLnN0b3JlLnNldENsYXNzaWZpY2F0aW9uQ29sb3IobGFiZWwsIGNvbG9yKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBVbnN1YnNjcmliZSBTdWJzY3JpcHRpb25zXG4gICAqL1xuICB1bnN1YnNjcmliZSgpIHtcbiAgICBpZiAodGhpcy5pbWFnZUNhY2hlZFN1YnNjcmlwdGlvbikge1xuICAgICAgdGhpcy5pbWFnZUNhY2hlZFN1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5maWxlbmFtZVN1YnNjcmlwdGlvbikge1xuICAgICAgdGhpcy5maWxlbmFtZVN1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5yZWdpb25VcGRhdGVTdWJzY3JpcHRpb24pIHtcbiAgICAgIHRoaXMucmVnaW9uVXBkYXRlU3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG4gICAgfVxuICAgIGlmICh0aGlzLnJlZ2lvbkxpdmVFZGl0U3Vic2NyaXB0aW9uKSB7XG4gICAgICB0aGlzLnJlZ2lvbkxpdmVFZGl0U3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG4gICAgfVxuICAgIGlmICh0aGlzLmNoYW5uZWxTdWIpIHtcbiAgICAgIHRoaXMuY2hhbm5lbFN1Yi51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBMaXZlLWFwcGx5IHRoZSBjaGFubmVsIGRpc3BsYXkgd2luZG93ICh6bWluL3ptYXgpICsgcmV2ZXJzZS9pbnZlcnQgdG8gdGhlXG4gICAqICBoZWF0bWFwLiBOby1vcCB3aGVuIG5vIFBsb3RseSBwbG90IGlzIG1vdW50ZWQgKE9TRCBvd25zIHRoZSBkaXYpLiAqL1xuICBwcml2YXRlIGFwcGx5Q2hhbm5lbERpc3BsYXkoY2hhbm5lbHM6IGFueVtdLCByZXY6IGJvb2xlYW4sIGludjogYm9vbGVhbik6IHZvaWQge1xuICAgIGlmICghdGhpcy5wbG90RGl2KSByZXR1cm47XG4gICAgY29uc3QgY2ggPSBjaGFubmVscz8uWzBdO1xuICAgIGNvbnN0IGNvbG9yc2NhbGUgPSB0aGlzLnN0b3JlLmN1cnJlbnRDb2xvcm1hcCgpPy5kYXRhPy52YWx1ZTtcbiAgICAvLyByZXZlcnNlLXNjYWxlIGFuZCBpbnZlcnQgZWFjaCBmbGlwIHRoZSByYW1wOyBib3RoIHRvZ2V0aGVyIGNhbmNlbC5cbiAgICBjb25zdCB1cGRhdGU6IGFueSA9IHsgcmV2ZXJzZXNjYWxlOiByZXYgIT09IGludiB9O1xuICAgIGlmIChjb2xvcnNjYWxlICE9IG51bGwpIHVwZGF0ZS5jb2xvcnNjYWxlID0gY29sb3JzY2FsZTsgLy8gbGl2ZSBjb2xvcm1hcCByZWNvbG9yXG4gICAgaWYgKGNoKSB7XG4gICAgICAvLyBTdXJmYWNlL2lzb3N1cmZhY2Uvc2NhdHRlcjNkIGNvbG91ci1tYXAgdmlhIGNtaW4vY21heDsgaGVhdG1hcC9jb250b3VyIHZpYSB6bWluL3ptYXguXG4gICAgICBjb25zdCB0aHJlZUQgPVxuICAgICAgICB0aGlzLnBsb3RUeXBlID09PSBQbG90VHlwZS5JU09TVVJGQUNFIHx8XG4gICAgICAgIHRoaXMucGxvdFR5cGUgPT09IFBsb3RUeXBlLlNVUkZBQ0UgfHxcbiAgICAgICAgdGhpcy5wbG90VHlwZSA9PT0gUGxvdFR5cGUuU0NBVFRFUjNEO1xuICAgICAgaWYgKHRocmVlRCkge1xuICAgICAgICB1cGRhdGUuY21pbiA9IGNoLm1pbjtcbiAgICAgICAgdXBkYXRlLmNtYXggPSBjaC5tYXg7XG4gICAgICAgIHVwZGF0ZS5jYXV0byA9IGZhbHNlO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdXBkYXRlLnptaW4gPSBjaC5taW47XG4gICAgICAgIHVwZGF0ZS56bWF4ID0gY2gubWF4O1xuICAgICAgICB1cGRhdGUuemF1dG8gPSBmYWxzZTtcbiAgICAgIH1cbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIFBsb3RseS5yZXN0eWxlKHRoaXMucGxvdERpdiwgdXBkYXRlKTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIC8qIG5vdCBhIGNvbG91ci1tYXBwZWQgdHJhY2UsIG9yIE9TRCBvd25zIHRoZSBkaXYgKi9cbiAgICB9XG4gIH1cblxuICAvKiogQmlubmVkIGludGVuc2l0eSBoaXN0b2dyYW0gZm9yIGEgY2hhbm5lbCBmcm9tIHRoZSBjYWNoZWQgc291cmNlIGZyYW1lc1xuICAgKiAgKHJhdywgcHJlLUxVVCkuIEdyYXlzY2FsZSBjZWxscyBhcmUgbnVtYmVyczsgUkdCIGNlbGxzIGFyZSBbcixnLGJdLiAqL1xuICBnZXRIaXN0b2dyYW0oY2hhbm5lbEluZGV4OiBudW1iZXIsIF9iaW5zOiBudW1iZXIpOiBJSGlzdG9ncmFtIHwgbnVsbCB7XG4gICAgY29uc3QgZnJhbWVzID0gdGhpcy5jYWNoZWRJbWFnZUZyYW1lcztcbiAgICBpZiAoIWZyYW1lcz8ubGVuZ3RoKSByZXR1cm4gbnVsbDtcbiAgICBjb25zdCBmcmFtZSA9IGZyYW1lc1t0aGlzLmFjdGl2ZUZyYW1lSW5kZXgoKV0gPz8gZnJhbWVzWzBdO1xuICAgIGlmICghZnJhbWU/Lmxlbmd0aCkgcmV0dXJuIG51bGw7XG4gICAgY29uc3QgY291bnRzID0gbmV3IEFycmF5KDI1NikuZmlsbCgwKTtcbiAgICBmb3IgKGNvbnN0IHJvdyBvZiBmcmFtZSkge1xuICAgICAgaWYgKCFyb3cpIGNvbnRpbnVlO1xuICAgICAgZm9yIChjb25zdCBjZWxsIG9mIHJvdykge1xuICAgICAgICBsZXQgdjogbnVtYmVyO1xuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShjZWxsKSkge1xuICAgICAgICAgIHYgPSBjaGFubmVsSW5kZXggPj0gMCAmJiBjaGFubmVsSW5kZXggPCBjZWxsLmxlbmd0aFxuICAgICAgICAgICAgPyBjZWxsW2NoYW5uZWxJbmRleF1cbiAgICAgICAgICAgIDogTWF0aC5yb3VuZChidDYwMUx1bWluYW5jZShjZWxsWzBdLCBjZWxsWzFdLCBjZWxsWzJdKSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdiA9IGNlbGw7XG4gICAgICAgIH1cbiAgICAgICAgdiA9IHYgfCAwO1xuICAgICAgICBpZiAodiA8IDApIHYgPSAwOyBlbHNlIGlmICh2ID4gMjU1KSB2ID0gMjU1O1xuICAgICAgICBjb3VudHNbdl0rKztcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGhpc3RvZ3JhbTI1Nihjb3VudHMpO1xuICB9XG5cbiAgLyoqIEFzeW5jIGhpc3RvZ3JhbSBzdHJlYW0g4oCUIFBsb3RseSByZW5kZXJzIGhlYXRtYXAgZnJhbWUgZGF0YSAoYWxyZWFkeSBpblxuICAgKiAgbWVtb3J5LCA4LWJpdCBsdW1pbmFuY2UpLCBzbyB0aGlzIGp1c3Qgd3JhcHMgdGhlIHN5bmNocm9ub3VzIDgtYml0XG4gICAqICBoaXN0b2dyYW0uIE5hdGl2ZSAxNi1iaXQgaGlzdG9ncmFtcyBvbmx5IGFwcGx5IHRvIHRoZSBPU0QgdGlsZSBwYXRoLiAqL1xuICBnZXRIaXN0b2dyYW0kKGNoYW5uZWxJbmRleDogbnVtYmVyLCBiaW5zOiBudW1iZXIpOiBPYnNlcnZhYmxlPElIaXN0b2dyYW0gfCBudWxsPiB7XG4gICAgcmV0dXJuIG9mKHRoaXMuZ2V0SGlzdG9ncmFtKGNoYW5uZWxJbmRleCwgYmlucykpO1xuICB9XG5cbiAgLyoqIERhdGEgZXhwb3J0ICgxNi1iaXQgVElGRikgaXMgYW4gT1NELXRpbGUtcGF0aCBmZWF0dXJlIGJhY2tlZCBieSB0aGUgc2VydmVyLlxuICAgKiAgUGxvdGx5IHJlbmRlcnMgaGVhdG1hcHMgZnJvbSBmcmFtZSBkYXRhLCBzbyB0aGVyZSdzIG5vdGhpbmcgdG8gZXhwb3J0IGhlcmUuICovXG4gIGV4cG9ydERhdGEoKTogdm9pZCB7XG4gICAgY29uc29sZS53YXJuKCdbcGxvdGx5XSAxNi1iaXQgZGF0YSBleHBvcnQgaXMgbm90IGF2YWlsYWJsZSBmb3IgdGhlIGhlYXRtYXAgYmFja2VuZC4nKTtcbiAgfVxuXG4gIGltcG9ydFJlZ2lvbnMoZ2VvSnNvblN0cjogc3RyaW5nKTogUmVnaW9uW10ge1xuICAgIHJldHVybiB0aGlzLnBsb3RVdGlsaXRpZXMuaW1wb3J0Uk9Jc0Zyb21HZW9Kc29uKGdlb0pzb25TdHIpO1xuICB9XG5cbiAgZXhwb3J0UmVnaW9ucyhyZWdpb25zOiBSZWdpb25bXSkge1xuICAgIGNvbnN0IGpzb25TdHJpbmcgPSB0aGlzLnBsb3RVdGlsaXRpZXMuZXhwb3J0Uk9Jc1RvR2VvSnNvbihyZWdpb25zKTtcbiAgICB0aGlzLnBsb3RVdGlsaXRpZXMuc2F2ZVRvRmlsZShqc29uU3RyaW5nLCB0aGlzLmZpbGVOYW1lKTtcbiAgfVxuXG4gIGdldEdlb0pzb25TdHJpbmcocmVnaW9uczogUmVnaW9uW10pOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLnBsb3RVdGlsaXRpZXMuZXhwb3J0Uk9Jc1RvR2VvSnNvbihyZWdpb25zKTtcbiAgfVxuXG4gIGdldFN0YWNrTG9hZGluZ1Byb2dyZXNzKCkge1xuICAgIHJldHVybiB0aGlzLmdldFN0YWNrTG9hZGluZ1Byb2dyZXNzJCgpO1xuICB9XG5cbiAgaXNTdGFja0xvYWRpbmcoKSB7XG4gICAgcmV0dXJuIHRoaXMuaXNTdGFja0xvYWRpbmckKCk7XG4gIH1cblxufVxuIl19