@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,2283 @@
1
+ import { Inject, Injectable, Optional } from '@angular/core';
2
+ import { BehaviorSubject, Subject, combineLatest, from, of } from 'rxjs';
3
+ import { saveAs } from 'file-saver';
4
+ import { Viewer, histogramScalar, colormapFromLut, tintColormap, reverseColormap, heightField, MultiChannelImageView, MultiChannelVolumeView, } from 'napari-js';
5
+ import { buildColormapLut } from '../../contracts/colormap-lut';
6
+ import { PlotType, PLOT_TYPE_DESCRIPTORS, isNapari3d, isNapariIsosurface, isNapariSurface, isNapariScatter, isNapariScatter3d, NAPARI_DEFAULT_DECIMATE, } from '../../contracts/plot-type';
7
+ import { ViewerFeature, capabilitiesOf, } from '../../contracts/capabilities.contract';
8
+ import { VIZ_CONFIG } from '../../contracts/viz-config';
9
+ import { TILE_ACCESS_PORT } from '../../contracts/ports/tile-access.port';
10
+ import { BaseStoreVisualizer } from '../base-store-visualizer';
11
+ import { SimpleSliceAccessService } from '../simple-slice-access.service';
12
+ import { VisualizerStore } from '../../store/visualizer-store.service';
13
+ import { RegionStore } from '../../store/region-store.service';
14
+ import { NapariScaleBar, formatUm } from './napari-scale-bar';
15
+ import { NapariRegionOverlay } from './napari-region-overlay';
16
+ import { NapariAxesLabels } from './napari-axes-labels';
17
+ import { NapariVolumeZHandle } from './napari-volume-z-handle';
18
+ import { WandToolService } from '../../toolbar/wand/wand-tool.service';
19
+ import { BrushToolService } from '../../toolbar/brush/brush-tool.service';
20
+ import { VertexEraserToolService, } from '../../toolbar/vertex-eraser/vertex-eraser-tool.service';
21
+ import { ZoomToBoxToolService, } from '../../toolbar/zoom-to-box/zoom-to-box-tool.service';
22
+ import { SamToolService } from '../../toolbar/segmentation/sam-tool.service';
23
+ import { SamPointToolService } from '../../toolbar/segmentation/sam-point-tool.service';
24
+ import { CellSegmentToolService } from '../../toolbar/segmentation/cell-segment-tool.service';
25
+ import { CELL_SEGMENTER } from '../../contracts/cell-segmenter.contract';
26
+ import * as i0 from "@angular/core";
27
+ import * as i1 from "../../store/visualizer-store.service";
28
+ import * as i2 from "../../store/region-store.service";
29
+ import * as i3 from "../../toolbar/wand/wand-tool.service";
30
+ import * as i4 from "../../toolbar/brush/brush-tool.service";
31
+ import * as i5 from "../../toolbar/vertex-eraser/vertex-eraser-tool.service";
32
+ import * as i6 from "../../toolbar/zoom-to-box/zoom-to-box-tool.service";
33
+ import * as i7 from "../../toolbar/segmentation/sam-tool.service";
34
+ import * as i8 from "../../toolbar/segmentation/sam-point-tool.service";
35
+ import * as i9 from "../../toolbar/segmentation/cell-segment-tool.service";
36
+ import * as i10 from "../simple-slice-access.service";
37
+ /** ICoordinateTransform over the napari camera: pointer client coords ↔ image/world coords. */
38
+ class NapariCoordinateTransform {
39
+ constructor(viewer, ready) {
40
+ this.viewer = viewer;
41
+ this.ready = ready;
42
+ }
43
+ clientToData(clientX, clientY) {
44
+ const [x, y] = this.viewer.canvasToWorld(clientX, clientY);
45
+ return { x, y };
46
+ }
47
+ dataLengthToScreen(dataLength) {
48
+ return dataLength * this.viewer.camera.zoom; // CSS px per world unit
49
+ }
50
+ isReady() {
51
+ return this.ready();
52
+ }
53
+ }
54
+ const VOLUME_MAX_SLICE = 1024; // "Full" in-plane cap; the default ¼ load uses 256
55
+ /** Reference in-plane world size (long side) for the volume/axes box, in arbitrary world units.
56
+ * The box is anchored to this reference regardless of the chosen decimate factor, so changing the
57
+ * resolution changes DETAIL, not the volume's proportions (Z no longer appears to shrink when the
58
+ * in-plane sampling grows). Set to the DEFAULT decimate's in-plane cap so the default view is
59
+ * unchanged; higher/lower resolutions keep that same shape. See {@link mountVolume}. */
60
+ const VOLUME_WORLD_INPLANE_REF = VOLUME_MAX_SLICE / NAPARI_DEFAULT_DECIMATE;
61
+ /** Max concurrent slice fetches when assembling a volume — keeps the connection pool busy
62
+ * without flooding it (browsers cap ~6/host) on a deep stack. */
63
+ const VOLUME_FETCH_CONCURRENCY = 8;
64
+ /** Volume/isosurface sampling for a decimate factor `scale` (1 = Full 1024, 2 = ½ 512, 4 = ¼ 256
65
+ * default, 8 = ⅛ 128): the in-plane cap halves each step; slices stay un-subsampled until ⅛, then
66
+ * subsample. */
67
+ function volumeResolutionFor(scale) {
68
+ const s = Math.max(1, Math.round(scale));
69
+ return {
70
+ maxSlice: Math.max(8, Math.round(VOLUME_MAX_SLICE / s)),
71
+ sliceStep: Math.max(1, Math.floor(s / 4)),
72
+ };
73
+ }
74
+ /** Fully-normalized intensity height as a fraction of the in-plane extent — matches the Plotly
75
+ * SURFACE z-aspect (~0.4) so the relief isn't exaggerated. */
76
+ const SURFACE_Z_ASPECT = 0.4;
77
+ /** "Full" mesh grid cap; the decimate factor divides it (default ¼ → 220, ½ → 440, Full → 880). */
78
+ const SURFACE_MAX_GRID = 880;
79
+ /** Coefficient scaling the pyramid tile budget with the target resolution — a higher target pulls a
80
+ * finer pyramid level (more real detail). Shared by the surface plane fetch + volume assembly. */
81
+ const STITCH_BUDGET_COEFF = 16;
82
+ /** Surface mesh target grid for a decimate factor `scale`: the grid cap shrinks by `scale` (every
83
+ * slice is kept — the z-slider needs them all). The source is fetched at a matching resolution. */
84
+ function surfaceResolutionFor(scale) {
85
+ const s = Math.max(1, Math.round(scale));
86
+ return { maxGrid: Math.max(16, Math.round(SURFACE_MAX_GRID / s)) };
87
+ }
88
+ /** 3D scatter: in-plane cap for the assembled voxel grid, and the max number of points emitted
89
+ * (the grid is flat-strided down to this) — keeps the billboard count interactive. */
90
+ const SCATTER3D_MAX_XY = 64;
91
+ const SCATTER3D_MAX_POINTS = 150000;
92
+ const TILE_SIZE = 512; // server tile edge (matches the OSD backend)
93
+ /** Max tiles stitched for one displayed slice (512px tiles → up to ~6144² at full res). Beyond
94
+ * this we step to a coarser pyramid level so a large image stays tractable. */
95
+ const MAX_STITCH_TILES = 144;
96
+ /** WebGPU default `maxTextureDimension2D` — a stitched slice's longest side must fit a texture. */
97
+ const MAX_TEXTURE_DIM = 8192;
98
+ /** Concurrent tile requests per stitched slice. Firing a whole grid at once (hundreds of requests
99
+ * for a large level) overwhelmed the tile server (504s); a small pool keeps the pipe full without
100
+ * flooding it — and slice-level workers already run several stitches in parallel on top of this. */
101
+ const TILE_FETCH_CONCURRENCY = 6;
102
+ /** How long to poll `/tiles/info` (202 while the server caches the source) before falling back to
103
+ * a single tile. Generous like the OSD backend — a cold whole-slide can take minutes to cache. */
104
+ const DESCRIPTOR_TIMEOUT_MS = 120000;
105
+ /** Default per-channel tints (Fiji-style) when the store/descriptor offers no colour. */
106
+ const DEFAULT_TINTS = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff', '#ffffff'];
107
+ /** Default tint for a channel index, cycling the Fiji palette. */
108
+ function tintFor(channel) {
109
+ return DEFAULT_TINTS[channel % DEFAULT_TINTS.length];
110
+ }
111
+ /**
112
+ * A WebGPU image backend built on the published `napari-js` library — the POC engine for
113
+ * jit-ui#102 (a browser-based napari shipped as a JS library, swapping image plotting with
114
+ * OpenSeadragon and 3D slicing/isosurfaces with Plotly).
115
+ *
116
+ * Render strategy: rather than re-implement the server's tile/pyramid protocol (which OSD
117
+ * already handles), this backend renders the **complete per-slice image URLs the app already
118
+ * produces** (`IImageInfo.urls`) — `urls[z]` for the 2D image, and the full `urls` stack
119
+ * assembled into a downsampled volume for the 3D types. Region state and display options
120
+ * delegate to the shared {@link RegionStore} / {@link VisualizerStore}, exactly as OSD does.
121
+ *
122
+ * Follow-ups (jit-ui#102): native-resolution pyramidal tiling, on-canvas tools, region
123
+ * overlay rendering, per-channel histograms, TIFF export.
124
+ */
125
+ export class NapariVisualizerService extends BaseStoreVisualizer {
126
+ constructor(tiles, store, regionStore, wandTool, brushTool, eraserTool, zoomToBoxTool, samTool, samPointTool, cellSegmentTool, cellSegmenter, simpleStack, config) {
127
+ super(regionStore, store);
128
+ this.tiles = tiles;
129
+ this.wandTool = wandTool;
130
+ this.brushTool = brushTool;
131
+ this.eraserTool = eraserTool;
132
+ this.zoomToBoxTool = zoomToBoxTool;
133
+ this.samTool = samTool;
134
+ this.samPointTool = samPointTool;
135
+ this.cellSegmentTool = cellSegmentTool;
136
+ this.cellSegmenter = cellSegmenter;
137
+ this.simpleStack = simpleStack;
138
+ this.capabilities = capabilitiesOf([
139
+ ViewerFeature.ImageDisplay,
140
+ ViewerFeature.StackSlider,
141
+ ViewerFeature.PixelReadback,
142
+ ViewerFeature.Surface3D,
143
+ ViewerFeature.Isosurface,
144
+ ]);
145
+ this.viewer = null;
146
+ this.canvas = null;
147
+ this.host = null;
148
+ this.loaded = null;
149
+ this.lastPixels = null;
150
+ this.currentPlotType = PlotType.NAPARI_IMAGE;
151
+ /** napari-js high-level view owning the 3D volume layers (one additive tinted layer per channel
152
+ * for multichannel, or a single grayscale volume). Null in 2D. */
153
+ this.volumeView = null;
154
+ /** True when the volume is composited from per-channel layers (vs a single grayscale volume). */
155
+ this.volumeMultichannel = false;
156
+ /** napari-js height-field surface mesh (NAPARI_SURFACE plot type; null otherwise). Built from a
157
+ * single grayscale slice by {@link buildSurface} via napari-js's `heightField` + `addSurface`. */
158
+ this.surfaceLayer = null;
159
+ /** Which band the surface samples (a channel index for multichannel, else the composite). */
160
+ this.surfaceChannel = undefined;
161
+ /** Pre-loaded per-slice luminance planes (already decimated to the surface grid), keyed by z,
162
+ * so the stack slider rebuilds the surface instantly. Filled by {@link preloadSurfacePlanes}. */
163
+ this.surfacePlanes = new Map();
164
+ /** In-plane grid cap for the active surface load (full grid ÷ the decimate factor). */
165
+ this.surfaceMaxGrid = SURFACE_MAX_GRID;
166
+ /** Contrast window [min,max] the current surface mesh was built with. A change reshapes the
167
+ * mesh (pixel height = intensity within [min,max]), so it triggers a geometry rebuild. */
168
+ this.surfaceWindow = null;
169
+ /** Persisted wireframe choice for the surface (re-applied when a new surface mounts). */
170
+ this.surfaceWireframe = false;
171
+ /** napari-js 2D scatter points (region centroids) + its region-change subscription. */
172
+ this.scatter2dPoints = null;
173
+ this.scatterRegionSub = null;
174
+ /** napari-js 3D scatter (voxel point cloud). */
175
+ this.scatter3dLayer = null;
176
+ /** Monotonic load generation. Bumped by {@link reset} and {@link cancelLoading}; the frame-loading
177
+ * loops (volume assembly, surface preload) capture it and bail when it changes, so a Cancel (or a
178
+ * new plot) actually stops fetching frames instead of running to completion in the background. */
179
+ this.loadToken = 0;
180
+ /** Decimate factor for the napari 3D types (1 = Full, 2 = ½ default, 4 = ¼, 8 = ⅛). Applied when a
181
+ * volume/isosurface/surface (re)loads; changing it needs a re-plot (it changes fetched data). */
182
+ this.resolutionScale = NAPARI_DEFAULT_DECIMATE;
183
+ /** 3D coordinate-axes / scale gizmo for the volume/isosurface view (null in 2D). */
184
+ this.axesLayer = null;
185
+ /** DOM X/Y/Z + scale labels tracking the 3D axes gizmo (null in 2D). */
186
+ this.axesLabels = null;
187
+ /** Draggable Z-height grip over the volume (null unless a volume/isosurface is mounted). */
188
+ this.zHandle = null;
189
+ /** User Z-height factor for the volume (1 = the volume's natural proportions); driven by the
190
+ * in-view drag handle. Reset per mount. */
191
+ this.volumeZScale = 1;
192
+ /** Volume world box at `volumeZScale = 1` (base) + the sampled voxel depth — enough to recompute
193
+ * the Z-axis voxel scale, axes depth, and overlay anchors as the handle stretches Z. */
194
+ this.volumeWorldBase = null;
195
+ this.volumeSampledDepth = 1;
196
+ /** Persisted axes on/off choice, re-applied when a new volume mounts. Defaults on. */
197
+ this.axesVisible = true;
198
+ this.volumeDims = null;
199
+ /** Assembled uint8 volume data per channel (key = channel index), kept for the volume intensity
200
+ * histogram. Key 0 holds the grayscale/composite volume in the single-channel case. */
201
+ this.volumeChannelData = new Map();
202
+ this.imageW = 0;
203
+ this.imageH = 0;
204
+ /** Monotonic slice-request id so a slow out-of-order slice fetch can't clobber a newer one. */
205
+ this.sliceReq = 0;
206
+ /** Cached `/tiles/info` pyramid descriptor + the infoB64 it was fetched for. */
207
+ this.descriptor = null;
208
+ this.descriptorKey = null;
209
+ /** How the current 2D image is composited (drives histogram + state application). */
210
+ this.imageMode = 'rgb';
211
+ /** napari-js high-level view that owns the per-channel layer set (build + live display
212
+ * updates) for the current {@link imageMode}. Rebuilt on each (re)render. */
213
+ this.channelView = null;
214
+ /** Live subscription applying channel-state / colormap changes to the layers. */
215
+ this.displaySub = null;
216
+ /** Latest grayscale colormap selection from the store (applied to the single gray layer). */
217
+ this.currentColormap = null;
218
+ this.currentReverse = false;
219
+ /** Latest invert toggle from the store (flips intensity before the colormap, like OSD). */
220
+ this.invertEnabled = false;
221
+ /** Physical scale bar overlay for the 2D image (null when 3D or the image has no µm/pixel). */
222
+ this.scaleBar = null;
223
+ /** SVG region-drawing overlay for the 2D image (null until a 2D image is plotted). */
224
+ this.regionOverlay = null;
225
+ /** Pixel-tool plumbing: the plot div id, coord transform, and bound tool hosts. */
226
+ this.plotDivId = '';
227
+ this.coordTransform = null;
228
+ this.wandHost = null;
229
+ this.eraserHost = null;
230
+ /** Cached CachedImageData built from the last readback (rebuilt when lastPixels changes). */
231
+ this.cachedImage = null;
232
+ this.cachedImageSource = null;
233
+ /** Visible world rect captured AT the last readback — must pair with `lastPixels` so the pixel
234
+ * tools' ratios/origin match the matrix's camera (using the live rect after a pan/zoom would
235
+ * mis-scale the region). */
236
+ this.lastPixelsRect = null;
237
+ /** Image smoothing (bilinear) vs nearest-neighbour (crisp pixels, the default). */
238
+ this.imageSmoothing = false;
239
+ /** True when the 2D image is rendered via pyramidal TiledSources (descriptor available). */
240
+ this.tiled = false;
241
+ /** Debounced readback timer + camera-change unsubscribe (keep lastPixels current for tools). */
242
+ this.readbackTimer = null;
243
+ this.cameraReadbackOff = null;
244
+ /** Coarse per-channel luminance sample (keyed by channel index) for the histogram in tiled mode,
245
+ * where the layers have no full in-memory pixels. Refreshed on plot + slice change. */
246
+ this.histSamples = new Map();
247
+ /** Native-bit-depth histograms from `/histogram`, keyed `${z}|${channel}` (>8-bit images). */
248
+ this.nativeHistograms = new Map();
249
+ this.stackLoading$ = new BehaviorSubject(false);
250
+ this.stackLoadingProgress$ = new BehaviorSubject(0);
251
+ this.autoscaleEvent$ = new Subject();
252
+ this.intensityProfile$ = new Subject();
253
+ this.viewportChange$ = new Subject();
254
+ this.api = config.slideCropServer;
255
+ }
256
+ /**
257
+ * Fetch + cache the server pyramid descriptor (`GET /tiles/info`): the REAL per-level tile grid,
258
+ * tile size, channel metadata and physical pixel size. The backend returns 202 while the source
259
+ * is still caching, so we poll briefly. Cached per `infoB64`; returns null if it never becomes
260
+ * ready (callers fall back to a single-tile fetch). This is the authoritative grid — guessing
261
+ * level dims from `trueImageSize` overshoots the real grid and the server 400s out-of-range tiles.
262
+ */
263
+ async ensureDescriptor() {
264
+ // Self-contained multi-slice stack (no tile server — e.g. a numbered image
265
+ // series assembled client-side, each slice a different file): there is no
266
+ // single server-tiled pyramid to describe. Returning null routes every
267
+ // caller (2D image, volume, surface) to their stitched/single-fetch
268
+ // fallback, which — via fetchSlice's own SimpleSliceAccessService branch —
269
+ // correctly fetches each slice's own URL instead of one fixed file's tile
270
+ // pyramid.
271
+ if (this.simpleStack.isSimple(this.loaded?.imageInfo))
272
+ return null;
273
+ const infoB64 = this.tiles.getSelectedInfoB64();
274
+ if (!infoB64)
275
+ return null;
276
+ if (this.descriptor && this.descriptorKey === infoB64)
277
+ return this.descriptor;
278
+ const headers = await this.tiles
279
+ .getAuthHeaders()
280
+ .catch(() => ({}));
281
+ const url = `${this.api}tiles/info?info=${infoB64}`;
282
+ const deadline = Date.now() + DESCRIPTOR_TIMEOUT_MS;
283
+ for (;;) {
284
+ try {
285
+ const resp = await fetch(url, { headers });
286
+ if (resp.status === 200) {
287
+ const body = (await resp.json());
288
+ if (body?.levels?.length) {
289
+ this.descriptor = body;
290
+ this.descriptorKey = infoB64;
291
+ return body;
292
+ }
293
+ }
294
+ // 202 (still caching) or empty body → re-poll until the deadline.
295
+ }
296
+ catch (err) {
297
+ console.warn('[napari-js] tiles/info poll retry', err);
298
+ }
299
+ if (Date.now() > deadline) {
300
+ console.warn('[napari-js] tiles/info not ready; falling back to single-tile fetch');
301
+ return null;
302
+ }
303
+ await new Promise((r) => setTimeout(r, 1200));
304
+ }
305
+ }
306
+ /**
307
+ * Fetch a COMPLETE rendered slice as an `ImageBitmap` by stitching the server's REAL tile grid
308
+ * (from `/tiles/info`) — not just the top-left tile, which only ever showed a large image's
309
+ * corner. Picks the finest pyramid level whose grid fits `budgetTiles` and whose longest side is
310
+ * within the GPU texture limit, fetches that grid concurrently, and stitches it into one canvas.
311
+ *
312
+ * `channel` selects a single band as grayscale (real levels only); omit for the server composite.
313
+ * `budgetTiles` caps the grid: the 2D view uses the full budget for detail; the 3D volume passes
314
+ * 1 for a cheap overview tile per slice (it downsamples to {@link VOLUME_MAX_SLICE}). Falls back
315
+ * to a single `col=0,row=0` tile when no descriptor is available (small/simple images, volumes).
316
+ */
317
+ async fetchSlice(z, channel, budgetTiles = MAX_STITCH_TILES,
318
+ /** When a specific channel has no pyramid level within `budgetTiles`, drop to the server
319
+ * COMPOSITE overview instead of stitching the huge per-channel level. Only safe when the caller
320
+ * wants a single decimated plane (the surface height): for a multichannel VOLUME every channel
321
+ * would then fetch the same composite and the channels would collapse into one, so volume
322
+ * assembly leaves this `false` to keep each band distinct. */
323
+ allowCompositeFallback = false) {
324
+ // Self-contained multi-slice stack (tiled:false): `z` indexes a completely
325
+ // different file's own preview URL, not an internal slice of one server-
326
+ // tiled file — resolved and fetched via SimpleSliceAccessService (shared
327
+ // with OSD; see its docs for why this can't be a bare fetch()) instead of
328
+ // building a /tile?info=...&z= request against whichever single file
329
+ // getSelectedInfoB64() points at (which would 400/mismatch for any z
330
+ // beyond that one file's own extent).
331
+ const info = this.loaded?.imageInfo;
332
+ if (this.simpleStack.isSimple(info)) {
333
+ const url = this.simpleStack.urlFor(info, z);
334
+ if (!url)
335
+ throw new Error(`[napari-js] no URL for slice ${z}`);
336
+ return this.simpleStack.fetchAsBitmap(url);
337
+ }
338
+ const infoB64 = this.tiles.getSelectedInfoB64();
339
+ if (!infoB64)
340
+ throw new Error('[napari-js] no selected image info (getSelectedInfoB64 null)');
341
+ const headers = await this.tiles
342
+ .getAuthHeaders()
343
+ .catch(() => ({}));
344
+ const desc = await this.ensureDescriptor();
345
+ // The requested band; may be dropped to the composite (undefined) below when the channel has no
346
+ // pyramid level small enough to stitch within budget.
347
+ let effectiveChannel = channel;
348
+ const fetchTile = async (res, col, row, t) => {
349
+ const ch = effectiveChannel == null ? '' : `&channel=${effectiveChannel}`;
350
+ const url = `${this.api}tile?info=${infoB64}&res=${res}&col=${col}&row=${row}&z=${z}&tileSize=${t}${ch}`;
351
+ const resp = await fetch(url, { headers });
352
+ if (!resp.ok) {
353
+ throw new Error(`[napari-js] slice fetch failed: ${resp.status} (${col}/${row} res ${res})`);
354
+ }
355
+ return createImageBitmap(await resp.blob());
356
+ };
357
+ // No descriptor → single top-left tile (legacy fallback; correct for small/simple images).
358
+ if (!desc || !desc.levels?.length)
359
+ return fetchTile(0, 0, 0, TILE_SIZE);
360
+ const t = desc.tileSize || TILE_SIZE;
361
+ // Per-channel tiles exist ONLY at REAL Bio-Formats levels (the front of `levels`); the server
362
+ // composite exists at every level, including the small overviews.
363
+ const perChannelLevels = desc.realLevels ?? desc.levels.length;
364
+ const usable = channel == null ? desc.levels : desc.levels.slice(0, Math.max(1, perChannelLevels));
365
+ // Finest level whose stitched grid fits BOTH the tile budget and the GPU texture limit; if none
366
+ // fits, the coarsest available (fits=false).
367
+ const tilesFor = (lvl) => Math.max(1, Math.ceil(lvl.width / t)) * Math.max(1, Math.ceil(lvl.height / t));
368
+ const pick = (levels) => {
369
+ let c = levels[0];
370
+ for (const lvl of levels) {
371
+ c = lvl;
372
+ if (tilesFor(lvl) <= budgetTiles && Math.max(lvl.width, lvl.height) <= MAX_TEXTURE_DIM) {
373
+ return { lvl, fits: true };
374
+ }
375
+ }
376
+ return { lvl: c, fits: false };
377
+ };
378
+ let sel = pick(usable);
379
+ // A specific channel only has tiles at the (few, large) real levels. When none of them fit the
380
+ // budget — e.g. the coarsest real level is still 14982×18670 → ~1100 full-res tiles — stitching
381
+ // it per slice floods the server (504s) and stalls the load. The composite pyramid has small
382
+ // overview levels, so fetch the composite instead and derive luminance from it. Every caller here
383
+ // (surface height, volume assembly, readback) downscales the plane anyway, so a composite-derived
384
+ // plane is the right trade for one that actually loads. Only kicks in when the channel can't fit.
385
+ if (allowCompositeFallback && !sel.fits && channel != null && desc.levels.length > perChannelLevels) {
386
+ const composite = pick(desc.levels);
387
+ if (composite.fits || tilesFor(composite.lvl) < tilesFor(sel.lvl)) {
388
+ effectiveChannel = undefined;
389
+ sel = composite;
390
+ console.warn(`[napari-js] channel ${channel} has no pyramid level within the ${budgetTiles}-tile ` +
391
+ `budget; using the composite overview (res ${sel.lvl.res}, ${sel.lvl.width}×` +
392
+ `${sel.lvl.height}) for this plane.`);
393
+ }
394
+ }
395
+ const chosen = sel.lvl;
396
+ const cols = Math.max(1, Math.ceil(chosen.width / t));
397
+ const rows = Math.max(1, Math.ceil(chosen.height / t));
398
+ if (!sel.fits && budgetTiles === MAX_STITCH_TILES) {
399
+ console.warn(`[napari-js] full resolution exceeds the ${budgetTiles}-tile/${MAX_TEXTURE_DIM}px budget; ` +
400
+ `displaying overview level res ${chosen.res} (${chosen.width}×${chosen.height}).`);
401
+ }
402
+ if (cols === 1 && rows === 1)
403
+ return fetchTile(chosen.res, 0, 0, t);
404
+ // Stitch into one level-sized canvas. Fetch the grid with BOUNDED concurrency: firing every tile
405
+ // at once (a big grid = hundreds of requests) overwhelmed the tile server (504s). Edge tiles are
406
+ // narrower/shorter; drawImage places each at its grid offset so partial tiles line up.
407
+ const coords = [];
408
+ for (let row = 0; row < rows; row++) {
409
+ for (let col = 0; col < cols; col++)
410
+ coords.push({ col, row });
411
+ }
412
+ const tiles = [];
413
+ const stitchWorker = async () => {
414
+ for (;;) {
415
+ const job = coords.shift();
416
+ if (!job)
417
+ return;
418
+ const bmp = await fetchTile(chosen.res, job.col, job.row, t);
419
+ tiles.push({ col: job.col, row: job.row, bmp });
420
+ }
421
+ };
422
+ await Promise.all(Array.from({ length: Math.min(TILE_FETCH_CONCURRENCY, coords.length) }, () => stitchWorker()));
423
+ let canvas;
424
+ if (typeof OffscreenCanvas !== 'undefined') {
425
+ canvas = new OffscreenCanvas(chosen.width, chosen.height);
426
+ }
427
+ else {
428
+ canvas = document.createElement('canvas');
429
+ canvas.width = chosen.width;
430
+ canvas.height = chosen.height;
431
+ }
432
+ const ctx = canvas.getContext('2d');
433
+ if (!ctx)
434
+ throw new Error('[napari-js] slice stitch: 2D context unavailable');
435
+ for (const { col, row, bmp } of tiles) {
436
+ ctx.drawImage(bmp, col * t, row * t);
437
+ bmp.close?.();
438
+ }
439
+ // Safety net: if the chosen level still exceeds the GPU texture limit (e.g. a multichannel
440
+ // image whose coarsest REAL level is huge — overview levels are composite-only), downscale the
441
+ // stitched canvas to fit so the WebGPU texture upload can't fail.
442
+ const longest = Math.max(chosen.width, chosen.height);
443
+ if (longest > MAX_TEXTURE_DIM) {
444
+ const scale = MAX_TEXTURE_DIM / longest;
445
+ const outW = Math.max(1, Math.floor(chosen.width * scale));
446
+ const outH = Math.max(1, Math.floor(chosen.height * scale));
447
+ console.warn(`[napari-js] stitched level ${chosen.width}×${chosen.height} exceeds the ` +
448
+ `${MAX_TEXTURE_DIM}px texture limit; downscaling to ${outW}×${outH}.`);
449
+ let out;
450
+ if (typeof OffscreenCanvas !== 'undefined') {
451
+ out = new OffscreenCanvas(outW, outH);
452
+ }
453
+ else {
454
+ out = document.createElement('canvas');
455
+ out.width = outW;
456
+ out.height = outH;
457
+ }
458
+ const octx = out.getContext('2d');
459
+ if (!octx)
460
+ throw new Error('[napari-js] slice downscale: 2D context unavailable');
461
+ octx.drawImage(canvas, 0, 0, outW, outH);
462
+ return createImageBitmap(out);
463
+ }
464
+ return createImageBitmap(canvas);
465
+ }
466
+ // ── IDataRenderer: load / render / viewport ───────────────────────────────
467
+ async load(imageInfo, zIndex) {
468
+ // Keeps SimpleSliceAccessService's blob cache in sync even when the user
469
+ // switches backends (e.g. OSD Image → napari Volume) on the same file —
470
+ // whichever backend loads a genuinely different file first evicts it.
471
+ this.simpleStack.noteActiveFile(imageInfo.fileName);
472
+ this.loaded = { imageInfo, z: zIndex, filename: imageInfo.fileName };
473
+ return this.loaded;
474
+ }
475
+ async plot(plotDiv, imageLoaded, imageInfo, screenHeight, plotType, _inPlace) {
476
+ const host = document.getElementById(plotDiv);
477
+ if (!host) {
478
+ console.error(`[napari-js] plot target #${plotDiv} not found`);
479
+ return false;
480
+ }
481
+ this.reset();
482
+ this.host = host;
483
+ this.plotDivId = plotDiv;
484
+ this.currentPlotType = plotType;
485
+ const canvas = document.createElement('canvas');
486
+ canvas.style.display = 'block';
487
+ canvas.style.width = '100%';
488
+ canvas.style.height = screenHeight ? `${screenHeight}px` : '100%';
489
+ host.appendChild(canvas);
490
+ this.canvas = canvas;
491
+ const info = imageLoaded?.imageInfo ?? imageInfo;
492
+ const z = imageLoaded?.z ?? 0;
493
+ try {
494
+ const viewer = new Viewer({ canvas, background: { r: 0.07, g: 0.07, b: 0.09, a: 1 } });
495
+ this.viewer = viewer;
496
+ await viewer.ready;
497
+ if (isNapariScatter(plotType)) {
498
+ await this.mountScatter(viewer, z);
499
+ }
500
+ else if (isNapariScatter3d(plotType)) {
501
+ await this.mountScatter3d(viewer, info);
502
+ }
503
+ else if (isNapariSurface(plotType)) {
504
+ await this.mountSurface(viewer);
505
+ }
506
+ else if (isNapari3d(plotType)) {
507
+ await this.mountVolume(viewer, info, plotType);
508
+ }
509
+ else {
510
+ await this.renderImage(z);
511
+ this.fitCameraSoon();
512
+ this.subscribeDisplayState();
513
+ this.installScaleBar();
514
+ this.regionOverlay = new NapariRegionOverlay(host, viewer, this.regionStore);
515
+ this.buildToolHosts();
516
+ // Keep the pixel-tool readback (lastPixels) current as the view pans/zooms and tiled
517
+ // levels load, so wand/brush/SAM sample the actually-displayed image (jit-ui#102).
518
+ this.cameraReadbackOff = viewer.camera.changed.connect(() => this.armReadback());
519
+ }
520
+ this.scheduleReadback();
521
+ return true;
522
+ }
523
+ catch (err) {
524
+ console.error('[napari-js] plot failed:', err);
525
+ return false;
526
+ }
527
+ }
528
+ // ── Channels: per-channel composite, LUT, native histograms (jit-ui#102) ──────────────────
529
+ /** The grayscale display colormap from the store selection (+reverse), defaulting to gray.
530
+ * Maps the jit-ui store colormap node to a napari `Colormap` via the library's LUT factory;
531
+ * the multichannel tint ramps are built inside {@link MultiChannelImageView} from each
532
+ * channel's hex colour. */
533
+ grayscaleColormap() {
534
+ const value = this.currentColormap?.data?.value;
535
+ const lut = value != null ? buildColormapLut(value, this.currentReverse) : null;
536
+ if (lut)
537
+ return colormapFromLut('gray-cmap', lut);
538
+ return this.currentReverse
539
+ ? colormapFromLut('gray-rev', [[255, 255, 255], [0, 0, 0]])
540
+ : 'gray';
541
+ }
542
+ /** Fetch a stitched slice and read it back as a single-channel uint8 plane. `channel` selects a
543
+ * band (multichannel); omit it for the grayscale composite (all overview levels available, so a
544
+ * large image picks a fitting downscaled level rather than only the full-res real level). */
545
+ async fetchChannelData(z, channel, budgetTiles) {
546
+ const bmp = await this.fetchSlice(z, channel, budgetTiles);
547
+ return this.bitmapToLuminance(bmp);
548
+ }
549
+ /** Decode an `ImageBitmap` (server bands are grayscale, R=G=B) to a single-channel uint8 plane.
550
+ * `maxSide` caps the longest side (downscaling on the canvas draw) — used to keep pre-loaded
551
+ * surface slice planes small. */
552
+ bitmapToLuminance(bmp, maxSide) {
553
+ const scale = maxSide ? Math.min(1, maxSide / Math.max(bmp.width, bmp.height, 1)) : 1;
554
+ const w = Math.max(1, Math.round(bmp.width * scale));
555
+ const h = Math.max(1, Math.round(bmp.height * scale));
556
+ let canvas;
557
+ if (typeof OffscreenCanvas !== 'undefined') {
558
+ canvas = new OffscreenCanvas(w, h);
559
+ }
560
+ else {
561
+ canvas = document.createElement('canvas');
562
+ canvas.width = w;
563
+ canvas.height = h;
564
+ }
565
+ const ctx = canvas.getContext('2d');
566
+ if (!ctx)
567
+ throw new Error('[napari-js] channel readback: 2D context unavailable');
568
+ // Scale the WHOLE bitmap into the (possibly smaller) target canvas — drawing at natural size
569
+ // would crop to the top-left w×h corner when downscaling a large slice (maxSide < bmp size).
570
+ ctx.drawImage(bmp, 0, 0, w, h);
571
+ bmp.close?.();
572
+ const rgba = ctx.getImageData(0, 0, w, h).data;
573
+ const data = new Uint8Array(w * h);
574
+ for (let i = 0; i < w * h; i++)
575
+ data[i] = rgba[i * 4];
576
+ return { data, width: w, height: h };
577
+ }
578
+ /**
579
+ * Render the 2D image for slice `z`. With a server pyramid descriptor we use a pyramidal
580
+ * {@link TiledSource} per layer so the view refines to higher resolution on zoom (like OSD) and
581
+ * sits naturally in full-resolution coordinates; without one we fall back to the single-level
582
+ * stitch. Three display modes either way: multichannel additive tint, grayscale colormap, RGB.
583
+ */
584
+ async renderImage(z, token) {
585
+ const v = this.viewer;
586
+ if (!v)
587
+ return;
588
+ const desc = await this.ensureDescriptor();
589
+ if (desc && desc.levels?.length) {
590
+ if (token != null && token !== this.sliceReq)
591
+ return;
592
+ await this.renderImageTiled(z, desc);
593
+ return;
594
+ }
595
+ return this.renderImageStitched(z, token);
596
+ }
597
+ /** Single-level stitch fallback (no descriptor): the pre-tiling path. */
598
+ async renderImageStitched(z, token) {
599
+ const v = this.viewer;
600
+ if (!v)
601
+ return;
602
+ const desc = await this.ensureDescriptor();
603
+ const states = this.store.currentChannelStates();
604
+ const channelCount = desc?.channels ?? (states.length || 1);
605
+ const multichannel = !!desc?.multichannel && channelCount > 1;
606
+ // 1) Fetch all pixel data BEFORE touching the viewer, so a superseded scrub can bail without
607
+ // having torn down the visible layers (avoids flicker / out-of-order layer state).
608
+ let mode;
609
+ let planes = [];
610
+ let bitmap = null;
611
+ if (multichannel) {
612
+ mode = 'multichannel';
613
+ planes = await Promise.all(Array.from({ length: channelCount }, (_, c) => this.fetchChannelData(z, c)));
614
+ }
615
+ else if (channelCount === 1) {
616
+ mode = 'grayscale';
617
+ // Composite fetch (no channel) → all overview levels usable, so a large grayscale image
618
+ // selects a fitting downscaled level instead of the full-res real level (texture limit).
619
+ planes = [await this.fetchChannelData(z)];
620
+ }
621
+ else {
622
+ mode = 'rgb';
623
+ bitmap = await this.fetchSlice(z);
624
+ }
625
+ // 2) Commit — unless a newer scrub superseded us or the viewer was torn down.
626
+ if ((token != null && token !== this.sliceReq) || this.viewer !== v) {
627
+ bitmap?.close?.();
628
+ return;
629
+ }
630
+ // The displayed texture may be a downscaled pyramid level; scale the layer into FULL-RESOLUTION
631
+ // world coordinates (level-0 pixels) so the camera, readback and — critically — pre-saved
632
+ // regions (stored in full-res coords, e.g. ndpi) all line up regardless of which level is shown.
633
+ // This mirrors OSD, whose coordinate system is always level 0.
634
+ const texW = mode === 'rgb' ? bitmap.width : planes[0]?.width ?? 0;
635
+ const texH = mode === 'rgb' ? bitmap.height : planes[0]?.height ?? 0;
636
+ const fullW = desc?.width || texW || 1;
637
+ const fullH = desc?.height || texH || 1;
638
+ const scale = [texW ? fullW / texW : 1, texH ? fullH / texH : 1];
639
+ this.imageMode = mode;
640
+ const interpolation = this.imageSmoothing ? 'linear' : 'nearest';
641
+ this.channelView = new MultiChannelImageView(v);
642
+ if (mode === 'multichannel') {
643
+ const views = planes.map((d, c) => {
644
+ const st = states.find((s) => s.index === c);
645
+ const color = st?.color ?? desc?.channelInfo?.[c]?.color ?? tintFor(c);
646
+ return {
647
+ source: { kind: 'typed', width: d.width, height: d.height, channels: 1, dtype: 'uint8', data: d.data },
648
+ tint: color,
649
+ name: st?.name ?? `ch${c}`,
650
+ contrastLimits: [st?.min ?? 0, st?.max ?? 255],
651
+ gamma: st?.gamma ?? 1,
652
+ visible: st?.visible ?? true,
653
+ invert: this.invertEnabled,
654
+ scale,
655
+ };
656
+ });
657
+ this.channelView.render('multichannel', views, { interpolation });
658
+ }
659
+ else if (mode === 'grayscale') {
660
+ const d = planes[0];
661
+ const st = states[0];
662
+ this.channelView.render('grayscale', [
663
+ {
664
+ source: { kind: 'typed', width: d.width, height: d.height, channels: 1, dtype: 'uint8', data: d.data },
665
+ colormap: this.grayscaleColormap(),
666
+ contrastLimits: [st?.min ?? 0, st?.max ?? 255],
667
+ gamma: st?.gamma ?? 1,
668
+ invert: this.invertEnabled,
669
+ scale,
670
+ },
671
+ ], { interpolation });
672
+ }
673
+ else {
674
+ this.channelView.render('rgb', [{ source: bitmap, scale }], { interpolation });
675
+ }
676
+ this.imageW = fullW;
677
+ this.imageH = fullH;
678
+ }
679
+ /**
680
+ * Render the 2D image with pyramidal {@link TiledSource}s — the view refines to higher resolution
681
+ * as you zoom in (the visual fetches the level whose texels ≈ screen pixels) and sits in full-res
682
+ * coordinates so regions align. Same three modes as the stitch path. Per-channel layers use the
683
+ * REAL pyramid levels (per-channel tiles only exist there); the composite uses all levels.
684
+ */
685
+ async renderImageTiled(z, desc) {
686
+ const v = this.viewer;
687
+ if (!v)
688
+ return;
689
+ const states = this.store.currentChannelStates();
690
+ const channelCount = desc.channels ?? (states.length || 1);
691
+ const multichannel = !!desc.multichannel && channelCount > 1;
692
+ const interpolation = this.imageSmoothing ? 'linear' : 'nearest';
693
+ this.tiled = true;
694
+ this.channelView = new MultiChannelImageView(v);
695
+ if (multichannel) {
696
+ this.imageMode = 'multichannel';
697
+ const views = [];
698
+ for (let c = 0; c < channelCount; c++) {
699
+ const st = states.find((s) => s.index === c);
700
+ const color = st?.color ?? desc.channelInfo?.[c]?.color ?? tintFor(c);
701
+ views.push({
702
+ source: this.buildTiledSource(desc, c, 1),
703
+ tint: color,
704
+ name: st?.name ?? `ch${c}`,
705
+ contrastLimits: [st?.min ?? 0, st?.max ?? 255],
706
+ gamma: st?.gamma ?? 1,
707
+ visible: st?.visible ?? true,
708
+ invert: this.invertEnabled,
709
+ });
710
+ }
711
+ this.channelView.render('multichannel', views, { interpolation });
712
+ }
713
+ else if (channelCount === 1) {
714
+ this.imageMode = 'grayscale';
715
+ const st = states[0];
716
+ this.channelView.render('grayscale', [
717
+ {
718
+ source: this.buildTiledSource(desc, undefined, 1),
719
+ colormap: this.grayscaleColormap(),
720
+ contrastLimits: [st?.min ?? 0, st?.max ?? 255],
721
+ gamma: st?.gamma ?? 1,
722
+ invert: this.invertEnabled,
723
+ },
724
+ ], { interpolation });
725
+ }
726
+ else {
727
+ this.imageMode = 'rgb';
728
+ this.channelView.render('rgb', [{ source: this.buildTiledSource(desc, undefined, 4) }], {
729
+ interpolation,
730
+ });
731
+ }
732
+ this.imageW = desc.width;
733
+ this.imageH = desc.height;
734
+ // Await on the initial render so getHistogram/autoContrast have data immediately; slice changes
735
+ // refresh fire-and-forget (the histogram pane retries).
736
+ await this.refreshHistogramSamples(z, desc);
737
+ }
738
+ /** Build a pyramidal TiledSource backed by the server `/tile` endpoint. `channel` selects a band
739
+ * (grayscale luminance, real levels only); omit it for the composite (RGBA, all levels). */
740
+ buildTiledSource(desc, channel, channels) {
741
+ const infoB64 = this.tiles.getSelectedInfoB64() ?? '';
742
+ // Per-channel tiles exist only at REAL Bio-Formats levels; the composite exists at all levels.
743
+ const usable = channel == null
744
+ ? desc.levels
745
+ : desc.levels.slice(0, Math.max(1, desc.realLevels ?? desc.levels.length));
746
+ const levelScales = usable.map((l) => desc.width / Math.max(1, l.width)); // level-0 px per level px
747
+ const tileSize = desc.tileSize || TILE_SIZE;
748
+ const ch = channel == null ? '' : `&channel=${channel}`;
749
+ const api = this.api;
750
+ return {
751
+ kind: 'tiled',
752
+ width: desc.width,
753
+ height: desc.height,
754
+ tileSize,
755
+ levels: usable.length,
756
+ levelScales,
757
+ depth: Math.max(1, desc.z || 1),
758
+ channels,
759
+ dtype: 'uint8',
760
+ fetchTile: async (key) => {
761
+ const res = usable[key.level]?.res ?? key.level;
762
+ const url = `${api}tile?info=${infoB64}&res=${res}&col=${key.col}&row=${key.row}&z=${key.z}&tileSize=${tileSize}${ch}`;
763
+ const headers = await this.tiles
764
+ .getAuthHeaders()
765
+ .catch(() => ({}));
766
+ const resp = await fetch(url, { headers });
767
+ if (!resp.ok) {
768
+ throw new Error(`[napari-js] tile ${key.level}/${key.col}/${key.row} → ${resp.status}`);
769
+ }
770
+ const bmp = await createImageBitmap(await resp.blob());
771
+ if (channels === 4)
772
+ return { width: bmp.width, height: bmp.height, data: bmp };
773
+ return this.bitmapToLuminance(bmp);
774
+ },
775
+ };
776
+ }
777
+ /** (Re)fetch a coarse per-channel luminance sample for the histogram (tiled mode has no full
778
+ * in-memory pixels). One cheap overview tile per channel; cached by channel index. */
779
+ async refreshHistogramSamples(z, desc) {
780
+ this.histSamples.clear();
781
+ if (this.imageMode === 'rgb')
782
+ return; // RGB uses the displayed-pixel readback (rgbHistogram)
783
+ const channelCount = this.imageMode === 'multichannel' ? desc.channels ?? 1 : 1;
784
+ for (let c = 0; c < channelCount; c++) {
785
+ try {
786
+ const ch = this.imageMode === 'multichannel' ? c : undefined;
787
+ const d = await this.fetchChannelData(z, ch, 1); // budget 1 → coarsest single tile
788
+ this.histSamples.set(c, d.data);
789
+ }
790
+ catch {
791
+ /* leave this channel's sample unset */
792
+ }
793
+ }
794
+ }
795
+ /** Subscribe channel states + grayscale colormap → live-apply to the rendered layers (no
796
+ * re-fetch; only z changes re-fetch). Replaces any prior subscription. */
797
+ subscribeDisplayState() {
798
+ this.displaySub?.unsubscribe();
799
+ this.displaySub = combineLatest([
800
+ this.store.getChannelStates(),
801
+ this.store.getColormap(),
802
+ this.store.getReverseScale(),
803
+ this.store.getInvert(),
804
+ ]).subscribe(([channels, colormap, reverse, invert]) => {
805
+ this.currentColormap = colormap ?? null;
806
+ this.currentReverse = reverse;
807
+ this.invertEnabled = invert;
808
+ this.applyDisplayState(channels);
809
+ });
810
+ }
811
+ /** Subscribe the store colormap (+reverse), invert, and channel state → the volume/isosurface
812
+ * transfer function: colour (channel tint or selected colormap), the intensity **window
813
+ * (min/max → contrastLimits)** and **gamma**, mirroring the grayscale image's display controls
814
+ * so the histogram pane drives the 3D render. Replaces any prior subscription. */
815
+ subscribeVolumeDisplayState() {
816
+ this.displaySub?.unsubscribe();
817
+ this.displaySub = combineLatest([
818
+ this.store.getColormap(),
819
+ this.store.getReverseScale(),
820
+ this.store.getInvert(),
821
+ this.store.getChannelStates(),
822
+ ]).subscribe(([colormap, reverse, invert, channels]) => {
823
+ this.currentColormap = colormap ?? null;
824
+ this.currentReverse = reverse;
825
+ this.invertEnabled = invert;
826
+ const view = this.volumeView;
827
+ if (!view)
828
+ return;
829
+ if (this.volumeMultichannel) {
830
+ // Each channel's layer is tinted by its colour and gets its own window/gamma/visibility.
831
+ view.layers.forEach((_, c) => {
832
+ const st = channels.find((s) => s.index === c);
833
+ view.updateChannel(c, {
834
+ colormap: this.channelTintColormap(st?.color ?? '#ffffff'),
835
+ ...(st
836
+ ? {
837
+ contrastLimits: [st.min, st.max],
838
+ gamma: st.gamma,
839
+ visible: st.visible,
840
+ }
841
+ : {}),
842
+ });
843
+ });
844
+ }
845
+ else {
846
+ const st = channels[0];
847
+ view.updateChannel(0, {
848
+ colormap: this.volumeColormap(st),
849
+ ...(st ? { contrastLimits: [st.min, st.max], gamma: st.gamma } : {}),
850
+ });
851
+ }
852
+ });
853
+ }
854
+ /** A channel's tint colormap (black→colour) with reverse-scale / invert applied by flipping. */
855
+ channelTintColormap(color) {
856
+ let cmap = tintColormap(color);
857
+ if (this.currentReverse)
858
+ cmap = reverseColormap(cmap);
859
+ if (this.invertEnabled)
860
+ cmap = reverseColormap(cmap);
861
+ return cmap;
862
+ }
863
+ /**
864
+ * Colour map for the volume/isosurface. A real colormap selection (viridis/magma/…) wins;
865
+ * otherwise the channel's colour tints it (so the channel-dialog colour swatch recolors the 3D
866
+ * render). Reverse-scale and invert each flip the ramp — the `VolumeLayer` has no per-layer
867
+ * invert, so both are emulated by reversing the colormap.
868
+ */
869
+ volumeColormap(st) {
870
+ const node = this.currentColormap;
871
+ // A colored colormap (viridis/magma/…) drives the volume; the default grayscale family
872
+ // (gray / Greys / Greys Inv) yields to the channel's colour so the dialog colour swatch
873
+ // recolors the 3D render.
874
+ const label = (node?.label ?? '').toLowerCase();
875
+ const grayFamily = label === '' || label.includes('grey') || label.includes('gray');
876
+ const value = node?.data?.value;
877
+ const lut = !grayFamily && value != null ? buildColormapLut(value, false) : null;
878
+ let cmap = lut
879
+ ? colormapFromLut('vol-cmap', lut)
880
+ : tintColormap(st?.color ?? '#ffffff');
881
+ // Reverse-scale and invert each flip the ramp (the VolumeLayer has no per-layer invert).
882
+ if (this.currentReverse)
883
+ cmap = reverseColormap(cmap);
884
+ if (this.invertEnabled)
885
+ cmap = reverseColormap(cmap);
886
+ return cmap;
887
+ }
888
+ /** Apply the current channel states / colormap to the live layers (no re-fetch), delegating the
889
+ * per-channel layer mutations to the {@link MultiChannelImageView}. */
890
+ applyDisplayState(channels) {
891
+ const view = this.channelView;
892
+ if (!this.viewer || !view)
893
+ return;
894
+ if (this.imageMode === 'multichannel') {
895
+ view.layers.forEach((_, c) => {
896
+ const st = channels.find((s) => s.index === c);
897
+ if (!st)
898
+ return;
899
+ view.updateChannel(c, {
900
+ tint: st.color,
901
+ contrastLimits: [st.min, st.max],
902
+ gamma: st.gamma,
903
+ visible: st.visible,
904
+ invert: this.invertEnabled,
905
+ });
906
+ });
907
+ }
908
+ else if (this.imageMode === 'grayscale') {
909
+ const st = channels[0];
910
+ view.updateChannel(0, {
911
+ colormap: this.grayscaleColormap(),
912
+ invert: this.invertEnabled,
913
+ ...(st ? { contrastLimits: [st.min, st.max], gamma: st.gamma } : {}),
914
+ });
915
+ }
916
+ }
917
+ /** (Re)install the physical scale bar over the 2D image, sized from the image's µm/pixel
918
+ * (`/tiles/info` mppX, falling back to the image metadata). No-op without a physical size. */
919
+ installScaleBar() {
920
+ this.scaleBar?.destroy();
921
+ this.scaleBar = null;
922
+ const mppX = this.descriptor?.mppX || this.loaded?.imageInfo.imageMeta?.[0]?.mppX || 0;
923
+ if (this.viewer && this.host && mppX > 0) {
924
+ this.scaleBar = new NapariScaleBar(this.host, this.viewer.camera, mppX);
925
+ }
926
+ }
927
+ /** Convert a napari-js `Histogram` (bin count + min/max) to the pane's `IHistogram` (bin edges). */
928
+ toIHistogram(h) {
929
+ const span = h.max - h.min || 1;
930
+ const bins = Array.from({ length: h.bins }, (_, i) => h.min + (i * span) / h.bins);
931
+ const counts = Array.from(h.counts);
932
+ return { bins, counts, max: counts.reduce((m, c) => (c > m ? c : m), 0) };
933
+ }
934
+ /**
935
+ * Mount the 3D volume/isosurface. A multichannel image becomes one additive, tinted
936
+ * {@link VolumeLayer} per channel (so each channel's colour composites into the render); a
937
+ * grayscale/composite image uses a single volume. Adds the axes gizmo + labels and wires the
938
+ * display-state subscription. The caller owns `viewer.ready`.
939
+ */
940
+ async mountVolume(viewer, info, plotType) {
941
+ const token = this.loadToken; // bail before rendering if a Cancel / new plot bumps this
942
+ const desc = await this.ensureDescriptor();
943
+ const channelCount = desc?.channels ?? 1;
944
+ const multichannel = !!desc?.multichannel && channelCount > 1;
945
+ const res = volumeResolutionFor(this.resolutionScale);
946
+ const rendering = isNapariIsosurface(plotType) ? 'iso' : 'mip';
947
+ const states = this.store.currentChannelStates();
948
+ this.volumeChannelData.clear();
949
+ this.volumeMultichannel = multichannel;
950
+ this.imageMode = multichannel ? 'multichannel' : 'grayscale';
951
+ const view = new MultiChannelVolumeView(viewer);
952
+ this.volumeView = view;
953
+ // Assemble per-channel scalar volumes from the server tiles (jit-specific); the napari-js view
954
+ // owns the layer orchestration (one additive tinted volume per channel, or a single grayscale
955
+ // volume). The adapter computes each channel's colormap (incl. invert/reverse flips).
956
+ let dims = null;
957
+ const channels = [];
958
+ this.stackLoading$.next(true);
959
+ this.stackLoadingProgress$.next(0);
960
+ try {
961
+ if (multichannel) {
962
+ for (let c = 0; c < channelCount; c++) {
963
+ const vol = await this.assembleVolume(info, res, c);
964
+ if (!vol)
965
+ continue;
966
+ dims = vol;
967
+ this.volumeChannelData.set(c, vol.data);
968
+ const st = states.find((s) => s.index === c);
969
+ const color = st?.color ?? desc?.channelInfo?.[c]?.color ?? tintFor(c);
970
+ channels.push({
971
+ data: vol.data,
972
+ width: vol.width,
973
+ height: vol.height,
974
+ depth: vol.depth,
975
+ colormap: this.channelTintColormap(color),
976
+ contrastLimits: [st?.min ?? 0, st?.max ?? 255],
977
+ gamma: st?.gamma ?? 1,
978
+ visible: st?.visible ?? true,
979
+ });
980
+ }
981
+ }
982
+ else {
983
+ const vol = await this.assembleVolume(info, res);
984
+ if (vol) {
985
+ dims = vol;
986
+ this.volumeChannelData.set(0, vol.data);
987
+ const st = states[0];
988
+ channels.push({
989
+ data: vol.data,
990
+ width: vol.width,
991
+ height: vol.height,
992
+ depth: vol.depth,
993
+ colormap: this.volumeColormap(st),
994
+ contrastLimits: [st?.min ?? 0, st?.max ?? 255],
995
+ gamma: st?.gamma ?? 1,
996
+ });
997
+ }
998
+ }
999
+ }
1000
+ finally {
1001
+ this.stackLoading$.next(false);
1002
+ this.stackLoadingProgress$.next(0);
1003
+ }
1004
+ if (!dims || !channels.length || this.loadToken !== token)
1005
+ return; // cancelled → don't render
1006
+ // Resolution-invariant world box. Sizing the box by the sampled voxel counts made higher
1007
+ // in-plane resolution grow X/Y while the depth stayed the (constant) slice count — so Z appeared
1008
+ // to shrink at higher resolution. Instead anchor the in-plane long side to a fixed reference and
1009
+ // let Z span the full slice count; the box shape is then identical at every decimate factor. The
1010
+ // per-axis `voxelSize` (napari `scale`) maps the sampled grid onto that fixed world box.
1011
+ const fullW = this.descriptor?.width ?? dims.width;
1012
+ const fullH = this.descriptor?.height ?? dims.height;
1013
+ const fullD = this.loaded?.imageInfo.imageMeta?.[0]?.z || this.loaded?.imageInfo.urls?.length || dims.depth;
1014
+ const fullLong = Math.max(1, fullW, fullH);
1015
+ const world = {
1016
+ width: (fullW * VOLUME_WORLD_INPLANE_REF) / fullLong,
1017
+ height: (fullH * VOLUME_WORLD_INPLANE_REF) / fullLong,
1018
+ depth: fullD,
1019
+ };
1020
+ // Base box (Z-scale = 1) + the sampled depth drive the live Z-height handle below; the persisted
1021
+ // `volumeZScale` (user drag) applies on top so changing resolution keeps the chosen height.
1022
+ this.volumeWorldBase = world;
1023
+ this.volumeSampledDepth = Math.max(1, dims.depth);
1024
+ const worldZ = world.depth * this.volumeZScale;
1025
+ const voxelSize = [
1026
+ world.width / dims.width,
1027
+ world.height / dims.height,
1028
+ worldZ / this.volumeSampledDepth,
1029
+ ];
1030
+ for (const ch of channels)
1031
+ ch.voxelSize = voxelSize;
1032
+ view.render(multichannel ? 'multichannel' : 'grayscale', channels, { rendering });
1033
+ this.imageW = dims.width;
1034
+ this.imageH = dims.height;
1035
+ this.volumeDims = dims;
1036
+ // 3D coordinate-axes / scale gizmo + labels, sharing the volume's world box so the gizmo tracks
1037
+ // the rendered proportions. Physical scale text still comes from the FULL image extent.
1038
+ const mppX = this.descriptor?.mppX || this.loaded?.imageInfo.imageMeta?.[0]?.mppX || 0;
1039
+ this.axesLayer = viewer.addAxes(world.width, world.height, worldZ, { visible: this.axesVisible });
1040
+ if (this.host) {
1041
+ this.axesLabels = new NapariAxesLabels(this.host, viewer.camera3d, this.buildAxesLabels({ width: world.width, height: world.height, depth: worldZ }, mppX));
1042
+ this.axesLabels.setVisible(this.axesVisible);
1043
+ // In-view drag handle at the TOP END OF THE Z AXIS (the box's min-XY corner, where the blue
1044
+ // "Z" axis + label live), so it reads as the Z-height control. Floated a little past the axis
1045
+ // tip (×1.12) so the grip clears the "Z · …" label. Drag ↕ to restretch Z live.
1046
+ this.zHandle = new NapariVolumeZHandle(this.host, viewer.camera3d, {
1047
+ topAnchor: () => [
1048
+ -this.volumeWorldBase.width / 2,
1049
+ -this.volumeWorldBase.height / 2,
1050
+ ((this.volumeWorldBase.depth * this.volumeZScale) / 2) * 1.12,
1051
+ ],
1052
+ getScale: () => this.volumeZScale,
1053
+ setScale: (s) => this.setVolumeZScale(s),
1054
+ });
1055
+ }
1056
+ this.subscribeVolumeDisplayState();
1057
+ }
1058
+ /**
1059
+ * Restretch the volume's Z height live (driven by the in-view {@link NapariVolumeZHandle}). Only
1060
+ * the per-axis `voxelSize` / axes depth change — the voxel textures are untouched — so dragging is
1061
+ * smooth. `factor` is relative to the volume's natural proportions (1). Persists across re-mounts
1062
+ * (resolution changes) so the chosen height sticks.
1063
+ */
1064
+ setVolumeZScale(factor) {
1065
+ this.volumeZScale = Math.min(10, Math.max(0.1, factor));
1066
+ const base = this.volumeWorldBase;
1067
+ if (!base || !this.volumeView)
1068
+ return;
1069
+ const worldZ = base.depth * this.volumeZScale;
1070
+ const vsZ = worldZ / this.volumeSampledDepth;
1071
+ for (const layer of this.volumeView.layers) {
1072
+ const [sx, sy] = layer.voxelSize;
1073
+ layer.voxelSize = [sx, sy, vsZ];
1074
+ }
1075
+ if (this.axesLayer)
1076
+ this.axesLayer.depth = worldZ;
1077
+ const mppX = this.descriptor?.mppX || this.loaded?.imageInfo.imageMeta?.[0]?.mppX || 0;
1078
+ this.axesLabels?.updateAnchors(this.buildAxesLabels({ width: base.width, height: base.height, depth: worldZ }, mppX));
1079
+ this.zHandle?.reposition();
1080
+ this.viewer?.requestRender();
1081
+ }
1082
+ /** Build the X/Y/Z axis-end label specs for the 3D gizmo. Anchors are in the volume's centred
1083
+ * world box (matching the AxesLayer geometry); the scale text reflects the FULL image extent —
1084
+ * physical µm when µm/pixel is known, else pixel (X/Y) / slice (Z) counts. */
1085
+ buildAxesLabels(vol, mppX) {
1086
+ const hx = vol.width / 2;
1087
+ const hy = vol.height / 2;
1088
+ const hz = vol.depth / 2;
1089
+ const descW = this.descriptor?.width ?? vol.width;
1090
+ const descH = this.descriptor?.height ?? vol.height;
1091
+ const slices = this.loaded?.imageInfo.imageMeta?.[0]?.z || this.loaded?.imageInfo.urls?.length || vol.depth;
1092
+ const len = (px) => (mppX > 0 ? formatUm(px * mppX) : `${px} px`);
1093
+ return [
1094
+ { anchor: [hx, -hy, -hz], text: `X · ${len(descW)}`, color: '#ed4545' },
1095
+ { anchor: [-hx, hy, -hz], text: `Y · ${len(descH)}`, color: '#4dd959' },
1096
+ { anchor: [-hx, -hy, hz], text: `Z · ${slices} px`, color: '#668cff' },
1097
+ ];
1098
+ }
1099
+ /**
1100
+ * Assemble a downsampled uint8 volume (luminance) from the per-slice tile endpoint. Slices are
1101
+ * fetched with bounded concurrency (keeps the connection pool full without flooding it on a deep
1102
+ * stack) and read into the volume as each arrives, driving {@link stackLoadingProgress$} so the
1103
+ * host shows a determinate progress bar instead of a bare spinner.
1104
+ */
1105
+ async assembleVolume(info, opts = {}, channel) {
1106
+ const token = this.loadToken; // bail if a Cancel / new plot bumps this while we fetch
1107
+ const fullDepth = info?.imageMeta?.[0]?.z || info?.urls?.length || 1;
1108
+ if (fullDepth < 1) {
1109
+ console.warn('[napari-js] no slices to assemble a volume');
1110
+ return null;
1111
+ }
1112
+ const step = Math.max(1, Math.floor(opts.sliceStep ?? 1));
1113
+ const maxSlice = opts.maxSlice ?? VOLUME_MAX_SLICE;
1114
+ // Source-slice indices sampled into the volume (every `step`th plane → low-res is faster).
1115
+ const zIndices = [];
1116
+ for (let z = 0; z < fullDepth; z += step)
1117
+ zIndices.push(z);
1118
+ const depth = zIndices.length;
1119
+ this.stackLoadingProgress$.next(0);
1120
+ try {
1121
+ // Fetch each slice at a pyramid level matching `maxSlice` (budget scales with the decimate
1122
+ // factor), so a higher factor pulls a finer level → more real in-plane detail; then downsample
1123
+ // to `maxSlice`. `channel` selects a band (multichannel volume); omit for the grayscale
1124
+ // composite. The caller owns the stackLoading flag (multichannel assembles channels in turn).
1125
+ const budget = this.tileBudgetFor(maxSlice);
1126
+ const first = await this.fetchSlice(zIndices[0], channel, budget);
1127
+ const scale = Math.min(1, maxSlice / Math.max(first.width, first.height, 1));
1128
+ const width = Math.max(1, Math.round(first.width * scale));
1129
+ const height = Math.max(1, Math.round(first.height * scale));
1130
+ const data = new Uint8Array(width * height * depth);
1131
+ let canvas;
1132
+ if (typeof OffscreenCanvas !== 'undefined') {
1133
+ canvas = new OffscreenCanvas(width, height);
1134
+ }
1135
+ else {
1136
+ canvas = document.createElement('canvas');
1137
+ canvas.width = width;
1138
+ canvas.height = height;
1139
+ }
1140
+ const ctx = canvas.getContext('2d');
1141
+ if (!ctx)
1142
+ throw new Error('napari-js volume assembly: 2D context unavailable');
1143
+ // Read one fetched slice bitmap into the volume plane `z` (luminance). Synchronous between
1144
+ // awaits, so the shared 2D context is safe to reuse across the concurrent fetch workers.
1145
+ let done = 0;
1146
+ const readSlice = (z, bmp) => {
1147
+ ctx.clearRect(0, 0, width, height);
1148
+ ctx.drawImage(bmp, 0, 0, width, height);
1149
+ const rgba = ctx.getImageData(0, 0, width, height).data;
1150
+ const base = z * width * height;
1151
+ for (let i = 0; i < width * height; i++) {
1152
+ data[base + i] =
1153
+ (rgba[i * 4] * 0.299 + rgba[i * 4 + 1] * 0.587 + rgba[i * 4 + 2] * 0.114) | 0;
1154
+ }
1155
+ bmp.close?.();
1156
+ done++;
1157
+ this.stackLoadingProgress$.next(Math.round((done / depth) * 100));
1158
+ };
1159
+ readSlice(0, first);
1160
+ // Remaining planes via a small pool of workers draining a shared queue. Each plane `p`
1161
+ // maps to source slice `zIndices[p]` (subsampled for low-res).
1162
+ const pending = [];
1163
+ for (let p = 1; p < depth; p++)
1164
+ pending.push(p);
1165
+ const worker = async () => {
1166
+ for (;;) {
1167
+ const p = pending.shift();
1168
+ if (p === undefined || this.loadToken !== token)
1169
+ return;
1170
+ readSlice(p, await this.fetchSlice(zIndices[p], channel, budget));
1171
+ }
1172
+ };
1173
+ const poolSize = Math.min(VOLUME_FETCH_CONCURRENCY, Math.max(1, depth - 1));
1174
+ await Promise.all(Array.from({ length: poolSize }, () => worker()));
1175
+ if (this.loadToken !== token)
1176
+ return null; // cancelled → don't render a partial volume
1177
+ return { data, width, height, depth };
1178
+ }
1179
+ finally {
1180
+ this.stackLoadingProgress$.next(0);
1181
+ }
1182
+ }
1183
+ /**
1184
+ * Mount the NAPARI_SCATTER 2D scatter: the slice image with a points layer at each region's
1185
+ * centroid (napari-js analog of Plotly's region-centroid scatter). Rebuilds the points live as
1186
+ * regions change.
1187
+ */
1188
+ async mountScatter(viewer, z) {
1189
+ await this.renderImage(z);
1190
+ this.fitCameraSoon();
1191
+ this.subscribeDisplayState();
1192
+ this.installScaleBar();
1193
+ this.rebuildScatterPoints();
1194
+ this.scatterRegionSub = this.regionStore
1195
+ .getRegionUpdateEvent()
1196
+ .subscribe(() => this.rebuildScatterPoints());
1197
+ this.scheduleReadback();
1198
+ }
1199
+ /** (Re)build the 2D scatter's point layer at the current region centroids. */
1200
+ rebuildScatterPoints() {
1201
+ const v = this.viewer;
1202
+ if (!v)
1203
+ return;
1204
+ if (this.scatter2dPoints) {
1205
+ v.layers.remove(this.scatter2dPoints);
1206
+ this.scatter2dPoints = null;
1207
+ }
1208
+ const centroids = this.regionCentroids();
1209
+ if (centroids.length === 0)
1210
+ return;
1211
+ this.scatter2dPoints = v.addPoints(centroids, {
1212
+ size: 12,
1213
+ faceColor: [1, 0.85, 0.2, 1],
1214
+ borderColor: [0, 0, 0, 1],
1215
+ borderWidth: 2,
1216
+ });
1217
+ }
1218
+ /** Region centroids as flat `[x, y, …]` data coords (rectangle / polygon / multipolygon). */
1219
+ regionCentroids() {
1220
+ const out = [];
1221
+ const polyCentroid = (xs, ys) => {
1222
+ const n = xs.length;
1223
+ if (n === 0)
1224
+ return;
1225
+ let cx = 0;
1226
+ let cy = 0;
1227
+ for (let i = 0; i < n; i++) {
1228
+ cx += xs[i];
1229
+ cy += ys[i];
1230
+ }
1231
+ out.push(cx / n, cy / n);
1232
+ };
1233
+ for (const r of this.regionStore.getRegions()) {
1234
+ const b = r.bounds;
1235
+ if (!b)
1236
+ continue;
1237
+ if ('width' in b && 'x' in b) {
1238
+ out.push(b.x + b.width / 2, b.y + b.height / 2);
1239
+ }
1240
+ else if ('xpoints' in b) {
1241
+ polyCentroid(b.xpoints, b.ypoints);
1242
+ }
1243
+ else if ('polygons' in b) {
1244
+ for (const p of b.polygons)
1245
+ polyCentroid(p.xpoints, p.ypoints);
1246
+ }
1247
+ }
1248
+ return new Float32Array(out);
1249
+ }
1250
+ /**
1251
+ * Mount the NAPARI_SCATTER3D 3D scatter: the downsampled voxel grid as a 3D point cloud colored
1252
+ * by intensity (napari-js analog of Plotly's voxel scatter3d). Assembles a coarse volume, then
1253
+ * emits a flat-strided sample of voxels (capped at {@link SCATTER3D_MAX_POINTS}) via `addPoints3D`.
1254
+ */
1255
+ async mountScatter3d(viewer, info) {
1256
+ this.imageMode = 'grayscale';
1257
+ this.volumeMultichannel = false;
1258
+ const res = volumeResolutionFor(this.resolutionScale);
1259
+ this.stackLoading$.next(true);
1260
+ this.stackLoadingProgress$.next(0);
1261
+ let vol = null;
1262
+ try {
1263
+ vol = await this.assembleVolume(info, {
1264
+ maxSlice: Math.min(res.maxSlice, SCATTER3D_MAX_XY),
1265
+ sliceStep: res.sliceStep,
1266
+ });
1267
+ }
1268
+ finally {
1269
+ this.stackLoading$.next(false);
1270
+ this.stackLoadingProgress$.next(0);
1271
+ }
1272
+ if (!vol || this.viewer !== viewer)
1273
+ return;
1274
+ const { data, width, height, depth } = vol;
1275
+ const zScale = Math.max(width, height) / Math.max(1, depth); // ≈ cubic aspect
1276
+ const total = width * height * depth;
1277
+ const stride = Math.max(1, Math.ceil(total / SCATTER3D_MAX_POINTS));
1278
+ const pos = [];
1279
+ const val = [];
1280
+ for (let i = 0; i < total; i += stride) {
1281
+ const x = i % width;
1282
+ const y = Math.floor(i / width) % height;
1283
+ const zi = Math.floor(i / (width * height));
1284
+ pos.push(x, y, zi * zScale);
1285
+ val.push(data[i]);
1286
+ }
1287
+ const st = this.store.currentChannelStates()[0];
1288
+ this.scatter3dLayer = viewer.addPoints3D(new Float32Array(pos), new Float32Array(val), {
1289
+ colormap: this.volumeColormap(st),
1290
+ contrastLimits: [st?.min ?? 0, st?.max ?? 255],
1291
+ size: 3,
1292
+ });
1293
+ this.imageW = width;
1294
+ this.imageH = height;
1295
+ this.volumeDims = { width, height, depth };
1296
+ // Feed the intensity histogram from the assembled volume (key 0).
1297
+ this.volumeChannelData.clear();
1298
+ this.volumeChannelData.set(0, data);
1299
+ this.subscribeScatter3dDisplayState();
1300
+ this.scheduleReadback();
1301
+ }
1302
+ /** Store colormap / reverse / invert / channel window → the 3D scatter's colormap + contrast. */
1303
+ subscribeScatter3dDisplayState() {
1304
+ this.displaySub?.unsubscribe();
1305
+ this.displaySub = combineLatest([
1306
+ this.store.getColormap(),
1307
+ this.store.getReverseScale(),
1308
+ this.store.getInvert(),
1309
+ this.store.getChannelStates(),
1310
+ ]).subscribe(([colormap, reverse, invert, channels]) => {
1311
+ this.currentColormap = colormap ?? null;
1312
+ this.currentReverse = reverse;
1313
+ this.invertEnabled = invert;
1314
+ const layer = this.scatter3dLayer;
1315
+ if (!layer)
1316
+ return;
1317
+ const st = channels[0];
1318
+ layer.colormap = this.volumeColormap(st);
1319
+ if (st)
1320
+ layer.contrastLimits = [st.min, st.max];
1321
+ this.viewer?.requestRender();
1322
+ });
1323
+ }
1324
+ /**
1325
+ * Mount the NAPARI_SURFACE height-field surface. A height field is single-scalar, so for a
1326
+ * multichannel image the surface follows ONE band — the first visible channel (fallback 0) —
1327
+ * coloured by that channel's window/colormap (like the Plotly SURFACE, which is grayscale-only).
1328
+ * Pre-loads every slice's height data with a progress bar (as the volume does) so the stack
1329
+ * slider re-slices instantly, then builds the mesh for the current slice. All mesh + GPU work
1330
+ * lives in napari-js (`heightField` + `Viewer.addSurface`); this backend supplies scalar slices.
1331
+ */
1332
+ async mountSurface(viewer) {
1333
+ const desc = await this.ensureDescriptor();
1334
+ const multichannel = !!desc?.multichannel && (desc?.channels ?? 1) > 1;
1335
+ const states = this.store.currentChannelStates();
1336
+ this.surfaceChannel = multichannel ? states.find((s) => s.visible)?.index ?? 0 : undefined;
1337
+ this.surfaceMaxGrid = surfaceResolutionFor(this.resolutionScale).maxGrid;
1338
+ this.imageMode = 'grayscale';
1339
+ this.volumeMultichannel = false;
1340
+ await this.preloadSurfacePlanes(viewer);
1341
+ if (this.viewer !== viewer)
1342
+ return;
1343
+ await this.buildSurface(viewer, this.loaded?.z ?? 0);
1344
+ this.installSurfaceAxes(viewer);
1345
+ // Subscribe after the first build so display-state edits target a live layer.
1346
+ this.subscribeSurfaceDisplayState();
1347
+ }
1348
+ /** Add the 3D axes gizmo + DOM labels around the (origin-centered) surface mesh, matching the
1349
+ * volume/isosurface. Installed once per mount; the box tracks the mesh bounds, X/Y show the
1350
+ * physical (or pixel) extent, and Z is the intensity/height axis. */
1351
+ installSurfaceAxes(viewer) {
1352
+ if (!this.surfaceLayer)
1353
+ return;
1354
+ const b = this.surfaceLayer.bounds();
1355
+ const boxW = Math.max(1, b.max[0] - b.min[0]);
1356
+ const boxH = Math.max(1, b.max[1] - b.min[1]);
1357
+ const boxD = Math.max(1, b.max[2] - b.min[2]);
1358
+ const mppX = this.descriptor?.mppX || this.loaded?.imageInfo.imageMeta?.[0]?.mppX || 0;
1359
+ const voxel = mppX > 0 ? (mppX * (this.descriptor?.width ?? this.imageW)) / Math.max(1, this.imageW) : 1;
1360
+ this.axesLayer = viewer.addAxes(boxW, boxH, boxD, {
1361
+ voxelSize: [voxel, voxel, 1],
1362
+ visible: this.axesVisible,
1363
+ });
1364
+ if (this.host) {
1365
+ this.axesLabels = new NapariAxesLabels(this.host, viewer.camera3d, this.buildSurfaceAxesLabels(boxW, boxH, boxD, mppX));
1366
+ this.axesLabels.setVisible(this.axesVisible);
1367
+ }
1368
+ }
1369
+ /** X/Y/Z end-labels for the surface gizmo: X/Y are the physical (µm) or pixel extent of the FULL
1370
+ * image; Z is the intensity/height axis. Anchors are in the centered box (matching AxesLayer). */
1371
+ buildSurfaceAxesLabels(boxW, boxH, boxD, mppX) {
1372
+ const hx = boxW / 2;
1373
+ const hy = boxH / 2;
1374
+ const hz = boxD / 2;
1375
+ const descW = this.descriptor?.width ?? this.imageW;
1376
+ const descH = this.descriptor?.height ?? this.imageH;
1377
+ const len = (px) => (mppX > 0 ? formatUm(px * mppX) : `${px} px`);
1378
+ return [
1379
+ { anchor: [hx, -hy, -hz], text: `X · ${len(descW)}`, color: '#ed4545' },
1380
+ { anchor: [-hx, hy, -hz], text: `Y · ${len(descH)}`, color: '#4dd959' },
1381
+ { anchor: [-hx, -hy, hz], text: 'Z · intensity', color: '#668cff' },
1382
+ ];
1383
+ }
1384
+ /**
1385
+ * Pre-fetch every stack slice's luminance plane (decimated to the surface grid) into
1386
+ * {@link surfacePlanes}, driving {@link stackLoadingProgress$} — the same load-with-progress UX
1387
+ * as the volume, but keeping one 2D plane per slice rather than packing a 3D volume. Bounded
1388
+ * concurrency keeps the connection pool busy without flooding it on a deep stack.
1389
+ */
1390
+ async preloadSurfacePlanes(viewer) {
1391
+ const token = this.loadToken; // bail if a Cancel / new plot bumps this while we fetch
1392
+ const info = this.loaded?.imageInfo;
1393
+ const depth = info?.imageMeta?.[0]?.z || info?.urls?.length || 1;
1394
+ const { maxGrid } = surfaceResolutionFor(this.resolutionScale);
1395
+ this.surfacePlanes.clear();
1396
+ this.stackLoading$.next(true);
1397
+ this.stackLoadingProgress$.next(0);
1398
+ try {
1399
+ const pending = [];
1400
+ for (let z = 0; z < depth; z++)
1401
+ pending.push(z);
1402
+ let done = 0;
1403
+ const worker = async () => {
1404
+ for (;;) {
1405
+ const z = pending.shift();
1406
+ if (z === undefined || this.viewer !== viewer || this.loadToken !== token)
1407
+ return;
1408
+ try {
1409
+ this.surfacePlanes.set(z, await this.fetchSurfacePlane(z, maxGrid));
1410
+ }
1411
+ catch (err) {
1412
+ console.warn(`[napari-js] surface slice ${z} preload failed`, err);
1413
+ }
1414
+ done++;
1415
+ this.stackLoadingProgress$.next(Math.round((done / depth) * 100));
1416
+ }
1417
+ };
1418
+ const pool = Math.min(VOLUME_FETCH_CONCURRENCY, Math.max(1, depth));
1419
+ await Promise.all(Array.from({ length: pool }, () => worker()));
1420
+ }
1421
+ finally {
1422
+ this.stackLoading$.next(false);
1423
+ this.stackLoadingProgress$.next(0);
1424
+ }
1425
+ }
1426
+ /**
1427
+ * Fetch slice `z` as a single WHOLE-image luminance plane (decimated to `maxGrid`). Prefers the
1428
+ * app's complete per-slice image (`smallUrls`/`urls`, exactly like the Plotly surface) so the
1429
+ * surface always covers the FULL slice — the tile-pyramid `fetchSlice` path would fall back to a
1430
+ * single top-left tile (a corner) for self-contained / non-`/tiles/info` images. Falls back to
1431
+ * stitching the tile grid only when no complete-image URL is available.
1432
+ */
1433
+ /** Tile budget to stitch a whole slice at ~`targetPx` resolution from the pyramid: a higher target
1434
+ * pulls a FINER pyramid level (more real detail). Shared by the surface plane fetch and the volume
1435
+ * assembly so both scale their in-plane resolution with the decimate factor. */
1436
+ tileBudgetFor(targetPx) {
1437
+ const tileSize = this.descriptor?.tileSize || TILE_SIZE;
1438
+ return Math.min(MAX_STITCH_TILES, Math.max(1, Math.round((targetPx / tileSize) ** 2 * STITCH_BUDGET_COEFF)));
1439
+ }
1440
+ async fetchSurfacePlane(z, maxGrid) {
1441
+ const info = this.loaded?.imageInfo;
1442
+ const desc = await this.ensureDescriptor();
1443
+ let plane = null;
1444
+ // Preferred: stitch the WHOLE slice from the server pyramid at a resolution driven by the
1445
+ // decimate factor — a higher target grid pulls a FINER pyramid level (more real detail). With a
1446
+ // descriptor, fetchSlice stitches the whole chosen level (never a corner), then we downscale to
1447
+ // the grid. This is what makes "Full" actually higher-res than "½", not just a fixed preview.
1448
+ if (desc?.levels?.length) {
1449
+ const budget = this.tileBudgetFor(maxGrid);
1450
+ // The surface is a single decimated plane, so the composite fallback is acceptable when the
1451
+ // channel has no small pyramid level (keeps it fast); a multichannel VOLUME must not do this.
1452
+ plane = this.bitmapToLuminance(await this.fetchSlice(z, this.surfaceChannel, budget, true), maxGrid);
1453
+ }
1454
+ // Fallback (no pyramid): the app's COMPLETE per-slice image (urls[z], not the small blurry
1455
+ // thumbnail) — whole slice, avoids a corner tile. Capped at the image's own resolution.
1456
+ if (!plane) {
1457
+ const url = info?.urls?.[z] ?? info?.smallUrls?.[z];
1458
+ if (url) {
1459
+ try {
1460
+ const headers = await this.tiles
1461
+ .getAuthHeaders()
1462
+ .catch(() => ({}));
1463
+ const resp = await fetch(url, { headers });
1464
+ if (resp.ok) {
1465
+ plane = this.bitmapToLuminance(await createImageBitmap(await resp.blob()), maxGrid);
1466
+ }
1467
+ }
1468
+ catch (err) {
1469
+ console.warn(`[napari-js] surface url fetch failed for z=${z}`, err);
1470
+ }
1471
+ }
1472
+ }
1473
+ // Last resort (no pyramid and no complete image): a single tile.
1474
+ if (!plane) {
1475
+ plane = this.bitmapToLuminance(await this.fetchSlice(z, this.surfaceChannel, this.tileBudgetFor(maxGrid), true), maxGrid);
1476
+ }
1477
+ return plane;
1478
+ }
1479
+ /** The channel state driving the surface: the chosen band for multichannel (matched by index),
1480
+ * else the single grayscale channel. */
1481
+ surfaceState(channels) {
1482
+ if (this.surfaceChannel == null)
1483
+ return channels[0];
1484
+ return channels.find((s) => s.index === this.surfaceChannel) ?? channels[0];
1485
+ }
1486
+ /**
1487
+ * (Re)build the surface mesh for slice `z` from the pre-loaded plane cache (instant — this is
1488
+ * what the stack slider calls); a slice missing from the cache is fetched on demand. napari-js's
1489
+ * pure `heightField` builds the triangle grid (z = normalized intensity), then `addSurface`
1490
+ * renders it. The slice plane also feeds the intensity histogram (key 0).
1491
+ */
1492
+ async buildSurface(viewer, z) {
1493
+ let plane = this.surfacePlanes.get(z);
1494
+ if (!plane) {
1495
+ this.stackLoading$.next(true);
1496
+ try {
1497
+ plane = await this.fetchSurfacePlane(z, this.surfaceMaxGrid);
1498
+ this.surfacePlanes.set(z, plane);
1499
+ }
1500
+ catch (err) {
1501
+ console.error('[napari-js] surface slice fetch failed:', err);
1502
+ }
1503
+ finally {
1504
+ this.stackLoading$.next(false);
1505
+ }
1506
+ }
1507
+ if (!plane || plane.width < 2 || plane.height < 2 || this.viewer !== viewer)
1508
+ return;
1509
+ const st = this.surfaceState(this.store.currentChannelStates());
1510
+ const win = [st?.min ?? 0, st?.max ?? 255];
1511
+ this.surfaceWindow = win;
1512
+ // Height AND colour are normalized by the same contrast window, so changing min/max reshapes the
1513
+ // surface (a pixel's height = its intensity within [min,max]). Center it for the axes gizmo. The
1514
+ // plane is already decimated to the grid cap → stride 1.
1515
+ const zScale = SURFACE_Z_ASPECT * Math.max(plane.width, plane.height);
1516
+ const { vertices, faces, values } = heightField(plane.data, plane.width, plane.height, {
1517
+ zScale,
1518
+ zLimits: win,
1519
+ center: true,
1520
+ });
1521
+ // Preserve the orbit camera across a re-slice / window rebuild — only the first mount frames, so
1522
+ // stepping the stack or changing the window keeps the current zoom/pan/orientation.
1523
+ const cam = viewer.camera3d;
1524
+ const preserveCamera = this.surfaceLayer != null;
1525
+ const savedTarget = cam.target;
1526
+ const savedDistance = cam.distance;
1527
+ if (this.surfaceLayer) {
1528
+ viewer.layers.remove(this.surfaceLayer);
1529
+ this.surfaceLayer = null;
1530
+ }
1531
+ this.surfaceLayer = viewer.addSurface(vertices, faces, values, {
1532
+ colormap: this.volumeColormap(st),
1533
+ contrastLimits: win,
1534
+ gamma: st?.gamma ?? 1,
1535
+ wireframe: this.surfaceWireframe,
1536
+ });
1537
+ if (preserveCamera) {
1538
+ cam.target = savedTarget;
1539
+ cam.distance = savedDistance;
1540
+ }
1541
+ this.imageW = plane.width;
1542
+ this.imageH = plane.height;
1543
+ this.volumeDims = { width: plane.width, height: plane.height, depth: Math.max(1, Math.round(zScale)) };
1544
+ // Reuse the volume intensity-histogram path: the slice's scalar plane is the histogram source.
1545
+ this.volumeChannelData.clear();
1546
+ this.volumeChannelData.set(0, plane.data);
1547
+ this.scheduleReadback();
1548
+ }
1549
+ /** Subscribe the store colormap / reverse / invert / channel window → the surface, so histogram
1550
+ * & channel-dialog edits update it live without a re-fetch. **min/max reshapes the surface's
1551
+ * height** (a pixel's height = its intensity within [min,max]), so a window change rebuilds the
1552
+ * mesh geometry (from the cached slice); colour-only edits (colormap/LUT, gamma, reverse, invert)
1553
+ * just update the layer's uniforms. */
1554
+ subscribeSurfaceDisplayState() {
1555
+ this.displaySub?.unsubscribe();
1556
+ this.displaySub = combineLatest([
1557
+ this.store.getColormap(),
1558
+ this.store.getReverseScale(),
1559
+ this.store.getInvert(),
1560
+ this.store.getChannelStates(),
1561
+ ]).subscribe(([colormap, reverse, invert, channels]) => {
1562
+ this.currentColormap = colormap ?? null;
1563
+ this.currentReverse = reverse;
1564
+ this.invertEnabled = invert;
1565
+ const layer = this.surfaceLayer;
1566
+ if (!layer || !this.viewer)
1567
+ return;
1568
+ const st = this.surfaceState(channels);
1569
+ const win = [st?.min ?? 0, st?.max ?? 255];
1570
+ const windowChanged = !this.surfaceWindow || win[0] !== this.surfaceWindow[0] || win[1] !== this.surfaceWindow[1];
1571
+ if (windowChanged) {
1572
+ // Height follows the contrast window → rebuild the mesh for the new [min,max] (camera kept).
1573
+ void this.buildSurface(this.viewer, this.loaded?.z ?? 0).catch((err) => console.error('[napari-js] surface window rebuild failed:', err));
1574
+ return;
1575
+ }
1576
+ // Colour-only change: update uniforms in place, no geometry rebuild.
1577
+ layer.colormap = this.volumeColormap(st);
1578
+ if (st) {
1579
+ layer.contrastLimits = win;
1580
+ layer.gamma = st.gamma;
1581
+ }
1582
+ this.viewer.requestRender();
1583
+ });
1584
+ }
1585
+ fitCameraSoon() {
1586
+ const run = () => {
1587
+ if (this.viewer && this.canvas && this.imageW > 0 && this.imageH > 0) {
1588
+ this.viewer.camera.fit(this.imageW, this.imageH, this.canvas.clientWidth || this.imageW, this.canvas.clientHeight || this.imageH);
1589
+ }
1590
+ };
1591
+ if (typeof requestAnimationFrame === 'function')
1592
+ requestAnimationFrame(run);
1593
+ else
1594
+ run();
1595
+ }
1596
+ /** @deprecated Plotly-specific; host re-drives plot() from its image stream. */
1597
+ reloadAndPlot() {
1598
+ /* no-op */
1599
+ }
1600
+ reset() {
1601
+ this.loadToken++; // invalidate any in-flight frame loading from the previous plot
1602
+ this.displaySub?.unsubscribe();
1603
+ this.displaySub = null;
1604
+ this.scaleBar?.destroy();
1605
+ this.scaleBar = null;
1606
+ this.regionOverlay?.destroy();
1607
+ this.regionOverlay = null;
1608
+ this.axesLabels?.destroy();
1609
+ this.axesLabels = null;
1610
+ this.zHandle?.destroy();
1611
+ this.zHandle = null;
1612
+ this.volumeWorldBase = null;
1613
+ this.cachedImage = null;
1614
+ this.cachedImageSource = null;
1615
+ this.lastPixelsRect = null;
1616
+ this.nativeHistograms.clear();
1617
+ this.tiled = false;
1618
+ this.histSamples.clear();
1619
+ if (this.readbackTimer != null) {
1620
+ clearTimeout(this.readbackTimer);
1621
+ this.readbackTimer = null;
1622
+ }
1623
+ this.cameraReadbackOff?.();
1624
+ this.cameraReadbackOff = null;
1625
+ this.channelView = null;
1626
+ this.viewer?.dispose();
1627
+ this.viewer = null;
1628
+ if (this.canvas && this.host?.contains(this.canvas))
1629
+ this.host.removeChild(this.canvas);
1630
+ this.canvas = null;
1631
+ this.lastPixels = null;
1632
+ this.volumeView = null;
1633
+ this.volumeMultichannel = false;
1634
+ this.surfaceLayer = null;
1635
+ this.surfaceChannel = undefined;
1636
+ this.surfacePlanes.clear();
1637
+ this.surfaceWindow = null;
1638
+ this.scatterRegionSub?.unsubscribe();
1639
+ this.scatterRegionSub = null;
1640
+ this.scatter2dPoints = null;
1641
+ this.scatter3dLayer = null;
1642
+ this.axesLayer = null;
1643
+ this.volumeDims = null;
1644
+ this.volumeChannelData.clear();
1645
+ }
1646
+ relayout(_trueImageSize) {
1647
+ this.viewer?.requestRender();
1648
+ }
1649
+ /** @deprecated Plotly-specific axis reset. */
1650
+ resetAxes() {
1651
+ this.fitCameraSoon();
1652
+ }
1653
+ /** @deprecated Plotly-specific autoscale. */
1654
+ autoscale() {
1655
+ this.fitCameraSoon();
1656
+ this.autoscaleEvent$.next(undefined);
1657
+ }
1658
+ zoomIn() {
1659
+ if (this.viewer)
1660
+ this.viewer.camera.zoom = this.viewer.camera.zoom * 1.3;
1661
+ }
1662
+ zoomOut() {
1663
+ if (this.viewer)
1664
+ this.viewer.camera.zoom = this.viewer.camera.zoom / 1.3;
1665
+ }
1666
+ setDragMode(_mode) {
1667
+ // No-op: pan and zoom are always the napari camera's default gestures, and pan/zoom gating for
1668
+ // drawing is owned by the region overlay's setMode (setControlsEnabled). Toggling controls here
1669
+ // would fight that — the host calls setDragMode(false) alongside overlay.setMode(<tool>).
1670
+ }
1671
+ setNavigatorVisible(_visible) {
1672
+ /* napari-js has no minimap; no-op */
1673
+ }
1674
+ setImageSmoothingEnabled(enabled) {
1675
+ this.imageSmoothing = enabled;
1676
+ // Apply live to the rendered image layers; baked into the next render too.
1677
+ this.channelView?.setInterpolation(enabled ? 'linear' : 'nearest');
1678
+ }
1679
+ setShowStack(_showstack) {
1680
+ /* stack navigated via setZIndex */
1681
+ }
1682
+ setZIndex(zIndex) {
1683
+ if (this.loaded)
1684
+ this.loaded.z = zIndex;
1685
+ const v = this.viewer;
1686
+ if (!v)
1687
+ return;
1688
+ // Surface: one slice → one mesh, so re-build the height field for the new slice.
1689
+ if (this.surfaceLayer) {
1690
+ void this.buildSurface(v, zIndex).catch((err) => console.error('[napari-js] setZIndex surface failed:', err));
1691
+ return;
1692
+ }
1693
+ // Volume / isosurface: step the volume's z plane in place.
1694
+ if (this.volumeView) {
1695
+ v.dims.z = zIndex;
1696
+ this.scheduleReadback();
1697
+ return;
1698
+ }
1699
+ // Tiled 2D image: just move the dims plane — the tiled visual fetches the new slice's tiles
1700
+ // (cached per z), no layer rebuild. Refresh the coarse histogram sample for the new slice.
1701
+ if (this.tiled) {
1702
+ v.dims.z = zIndex;
1703
+ if (this.descriptor)
1704
+ void this.refreshHistogramSamples(zIndex, this.descriptor);
1705
+ this.scheduleReadback();
1706
+ return;
1707
+ }
1708
+ // 2D image (stitch fallback): re-render the slice (re-fetches per-channel / composite). Branch
1709
+ // of a volume layer (not currentPlotType, which could be stale and silently no-op the swap).
1710
+ // The token lets renderImage drop a superseded scrub so a slow older slice can't clobber a newer one.
1711
+ const req = ++this.sliceReq;
1712
+ void this.renderImage(zIndex, req)
1713
+ .then(() => {
1714
+ if (req === this.sliceReq)
1715
+ this.scheduleReadback();
1716
+ })
1717
+ .catch((err) => console.error('[napari-js] setZIndex slice failed:', err));
1718
+ }
1719
+ setStackLoading(stackLoading) {
1720
+ this.stackLoading$.next(stackLoading);
1721
+ }
1722
+ /** Cancel in-flight frame loading: bump the load generation so the volume-assembly / surface
1723
+ * preload workers stop fetching more frames, and clear the loading flag + progress. */
1724
+ cancelLoading() {
1725
+ this.loadToken++;
1726
+ this.stackLoading$.next(false);
1727
+ this.stackLoadingProgress$.next(0);
1728
+ }
1729
+ isStackLoading() {
1730
+ return this.stackLoading$.asObservable();
1731
+ }
1732
+ getStackLoadingProgress() {
1733
+ return this.stackLoadingProgress$.asObservable();
1734
+ }
1735
+ getTrueImageSize() {
1736
+ return this.imageW > 0 && this.imageH > 0 ? { width: this.imageW, height: this.imageH } : null;
1737
+ }
1738
+ getCurrentImage() {
1739
+ return Promise.resolve(null);
1740
+ }
1741
+ getDisplayedPixelData() {
1742
+ return this.lastPixels;
1743
+ }
1744
+ getDisplayedSourceRect() {
1745
+ const v = this.viewer;
1746
+ const size = this.getTrueImageSize();
1747
+ if (!v || !size)
1748
+ return null;
1749
+ const r = v.visibleWorldRect();
1750
+ const x = Math.max(0, r.x);
1751
+ const y = Math.max(0, r.y);
1752
+ return {
1753
+ x,
1754
+ y,
1755
+ width: Math.min(size.width, r.x + r.width) - x,
1756
+ height: Math.min(size.height, r.y + r.height) - y,
1757
+ };
1758
+ }
1759
+ downloadImage() {
1760
+ void this.exportComposite();
1761
+ }
1762
+ setPlotType(plotType) {
1763
+ this.currentPlotType = plotType;
1764
+ }
1765
+ /** Map a Plotly-style 3D drag mode onto napari-js's camera drag mode. */
1766
+ setSurfaceDragMode(mode) {
1767
+ if (!this.viewer)
1768
+ return;
1769
+ const m = mode === 'pan' ? 'pan' : mode === 'zoom' ? 'zoom' : 'rotate'; // orbit/turntable → rotate
1770
+ this.viewer.setCameraDragMode(m);
1771
+ }
1772
+ /** Re-frame the 3D camera on the current 3D layer. A surface mesh sits in the positive octant
1773
+ * (not centred), so frame it by its own bounds; a volume/isosurface uses its centred box. */
1774
+ resetSurfaceCamera() {
1775
+ if (!this.viewer)
1776
+ return;
1777
+ const meshLayer = this.surfaceLayer ?? this.scatter3dLayer;
1778
+ if (meshLayer) {
1779
+ const b = meshLayer.bounds();
1780
+ this.viewer.camera3d.target = b.center;
1781
+ this.viewer.camera3d.distance = Math.max(b.radius * 2.5, 1e-3);
1782
+ this.viewer.requestRender();
1783
+ return;
1784
+ }
1785
+ const d = this.volumeDims;
1786
+ if (d)
1787
+ this.viewer.camera3d.frame(d.width, d.height, d.depth);
1788
+ }
1789
+ getAutoscaleEvent() {
1790
+ return this.autoscaleEvent$.asObservable();
1791
+ }
1792
+ getPlotTypeDescriptors() {
1793
+ // The WebGPU napari-js options, offered alongside (not replacing) the OSD/Plotly types.
1794
+ return [
1795
+ PLOT_TYPE_DESCRIPTORS[PlotType.NAPARI_IMAGE],
1796
+ PLOT_TYPE_DESCRIPTORS[PlotType.NAPARI_SCATTER],
1797
+ PLOT_TYPE_DESCRIPTORS[PlotType.NAPARI_SURFACE],
1798
+ PLOT_TYPE_DESCRIPTORS[PlotType.NAPARI_SCATTER3D],
1799
+ PLOT_TYPE_DESCRIPTORS[PlotType.NAPARI_VOLUME],
1800
+ PLOT_TYPE_DESCRIPTORS[PlotType.NAPARI_ISOSURFACE],
1801
+ ];
1802
+ }
1803
+ /** The napari 3D decimate factor (1 = full … 8 = ⅛). Read by the toolbar to init the control. */
1804
+ getResolutionScale() {
1805
+ return this.resolutionScale;
1806
+ }
1807
+ /** Set the decimate factor for the napari 3D types. Takes effect on the next (re)load — the host
1808
+ * re-plots after calling this, since decimation changes the fetched/assembled data. */
1809
+ setResolutionScale(scale) {
1810
+ this.resolutionScale = Math.max(1, Math.round(scale));
1811
+ }
1812
+ getIntensityProfile$() {
1813
+ return this.intensityProfile$.asObservable();
1814
+ }
1815
+ renderIntensityInset(_divId, _profiles) {
1816
+ /* Plotly owns the intensity inset */
1817
+ }
1818
+ /** Read the displayed composite into `lastPixels` (the pixel-tools' source) + emit the clamped
1819
+ * visible region. A fresh PixelData object means cachedImageData() rebuilds automatically. */
1820
+ async runReadback() {
1821
+ const v = this.viewer;
1822
+ if (!v)
1823
+ return;
1824
+ try {
1825
+ const px = await v.readDisplayedPixels();
1826
+ if (this.viewer !== v)
1827
+ return;
1828
+ const rect = v.visibleWorldRect(); // capture WITH the pixels (same camera)
1829
+ this.lastPixels = px;
1830
+ this.lastPixelsRect = rect;
1831
+ const size = this.getTrueImageSize();
1832
+ if (size && rect) {
1833
+ const x = Math.max(0, Math.min(size.width, rect.x));
1834
+ const y = Math.max(0, Math.min(size.height, rect.y));
1835
+ const width = Math.max(1, Math.min(size.width - x, rect.width));
1836
+ const height = Math.max(1, Math.min(size.height - y, rect.height));
1837
+ this.viewportChange$.next({ x, y, width, height });
1838
+ }
1839
+ }
1840
+ catch {
1841
+ /* readback unavailable */
1842
+ }
1843
+ }
1844
+ scheduleReadback() {
1845
+ if (!this.viewer)
1846
+ return;
1847
+ if (typeof requestAnimationFrame === 'function')
1848
+ requestAnimationFrame(() => void this.runReadback());
1849
+ else
1850
+ setTimeout(() => void this.runReadback(), 0);
1851
+ }
1852
+ /** Debounced readback — armed on camera changes so `lastPixels` tracks the current view after a
1853
+ * pan/zoom settles (and after tiled levels finish loading), which the on-canvas pixel tools
1854
+ * (wand/brush/SAM) read synchronously. Coalesces rapid changes. */
1855
+ armReadback(delayMs = 250) {
1856
+ if (this.readbackTimer != null)
1857
+ clearTimeout(this.readbackTimer);
1858
+ this.readbackTimer = setTimeout(() => {
1859
+ this.readbackTimer = null;
1860
+ void this.runReadback();
1861
+ }, delayMs);
1862
+ }
1863
+ // ── IRegionStore + classification colours ──────────────────────────────────
1864
+ // Inherited from BaseStoreVisualizer — pure delegations to the shared
1865
+ // RegionStore / VisualizerStore (identical to the OSD backend).
1866
+ // ── Pixel tools: reuse the shared, backend-agnostic tool services with a napari host ───────
1867
+ /** Build the coordinate transform + tool hosts for the current viewer (called from plot()). The
1868
+ * pixel tools read displayed pixels synchronously from the last readback and convert pointer
1869
+ * coords via the napari camera, exactly mirroring the OSD host. */
1870
+ buildToolHosts() {
1871
+ if (!this.viewer)
1872
+ return;
1873
+ this.coordTransform = new NapariCoordinateTransform(this.viewer, () => !!this.viewer && this.imageW > 0);
1874
+ const regions = this.regionStore;
1875
+ this.wandHost = {
1876
+ getRegions: () => regions.getRegions(),
1877
+ setRegions: (r) => regions.setRegions(r),
1878
+ getCachedImageData: () => this.cachedImageData(),
1879
+ getActiveFrameIndex: () => this.loaded?.z ?? 0,
1880
+ getOverlayContainer: () => this.host,
1881
+ getCoordinateTransform: () => this.coordTransform,
1882
+ getFileName: () => this.loaded?.filename,
1883
+ getShapeColor: () => regions.getShapeColor(),
1884
+ };
1885
+ this.eraserHost = {
1886
+ getRegions: () => regions.getRegions(),
1887
+ setRegions: (r) => regions.setRegions(r),
1888
+ invalidateWandRegion: () => this.wandTool.clearActiveRegion(),
1889
+ getOverlayContainer: () => this.host,
1890
+ getCoordinateTransform: () => this.coordTransform,
1891
+ getCachedImageRatio: () => this.cachedImageData()?.ratios[0] ?? 1,
1892
+ };
1893
+ }
1894
+ zoomBoxHost() {
1895
+ return {
1896
+ getPlotDiv: () => this.plotDivId,
1897
+ pixelToData: (px, py) => {
1898
+ const rect = this.host?.getBoundingClientRect();
1899
+ if (!this.viewer || !rect)
1900
+ return { x: 0, y: 0 };
1901
+ const [x, y] = this.viewer.canvasToWorld(rect.left + px, rect.top + py);
1902
+ return { x, y };
1903
+ },
1904
+ applyZoomToBox: (coords) => this.applyZoomToBox(coords),
1905
+ };
1906
+ }
1907
+ /** Zoom/pan the camera to fit a data-space rectangle `[xMin, xMax, yMax, yMin]`. */
1908
+ applyZoomToBox(coordinates) {
1909
+ const v = this.viewer;
1910
+ if (!v || !this.canvas || coordinates.length < 4)
1911
+ return;
1912
+ const [xMin, xMax, yA, yB] = coordinates;
1913
+ const x0 = Math.min(xMin, xMax);
1914
+ const x1 = Math.max(xMin, xMax);
1915
+ const y0 = Math.min(yA, yB);
1916
+ const y1 = Math.max(yA, yB);
1917
+ const w = Math.max(1, x1 - x0);
1918
+ const h = Math.max(1, y1 - y0);
1919
+ const vw = this.canvas.clientWidth || w;
1920
+ const vh = this.canvas.clientHeight || h;
1921
+ v.camera.center = [(x0 + x1) / 2, (y0 + y1) / 2];
1922
+ v.camera.zoom = Math.min(vw / w, vh / h);
1923
+ v.requestRender();
1924
+ }
1925
+ /** Build CachedImageData from the most recent readback (RGBA device pixels → [y][x]=[r,g,b]),
1926
+ * with ratios/origin mapping image coords ↔ readback pixels. Cached until the readback changes. */
1927
+ cachedImageData() {
1928
+ const px = this.lastPixels;
1929
+ if (!px || !this.viewer)
1930
+ return null;
1931
+ if (this.cachedImage && this.cachedImageSource === px)
1932
+ return this.cachedImage;
1933
+ const w = px.width;
1934
+ const h = px.height;
1935
+ const data = px.data;
1936
+ const matrix = new Array(h);
1937
+ for (let y = 0; y < h; y++) {
1938
+ const row = new Array(w);
1939
+ const base = y * w * 4;
1940
+ for (let x = 0; x < w; x++) {
1941
+ const o = base + x * 4;
1942
+ row[x] = [data[o], data[o + 1], data[o + 2]];
1943
+ }
1944
+ matrix[y] = row;
1945
+ }
1946
+ // Use the rect captured WITH this readback (not the live one) so ratios/origin match the
1947
+ // matrix's camera — otherwise a pan/zoom since the readback mis-scales the traced region.
1948
+ const rect = this.lastPixelsRect ?? this.viewer.visibleWorldRect();
1949
+ this.cachedImage = {
1950
+ frames: [matrix],
1951
+ width: w,
1952
+ height: h,
1953
+ ratios: [rect.width / w, rect.height / h],
1954
+ isGrayscale: false,
1955
+ originX: rect.x,
1956
+ originY: rect.y,
1957
+ };
1958
+ this.cachedImageSource = px;
1959
+ return this.cachedImage;
1960
+ }
1961
+ // ── IToolController ────────────────────────────────────────────────────────
1962
+ // Control gating: a tool that ACTIVATES disables the camera controls; deactivation is a no-op
1963
+ // because the host always calls the region overlay's setMode FIRST in each toggle (which sets
1964
+ // the baseline enabled/disabled), so re-enabling here would fight a freshly-activated draw mode.
1965
+ setWandMode(active, options) {
1966
+ if (!this.viewer)
1967
+ return; // no plot yet → nothing to drive
1968
+ if (!active) {
1969
+ this.wandTool.setMode(false);
1970
+ return;
1971
+ }
1972
+ this.viewer?.setControlsEnabled(false);
1973
+ this.cachedImageSource = null;
1974
+ this.armReadback(0);
1975
+ if (this.wandHost)
1976
+ this.wandTool.bindHost(this.wandHost);
1977
+ this.wandTool.setMode(true, (options ?? {}));
1978
+ }
1979
+ setWandOptions(options) {
1980
+ this.wandTool.setOptions((options ?? {}));
1981
+ }
1982
+ clearActiveWandRegion() {
1983
+ this.wandTool.clearActiveRegion();
1984
+ }
1985
+ setBrushMode(active, options) {
1986
+ if (!this.viewer)
1987
+ return;
1988
+ if (!active) {
1989
+ this.brushTool.setMode(false);
1990
+ return;
1991
+ }
1992
+ this.viewer?.setControlsEnabled(false);
1993
+ this.cachedImageSource = null;
1994
+ this.armReadback(0);
1995
+ if (this.wandHost)
1996
+ this.brushTool.bindHost(this.wandHost);
1997
+ this.brushTool.setMode(true, (options ?? {}));
1998
+ }
1999
+ setBrushOptions(options) {
2000
+ if (options?.size != null)
2001
+ this.brushTool.setSize(options.size);
2002
+ }
2003
+ setVertexEraserMode(active) {
2004
+ if (!this.viewer)
2005
+ return;
2006
+ if (active) {
2007
+ this.viewer?.setControlsEnabled(false);
2008
+ this.cachedImageSource = null;
2009
+ if (this.eraserHost)
2010
+ this.eraserTool.bindHost(this.eraserHost);
2011
+ }
2012
+ this.eraserTool.setMode(active);
2013
+ }
2014
+ setVertexEraserRadius(radius) {
2015
+ this.eraserTool.setRadius(radius);
2016
+ }
2017
+ setZoomToBoxMode(active) {
2018
+ if (!this.viewer)
2019
+ return;
2020
+ if (active) {
2021
+ this.viewer?.setControlsEnabled(false);
2022
+ this.zoomToBoxTool.bindHost(this.zoomBoxHost());
2023
+ }
2024
+ this.zoomToBoxTool.setMode(active);
2025
+ }
2026
+ // SAM / cellpose — server round-trips that read the drawn rectangles + displayed pixels through
2027
+ // the same wand host (rectangles from the RegionStore, image from the readback).
2028
+ async segmentRectangles() {
2029
+ if (!this.viewer || !this.wandHost)
2030
+ return 0;
2031
+ await this.runReadback(); // the SAM embedding samples the currently-displayed image
2032
+ this.cachedImageSource = null;
2033
+ this.samTool.bindHost(this.wandHost);
2034
+ return this.samTool.segmentBoxes();
2035
+ }
2036
+ async segmentRectanglesCellpose() {
2037
+ if (!this.viewer || !this.wandHost || !this.cellSegmenter)
2038
+ return 0;
2039
+ await this.runReadback();
2040
+ this.cachedImageSource = null;
2041
+ this.cellSegmentTool.bindHost(this.wandHost);
2042
+ return this.cellSegmentTool.segmentBoxes(this.cellSegmenter);
2043
+ }
2044
+ setSamModel(id) {
2045
+ this.samTool.setModel(id);
2046
+ this.samPointTool.setModel(id);
2047
+ }
2048
+ setSamPointMode(active) {
2049
+ if (!this.viewer)
2050
+ return;
2051
+ if (active) {
2052
+ this.viewer?.setControlsEnabled(false);
2053
+ this.cachedImageSource = null;
2054
+ this.armReadback(0); // refresh the readback the click's embedding will sample
2055
+ if (this.wandHost)
2056
+ this.samPointTool.bindHost(this.wandHost);
2057
+ }
2058
+ this.samPointTool.setMode(active);
2059
+ }
2060
+ commitSamPoints() {
2061
+ this.samPointTool.commit();
2062
+ }
2063
+ clearSamPoints() {
2064
+ this.samPointTool.clear();
2065
+ }
2066
+ // ── IDisplayOptions ───────────────────────────────────────────────────────
2067
+ // Inherited from BaseStoreVisualizer — pure delegations to the shared
2068
+ // VisualizerStore (identical to the OSD backend).
2069
+ // ── IIntensitySampling: Plotly owns sampling; emit viewport changes ───────
2070
+ ensureIntensitySampling(_imageInfo, _zIndex) {
2071
+ return Promise.resolve();
2072
+ }
2073
+ refreshIntensitySamplingForRoi(_x, _y, _width, _height, _zIndex) { }
2074
+ getViewportChange$() {
2075
+ return this.viewportChange$.asObservable();
2076
+ }
2077
+ // ── IVisualizer composite members ─────────────────────────────────────────
2078
+ getRegionOverlay() {
2079
+ return this.regionOverlay;
2080
+ }
2081
+ getIsosurfaceControls() {
2082
+ if (!this.volumeView)
2083
+ return null;
2084
+ return {
2085
+ setIsoRange: (isoMin, isoMax) => {
2086
+ // Apply to every channel's volume layer.
2087
+ for (const layer of this.volumeView?.layers ?? []) {
2088
+ layer.contrastLimits = [isoMin, isoMax];
2089
+ layer.rendering = 'iso';
2090
+ layer.isoThreshold = 0.5;
2091
+ }
2092
+ this.viewer?.requestRender();
2093
+ },
2094
+ };
2095
+ }
2096
+ getIntensityControls() {
2097
+ return null;
2098
+ }
2099
+ getSurface3dControls() {
2100
+ if (!this.volumeView && !this.surfaceLayer && !this.scatter3dLayer)
2101
+ return null;
2102
+ return {
2103
+ setSurfaceDragMode: (mode) => this.setSurfaceDragMode(mode),
2104
+ resetSurfaceCamera: () => this.resetSurfaceCamera(),
2105
+ setAxesVisible: (visible) => {
2106
+ this.axesVisible = visible;
2107
+ this.axesLabels?.setVisible(visible);
2108
+ if (this.axesLayer) {
2109
+ this.axesLayer.visible = visible;
2110
+ this.viewer?.requestRender();
2111
+ }
2112
+ },
2113
+ axesVisible: () => this.axesVisible,
2114
+ // Surface wireframe (napari-js surface only) — a live layer property, no rebuild needed.
2115
+ setWireframe: (on) => {
2116
+ this.surfaceWireframe = on;
2117
+ if (this.surfaceLayer) {
2118
+ this.surfaceLayer.wireframe = on;
2119
+ this.viewer?.requestRender();
2120
+ }
2121
+ },
2122
+ wireframe: () => this.surfaceWireframe,
2123
+ };
2124
+ }
2125
+ getHistogram(channelIndex, bins) {
2126
+ const v = this.viewer;
2127
+ if (!v)
2128
+ return null;
2129
+ // Volume / isosurface: intensity histogram of the assembled (downsampled) uint8 volume for the
2130
+ // requested channel (multichannel) or the single grayscale volume (key 0).
2131
+ if (this.volumeChannelData.size) {
2132
+ const data = this.volumeChannelData.get(channelIndex) ?? this.volumeChannelData.get(0);
2133
+ return data ? this.toIHistogram(histogramScalar(data, bins, 0, 255)) : null;
2134
+ }
2135
+ // Tiled mode has no full in-memory pixels → use the coarse per-channel sample (RGB: readback).
2136
+ if (this.tiled) {
2137
+ if (this.imageMode === 'rgb')
2138
+ return this.rgbHistogram(channelIndex, bins);
2139
+ const sample = this.histSamples.get(this.imageMode === 'grayscale' ? 0 : channelIndex);
2140
+ return sample ? this.toIHistogram(histogramScalar(sample, bins, 0, 255)) : null;
2141
+ }
2142
+ // Grayscale/multichannel (stitch): native per-channel histogram straight from the in-memory
2143
+ // scalar layer (no GPU readback). RGB: bin the displayed pixels' R/G/B byte (8-bit client path).
2144
+ const layer = this.channelView?.layers[this.imageMode === 'grayscale' ? 0 : channelIndex];
2145
+ if (layer) {
2146
+ const h = v.layerHistogram(layer, bins);
2147
+ if (h)
2148
+ return this.toIHistogram(h);
2149
+ }
2150
+ if (this.imageMode === 'rgb')
2151
+ return this.rgbHistogram(channelIndex, bins);
2152
+ return null;
2153
+ }
2154
+ getHistogram$(channelIndex, bins) {
2155
+ // >8-bit channels: fetch the true native distribution from the server (the displayed pixels
2156
+ // are 8-bit, so the client histogram would be clipped). 8-bit channels use the client path.
2157
+ const bitDepth = this.descriptor?.channelInfo?.[channelIndex]?.bitDepth ?? 8;
2158
+ if (bitDepth > 8) {
2159
+ const z = this.loaded?.z ?? 0;
2160
+ const key = `${z}|${channelIndex}`;
2161
+ const cached = this.nativeHistograms.get(key);
2162
+ if (cached)
2163
+ return of(cached);
2164
+ return from(this.fetchNativeHistogram(channelIndex, bins, z, key));
2165
+ }
2166
+ return of(this.getHistogram(channelIndex, bins));
2167
+ }
2168
+ /** Fetch + cache one channel's native-bit-depth histogram from `GET /histogram` (>8-bit). */
2169
+ async fetchNativeHistogram(channel, bins, z, key) {
2170
+ const infoB64 = this.tiles.getSelectedInfoB64();
2171
+ if (!infoB64)
2172
+ return null;
2173
+ const headers = await this.tiles
2174
+ .getAuthHeaders()
2175
+ .catch(() => ({}));
2176
+ const url = `${this.api}histogram?info=${infoB64}&channel=${channel}&z=${z}&bins=${bins}`;
2177
+ try {
2178
+ const resp = await fetch(url, { headers });
2179
+ if (!resp.ok)
2180
+ return null; // 202 (still caching) or transient → null; the pane retries
2181
+ const hi = (await resp.json());
2182
+ if (!hi?.counts)
2183
+ return null;
2184
+ const out = {
2185
+ bins: hi.counts.map((_, i) => hi.rangeMin + i * hi.binWidth),
2186
+ counts: hi.counts,
2187
+ max: hi.counts.reduce((m, c) => (c > m ? c : m), 0),
2188
+ bitDepth: hi.bitDepth,
2189
+ rangeMin: hi.rangeMin,
2190
+ rangeMax: hi.rangeMax,
2191
+ observedMin: hi.observedMin,
2192
+ observedMax: hi.observedMax,
2193
+ };
2194
+ this.nativeHistograms.set(key, out);
2195
+ return out;
2196
+ }
2197
+ catch (err) {
2198
+ console.warn('[napari-js] native histogram fetch failed', err);
2199
+ return null;
2200
+ }
2201
+ }
2202
+ /** Per-channel histogram of the displayed RGB composite (R/G/B byte), from the last readback. */
2203
+ rgbHistogram(channelIndex, bins) {
2204
+ const px = this.lastPixels;
2205
+ if (!px)
2206
+ return null;
2207
+ const band = Math.max(0, Math.min(2, channelIndex));
2208
+ const n = Math.max(1, Math.min(256, Math.floor(bins) || 256));
2209
+ const counts = new Array(n).fill(0);
2210
+ const data = px.data;
2211
+ for (let i = 0; i < data.length; i += 4) {
2212
+ if (data[i + 3] === 0)
2213
+ continue; // skip transparent padding
2214
+ counts[Math.min(n - 1, (data[i + band] * n) >> 8)]++;
2215
+ }
2216
+ const binsArr = Array.from({ length: n }, (_, i) => (i * 256) / n);
2217
+ return { bins: binsArr, counts, max: counts.reduce((m, c) => (c > m ? c : m), 0) };
2218
+ }
2219
+ exportComposite() {
2220
+ const v = this.viewer;
2221
+ if (!v)
2222
+ return;
2223
+ void v.screenshot().then((blob) => {
2224
+ const url = URL.createObjectURL(blob);
2225
+ const a = document.createElement('a');
2226
+ a.href = url;
2227
+ a.download = 'napari-js.png';
2228
+ a.click();
2229
+ URL.revokeObjectURL(url);
2230
+ });
2231
+ }
2232
+ /** Native-bit-depth (16/32-bit) multi-band TIFF export via the server `/export/tiff` endpoint —
2233
+ * the displayed PNG is an 8-bit figure, this preserves the true pixel values. Visible channels
2234
+ * only (omitted when all are visible → server default). Mirrors the OSD backend. */
2235
+ async exportData() {
2236
+ const infoB64 = this.tiles.getSelectedInfoB64();
2237
+ if (!infoB64)
2238
+ return;
2239
+ const states = this.store.currentChannelStates();
2240
+ const visible = states.filter((c) => c.visible).map((c) => c.index);
2241
+ const chParam = visible.length && visible.length < states.length ? `&channels=${visible.join(',')}` : '';
2242
+ const z = this.loaded?.z ?? 0;
2243
+ const url = `${this.api}export/tiff?info=${infoB64}&z=${z}${chParam}`;
2244
+ const stem = (this.loaded?.filename || 'image').replace(/\.[^.]+$/, '');
2245
+ const headers = await this.tiles
2246
+ .getAuthHeaders()
2247
+ .catch(() => ({}));
2248
+ try {
2249
+ const resp = await fetch(url, { headers });
2250
+ if (resp.status === 202) {
2251
+ console.warn('[napari-js] 16-bit export: file still caching — try again shortly.');
2252
+ return;
2253
+ }
2254
+ if (!resp.ok)
2255
+ throw new Error(`status ${resp.status}`);
2256
+ saveAs(await resp.blob(), `${stem}_16bit.ome.tif`);
2257
+ }
2258
+ catch (err) {
2259
+ console.warn('[napari-js] 16-bit TIFF export failed', err);
2260
+ }
2261
+ }
2262
+ unsubscribe() {
2263
+ this.reset();
2264
+ }
2265
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: NapariVisualizerService, deps: [{ token: TILE_ACCESS_PORT }, { token: i1.VisualizerStore }, { token: i2.RegionStore }, { token: i3.WandToolService }, { token: i4.BrushToolService }, { token: i5.VertexEraserToolService }, { token: i6.ZoomToBoxToolService }, { token: i7.SamToolService }, { token: i8.SamPointToolService }, { token: i9.CellSegmentToolService }, { token: CELL_SEGMENTER, optional: true }, { token: i10.SimpleSliceAccessService }, { token: VIZ_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable }); }
2266
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: NapariVisualizerService, providedIn: 'root' }); }
2267
+ }
2268
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: NapariVisualizerService, decorators: [{
2269
+ type: Injectable,
2270
+ args: [{ providedIn: 'root' }]
2271
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
2272
+ type: Inject,
2273
+ args: [TILE_ACCESS_PORT]
2274
+ }] }, { type: i1.VisualizerStore }, { type: i2.RegionStore }, { type: i3.WandToolService }, { type: i4.BrushToolService }, { type: i5.VertexEraserToolService }, { type: i6.ZoomToBoxToolService }, { type: i7.SamToolService }, { type: i8.SamPointToolService }, { type: i9.CellSegmentToolService }, { type: undefined, decorators: [{
2275
+ type: Optional
2276
+ }, {
2277
+ type: Inject,
2278
+ args: [CELL_SEGMENTER]
2279
+ }] }, { type: i10.SimpleSliceAccessService }, { type: undefined, decorators: [{
2280
+ type: Inject,
2281
+ args: [VIZ_CONFIG]
2282
+ }] }] });
2283
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmFwYXJpLXZpc3VhbGl6ZXIuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvaW1wbGVtZW50YXRpb25zL25hcGFyaS1qcy9uYXBhcmktdmlzdWFsaXplci5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLFFBQVEsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUM3RCxPQUFPLEVBQWMsZUFBZSxFQUFFLE9BQU8sRUFBZ0IsYUFBYSxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFFbkcsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLFlBQVksQ0FBQztBQUNwQyxPQUFPLEVBQ0wsTUFBTSxFQUNOLGVBQWUsRUFDZixlQUFlLEVBQ2YsWUFBWSxFQUNaLGVBQWUsRUFDZixXQUFXLEVBQ1gscUJBQXFCLEVBQ3JCLHNCQUFzQixHQUN2QixNQUFNLFdBQVcsQ0FBQztBQWdCbkIsT0FBTyxFQUFFLGdCQUFnQixFQUFPLE1BQU0sOEJBQThCLENBQUM7QUFDckUsT0FBTyxFQUNMLFFBQVEsRUFFUixxQkFBcUIsRUFDckIsVUFBVSxFQUNWLGtCQUFrQixFQUNsQixlQUFlLEVBQ2YsZUFBZSxFQUNmLGlCQUFpQixFQUNqQix1QkFBdUIsR0FDeEIsTUFBTSwyQkFBMkIsQ0FBQztBQVNuQyxPQUFPLEVBRUwsYUFBYSxFQUNiLGNBQWMsR0FDZixNQUFNLHVDQUF1QyxDQUFDO0FBSS9DLE9BQU8sRUFBRSxVQUFVLEVBQWEsTUFBTSw0QkFBNEIsQ0FBQztBQUNuRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQWtCLE1BQU0sd0NBQXdDLENBQUM7QUFDMUYsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDL0QsT0FBTyxFQUFFLHdCQUF3QixFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDMUUsT0FBTyxFQUFF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MkIsQ0FBQyxDQUFDO1FBQy9DLElBQUksQ0FBQztZQUNILE1BQU0sSUFBSSxHQUFHLE1BQU0sS0FBSyxDQUFDLEdBQUcsRUFBRSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7WUFDM0MsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLEdBQUcsRUFBRSxDQUFDO2dCQUN4QixPQUFPLENBQUMsSUFBSSxDQUFDLG9FQUFvRSxDQUFDLENBQUM7Z0JBQ25GLE9BQU87WUFDVCxDQUFDO1lBQ0QsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFO2dCQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsVUFBVSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztZQUN2RCxNQUFNLENBQUMsTUFBTSxJQUFJLENBQUMsSUFBSSxFQUFFLEVBQUUsR0FBRyxJQUFJLGdCQUFnQixDQUFDLENBQUM7UUFDckQsQ0FBQztRQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7WUFDYixPQUFPLENBQUMsSUFBSSxDQUFDLHVDQUF1QyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQzdELENBQUM7SUFDSCxDQUFDO0lBQ0QsV0FBVztRQUNULElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUNmLENBQUM7OEdBMXVFVSx1QkFBdUIsa0JBb0l4QixnQkFBZ0Isd1RBVUosY0FBYyxzRUFFMUIsVUFBVTtrSEFoSlQsdUJBQXVCLGNBRFYsTUFBTTs7MkZBQ25CLHVCQUF1QjtrQkFEbkMsVUFBVTttQkFBQyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUU7OzBCQXFJN0IsTUFBTTsyQkFBQyxnQkFBZ0I7OzBCQVV2QixRQUFROzswQkFBSSxNQUFNOzJCQUFDLGNBQWM7OzBCQUVqQyxNQUFNOzJCQUFDLFVBQVUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3QsIEluamVjdGFibGUsIE9wdGlvbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBPYnNlcnZhYmxlLCBCZWhhdmlvclN1YmplY3QsIFN1YmplY3QsIFN1YnNjcmlwdGlvbiwgY29tYmluZUxhdGVzdCwgZnJvbSwgb2YgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IEltYWdlIH0gZnJvbSAnaW1hZ2UtanMnO1xuaW1wb3J0IHsgc2F2ZUFzIH0gZnJvbSAnZmlsZS1zYXZlcic7XG5pbXBvcnQge1xuICBWaWV3ZXIsXG4gIGhpc3RvZ3JhbVNjYWxhcixcbiAgY29sb3JtYXBGcm9tTHV0LFxuICB0aW50Q29sb3JtYXAsXG4gIHJldmVyc2VDb2xvcm1hcCxcbiAgaGVpZ2h0RmllbGQsXG4gIE11bHRpQ2hhbm5lbEltYWdlVmlldyxcbiAgTXVsdGlDaGFubmVsVm9sdW1lVmlldyxcbn0gZnJvbSAnbmFwYXJpLWpzJztcbmltcG9ydCB0eXBlIHtcbiAgQXhlc0xheWVyLFxuICBTdXJmYWNlTGF5ZXIsXG4gIFBvaW50c0xheWVyLFxuICBQb2ludHMzRExheWVyLFxuICBUaWxlZFNvdXJjZSxcbiAgVGlsZUtleSxcbiAgUGl4ZWxDaHVuayxcbiAgQ2hhbm5lbFZpZXcsXG4gIFZvbHVtZUNoYW5uZWwsXG4gIENvbG9ybWFwLFxufSBmcm9tICduYXBhcmktanMnO1xuXG5pbXBvcnQgeyBJSW1hZ2VJbmZvIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL2ltYWdlLmNvbnRyYWN0JztcbmltcG9ydCB7IElDaGFubmVsU3RhdGUgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvY2hhbm5lbC1oaXN0b2dyYW0tYXBpLmNvbnRyYWN0JztcbmltcG9ydCB7IGJ1aWxkQ29sb3JtYXBMdXQsIFJnYiB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9jb2xvcm1hcC1sdXQnO1xuaW1wb3J0IHtcbiAgUGxvdFR5cGUsXG4gIFBsb3RUeXBlRGVzY3JpcHRvcixcbiAgUExPVF9UWVBFX0RFU0NSSVBUT1JTLFxuICBpc05hcGFyaTNkLFxuICBpc05hcGFyaUlzb3N1cmZhY2UsXG4gIGlzTmFwYXJpU3VyZmFjZSxcbiAgaXNOYXBhcmlTY2F0dGVyLFxuICBpc05hcGFyaVNjYXR0ZXIzZCxcbiAgTkFQQVJJX0RFRkFVTFRfREVDSU1BVEUsXG59IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9wbG90LXR5cGUnO1xuaW1wb3J0IHtcbiAgSVZpc3VhbGl6ZXIsXG4gIFBpeGVsRGF0YSxcbiAgSW50ZW5zaXR5UHJvZmlsZSxcbiAgSUlzb3N1cmZhY2VDb250cm9scyxcbiAgSUludGVuc2l0eUNvbnRyb2xzLFxuICBJU3VyZmFjZTNkQ29udHJvbHMsXG59IGZyb20gJy4uLy4uL2NvbnRyYWN0cy92aXN1YWxpemVyLmNvbnRyYWN0JztcbmltcG9ydCB7XG4gIFZpZXdlckNhcGFiaWxpdGllcyxcbiAgVmlld2VyRmVhdHVyZSxcbiAgY2FwYWJpbGl0aWVzT2YsXG59IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9jYXBhYmlsaXRpZXMuY29udHJhY3QnO1xuaW1wb3J0IHsgSVJlZ2lvbk92ZXJsYXkgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvcmVnaW9uLW92ZXJsYXkuY29udHJhY3QnO1xuaW1wb3J0IHsgSUhpc3RvZ3JhbSB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9jaGFubmVsLWhpc3RvZ3JhbS1hcGkuY29udHJhY3QnO1xuaW1wb3J0IHsgQ29sb3JtYXBOb2RlLCBJV2FuZE9wdGlvbnMsIElCcnVzaE9wdGlvbnMgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvZGlzcGxheS10eXBlcyc7XG5pbXBvcnQgeyBWSVpfQ09ORklHLCBWaXpDb25maWcgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvdml6LWNvbmZpZyc7XG5pbXBvcnQgeyBUSUxFX0FDQ0VTU19QT1JULCBUaWxlQWNjZXNzUG9ydCB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9wb3J0cy90aWxlLWFjY2Vzcy5wb3J0JztcbmltcG9ydCB7IEJhc2VTdG9yZVZpc3VhbGl6ZXIgfSBmcm9tICcuLi9iYXNlLXN0b3JlLXZpc3VhbGl6ZXInO1xuaW1wb3J0IHsgU2ltcGxlU2xpY2VBY2Nlc3NTZXJ2aWNlIH0gZnJvbSAnLi4vc2ltcGxlLXNsaWNlLWFjY2Vzcy5zZXJ2aWNlJztcbmltcG9ydCB7IFZpc3VhbGl6ZXJTdG9yZSB9IGZyb20gJy4uLy4uL3N0b3JlL3Zpc3VhbGl6ZXItc3RvcmUuc2VydmljZSc7XG5pbXBvcnQgeyBSZWdpb25TdG9yZSB9IGZyb20gJy4uLy4uL3N0b3JlL3JlZ2lvbi1zdG9yZS5zZXJ2aWNlJztcbmltcG9ydCB7IE5hcGFyaVNjYWxlQmFyLCBmb3JtYXRVbSB9IGZyb20gJy4vbmFwYXJpLXNjYWxlLWJhcic7XG5pbXBvcnQgeyBOYXBhcmlSZWdpb25PdmVybGF5IH0gZnJvbSAnLi9uYXBhcmktcmVnaW9uLW92ZXJsYXknO1xuaW1wb3J0IHsgTmFwYXJpQXhlc0xhYmVscywgQXhpc0xhYmVsU3BlYyB9IGZyb20gJy4vbmFwYXJpLWF4ZXMtbGFiZWxzJztcbmltcG9ydCB7IE5hcGFyaVZvbHVtZVpIYW5kbGUgfSBmcm9tICcuL25hcGFyaS12b2x1bWUtei1oYW5kbGUnO1xuaW1wb3J0IHtcbiAgSUNvb3JkaW5hdGVUcmFuc2Zvcm0sXG59IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9jb29yZGluYXRlLXRyYW5zZm9ybS5jb250cmFjdCc7XG5pbXBvcnQgeyBXYW5kVG9vbFNlcnZpY2UsIFdhbmRUb29sSG9zdCwgQ2FjaGVkSW1hZ2VEYXRhIH0gZnJvbSAnLi4vLi4vdG9vbGJhci93YW5kL3dhbmQtdG9vbC5zZXJ2aWNlJztcbmltcG9ydCB7IFdhbmRPcHRpb25zIH0gZnJvbSAnLi4vLi4vdG9vbGJhci93YW5kL3dhbmQuc2VydmljZSc7XG5pbXBvcnQgeyBCcnVzaFRvb2xTZXJ2aWNlLCBCcnVzaE9wdGlvbnMgfSBmcm9tICcuLi8uLi90b29sYmFyL2JydXNoL2JydXNoLXRvb2wuc2VydmljZSc7XG5pbXBvcnQge1xuICBWZXJ0ZXhFcmFzZXJUb29sU2VydmljZSxcbiAgVmVydGV4RXJhc2VyVG9vbEhvc3QsXG59IGZyb20gJy4uLy4uL3Rvb2xiYXIvdmVydGV4LWVyYXNlci92ZXJ0ZXgtZXJhc2VyLXRvb2wuc2VydmljZSc7XG5pbXBvcnQge1xuICBab29tVG9Cb3hUb29sU2VydmljZSxcbiAgWm9vbVRvQm94VG9vbEhvc3QsXG59IGZyb20gJy4uLy4uL3Rvb2xiYXIvem9vbS10by1ib3gvem9vbS10by1ib3gtdG9vbC5zZXJ2aWNlJztcbmltcG9ydCB7IFNhbVRvb2xTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vdG9vbGJhci9zZWdtZW50YXRpb24vc2FtLXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBTYW1Qb2ludFRvb2xTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vdG9vbGJhci9zZWdtZW50YXRpb24vc2FtLXBvaW50LXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBDZWxsU2VnbWVudFRvb2xTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vdG9vbGJhci9zZWdtZW50YXRpb24vY2VsbC1zZWdtZW50LXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBJQ2VsbFNlZ21lbnRlciwgQ0VMTF9TRUdNRU5URVIgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvY2VsbC1zZWdtZW50ZXIuY29udHJhY3QnO1xuXG4vKiogSUNvb3JkaW5hdGVUcmFuc2Zvcm0gb3ZlciB0aGUgbmFwYXJpIGNhbWVyYTogcG9pbnRlciBjbGllbnQgY29vcmRzIOKGlCBpbWFnZS93b3JsZCBjb29yZHMuICovXG5jbGFzcyBOYXBhcmlDb29yZGluYXRlVHJhbnNmb3JtIGltcGxlbWVudHMgSUNvb3JkaW5hdGVUcmFuc2Zvcm0ge1xuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHJlYWRvbmx5IHZpZXdlcjoge1xuICAgICAgY2FudmFzVG9Xb3JsZChjbGllbnRYOiBudW1iZXIsIGNsaWVudFk6IG51bWJlcik6IFtudW1iZXIsIG51bWJlcl07XG4gICAgICByZWFkb25seSBjYW1lcmE6IHsgem9vbTogbnVtYmVyIH07XG4gICAgfSxcbiAgICBwcml2YXRlIHJlYWRvbmx5IHJlYWR5OiAoKSA9PiBib29sZWFuLFxuICApIHt9XG4gIGNsaWVudFRvRGF0YShjbGllbnRYOiBudW1iZXIsIGNsaWVudFk6IG51bWJlcik6IHsgeDogbnVtYmVyOyB5OiBudW1iZXIgfSB7XG4gICAgY29uc3QgW3gsIHldID0gdGhpcy52aWV3ZXIuY2FudmFzVG9Xb3JsZChjbGllbnRYLCBjbGllbnRZKTtcbiAgICByZXR1cm4geyB4LCB5IH07XG4gIH1cbiAgZGF0YUxlbmd0aFRvU2NyZWVuKGRhdGFMZW5ndGg6IG51bWJlcik6IG51bWJlciB7XG4gICAgcmV0dXJuIGRhdGFMZW5ndGggKiB0aGlzLnZpZXdlci5jYW1lcmEuem9vbTsgLy8gQ1NTIHB4IHBlciB3b3JsZCB1bml0XG4gIH1cbiAgaXNSZWFkeSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5yZWFkeSgpO1xuICB9XG59XG5cbi8qKiBPcGFxdWUgaGFuZGxlIGZyb20ge0BsaW5rIE5hcGFyaVZpc3VhbGl6ZXJTZXJ2aWNlLmxvYWR9LCBwYXNzZWQgYmFjayB0byBwbG90KCkuICovXG5pbnRlcmZhY2UgTmFwYXJpTG9hZGVkIHtcbiAgaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvO1xuICB6OiBudW1iZXI7XG4gIC8qKiBNdXN0IG1hdGNoIGBJSW1hZ2VJbmZvLmZpbGVOYW1lYCDigJQgdGhlIHJlbmRlciBvcmNoZXN0cmF0b3IgZHJvcHMgdGhlIHJlc3VsdCBpZiB0aGVcbiAgICogIGhhbmRsZSdzIGBmaWxlbmFtZWAgZG9lc24ndCBtYXRjaCB0aGUgcmVxdWVzdGVkIGltYWdlIChndWFyZHMgYWdhaW5zdCBzdGFsZSBjbGlja3MpLiAqL1xuICBmaWxlbmFtZTogc3RyaW5nO1xufVxuXG5jb25zdCBWT0xVTUVfTUFYX1NMSUNFID0gMTAyNDsgLy8gXCJGdWxsXCIgaW4tcGxhbmUgY2FwOyB0aGUgZGVmYXVsdCDCvCBsb2FkIHVzZXMgMjU2XG4vKiogUmVmZXJlbmNlIGluLXBsYW5lIHdvcmxkIHNpemUgKGxvbmcgc2lkZSkgZm9yIHRoZSB2b2x1bWUvYXhlcyBib3gsIGluIGFyYml0cmFyeSB3b3JsZCB1bml0cy5cbiAqICBUaGUgYm94IGlzIGFuY2hvcmVkIHRvIHRoaXMgcmVmZXJlbmNlIHJlZ2FyZGxlc3Mgb2YgdGhlIGNob3NlbiBkZWNpbWF0ZSBmYWN0b3IsIHNvIGNoYW5naW5nIHRoZVxuICogIHJlc29sdXRpb24gY2hhbmdlcyBERVRBSUwsIG5vdCB0aGUgdm9sdW1lJ3MgcHJvcG9ydGlvbnMgKFogbm8gbG9uZ2VyIGFwcGVhcnMgdG8gc2hyaW5rIHdoZW4gdGhlXG4gKiAgaW4tcGxhbmUgc2FtcGxpbmcgZ3Jvd3MpLiBTZXQgdG8gdGhlIERFRkFVTFQgZGVjaW1hdGUncyBpbi1wbGFuZSBjYXAgc28gdGhlIGRlZmF1bHQgdmlldyBpc1xuICogIHVuY2hhbmdlZDsgaGlnaGVyL2xvd2VyIHJlc29sdXRpb25zIGtlZXAgdGhhdCBzYW1lIHNoYXBlLiBTZWUge0BsaW5rIG1vdW50Vm9sdW1lfS4gKi9cbmNvbnN0IFZPTFVNRV9XT1JMRF9JTlBMQU5FX1JFRiA9IFZPTFVNRV9NQVhfU0xJQ0UgLyBOQVBBUklfREVGQVVMVF9ERUNJTUFURTtcbi8qKiBNYXggY29uY3VycmVudCBzbGljZSBmZXRjaGVzIHdoZW4gYXNzZW1ibGluZyBhIHZvbHVtZSDigJQga2VlcHMgdGhlIGNvbm5lY3Rpb24gcG9vbCBidXN5XG4gKiAgd2l0aG91dCBmbG9vZGluZyBpdCAoYnJvd3NlcnMgY2FwIH42L2hvc3QpIG9uIGEgZGVlcCBzdGFjay4gKi9cbmNvbnN0IFZPTFVNRV9GRVRDSF9DT05DVVJSRU5DWSA9IDg7XG5cbi8qKiBWb2x1bWUvaXNvc3VyZmFjZSBzYW1wbGluZyBmb3IgYSBkZWNpbWF0ZSBmYWN0b3IgYHNjYWxlYCAoMSA9IEZ1bGwgMTAyNCwgMiA9IMK9IDUxMiwgNCA9IMK8IDI1NlxuICogIGRlZmF1bHQsIDggPSDihZsgMTI4KTogdGhlIGluLXBsYW5lIGNhcCBoYWx2ZXMgZWFjaCBzdGVwOyBzbGljZXMgc3RheSB1bi1zdWJzYW1wbGVkIHVudGlsIOKFmywgdGhlblxuICogIHN1YnNhbXBsZS4gKi9cbmZ1bmN0aW9uIHZvbHVtZVJlc29sdXRpb25Gb3Ioc2NhbGU6IG51bWJlcik6IHsgbWF4U2xpY2U6IG51bWJlcjsgc2xpY2VTdGVwOiBudW1iZXIgfSB7XG4gIGNvbnN0IHMgPSBNYXRoLm1heCgxLCBNYXRoLnJvdW5kKHNjYWxlKSk7XG4gIHJldHVybiB7XG4gICAgbWF4U2xpY2U6IE1hdGgubWF4KDgsIE1hdGgucm91bmQoVk9MVU1FX01BWF9TTElDRSAvIHMpKSxcbiAgICBzbGljZVN0ZXA6IE1hdGgubWF4KDEsIE1hdGguZmxvb3IocyAvIDQpKSxcbiAgfTtcbn1cbi8qKiBGdWxseS1ub3JtYWxpemVkIGludGVuc2l0eSBoZWlnaHQgYXMgYSBmcmFjdGlvbiBvZiB0aGUgaW4tcGxhbmUgZXh0ZW50IOKAlCBtYXRjaGVzIHRoZSBQbG90bHlcbiAqICBTVVJGQUNFIHotYXNwZWN0ICh+MC40KSBzbyB0aGUgcmVsaWVmIGlzbid0IGV4YWdnZXJhdGVkLiAqL1xuY29uc3QgU1VSRkFDRV9aX0FTUEVDVCA9IDAuNDtcbi8qKiBcIkZ1bGxcIiBtZXNoIGdyaWQgY2FwOyB0aGUgZGVjaW1hdGUgZmFjdG9yIGRpdmlkZXMgaXQgKGRlZmF1bHQgwrwg4oaSIDIyMCwgwr0g4oaSIDQ0MCwgRnVsbCDihpIgODgwKS4gKi9cbmNvbnN0IFNVUkZBQ0VfTUFYX0dSSUQgPSA4ODA7XG4vKiogQ29lZmZpY2llbnQgc2NhbGluZyB0aGUgcHlyYW1pZCB0aWxlIGJ1ZGdldCB3aXRoIHRoZSB0YXJnZXQgcmVzb2x1dGlvbiDigJQgYSBoaWdoZXIgdGFyZ2V0IHB1bGxzIGFcbiAqICBmaW5lciBweXJhbWlkIGxldmVsIChtb3JlIHJlYWwgZGV0YWlsKS4gU2hhcmVkIGJ5IHRoZSBzdXJmYWNlIHBsYW5lIGZldGNoICsgdm9sdW1lIGFzc2VtYmx5LiAqL1xuY29uc3QgU1RJVENIX0JVREdFVF9DT0VGRiA9IDE2O1xuLyoqIFN1cmZhY2UgbWVzaCB0YXJnZXQgZ3JpZCBmb3IgYSBkZWNpbWF0ZSBmYWN0b3IgYHNjYWxlYDogdGhlIGdyaWQgY2FwIHNocmlua3MgYnkgYHNjYWxlYCAoZXZlcnlcbiAqICBzbGljZSBpcyBrZXB0IOKAlCB0aGUgei1zbGlkZXIgbmVlZHMgdGhlbSBhbGwpLiBUaGUgc291cmNlIGlzIGZldGNoZWQgYXQgYSBtYXRjaGluZyByZXNvbHV0aW9uLiAqL1xuZnVuY3Rpb24gc3VyZmFjZVJlc29sdXRpb25Gb3Ioc2NhbGU6IG51bWJlcik6IHsgbWF4R3JpZDogbnVtYmVyIH0ge1xuICBjb25zdCBzID0gTWF0aC5tYXgoMSwgTWF0aC5yb3VuZChzY2FsZSkpO1xuICByZXR1cm4geyBtYXhHcmlkOiBNYXRoLm1heCgxNiwgTWF0aC5yb3VuZChTVVJGQUNFX01BWF9HUklEIC8gcykpIH07XG59XG5cbi8qKiAzRCBzY2F0dGVyOiBpbi1wbGFuZSBjYXAgZm9yIHRoZSBhc3NlbWJsZWQgdm94ZWwgZ3JpZCwgYW5kIHRoZSBtYXggbnVtYmVyIG9mIHBvaW50cyBlbWl0dGVkXG4gKiAgKHRoZSBncmlkIGlzIGZsYXQtc3RyaWRlZCBkb3duIHRvIHRoaXMpIOKAlCBrZWVwcyB0aGUgYmlsbGJvYXJkIGNvdW50IGludGVyYWN0aXZlLiAqL1xuY29uc3QgU0NBVFRFUjNEX01BWF9YWSA9IDY0O1xuY29uc3QgU0NBVFRFUjNEX01BWF9QT0lOVFMgPSAxNTAwMDA7XG5cbmNvbnN0IFRJTEVfU0laRSA9IDUxMjsgLy8gc2VydmVyIHRpbGUgZWRnZSAobWF0Y2hlcyB0aGUgT1NEIGJhY2tlbmQpXG4vKiogTWF4IHRpbGVzIHN0aXRjaGVkIGZvciBvbmUgZGlzcGxheWVkIHNsaWNlICg1MTJweCB0aWxlcyDihpIgdXAgdG8gfjYxNDTCsiBhdCBmdWxsIHJlcykuIEJleW9uZFxuICogIHRoaXMgd2Ugc3RlcCB0byBhIGNvYXJzZXIgcHlyYW1pZCBsZXZlbCBzbyBhIGxhcmdlIGltYWdlIHN0YXlzIHRyYWN0YWJsZS4gKi9cbmNvbnN0IE1BWF9TVElUQ0hfVElMRVMgPSAxNDQ7XG4vKiogV2ViR1BVIGRlZmF1bHQgYG1heFRleHR1cmVEaW1lbnNpb24yRGAg4oCUIGEgc3RpdGNoZWQgc2xpY2UncyBsb25nZXN0IHNpZGUgbXVzdCBmaXQgYSB0ZXh0dXJlLiAqL1xuY29uc3QgTUFYX1RFWFRVUkVfRElNID0gODE5Mjtcbi8qKiBDb25jdXJyZW50IHRpbGUgcmVxdWVzdHMgcGVyIHN0aXRjaGVkIHNsaWNlLiBGaXJpbmcgYSB3aG9sZSBncmlkIGF0IG9uY2UgKGh1bmRyZWRzIG9mIHJlcXVlc3RzXG4gKiAgZm9yIGEgbGFyZ2UgbGV2ZWwpIG92ZXJ3aGVsbWVkIHRoZSB0aWxlIHNlcnZlciAoNTA0cyk7IGEgc21hbGwgcG9vbCBrZWVwcyB0aGUgcGlwZSBmdWxsIHdpdGhvdXRcbiAqICBmbG9vZGluZyBpdCDigJQgYW5kIHNsaWNlLWxldmVsIHdvcmtlcnMgYWxyZWFkeSBydW4gc2V2ZXJhbCBzdGl0Y2hlcyBpbiBwYXJhbGxlbCBvbiB0b3Agb2YgdGhpcy4gKi9cbmNvbnN0IFRJTEVfRkVUQ0hfQ09OQ1VSUkVOQ1kgPSA2O1xuLyoqIEhvdyBsb25nIHRvIHBvbGwgYC90aWxlcy9pbmZvYCAoMjAyIHdoaWxlIHRoZSBzZXJ2ZXIgY2FjaGVzIHRoZSBzb3VyY2UpIGJlZm9yZSBmYWxsaW5nIGJhY2sgdG9cbiAqICBhIHNpbmdsZSB0aWxlLiBHZW5lcm91cyBsaWtlIHRoZSBPU0QgYmFja2VuZCDigJQgYSBjb2xkIHdob2xlLXNsaWRlIGNhbiB0YWtlIG1pbnV0ZXMgdG8gY2FjaGUuICovXG5jb25zdCBERVNDUklQVE9SX1RJTUVPVVRfTVMgPSAxMjAwMDA7XG5cbi8qKiBEZWZhdWx0IHBlci1jaGFubmVsIHRpbnRzIChGaWppLXN0eWxlKSB3aGVuIHRoZSBzdG9yZS9kZXNjcmlwdG9yIG9mZmVycyBubyBjb2xvdXIuICovXG5jb25zdCBERUZBVUxUX1RJTlRTID0gWycjZmYwMDAwJywgJyMwMGZmMDAnLCAnIzAwMDBmZicsICcjZmZmZjAwJywgJyNmZjAwZmYnLCAnIzAwZmZmZicsICcjZmZmZmZmJ107XG5cbi8qKiBEZWZhdWx0IHRpbnQgZm9yIGEgY2hhbm5lbCBpbmRleCwgY3ljbGluZyB0aGUgRmlqaSBwYWxldHRlLiAqL1xuZnVuY3Rpb24gdGludEZvcihjaGFubmVsOiBudW1iZXIpOiBzdHJpbmcge1xuICByZXR1cm4gREVGQVVMVF9USU5UU1tjaGFubmVsICUgREVGQVVMVF9USU5UUy5sZW5ndGhdO1xufVxuXG4vKiogT25lIHB5cmFtaWQgbGV2ZWwgZnJvbSBgR0VUIC90aWxlcy9pbmZvYCAocmVzIDAgPSBmdWxsIHJlc29sdXRpb24pLiAqL1xuaW50ZXJmYWNlIFRpbGVMZXZlbCB7XG4gIHJlczogbnVtYmVyO1xuICB3aWR0aDogbnVtYmVyO1xuICBoZWlnaHQ6IG51bWJlcjtcbn1cblxuLyoqIFN1YnNldCBvZiB0aGUgYC90aWxlcy9pbmZvYCBkZXNjcmlwdG9yIHRoaXMgYmFja2VuZCByZWFkcyAodGlsZSBncmlkLCBjaGFubmVscywgc2NhbGUpLiAqL1xuaW50ZXJmYWNlIFRpbGVEZXNjcmlwdG9yIHtcbiAgd2lkdGg6IG51bWJlcjtcbiAgaGVpZ2h0OiBudW1iZXI7XG4gIHRpbGVTaXplOiBudW1iZXI7XG4gIHo6IG51bWJlcjtcbiAgY2hhbm5lbHM6IG51bWJlcjtcbiAgbXVsdGljaGFubmVsPzogYm9vbGVhbjtcbiAgLyoqIFJlYWwgQmlvLUZvcm1hdHMgbGV2ZWxzIGF0IHRoZSBmcm9udCBvZiBgbGV2ZWxzYDsgcGVyLWNoYW5uZWwgdGlsZXMgZXhpc3Qgb25seSB0aGVyZS4gKi9cbiAgcmVhbExldmVscz86IG51bWJlcjtcbiAgY2hhbm5lbEluZm8/OiBBcnJheTx7XG4gICAgbmFtZT86IHN0cmluZztcbiAgICBjb2xvcj86IHN0cmluZztcbiAgICBiaXREZXB0aD86IG51bWJlcjtcbiAgICBtaW5BbGxvd2VkPzogbnVtYmVyO1xuICAgIG1heEFsbG93ZWQ/OiBudW1iZXI7XG4gIH0+IHwgbnVsbDtcbiAgbGV2ZWxzOiBUaWxlTGV2ZWxbXTtcbiAgbXBwWD86IG51bWJlcjtcbiAgbXBwWT86IG51bWJlcjtcbn1cblxuLyoqXG4gKiBBIFdlYkdQVSBpbWFnZSBiYWNrZW5kIGJ1aWx0IG9uIHRoZSBwdWJsaXNoZWQgYG5hcGFyaS1qc2AgbGlicmFyeSDigJQgdGhlIFBPQyBlbmdpbmUgZm9yXG4gKiBqaXQtdWkjMTAyIChhIGJyb3dzZXItYmFzZWQgbmFwYXJpIHNoaXBwZWQgYXMgYSBKUyBsaWJyYXJ5LCBzd2FwcGluZyBpbWFnZSBwbG90dGluZyB3aXRoXG4gKiBPcGVuU2VhZHJhZ29uIGFuZCAzRCBzbGljaW5nL2lzb3N1cmZhY2VzIHdpdGggUGxvdGx5KS5cbiAqXG4gKiBSZW5kZXIgc3RyYXRlZ3k6IHJhdGhlciB0aGFuIHJlLWltcGxlbWVudCB0aGUgc2VydmVyJ3MgdGlsZS9weXJhbWlkIHByb3RvY29sICh3aGljaCBPU0RcbiAqIGFscmVhZHkgaGFuZGxlcyksIHRoaXMgYmFja2VuZCByZW5kZXJzIHRoZSAqKmNvbXBsZXRlIHBlci1zbGljZSBpbWFnZSBVUkxzIHRoZSBhcHAgYWxyZWFkeVxuICogcHJvZHVjZXMqKiAoYElJbWFnZUluZm8udXJsc2ApIOKAlCBgdXJsc1t6XWAgZm9yIHRoZSAyRCBpbWFnZSwgYW5kIHRoZSBmdWxsIGB1cmxzYCBzdGFja1xuICogYXNzZW1ibGVkIGludG8gYSBkb3duc2FtcGxlZCB2b2x1bWUgZm9yIHRoZSAzRCB0eXBlcy4gUmVnaW9uIHN0YXRlIGFuZCBkaXNwbGF5IG9wdGlvbnNcbiAqIGRlbGVnYXRlIHRvIHRoZSBzaGFyZWQge0BsaW5rIFJlZ2lvblN0b3JlfSAvIHtAbGluayBWaXN1YWxpemVyU3RvcmV9LCBleGFjdGx5IGFzIE9TRCBkb2VzLlxuICpcbiAqIEZvbGxvdy11cHMgKGppdC11aSMxMDIpOiBuYXRpdmUtcmVzb2x1dGlvbiBweXJhbWlkYWwgdGlsaW5nLCBvbi1jYW52YXMgdG9vbHMsIHJlZ2lvblxuICogb3ZlcmxheSByZW5kZXJpbmcsIHBlci1jaGFubmVsIGhpc3RvZ3JhbXMsIFRJRkYgZXhwb3J0LlxuICovXG5ASW5qZWN0YWJsZSh7IHByb3ZpZGVkSW46ICdyb290JyB9KVxuZXhwb3J0IGNsYXNzIE5hcGFyaVZpc3VhbGl6ZXJTZXJ2aWNlIGV4dGVuZHMgQmFzZVN0b3JlVmlzdWFsaXplciBpbXBsZW1lbnRzIElWaXN1YWxpemVyIHtcbiAgcmVhZG9ubHkgY2FwYWJpbGl0aWVzOiBWaWV3ZXJDYXBhYmlsaXRpZXMgPSBjYXBhYmlsaXRpZXNPZihbXG4gICAgVmlld2VyRmVhdHVyZS5JbWFnZURpc3BsYXksXG4gICAgVmlld2VyRmVhdHVyZS5TdGFja1NsaWRlcixcbiAgICBWaWV3ZXJGZWF0dXJlLlBpeGVsUmVhZGJhY2ssXG4gICAgVmlld2VyRmVhdHVyZS5TdXJmYWNlM0QsXG4gICAgVmlld2VyRmVhdHVyZS5Jc29zdXJmYWNlLFxuICBdKTtcblxuICBwcml2YXRlIHJlYWRvbmx5IGFwaTogc3RyaW5nO1xuXG4gIHByaXZhdGUgdmlld2VyOiBWaWV3ZXIgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBjYW52YXM6IEhUTUxDYW52YXNFbGVtZW50IHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgaG9zdDogSFRNTEVsZW1lbnQgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBsb2FkZWQ6IE5hcGFyaUxvYWRlZCB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGxhc3RQaXhlbHM6IFBpeGVsRGF0YSB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGN1cnJlbnRQbG90VHlwZTogUGxvdFR5cGUgPSBQbG90VHlwZS5OQVBBUklfSU1BR0U7XG4gIC8qKiBuYXBhcmktanMgaGlnaC1sZXZlbCB2aWV3IG93bmluZyB0aGUgM0Qgdm9sdW1lIGxheWVycyAob25lIGFkZGl0aXZlIHRpbnRlZCBsYXllciBwZXIgY2hhbm5lbFxuICAgKiAgZm9yIG11bHRpY2hhbm5lbCwgb3IgYSBzaW5nbGUgZ3JheXNjYWxlIHZvbHVtZSkuIE51bGwgaW4gMkQuICovXG4gIHByaXZhdGUgdm9sdW1lVmlldzogTXVsdGlDaGFubmVsVm9sdW1lVmlldyB8IG51bGwgPSBudWxsO1xuICAvKiogVHJ1ZSB3aGVuIHRoZSB2b2x1bWUgaXMgY29tcG9zaXRlZCBmcm9tIHBlci1jaGFubmVsIGxheWVycyAodnMgYSBzaW5nbGUgZ3JheXNjYWxlIHZvbHVtZSkuICovXG4gIHByaXZhdGUgdm9sdW1lTXVsdGljaGFubmVsID0gZmFsc2U7XG4gIC8qKiBuYXBhcmktanMgaGVpZ2h0LWZpZWxkIHN1cmZhY2UgbWVzaCAoTkFQQVJJX1NVUkZBQ0UgcGxvdCB0eXBlOyBudWxsIG90aGVyd2lzZSkuIEJ1aWx0IGZyb20gYVxuICAgKiAgc2luZ2xlIGdyYXlzY2FsZSBzbGljZSBieSB7QGxpbmsgYnVpbGRTdXJmYWNlfSB2aWEgbmFwYXJpLWpzJ3MgYGhlaWdodEZpZWxkYCArIGBhZGRTdXJmYWNlYC4gKi9cbiAgcHJpdmF0ZSBzdXJmYWNlTGF5ZXI6IFN1cmZhY2VMYXllciB8IG51bGwgPSBudWxsO1xuICAvKiogV2hpY2ggYmFuZCB0aGUgc3VyZmFjZSBzYW1wbGVzIChhIGNoYW5uZWwgaW5kZXggZm9yIG11bHRpY2hhbm5lbCwgZWxzZSB0aGUgY29tcG9zaXRlKS4gKi9cbiAgcHJpdmF0ZSBzdXJmYWNlQ2hhbm5lbDogbnVtYmVyIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xuICAvKiogUHJlLWxvYWRlZCBwZXItc2xpY2UgbHVtaW5hbmNlIHBsYW5lcyAoYWxyZWFkeSBkZWNpbWF0ZWQgdG8gdGhlIHN1cmZhY2UgZ3JpZCksIGtleWVkIGJ5IHosXG4gICAqICBzbyB0aGUgc3RhY2sgc2xpZGVyIHJlYnVpbGRzIHRoZSBzdXJmYWNlIGluc3RhbnRseS4gRmlsbGVkIGJ5IHtAbGluayBwcmVsb2FkU3VyZmFjZVBsYW5lc30uICovXG4gIHByaXZhdGUgcmVhZG9ubHkgc3VyZmFjZVBsYW5lcyA9IG5ldyBNYXA8XG4gICAgbnVtYmVyLFxuICAgIHsgZGF0YTogVWludDhBcnJheTsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfVxuICA+KCk7XG4gIC8qKiBJbi1wbGFuZSBncmlkIGNhcCBmb3IgdGhlIGFjdGl2ZSBzdXJmYWNlIGxvYWQgKGZ1bGwgZ3JpZCDDtyB0aGUgZGVjaW1hdGUgZmFjdG9yKS4gKi9cbiAgcHJpdmF0ZSBzdXJmYWNlTWF4R3JpZCA9IFNVUkZBQ0VfTUFYX0dSSUQ7XG4gIC8qKiBDb250cmFzdCB3aW5kb3cgW21pbixtYXhdIHRoZSBjdXJyZW50IHN1cmZhY2UgbWVzaCB3YXMgYnVpbHQgd2l0aC4gQSBjaGFuZ2UgcmVzaGFwZXMgdGhlXG4gICAqICBtZXNoIChwaXhlbCBoZWlnaHQgPSBpbnRlbnNpdHkgd2l0aGluIFttaW4sbWF4XSksIHNvIGl0IHRyaWdnZXJzIGEgZ2VvbWV0cnkgcmVidWlsZC4gKi9cbiAgcHJpdmF0ZSBzdXJmYWNlV2luZG93OiBbbnVtYmVyLCBudW1iZXJdIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBQZXJzaXN0ZWQgd2lyZWZyYW1lIGNob2ljZSBmb3IgdGhlIHN1cmZhY2UgKHJlLWFwcGxpZWQgd2hlbiBhIG5ldyBzdXJmYWNlIG1vdW50cykuICovXG4gIHByaXZhdGUgc3VyZmFjZVdpcmVmcmFtZSA9IGZhbHNlO1xuICAvKiogbmFwYXJpLWpzIDJEIHNjYXR0ZXIgcG9pbnRzIChyZWdpb24gY2VudHJvaWRzKSArIGl0cyByZWdpb24tY2hhbmdlIHN1YnNjcmlwdGlvbi4gKi9cbiAgcHJpdmF0ZSBzY2F0dGVyMmRQb2ludHM6IFBvaW50c0xheWVyIHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgc2NhdHRlclJlZ2lvblN1YjogU3Vic2NyaXB0aW9uIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBuYXBhcmktanMgM0Qgc2NhdHRlciAodm94ZWwgcG9pbnQgY2xvdWQpLiAqL1xuICBwcml2YXRlIHNjYXR0ZXIzZExheWVyOiBQb2ludHMzRExheWVyIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBNb25vdG9uaWMgbG9hZCBnZW5lcmF0aW9uLiBCdW1wZWQgYnkge0BsaW5rIHJlc2V0fSBhbmQge0BsaW5rIGNhbmNlbExvYWRpbmd9OyB0aGUgZnJhbWUtbG9hZGluZ1xuICAgKiAgbG9vcHMgKHZvbHVtZSBhc3NlbWJseSwgc3VyZmFjZSBwcmVsb2FkKSBjYXB0dXJlIGl0IGFuZCBiYWlsIHdoZW4gaXQgY2hhbmdlcywgc28gYSBDYW5jZWwgKG9yIGFcbiAgICogIG5ldyBwbG90KSBhY3R1YWxseSBzdG9wcyBmZXRjaGluZyBmcmFtZXMgaW5zdGVhZCBvZiBydW5uaW5nIHRvIGNvbXBsZXRpb24gaW4gdGhlIGJhY2tncm91bmQuICovXG4gIHByaXZhdGUgbG9hZFRva2VuID0gMDtcbiAgLyoqIERlY2ltYXRlIGZhY3RvciBmb3IgdGhlIG5hcGFyaSAzRCB0eXBlcyAoMSA9IEZ1bGwsIDIgPSDCvSBkZWZhdWx0LCA0ID0gwrwsIDggPSDihZspLiBBcHBsaWVkIHdoZW4gYVxuICAgKiAgdm9sdW1lL2lzb3N1cmZhY2Uvc3VyZmFjZSAocmUpbG9hZHM7IGNoYW5naW5nIGl0IG5lZWRzIGEgcmUtcGxvdCAoaXQgY2hhbmdlcyBmZXRjaGVkIGRhdGEpLiAqL1xuICBwcml2YXRlIHJlc29sdXRpb25TY2FsZSA9IE5BUEFSSV9ERUZBVUxUX0RFQ0lNQVRFO1xuICAvKiogM0QgY29vcmRpbmF0ZS1heGVzIC8gc2NhbGUgZ2l6bW8gZm9yIHRoZSB2b2x1bWUvaXNvc3VyZmFjZSB2aWV3IChudWxsIGluIDJEKS4gKi9cbiAgcHJpdmF0ZSBheGVzTGF5ZXI6IEF4ZXNMYXllciB8IG51bGwgPSBudWxsO1xuICAvKiogRE9NIFgvWS9aICsgc2NhbGUgbGFiZWxzIHRyYWNraW5nIHRoZSAzRCBheGVzIGdpem1vIChudWxsIGluIDJEKS4gKi9cbiAgcHJpdmF0ZSBheGVzTGFiZWxzOiBOYXBhcmlBeGVzTGFiZWxzIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBEcmFnZ2FibGUgWi1oZWlnaHQgZ3JpcCBvdmVyIHRoZSB2b2x1bWUgKG51bGwgdW5sZXNzIGEgdm9sdW1lL2lzb3N1cmZhY2UgaXMgbW91bnRlZCkuICovXG4gIHByaXZhdGUgekhhbmRsZTogTmFwYXJpVm9sdW1lWkhhbmRsZSB8IG51bGwgPSBudWxsO1xuICAvKiogVXNlciBaLWhlaWdodCBmYWN0b3IgZm9yIHRoZSB2b2x1bWUgKDEgPSB0aGUgdm9sdW1lJ3MgbmF0dXJhbCBwcm9wb3J0aW9ucyk7IGRyaXZlbiBieSB0aGVcbiAgICogIGluLXZpZXcgZHJhZyBoYW5kbGUuIFJlc2V0IHBlciBtb3VudC4gKi9cbiAgcHJpdmF0ZSB2b2x1bWVaU2NhbGUgPSAxO1xuICAvKiogVm9sdW1lIHdvcmxkIGJveCBhdCBgdm9sdW1lWlNjYWxlID0gMWAgKGJhc2UpICsgdGhlIHNhbXBsZWQgdm94ZWwgZGVwdGgg4oCUIGVub3VnaCB0byByZWNvbXB1dGVcbiAgICogIHRoZSBaLWF4aXMgdm94ZWwgc2NhbGUsIGF4ZXMgZGVwdGgsIGFuZCBvdmVybGF5IGFuY2hvcnMgYXMgdGhlIGhhbmRsZSBzdHJldGNoZXMgWi4gKi9cbiAgcHJpdmF0ZSB2b2x1bWVXb3JsZEJhc2U6IHsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXI7IGRlcHRoOiBudW1iZXIgfSB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIHZvbHVtZVNhbXBsZWREZXB0aCA9IDE7XG4gIC8qKiBQZXJzaXN0ZWQgYXhlcyBvbi9vZmYgY2hvaWNlLCByZS1hcHBsaWVkIHdoZW4gYSBuZXcgdm9sdW1lIG1vdW50cy4gRGVmYXVsdHMgb24uICovXG4gIHByaXZhdGUgYXhlc1Zpc2libGUgPSB0cnVlO1xuICBwcml2YXRlIHZvbHVtZURpbXM6IHsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXI7IGRlcHRoOiBudW1iZXIgfSB8IG51bGwgPSBudWxsO1xuICAvKiogQXNzZW1ibGVkIHVpbnQ4IHZvbHVtZSBkYXRhIHBlciBjaGFubmVsIChrZXkgPSBjaGFubmVsIGluZGV4KSwga2VwdCBmb3IgdGhlIHZvbHVtZSBpbnRlbnNpdHlcbiAgICogIGhpc3RvZ3JhbS4gS2V5IDAgaG9sZHMgdGhlIGdyYXlzY2FsZS9jb21wb3NpdGUgdm9sdW1lIGluIHRoZSBzaW5nbGUtY2hhbm5lbCBjYXNlLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IHZvbHVtZUNoYW5uZWxEYXRhID0gbmV3IE1hcDxudW1iZXIsIFVpbnQ4QXJyYXk+KCk7XG4gIHByaXZhdGUgaW1hZ2VXID0gMDtcbiAgcHJpdmF0ZSBpbWFnZUggPSAwO1xuICAvKiogTW9ub3RvbmljIHNsaWNlLXJlcXVlc3QgaWQgc28gYSBzbG93IG91dC1vZi1vcmRlciBzbGljZSBmZXRjaCBjYW4ndCBjbG9iYmVyIGEgbmV3ZXIgb25lLiAqL1xuICBwcml2YXRlIHNsaWNlUmVxID0gMDtcbiAgLyoqIENhY2hlZCBgL3RpbGVzL2luZm9gIHB5cmFtaWQgZGVzY3JpcHRvciArIHRoZSBpbmZvQjY0IGl0IHdhcyBmZXRjaGVkIGZvci4gKi9cbiAgcHJpdmF0ZSBkZXNjcmlwdG9yOiBUaWxlRGVzY3JpcHRvciB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGRlc2NyaXB0b3JLZXk6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuXG4gIC8qKiBIb3cgdGhlIGN1cnJlbnQgMkQgaW1hZ2UgaXMgY29tcG9zaXRlZCAoZHJpdmVzIGhpc3RvZ3JhbSArIHN0YXRlIGFwcGxpY2F0aW9uKS4gKi9cbiAgcHJpdmF0ZSBpbWFnZU1vZGU6ICdncmF5c2NhbGUnIHwgJ211bHRpY2hhbm5lbCcgfCAncmdiJyA9ICdyZ2InO1xuICAvKiogbmFwYXJpLWpzIGhpZ2gtbGV2ZWwgdmlldyB0aGF0IG93bnMgdGhlIHBlci1jaGFubmVsIGxheWVyIHNldCAoYnVpbGQgKyBsaXZlIGRpc3BsYXlcbiAgICogIHVwZGF0ZXMpIGZvciB0aGUgY3VycmVudCB7QGxpbmsgaW1hZ2VNb2RlfS4gUmVidWlsdCBvbiBlYWNoIChyZSlyZW5kZXIuICovXG4gIHByaXZhdGUgY2hhbm5lbFZpZXc6IE11bHRpQ2hhbm5lbEltYWdlVmlldyB8IG51bGwgPSBudWxsO1xuICAvKiogTGl2ZSBzdWJzY3JpcHRpb24gYXBwbHlpbmcgY2hhbm5lbC1zdGF0ZSAvIGNvbG9ybWFwIGNoYW5nZXMgdG8gdGhlIGxheWVycy4gKi9cbiAgcHJpdmF0ZSBkaXNwbGF5U3ViOiBTdWJzY3JpcHRpb24gfCBudWxsID0gbnVsbDtcbiAgLyoqIExhdGVzdCBncmF5c2NhbGUgY29sb3JtYXAgc2VsZWN0aW9uIGZyb20gdGhlIHN0b3JlIChhcHBsaWVkIHRvIHRoZSBzaW5nbGUgZ3JheSBsYXllcikuICovXG4gIHByaXZhdGUgY3VycmVudENvbG9ybWFwOiBDb2xvcm1hcE5vZGUgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBjdXJyZW50UmV2ZXJzZSA9IGZhbHNlO1xuICAvKiogTGF0ZXN0IGludmVydCB0b2dnbGUgZnJvbSB0aGUgc3RvcmUgKGZsaXBzIGludGVuc2l0eSBiZWZvcmUgdGhlIGNvbG9ybWFwLCBsaWtlIE9TRCkuICovXG4gIHByaXZhdGUgaW52ZXJ0RW5hYmxlZCA9IGZhbHNlO1xuICAvKiogUGh5c2ljYWwgc2NhbGUgYmFyIG92ZXJsYXkgZm9yIHRoZSAyRCBpbWFnZSAobnVsbCB3aGVuIDNEIG9yIHRoZSBpbWFnZSBoYXMgbm8gwrVtL3BpeGVsKS4gKi9cbiAgcHJpdmF0ZSBzY2FsZUJhcjogTmFwYXJpU2NhbGVCYXIgfCBudWxsID0gbnVsbDtcbiAgLyoqIFNWRyByZWdpb24tZHJhd2luZyBvdmVybGF5IGZvciB0aGUgMkQgaW1hZ2UgKG51bGwgdW50aWwgYSAyRCBpbWFnZSBpcyBwbG90dGVkKS4gKi9cbiAgcHJpdmF0ZSByZWdpb25PdmVybGF5OiBOYXBhcmlSZWdpb25PdmVybGF5IHwgbnVsbCA9IG51bGw7XG4gIC8qKiBQaXhlbC10b29sIHBsdW1iaW5nOiB0aGUgcGxvdCBkaXYgaWQsIGNvb3JkIHRyYW5zZm9ybSwgYW5kIGJvdW5kIHRvb2wgaG9zdHMuICovXG4gIHByaXZhdGUgcGxvdERpdklkID0gJyc7XG4gIHByaXZhdGUgY29vcmRUcmFuc2Zvcm06IElDb29yZGluYXRlVHJhbnNmb3JtIHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgd2FuZEhvc3Q6IFdhbmRUb29sSG9zdCB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGVyYXNlckhvc3Q6IFZlcnRleEVyYXNlclRvb2xIb3N0IHwgbnVsbCA9IG51bGw7XG4gIC8qKiBDYWNoZWQgQ2FjaGVkSW1hZ2VEYXRhIGJ1aWx0IGZyb20gdGhlIGxhc3QgcmVhZGJhY2sgKHJlYnVpbHQgd2hlbiBsYXN0UGl4ZWxzIGNoYW5nZXMpLiAqL1xuICBwcml2YXRlIGNhY2hlZEltYWdlOiBDYWNoZWRJbWFnZURhdGEgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBjYWNoZWRJbWFnZVNvdXJjZTogUGl4ZWxEYXRhIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBWaXNpYmxlIHdvcmxkIHJlY3QgY2FwdHVyZWQgQVQgdGhlIGxhc3QgcmVhZGJhY2sg4oCUIG11c3QgcGFpciB3aXRoIGBsYXN0UGl4ZWxzYCBzbyB0aGUgcGl4ZWxcbiAgICogIHRvb2xzJyByYXRpb3Mvb3JpZ2luIG1hdGNoIHRoZSBtYXRyaXgncyBjYW1lcmEgKHVzaW5nIHRoZSBsaXZlIHJlY3QgYWZ0ZXIgYSBwYW4vem9vbSB3b3VsZFxuICAgKiAgbWlzLXNjYWxlIHRoZSByZWdpb24pLiAqL1xuICBwcml2YXRlIGxhc3RQaXhlbHNSZWN0OiB7IHg6IG51bWJlcjsgeTogbnVtYmVyOyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlciB9IHwgbnVsbCA9IG51bGw7XG4gIC8qKiBJbWFnZSBzbW9vdGhpbmcgKGJpbGluZWFyKSB2cyBuZWFyZXN0LW5laWdoYm91ciAoY3Jpc3AgcGl4ZWxzLCB0aGUgZGVmYXVsdCkuICovXG4gIHByaXZhdGUgaW1hZ2VTbW9vdGhpbmcgPSBmYWxzZTtcbiAgLyoqIFRydWUgd2hlbiB0aGUgMkQgaW1hZ2UgaXMgcmVuZGVyZWQgdmlhIHB5cmFtaWRhbCBUaWxlZFNvdXJjZXMgKGRlc2NyaXB0b3IgYXZhaWxhYmxlKS4gKi9cbiAgcHJpdmF0ZSB0aWxlZCA9IGZhbHNlO1xuICAvKiogRGVib3VuY2VkIHJlYWRiYWNrIHRpbWVyICsgY2FtZXJhLWNoYW5nZSB1bnN1YnNjcmliZSAoa2VlcCBsYXN0UGl4ZWxzIGN1cnJlbnQgZm9yIHRvb2xzKS4gKi9cbiAgcHJpdmF0ZSByZWFkYmFja1RpbWVyOiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PiB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGNhbWVyYVJlYWRiYWNrT2ZmOiAoKCkgPT4gdm9pZCkgfCBudWxsID0gbnVsbDtcbiAgLyoqIENvYXJzZSBwZXItY2hhbm5lbCBsdW1pbmFuY2Ugc2FtcGxlIChrZXllZCBieSBjaGFubmVsIGluZGV4KSBmb3IgdGhlIGhpc3RvZ3JhbSBpbiB0aWxlZCBtb2RlLFxuICAgKiAgd2hlcmUgdGhlIGxheWVycyBoYXZlIG5vIGZ1bGwgaW4tbWVtb3J5IHBpeGVscy4gUmVmcmVzaGVkIG9uIHBsb3QgKyBzbGljZSBjaGFuZ2UuICovXG4gIHByaXZhdGUgcmVhZG9ubHkgaGlzdFNhbXBsZXMgPSBuZXcgTWFwPG51bWJlciwgVWludDhBcnJheT4oKTtcbiAgLyoqIE5hdGl2ZS1iaXQtZGVwdGggaGlzdG9ncmFtcyBmcm9tIGAvaGlzdG9ncmFtYCwga2V5ZWQgYCR7en18JHtjaGFubmVsfWAgKD44LWJpdCBpbWFnZXMpLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IG5hdGl2ZUhpc3RvZ3JhbXMgPSBuZXcgTWFwPHN0cmluZywgSUhpc3RvZ3JhbT4oKTtcblxuICBwcml2YXRlIHJlYWRvbmx5IHN0YWNrTG9hZGluZyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcbiAgcHJpdmF0ZSByZWFkb25seSBzdGFja0xvYWRpbmdQcm9ncmVzcyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PG51bWJlcj4oMCk7XG4gIHByaXZhdGUgcmVhZG9ubHkgYXV0b3NjYWxlRXZlbnQkID0gbmV3IFN1YmplY3Q8dW5rbm93bj4oKTtcbiAgcHJpdmF0ZSByZWFkb25seSBpbnRlbnNpdHlQcm9maWxlJCA9IG5ldyBTdWJqZWN0PEludGVuc2l0eVByb2ZpbGVbXT4oKTtcbiAgcHJpdmF0ZSByZWFkb25seSB2aWV3cG9ydENoYW5nZSQgPSBuZXcgU3ViamVjdDx7XG4gICAgeDogbnVtYmVyO1xuICAgIHk6IG51bWJlcjtcbiAgICB3aWR0aDogbnVtYmVyO1xuICAgIGhlaWdodDogbnVtYmVyO1xuICB9PigpO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIEBJbmplY3QoVElMRV9BQ0NFU1NfUE9SVCkgcHJpdmF0ZSByZWFkb25seSB0aWxlczogVGlsZUFjY2Vzc1BvcnQsXG4gICAgc3RvcmU6IFZpc3VhbGl6ZXJTdG9yZSxcbiAgICByZWdpb25TdG9yZTogUmVnaW9uU3RvcmUsXG4gICAgcHJpdmF0ZSByZWFkb25seSB3YW5kVG9vbDogV2FuZFRvb2xTZXJ2aWNlLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgYnJ1c2hUb29sOiBCcnVzaFRvb2xTZXJ2aWNlLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgZXJhc2VyVG9vbDogVmVydGV4RXJhc2VyVG9vbFNlcnZpY2UsXG4gICAgcHJpdmF0ZSByZWFkb25seSB6b29tVG9Cb3hUb29sOiBab29tVG9Cb3hUb29sU2VydmljZSxcbiAgICBwcml2YXRlIHJlYWRvbmx5IHNhbVRvb2w6IFNhbVRvb2xTZXJ2aWNlLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgc2FtUG9pbnRUb29sOiBTYW1Qb2ludFRvb2xTZXJ2aWNlLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgY2VsbFNlZ21lbnRUb29sOiBDZWxsU2VnbWVudFRvb2xTZXJ2aWNlLFxuICAgIEBPcHRpb25hbCgpIEBJbmplY3QoQ0VMTF9TRUdNRU5URVIpIHByaXZhdGUgcmVhZG9ubHkgY2VsbFNlZ21lbnRlcjogSUNlbGxTZWdtZW50ZXIgfCBudWxsLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgc2ltcGxlU3RhY2s6IFNpbXBsZVNsaWNlQWNjZXNzU2VydmljZSxcbiAgICBASW5qZWN0KFZJWl9DT05GSUcpIGNvbmZpZzogVml6Q29uZmlnLFxuICApIHtcbiAgICBzdXBlcihyZWdpb25TdG9yZSwgc3RvcmUpO1xuICAgIHRoaXMuYXBpID0gY29uZmlnLnNsaWRlQ3JvcFNlcnZlcjtcbiAgfVxuXG4gIC8qKlxuICAgKiBGZXRjaCArIGNhY2hlIHRoZSBzZXJ2ZXIgcHlyYW1pZCBkZXNjcmlwdG9yIChgR0VUIC90aWxlcy9pbmZvYCk6IHRoZSBSRUFMIHBlci1sZXZlbCB0aWxlIGdyaWQsXG4gICAqIHRpbGUgc2l6ZSwgY2hhbm5lbCBtZXRhZGF0YSBhbmQgcGh5c2ljYWwgcGl4ZWwgc2l6ZS4gVGhlIGJhY2tlbmQgcmV0dXJucyAyMDIgd2hpbGUgdGhlIHNvdXJjZVxuICAgKiBpcyBzdGlsbCBjYWNoaW5nLCBzbyB3ZSBwb2xsIGJyaWVmbHkuIENhY2hlZCBwZXIgYGluZm9CNjRgOyByZXR1cm5zIG51bGwgaWYgaXQgbmV2ZXIgYmVjb21lc1xuICAgKiByZWFkeSAoY2FsbGVycyBmYWxsIGJhY2sgdG8gYSBzaW5nbGUtdGlsZSBmZXRjaCkuIFRoaXMgaXMgdGhlIGF1dGhvcml0YXRpdmUgZ3JpZCDigJQgZ3Vlc3NpbmdcbiAgICogbGV2ZWwgZGltcyBmcm9tIGB0cnVlSW1hZ2VTaXplYCBvdmVyc2hvb3RzIHRoZSByZWFsIGdyaWQgYW5kIHRoZSBzZXJ2ZXIgNDAwcyBvdXQtb2YtcmFuZ2UgdGlsZXMuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGVuc3VyZURlc2NyaXB0b3IoKTogUHJvbWlzZTxUaWxlRGVzY3JpcHRvciB8IG51bGw+IHtcbiAgICAvLyBTZWxmLWNvbnRhaW5lZCBtdWx0aS1zbGljZSBzdGFjayAobm8gdGlsZSBzZXJ2ZXIg4oCUIGUuZy4gYSBudW1iZXJlZCBpbWFnZVxuICAgIC8vIHNlcmllcyBhc3NlbWJsZWQgY2xpZW50LXNpZGUsIGVhY2ggc2xpY2UgYSBkaWZmZXJlbnQgZmlsZSk6IHRoZXJlIGlzIG5vXG4gICAgLy8gc2luZ2xlIHNlcnZlci10aWxlZCBweXJhbWlkIHRvIGRlc2NyaWJlLiBSZXR1cm5pbmcgbnVsbCByb3V0ZXMgZXZlcnlcbiAgICAvLyBjYWxsZXIgKDJEIGltYWdlLCB2b2x1bWUsIHN1cmZhY2UpIHRvIHRoZWlyIHN0aXRjaGVkL3NpbmdsZS1mZXRjaFxuICAgIC8vIGZhbGxiYWNrLCB3aGljaCDigJQgdmlhIGZldGNoU2xpY2UncyBvd24gU2ltcGxlU2xpY2VBY2Nlc3NTZXJ2aWNlIGJyYW5jaCDigJRcbiAgICAvLyBjb3JyZWN0bHkgZmV0Y2hlcyBlYWNoIHNsaWNlJ3Mgb3duIFVSTCBpbnN0ZWFkIG9mIG9uZSBmaXhlZCBmaWxlJ3MgdGlsZVxuICAgIC8vIHB5cmFtaWQuXG4gICAgaWYgKHRoaXMuc2ltcGxlU3RhY2suaXNTaW1wbGUodGhpcy5sb2FkZWQ/LmltYWdlSW5mbykpIHJldHVybiBudWxsO1xuICAgIGNvbnN0IGluZm9CNjQgPSB0aGlzLnRpbGVzLmdldFNlbGVjdGVkSW5mb0I2NCgpO1xuICAgIGlmICghaW5mb0I2NCkgcmV0dXJuIG51bGw7XG4gICAgaWYgKHRoaXMuZGVzY3JpcHRvciAmJiB0aGlzLmRlc2NyaXB0b3JLZXkgPT09IGluZm9CNjQpIHJldHVybiB0aGlzLmRlc2NyaXB0b3I7XG4gICAgY29uc3QgaGVhZGVycyA9IGF3YWl0IHRoaXMudGlsZXNcbiAgICAgIC5nZXRBdXRoSGVhZGVycygpXG4gICAgICAuY2F0Y2goKCkgPT4gKHt9KSBhcyBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+KTtcbiAgICBjb25zdCB1cmwgPSBgJHt0aGlzLmFwaX10aWxlcy9pbmZvP2luZm89JHtpbmZvQjY0fWA7XG4gICAgY29uc3QgZGVhZGxpbmUgPSBEYXRlLm5vdygpICsgREVTQ1JJUFRPUl9USU1FT1VUX01TO1xuICAgIGZvciAoOzspIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHJlc3AgPSBhd2FpdCBmZXRjaCh1cmwsIHsgaGVhZGVycyB9KTtcbiAgICAgICAgaWYgKHJlc3Auc3RhdHVzID09PSAyMDApIHtcbiAgICAgICAgICBjb25zdCBib2R5ID0gKGF3YWl0IHJlc3AuanNvbigpKSBhcyBUaWxlRGVzY3JpcHRvcjtcbiAgICAgICAgICBpZiAoYm9keT8ubGV2ZWxzPy5sZW5ndGgpIHtcbiAgICAgICAgICAgIHRoaXMuZGVzY3JpcHRvciA9IGJvZHk7XG4gICAgICAgICAgICB0aGlzLmRlc2NyaXB0b3JLZXkgPSBpbmZvQjY0O1xuICAgICAgICAgICAgcmV0dXJuIGJvZHk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIC8vIDIwMiAoc3RpbGwgY2FjaGluZykgb3IgZW1wdHkgYm9keSDihpIgcmUtcG9sbCB1bnRpbCB0aGUgZGVhZGxpbmUuXG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgY29uc29sZS53YXJuKCdbbmFwYXJpLWpzXSB0aWxlcy9pbmZvIHBvbGwgcmV0cnknLCBlcnIpO1xuICAgICAgfVxuICAgICAgaWYgKERhdGUubm93KCkgPiBkZWFkbGluZSkge1xuICAgICAgICBjb25zb2xlLndhcm4oJ1tuYXBhcmktanNdIHRpbGVzL2luZm8gbm90IHJlYWR5OyBmYWxsaW5nIGJhY2sgdG8gc2luZ2xlLXRpbGUgZmV0Y2gnKTtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocikgPT4gc2V0VGltZW91dChyLCAxMjAwKSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEZldGNoIGEgQ09NUExFVEUgcmVuZGVyZWQgc2xpY2UgYXMgYW4gYEltYWdlQml0bWFwYCBieSBzdGl0Y2hpbmcgdGhlIHNlcnZlcidzIFJFQUwgdGlsZSBncmlkXG4gICAqIChmcm9tIGAvdGlsZXMvaW5mb2ApIOKAlCBub3QganVzdCB0aGUgdG9wLWxlZnQgdGlsZSwgd2hpY2ggb25seSBldmVyIHNob3dlZCBhIGxhcmdlIGltYWdlJ3NcbiAgICogY29ybmVyLiBQaWNrcyB0aGUgZmluZXN0IHB5cmFtaWQgbGV2ZWwgd2hvc2UgZ3JpZCBmaXRzIGBidWRnZXRUaWxlc2AgYW5kIHdob3NlIGxvbmdlc3Qgc2lkZSBpc1xuICAgKiB3aXRoaW4gdGhlIEdQVSB0ZXh0dXJlIGxpbWl0LCBmZXRjaGVzIHRoYXQgZ3JpZCBjb25jdXJyZW50bHksIGFuZCBzdGl0Y2hlcyBpdCBpbnRvIG9uZSBjYW52YXMuXG4gICAqXG4gICAqIGBjaGFubmVsYCBzZWxlY3RzIGEgc2luZ2xlIGJhbmQgYXMgZ3JheXNjYWxlIChyZWFsIGxldmVscyBvbmx5KTsgb21pdCBmb3IgdGhlIHNlcnZlciBjb21wb3NpdGUuXG4gICAqIGBidWRnZXRUaWxlc2AgY2FwcyB0aGUgZ3JpZDogdGhlIDJEIHZpZXcgdXNlcyB0aGUgZnVsbCBidWRnZXQgZm9yIGRldGFpbDsgdGhlIDNEIHZvbHVtZSBwYXNzZXNcbiAgICogMSBmb3IgYSBjaGVhcCBvdmVydmlldyB0aWxlIHBlciBzbGljZSAoaXQgZG93bnNhbXBsZXMgdG8ge0BsaW5rIFZPTFVNRV9NQVhfU0xJQ0V9KS4gRmFsbHMgYmFja1xuICAgKiB0byBhIHNpbmdsZSBgY29sPTAscm93PTBgIHRpbGUgd2hlbiBubyBkZXNjcmlwdG9yIGlzIGF2YWlsYWJsZSAoc21hbGwvc2ltcGxlIGltYWdlcywgdm9sdW1lcykuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGZldGNoU2xpY2UoXG4gICAgejogbnVtYmVyLFxuICAgIGNoYW5uZWw/OiBudW1iZXIsXG4gICAgYnVkZ2V0VGlsZXM6IG51bWJlciA9IE1BWF9TVElUQ0hfVElMRVMsXG4gICAgLyoqIFdoZW4gYSBzcGVjaWZpYyBjaGFubmVsIGhhcyBubyBweXJhbWlkIGxldmVsIHdpdGhpbiBgYnVkZ2V0VGlsZXNgLCBkcm9wIHRvIHRoZSBzZXJ2ZXJcbiAgICAgKiAgQ09NUE9TSVRFIG92ZXJ2aWV3IGluc3RlYWQgb2Ygc3RpdGNoaW5nIHRoZSBodWdlIHBlci1jaGFubmVsIGxldmVsLiBPbmx5IHNhZmUgd2hlbiB0aGUgY2FsbGVyXG4gICAgICogIHdhbnRzIGEgc2luZ2xlIGRlY2ltYXRlZCBwbGFuZSAodGhlIHN1cmZhY2UgaGVpZ2h0KTogZm9yIGEgbXVsdGljaGFubmVsIFZPTFVNRSBldmVyeSBjaGFubmVsXG4gICAgICogIHdvdWxkIHRoZW4gZmV0Y2ggdGhlIHNhbWUgY29tcG9zaXRlIGFuZCB0aGUgY2hhbm5lbHMgd291bGQgY29sbGFwc2UgaW50byBvbmUsIHNvIHZvbHVtZVxuICAgICAqICBhc3NlbWJseSBsZWF2ZXMgdGhpcyBgZmFsc2VgIHRvIGtlZXAgZWFjaCBiYW5kIGRpc3RpbmN0LiAqL1xuICAgIGFsbG93Q29tcG9zaXRlRmFsbGJhY2sgPSBmYWxzZSxcbiAgKTogUHJvbWlzZTxJbWFnZUJpdG1hcD4ge1xuICAgIC8vIFNlbGYtY29udGFpbmVkIG11bHRpLXNsaWNlIHN0YWNrICh0aWxlZDpmYWxzZSk6IGB6YCBpbmRleGVzIGEgY29tcGxldGVseVxuICAgIC8vIGRpZmZlcmVudCBmaWxlJ3Mgb3duIHByZXZpZXcgVVJMLCBub3QgYW4gaW50ZXJuYWwgc2xpY2Ugb2Ygb25lIHNlcnZlci1cbiAgICAvLyB0aWxlZCBmaWxlIOKAlCByZXNvbHZlZCBhbmQgZmV0Y2hlZCB2aWEgU2ltcGxlU2xpY2VBY2Nlc3NTZXJ2aWNlIChzaGFyZWRcbiAgICAvLyB3aXRoIE9TRDsgc2VlIGl0cyBkb2NzIGZvciB3aHkgdGhpcyBjYW4ndCBiZSBhIGJhcmUgZmV0Y2goKSkgaW5zdGVhZCBvZlxuICAgIC8vIGJ1aWxkaW5nIGEgL3RpbGU/aW5mbz0uLi4mej0gcmVxdWVzdCBhZ2FpbnN0IHdoaWNoZXZlciBzaW5nbGUgZmlsZVxuICAgIC8vIGdldFNlbGVjdGVkSW5mb0I2NCgpIHBvaW50cyBhdCAod2hpY2ggd291bGQgNDAwL21pc21hdGNoIGZvciBhbnkgelxuICAgIC8vIGJleW9uZCB0aGF0IG9uZSBmaWxlJ3Mgb3duIGV4dGVudCkuXG4gICAgY29uc3QgaW5mbyA9IHRoaXMubG9hZGVkPy5pbWFnZUluZm87XG4gICAgaWYgKHRoaXMuc2ltcGxlU3RhY2suaXNTaW1wbGUoaW5mbykpIHtcbiAgICAgIGNvbnN0IHVybCA9IHRoaXMuc2ltcGxlU3RhY2sudXJsRm9yKGluZm8gYXMgSUltYWdlSW5mbywgeik7XG4gICAgICBpZiAoIXVybCkgdGhyb3cgbmV3IEVycm9yKGBbbmFwYXJpLWpzXSBubyBVUkwgZm9yIHNsaWNlICR7en1gKTtcbiAgICAgIHJldHVybiB0aGlzLnNpbXBsZVN0YWNrLmZldGNoQXNCaXRtYXAodXJsKTtcbiAgICB9XG4gICAgY29uc3QgaW5mb0I2NCA9IHRoaXMudGlsZXMuZ2V0U2VsZWN0ZWRJbmZvQjY0KCk7XG4gICAgaWYgKCFpbmZvQjY0KSB0aHJvdyBuZXcgRXJyb3IoJ1tuYXBhcmktanNdIG5vIHNlbGVjdGVkIGltYWdlIGluZm8gKGdldFNlbGVjdGVkSW5mb0I2NCBudWxsKScpO1xuICAgIGNvbnN0IGhlYWRlcnMgPSBhd2FpdCB0aGlzLnRpbGVzXG4gICAgICAuZ2V0QXV0aEhlYWRlcnMoKVxuICAgICAgLmNhdGNoKCgpID0+ICh7fSkgYXMgUmVjb3JkPHN0cmluZywgc3RyaW5nPik7XG4gICAgY29uc3QgZGVzYyA9IGF3YWl0IHRoaXMuZW5zdXJlRGVzY3JpcHRvcigpO1xuXG4gICAgLy8gVGhlIHJlcXVlc3RlZCBiYW5kOyBtYXkgYmUgZHJvcHBlZCB0byB0aGUgY29tcG9zaXRlICh1bmRlZmluZWQpIGJlbG93IHdoZW4gdGhlIGNoYW5uZWwgaGFzIG5vXG4gICAgLy8gcHlyYW1pZCBsZXZlbCBzbWFsbCBlbm91Z2ggdG8gc3RpdGNoIHdpdGhpbiBidWRnZXQuXG4gICAgbGV0IGVmZmVjdGl2ZUNoYW5uZWwgPSBjaGFubmVsO1xuICAgIGNvbnN0IGZldGNoVGlsZSA9IGFzeW5jIChcbiAgICAgIHJlczogbnVtYmVyLFxuICAgICAgY29sOiBudW1iZXIsXG4gICAgICByb3c6IG51bWJlcixcbiAgICAgIHQ6IG51bWJlcixcbiAgICApOiBQcm9taXNlPEltYWdlQml0bWFwPiA9PiB7XG4gICAgICBjb25zdCBjaCA9IGVmZmVjdGl2ZUNoYW5uZWwgPT0gbnVsbCA/ICcnIDogYCZjaGFubmVsPSR7ZWZmZWN0aXZlQ2hhbm5lbH1gO1xuICAgICAgY29uc3QgdXJsID0gYCR7dGhpcy5hcGl9dGlsZT9pbmZvPSR7aW5mb0I2NH0mcmVzPSR7cmVzfSZjb2w9JHtjb2x9JnJvdz0ke3Jvd30mej0ke3p9JnRpbGVTaXplPSR7dH0ke2NofWA7XG4gICAgICBjb25zdCByZXNwID0gYXdhaXQgZmV0Y2godXJsLCB7IGhlYWRlcnMgfSk7XG4gICAgICBpZiAoIXJlc3Aub2spIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBbbmFwYXJpLWpzXSBzbGljZSBmZXRjaCBmYWlsZWQ6ICR7cmVzcC5zdGF0dXN9ICgke2NvbH0vJHtyb3d9IHJlcyAke3Jlc30pYCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gY3JlYXRlSW1hZ2VCaXRtYXAoYXdhaXQgcmVzcC5ibG9iKCkpO1xuICAgIH07XG5cbiAgICAvLyBObyBkZXNjcmlwdG9yIOKGkiBzaW5nbGUgdG9wLWxlZnQgdGlsZSAobGVnYWN5IGZhbGxiYWNrOyBjb3JyZWN0IGZvciBzbWFsbC9zaW1wbGUgaW1hZ2VzKS5cbiAgICBpZiAoIWRlc2MgfHwgIWRlc2MubGV2ZWxzPy5sZW5ndGgpIHJldHVybiBmZXRjaFRpbGUoMCwgMCwgMCwgVElMRV9TSVpFKTtcblxuICAgIGNvbnN0IHQgPSBkZXNjLnRpbGVTaXplIHx8IFRJTEVfU0laRTtcbiAgICAvLyBQZXItY2hhbm5lbCB0aWxlcyBleGlzdCBPTkxZIGF0IFJFQUwgQmlvLUZvcm1hdHMgbGV2ZWxzICh0aGUgZnJvbnQgb2YgYGxldmVsc2ApOyB0aGUgc2VydmVyXG4gICAgLy8gY29tcG9zaXRlIGV4aXN0cyBhdCBldmVyeSBsZXZlbCwgaW5jbHVkaW5nIHRoZSBzbWFsbCBvdmVydmlld3MuXG4gICAgY29uc3QgcGVyQ2hhbm5lbExldmVscyA9IGRlc2MucmVhbExldmVscyA/PyBkZXNjLmxldmVscy5sZW5ndGg7XG4gICAgY29uc3QgdXNhYmxlID1cbiAgICAgIGNoYW5uZWwgPT0gbnVsbCA/IGRlc2MubGV2ZWxzIDogZGVzYy5sZXZlbHMuc2xpY2UoMCwgTWF0aC5tYXgoMSwgcGVyQ2hhbm5lbExldmVscykpO1xuXG4gICAgLy8gRmluZXN0IGxldmVsIHdob3NlIHN0aXRjaGVkIGdyaWQgZml0cyBCT1RIIHRoZSB0aWxlIGJ1ZGdldCBhbmQgdGhlIEdQVSB0ZXh0dXJlIGxpbWl0OyBpZiBub25lXG4gICAgLy8gZml0cywgdGhlIGNvYXJzZXN0IGF2YWlsYWJsZSAoZml0cz1mYWxzZSkuXG4gICAgY29uc3QgdGlsZXNGb3IgPSAobHZsOiBUaWxlTGV2ZWwpOiBudW1iZXIgPT5cbiAgICAgIE1hdGgubWF4KDEsIE1hdGguY2VpbChsdmwud2lkdGggLyB0KSkgKiBNYXRoLm1heCgxLCBNYXRoLmNlaWwobHZsLmhlaWdodCAvIHQpKTtcbiAgICBjb25zdCBwaWNrID0gKGxldmVsczogVGlsZUxldmVsW10pOiB7IGx2bDogVGlsZUxldmVsOyBmaXRzOiBib29sZWFuIH0gPT4ge1xuICAgICAgbGV0IGMgPSBsZXZlbHNbMF07XG4gICAgICBmb3IgKGNvbnN0IGx2bCBvZiBsZXZlbHMpIHtcbiAgICAgICAgYyA9IGx2bDtcbiAgICAgICAgaWYgKHRpbGVzRm9yKGx2bCkgPD0gYnVkZ2V0VGlsZXMgJiYgTWF0aC5tYXgobHZsLndpZHRoLCBsdmwuaGVpZ2h0KSA8PSBNQVhfVEVYVFVSRV9ESU0pIHtcbiAgICAgICAgICByZXR1cm4geyBsdmwsIGZpdHM6IHRydWUgfTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIHsgbHZsOiBjLCBmaXRzOiBmYWxzZSB9O1xuICAgIH07XG5cbiAgICBsZXQgc2VsID0gcGljayh1c2FibGUpO1xuICAgIC8vIEEgc3BlY2lmaWMgY2hhbm5lbCBvbmx5IGhhcyB0aWxlcyBhdCB0aGUgKGZldywgbGFyZ2UpIHJlYWwgbGV2ZWxzLiBXaGVuIG5vbmUgb2YgdGhlbSBmaXQgdGhlXG4gICAgLy8gYnVkZ2V0IOKAlCBlLmcuIHRoZSBjb2Fyc2VzdCByZWFsIGxldmVsIGlzIHN0aWxsIDE0OTgyw5cxODY3MCDihpIgfjExMDAgZnVsbC1yZXMgdGlsZXMg4oCUIHN0aXRjaGluZ1xuICAgIC8vIGl0IHBlciBzbGljZSBmbG9vZHMgdGhlIHNlcnZlciAoNTA0cykgYW5kIHN0YWxscyB0aGUgbG9hZC4gVGhlIGNvbXBvc2l0ZSBweXJhbWlkIGhhcyBzbWFsbFxuICAgIC8vIG92ZXJ2aWV3IGxldmVscywgc28gZmV0Y2ggdGhlIGNvbXBvc2l0ZSBpbnN0ZWFkIGFuZCBkZXJpdmUgbHVtaW5hbmNlIGZyb20gaXQuIEV2ZXJ5IGNhbGxlciBoZXJlXG4gICAgLy8gKHN1cmZhY2UgaGVpZ2h0LCB2b2x1bWUgYXNzZW1ibHksIHJlYWRiYWNrKSBkb3duc2NhbGVzIHRoZSBwbGFuZSBhbnl3YXksIHNvIGEgY29tcG9zaXRlLWRlcml2ZWRcbiAgICAvLyBwbGFuZSBpcyB0aGUgcmlnaHQgdHJhZGUgZm9yIG9uZSB0aGF0IGFjdHVhbGx5IGxvYWRzLiBPbmx5IGtpY2tzIGluIHdoZW4gdGhlIGNoYW5uZWwgY2FuJ3QgZml0LlxuICAgIGlmIChhbGxvd0NvbXBvc2l0ZUZhbGxiYWNrICYmICFzZWwuZml0cyAmJiBjaGFubmVsICE9IG51bGwgJiYgZGVzYy5sZXZlbHMubGVuZ3RoID4gcGVyQ2hhbm5lbExldmVscykge1xuICAgICAgY29uc3QgY29tcG9zaXRlID0gcGljayhkZXNjLmxldmVscyk7XG4gICAgICBpZiAoY29tcG9zaXRlLmZpdHMgfHwgdGlsZXNGb3IoY29tcG9zaXRlLmx2bCkgPCB0aWxlc0ZvcihzZWwubHZsKSkge1xuICAgICAgICBlZmZlY3RpdmVDaGFubmVsID0gdW5kZWZpbmVkO1xuICAgICAgICBzZWwgPSBjb21wb3NpdGU7XG4gICAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgICBgW25hcGFyaS1qc10gY2hhbm5lbCAke2NoYW5uZWx9IGhhcyBubyBweXJhbWlkIGxldmVsIHdpdGhpbiB0aGUgJHtidWRnZXRUaWxlc30tdGlsZSBgICtcbiAgICAgICAgICAgIGBidWRnZXQ7IHVzaW5nIHRoZSBjb21wb3NpdGUgb3ZlcnZpZXcgKHJlcyAke3NlbC5sdmwucmVzfSwgJHtzZWwubHZsLndpZHRofcOXYCArXG4gICAgICAgICAgICBgJHtzZWwubHZsLmhlaWdodH0pIGZvciB0aGlzIHBsYW5lLmAsXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuICAgIGNvbnN0IGNob3NlbiA9IHNlbC5sdmw7XG4gICAgY29uc3QgY29scyA9IE1hdGgubWF4KDEsIE1hdGguY2VpbChjaG9zZW4ud2lkdGggLyB0KSk7XG4gICAgY29uc3Qgcm93cyA9IE1hdGgubWF4KDEsIE1hdGguY2VpbChjaG9zZW4uaGVpZ2h0IC8gdCkpO1xuICAgIGlmICghc2VsLmZpdHMgJiYgYnVkZ2V0VGlsZXMgPT09IE1BWF9TVElUQ0hfVElMRVMpIHtcbiAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgYFtuYXBhcmktanNdIGZ1bGwgcmVzb2x1dGlvbiBleGNlZWRzIHRoZSAke2J1ZGdldFRpbGVzfS10aWxlLyR7TUFYX1RFWFRVUkVfRElNfXB4IGJ1ZGdldDsgYCArXG4gICAgICAgICAgYGRpc3BsYXlpbmcgb3ZlcnZpZXcgbGV2ZWwgcmVzICR7Y2hvc2VuLnJlc30gKCR7Y2hvc2VuLndpZHRofcOXJHtjaG9zZW4uaGVpZ2h0fSkuYCxcbiAgICAgICk7XG4gICAgfVxuXG4gICAgaWYgKGNvbHMgPT09IDEgJiYgcm93cyA9PT0gMSkgcmV0dXJuIGZldGNoVGlsZShjaG9zZW4ucmVzLCAwLCAwLCB0KTtcblxuICAgIC8vIFN0aXRjaCBpbnRvIG9uZSBsZXZlbC1zaXplZCBjYW52YXMuIEZldGNoIHRoZSBncmlkIHdpdGggQk9VTkRFRCBjb25jdXJyZW5jeTogZmlyaW5nIGV2ZXJ5IHRpbGVcbiAgICAvLyBhdCBvbmNlIChhIGJpZyBncmlkID0gaHVuZHJlZHMgb2YgcmVxdWVzdHMpIG92ZXJ3aGVsbWVkIHRoZSB0aWxlIHNlcnZlciAoNTA0cykuIEVkZ2UgdGlsZXMgYXJlXG4gICAgLy8gbmFycm93ZXIvc2hvcnRlcjsgZHJhd0ltYWdlIHBsYWNlcyBlYWNoIGF0IGl0cyBncmlkIG9mZnNldCBzbyBwYXJ0aWFsIHRpbGVzIGxpbmUgdXAuXG4gICAgY29uc3QgY29vcmRzOiBBcnJheTx7IGNvbDogbnVtYmVyOyByb3c6IG51bWJlciB9PiA9IFtdO1xuICAgIGZvciAobGV0IHJvdyA9IDA7IHJvdyA8IHJvd3M7IHJvdysrKSB7XG4gICAgICBmb3IgKGxldCBjb2wgPSAwOyBjb2wgPCBjb2xzOyBjb2wrKykgY29vcmRzLnB1c2goeyBjb2wsIHJvdyB9KTtcbiAgICB9XG4gICAgY29uc3QgdGlsZXM6IEFycmF5PHsgY29sOiBudW1iZXI7IHJvdzogbnVtYmVyOyBibXA6IEltYWdlQml0bWFwIH0+ID0gW107XG4gICAgY29uc3Qgc3RpdGNoV29ya2VyID0gYXN5bmMgKCk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgICAgZm9yICg7Oykge1xuICAgICAgICBjb25zdCBqb2IgPSBjb29yZHMuc2hpZnQoKTtcbiAgICAgICAgaWYgKCFqb2IpIHJldHVybjtcbiAgICAgICAgY29uc3QgYm1wID0gYXdhaXQgZmV0Y2hUaWxlKGNob3Nlbi5yZXMsIGpvYi5jb2wsIGpvYi5yb3csIHQpO1xuICAgICAgICB0aWxlcy5wdXNoKHsgY29sOiBqb2IuY29sLCByb3c6IGpvYi5yb3csIGJtcCB9KTtcbiAgICAgIH1cbiAgICB9O1xuICAgIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgQXJyYXkuZnJvbSh7IGxlbmd0aDogTWF0aC5taW4oVElMRV9GRVRDSF9DT05DVVJSRU5DWSwgY29vcmRzLmxlbmd0aCkgfSwgKCkgPT4gc3RpdGNoV29ya2VyKCkpLFxuICAgICk7XG5cbiAgICBsZXQgY2FudmFzOiBIVE1MQ2FudmFzRWxlbWVudCB8IE9mZnNjcmVlbkNhbnZhcztcbiAgICBpZiAodHlwZW9mIE9mZnNjcmVlbkNhbnZhcyAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIGNhbnZhcyA9IG5ldyBPZmZzY3JlZW5DYW52YXMoY2hvc2VuLndpZHRoLCBjaG9zZW4uaGVpZ2h0KTtcbiAgICB9IGVsc2Uge1xuICAgICAgY2FudmFzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnY2FudmFzJyk7XG4gICAgICBjYW52YXMud2lkdGggPSBjaG9zZW4ud2lkdGg7XG4gICAgICBjYW52YXMuaGVpZ2h0ID0gY2hvc2VuLmhlaWdodDtcbiAgICB9XG4gICAgY29uc3QgY3R4ID0gY2FudmFzLmdldENvbnRleHQoJzJkJykgYXNcbiAgICAgIHwgQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJEXG4gICAgICB8IE9mZnNjcmVlbkNhbnZhc1JlbmRlcmluZ0NvbnRleHQyRFxuICAgICAgfCBudWxsO1xuICAgIGlmICghY3R4KSB0aHJvdyBuZXcgRXJyb3IoJ1tuYXBhcmktanNdIHNsaWNlIHN0aXRjaDogMkQgY29udGV4dCB1bmF2YWlsYWJsZScpO1xuICAgIGZvciAoY29uc3QgeyBjb2wsIHJvdywgYm1wIH0gb2YgdGlsZXMpIHtcbiAgICAgIGN0eC5kcmF3SW1hZ2UoYm1wLCBjb2wgKiB0LCByb3cgKiB0KTtcbiAgICAgIGJtcC5jbG9zZT8uKCk7XG4gICAgfVxuXG4gICAgLy8gU2FmZXR5IG5ldDogaWYgdGhlIGNob3NlbiBsZXZlbCBzdGlsbCBleGNlZWRzIHRoZSBHUFUgdGV4dHVyZSBsaW1pdCAoZS5nLiBhIG11bHRpY2hhbm5lbFxuICAgIC8vIGltYWdlIHdob3NlIGNvYXJzZXN0IFJFQUwgbGV2ZWwgaXMgaHVnZSDigJQgb3ZlcnZpZXcgbGV2ZWxzIGFyZSBjb21wb3NpdGUtb25seSksIGRvd25zY2FsZSB0aGVcbiAgICAvLyBzdGl0Y2hlZCBjYW52YXMgdG8gZml0IHNvIHRoZSBXZWJHUFUgdGV4dHVyZSB1cGxvYWQgY2FuJ3QgZmFpbC5cbiAgICBjb25zdCBsb25nZXN0ID0gTWF0aC5tYXgoY2hvc2VuLndpZHRoLCBjaG9zZW4uaGVpZ2h0KTtcbiAgICBpZiAobG9uZ2VzdCA+IE1BWF9URVhUVVJFX0RJTSkge1xuICAgICAgY29uc3Qgc2NhbGUgPSBNQVhfVEVYVFVSRV9ESU0gLyBsb25nZXN0O1xuICAgICAgY29uc3Qgb3V0VyA9IE1hdGgubWF4KDEsIE1hdGguZmxvb3IoY2hvc2VuLndpZHRoICogc2NhbGUpKTtcbiAgICAgIGNvbnN0IG91dEggPSBNYXRoLm1heCgxLCBNYXRoLmZsb29yKGNob3Nlbi5oZWlnaHQgKiBzY2FsZSkpO1xuICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICBgW25hcGFyaS1qc10gc3RpdGNoZWQgbGV2ZWwgJHtjaG9zZW4ud2lkdGh9w5cke2Nob3Nlbi5oZWlnaHR9IGV4Y2VlZHMgdGhlIGAgK1xuICAgICAgICAgIGAke01BWF9URVhUVVJFX0RJTX1weCB0ZXh0dXJlIGxpbWl0OyBkb3duc2NhbGluZyB0byAke291dFd9w5cke291dEh9LmAsXG4gICAgICApO1xuICAgICAgbGV0IG91dDogSFRNTENhbnZhc0VsZW1lbnQgfCBPZmZzY3JlZW5DYW52YXM7XG4gICAgICBpZiAodHlwZW9mIE9mZnNjcmVlbkNhbnZhcyAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgb3V0ID0gbmV3IE9mZnNjcmVlbkNhbnZhcyhvdXRXLCBvdXRIKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIG91dCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICAgICAgICBvdXQud2lkdGggPSBvdXRXO1xuICAgICAgICBvdXQuaGVpZ2h0ID0gb3V0SDtcbiAgICAgIH1cbiAgICAgIGNvbnN0IG9jdHggPSBvdXQuZ2V0Q29udGV4dCgnMmQnKSBhc1xuICAgICAgICB8IENhbnZhc1JlbmRlcmluZ0NvbnRleHQyRFxuICAgICAgICB8IE9mZnNjcmVlbkNhbnZhc1JlbmRlcmluZ0NvbnRleHQyRFxuICAgICAgICB8IG51bGw7XG4gICAgICBpZiAoIW9jdHgpIHRocm93IG5ldyBFcnJvcignW25hcGFyaS1qc10gc2xpY2UgZG93bnNjYWxlOiAyRCBjb250ZXh0IHVuYXZhaWxhYmxlJyk7XG4gICAgICBvY3R4LmRyYXdJbWFnZShjYW52YXMgYXMgdW5rbm93biBhcyBDYW52YXNJbWFnZVNvdXJjZSwgMCwgMCwgb3V0Vywgb3V0SCk7XG4gICAgICByZXR1cm4gY3JlYXRlSW1hZ2VCaXRtYXAob3V0IGFzIHVua25vd24gYXMgSW1hZ2VCaXRtYXBTb3VyY2UpO1xuICAgIH1cbiAgICByZXR1cm4gY3JlYXRlSW1hZ2VCaXRtYXAoY2FudmFzIGFzIHVua25vd24gYXMgSW1hZ2VCaXRtYXBTb3VyY2UpO1xuICB9XG5cbiAgLy8g4pSA4pSAIElEYXRhUmVuZGVyZXI6IGxvYWQgLyByZW5kZXIgLyB2aWV3cG9ydCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgYXN5bmMgbG9hZChpbWFnZUluZm86IElJbWFnZUluZm8sIHpJbmRleDogbnVtYmVyKTogUHJvbWlzZTxOYXBhcmlMb2FkZWQ+IHtcbiAgICAvLyBLZWVwcyBTaW1wbGVTbGljZUFjY2Vzc1NlcnZpY2UncyBibG9iIGNhY2hlIGluIHN5bmMgZXZlbiB3aGVuIHRoZSB1c2VyXG4gICAgLy8gc3dpdGNoZXMgYmFja2VuZHMgKGUuZy4gT1NEIEltYWdlIOKGkiBuYXBhcmkgVm9sdW1lKSBvbiB0aGUgc2FtZSBmaWxlIOKAlFxuICAgIC8vIHdoaWNoZXZlciBiYWNrZW5kIGxvYWRzIGEgZ2VudWluZWx5IGRpZmZlcmVudCBmaWxlIGZpcnN0IGV2aWN0cyBpdC5cbiAgICB0aGlzLnNpbXBsZVN0YWNrLm5vdGVBY3RpdmVGaWxlKGltYWdlSW5mby5maWxlTmFtZSk7XG4gICAgdGhpcy5sb2FkZWQgPSB7IGltYWdlSW5mbywgejogekluZGV4LCBmaWxlbmFtZTogaW1hZ2VJbmZvLmZpbGVOYW1lIH07XG4gICAgcmV0dXJuIHRoaXMubG9hZGVkO1xuICB9XG5cbiAgYXN5bmMgcGxvdChcbiAgICBwbG90RGl2OiBzdHJpbmcsXG4gICAgaW1hZ2VMb2FkZWQ6IHVua25vd24sXG4gICAgaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvLFxuICAgIHNjcmVlbkhlaWdodDogbnVtYmVyLFxuICAgIHBsb3RUeXBlOiBQbG90VHlwZSxcbiAgICBfaW5QbGFjZT86IGJvb2xlYW4sXG4gICk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGNvbnN0IGhvc3QgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChwbG90RGl2KTtcbiAgICBpZiAoIWhvc3QpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoYFtuYXBhcmktanNdIHBsb3QgdGFyZ2V0ICMke3Bsb3REaXZ9IG5vdCBmb3VuZGApO1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICB0aGlzLnJlc2V0KCk7XG4gICAgdGhpcy5ob3N0ID0gaG9zdDtcbiAgICB0aGlzLnBsb3REaXZJZCA9IHBsb3REaXY7XG4gICAgdGhpcy5jdXJyZW50UGxvdFR5cGUgPSBwbG90VHlwZTtcblxuICAgIGNvbnN0IGNhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICAgIGNhbnZhcy5zdHlsZS5kaXNwbGF5ID0gJ2Jsb2NrJztcbiAgICBjYW52YXMuc3R5bGUud2lkdGggPSAnMTAwJSc7XG4gICAgY2FudmFzLnN0eWxlLmhlaWdodCA9IHNjcmVlbkhlaWdodCA/IGAke3NjcmVlbkhlaWdodH1weGAgOiAnMTAwJSc7XG4gICAgaG9zdC5hcHBlbmRDaGlsZChjYW52YXMpO1xuICAgIHRoaXMuY2FudmFzID0gY2FudmFzO1xuXG4gICAgY29uc3QgaW5mbyA9IChpbWFnZUxvYWRlZCBhcyBOYXBhcmlMb2FkZWQpPy5pbWFnZUluZm8gPz8gaW1hZ2VJbmZvO1xuICAgIGNvbnN0IHogPSAoaW1hZ2VMb2FkZWQgYXMgTmFwYXJpTG9hZGVkKT8ueiA/PyAwO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHZpZXdlciA9IG5ldyBWaWV3ZXIoeyBjYW52YXMsIGJhY2tncm91bmQ6IHsgcjogMC4wNywgZzogMC4wNywgYjogMC4wOSwgYTogMSB9IH0pO1xuICAgICAgdGhpcy52aWV3ZXIgPSB2aWV3ZXI7XG4gICAgICBhd2FpdCB2aWV3ZXIucmVhZHk7XG5cbiAgICAgIGlmIChpc05hcGFyaVNjYXR0ZXIocGxvdFR5cGUpKSB7XG4gICAgICAgIGF3YWl0IHRoaXMubW91bnRTY2F0dGVyKHZpZXdlciwgeik7XG4gICAgICB9IGVsc2UgaWYgKGlzTmFwYXJpU2NhdHRlcjNkKHBsb3RUeXBlKSkge1xuICAgICAgICBhd2FpdCB0aGlzLm1vdW50U2NhdHRlcjNkKHZpZXdlciwgaW5mbyk7XG4gICAgICB9IGVsc2UgaWYgKGlzTmFwYXJpU3VyZmFjZShwbG90VHlwZSkpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5tb3VudFN1cmZhY2Uodmlld2VyKTtcbiAgICAgIH0gZWxzZSBpZiAoaXNOYXBhcmkzZChwbG90VHlwZSkpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5tb3VudFZvbHVtZSh2aWV3ZXIsIGluZm8sIHBsb3RUeXBlKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGF3YWl0IHRoaXMucmVuZGVySW1hZ2Uoeik7XG4gICAgICAgIHRoaXMuZml0Q2FtZXJhU29vbigpO1xuICAgICAgICB0aGlzLnN1YnNjcmliZURpc3BsYXlTdGF0ZSgpO1xuICAgICAgICB0aGlzLmluc3RhbGxTY2FsZUJhcigpO1xuICAgICAgICB0aGlzLnJlZ2lvbk92ZXJsYXkgPSBuZXcgTmFwYXJpUmVnaW9uT3ZlcmxheShob3N0LCB2aWV3ZXIsIHRoaXMucmVnaW9uU3RvcmUpO1xuICAgICAgICB0aGlzLmJ1aWxkVG9vbEhvc3RzKCk7XG4gICAgICAgIC8vIEtlZXAgdGhlIHBpeGVsLXRvb2wgcmVhZGJhY2sgKGxhc3RQaXhlbHMpIGN1cnJlbnQgYXMgdGhlIHZpZXcgcGFucy96b29tcyBhbmQgdGlsZWRcbiAgICAgICAgLy8gbGV2ZWxzIGxvYWQsIHNvIHdhbmQvYnJ1c2gvU0FNIHNhbXBsZSB0aGUgYWN0dWFsbHktZGlzcGxheWVkIGltYWdlIChqaXQtdWkjMTAyKS5cbiAgICAgICAgdGhpcy5jYW1lcmFSZWFkYmFja09mZiA9IHZpZXdlci5jYW1lcmEuY2hhbmdlZC5jb25uZWN0KCgpID0+IHRoaXMuYXJtUmVhZGJhY2soKSk7XG4gICAgICB9XG4gICAgICB0aGlzLnNjaGVkdWxlUmVhZGJhY2soKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgY29uc29sZS5lcnJvcignW25hcGFyaS1qc10gcGxvdCBmYWlsZWQ6JywgZXJyKTtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cblxuICAvLyDilIDilIAgQ2hhbm5lbHM6IHBlci1jaGFubmVsIGNvbXBvc2l0ZSwgTFVULCBuYXRpdmUgaGlzdG9ncmFtcyAoaml0LXVpIzEwMikg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLyoqIFRoZSBncmF5c2NhbGUgZGlzcGxheSBjb2xvcm1hcCBmcm9tIHRoZSBzdG9yZSBzZWxlY3Rpb24gKCtyZXZlcnNlKSwgZGVmYXVsdGluZyB0byBncmF5LlxuICAgKiAgTWFwcyB0aGUgaml0LXVpIHN0b3JlIGNvbG9ybWFwIG5vZGUgdG8gYSBuYXBhcmkgYENvbG9ybWFwYCB2aWEgdGhlIGxpYnJhcnkncyBMVVQgZmFjdG9yeTtcbiAgICogIHRoZSBtdWx0aWNoYW5uZWwgdGludCByYW1wcyBhcmUgYnVpbHQgaW5zaWRlIHtAbGluayBNdWx0aUNoYW5uZWxJbWFnZVZpZXd9IGZyb20gZWFjaFxuICAgKiAgY2hhbm5lbCdzIGhleCBjb2xvdXIuICovXG4gIHByaXZhdGUgZ3JheXNjYWxlQ29sb3JtYXAoKTogQ29sb3JtYXAgfCBzdHJpbmcge1xuICAgIGNvbnN0IHZhbHVlID0gKHRoaXMuY3VycmVudENvbG9ybWFwIGFzIHsgZGF0YT86IHsgdmFsdWU/OiB1bmtub3duIH0gfSB8IG51bGwpPy5kYXRhPy52YWx1ZTtcbiAgICBjb25zdCBsdXQgPSB2YWx1ZSAhPSBudWxsID8gYnVpbGRDb2xvcm1hcEx1dCh2YWx1ZSwgdGhpcy5jdXJyZW50UmV2ZXJzZSkgOiBudWxsO1xuICAgIGlmIChsdXQpIHJldHVybiBjb2xvcm1hcEZyb21MdXQoJ2dyYXktY21hcCcsIGx1dCk7XG4gICAgcmV0dXJuIHRoaXMuY3VycmVudFJldmVyc2VcbiAgICAgID8gY29sb3JtYXBGcm9tTHV0KCdncmF5LXJldicsIFtbMjU1LCAyNTUsIDI1NV0sIFswLCAwLCAwXV0gYXMgUmdiW10pXG4gICAgICA6ICdncmF5JztcbiAgfVxuXG4gIC8qKiBGZXRjaCBhIHN0aXRjaGVkIHNsaWNlIGFuZCByZWFkIGl0IGJhY2sgYXMgYSBzaW5nbGUtY2hhbm5lbCB1aW50OCBwbGFuZS4gYGNoYW5uZWxgIHNlbGVjdHMgYVxuICAgKiAgYmFuZCAobXVsdGljaGFubmVsKTsgb21pdCBpdCBmb3IgdGhlIGdyYXlzY2FsZSBjb21wb3NpdGUgKGFsbCBvdmVydmlldyBsZXZlbHMgYXZhaWxhYmxlLCBzbyBhXG4gICAqICBsYXJnZSBpbWFnZSBwaWNrcyBhIGZpdHRpbmcgZG93bnNjYWxlZCBsZXZlbCByYXRoZXIgdGhhbiBvbmx5IHRoZSBmdWxsLXJlcyByZWFsIGxldmVsKS4gKi9cbiAgcHJpdmF0ZSBhc3luYyBmZXRjaENoYW5uZWxEYXRhKFxuICAgIHo6IG51bWJlcixcbiAgICBjaGFubmVsPzogbnVtYmVyLFxuICAgIGJ1ZGdldFRpbGVzPzogbnVtYmVyLFxuICApOiBQcm9taXNlPHsgZGF0YTogVWludDhBcnJheTsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfT4ge1xuICAgIGNvbnN0IGJtcCA9IGF3YWl0IHRoaXMuZmV0Y2hTbGljZSh6LCBjaGFubmVsLCBidWRnZXRUaWxlcyk7XG4gICAgcmV0dXJuIHRoaXMuYml0bWFwVG9MdW1pbmFuY2UoYm1wKTtcbiAgfVxuXG4gIC8qKiBEZWNvZGUgYW4gYEltYWdlQml0bWFwYCAoc2VydmVyIGJhbmRzIGFyZSBncmF5c2NhbGUsIFI9Rz1CKSB0byBhIHNpbmdsZS1jaGFubmVsIHVpbnQ4IHBsYW5lLlxuICAgKiAgYG1heFNpZGVgIGNhcHMgdGhlIGxvbmdlc3Qgc2lkZSAoZG93bnNjYWxpbmcgb24gdGhlIGNhbnZhcyBkcmF3KSDigJQgdXNlZCB0byBrZWVwIHByZS1sb2FkZWRcbiAgICogIHN1cmZhY2Ugc2xpY2UgcGxhbmVzIHNtYWxsLiAqL1xuICBwcml2YXRlIGJpdG1hcFRvTHVtaW5hbmNlKFxuICAgIGJtcDogSW1hZ2VCaXRtYXAsXG4gICAgbWF4U2lkZT86IG51bWJlcixcbiAgKTogeyBkYXRhOiBVaW50OEFycmF5OyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlciB9IHtcbiAgICBjb25zdCBzY2FsZSA9IG1heFNpZGUgPyBNYXRoLm1pbigxLCBtYXhTaWRlIC8gTWF0aC5tYXgoYm1wLndpZHRoLCBibXAuaGVpZ2h0LCAxKSkgOiAxO1xuICAgIGNvbnN0IHcgPSBNYXRoLm1heCgxLCBNYXRoLnJvdW5kKGJtcC53aWR0aCAqIHNjYWxlKSk7XG4gICAgY29uc3QgaCA9IE1hdGgubWF4KDEsIE1hdGgucm91bmQoYm1wLmhlaWdodCAqIHNjYWxlKSk7XG4gICAgbGV0IGNhbnZhczogSFRNTENhbnZhc0VsZW1lbnQgfCBPZmZzY3JlZW5DYW52YXM7XG4gICAgaWYgKHR5cGVvZiBPZmZzY3JlZW5DYW52YXMgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICBjYW52YXMgPSBuZXcgT2Zmc2NyZWVuQ2FudmFzKHcsIGgpO1xuICAgIH0gZWxzZSB7XG4gICAgICBjYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKTtcbiAgICAgIGNhbnZhcy53aWR0aCA9IHc7XG4gICAgICBjYW52YXMuaGVpZ2h0ID0gaDtcbiAgICB9XG4gICAgY29uc3QgY3R4ID0gY2FudmFzLmdldENvbnRleHQoJzJkJykgYXNcbiAgICAgIHwgQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJEXG4gICAgICB8IE9mZnNjcmVlbkNhbnZhc1JlbmRlcmluZ0NvbnRleHQyRFxuICAgICAgfCBudWxsO1xuICAgIGlmICghY3R4KSB0aHJvdyBuZXcgRXJyb3IoJ1tuYXBhcmktanNdIGNoYW5uZWwgcmVhZGJhY2s6IDJEIGNvbnRleHQgdW5hdmFpbGFibGUnKTtcbiAgICAvLyBTY2FsZSB0aGUgV0hPTEUgYml0bWFwIGludG8gdGhlIChwb3NzaWJseSBzbWFsbGVyKSB0YXJnZXQgY2FudmFzIOKAlCBkcmF3aW5nIGF0IG5hdHVyYWwgc2l6ZVxuICAgIC8vIHdvdWxkIGNyb3AgdG8gdGhlIHRvcC1sZWZ0IHfDl2ggY29ybmVyIHdoZW4gZG93bnNjYWxpbmcgYSBsYXJnZSBzbGljZSAobWF4U2lkZSA8IGJtcCBzaXplKS5cbiAgICBjdHguZHJhd0ltYWdlKGJtcCwgMCwgMCwgdywgaCk7XG4gICAgYm1wLmNsb3NlPy4oKTtcbiAgICBjb25zdCByZ2JhID0gY3R4LmdldEltYWdlRGF0YSgwLCAwLCB3LCBoKS5kYXRhO1xuICAgIGNvbnN0IGRhdGEgPSBuZXcgVWludDhBcnJheSh3ICogaCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB3ICogaDsgaSsrKSBkYXRhW2ldID0gcmdiYVtpICogNF07XG4gICAgcmV0dXJuIHsgZGF0YSwgd2lkdGg6IHcsIGhlaWdodDogaCB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbmRlciB0aGUgMkQgaW1hZ2UgZm9yIHNsaWNlIGB6YC4gV2l0aCBhIHNlcnZlciBweXJhbWlkIGRlc2NyaXB0b3Igd2UgdXNlIGEgcHlyYW1pZGFsXG4gICAqIHtAbGluayBUaWxlZFNvdXJjZX0gcGVyIGxheWVyIHNvIHRoZSB2aWV3IHJlZmluZXMgdG8gaGlnaGVyIHJlc29sdXRpb24gb24gem9vbSAobGlrZSBPU0QpIGFuZFxuICAgKiBzaXRzIG5hdHVyYWxseSBpbiBmdWxsLXJlc29sdXRpb24gY29vcmRpbmF0ZXM7IHdpdGhvdXQgb25lIHdlIGZhbGwgYmFjayB0byB0aGUgc2luZ2xlLWxldmVsXG4gICAqIHN0aXRjaC4gVGhyZWUgZGlzcGxheSBtb2RlcyBlaXRoZXIgd2F5OiBtdWx0aWNoYW5uZWwgYWRkaXRpdmUgdGludCwgZ3JheXNjYWxlIGNvbG9ybWFwLCBSR0IuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHJlbmRlckltYWdlKHo6IG51bWJlciwgdG9rZW4/OiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB2ID0gdGhpcy52aWV3ZXI7XG4gICAgaWYgKCF2KSByZXR1cm47XG4gICAgY29uc3QgZGVzYyA9IGF3YWl0IHRoaXMuZW5zdXJlRGVzY3JpcHRvcigpO1xuICAgIGlmIChkZXNjICYmIGRlc2MubGV2ZWxzPy5sZW5ndGgpIHtcbiAgICAgIGlmICh0b2tlbiAhPSBudWxsICYmIHRva2VuICE9PSB0aGlzLnNsaWNlUmVxKSByZXR1cm47XG4gICAgICBhd2FpdCB0aGlzLnJlbmRlckltYWdlVGlsZWQoeiwgZGVzYyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHJldHVybiB0aGlzLnJlbmRlckltYWdlU3RpdGNoZWQoeiwgdG9rZW4pO1xuICB9XG5cbiAgLyoqIFNpbmdsZS1sZXZlbCBzdGl0Y2ggZmFsbGJhY2sgKG5vIGRlc2NyaXB0b3IpOiB0aGUgcHJlLXRpbGluZyBwYXRoLiAqL1xuICBwcml2YXRlIGFzeW5jIHJlbmRlckltYWdlU3RpdGNoZWQoejogbnVtYmVyLCB0b2tlbj86IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHYgPSB0aGlzLnZpZXdlcjtcbiAgICBpZiAoIXYpIHJldHVybjtcbiAgICBjb25zdCBkZXNjID0gYXdhaXQgdGhpcy5lbnN1cmVEZXNjcmlwdG9yKCk7XG4gICAgY29uc3Qgc3RhdGVzID0gdGhpcy5zdG9yZS5jdXJyZW50Q2hhbm5lbFN0YXRlcygpO1xuICAgIGNvbnN0IGNoYW5uZWxDb3VudCA9IGRlc2M/LmNoYW5uZWxzID8/IChzdGF0ZXMubGVuZ3RoIHx8IDEpO1xuICAgIGNvbnN0IG11bHRpY2hhbm5lbCA9ICEhZGVzYz8ubXVsdGljaGFubmVsICYmIGNoYW5uZWxDb3VudCA+IDE7XG5cbiAgICAvLyAxKSBGZXRjaCBhbGwgcGl4ZWwgZGF0YSBCRUZPUkUgdG91Y2hpbmcgdGhlIHZpZXdlciwgc28gYSBzdXBlcnNlZGVkIHNjcnViIGNhbiBiYWlsIHdpdGhvdXRcbiAgICAvLyAgICBoYXZpbmcgdG9ybiBkb3duIHRoZSB2aXNpYmxlIGxheWVycyAoYXZvaWRzIGZsaWNrZXIgLyBvdXQtb2Ytb3JkZXIgbGF5ZXIgc3RhdGUpLlxuICAgIGxldCBtb2RlOiAnZ3JheXNjYWxlJyB8ICdtdWx0aWNoYW5uZWwnIHwgJ3JnYic7XG4gICAgbGV0IHBsYW5lczogQXJyYXk8eyBkYXRhOiBVaW50OEFycmF5OyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlciB9PiA9IFtdO1xuICAgIGxldCBiaXRtYXA6IEltYWdlQml0bWFwIHwgbnVsbCA9IG51bGw7XG4gICAgaWYgKG11bHRpY2hhbm5lbCkge1xuICAgICAgbW9kZSA9ICdtdWx0aWNoYW5uZWwnO1xuICAgICAgcGxhbmVzID0gYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgIEFycmF5LmZyb20oeyBsZW5ndGg6IGNoYW5uZWxDb3VudCB9LCAoXywgYykgPT4gdGhpcy5mZXRjaENoYW5uZWxEYXRhKHosIGMpKSxcbiAgICAgICk7XG4gICAgfSBlbHNlIGlmIChjaGFubmVsQ291bnQgPT09IDEpIHtcbiAgICAgIG1vZGUgPSAnZ3JheXNjYWxlJztcbiAgICAgIC8vIENvbXBvc2l0ZSBmZXRjaCAobm8gY2hhbm5lbCkg4oaSIGFsbCBvdmVydmlldyBsZXZlbHMgdXNhYmxlLCBzbyBhIGxhcmdlIGdyYXlzY2FsZSBpbWFnZVxuICAgICAgLy8gc2VsZWN0cyBhIGZpdHRpbmcgZG93bnNjYWxlZCBsZXZlbCBpbnN0ZWFkIG9mIHRoZSBmdWxsLXJlcyByZWFsIGxldmVsICh0ZXh0dXJlIGxpbWl0KS5cbiAgICAgIHBsYW5lcyA9IFthd2FpdCB0aGlzLmZldGNoQ2hhbm5lbERhdGEoeildO1xuICAgIH0gZWxzZSB7XG4gICAgICBtb2RlID0gJ3JnYic7XG4gICAgICBiaXRtYXAgPSBhd2FpdCB0aGlzLmZldGNoU2xpY2Uoeik7XG4gICAgfVxuXG4gICAgLy8gMikgQ29tbWl0IOKAlCB1bmxlc3MgYSBuZXdlciBzY3J1YiBzdXBlcnNlZGVkIHVzIG9yIHRoZSB2aWV3ZXIgd2FzIHRvcm4gZG93bi5cbiAgICBpZiAoKHRva2VuICE9IG51bGwgJiYgdG9rZW4gIT09IHRoaXMuc2xpY2VSZXEpIHx8IHRoaXMudmlld2VyICE9PSB2KSB7XG4gICAgICBiaXRtYXA/LmNsb3NlPy4oKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBUaGUgZGlzcGxheWVkIHRleHR1cmUgbWF5IGJlIGEgZG93bnNjYWxlZCBweXJhbWlkIGxldmVsOyBzY2FsZSB0aGUgbGF5ZXIgaW50byBGVUxMLVJFU09MVVRJT05cbiAgICAvLyB3b3JsZCBjb29yZGluYXRlcyAobGV2ZWwtMCBwaXhlbHMpIHNvIHRoZSBjYW1lcmEsIHJlYWRiYWNrIGFuZCDigJQgY3JpdGljYWxseSDigJQgcHJlLXNhdmVkXG4gICAgLy8gcmVnaW9ucyAoc3RvcmVkIGluIGZ1bGwtcmVzIGNvb3JkcywgZS5nLiBuZHBpKSBhbGwgbGluZSB1cCByZWdhcmRsZXNzIG9mIHdoaWNoIGxldmVsIGlzIHNob3duLlxuICAgIC8vIFRoaXMgbWlycm9ycyBPU0QsIHdob3NlIGNvb3JkaW5hdGUgc3lzdGVtIGlzIGFsd2F5cyBsZXZlbCAwLlxuICAgIGNvbnN0IHRleFcgPSBtb2RlID09PSAncmdiJyA/IChiaXRtYXAgYXMgSW1hZ2VCaXRtYXApLndpZHRoIDogcGxhbmVzWzBdPy53aWR0aCA/PyAwO1xuICAgIGNvbnN0IHRleEggPSBtb2RlID09PSAncmdiJyA/IChiaXRtYXAgYXMgSW1hZ2VCaXRtYXApLmhlaWdodCA6IHBsYW5lc1swXT8uaGVpZ2h0ID8/IDA7XG4gICAgY29uc3QgZnVsbFcgPSBkZXNjPy53aWR0aCB8fCB0ZXhXIHx8IDE7XG4gICAgY29uc3QgZnVsbEggPSBkZXNjPy5oZWlnaHQgfHwgdGV4SCB8fCAxO1xuICAgIGNvbnN0IHNjYWxlOiBbbnVtYmVyLCBudW1iZXJdID0gW3RleFcgPyBmdWxsVyAvIHRleFcgOiAxLCB0ZXhIID8gZnVsbEggLyB0ZXhIIDogMV07XG5cbiAgICB0aGlzLmltYWdlTW9kZSA9IG1vZGU7XG4gICAgY29uc3QgaW50ZXJwb2xhdGlvbjogJ2xpbmVhcicgfCAnbmVhcmVzdCcgPSB0aGlzLmltYWdlU21vb3RoaW5nID8gJ2xpbmVhcicgOiAnbmVhcmVzdCc7XG4gICAgdGhpcy5jaGFubmVsVmlldyA9IG5ldyBNdWx0aUNoYW5uZWxJbWFnZVZpZXcodik7XG4gICAgaWYgKG1vZGUgPT09ICdtdWx0aWNoYW5uZWwnKSB7XG4gICAgICBjb25zdCB2aWV3czogQ2hhbm5lbFZpZXdbXSA9IHBsYW5lcy5tYXAoKGQsIGMpID0+IHtcbiAgICAgICAgY29uc3Qgc3QgPSBzdGF0ZXMuZmluZCgocykgPT4gcy5pbmRleCA9PT0gYyk7XG4gICAgICAgIGNvbnN0IGNvbG9yID0gc3Q/LmNvbG9yID8/IGRlc2M/LmNoYW5uZWxJbmZvPy5bY10/LmNvbG9yID8/IHRpbnRGb3IoYyk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgc291cmNlOiB7IGtpbmQ6ICd0eXBlZCcsIHdpZHRoOiBkLndpZHRoLCBoZWlnaHQ6IGQuaGVpZ2h0LCBjaGFubmVsczogMSwgZHR5cGU6ICd1aW50OCcsIGRhdGE6IGQuZGF0YSB9LFxuICAgICAgICAgIHRpbnQ6IGNvbG9yLFxuICAgICAgICAgIG5hbWU6IHN0Py5uYW1lID8/IGBjaCR7Y31gLFxuICAgICAgICAgIGNvbnRyYXN0TGltaXRzOiBbc3Q/Lm1pbiA/PyAwLCBzdD8ubWF4ID8/IDI1NV0sXG4gICAgICAgICAgZ2FtbWE6IHN0Py5nYW1tYSA/PyAxLFxuICAgICAgICAgIHZpc2libGU6IHN0Py52aXNpYmxlID8/IHRydWUsXG4gICAgICAgICAgaW52ZXJ0OiB0aGlzLmludmVydEVuYWJsZWQsXG4gICAgICAgICAgc2NhbGUsXG4gICAgICAgIH07XG4gICAgICB9KTtcbiAgICAgIHRoaXMuY2hhbm5lbFZpZXcucmVuZGVyKCdtdWx0aWNoYW5uZWwnLCB2aWV3cywgeyBpbnRlcnBvbGF0aW9uIH0pO1xuICAgIH0gZWxzZSBpZiAobW9kZSA9PT0gJ2dyYXlzY2FsZScpIHtcbiAgICAgIGNvbnN0IGQgPSBwbGFuZXNbMF07XG4gICAgICBjb25zdCBzdCA9IHN0YXRlc1swXTtcbiAgICAgIHRoaXMuY2hhbm5lbFZpZXcucmVuZGVyKFxuICAgICAgICAnZ3JheXNjYWxlJyxcbiAgICAgICAgW1xuICAgICAgICAgIHtcbiAgICAgICAgICAgIHNvdXJjZTogeyBraW5kOiAndHlwZWQnLCB3aWR0aDogZC53aWR0aCwgaGVpZ2h0OiBkLmhlaWdodCwgY2hhbm5lbHM6IDEsIGR0eXBlOiAndWludDgnLCBkYXRhOiBkLmRhdGEgfSxcbiAgICAgICAgICAgIGNvbG9ybWFwOiB0aGlzLmdyYXlzY2FsZUNvbG9ybWFwKCksXG4gICAgICAgICAgICBjb250cmFzdExpbWl0czogW3N0Py5taW4gPz8gMCwgc3Q/Lm1heCA/PyAyNTVdLFxuICAgICAgICAgICAgZ2FtbWE6IHN0Py5nYW1tYSA/PyAxLFxuICAgICAgICAgICAgaW52ZXJ0OiB0aGlzLmludmVydEVuYWJsZWQsXG4gICAgICAgICAgICBzY2FsZSxcbiAgICAgICAgICB9LFxuICAgICAgICBdLFxuICAgICAgICB7IGludGVycG9sYXRpb24gfSxcbiAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuY2hhbm5lbFZpZXcucmVuZGVyKCdyZ2InLCBbeyBzb3VyY2U6IGJpdG1hcCBhcyBJbWFnZUJpdG1hcCwgc2NhbGUgfV0sIHsgaW50ZXJwb2xhdGlvbiB9KTtcbiAgICB9XG4gICAgdGhpcy5pbWFnZVcgPSBmdWxsVztcbiAgICB0aGlzLmltYWdlSCA9IGZ1bGxIO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbmRlciB0aGUgMkQgaW1hZ2Ugd2l0aCBweXJhbWlkYWwge0BsaW5rIFRpbGVkU291cmNlfXMg4oCUIHRoZSB2aWV3IHJlZmluZXMgdG8gaGlnaGVyIHJlc29sdXRpb25cbiAgICogYXMgeW91IHpvb20gaW4gKHRoZSB2aXN1YWwgZmV0Y2hlcyB0aGUgbGV2ZWwgd2hvc2UgdGV4ZWxzIOKJiCBzY3JlZW4gcGl4ZWxzKSBhbmQgc2l0cyBpbiBmdWxsLXJlc1xuICAgKiBjb29yZGluYXRlcyBzbyByZWdpb25zIGFsaWduLiBTYW1lIHRocmVlIG1vZGVzIGFzIHRoZSBzdGl0Y2ggcGF0aC4gUGVyLWNoYW5uZWwgbGF5ZXJzIHVzZSB0aGVcbiAgICogUkVBTCBweXJhbWlkIGxldmVscyAocGVyLWNoYW5uZWwgdGlsZXMgb25seSBleGlzdCB0aGVyZSk7IHRoZSBjb21wb3NpdGUgdXNlcyBhbGwgbGV2ZWxzLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyByZW5kZXJJbWFnZVRpbGVkKHo6IG51bWJlciwgZGVzYzogVGlsZURlc2NyaXB0b3IpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB2ID0gdGhpcy52aWV3ZXI7XG4gICAgaWYgKCF2KSByZXR1cm47XG4gICAgY29uc3Qgc3RhdGVzID0gdGhpcy5zdG9yZS5jdXJyZW50Q2hhbm5lbFN0YXRlcygpO1xuICAgIGNvbnN0IGNoYW5uZWxDb3VudCA9IGRlc2MuY2hhbm5lbHMgPz8gKHN0YXRlcy5sZW5ndGggfHwgMSk7XG4gICAgY29uc3QgbXVsdGljaGFubmVsID0gISFkZXNjLm11bHRpY2hhbm5lbCAmJiBjaGFubmVsQ291bnQgPiAxO1xuICAgIGNvbnN0IGludGVycG9sYXRpb246ICdsaW5lYXInIHwgJ25lYXJlc3QnID0gdGhpcy5pbWFnZVNtb290aGluZyA/ICdsaW5lYXInIDogJ25lYXJlc3QnO1xuXG4gICAgdGhpcy50aWxlZCA9IHRydWU7XG4gICAgdGhpcy5jaGFubmVsVmlldyA9IG5ldyBNdWx0aUNoYW5uZWxJbWFnZVZpZXcodik7XG5cbiAgICBpZiAobXVsdGljaGFubmVsKSB7XG4gICAgICB0aGlzLmltYWdlTW9kZSA9ICdtdWx0aWNoYW5uZWwnO1xuICAgICAgY29uc3Qgdmlld3M6IENoYW5uZWxWaWV3W10gPSBbXTtcbiAgICAgIGZvciAobGV0IGMgPSAwOyBjIDwgY2hhbm5lbENvdW50OyBjKyspIHtcbiAgICAgICAgY29uc3Qgc3QgPSBzdGF0ZXMuZmluZCgocykgPT4gcy5pbmRleCA9PT0gYyk7XG4gICAgICAgIGNvbnN0IGNvbG9yID0gc3Q/LmNvbG9yID8/IGRlc2MuY2hhbm5lbEluZm8/LltjXT8uY29sb3IgPz8gdGludEZvcihjKTtcbiAgICAgICAgdmlld3MucHVzaCh7XG4gICAgICAgICAgc291cmNlOiB0aGlzLmJ1aWxkVGlsZWRTb3VyY2UoZGVzYywgYywgMSksXG4gICAgICAgICAgdGludDogY29sb3IsXG4gICAgICAgICAgbmFtZTogc3Q/Lm5hbWUgPz8gYGNoJHtjfWAsXG4gICAgICAgICAgY29udHJhc3RMaW1pdHM6IFtzdD8ubWluID8/IDAsIHN0Py5tYXggPz8gMjU1XSxcbiAgICAgICAgICBnYW1tYTogc3Q/LmdhbW1hID8/IDEsXG4gICAgICAgICAgdmlzaWJsZTogc3Q/LnZpc2libGUgPz8gdHJ1ZSxcbiAgICAgICAgICBpbnZlcnQ6IHRoaXMuaW52ZXJ0RW5hYmxlZCxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICB0aGlzLmNoYW5uZWxWaWV3LnJlbmRlcignbXVsdGljaGFubmVsJywgdmlld3MsIHsgaW50ZXJwb2xhdGlvbiB9KTtcbiAgICB9IGVsc2UgaWYgKGNoYW5uZWxDb3VudCA9PT0gMSkge1xuICAgICAgdGhpcy5pbWFnZU1vZGUgPSAnZ3JheXNjYWxlJztcbiAgICAgIGNvbnN0IHN0ID0gc3RhdGVzWzBdO1xuICAgICAgdGhpcy5jaGFubmVsVmlldy5yZW5kZXIoXG4gICAgICAgICdncmF5c2NhbGUnLFxuICAgICAgICBbXG4gICAgICAgICAge1xuICAgICAgICAgICAgc291cmNlOiB0aGlzLmJ1aWxkVGlsZWRTb3VyY2UoZGVzYywgdW5kZWZpbmVkLCAxKSxcbiAgICAgICAgICAgIGNvbG9ybWFwOiB0aGlzLmdyYXlzY2FsZUNvbG9ybWFwKCksXG4gICAgICAgICAgICBjb250cmFzdExpbWl0czogW3N0Py5taW4gPz8gMCwgc3Q/Lm1heCA/PyAyNTVdLFxuICAgICAgICAgICAgZ2FtbWE6IHN0Py5nYW1tYSA/PyAxLFxuICAgICAgICAgICAgaW52ZXJ0OiB0aGlzLmludmVydEVuYWJsZWQsXG4gICAgICAgICAgfSxcbiAgICAgICAgXSxcbiAgICAgICAgeyBpbnRlcnBvbGF0aW9uIH0sXG4gICAgICApO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmltYWdlTW9kZSA9ICdyZ2InO1xuICAgICAgdGhpcy5jaGFubmVsVmlldy5yZW5kZXIoJ3JnYicsIFt7IHNvdXJjZTogdGhpcy5idWlsZFRpbGVkU291cmNlKGRlc2MsIHVuZGVmaW5lZCwgNCkgfV0sIHtcbiAgICAgICAgaW50ZXJwb2xhdGlvbixcbiAgICAgIH0pO1xuICAgIH1cbiAgICB0aGlzLmltYWdlVyA9IGRlc2Mud2lkdGg7XG4gICAgdGhpcy5pbWFnZUggPSBkZXNjLmhlaWdodDtcbiAgICAvLyBBd2FpdCBvbiB0aGUgaW5pdGlhbCByZW5kZXIgc28gZ2V0SGlzdG9ncmFtL2F1dG9Db250cmFzdCBoYXZlIGRhdGEgaW1tZWRpYXRlbHk7IHNsaWNlIGNoYW5nZXNcbiAgICAvLyByZWZyZXNoIGZpcmUtYW5kLWZvcmdldCAodGhlIGhpc3RvZ3JhbSBwYW5lIHJldHJpZXMpLlxuICAgIGF3YWl0IHRoaXMucmVmcmVzaEhpc3RvZ3JhbVNhbXBsZXMoeiwgZGVzYyk7XG4gIH1cblxuICAvKiogQnVpbGQgYSBweXJhbWlkYWwgVGlsZWRTb3VyY2UgYmFja2VkIGJ5IHRoZSBzZXJ2ZXIgYC90aWxlYCBlbmRwb2ludC4gYGNoYW5uZWxgIHNlbGVjdHMgYSBiYW5kXG4gICAqICAoZ3JheXNjYWxlIGx1bWluYW5jZSwgcmVhbCBsZXZlbHMgb25seSk7IG9taXQgaXQgZm9yIHRoZSBjb21wb3NpdGUgKFJHQkEsIGFsbCBsZXZlbHMpLiAqL1xuICBwcml2YXRlIGJ1aWxkVGlsZWRTb3VyY2UoXG4gICAgZGVzYzogVGlsZURlc2NyaXB0b3IsXG4gICAgY2hhbm5lbDogbnVtYmVyIHwgdW5kZWZpbmVkLFxuICAgIGNoYW5uZWxzOiAxIHwgNCxcbiAgKTogVGlsZWRTb3VyY2Uge1xuICAgIGNvbnN0IGluZm9CNjQgPSB0aGlzLnRpbGVzLmdldFNlbGVjdGVkSW5mb0I2NCgpID8/ICcnO1xuICAgIC8vIFBlci1jaGFubmVsIHRpbGVzIGV4aXN0IG9ubHkgYXQgUkVBTCBCaW8tRm9ybWF0cyBsZXZlbHM7IHRoZSBjb21wb3NpdGUgZXhpc3RzIGF0IGFsbCBsZXZlbHMuXG4gICAgY29uc3QgdXNhYmxlID1cbiAgICAgIGNoYW5uZWwgPT0gbnVsbFxuICAgICAgICA/IGRlc2MubGV2ZWxzXG4gICAgICAgIDogZGVzYy5sZXZlbHMuc2xpY2UoMCwgTWF0aC5tYXgoMSwgZGVzYy5yZWFsTGV2ZWxzID8/IGRlc2MubGV2ZWxzLmxlbmd0aCkpO1xuICAgIGNvbnN0IGxldmVsU2NhbGVzID0gdXNhYmxlLm1hcCgobCkgPT4gZGVzYy53aWR0aCAvIE1hdGgubWF4KDEsIGwud2lkdGgpKTsgLy8gbGV2ZWwtMCBweCBwZXIgbGV2ZWwgcHhcbiAgICBjb25zdCB0aWxlU2l6ZSA9IGRlc2MudGlsZVNpemUgfHwgVElMRV9TSVpFO1xuICAgIGNvbnN0IGNoID0gY2hhbm5lbCA9PSBudWxsID8gJycgOiBgJmNoYW5uZWw9JHtjaGFubmVsfWA7XG4gICAgY29uc3QgYXBpID0gdGhpcy5hcGk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGtpbmQ6ICd0aWxlZCcsXG4gICAgICB3aWR0aDogZGVzYy53aWR0aCxcbiAgICAgIGhlaWdodDogZGVzYy5oZWlnaHQsXG4gICAgICB0aWxlU2l6ZSxcbiAgICAgIGxldmVsczogdXNhYmxlLmxlbmd0aCxcbiAgICAgIGxldmVsU2NhbGVzLFxuICAgICAgZGVwdGg6IE1hdGgubWF4KDEsIGRlc2MueiB8fCAxKSxcbiAgICAgIGNoYW5uZWxzLFxuICAgICAgZHR5cGU6ICd1aW50OCcsXG4gICAgICBmZXRjaFRpbGU6IGFzeW5jIChrZXk6IFRpbGVLZXkpOiBQcm9taXNlPFBpeGVsQ2h1bms+ID0+IHtcbiAgICAgICAgY29uc3QgcmVzID0gdXNhYmxlW2tleS5sZXZlbF0/LnJlcyA/PyBrZXkubGV2ZWw7XG4gICAgICAgIGNvbnN0IHVybCA9IGAke2FwaX10aWxlP2luZm89JHtpbmZvQjY0fSZyZXM9JHtyZXN9JmNvbD0ke2tleS5jb2x9JnJvdz0ke2tleS5yb3d9Jno9JHtrZXkuen0mdGlsZVNpemU9JHt0aWxlU2l6ZX0ke2NofWA7XG4gICAgICAgIGNvbnN0IGhlYWRlcnMgPSBhd2FpdCB0aGlzLnRpbGVzXG4gICAgICAgICAgLmdldEF1dGhIZWFkZXJzKClcbiAgICAgICAgICAuY2F0Y2goKCkgPT4gKHt9KSBhcyBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+KTtcbiAgICAgICAgY29uc3QgcmVzcCA9IGF3YWl0IGZldGNoKHVybCwgeyBoZWFkZXJzIH0pO1xuICAgICAgICBpZiAoIXJlc3Aub2spIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFtuYXBhcmktanNdIHRpbGUgJHtrZXkubGV2ZWx9LyR7a2V5LmNvbH0vJHtrZXkucm93fSDihpIgJHtyZXNwLnN0YXR1c31gKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBibXAgPSBhd2FpdCBjcmVhdGVJbWFnZUJpdG1hcChhd2FpdCByZXNwLmJsb2IoKSk7XG4gICAgICAgIGlmIChjaGFubmVscyA9PT0gNCkgcmV0dXJuIHsgd2lkdGg6IGJtcC53aWR0aCwgaGVpZ2h0OiBibXAuaGVpZ2h0LCBkYXRhOiBibXAgfTtcbiAgICAgICAgcmV0dXJuIHRoaXMuYml0bWFwVG9MdW1pbmFuY2UoYm1wKTtcbiAgICAgIH0sXG4gICAgfTtcbiAgfVxuXG4gIC8qKiAoUmUpZmV0Y2ggYSBjb2Fyc2UgcGVyLWNoYW5uZWwgbHVtaW5hbmNlIHNhbXBsZSBmb3IgdGhlIGhpc3RvZ3JhbSAodGlsZWQgbW9kZSBoYXMgbm8gZnVsbFxuICAgKiAgaW4tbWVtb3J5IHBpeGVscykuIE9uZSBjaGVhcCBvdmVydmlldyB0aWxlIHBlciBjaGFubmVsOyBjYWNoZWQgYnkgY2hhbm5lbCBpbmRleC4gKi9cbiAgcHJpdmF0ZSBhc3luYyByZWZyZXNoSGlzdG9ncmFtU2FtcGxlcyh6OiBudW1iZXIsIGRlc2M6IFRpbGVEZXNjcmlwdG9yKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5oaXN0U2FtcGxlcy5jbGVhcigpO1xuICAgIGlmICh0aGlzLmltYWdlTW9kZSA9PT0gJ3JnYicpIHJldHVybjsgLy8gUkdCIHVzZXMgdGhlIGRpc3BsYXllZC1waXhlbCByZWFkYmFjayAocmdiSGlzdG9ncmFtKVxuICAgIGNvbnN0IGNoYW5uZWxDb3VudCA9IHRoaXMuaW1hZ2VNb2RlID09PSAnbXVsdGljaGFubmVsJyA/IGRlc2MuY2hhbm5lbHMgPz8gMSA6IDE7XG4gICAgZm9yIChsZXQgYyA9IDA7IGMgPCBjaGFubmVsQ291bnQ7IGMrKykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgY2ggPSB0aGlzLmltYWdlTW9kZSA9PT0gJ211bHRpY2hhbm5lbCcgPyBjIDogdW5kZWZpbmVkO1xuICAgICAgICBjb25zdCBkID0gYXdhaXQgdGhpcy5mZXRjaENoYW5uZWxEYXRhKHosIGNoLCAxKTsgLy8gYnVkZ2V0IDEg4oaSIGNvYXJzZXN0IHNpbmdsZSB0aWxlXG4gICAgICAgIHRoaXMuaGlzdFNhbXBsZXMuc2V0KGMsIGQuZGF0YSk7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLyogbGVhdmUgdGhpcyBjaGFubmVsJ3Mgc2FtcGxlIHVuc2V0ICovXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqIFN1YnNjcmliZSBjaGFubmVsIHN0YXRlcyArIGdyYXlzY2FsZSBjb2xvcm1hcCDihpIgbGl2ZS1hcHBseSB0byB0aGUgcmVuZGVyZWQgbGF5ZXJzIChub1xuICAgKiAgcmUtZmV0Y2g7IG9ubHkgeiBjaGFuZ2VzIHJlLWZldGNoKS4gUmVwbGFjZXMgYW55IHByaW9yIHN1YnNjcmlwdGlvbi4gKi9cbiAgcHJpdmF0ZSBzdWJzY3JpYmVEaXNwbGF5U3RhdGUoKTogdm9pZCB7XG4gICAgdGhpcy5kaXNwbGF5U3ViPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMuZGlzcGxheVN1YiA9IGNvbWJpbmVMYXRlc3QoW1xuICAgICAgdGhpcy5zdG9yZS5nZXRDaGFubmVsU3RhdGVzKCksXG4gICAgICB0aGlzLnN0b3JlLmdldENvbG9ybWFwKCksXG4gICAgICB0aGlzLnN0b3JlLmdldFJldmVyc2VTY2FsZSgpLFxuICAgICAgdGhpcy5zdG9yZS5nZXRJbnZlcnQoKSxcbiAgICBdKS5zdWJzY3JpYmUoKFtjaGFubmVscywgY29sb3JtYXAsIHJldmVyc2UsIGludmVydF0pID0+IHtcbiAgICAgIHRoaXMuY3VycmVudENvbG9ybWFwID0gKGNvbG9ybWFwIGFzIENvbG9ybWFwTm9kZSkgPz8gbnVsbDtcbiAgICAgIHRoaXMuY3VycmVudFJldmVyc2UgPSByZXZlcnNlO1xuICAgICAgdGhpcy5pbnZlcnRFbmFibGVkID0gaW52ZXJ0O1xuICAgICAgdGhpcy5hcHBseURpc3BsYXlTdGF0ZShjaGFubmVscyk7XG4gICAgfSk7XG4gIH1cblxuICAvKiogU3Vic2NyaWJlIHRoZSBzdG9yZSBjb2xvcm1hcCAoK3JldmVyc2UpLCBpbnZlcnQsIGFuZCBjaGFubmVsIHN0YXRlIOKGkiB0aGUgdm9sdW1lL2lzb3N1cmZhY2VcbiAgICogIHRyYW5zZmVyIGZ1bmN0aW9uOiBjb2xvdXIgKGNoYW5uZWwgdGludCBvciBzZWxlY3RlZCBjb2xvcm1hcCksIHRoZSBpbnRlbnNpdHkgKip3aW5kb3dcbiAgICogIChtaW4vbWF4IOKGkiBjb250cmFzdExpbWl0cykqKiBhbmQgKipnYW1tYSoqLCBtaXJyb3JpbmcgdGhlIGdyYXlzY2FsZSBpbWFnZSdzIGRpc3BsYXkgY29udHJvbHNcbiAgICogIHNvIHRoZSBoaXN0b2dyYW0gcGFuZSBkcml2ZXMgdGhlIDNEIHJlbmRlci4gUmVwbGFjZXMgYW55IHByaW9yIHN1YnNjcmlwdGlvbi4gKi9cbiAgcHJpdmF0ZSBzdWJzY3JpYmVWb2x1bWVEaXNwbGF5U3RhdGUoKTogdm9pZCB7XG4gICAgdGhpcy5kaXNwbGF5U3ViPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMuZGlzcGxheVN1YiA9IGNvbWJpbmVMYXRlc3QoW1xuICAgICAgdGhpcy5zdG9yZS5nZXRDb2xvcm1hcCgpLFxuICAgICAgdGhpcy5zdG9yZS5nZXRSZXZlcnNlU2NhbGUoKSxcbiAgICAgIHRoaXMuc3RvcmUuZ2V0SW52ZXJ0KCksXG4gICAgICB0aGlzLnN0b3JlLmdldENoYW5uZWxTdGF0ZXMoKSxcbiAgICBdKS5zdWJzY3JpYmUoKFtjb2xvcm1hcCwgcmV2ZXJzZSwgaW52ZXJ0LCBjaGFubmVsc10pID0+IHtcbiAgICAgIHRoaXMuY3VycmVudENvbG9ybWFwID0gKGNvbG9ybWFwIGFzIENvbG9ybWFwTm9kZSkgPz8gbnVsbDtcbiAgICAgIHRoaXMuY3VycmVudFJldmVyc2UgPSByZXZlcnNlO1xuICAgICAgdGhpcy5pbnZlcnRFbmFibGVkID0gaW52ZXJ0O1xuICAgICAgY29uc3QgdmlldyA9IHRoaXMudm9sdW1lVmlldztcbiAgICAgIGlmICghdmlldykgcmV0dXJuO1xuICAgICAgaWYgKHRoaXMudm9sdW1lTXVsdGljaGFubmVsKSB7XG4gICAgICAgIC8vIEVhY2ggY2hhbm5lbCdzIGxheWVyIGlzIHRpbnRlZCBieSBpdHMgY29sb3VyIGFuZCBnZXRzIGl0cyBvd24gd2luZG93L2dhbW1hL3Zpc2liaWxpdHkuXG4gICAgICAgIHZpZXcubGF5ZXJzLmZvckVhY2goKF8sIGMpID0+IHtcbiAgICAgICAgICBjb25zdCBzdCA9IGNoYW5uZWxzLmZpbmQoKHMpID0+IHMuaW5kZXggPT09IGMpO1xuICAgICAgICAgIHZpZXcudXBkYXRlQ2hhbm5lbChjLCB7XG4gICAgICAgICAgICBjb2xvcm1hcDogdGhpcy5jaGFubmVsVGludENvbG9ybWFwKHN0Py5jb2xvciA/PyAnI2ZmZmZmZicpLFxuICAgICAgICAgICAgLi4uKHN0XG4gICAgICAgICAgICAgID8ge1xuICAgICAgICAgICAgICAgICAgY29udHJhc3RMaW1pdHM6IFtzdC5taW4sIHN0Lm1heF0gYXMgW251bWJlciwgbnVtYmVyXSxcbiAgICAgICAgICAgICAgICAgIGdhbW1hOiBzdC5nYW1tYSxcbiAgICAgICAgICAgICAgICAgIHZpc2libGU6IHN0LnZpc2libGUsXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICA6IHt9KSxcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBzdCA9IGNoYW5uZWxzWzBdO1xuICAgICAgICB2aWV3LnVwZGF0ZUNoYW5uZWwoMCwge1xuICAgICAgICAgIGNvbG9ybWFwOiB0aGlzLnZvbHVtZUNvbG9ybWFwKHN0KSxcbiAgICAgICAgICAuLi4oc3QgPyB7IGNvbnRyYXN0TGltaXRzOiBbc3QubWluLCBzdC5tYXhdIGFzIFtudW1iZXIsIG51bWJlcl0sIGdhbW1hOiBzdC5nYW1tYSB9IDoge30pLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBBIGNoYW5uZWwncyB0aW50IGNvbG9ybWFwIChibGFja+KGkmNvbG91cikgd2l0aCByZXZlcnNlLXNjYWxlIC8gaW52ZXJ0IGFwcGxpZWQgYnkgZmxpcHBpbmcuICovXG4gIHByaXZhdGUgY2hhbm5lbFRpbnRDb2xvcm1hcChjb2xvcjogc3RyaW5nKTogQ29sb3JtYXAgfCBzdHJpbmcge1xuICAgIGxldCBjbWFwOiBDb2xvcm1hcCB8IHN0cmluZyA9IHRpbnRDb2xvcm1hcChjb2xvcik7XG4gICAgaWYgKHRoaXMuY3VycmVudFJldmVyc2UpIGNtYXAgPSByZXZlcnNlQ29sb3JtYXAoY21hcCk7XG4gICAgaWYgKHRoaXMuaW52ZXJ0RW5hYmxlZCkgY21hcCA9IHJldmVyc2VDb2xvcm1hcChjbWFwKTtcbiAgICByZXR1cm4gY21hcDtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb2xvdXIgbWFwIGZvciB0aGUgdm9sdW1lL2lzb3N1cmZhY2UuIEEgcmVhbCBjb2xvcm1hcCBzZWxlY3Rpb24gKHZpcmlkaXMvbWFnbWEv4oCmKSB3aW5zO1xuICAgKiBvdGhlcndpc2UgdGhlIGNoYW5uZWwncyBjb2xvdXIgdGludHMgaXQgKHNvIHRoZSBjaGFubmVsLWRpYWxvZyBjb2xvdXIgc3dhdGNoIHJlY29sb3JzIHRoZSAzRFxuICAgKiByZW5kZXIpLiBSZXZlcnNlLXNjYWxlIGFuZCBpbnZlcnQgZWFjaCBmbGlwIHRoZSByYW1wIOKAlCB0aGUgYFZvbHVtZUxheWVyYCBoYXMgbm8gcGVyLWxheWVyXG4gICAqIGludmVydCwgc28gYm90aCBhcmUgZW11bGF0ZWQgYnkgcmV2ZXJzaW5nIHRoZSBjb2xvcm1hcC5cbiAgICovXG4gIHByaXZhdGUgdm9sdW1lQ29sb3JtYXAoc3Q6IElDaGFubmVsU3RhdGUgfCB1bmRlZmluZWQpOiBDb2xvcm1hcCB8IHN0cmluZyB7XG4gICAgY29uc3Qgbm9kZSA9IHRoaXMuY3VycmVudENvbG9ybWFwIGFzIHsgbGFiZWw/OiBzdHJpbmc7IGRhdGE/OiB7IHZhbHVlPzogdW5rbm93biB9IH0gfCBudWxsO1xuICAgIC8vIEEgY29sb3JlZCBjb2xvcm1hcCAodmlyaWRpcy9tYWdtYS/igKYpIGRyaXZlcyB0aGUgdm9sdW1lOyB0aGUgZGVmYXVsdCBncmF5c2NhbGUgZmFtaWx5XG4gICAgLy8gKGdyYXkgLyBHcmV5cyAvIEdyZXlzIEludikgeWllbGRzIHRvIHRoZSBjaGFubmVsJ3MgY29sb3VyIHNvIHRoZSBkaWFsb2cgY29sb3VyIHN3YXRjaFxuICAgIC8vIHJlY29sb3JzIHRoZSAzRCByZW5kZXIuXG4gICAgY29uc3QgbGFiZWwgPSAobm9kZT8ubGFiZWwgPz8gJycpLnRvTG93ZXJDYXNlKCk7XG4gICAgY29uc3QgZ3JheUZhbWlseSA9IGxhYmVsID09PSAnJyB8fCBsYWJlbC5pbmNsdWRlcygnZ3JleScpIHx8IGxhYmVsLmluY2x1ZGVzKCdncmF5Jyk7XG4gICAgY29uc3QgdmFsdWUgPSBub2RlPy5kYXRhPy52YWx1ZTtcbiAgICBjb25zdCBsdXQgPSAhZ3JheUZhbWlseSAmJiB2YWx1ZSAhPSBudWxsID8gYnVpbGRDb2xvcm1hcEx1dCh2YWx1ZSwgZmFsc2UpIDogbnVsbDtcbiAgICBsZXQgY21hcDogQ29sb3JtYXAgfCBzdHJpbmcgPSBsdXRcbiAgICAgID8gY29sb3JtYXBGcm9tTHV0KCd2b2wtY21hcCcsIGx1dClcbiAgICAgIDogdGludENvbG9ybWFwKHN0Py5jb2xvciA/PyAnI2ZmZmZmZicpO1xuICAgIC8vIFJldmVyc2Utc2NhbGUgYW5kIGludmVydCBlYWNoIGZsaXAgdGhlIHJhbXAgKHRoZSBWb2x1bWVMYXllciBoYXMgbm8gcGVyLWxheWVyIGludmVydCkuXG4gICAgaWYgKHRoaXMuY3VycmVudFJldmVyc2UpIGNtYXAgPSByZXZlcnNlQ29sb3JtYXAoY21hcCk7XG4gICAgaWYgKHRoaXMuaW52ZXJ0RW5hYmxlZCkgY21hcCA9IHJldmVyc2VDb2xvcm1hcChjbWFwKTtcbiAgICByZXR1cm4gY21hcDtcbiAgfVxuXG4gIC8qKiBBcHBseSB0aGUgY3VycmVudCBjaGFubmVsIHN0YXRlcyAvIGNvbG9ybWFwIHRvIHRoZSBsaXZlIGxheWVycyAobm8gcmUtZmV0Y2gpLCBkZWxlZ2F0aW5nIHRoZVxuICAgKiAgcGVyLWNoYW5uZWwgbGF5ZXIgbXV0YXRpb25zIHRvIHRoZSB7QGxpbmsgTXVsdGlDaGFubmVsSW1hZ2VWaWV3fS4gKi9cbiAgcHJpdmF0ZSBhcHBseURpc3BsYXlTdGF0ZShjaGFubmVsczogSUNoYW5uZWxTdGF0ZVtdKTogdm9pZCB7XG4gICAgY29uc3QgdmlldyA9IHRoaXMuY2hhbm5lbFZpZXc7XG4gICAgaWYgKCF0aGlzLnZpZXdlciB8fCAhdmlldykgcmV0dXJuO1xuICAgIGlmICh0aGlzLmltYWdlTW9kZSA9PT0gJ211bHRpY2hhbm5lbCcpIHtcbiAgICAgIHZpZXcubGF5ZXJzLmZvckVhY2goKF8sIGMpID0+IHtcbiAgICAgICAgY29uc3Qgc3QgPSBjaGFubmVscy5maW5kKChzKSA9PiBzLmluZGV4ID09PSBjKTtcbiAgICAgICAgaWYgKCFzdCkgcmV0dXJuO1xuICAgICAgICB2aWV3LnVwZGF0ZUNoYW5uZWwoYywge1xuICAgICAgICAgIHRpbnQ6IHN0LmNvbG9yLFxuICAgICAgICAgIGNvbnRyYXN0TGltaXRzOiBbc3QubWluLCBzdC5tYXhdLFxuICAgICAgICAgIGdhbW1hOiBzdC5nYW1tYSxcbiAgICAgICAgICB2aXNpYmxlOiBzdC52aXNpYmxlLFxuICAgICAgICAgIGludmVydDogdGhpcy5pbnZlcnRFbmFibGVkLFxuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH0gZWxzZSBpZiAodGhpcy5pbWFnZU1vZGUgPT09ICdncmF5c2NhbGUnKSB7XG4gICAgICBjb25zdCBzdCA9IGNoYW5uZWxzWzBdO1xuICAgICAgdmlldy51cGRhdGVDaGFubmVsKDAsIHtcbiAgICAgICAgY29sb3JtYXA6IHRoaXMuZ3JheXNjYWxlQ29sb3JtYXAoKSxcbiAgICAgICAgaW52ZXJ0OiB0aGlzLmludmVydEVuYWJsZWQsXG4gICAgICAgIC4uLihzdCA/IHsgY29udHJhc3RMaW1pdHM6IFtzdC5taW4sIHN0Lm1heF0gYXMgW251bWJlciwgbnVtYmVyXSwgZ2FtbWE6IHN0LmdhbW1hIH0gOiB7fSksXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICAvKiogKFJlKWluc3RhbGwgdGhlIHBoeXNpY2FsIHNjYWxlIGJhciBvdmVyIHRoZSAyRCBpbWFnZSwgc2l6ZWQgZnJvbSB0aGUgaW1hZ2UncyDCtW0vcGl4ZWxcbiAgICogIChgL3RpbGVzL2luZm9gIG1wcFgsIGZhbGxpbmcgYmFjayB0byB0aGUgaW1hZ2UgbWV0YWRhdGEpLiBOby1vcCB3aXRob3V0IGEgcGh5c2ljYWwgc2l6ZS4gKi9cbiAgcHJpdmF0ZSBpbnN0YWxsU2NhbGVCYXIoKTogdm9pZCB7XG4gICAgdGhpcy5zY2FsZUJhcj8uZGVzdHJveSgpO1xuICAgIHRoaXMuc2NhbGVCYXIgPSBudWxsO1xuICAgIGNvbnN0IG1wcFggPSB0aGlzLmRlc2NyaXB0b3I/Lm1wcFggfHwgdGhpcy5sb2FkZWQ/LmltYWdlSW5mby5pbWFnZU1ldGE/LlswXT8ubXBwWCB8fCAwO1xuICAgIGlmICh0aGlzLnZpZXdlciAmJiB0aGlzLmhvc3QgJiYgbXBwWCA+IDApIHtcbiAgICAgIHRoaXMuc2NhbGVCYXIgPSBuZXcgTmFwYXJpU2NhbGVCYXIodGhpcy5ob3N0LCB0aGlzLnZpZXdlci5jYW1lcmEsIG1wcFgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBDb252ZXJ0IGEgbmFwYXJpLWpzIGBIaXN0b2dyYW1gIChiaW4gY291bnQgKyBtaW4vbWF4KSB0byB0aGUgcGFuZSdzIGBJSGlzdG9ncmFtYCAoYmluIGVkZ2VzKS4gKi9cbiAgcHJpdmF0ZSB0b0lIaXN0b2dyYW0oaDogeyBjb3VudHM6IFVpbnQzMkFycmF5OyBiaW5zOiBudW1iZXI7IG1pbjogbnVtYmVyOyBtYXg6IG51bWJlciB9KTogSUhpc3RvZ3JhbSB7XG4gICAgY29uc3Qgc3BhbiA9IGgubWF4IC0gaC5taW4gfHwgMTtcbiAgICBjb25zdCBiaW5zID0gQXJyYXkuZnJvbSh7IGxlbmd0aDogaC5iaW5zIH0sIChfLCBpKSA9PiBoLm1pbiArIChpICogc3BhbikgLyBoLmJpbnMpO1xuICAgIGNvbnN0IGNvdW50cyA9IEFycmF5LmZyb20oaC5jb3VudHMpO1xuICAgIHJldHVybiB7IGJpbnMsIGNvdW50cywgbWF4OiBjb3VudHMucmVkdWNlKChtLCBjKSA9PiAoYyA+IG0gPyBjIDogbSksIDApIH07XG4gIH1cblxuICAvKipcbiAgICogTW91bnQgdGhlIDNEIHZvbHVtZS9pc29zdXJmYWNlLiBBIG11bHRpY2hhbm5lbCBpbWFnZSBiZWNvbWVzIG9uZSBhZGRpdGl2ZSwgdGludGVkXG4gICAqIHtAbGluayBWb2x1bWVMYXllcn0gcGVyIGNoYW5uZWwgKHNvIGVhY2ggY2hhbm5lbCdzIGNvbG91ciBjb21wb3NpdGVzIGludG8gdGhlIHJlbmRlcik7IGFcbiAgICogZ3JheXNjYWxlL2NvbXBvc2l0ZSBpbWFnZSB1c2VzIGEgc2luZ2xlIHZvbHVtZS4gQWRkcyB0aGUgYXhlcyBnaXptbyArIGxhYmVscyBhbmQgd2lyZXMgdGhlXG4gICAqIGRpc3BsYXktc3RhdGUgc3Vic2NyaXB0aW9uLiBUaGUgY2FsbGVyIG93bnMgYHZpZXdlci5yZWFkeWAuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIG1vdW50Vm9sdW1lKFxuICAgIHZpZXdlcjogVmlld2VyLFxuICAgIGluZm86IElJbWFnZUluZm8gfCB1bmRlZmluZWQsXG4gICAgcGxvdFR5cGU6IFBsb3RUeXBlLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB0b2tlbiA9IHRoaXMubG9hZFRva2VuOyAvLyBiYWlsIGJlZm9yZSByZW5kZXJpbmcgaWYgYSBDYW5jZWwgLyBuZXcgcGxvdCBidW1wcyB0aGlzXG4gICAgY29uc3QgZGVzYyA9IGF3YWl0IHRoaXMuZW5zdXJlRGVzY3JpcHRvcigpO1xuICAgIGNvbnN0IGNoYW5uZWxDb3VudCA9IGRlc2M/LmNoYW5uZWxzID8/IDE7XG4gICAgY29uc3QgbXVsdGljaGFubmVsID0gISFkZXNjPy5tdWx0aWNoYW5uZWwgJiYgY2hhbm5lbENvdW50ID4gMTtcbiAgICBjb25zdCByZXMgPSB2b2x1bWVSZXNvbHV0aW9uRm9yKHRoaXMucmVzb2x1dGlvblNjYWxlKTtcbiAgICBjb25zdCByZW5kZXJpbmc6ICdpc28nIHwgJ21pcCcgPSBpc05hcGFyaUlzb3N1cmZhY2UocGxvdFR5cGUpID8gJ2lzbycgOiAnbWlwJztcbiAgICBjb25zdCBzdGF0ZXMgPSB0aGlzLnN0b3JlLmN1cnJlbnRDaGFubmVsU3RhdGVzKCk7XG5cbiAgICB0aGlzLnZvbHVtZUNoYW5uZWxEYXRhLmNsZWFyKCk7XG4gICAgdGhpcy52b2x1bWVNdWx0aWNoYW5uZWwgPSBtdWx0aWNoYW5uZWw7XG4gICAgdGhpcy5pbWFnZU1vZGUgPSBtdWx0aWNoYW5uZWwgPyAnbXVsdGljaGFubmVsJyA6ICdncmF5c2NhbGUnO1xuICAgIGNvbnN0IHZpZXcgPSBuZXcgTXVsdGlDaGFubmVsVm9sdW1lVmlldyh2aWV3ZXIpO1xuICAgIHRoaXMudm9sdW1lVmlldyA9IHZpZXc7XG5cbiAgICAvLyBBc3NlbWJsZSBwZXItY2hhbm5lbCBzY2FsYXIgdm9sdW1lcyBmcm9tIHRoZSBzZXJ2ZXIgdGlsZXMgKGppdC1zcGVjaWZpYyk7IHRoZSBuYXBhcmktanMgdmlld1xuICAgIC8vIG93bnMgdGhlIGxheWVyIG9yY2hlc3RyYXRpb24gKG9uZSBhZGRpdGl2ZSB0aW50ZWQgdm9sdW1lIHBlciBjaGFubmVsLCBvciBhIHNpbmdsZSBncmF5c2NhbGVcbiAgICAvLyB2b2x1bWUpLiBUaGUgYWRhcHRlciBjb21wdXRlcyBlYWNoIGNoYW5uZWwncyBjb2xvcm1hcCAoaW5jbC4gaW52ZXJ0L3JldmVyc2UgZmxpcHMpLlxuICAgIGxldCBkaW1zOiB7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyOyBkZXB0aDogbnVtYmVyIH0gfCBudWxsID0gbnVsbDtcbiAgICBjb25zdCBjaGFubmVsczogVm9sdW1lQ2hhbm5lbFtdID0gW107XG4gICAgdGhpcy5zdGFja0xvYWRpbmckLm5leHQodHJ1ZSk7XG4gICAgdGhpcy5zdGFja0xvYWRpbmdQcm9ncmVzcyQubmV4dCgwKTtcbiAgICB0cnkge1xuICAgICAgaWYgKG11bHRpY2hhbm5lbCkge1xuICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IGNoYW5uZWxDb3VudDsgYysrKSB7XG4gICAgICAgICAgY29uc3Qgdm9sID0gYXdhaXQgdGhpcy5hc3NlbWJsZVZvbHVtZShpbmZvLCByZXMsIGMpO1xuICAgICAgICAgIGlmICghdm9sKSBjb250aW51ZTtcbiAgICAgICAgICBkaW1zID0gdm9sO1xuICAgICAgICAgIHRoaXMudm9sdW1lQ2hhbm5lbERhdGEuc2V0KGMsIHZvbC5kYXRhKTtcbiAgICAgICAgICBjb25zdCBzdCA9IHN0YXRlcy5maW5kKChzKSA9PiBzLmluZGV4ID09PSBjKTtcbiAgICAgICAgICBjb25zdCBjb2xvciA9IHN0Py5jb2xvciA/PyBkZXNjPy5jaGFubmVsSW5mbz8uW2NdPy5jb2xvciA/PyB0aW50Rm9yKGMpO1xuICAgICAgICAgIGNoYW5uZWxzLnB1c2goe1xuICAgICAgICAgICAgZGF0YTogdm9sLmRhdGEsXG4gICAgICAgICAgICB3aWR0aDogdm9sLndpZHRoLFxuICAgICAgICAgICAgaGVpZ2h0OiB2b2wuaGVpZ2h0LFxuICAgICAgICAgICAgZGVwdGg6IHZvbC5kZXB0aCxcbiAgICAgICAgICAgIGNvbG9ybWFwOiB0aGlzLmNoYW5uZWxUaW50Q29sb3JtYXAoY29sb3IpLFxuICAgICAgICAgICAgY29udHJhc3RMaW1pdHM6IFtzdD8ubWluID8/IDAsIHN0Py5tYXggPz8gMjU1XSxcbiAgICAgICAgICAgIGdhbW1hOiBzdD8uZ2FtbWEgPz8gMSxcbiAgICAgICAgICAgIHZpc2libGU6IHN0Py52aXNpYmxlID8/IHRydWUsXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IHZvbCA9IGF3YWl0IHRoaXMuYXNzZW1ibGVWb2x1bWUoaW5mbywgcmVzKTtcbiAgICAgICAgaWYgKHZvbCkge1xuICAgICAgICAgIGRpbXMgPSB2b2w7XG4gICAgICAgICAgdGhpcy52b2x1bWVDaGFubmVsRGF0YS5zZXQoMCwgdm9sLmRhdGEpO1xuICAgICAgICAgIGNvbnN0IHN0ID0gc3RhdGVzWzBdO1xuICAgICAgICAgIGNoYW5uZWxzLnB1c2goe1xuICAgICAgICAgICAgZGF0YTogdm9sLmRhdGEsXG4gICAgICAgICAgICB3aWR0aDogdm9sLndpZHRoLFxuICAgICAgICAgICAgaGVpZ2h0OiB2b2wuaGVpZ2h0LFxuICAgICAgICAgICAgZGVwdGg6IHZvbC5kZXB0aCxcbiAgICAgICAgICAgIGNvbG9ybWFwOiB0aGlzLnZvbHVtZUNvbG9ybWFwKHN0KSxcbiAgICAgICAgICAgIGNvbnRyYXN0TGltaXRzOiBbc3Q/Lm1pbiA/PyAwLCBzdD8ubWF4ID8/IDI1NV0sXG4gICAgICAgICAgICBnYW1tYTogc3Q/LmdhbW1hID8/IDEsXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGZpbmFsbHkge1xuICAgICAgdGhpcy5zdGFja0xvYWRpbmckLm5leHQoZmFsc2UpO1xuICAgICAgdGhpcy5zdGFja0xvYWRpbmdQcm9ncmVzcyQubmV4dCgwKTtcbiAgICB9XG5cbiAgICBpZiAoIWRpbXMgfHwgIWNoYW5uZWxzLmxlbmd0aCB8fCB0aGlzLmxvYWRUb2tlbiAhPT0gdG9rZW4pIHJldHVybjsgLy8gY2FuY2VsbGVkIOKGkiBkb24ndCByZW5kZXJcblxuICAgIC8vIFJlc29sdXRpb24taW52YXJpYW50IHdvcmxkIGJveC4gU2l6aW5nIHRoZSBib3ggYnkgdGhlIHNhbXBsZWQgdm94ZWwgY291bnRzIG1hZGUgaGlnaGVyXG4gICAgLy8gaW4tcGxhbmUgcmVzb2x1dGlvbiBncm93IFgvWSB3aGlsZSB0aGUgZGVwdGggc3RheWVkIHRoZSAoY29uc3RhbnQpIHNsaWNlIGNvdW50IOKAlCBzbyBaIGFwcGVhcmVkXG4gICAgLy8gdG8gc2hyaW5rIGF0IGhpZ2hlciByZXNvbHV0aW9uLiBJbnN0ZWFkIGFuY2hvciB0aGUgaW4tcGxhbmUgbG9uZyBzaWRlIHRvIGEgZml4ZWQgcmVmZXJlbmNlIGFuZFxuICAgIC8vIGxldCBaIHNwYW4gdGhlIGZ1bGwgc2xpY2UgY291bnQ7IHRoZSBib3ggc2hhcGUgaXMgdGhlbiBpZGVudGljYWwgYXQgZXZlcnkgZGVjaW1hdGUgZmFjdG9yLiBUaGVcbiAgICAvLyBwZXItYXhpcyBgdm94ZWxTaXplYCAobmFwYXJpIGBzY2FsZWApIG1hcHMgdGhlIHNhbXBsZWQgZ3JpZCBvbnRvIHRoYXQgZml4ZWQgd29ybGQgYm94LlxuICAgIGNvbnN0IGZ1bGxXID0gdGhpcy5kZXNjcmlwdG9yPy53aWR0aCA/PyBkaW1zLndpZHRoO1xuICAgIGNvbnN0IGZ1bGxIID0gdGhpcy5kZXNjcmlwdG9yPy5oZWlnaHQgPz8gZGltcy5oZWlnaHQ7XG4gICAgY29uc3QgZnVsbEQgPVxuICAgICAgdGhpcy5sb2FkZWQ/LmltYWdlSW5mby5pbWFnZU1ldGE/LlswXT8ueiB8fCB0aGlzLmxvYWRlZD8uaW1hZ2VJbmZvLnVybHM/Lmxlbmd0aCB8fCBkaW1zLmRlcHRoO1xuICAgIGNvbnN0IGZ1bGxMb25nID0gTWF0aC5tYXgoMSwgZnVsbFcsIGZ1bGxIKTtcbiAgICBjb25zdCB3b3JsZCA9IHtcbiAgICAgIHdpZHRoOiAoZnVsbFcgKiBWT0xVTUVfV09STERfSU5QTEFORV9SRUYpIC8gZnVsbExvbmcsXG4gICAgICBoZWlnaHQ6IChmdWxsSCAqIFZPTFVNRV9XT1JMRF9JTlBMQU5FX1JFRikgLyBmdWxsTG9uZyxcbiAgICAgIGRlcHRoOiBmdWxsRCxcbiAgICB9O1xuICAgIC8vIEJhc2UgYm94IChaLXNjYWxlID0gMSkgKyB0aGUgc2FtcGxlZCBkZXB0aCBkcml2ZSB0aGUgbGl2ZSBaLWhlaWdodCBoYW5kbGUgYmVsb3c7IHRoZSBwZXJzaXN0ZWRcbiAgICAvLyBgdm9sdW1lWlNjYWxlYCAodXNlciBkcmFnKSBhcHBsaWVzIG9uIHRvcCBzbyBjaGFuZ2luZyByZXNvbHV0aW9uIGtlZXBzIHRoZSBjaG9zZW4gaGVpZ2h0LlxuICAgIHRoaXMudm9sdW1lV29ybGRCYXNlID0gd29ybGQ7XG4gICAgdGhpcy52b2x1bWVTYW1wbGVkRGVwdGggPSBNYXRoLm1heCgxLCBkaW1zLmRlcHRoKTtcbiAgICBjb25zdCB3b3JsZFogPSB3b3JsZC5kZXB0aCAqIHRoaXMudm9sdW1lWlNjYWxlO1xuICAgIGNvbnN0IHZveGVsU2l6ZTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdID0gW1xuICAgICAgd29ybGQud2lkdGggLyBkaW1zLndpZHRoLFxuICAgICAgd29ybGQuaGVpZ2h0IC8gZGltcy5oZWlnaHQsXG4gICAgICB3b3JsZFogLyB0aGlzLnZvbHVtZVNhbXBsZWREZXB0aCxcbiAgICBdO1xuICAgIGZvciAoY29uc3QgY2ggb2YgY2hhbm5lbHMpIGNoLnZveGVsU2l6ZSA9IHZveGVsU2l6ZTtcblxuICAgIHZpZXcucmVuZGVyKG11bHRpY2hhbm5lbCA/ICdtdWx0aWNoYW5uZWwnIDogJ2dyYXlzY2FsZScsIGNoYW5uZWxzLCB7IHJlbmRlcmluZyB9KTtcbiAgICB0aGlzLmltYWdlVyA9IGRpbXMud2lkdGg7XG4gICAgdGhpcy5pbWFnZUggPSBkaW1zLmhlaWdodDtcbiAgICB0aGlzLnZvbHVtZURpbXMgPSBkaW1zO1xuXG4gICAgLy8gM0QgY29vcmRpbmF0ZS1heGVzIC8gc2NhbGUgZ2l6bW8gKyBsYWJlbHMsIHNoYXJpbmcgdGhlIHZvbHVtZSdzIHdvcmxkIGJveCBzbyB0aGUgZ2l6bW8gdHJhY2tzXG4gICAgLy8gdGhlIHJlbmRlcmVkIHByb3BvcnRpb25zLiBQaHlzaWNhbCBzY2FsZSB0ZXh0IHN0aWxsIGNvbWVzIGZyb20gdGhlIEZVTEwgaW1hZ2UgZXh0ZW50LlxuICAgIGNvbnN0IG1wcFggPSB0aGlzLmRlc2NyaXB0b3I/Lm1wcFggfHwgdGhpcy5sb2FkZWQ/LmltYWdlSW5mby5pbWFnZU1ldGE/LlswXT8ubXBwWCB8fCAwO1xuICAgIHRoaXMuYXhlc0xheWVyID0gdmlld2VyLmFkZEF4ZXMod29ybGQud2lkdGgsIHdvcmxkLmhlaWdodCwgd29ybGRaLCB7IHZpc2libGU6IHRoaXMuYXhlc1Zpc2libGUgfSk7XG4gICAgaWYgKHRoaXMuaG9zdCkge1xuICAgICAgdGhpcy5heGVzTGFiZWxzID0gbmV3IE5hcGFyaUF4ZXNMYWJlbHMoXG4gICAgICAgIHRoaXMuaG9zdCxcbiAgICAgICAgdmlld2VyLmNhbWVyYTNkLFxuICAgICAgICB0aGlzLmJ1aWxkQXhlc0xhYmVscyh7IHdpZHRoOiB3b3JsZC53aWR0aCwgaGVpZ2h0OiB3b3JsZC5oZWlnaHQsIGRlcHRoOiB3b3JsZFogfSwgbXBwWCksXG4gICAgICApO1xuICAgICAgdGhpcy5heGVzTGFiZWxzLnNldFZpc2libGUodGhpcy5heGVzVmlzaWJsZSk7XG4gICAgICAvLyBJbi12aWV3IGRyYWcgaGFuZGxlIGF0IHRoZSBUT1AgRU5EIE9GIFRIRSBaIEFYSVMgKHRoZSBib3gncyBtaW4tWFkgY29ybmVyLCB3aGVyZSB0aGUgYmx1ZVxuICAgICAgLy8gXCJaXCIgYXhpcyArIGxhYmVsIGxpdmUpLCBzbyBpdCByZWFkcyBhcyB0aGUgWi1oZWlnaHQgY29udHJvbC4gRmxvYXRlZCBhIGxpdHRsZSBwYXN0IHRoZSBheGlzXG4gICAgICAvLyB0aXAgKMOXMS4xMikgc28gdGhlIGdyaXAgY2xlYXJzIHRoZSBcIlogwrcg4oCmXCIgbGFiZWwuIERyYWcg4oaVIHRvIHJlc3RyZXRjaCBaIGxpdmUuXG4gICAgICB0aGlzLnpIYW5kbGUgPSBuZXcgTmFwYXJpVm9sdW1lWkhhbmRsZSh0aGlzLmhvc3QsIHZpZXdlci5jYW1lcmEzZCwge1xuICAgICAgICB0b3BBbmNob3I6ICgpID0+IFtcbiAgICAgICAgICAtdGhpcy52b2x1bWVXb3JsZEJhc2UhLndpZHRoIC8gMixcbiAgICAgICAgICAtdGhpcy52b2x1bWVXb3JsZEJhc2UhLmhlaWdodCAvIDIsXG4gICAgICAgICAgKCh0aGlzLnZvbHVtZVdvcmxkQmFzZSEuZGVwdGggKiB0aGlzLnZvbHVtZVpTY2FsZSkgLyAyKSAqIDEuMTIsXG4gICAgICAgIF0sXG4gICAgICAgIGdldFNjYWxlOiAoKSA9PiB0aGlzLnZvbHVtZVpTY2FsZSxcbiAgICAgICAgc2V0U2NhbGU6IChzKSA9PiB0aGlzLnNldFZvbHVtZVpTY2FsZShzKSxcbiAgICAgIH0pO1xuICAgIH1cbiAgICB0aGlzLnN1YnNjcmliZVZvbHVtZURpc3BsYXlTdGF0ZSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc3RyZXRjaCB0aGUgdm9sdW1lJ3MgWiBoZWlnaHQgbGl2ZSAoZHJpdmVuIGJ5IHRoZSBpbi12aWV3IHtAbGluayBOYXBhcmlWb2x1bWVaSGFuZGxlfSkuIE9ubHlcbiAgICogdGhlIHBlci1heGlzIGB2b3hlbFNpemVgIC8gYXhlcyBkZXB0aCBjaGFuZ2Ug4oCUIHRoZSB2b3hlbCB0ZXh0dXJlcyBhcmUgdW50b3VjaGVkIOKAlCBzbyBkcmFnZ2luZyBpc1xuICAgKiBzbW9vdGguIGBmYWN0b3JgIGlzIHJlbGF0aXZlIHRvIHRoZSB2b2x1bWUncyBuYXR1cmFsIHByb3BvcnRpb25zICgxKS4gUGVyc2lzdHMgYWNyb3NzIHJlLW1vdW50c1xuICAgKiAocmVzb2x1dGlvbiBjaGFuZ2VzKSBzbyB0aGUgY2hvc2VuIGhlaWdodCBzdGlja3MuXG4gICAqL1xuICBwcml2YXRlIHNldFZvbHVtZVpTY2FsZShmYWN0b3I6IG51bWJlcik6IHZvaWQge1xuICAgIHRoaXMudm9sdW1lWlNjYWxlID0gTWF0aC5taW4oMTAsIE1hdGgubWF4KDAuMSwgZmFjdG9yKSk7XG4gICAgY29uc3QgYmFzZSA9IHRoaXMudm9sdW1lV29ybGRCYXNlO1xuICAgIGlmICghYmFzZSB8fCAhdGhpcy52b2x1bWVWaWV3KSByZXR1cm47XG4gICAgY29uc3Qgd29ybGRaID0gYmFzZS5kZXB0aCAqIHRoaXMudm9sdW1lWlNjYWxlO1xuICAgIGNvbnN0IHZzWiA9IHdvcmxkWiAvIHRoaXMudm9sdW1lU2FtcGxlZERlcHRoO1xuICAgIGZvciAoY29uc3QgbGF5ZXIgb2YgdGhpcy52b2x1bWVWaWV3LmxheWVycykge1xuICAgICAgY29uc3QgW3N4LCBzeV0gPSBsYXllci52b3hlbFNpemU7XG4gICAgICBsYXllci52b3hlbFNpemUgPSBbc3gsIHN5LCB2c1pdO1xuICAgIH1cbiAgICBpZiAodGhpcy5heGVzTGF5ZXIpIHRoaXMuYXhlc0xheWVyLmRlcHRoID0gd29ybGRaO1xuICAgIGNvbnN0IG1wcFggPSB0aGlzLmRlc2NyaXB0b3I/Lm1wcFggfHwgdGhpcy5sb2FkZWQ/LmltYWdlSW5mby5pbWFnZU1ldGE/LlswXT8ubXBwWCB8fCAwO1xuICAgIHRoaXMuYXhlc0xhYmVscz8udXBkYXRlQW5jaG9ycyhcbiAgICAgIHRoaXMuYnVpbGRBeGVzTGFiZWxzKHsgd2lkdGg6IGJhc2Uud2lkdGgsIGhlaWdodDogYmFzZS5oZWlnaHQsIGRlcHRoOiB3b3JsZFogfSwgbXBwWCksXG4gICAgKTtcbiAgICB0aGlzLnpIYW5kbGU/LnJlcG9zaXRpb24oKTtcbiAgICB0aGlzLnZpZXdlcj8ucmVxdWVzdFJlbmRlcigpO1xuICB9XG5cbiAgLyoqIEJ1aWxkIHRoZSBYL1kvWiBheGlzLWVuZCBsYWJlbCBzcGVjcyBmb3IgdGhlIDNEIGdpem1vLiBBbmNob3JzIGFyZSBpbiB0aGUgdm9sdW1lJ3MgY2VudHJlZFxuICAgKiAgd29ybGQgYm94IChtYXRjaGluZyB0aGUgQXhlc0xheWVyIGdlb21ldHJ5KTsgdGhlIHNjYWxlIHRleHQgcmVmbGVjdHMgdGhlIEZVTEwgaW1hZ2UgZXh0ZW50IOKAlFxuICAgKiAgcGh5c2ljYWwgwrVtIHdoZW4gwrVtL3BpeGVsIGlzIGtub3duLCBlbHNlIHBpeGVsIChYL1kpIC8gc2xpY2UgKFopIGNvdW50cy4gKi9cbiAgcHJpdmF0ZSBidWlsZEF4ZXNMYWJlbHMoXG4gICAgdm9sOiB7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyOyBkZXB0aDogbnVtYmVyIH0sXG4gICAgbXBwWDogbnVtYmVyLFxuICApOiBBeGlzTGFiZWxTcGVjW10ge1xuICAgIGNvbnN0IGh4ID0gdm9sLndpZHRoIC8gMjtcbiAgICBjb25zdCBoeSA9IHZvbC5oZWlnaHQgLyAyO1xuICAgIGNvbnN0IGh6ID0gdm9sLmRlcHRoIC8gMjtcbiAgICBjb25zdCBkZXNjVyA9IHRoaXMuZGVzY3JpcHRvcj8ud2lkdGggPz8gdm9sLndpZHRoO1xuICAgIGNvbnN0IGRlc2NIID0gdGhpcy5kZXNjcmlwdG9yPy5oZWlnaHQgPz8gdm9sLmhlaWdodDtcbiAgICBjb25zdCBzbGljZXMgPVxuICAgICAgdGhpcy5sb2FkZWQ/LmltYWdlSW5mby5pbWFnZU1ldGE/LlswXT8ueiB8fCB0aGlzLmxvYWRlZD8uaW1hZ2VJbmZvLnVybHM/Lmxlbmd0aCB8fCB2b2wuZGVwdGg7XG4gICAgY29uc3QgbGVuID0gKHB4OiBudW1iZXIpOiBzdHJpbmcgPT4gKG1wcFggPiAwID8gZm9ybWF0VW0ocHggKiBtcHBYKSA6IGAke3B4fSBweGApO1xuICAgIHJldHVybiBbXG4gICAgICB7IGFuY2hvcjogW2h4LCAtaHksIC1oel0sIHRleHQ6IGBYIMK3ICR7bGVuKGRlc2NXKX1gLCBjb2xvcjogJyNlZDQ1NDUnIH0sXG4gICAgICB7IGFuY2hvcjogWy1oeCwgaHksIC1oel0sIHRleHQ6IGBZIMK3ICR7bGVuKGRlc2NIKX1gLCBjb2xvcjogJyM0ZGQ5NTknIH0sXG4gICAgICB7IGFuY2hvcjogWy1oeCwgLWh5LCBoel0sIHRleHQ6IGBaIMK3ICR7c2xpY2VzfSBweGAsIGNvbG9yOiAnIzY2OGNmZicgfSxcbiAgICBdO1xuICB9XG5cbiAgLyoqXG4gICAqIEFzc2VtYmxlIGEgZG93bnNhbXBsZWQgdWludDggdm9sdW1lIChsdW1pbmFuY2UpIGZyb20gdGhlIHBlci1zbGljZSB0aWxlIGVuZHBvaW50LiBTbGljZXMgYXJlXG4gICAqIGZldGNoZWQgd2l0aCBib3VuZGVkIGNvbmN1cnJlbmN5IChrZWVwcyB0aGUgY29ubmVjdGlvbiBwb29sIGZ1bGwgd2l0aG91dCBmbG9vZGluZyBpdCBvbiBhIGRlZXBcbiAgICogc3RhY2spIGFuZCByZWFkIGludG8gdGhlIHZvbHVtZSBhcyBlYWNoIGFycml2ZXMsIGRyaXZpbmcge0BsaW5rIHN0YWNrTG9hZGluZ1Byb2dyZXNzJH0gc28gdGhlXG4gICAqIGhvc3Qgc2hvd3MgYSBkZXRlcm1pbmF0ZSBwcm9ncmVzcyBiYXIgaW5zdGVhZCBvZiBhIGJhcmUgc3Bpbm5lci5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgYXNzZW1ibGVWb2x1bWUoXG4gICAgaW5mbzogSUltYWdlSW5mbyB8IHVuZGVmaW5lZCxcbiAgICBvcHRzOiB7IG1heFNsaWNlPzogbnVtYmVyOyBzbGljZVN0ZXA/OiBudW1iZXIgfSA9IHt9LFxuICAgIGNoYW5uZWw/OiBudW1iZXIsXG4gICk6IFByb21pc2U8eyBkYXRhOiBVaW50OEFycmF5OyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlcjsgZGVwdGg6IG51bWJlciB9IHwgbnVsbD4ge1xuICAgIGNvbnN0IHRva2VuID0gdGhpcy5sb2FkVG9rZW47IC8vIGJhaWwgaWYgYSBDYW5jZWwgLyBuZXcgcGxvdCBidW1wcyB0aGlzIHdoaWxlIHdlIGZldGNoXG4gICAgY29uc3QgZnVsbERlcHRoID0gaW5mbz8uaW1hZ2VNZXRhPy5bMF0/LnogfHwgaW5mbz8udXJscz8ubGVuZ3RoIHx8IDE7XG4gICAgaWYgKGZ1bGxEZXB0aCA8IDEpIHtcbiAgICAgIGNvbnNvbGUud2FybignW25hcGFyaS1qc10gbm8gc2xpY2VzIHRvIGFzc2VtYmxlIGEgdm9sdW1lJyk7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgY29uc3Qgc3RlcCA9IE1hdGgubWF4KDEsIE1hdGguZmxvb3Iob3B0cy5zbGljZVN0ZXAgPz8gMSkpO1xuICAgIGNvbnN0IG1heFNsaWNlID0gb3B0cy5tYXhTbGljZSA/PyBWT0xVTUVfTUFYX1NMSUNFO1xuICAgIC8vIFNvdXJjZS1zbGljZSBpbmRpY2VzIHNhbXBsZWQgaW50byB0aGUgdm9sdW1lIChldmVyeSBgc3RlcGB0aCBwbGFuZSDihpIgbG93LXJlcyBpcyBmYXN0ZXIpLlxuICAgIGNvbnN0IHpJbmRpY2VzOiBudW1iZXJbXSA9IFtdO1xuICAgIGZvciAobGV0IHogPSAwOyB6IDwgZnVsbERlcHRoOyB6ICs9IHN0ZXApIHpJbmRpY2VzLnB1c2goeik7XG4gICAgY29uc3QgZGVwdGggPSB6SW5kaWNlcy5sZW5ndGg7XG5cbiAgICB0aGlzLnN0YWNrTG9hZGluZ1Byb2dyZXNzJC5uZXh0KDApO1xuICAgIHRyeSB7XG4gICAgICAvLyBGZXRjaCBlYWNoIHNsaWNlIGF0IGEgcHlyYW1pZCBsZXZlbCBtYXRjaGluZyBgbWF4U2xpY2VgIChidWRnZXQgc2NhbGVzIHdpdGggdGhlIGRlY2ltYXRlXG4gICAgICAvLyBmYWN0b3IpLCBzbyBhIGhpZ2hlciBmYWN0b3IgcHVsbHMgYSBmaW5lciBsZXZlbCDihpIgbW9yZSByZWFsIGluLXBsYW5lIGRldGFpbDsgdGhlbiBkb3duc2FtcGxlXG4gICAgICAvLyB0byBgbWF4U2xpY2VgLiBgY2hhbm5lbGAgc2VsZWN0cyBhIGJhbmQgKG11bHRpY2hhbm5lbCB2b2x1bWUpOyBvbWl0IGZvciB0aGUgZ3JheXNjYWxlXG4gICAgICAvLyBjb21wb3NpdGUuIFRoZSBjYWxsZXIgb3ducyB0aGUgc3RhY2tMb2FkaW5nIGZsYWcgKG11bHRpY2hhbm5lbCBhc3NlbWJsZXMgY2hhbm5lbHMgaW4gdHVybikuXG4gICAgICBjb25zdCBidWRnZXQgPSB0aGlzLnRpbGVCdWRnZXRGb3IobWF4U2xpY2UpO1xuICAgICAgY29uc3QgZmlyc3QgPSBhd2FpdCB0aGlzLmZldGNoU2xpY2UoekluZGljZXNbMF0sIGNoYW5uZWwsIGJ1ZGdldCk7XG4gICAgICBjb25zdCBzY2FsZSA9IE1hdGgubWluKDEsIG1heFNsaWNlIC8gTWF0aC5tYXgoZmlyc3Qud2lkdGgsIGZpcnN0LmhlaWdodCwgMSkpO1xuICAgICAgY29uc3Qgd2lkdGggPSBNYXRoLm1heCgxLCBNYXRoLnJvdW5kKGZpcnN0LndpZHRoICogc2NhbGUpKTtcbiAgICAgIGNvbnN0IGhlaWdodCA9IE1hdGgubWF4KDEsIE1hdGgucm91bmQoZmlyc3QuaGVpZ2h0ICogc2NhbGUpKTtcbiAgICAgIGNvbnN0IGRhdGEgPSBuZXcgVWludDhBcnJheSh3aWR0aCAqIGhlaWdodCAqIGRlcHRoKTtcblxuICAgICAgbGV0IGNhbnZhczogSFRNTENhbnZhc0VsZW1lbnQgfCBPZmZzY3JlZW5DYW52YXM7XG4gICAgICBpZiAodHlwZW9mIE9mZnNjcmVlbkNhbnZhcyAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgY2FudmFzID0gbmV3IE9mZnNjcmVlbkNhbnZhcyh3aWR0aCwgaGVpZ2h0KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICAgICAgICBjYW52YXMud2lkdGggPSB3aWR0aDtcbiAgICAgICAgY2FudmFzLmhlaWdodCA9IGhlaWdodDtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGN0eCA9IGNhbnZhcy5nZXRDb250ZXh0KCcyZCcpIGFzXG4gICAgICAgIHwgQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJEXG4gICAgICAgIHwgT2Zmc2NyZWVuQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJEXG4gICAgICAgIHwgbnVsbDtcbiAgICAgIGlmICghY3R4KSB0aHJvdyBuZXcgRXJyb3IoJ25hcGFyaS1qcyB2b2x1bWUgYXNzZW1ibHk6IDJEIGNvbnRleHQgdW5hdmFpbGFibGUnKTtcblxuICAgICAgLy8gUmVhZCBvbmUgZmV0Y2hlZCBzbGljZSBiaXRtYXAgaW50byB0aGUgdm9sdW1lIHBsYW5lIGB6YCAobHVtaW5hbmNlKS4gU3luY2hyb25vdXMgYmV0d2VlblxuICAgICAgLy8gYXdhaXRzLCBzbyB0aGUgc2hhcmVkIDJEIGNvbnRleHQgaXMgc2FmZSB0byByZXVzZSBhY3Jvc3MgdGhlIGNvbmN1cnJlbnQgZmV0Y2ggd29ya2Vycy5cbiAgICAgIGxldCBkb25lID0gMDtcbiAgICAgIGNvbnN0IHJlYWRTbGljZSA9ICh6OiBudW1iZXIsIGJtcDogSW1hZ2VCaXRtYXApOiB2b2lkID0+IHtcbiAgICAgICAgY3R4LmNsZWFyUmVjdCgwLCAwLCB3aWR0aCwgaGVpZ2h0KTtcbiAgICAgICAgY3R4LmRyYXdJbWFnZShibXAsIDAsIDAsIHdpZHRoLCBoZWlnaHQpO1xuICAgICAgICBjb25zdCByZ2JhID0gY3R4LmdldEltYWdlRGF0YSgwLCAwLCB3aWR0aCwgaGVpZ2h0KS5kYXRhO1xuICAgICAgICBjb25zdCBiYXNlID0geiAqIHdpZHRoICogaGVpZ2h0O1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHdpZHRoICogaGVpZ2h0OyBpKyspIHtcbiAgICAgICAgICBkYXRhW2Jhc2UgKyBpXSA9XG4gICAgICAgICAgICAocmdiYVtpICogNF0gKiAwLjI5OSArIHJnYmFbaSAqIDQgKyAxXSAqIDAuNTg3ICsgcmdiYVtpICogNCArIDJdICogMC4xMTQpIHwgMDtcbiAgICAgICAgfVxuICAgICAgICBibXAuY2xvc2U/LigpO1xuICAgICAgICBkb25lKys7XG4gICAgICAgIHRoaXMuc3RhY2tMb2FkaW5nUHJvZ3Jlc3MkLm5leHQoTWF0aC5yb3VuZCgoZG9uZSAvIGRlcHRoKSAqIDEwMCkpO1xuICAgICAgfTtcblxuICAgICAgcmVhZFNsaWNlKDAsIGZpcnN0KTtcblxuICAgICAgLy8gUmVtYWluaW5nIHBsYW5lcyB2aWEgYSBzbWFsbCBwb29sIG9mIHdvcmtlcnMgZHJhaW5pbmcgYSBzaGFyZWQgcXVldWUuIEVhY2ggcGxhbmUgYHBgXG4gICAgICAvLyBtYXBzIHRvIHNvdXJjZSBzbGljZSBgekluZGljZXNbcF1gIChzdWJzYW1wbGVkIGZvciBsb3ctcmVzKS5cbiAgICAgIGNvbnN0IHBlbmRpbmc6IG51bWJlcltdID0gW107XG4gICAgICBmb3IgKGxldCBwID0gMTsgcCA8IGRlcHRoOyBwKyspIHBlbmRpbmcucHVzaChwKTtcbiAgICAgIGNvbnN0IHdvcmtlciA9IGFzeW5jICgpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICAgICAgZm9yICg7Oykge1xuICAgICAgICAgIGNvbnN0IHAgPSBwZW5kaW5nLnNoaWZ0KCk7XG4gICAgICAgICAgaWYgKHAgPT09IHVuZGVmaW5lZCB8fCB0aGlzLmxvYWRUb2tlbiAhPT0gdG9rZW4pIHJldHVybjtcbiAgICAgICAgICByZWFkU2xpY2UocCwgYXdhaXQgdGhpcy5mZXRjaFNsaWNlKHpJbmRpY2VzW3BdLCBjaGFubmVsLCBidWRnZXQpKTtcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGNvbnN0IHBvb2xTaXplID0gTWF0aC5taW4oVk9MVU1FX0ZFVENIX0NPTkNVUlJFTkNZLCBNYXRoLm1heCgxLCBkZXB0aCAtIDEpKTtcbiAgICAgIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oeyBsZW5ndGg6IHBvb2xTaXplIH0sICgpID0+IHdvcmtlcigpKSk7XG5cbiAgICAgIGlmICh0aGlzLmxvYWRUb2tlbiAhPT0gdG9rZW4pIHJldHVybiBudWxsOyAvLyBjYW5jZWxsZWQg4oaSIGRvbid0IHJlbmRlciBhIHBhcnRpYWwgdm9sdW1lXG4gICAgICByZXR1cm4geyBkYXRhLCB3aWR0aCwgaGVpZ2h0LCBkZXB0aCB9O1xuICAgIH0gZmluYWxseSB7XG4gICAgICB0aGlzLnN0YWNrTG9hZGluZ1Byb2dyZXNzJC5uZXh0KDApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBNb3VudCB0aGUgTkFQQVJJX1NDQVRURVIgMkQgc2NhdHRlcjogdGhlIHNsaWNlIGltYWdlIHdpdGggYSBwb2ludHMgbGF5ZXIgYXQgZWFjaCByZWdpb24nc1xuICAgKiBjZW50cm9pZCAobmFwYXJpLWpzIGFuYWxvZyBvZiBQbG90bHkncyByZWdpb24tY2VudHJvaWQgc2NhdHRlcikuIFJlYnVpbGRzIHRoZSBwb2ludHMgbGl2ZSBhc1xuICAgKiByZWdpb25zIGNoYW5nZS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgbW91bnRTY2F0dGVyKHZpZXdlcjogVmlld2VyLCB6OiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCB0aGlzLnJlbmRlckltYWdlKHopO1xuICAgIHRoaXMuZml0Q2FtZXJhU29vbigpO1xuICAgIHRoaXMuc3Vic2NyaWJlRGlzcGxheVN0YXRlKCk7XG4gICAgdGhpcy5pbnN0YWxsU2NhbGVCYXIoKTtcbiAgICB0aGlzLnJlYnVpbGRTY2F0dGVyUG9pbnRzKCk7XG4gICAgdGhpcy5zY2F0dGVyUmVnaW9uU3ViID0gdGhpcy5yZWdpb25TdG9yZVxuICAgICAgLmdldFJlZ2lvblVwZGF0ZUV2ZW50KClcbiAgICAgIC5zdWJzY3JpYmUoKCkgPT4gdGhpcy5yZWJ1aWxkU2NhdHRlclBvaW50cygpKTtcbiAgICB0aGlzLnNjaGVkdWxlUmVhZGJhY2soKTtcbiAgfVxuXG4gIC8qKiAoUmUpYnVpbGQgdGhlIDJEIHNjYXR0ZXIncyBwb2ludCBsYXllciBhdCB0aGUgY3VycmVudCByZWdpb24gY2VudHJvaWRzLiAqL1xuICBwcml2YXRlIHJlYnVpbGRTY2F0dGVyUG9pbnRzKCk6IHZvaWQge1xuICAgIGNvbnN0IHYgPSB0aGlzLnZpZXdlcjtcbiAgICBpZiAoIXYpIHJldHVybjtcbiAgICBpZiAodGhpcy5zY2F0dGVyMmRQb2ludHMpIHtcbiAgICAgIHYubGF5ZXJzLnJlbW92ZSh0aGlzLnNjYXR0ZXIyZFBvaW50cyk7XG4gICAgICB0aGlzLnNjYXR0ZXIyZFBvaW50cyA9IG51bGw7XG4gICAgfVxuICAgIGNvbnN0IGNlbnRyb2lkcyA9IHRoaXMucmVnaW9uQ2VudHJvaWRzKCk7XG4gICAgaWYgKGNlbnRyb2lkcy5sZW5ndGggPT09IDApIHJldHVybjtcbiAgICB0aGlzLnNjYXR0ZXIyZFBvaW50cyA9IHYuYWRkUG9pbnRzKGNlbnRyb2lkcywge1xuICAgICAgc2l6ZTogMTIsXG4gICAgICBmYWNlQ29sb3I6IFsxLCAwLjg1LCAwLjIsIDFdLFxuICAgICAgYm9yZGVyQ29sb3I6IFswLCAwLCAwLCAxXSxcbiAgICAgIGJvcmRlcldpZHRoOiAyLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqIFJlZ2lvbiBjZW50cm9pZHMgYXMgZmxhdCBgW3gsIHksIOKApl1gIGRhdGEgY29vcmRzIChyZWN0YW5nbGUgLyBwb2x5Z29uIC8gbXVsdGlwb2x5Z29uKS4gKi9cbiAgcHJpdmF0ZSByZWdpb25DZW50cm9pZHMoKTogRmxvYXQzMkFycmF5IHtcbiAgICBjb25zdCBvdXQ6IG51bWJlcltdID0gW107XG4gICAgY29uc3QgcG9seUNlbnRyb2lkID0gKHhzOiBudW1iZXJbXSwgeXM6IG51bWJlcltdKTogdm9pZCA9PiB7XG4gICAgICBjb25zdCBuID0geHMubGVuZ3RoO1xuICAgICAgaWYgKG4gPT09IDApIHJldHVybjtcbiAgICAgIGxldCBjeCA9IDA7XG4gICAgICBsZXQgY3kgPSAwO1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuOyBpKyspIHtcbiAgICAgICAgY3ggKz0geHNbaV07XG4gICAgICAgIGN5ICs9IHlzW2ldO1xuICAgICAgfVxuICAgICAgb3V0LnB1c2goY3ggLyBuLCBjeSAvIG4pO1xuICAgIH07XG4gICAgZm9yIChjb25zdCByIG9mIHRoaXMucmVnaW9uU3RvcmUuZ2V0UmVnaW9ucygpKSB7XG4gICAgICBjb25zdCBiID0gci5ib3VuZHMgYXNcbiAgICAgICAgfCB7IHg6IG51bWJlcjsgeTogbnVtYmVyOyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlciB9XG4gICAgICAgIHwgeyB4cG9pbnRzOiBudW1iZXJbXTsgeXBvaW50czogbnVtYmVyW10gfVxuICAgICAgICB8IHsgcG9seWdvbnM6IHsgeHBvaW50czogbnVtYmVyW107IHlwb2ludHM6IG51bWJlcltdIH1bXSB9XG4gICAgICAgIHwgbnVsbFxuICAgICAgICB8IHVuZGVmaW5lZDtcbiAgICAgIGlmICghYikgY29udGludWU7XG4gICAgICBpZiAoJ3dpZHRoJyBpbiBiICYmICd4JyBpbiBiKSB7XG4gICAgICAgIG91dC5wdXNoKGIueCArIGIud2lkdGggLyAyLCBiLnkgKyBiLmhlaWdodCAvIDIpO1xuICAgICAgfSBlbHNlIGlmICgneHBvaW50cycgaW4gYikge1xuICAgICAgICBwb2x5Q2VudHJvaWQoYi54cG9pbnRzLCBiLnlwb2ludHMpO1xuICAgICAgfSBlbHNlIGlmICgncG9seWdvbnMnIGluIGIpIHtcbiAgICAgICAgZm9yIChjb25zdCBwIG9mIGIucG9seWdvbnMpIHBvbHlDZW50cm9pZChwLnhwb2ludHMsIHAueXBvaW50cyk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBuZXcgRmxvYXQzMkFycmF5KG91dCk7XG4gIH1cblxuICAvKipcbiAgICogTW91bnQgdGhlIE5BUEFSSV9TQ0FUVEVSM0QgM0Qgc2NhdHRlcjogdGhlIGRvd25zYW1wbGVkIHZveGVsIGdyaWQgYXMgYSAzRCBwb2ludCBjbG91ZCBjb2xvcmVkXG4gICAqIGJ5IGludGVuc2l0eSAobmFwYXJpLWpzIGFuYWxvZyBvZiBQbG90bHkncyB2b3hlbCBzY2F0dGVyM2QpLiBBc3NlbWJsZXMgYSBjb2Fyc2Ugdm9sdW1lLCB0aGVuXG4gICAqIGVtaXRzIGEgZmxhdC1zdHJpZGVkIHNhbXBsZSBvZiB2b3hlbHMgKGNhcHBlZCBhdCB7QGxpbmsgU0NBVFRFUjNEX01BWF9QT0lOVFN9KSB2aWEgYGFkZFBvaW50czNEYC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgbW91bnRTY2F0dGVyM2Qodmlld2VyOiBWaWV3ZXIsIGluZm86IElJbWFnZUluZm8gfCB1bmRlZmluZWQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmltYWdlTW9kZSA9ICdncmF5c2NhbGUnO1xuICAgIHRoaXMudm9sdW1lTXVsdGljaGFubmVsID0gZmFsc2U7XG4gICAgY29uc3QgcmVzID0gdm9sdW1lUmVzb2x1dGlvbkZvcih0aGlzLnJlc29sdXRpb25TY2FsZSk7XG4gICAgdGhpcy5zdGFja0xvYWRpbmckLm5leHQodHJ1ZSk7XG4gICAgdGhpcy5zdGFja0xvYWRpbmdQcm9ncmVzcyQubmV4dCgwKTtcbiAgICBsZXQgdm9sOiB7IGRhdGE6IFVpbnQ4QXJyYXk7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyOyBkZXB0aDogbnVtYmVyIH0gfCBudWxsID0gbnVsbDtcbiAgICB0cnkge1xuICAgICAgdm9sID0gYXdhaXQgdGhpcy5hc3NlbWJsZVZvbHVtZShpbmZvLCB7XG4gICAgICAgIG1heFNsaWNlOiBNYXRoLm1pbihyZXMubWF4U2xpY2UsIFNDQVRURVIzRF9NQVhfWFkpLFxuICAgICAgICBzbGljZVN0ZXA6IHJlcy5zbGljZVN0ZXAsXG4gICAgICB9KTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgdGhpcy5zdGFja0xvYWRpbmckLm5leHQoZmFsc2UpO1xuICAgICAgdGhpcy5zdGFja0xvYWRpbmdQcm9ncmVzcyQubmV4dCgwKTtcbiAgICB9XG4gICAgaWYgKCF2b2wgfHwgdGhpcy52aWV3ZXIgIT09IHZpZXdlcikgcmV0dXJuO1xuXG4gICAgY29uc3QgeyBkYXRhLCB3aWR0aCwgaGVpZ2h0LCBkZXB0aCB9ID0gdm9sO1xuICAgIGNvbnN0IHpTY2FsZSA9IE1hdGgubWF4KHdpZHRoLCBoZWlnaHQpIC8gTWF0aC5tYXgoMSwgZGVwdGgpOyAvLyDiiYggY3ViaWMgYXNwZWN0XG4gICAgY29uc3QgdG90YWwgPSB3aWR0aCAqIGhlaWdodCAqIGRlcHRoO1xuICAgIGNvbnN0IHN0cmlkZSA9IE1hdGgubWF4KDEsIE1hdGguY2VpbCh0b3RhbCAvIFNDQVRURVIzRF9NQVhfUE9JTlRTKSk7XG4gICAgY29uc3QgcG9zOiBudW1iZXJbXSA9IFtdO1xuICAgIGNvbnN0IHZhbDogbnVtYmVyW10gPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRvdGFsOyBpICs9IHN0cmlkZSkge1xuICAgICAgY29uc3QgeCA9IGkgJSB3aWR0aDtcbiAgICAgIGNvbnN0IHkgPSBNYXRoLmZsb29yKGkgLyB3aWR0aCkgJSBoZWlnaHQ7XG4gICAgICBjb25zdCB6aSA9IE1hdGguZmxvb3IoaSAvICh3aWR0aCAqIGhlaWdodCkpO1xuICAgICAgcG9zLnB1c2goeCwgeSwgemkgKiB6U2NhbGUpO1xuICAgICAgdmFsLnB1c2goZGF0YVtpXSk7XG4gICAgfVxuXG4gICAgY29uc3Qgc3QgPSB0aGlzLnN0b3JlLmN1cnJlbnRDaGFubmVsU3RhdGVzKClbMF07XG4gICAgdGhpcy5zY2F0dGVyM2RMYXllciA9IHZpZXdlci5hZGRQb2ludHMzRChuZXcgRmxvYXQzMkFycmF5KHBvcyksIG5ldyBGbG9hdDMyQXJyYXkodmFsKSwge1xuICAgICAgY29sb3JtYXA6IHRoaXMudm9sdW1lQ29sb3JtYXAoc3QpLFxuICAgICAgY29udHJhc3RMaW1pdHM6IFtzdD8ubWluID8/IDAsIHN0Py5tYXggPz8gMjU1XSxcbiAgICAgIHNpemU6IDMsXG4gICAgfSk7XG4gICAgdGhpcy5pbWFnZVcgPSB3aWR0aDtcbiAgICB0aGlzLmltYWdlSCA9IGhlaWdodDtcbiAgICB0aGlzLnZvbHVtZURpbXMgPSB7IHdpZHRoLCBoZWlnaHQsIGRlcHRoIH07XG4gICAgLy8gRmVlZCB0aGUgaW50ZW5zaXR5IGhpc3RvZ3JhbSBmcm9tIHRoZSBhc3NlbWJsZWQgdm9sdW1lIChrZXkgMCkuXG4gICAgdGhpcy52b2x1bWVDaGFubmVsRGF0YS5jbGVhcigpO1xuICAgIHRoaXMudm9sdW1lQ2hhbm5lbERhdGEuc2V0KDAsIGRhdGEpO1xuICAgIHRoaXMuc3Vic2NyaWJlU2NhdHRlcjNkRGlzcGxheVN0YXRlKCk7XG4gICAgdGhpcy5zY2hlZHVsZVJlYWRiYWNrKCk7XG4gIH1cblxuICAvKiogU3RvcmUgY29sb3JtYXAgLyByZXZlcnNlIC8gaW52ZXJ0IC8gY2hhbm5lbCB3aW5kb3cg4oaSIHRoZSAzRCBzY2F0dGVyJ3MgY29sb3JtYXAgKyBjb250cmFzdC4gKi9cbiAgcHJpdmF0ZSBzdWJzY3JpYmVTY2F0dGVyM2REaXNwbGF5U3RhdGUoKTogdm9pZCB7XG4gICAgdGhpcy5kaXNwbGF5U3ViPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMuZGlzcGxheVN1YiA9IGNvbWJpbmVMYXRlc3QoW1xuICAgICAgdGhpcy5zdG9yZS5nZXRDb2xvcm1hcCgpLFxuICAgICAgdGhpcy5zdG9yZS5nZXRSZXZlcnNlU2NhbGUoKSxcbiAgICAgIHRoaXMuc3RvcmUuZ2V0SW52ZXJ0KCksXG4gICAgICB0aGlzLnN0b3JlLmdldENoYW5uZWxTdGF0ZXMoKSxcbiAgICBdKS5zdWJzY3JpYmUoKFtjb2xvcm1hcCwgcmV2ZXJzZSwgaW52ZXJ0LCBjaGFubmVsc10pID0+IHtcbiAgICAgIHRoaXMuY3VycmVudENvbG9ybWFwID0gKGNvbG9ybWFwIGFzIENvbG9ybWFwTm9kZSkgPz8gbnVsbDtcbiAgICAgIHRoaXMuY3VycmVudFJldmVyc2UgPSByZXZlcnNlO1xuICAgICAgdGhpcy5pbnZlcnRFbmFibGVkID0gaW52ZXJ0O1xuICAgICAgY29uc3QgbGF5ZXIgPSB0aGlzLnNjYXR0ZXIzZExheWVyO1xuICAgICAgaWYgKCFsYXllcikgcmV0dXJuO1xuICAgICAgY29uc3Qgc3QgPSBjaGFubmVsc1swXTtcbiAgICAgIGxheWVyLmNvbG9ybWFwID0gdGhpcy52b2x1bWVDb2xvcm1hcChzdCk7XG4gICAgICBpZiAoc3QpIGxheWVyLmNvbnRyYXN0TGltaXRzID0gW3N0Lm1pbiwgc3QubWF4XTtcbiAgICAgIHRoaXMudmlld2VyPy5yZXF1ZXN0UmVuZGVyKCk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogTW91bnQgdGhlIE5BUEFSSV9TVVJGQUNFIGhlaWdodC1maWVsZCBzdXJmYWNlLiBBIGhlaWdodCBmaWVsZCBpcyBzaW5nbGUtc2NhbGFyLCBzbyBmb3IgYVxuICAgKiBtdWx0aWNoYW5uZWwgaW1hZ2UgdGhlIHN1cmZhY2UgZm9sbG93cyBPTkUgYmFuZCDigJQgdGhlIGZpcnN0IHZpc2libGUgY2hhbm5lbCAoZmFsbGJhY2sgMCkg4oCUXG4gICAqIGNvbG91cmVkIGJ5IHRoYXQgY2hhbm5lbCdzIHdpbmRvdy9jb2xvcm1hcCAobGlrZSB0aGUgUGxvdGx5IFNVUkZBQ0UsIHdoaWNoIGlzIGdyYXlzY2FsZS1vbmx5KS5cbiAgICogUHJlLWxvYWRzIGV2ZXJ5IHNsaWNlJ3MgaGVpZ2h0IGRhdGEgd2l0aCBhIHByb2dyZXNzIGJhciAoYXMgdGhlIHZvbHVtZSBkb2VzKSBzbyB0aGUgc3RhY2tcbiAgICogc2xpZGVyIHJlLXNsaWNlcyBpbnN0YW50bHksIHRoZW4gYnVpbGRzIHRoZSBtZXNoIGZvciB0aGUgY3VycmVudCBzbGljZS4gQWxsIG1lc2ggKyBHUFUgd29ya1xuICAgKiBsaXZlcyBpbiBuYXBhcmktanMgKGBoZWlnaHRGaWVsZGAgKyBgVmlld2VyLmFkZFN1cmZhY2VgKTsgdGhpcyBiYWNrZW5kIHN1cHBsaWVzIHNjYWxhciBzbGljZXMuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIG1vdW50U3VyZmFjZSh2aWV3ZXI6IFZpZXdlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGRlc2MgPSBhd2FpdCB0aGlzLmVuc3VyZURlc2NyaXB0b3IoKTtcbiAgICBjb25zdCBtdWx0aWNoYW5uZWwgPSAhIWRlc2M/Lm11bHRpY2hhbm5lbCAmJiAoZGVzYz8uY2hhbm5lbHMgPz8gMSkgPiAxO1xuICAgIGNvbnN0IHN0YXRlcyA9IHRoaXMuc3RvcmUuY3VycmVudENoYW5uZWxTdGF0ZXMoKTtcbiAgICB0aGlzLnN1cmZhY2VDaGFubmVsID0gbXVsdGljaGFubmVsID8gc3RhdGVzLmZpbmQoKHMpID0+IHMudmlzaWJsZSk/LmluZGV4ID8/IDAgOiB1bmRlZmluZWQ7XG4gICAgdGhpcy5zdXJmYWNlTWF4R3JpZCA9IHN1cmZhY2VSZXNvbHV0aW9uRm9yKHRoaXMucmVzb2x1dGlvblNjYWxlKS5tYXhHcmlkO1xuICAgIHRoaXMuaW1hZ2VNb2RlID0gJ2dyYXlzY2FsZSc7XG4gICAgdGhpcy52b2x1bWVNdWx0aWNoYW5uZWwgPSBmYWxzZTtcbiAgICBhd2FpdCB0aGlzLnByZWxvYWRTdXJmYWNlUGxhbmVzKHZpZXdlcik7XG4gICAgaWYgKHRoaXMudmlld2VyICE9PSB2aWV3ZXIpIHJldHVybjtcbiAgICBhd2FpdCB0aGlzLmJ1aWxkU3VyZmFjZSh2aWV3ZXIsIHRoaXMubG9hZGVkPy56ID8/IDApO1xuICAgIHRoaXMuaW5zdGFsbFN1cmZhY2VBeGVzKHZpZXdlcik7XG4gICAgLy8gU3Vic2NyaWJlIGFmdGVyIHRoZSBmaXJzdCBidWlsZCBzbyBkaXNwbGF5LXN0YXRlIGVkaXRzIHRhcmdldCBhIGxpdmUgbGF5ZXIuXG4gICAgdGhpcy5zdWJzY3JpYmVTdXJmYWNlRGlzcGxheVN0YXRlKCk7XG4gIH1cblxuICAvKiogQWRkIHRoZSAzRCBheGVzIGdpem1vICsgRE9NIGxhYmVscyBhcm91bmQgdGhlIChvcmlnaW4tY2VudGVyZWQpIHN1cmZhY2UgbWVzaCwgbWF0Y2hpbmcgdGhlXG4gICAqICB2b2x1bWUvaXNvc3VyZmFjZS4gSW5zdGFsbGVkIG9uY2UgcGVyIG1vdW50OyB0aGUgYm94IHRyYWNrcyB0aGUgbWVzaCBib3VuZHMsIFgvWSBzaG93IHRoZVxuICAgKiAgcGh5c2ljYWwgKG9yIHBpeGVsKSBleHRlbnQsIGFuZCBaIGlzIHRoZSBpbnRlbnNpdHkvaGVpZ2h0IGF4aXMuICovXG4gIHByaXZhdGUgaW5zdGFsbFN1cmZhY2VBeGVzKHZpZXdlcjogVmlld2VyKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLnN1cmZhY2VMYXllcikgcmV0dXJuO1xuICAgIGNvbnN0IGIgPSB0aGlzLnN1cmZhY2VMYXllci5ib3VuZHMoKTtcbiAgICBjb25zdCBib3hXID0gTWF0aC5tYXgoMSwgYi5tYXhbMF0gLSBiLm1pblswXSk7XG4gICAgY29uc3QgYm94SCA9IE1hdGgubWF4KDEsIGIubWF4WzFdIC0gYi5taW5bMV0pO1xuICAgIGNvbnN0IGJveEQgPSBNYXRoLm1heCgxLCBiLm1heFsyXSAtIGIubWluWzJdKTtcbiAgICBjb25zdCBtcHBYID0gdGhpcy5kZXNjcmlwdG9yPy5tcHBYIHx8IHRoaXMubG9hZGVkPy5pbWFnZUluZm8uaW1hZ2VNZXRhPy5bMF0/Lm1wcFggfHwgMDtcbiAgICBjb25zdCB2b3hlbCA9IG1wcFggPiAwID8gKG1wcFggKiAodGhpcy5kZXNjcmlwdG9yPy53aWR0aCA/PyB0aGlzLmltYWdlVykpIC8gTWF0aC5tYXgoMSwgdGhpcy5pbWFnZVcpIDogMTtcbiAgICB0aGlzLmF4ZXNMYXllciA9IHZpZXdlci5hZGRBeGVzKGJveFcsIGJveEgsIGJveEQsIHtcbiAgICAgIHZveGVsU2l6ZTogW3ZveGVsLCB2b3hlbCwgMV0sXG4gICAgICB2aXNpYmxlOiB0aGlzLmF4ZXNWaXNpYmxlLFxuICAgIH0pO1xuICAgIGlmICh0aGlzLmhvc3QpIHtcbiAgICAgIHRoaXMuYXhlc0xhYmVscyA9IG5ldyBOYXBhcmlBeGVzTGFiZWxzKFxuICAgICAgICB0aGlzLmhvc3QsXG4gICAgICAgIHZpZXdlci5jYW1lcmEzZCxcbiAgICAgICAgdGhpcy5idWlsZFN1cmZhY2VBeGVzTGFiZWxzKGJveFcsIGJveEgsIGJveEQsIG1wcFgpLFxuICAgICAgKTtcbiAgICAgIHRoaXMuYXhlc0xhYmVscy5zZXRWaXNpYmxlKHRoaXMuYXhlc1Zpc2libGUpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBYL1kvWiBlbmQtbGFiZWxzIGZvciB0aGUgc3VyZmFjZSBnaXptbzogWC9ZIGFyZSB0aGUgcGh5c2ljYWwgKMK1bSkgb3IgcGl4ZWwgZXh0ZW50IG9mIHRoZSBGVUxMXG4gICAqICBpbWFnZTsgWiBpcyB0aGUgaW50ZW5zaXR5L2hlaWdodCBheGlzLiBBbmNob3JzIGFyZSBpbiB0aGUgY2VudGVyZWQgYm94IChtYXRjaGluZyBBeGVzTGF5ZXIpLiAqL1xuICBwcml2YXRlIGJ1aWxkU3VyZmFjZUF4ZXNMYWJlbHMoXG4gICAgYm94VzogbnVtYmVyLFxuICAgIGJveEg6IG51bWJlcixcbiAgICBib3hEOiBudW1iZXIsXG4gICAgbXBwWDogbnVtYmVyLFxuICApOiBBeGlzTGFiZWxTcGVjW10ge1xuICAgIGNvbnN0IGh4ID0gYm94VyAvIDI7XG4gICAgY29uc3QgaHkgPSBib3hIIC8gMjtcbiAgICBjb25zdCBoeiA9IGJveEQgLyAyO1xuICAgIGNvbnN0IGRlc2NXID0gdGhpcy5kZXNjcmlwdG9yPy53aWR0aCA/PyB0aGlzLmltYWdlVztcbiAgICBjb25zdCBkZXNjSCA9IHRoaXMuZGVzY3JpcHRvcj8uaGVpZ2h0ID8/IHRoaXMuaW1hZ2VIO1xuICAgIGNvbnN0IGxlbiA9IChweDogbnVtYmVyKTogc3RyaW5nID0+IChtcHBYID4gMCA/IGZvcm1hdFVtKHB4ICogbXBwWCkgOiBgJHtweH0gcHhgKTtcbiAgICByZXR1cm4gW1xuICAgICAgeyBhbmNob3I6IFtoeCwgLWh5LCAtaHpdLCB0ZXh0OiBgWCDCtyAke2xlbihkZXNjVyl9YCwgY29sb3I6ICcjZWQ0NTQ1JyB9LFxuICAgICAgeyBhbmNob3I6IFstaHgsIGh5LCAtaHpdLCB0ZXh0OiBgWSDCtyAke2xlbihkZXNjSCl9YCwgY29sb3I6ICcjNGRkOTU5JyB9LFxuICAgICAgeyBhbmNob3I6IFstaHgsIC1oeSwgaHpdLCB0ZXh0OiAnWiDCtyBpbnRlbnNpdHknLCBjb2xvcjogJyM2NjhjZmYnIH0sXG4gICAgXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQcmUtZmV0Y2ggZXZlcnkgc3RhY2sgc2xpY2UncyBsdW1pbmFuY2UgcGxhbmUgKGRlY2ltYXRlZCB0byB0aGUgc3VyZmFjZSBncmlkKSBpbnRvXG4gICAqIHtAbGluayBzdXJmYWNlUGxhbmVzfSwgZHJpdmluZyB7QGxpbmsgc3RhY2tMb2FkaW5nUHJvZ3Jlc3MkfSDigJQgdGhlIHNhbWUgbG9hZC13aXRoLXByb2dyZXNzIFVYXG4gICAqIGFzIHRoZSB2b2x1bWUsIGJ1dCBrZWVwaW5nIG9uZSAyRCBwbGFuZSBwZXIgc2xpY2UgcmF0aGVyIHRoYW4gcGFja2luZyBhIDNEIHZvbHVtZS4gQm91bmRlZFxuICAgKiBjb25jdXJyZW5jeSBrZWVwcyB0aGUgY29ubmVjdGlvbiBwb29sIGJ1c3kgd2l0aG91dCBmbG9vZGluZyBpdCBvbiBhIGRlZXAgc3RhY2suXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHByZWxvYWRTdXJmYWNlUGxhbmVzKHZpZXdlcjogVmlld2VyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgdG9rZW4gPSB0aGlzLmxvYWRUb2tlbjsgLy8gYmFpbCBpZiBhIENhbmNlbCAvIG5ldyBwbG90IGJ1bXBzIHRoaXMgd2hpbGUgd2UgZmV0Y2hcbiAgICBjb25zdCBpbmZvID0gdGhpcy5sb2FkZWQ/LmltYWdlSW5mbztcbiAgICBjb25zdCBkZXB0aCA9IGluZm8/LmltYWdlTWV0YT8uWzBdPy56IHx8IGluZm8/LnVybHM/Lmxlbmd0aCB8fCAxO1xuICAgIGNvbnN0IHsgbWF4R3JpZCB9ID0gc3VyZmFjZVJlc29sdXRpb25Gb3IodGhpcy5yZXNvbHV0aW9uU2NhbGUpO1xuICAgIHRoaXMuc3VyZmFjZVBsYW5lcy5jbGVhcigpO1xuICAgIHRoaXMuc3RhY2tMb2FkaW5nJC5uZXh0KHRydWUpO1xuICAgIHRoaXMuc3RhY2tMb2FkaW5nUHJvZ3Jlc3MkLm5leHQoMCk7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHBlbmRpbmc6IG51bWJlcltdID0gW107XG4gICAgICBmb3IgKGxldCB6ID0gMDsgeiA8IGRlcHRoOyB6KyspIHBlbmRpbmcucHVzaCh6KTtcbiAgICAgIGxldCBkb25lID0gMDtcbiAgICAgIGNvbnN0IHdvcmtlciA9IGFzeW5jICgpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICAgICAgZm9yICg7Oykge1xuICAgICAgICAgIGNvbnN0IHogPSBwZW5kaW5nLnNoaWZ0KCk7XG4gICAgICAgICAgaWYgKHogPT09IHVuZGVmaW5lZCB8fCB0aGlzLnZpZXdlciAhPT0gdmlld2VyIHx8IHRoaXMubG9hZFRva2VuICE9PSB0b2tlbikgcmV0dXJuO1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICB0aGlzLnN1cmZhY2VQbGFuZXMuc2V0KHosIGF3YWl0IHRoaXMuZmV0Y2hTdXJmYWNlUGxhbmUoeiwgbWF4R3JpZCkpO1xuICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKGBbbmFwYXJpLWpzXSBzdXJmYWNlIHNsaWNlICR7en0gcHJlbG9hZCBmYWlsZWRgLCBlcnIpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBkb25lKys7XG4gICAgICAgICAgdGhpcy5zdGFja0xvYWRpbmdQcm9ncmVzcyQubmV4dChNYXRoLnJvdW5kKChkb25lIC8gZGVwdGgpICogMTAwKSk7XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBjb25zdCBwb29sID0gTWF0aC5taW4oVk9MVU1FX0ZFVENIX0NPTkNVUlJFTkNZLCBNYXRoLm1heCgxLCBkZXB0aCkpO1xuICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoQXJyYXkuZnJvbSh7IGxlbmd0aDogcG9vbCB9LCAoKSA9PiB3b3JrZXIoKSkpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICB0aGlzLnN0YWNrTG9hZGluZyQubmV4dChmYWxzZSk7XG4gICAgICB0aGlzLnN0YWNrTG9hZGluZ1Byb2dyZXNzJC5uZXh0KDApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBGZXRjaCBzbGljZSBgemAgYXMgYSBzaW5nbGUgV0hPTEUtaW1hZ2UgbHVtaW5hbmNlIHBsYW5lIChkZWNpbWF0ZWQgdG8gYG1heEdyaWRgKS4gUHJlZmVycyB0aGVcbiAgICogYXBwJ3MgY29tcGxldGUgcGVyLXNsaWNlIGltYWdlIChgc21hbGxVcmxzYC9gdXJsc2AsIGV4YWN0bHkgbGlrZSB0aGUgUGxvdGx5IHN1cmZhY2UpIHNvIHRoZVxuICAgKiBzdXJmYWNlIGFsd2F5cyBjb3ZlcnMgdGhlIEZVTEwgc2xpY2Ug4oCUIHRoZSB0aWxlLXB5cmFtaWQgYGZldGNoU2xpY2VgIHBhdGggd291bGQgZmFsbCBiYWNrIHRvIGFcbiAgICogc2luZ2xlIHRvcC1sZWZ0IHRpbGUgKGEgY29ybmVyKSBmb3Igc2VsZi1jb250YWluZWQgLyBub24tYC90aWxlcy9pbmZvYCBpbWFnZXMuIEZhbGxzIGJhY2sgdG9cbiAgICogc3RpdGNoaW5nIHRoZSB0aWxlIGdyaWQgb25seSB3aGVuIG5vIGNvbXBsZXRlLWltYWdlIFVSTCBpcyBhdmFpbGFibGUuXG4gICAqL1xuICAvKiogVGlsZSBidWRnZXQgdG8gc3RpdGNoIGEgd2hvbGUgc2xpY2UgYXQgfmB0YXJnZXRQeGAgcmVzb2x1dGlvbiBmcm9tIHRoZSBweXJhbWlkOiBhIGhpZ2hlciB0YXJnZXRcbiAgICogIHB1bGxzIGEgRklORVIgcHlyYW1pZCBsZXZlbCAobW9yZSByZWFsIGRldGFpbCkuIFNoYXJlZCBieSB0aGUgc3VyZmFjZSBwbGFuZSBmZXRjaCBhbmQgdGhlIHZvbHVtZVxuICAgKiAgYXNzZW1ibHkgc28gYm90aCBzY2FsZSB0aGVpciBpbi1wbGFuZSByZXNvbHV0aW9uIHdpdGggdGhlIGRlY2ltYXRlIGZhY3Rvci4gKi9cbiAgcHJpdmF0ZSB0aWxlQnVkZ2V0Rm9yKHRhcmdldFB4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGNvbnN0IHRpbGVTaXplID0gdGhpcy5kZXNjcmlwdG9yPy50aWxlU2l6ZSB8fCBUSUxFX1NJWkU7XG4gICAgcmV0dXJuIE1hdGgubWluKFxuICAgICAgTUFYX1NUSVRDSF9USUxFUyxcbiAgICAgIE1hdGgubWF4KDEsIE1hdGgucm91bmQoKHRhcmdldFB4IC8gdGlsZVNpemUpICoqIDIgKiBTVElUQ0hfQlVER0VUX0NPRUZGKSksXG4gICAgKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZmV0Y2hTdXJmYWNlUGxhbmUoXG4gICAgejogbnVtYmVyLFxuICAgIG1heEdyaWQ6IG51bWJlcixcbiAgKTogUHJvbWlzZTx7IGRhdGE6IFVpbnQ4QXJyYXk7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyIH0+IHtcbiAgICBjb25zdCBpbmZvID0gdGhpcy5sb2FkZWQ/LmltYWdlSW5mbztcbiAgICBjb25zdCBkZXNjID0gYXdhaXQgdGhpcy5lbnN1cmVEZXNjcmlwdG9yKCk7XG4gICAgbGV0IHBsYW5lOiB7IGRhdGE6IFVpbnQ4QXJyYXk7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyIH0gfCBudWxsID0gbnVsbDtcblxuICAgIC8vIFByZWZlcnJlZDogc3RpdGNoIHRoZSBXSE9MRSBzbGljZSBmcm9tIHRoZSBzZXJ2ZXIgcHlyYW1pZCBhdCBhIHJlc29sdXRpb24gZHJpdmVuIGJ5IHRoZVxuICAgIC8vIGRlY2ltYXRlIGZhY3RvciDigJQgYSBoaWdoZXIgdGFyZ2V0IGdyaWQgcHVsbHMgYSBGSU5FUiBweXJhbWlkIGxldmVsIChtb3JlIHJlYWwgZGV0YWlsKS4gV2l0aCBhXG4gICAgLy8gZGVzY3JpcHRvciwgZmV0Y2hTbGljZSBzdGl0Y2hlcyB0aGUgd2hvbGUgY2hvc2VuIGxldmVsIChuZXZlciBhIGNvcm5lciksIHRoZW4gd2UgZG93bnNjYWxlIHRvXG4gICAgLy8gdGhlIGdyaWQuIFRoaXMgaXMgd2hhdCBtYWtlcyBcIkZ1bGxcIiBhY3R1YWxseSBoaWdoZXItcmVzIHRoYW4gXCLCvVwiLCBub3QganVzdCBhIGZpeGVkIHByZXZpZXcuXG4gICAgaWYgKGRlc2M/LmxldmVscz8ubGVuZ3RoKSB7XG4gICAgICBjb25zdCBidWRnZXQgPSB0aGlzLnRpbGVCdWRnZXRGb3IobWF4R3JpZCk7XG4gICAgICAvLyBUaGUgc3VyZmFjZSBpcyBhIHNpbmdsZSBkZWNpbWF0ZWQgcGxhbmUsIHNvIHRoZSBjb21wb3NpdGUgZmFsbGJhY2sgaXMgYWNjZXB0YWJsZSB3aGVuIHRoZVxuICAgICAgLy8gY2hhbm5lbCBoYXMgbm8gc21hbGwgcHlyYW1pZCBsZXZlbCAoa2VlcHMgaXQgZmFzdCk7IGEgbXVsdGljaGFubmVsIFZPTFVNRSBtdXN0IG5vdCBkbyB0aGlzLlxuICAgICAgcGxhbmUgPSB0aGlzLmJpdG1hcFRvTHVtaW5hbmNlKFxuICAgICAgICBhd2FpdCB0aGlzLmZldGNoU2xpY2UoeiwgdGhpcy5zdXJmYWNlQ2hhbm5lbCwgYnVkZ2V0LCB0cnVlKSxcbiAgICAgICAgbWF4R3JpZCxcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gRmFsbGJhY2sgKG5vIHB5cmFtaWQpOiB0aGUgYXBwJ3MgQ09NUExFVEUgcGVyLXNsaWNlIGltYWdlICh1cmxzW3pdLCBub3QgdGhlIHNtYWxsIGJsdXJyeVxuICAgIC8vIHRodW1ibmFpbCkg4oCUIHdob2xlIHNsaWNlLCBhdm9pZHMgYSBjb3JuZXIgdGlsZS4gQ2FwcGVkIGF0IHRoZSBpbWFnZSdzIG93biByZXNvbHV0aW9uLlxuICAgIGlmICghcGxhbmUpIHtcbiAgICAgIGNvbnN0IHVybCA9IGluZm8/LnVybHM/Llt6XSA/PyBpbmZvPy5zbWFsbFVybHM/Llt6XTtcbiAgICAgIGlmICh1cmwpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBoZWFkZXJzID0gYXdhaXQgdGhpcy50aWxlc1xuICAgICAgICAgICAgLmdldEF1dGhIZWFkZXJzKClcbiAgICAgICAgICAgIC5jYXRjaCgoKSA9PiAoe30pIGFzIFJlY29yZDxzdHJpbmcsIHN0cmluZz4pO1xuICAgICAgICAgIGNvbnN0IHJlc3AgPSBhd2FpdCBmZXRjaCh1cmwsIHsgaGVhZGVycyB9KTtcbiAgICAgICAgICBpZiAocmVzcC5vaykge1xuICAgICAgICAgICAgcGxhbmUgPSB0aGlzLmJpdG1hcFRvTHVtaW5hbmNlKGF3YWl0IGNyZWF0ZUltYWdlQml0bWFwKGF3YWl0IHJlc3AuYmxvYigpKSwgbWF4R3JpZCk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICBjb25zb2xlLndhcm4oYFtuYXBhcmktanNdIHN1cmZhY2UgdXJsIGZldGNoIGZhaWxlZCBmb3Igej0ke3p9YCwgZXJyKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIExhc3QgcmVzb3J0IChubyBweXJhbWlkIGFuZCBubyBjb21wbGV0ZSBpbWFnZSk6IGEgc2luZ2xlIHRpbGUuXG4gICAgaWYgKCFwbGFuZSkge1xuICAgICAgcGxhbmUgPSB0aGlzLmJpdG1hcFRvTHVtaW5hbmNlKFxuICAgICAgICBhd2FpdCB0aGlzLmZldGNoU2xpY2UoeiwgdGhpcy5zdXJmYWNlQ2hhbm5lbCwgdGhpcy50aWxlQnVkZ2V0Rm9yKG1heEdyaWQpLCB0cnVlKSxcbiAgICAgICAgbWF4R3JpZCxcbiAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiBwbGFuZTtcbiAgfVxuXG4gIC8qKiBUaGUgY2hhbm5lbCBzdGF0ZSBkcml2aW5nIHRoZSBzdXJmYWNlOiB0aGUgY2hvc2VuIGJhbmQgZm9yIG11bHRpY2hhbm5lbCAobWF0Y2hlZCBieSBpbmRleCksXG4gICAqICBlbHNlIHRoZSBzaW5nbGUgZ3JheXNjYWxlIGNoYW5uZWwuICovXG4gIHByaXZhdGUgc3VyZmFjZVN0YXRlKGNoYW5uZWxzOiBJQ2hhbm5lbFN0YXRlW10pOiBJQ2hhbm5lbFN0YXRlIHwgdW5kZWZpbmVkIHtcbiAgICBpZiAodGhpcy5zdXJmYWNlQ2hhbm5lbCA9PSBudWxsKSByZXR1cm4gY2hhbm5lbHNbMF07XG4gICAgcmV0dXJuIGNoYW5uZWxzLmZpbmQoKHMpID0+IHMuaW5kZXggPT09IHRoaXMuc3VyZmFjZUNoYW5uZWwpID8/IGNoYW5uZWxzWzBdO1xuICB9XG5cbiAgLyoqXG4gICAqIChSZSlidWlsZCB0aGUgc3VyZmFjZSBtZXNoIGZvciBzbGljZSBgemAgZnJvbSB0aGUgcHJlLWxvYWRlZCBwbGFuZSBjYWNoZSAoaW5zdGFudCDigJQgdGhpcyBpc1xuICAgKiB3aGF0IHRoZSBzdGFjayBzbGlkZXIgY2FsbHMpOyBhIHNsaWNlIG1pc3NpbmcgZnJvbSB0aGUgY2FjaGUgaXMgZmV0Y2hlZCBvbiBkZW1hbmQuIG5hcGFyaS1qcydzXG4gICAqIHB1cmUgYGhlaWdodEZpZWxkYCBidWlsZHMgdGhlIHRyaWFuZ2xlIGdyaWQgKHogPSBub3JtYWxpemVkIGludGVuc2l0eSksIHRoZW4gYGFkZFN1cmZhY2VgXG4gICAqIHJlbmRlcnMgaXQuIFRoZSBzbGljZSBwbGFuZSBhbHNvIGZlZWRzIHRoZSBpbnRlbnNpdHkgaGlzdG9ncmFtIChrZXkgMCkuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGJ1aWxkU3VyZmFjZSh2aWV3ZXI6IFZpZXdlciwgejogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgbGV0IHBsYW5lID0gdGhpcy5zdXJmYWNlUGxhbmVzLmdldCh6KTtcbiAgICBpZiAoIXBsYW5lKSB7XG4gICAgICB0aGlzLnN0YWNrTG9hZGluZyQubmV4dCh0cnVlKTtcbiAgICAgIHRyeSB7XG4gICAgICAgIHBsYW5lID0gYXdhaXQgdGhpcy5mZXRjaFN1cmZhY2VQbGFuZSh6LCB0aGlzLnN1cmZhY2VNYXhHcmlkKTtcbiAgICAgICAgdGhpcy5zdXJmYWNlUGxhbmVzLnNldCh6LCBwbGFuZSk7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcignW25hcGFyaS1qc10gc3VyZmFjZSBzbGljZSBmZXRjaCBmYWlsZWQ6JywgZXJyKTtcbiAgICAgIH0gZmluYWxseSB7XG4gICAgICAgIHRoaXMuc3RhY2tMb2FkaW5nJC5uZXh0KGZhbHNlKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKCFwbGFuZSB8fCBwbGFuZS53aWR0aCA8IDIgfHwgcGxhbmUuaGVpZ2h0IDwgMiB8fCB0aGlzLnZpZXdlciAhPT0gdmlld2VyKSByZXR1cm47XG5cbiAgICBjb25zdCBzdCA9IHRoaXMuc3VyZmFjZVN0YXRlKHRoaXMuc3RvcmUuY3VycmVudENoYW5uZWxTdGF0ZXMoKSk7XG4gICAgY29uc3Qgd2luOiBbbnVtYmVyLCBudW1iZXJdID0gW3N0Py5taW4gPz8gMCwgc3Q/Lm1heCA/PyAyNTVdO1xuICAgIHRoaXMuc3VyZmFjZVdpbmRvdyA9IHdpbjtcbiAgICAvLyBIZWlnaHQgQU5EIGNvbG91ciBhcmUgbm9ybWFsaXplZCBieSB0aGUgc2FtZSBjb250cmFzdCB3aW5kb3csIHNvIGNoYW5naW5nIG1pbi9tYXggcmVzaGFwZXMgdGhlXG4gICAgLy8gc3VyZmFjZSAoYSBwaXhlbCdzIGhlaWdodCA9IGl0cyBpbnRlbnNpdHkgd2l0aGluIFttaW4sbWF4XSkuIENlbnRlciBpdCBmb3IgdGhlIGF4ZXMgZ2l6bW8uIFRoZVxuICAgIC8vIHBsYW5lIGlzIGFscmVhZHkgZGVjaW1hdGVkIHRvIHRoZSBncmlkIGNhcCDihpIgc3RyaWRlIDEuXG4gICAgY29uc3QgelNjYWxlID0gU1VSRkFDRV9aX0FTUEVDVCAqIE1hdGgubWF4KHBsYW5lLndpZHRoLCBwbGFuZS5oZWlnaHQpO1xuICAgIGNvbnN0IHsgdmVydGljZXMsIGZhY2VzLCB2YWx1ZXMgfSA9IGhlaWdodEZpZWxkKHBsYW5lLmRhdGEsIHBsYW5lLndpZHRoLCBwbGFuZS5oZWlnaHQsIHtcbiAgICAgIHpTY2FsZSxcbiAgICAgIHpMaW1pdHM6IHdpbixcbiAgICAgIGNlbnRlcjogdHJ1ZSxcbiAgICB9KTtcblxuICAgIC8vIFByZXNlcnZlIHRoZSBvcmJpdCBjYW1lcmEgYWNyb3NzIGEgcmUtc2xpY2UgLyB3aW5kb3cgcmVidWlsZCDigJQgb25seSB0aGUgZmlyc3QgbW91bnQgZnJhbWVzLCBzb1xuICAgIC8vIHN0ZXBwaW5nIHRoZSBzdGFjayBvciBjaGFuZ2luZyB0aGUgd2luZG93IGtlZXBzIHRoZSBjdXJyZW50IHpvb20vcGFuL29yaWVudGF0aW9uLlxuICAgIGNvbnN0IGNhbSA9IHZpZXdlci5jYW1lcmEzZDtcbiAgICBjb25zdCBwcmVzZXJ2ZUNhbWVyYSA9IHRoaXMuc3VyZmFjZUxheWVyICE9IG51bGw7XG4gICAgY29uc3Qgc2F2ZWRUYXJnZXQgPSBjYW0udGFyZ2V0O1xuICAgIGNvbnN0IHNhdmVkRGlzdGFuY2UgPSBjYW0uZGlzdGFuY2U7XG4gICAgaWYgKHRoaXMuc3VyZmFjZUxheWVyKSB7XG4gICAgICB2aWV3ZXIubGF5ZXJzLnJlbW92ZSh0aGlzLnN1cmZhY2VMYXllcik7XG4gICAgICB0aGlzLnN1cmZhY2VMYXllciA9IG51bGw7XG4gICAgfVxuICAgIHRoaXMuc3VyZmFjZUxheWVyID0gdmlld2VyLmFkZFN1cmZhY2UodmVydGljZXMsIGZhY2VzLCB2YWx1ZXMsIHtcbiAgICAgIGNvbG9ybWFwOiB0aGlzLnZvbHVtZUNvbG9ybWFwKHN0KSxcbiAgICAgIGNvbnRyYXN0TGltaXRzOiB3aW4sXG4gICAgICBnYW1tYTogc3Q/LmdhbW1hID8/IDEsXG4gICAgICB3aXJlZnJhbWU6IHRoaXMuc3VyZmFjZVdpcmVmcmFtZSxcbiAgICB9KTtcbiAgICBpZiAocHJlc2VydmVDYW1lcmEpIHtcbiAgICAgIGNhbS50YXJnZXQgPSBzYXZlZFRhcmdldDtcbiAgICAgIGNhbS5kaXN0YW5jZSA9IHNhdmVkRGlzdGFuY2U7XG4gICAgfVxuXG4gICAgdGhpcy5pbWFnZVcgPSBwbGFuZS53aWR0aDtcbiAgICB0aGlzLmltYWdlSCA9IHBsYW5lLmhlaWdodDtcbiAgICB0aGlzLnZvbHVtZURpbXMgPSB7IHdpZHRoOiBwbGFuZS53aWR0aCwgaGVpZ2h0OiBwbGFuZS5oZWlnaHQsIGRlcHRoOiBNYXRoLm1heCgxLCBNYXRoLnJvdW5kKHpTY2FsZSkpIH07XG4gICAgLy8gUmV1c2UgdGhlIHZvbHVtZSBpbnRlbnNpdHktaGlzdG9ncmFtIHBhdGg6IHRoZSBzbGljZSdzIHNjYWxhciBwbGFuZSBpcyB0aGUgaGlzdG9ncmFtIHNvdXJjZS5cbiAgICB0aGlzLnZvbHVtZUNoYW5uZWxEYXRhLmNsZWFyKCk7XG4gICAgdGhpcy52b2x1bWVDaGFubmVsRGF0YS5zZXQoMCwgcGxhbmUuZGF0YSk7XG4gICAgdGhpcy5zY2hlZHVsZVJlYWRiYWNrKCk7XG4gIH1cblxuICAvKiogU3Vic2NyaWJlIHRoZSBzdG9yZSBjb2xvcm1hcCAvIHJldmVyc2UgLyBpbnZlcnQgLyBjaGFubmVsIHdpbmRvdyDihpIgdGhlIHN1cmZhY2UsIHNvIGhpc3RvZ3JhbVxuICAgKiAgJiBjaGFubmVsLWRpYWxvZyBlZGl0cyB1cGRhdGUgaXQgbGl2ZSB3aXRob3V0IGEgcmUtZmV0Y2guICoqbWluL21heCByZXNoYXBlcyB0aGUgc3VyZmFjZSdzXG4gICAqICBoZWlnaHQqKiAoYSBwaXhlbCdzIGhlaWdodCA9IGl0cyBpbnRlbnNpdHkgd2l0aGluIFttaW4sbWF4XSksIHNvIGEgd2luZG93IGNoYW5nZSByZWJ1aWxkcyB0aGVcbiAgICogIG1lc2ggZ2VvbWV0cnkgKGZyb20gdGhlIGNhY2hlZCBzbGljZSk7IGNvbG91ci1vbmx5IGVkaXRzIChjb2xvcm1hcC9MVVQsIGdhbW1hLCByZXZlcnNlLCBpbnZlcnQpXG4gICAqICBqdXN0IHVwZGF0ZSB0aGUgbGF5ZXIncyB1bmlmb3Jtcy4gKi9cbiAgcHJpdmF0ZSBzdWJzY3JpYmVTdXJmYWNlRGlzcGxheVN0YXRlKCk6IHZvaWQge1xuICAgIHRoaXMuZGlzcGxheVN1Yj8udW5zdWJzY3JpYmUoKTtcbiAgICB0aGlzLmRpc3BsYXlTdWIgPSBjb21iaW5lTGF0ZXN0KFtcbiAgICAgIHRoaXMuc3RvcmUuZ2V0Q29sb3JtYXAoKSxcbiAgICAgIHRoaXMuc3RvcmUuZ2V0UmV2ZXJzZVNjYWxlKCksXG4gICAgICB0aGlzLnN0b3JlLmdldEludmVydCgpLFxuICAgICAgdGhpcy5zdG9yZS5nZXRDaGFubmVsU3RhdGVzKCksXG4gICAgXSkuc3Vic2NyaWJlKChbY29sb3JtYXAsIHJldmVyc2UsIGludmVydCwgY2hhbm5lbHNdKSA9PiB7XG4gICAgICB0aGlzLmN1cnJlbnRDb2xvcm1hcCA9IChjb2xvcm1hcCBhcyBDb2xvcm1hcE5vZGUpID8/IG51bGw7XG4gICAgICB0aGlzLmN1cnJlbnRSZXZlcnNlID0gcmV2ZXJzZTtcbiAgICAgIHRoaXMuaW52ZXJ0RW5hYmxlZCA9IGludmVydDtcbiAgICAgIGNvbnN0IGxheWVyID0gdGhpcy5zdXJmYWNlTGF5ZXI7XG4gICAgICBpZiAoIWxheWVyIHx8ICF0aGlzLnZpZXdlcikgcmV0dXJuO1xuICAgICAgY29uc3Qgc3QgPSB0aGlzLnN1cmZhY2VTdGF0ZShjaGFubmVscyk7XG4gICAgICBjb25zdCB3aW46IFtudW1iZXIsIG51bWJlcl0gPSBbc3Q/Lm1pbiA/PyAwLCBzdD8ubWF4ID8/IDI1NV07XG4gICAgICBjb25zdCB3aW5kb3dDaGFuZ2VkID1cbiAgICAgICAgIXRoaXMuc3VyZmFjZVdpbmRvdyB8fCB3aW5bMF0gIT09IHRoaXMuc3VyZmFjZVdpbmRvd1swXSB8fCB3aW5bMV0gIT09IHRoaXMuc3VyZmFjZVdpbmRvd1sxXTtcbiAgICAgIGlmICh3aW5kb3dDaGFuZ2VkKSB7XG4gICAgICAgIC8vIEhlaWdodCBmb2xsb3dzIHRoZSBjb250cmFzdCB3aW5kb3cg4oaSIHJlYnVpbGQgdGhlIG1lc2ggZm9yIHRoZSBuZXcgW21pbixtYXhdIChjYW1lcmEga2VwdCkuXG4gICAgICAgIHZvaWQgdGhpcy5idWlsZFN1cmZhY2UodGhpcy52aWV3ZXIsIHRoaXMubG9hZGVkPy56ID8/IDApLmNhdGNoKChlcnIpID0+XG4gICAgICAgICAgY29uc29sZS5lcnJvcignW25hcGFyaS1qc10gc3VyZmFjZSB3aW5kb3cgcmVidWlsZCBmYWlsZWQ6JywgZXJyKSxcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgLy8gQ29sb3VyLW9ubHkgY2hhbmdlOiB1cGRhdGUgdW5pZm9ybXMgaW4gcGxhY2UsIG5vIGdlb21ldHJ5IHJlYnVpbGQuXG4gICAgICBsYXllci5jb2xvcm1hcCA9IHRoaXMudm9sdW1lQ29sb3JtYXAoc3QpO1xuICAgICAgaWYgKHN0KSB7XG4gICAgICAgIGxheWVyLmNvbnRyYXN0TGltaXRzID0gd2luO1xuICAgICAgICBsYXllci5nYW1tYSA9IHN0LmdhbW1hO1xuICAgICAgfVxuICAgICAgdGhpcy52aWV3ZXIucmVxdWVzdFJlbmRlcigpO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBmaXRDYW1lcmFTb29uKCk6IHZvaWQge1xuICAgIGNvbnN0IHJ1biA9ICgpOiB2b2lkID0+IHtcbiAgICAgIGlmICh0aGlzLnZpZXdlciAmJiB0aGlzLmNhbnZhcyAmJiB0aGlzLmltYWdlVyA+IDAgJiYgdGhpcy5pbWFnZUggPiAwKSB7XG4gICAgICAgIHRoaXMudmlld2VyLmNhbWVyYS5maXQoXG4gICAgICAgICAgdGhpcy5pbWFnZVcsXG4gICAgICAgICAgdGhpcy5pbWFnZUgsXG4gICAgICAgICAgdGhpcy5jYW52YXMuY2xpZW50V2lkdGggfHwgdGhpcy5pbWFnZVcsXG4gICAgICAgICAgdGhpcy5jYW52YXMuY2xpZW50SGVpZ2h0IHx8IHRoaXMuaW1hZ2VILFxuICAgICAgICApO1xuICAgICAgfVxuICAgIH07XG4gICAgaWYgKHR5cGVvZiByZXF1ZXN0QW5pbWF0aW9uRnJhbWUgPT09ICdmdW5jdGlvbicpIHJlcXVlc3RBbmltYXRpb25GcmFtZShydW4pO1xuICAgIGVsc2UgcnVuKCk7XG4gIH1cblxuICAvKiogQGRlcHJlY2F0ZWQgUGxvdGx5LXNwZWNpZmljOyBob3N0IHJlLWRyaXZlcyBwbG90KCkgZnJvbSBpdHMgaW1hZ2Ugc3RyZWFtLiAqL1xuICByZWxvYWRBbmRQbG90KCk6IHZvaWQge1xuICAgIC8qIG5vLW9wICovXG4gIH1cblxuICByZXNldCgpOiB2b2lkIHtcbiAgICB0aGlzLmxvYWRUb2tlbisrOyAvLyBpbnZhbGlkYXRlIGFueSBpbi1mbGlnaHQgZnJhbWUgbG9hZGluZyBmcm9tIHRoZSBwcmV2aW91cyBwbG90XG4gICAgdGhpcy5kaXNwbGF5U3ViPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMuZGlzcGxheVN1YiA9IG51bGw7XG4gICAgdGhpcy5zY2FsZUJhcj8uZGVzdHJveSgpO1xuICAgIHRoaXMuc2NhbGVCYXIgPSBudWxsO1xuICAgIHRoaXMucmVnaW9uT3ZlcmxheT8uZGVzdHJveSgpO1xuICAgIHRoaXMucmVnaW9uT3ZlcmxheSA9IG51bGw7XG4gICAgdGhpcy5heGVzTGFiZWxzPy5kZXN0cm95KCk7XG4gICAgdGhpcy5heGVzTGFiZWxzID0gbnVsbDtcbiAgICB0aGlzLnpIYW5kbGU/LmRlc3Ryb3koKTtcbiAgICB0aGlzLnpIYW5kbGUgPSBudWxsO1xuICAgIHRoaXMudm9sdW1lV29ybGRCYXNlID0gbnVsbDtcbiAgICB0aGlzLmNhY2hlZEltYWdlID0gbnVsbDtcbiAgICB0aGlzLmNhY2hlZEltYWdlU291cmNlID0gbnVsbDtcbiAgICB0aGlzLmxhc3RQaXhlbHNSZWN0ID0gbnVsbDtcbiAgICB0aGlzLm5hdGl2ZUhpc3RvZ3JhbXMuY2xlYXIoKTtcbiAgICB0aGlzLnRpbGVkID0gZmFsc2U7XG4gICAgdGhpcy5oaXN0U2FtcGxlcy5jbGVhcigpO1xuICAgIGlmICh0aGlzLnJlYWRiYWNrVGltZXIgIT0gbnVsbCkge1xuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMucmVhZGJhY2tUaW1lcik7XG4gICAgICB0aGlzLnJlYWRiYWNrVGltZXIgPSBudWxsO1xuICAgIH1cbiAgICB0aGlzLmNhbWVyYVJlYWRiYWNrT2ZmPy4oKTtcbiAgICB0aGlzLmNhbWVyYVJlYWRiYWNrT2ZmID0gbnVsbDtcbiAgICB0aGlzLmNoYW5uZWxWaWV3ID0gbnVsbDtcbiAgICB0aGlzLnZpZXdlcj8uZGlzcG9zZSgpO1xuICAgIHRoaXMudmlld2VyID0gbnVsbDtcbiAgICBpZiAodGhpcy5jYW52YXMgJiYgdGhpcy5ob3N0Py5jb250YWlucyh0aGlzLmNhbnZhcykpIHRoaXMuaG9zdC5yZW1vdmVDaGlsZCh0aGlzLmNhbnZhcyk7XG4gICAgdGhpcy5jYW52YXMgPSBudWxsO1xuICAgIHRoaXMubGFzdFBpeGVscyA9IG51bGw7XG4gICAgdGhpcy52b2x1bWVWaWV3ID0gbnVsbDtcbiAgICB0aGlzLnZvbHVtZU11bHRpY2hhbm5lbCA9IGZhbHNlO1xuICAgIHRoaXMuc3VyZmFjZUxheWVyID0gbnVsbDtcbiAgICB0aGlzLnN1cmZhY2VDaGFubmVsID0gdW5kZWZpbmVkO1xuICAgIHRoaXMuc3VyZmFjZVBsYW5lcy5jbGVhcigpO1xuICAgIHRoaXMuc3VyZmFjZVdpbmRvdyA9IG51bGw7XG4gICAgdGhpcy5zY2F0dGVyUmVnaW9uU3ViPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMuc2NhdHRlclJlZ2lvblN1YiA9IG51bGw7XG4gICAgdGhpcy5zY2F0dGVyMmRQb2ludHMgPSBudWxsO1xuICAgIHRoaXMuc2NhdHRlcjNkTGF5ZXIgPSBudWxsO1xuICAgIHRoaXMuYXhlc0xheWVyID0gbnVsbDtcbiAgICB0aGlzLnZvbHVtZURpbXMgPSBudWxsO1xuICAgIHRoaXMudm9sdW1lQ2hhbm5lbERhdGEuY2xlYXIoKTtcbiAgfVxuXG4gIHJlbGF5b3V0KF90cnVlSW1hZ2VTaXplPzogbnVtYmVyW10pOiB2b2lkIHtcbiAgICB0aGlzLnZpZXdlcj8ucmVxdWVzdFJlbmRlcigpO1xuICB9XG5cbiAgLyoqIEBkZXByZWNhdGVkIFBsb3RseS1zcGVjaWZpYyBheGlzIHJlc2V0LiAqL1xuICByZXNldEF4ZXMoKTogdm9pZCB7XG4gICAgdGhpcy5maXRDYW1lcmFTb29uKCk7XG4gIH1cblxuICAvKiogQGRlcHJlY2F0ZWQgUGxvdGx5LXNwZWNpZmljIGF1dG9zY2FsZS4gKi9cbiAgYXV0b3NjYWxlKCk6IHZvaWQge1xuICAgIHRoaXMuZml0Q2FtZXJhU29vbigpO1xuICAgIHRoaXMuYXV0b3NjYWxlRXZlbnQkLm5leHQodW5kZWZpbmVkKTtcbiAgfVxuXG4gIHpvb21JbigpOiB2b2lkIHtcbiAgICBpZiAodGhpcy52aWV3ZXIpIHRoaXMudmlld2VyLmNhbWVyYS56b29tID0gdGhpcy52aWV3ZXIuY2FtZXJhLnpvb20gKiAxLjM7XG4gIH1cblxuICB6b29tT3V0KCk6IHZvaWQge1xuICAgIGlmICh0aGlzLnZpZXdlcikgdGhpcy52aWV3ZXIuY2FtZXJhLnpvb20gPSB0aGlzLnZpZXdlci5jYW1lcmEuem9vbSAvIDEuMztcbiAgfVxuXG4gIHNldERyYWdNb2RlKF9tb2RlOiBzdHJpbmcgfCBmYWxzZSk6IHZvaWQge1xuICAgIC8vIE5vLW9wOiBwYW4gYW5kIHpvb20gYXJlIGFsd2F5cyB0aGUgbmFwYXJpIGNhbWVyYSdzIGRlZmF1bHQgZ2VzdHVyZXMsIGFuZCBwYW4vem9vbSBnYXRpbmcgZm9yXG4gICAgLy8gZHJhd2luZyBpcyBvd25lZCBieSB0aGUgcmVnaW9uIG92ZXJsYXkncyBzZXRNb2RlIChzZXRDb250cm9sc0VuYWJsZWQpLiBUb2dnbGluZyBjb250cm9scyBoZXJlXG4gICAgLy8gd291bGQgZmlnaHQgdGhhdCDigJQgdGhlIGhvc3QgY2FsbHMgc2V0RHJhZ01vZGUoZmFsc2UpIGFsb25nc2lkZSBvdmVybGF5LnNldE1vZGUoPHRvb2w+KS5cbiAgfVxuXG4gIHNldE5hdmlnYXRvclZpc2libGUoX3Zpc2libGU6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICAvKiBuYXBhcmktanMgaGFzIG5vIG1pbmltYXA7IG5vLW9wICovXG4gIH1cblxuICBzZXRJbWFnZVNtb290aGluZ0VuYWJsZWQoZW5hYmxlZDogYm9vbGVhbik6IHZvaWQge1xuICAgIHRoaXMuaW1hZ2VTbW9vdGhpbmcgPSBlbmFibGVkO1xuICAgIC8vIEFwcGx5IGxpdmUgdG8gdGhlIHJlbmRlcmVkIGltYWdlIGxheWVyczsgYmFrZWQgaW50byB0aGUgbmV4dCByZW5kZXIgdG9vLlxuICAgIHRoaXMuY2hhbm5lbFZpZXc/LnNldEludGVycG9sYXRpb24oZW5hYmxlZCA/ICdsaW5lYXInIDogJ25lYXJlc3QnKTtcbiAgfVxuXG4gIHNldFNob3dTdGFjayhfc2hvd3N0YWNrOiBib29sZWFuKTogdm9pZCB7XG4gICAgLyogc3RhY2sgbmF2aWdhdGVkIHZpYSBzZXRaSW5kZXggKi9cbiAgfVxuXG4gIHNldFpJbmRleCh6SW5kZXg6IG51bWJlcik6IHZvaWQge1xuICAgIGlmICh0aGlzLmxvYWRlZCkgdGhpcy5sb2FkZWQueiA9IHpJbmRleDtcbiAgICBjb25zdCB2ID0gdGhpcy52aWV3ZXI7XG4gICAgaWYgKCF2KSByZXR1cm47XG4gICAgLy8gU3VyZmFjZTogb25lIHNsaWNlIOKGkiBvbmUgbWVzaCwgc28gcmUtYnVpbGQgdGhlIGhlaWdodCBmaWVsZCBmb3IgdGhlIG5ldyBzbGljZS5cbiAgICBpZiAodGhpcy5zdXJmYWNlTGF5ZXIpIHtcbiAgICAgIHZvaWQgdGhpcy5idWlsZFN1cmZhY2UodiwgekluZGV4KS5jYXRjaCgoZXJyKSA9PlxuICAgICAgICBjb25zb2xlLmVycm9yKCdbbmFwYXJpLWpzXSBzZXRaSW5kZXggc3VyZmFjZSBmYWlsZWQ6JywgZXJyKSxcbiAgICAgICk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIFZvbHVtZSAvIGlzb3N1cmZhY2U6IHN0ZXAgdGhlIHZvbHVtZSdzIHogcGxhbmUgaW4gcGxhY2UuXG4gICAgaWYgKHRoaXMudm9sdW1lVmlldykge1xuICAgICAgdi5kaW1zLnogPSB6SW5kZXg7XG4gICAgICB0aGlzLnNjaGVkdWxlUmVhZGJhY2soKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gVGlsZWQgMkQgaW1hZ2U6IGp1c3QgbW92ZSB0aGUgZGltcyBwbGFuZSDigJQgdGhlIHRpbGVkIHZpc3VhbCBmZXRjaGVzIHRoZSBuZXcgc2xpY2UncyB0aWxlc1xuICAgIC8vIChjYWNoZWQgcGVyIHopLCBubyBsYXllciByZWJ1aWxkLiBSZWZyZXNoIHRoZSBjb2Fyc2UgaGlzdG9ncmFtIHNhbXBsZSBmb3IgdGhlIG5ldyBzbGljZS5cbiAgICBpZiAodGhpcy50aWxlZCkge1xuICAgICAgdi5kaW1zLnogPSB6SW5kZXg7XG4gICAgICBpZiAodGhpcy5kZXNjcmlwdG9yKSB2b2lkIHRoaXMucmVmcmVzaEhpc3RvZ3JhbVNhbXBsZXMoekluZGV4LCB0aGlzLmRlc2NyaXB0b3IpO1xuICAgICAgdGhpcy5zY2hlZHVsZVJlYWRiYWNrKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIDJEIGltYWdlIChzdGl0Y2ggZmFsbGJhY2spOiByZS1yZW5kZXIgdGhlIHNsaWNlIChyZS1mZXRjaGVzIHBlci1jaGFubmVsIC8gY29tcG9zaXRlKS4gQnJhbmNoXG4gICAgLy8gb2YgYSB2b2x1bWUgbGF5ZXIgKG5vdCBjdXJyZW50UGxvdFR5cGUsIHdoaWNoIGNvdWxkIGJlIHN0YWxlIGFuZCBzaWxlbnRseSBuby1vcCB0aGUgc3dhcCkuXG4gICAgLy8gVGhlIHRva2VuIGxldHMgcmVuZGVySW1hZ2UgZHJvcCBhIHN1cGVyc2VkZWQgc2NydWIgc28gYSBzbG93IG9sZGVyIHNsaWNlIGNhbid0IGNsb2JiZXIgYSBuZXdlciBvbmUuXG4gICAgY29uc3QgcmVxID0gKyt0aGlzLnNsaWNlUmVxO1xuICAgIHZvaWQgdGhpcy5yZW5kZXJJbWFnZSh6SW5kZXgsIHJlcSlcbiAgICAgIC50aGVuKCgpID0+IHtcbiAgICAgICAgaWYgKHJlcSA9PT0gdGhpcy5zbGljZVJlcSkgdGhpcy5zY2hlZHVsZVJlYWRiYWNrKCk7XG4gICAgICB9KVxuICAgICAgLmNhdGNoKChlcnIpID0+IGNvbnNvbGUuZXJyb3IoJ1tuYXBhcmktanNdIHNldFpJbmRleCBzbGljZSBmYWlsZWQ6JywgZXJyKSk7XG4gIH1cblxuICBzZXRTdGFja0xvYWRpbmcoc3RhY2tMb2FkaW5nOiBib29sZWFuKTogdm9pZCB7XG4gICAgdGhpcy5zdGFja0xvYWRpbmckLm5leHQoc3RhY2tMb2FkaW5nKTtcbiAgfVxuXG4gIC8qKiBDYW5jZWwgaW4tZmxpZ2h0IGZyYW1lIGxvYWRpbmc6IGJ1bXAgdGhlIGxvYWQgZ2VuZXJhdGlvbiBzbyB0aGUgdm9sdW1lLWFzc2VtYmx5IC8gc3VyZmFjZVxuICAgKiAgcHJlbG9hZCB3b3JrZXJzIHN0b3AgZmV0Y2hpbmcgbW9yZSBmcmFtZXMsIGFuZCBjbGVhciB0aGUgbG9hZGluZyBmbGFnICsgcHJvZ3Jlc3MuICovXG4gIGNhbmNlbExvYWRpbmcoKTogdm9pZCB7XG4gICAgdGhpcy5sb2FkVG9rZW4rKztcbiAgICB0aGlzLnN0YWNrTG9hZGluZyQubmV4dChmYWxzZSk7XG4gICAgdGhpcy5zdGFja0xvYWRpbmdQcm9ncmVzcyQubmV4dCgwKTtcbiAgfVxuXG4gIGlzU3RhY2tMb2FkaW5nKCk6IE9ic2VydmFibGU8Ym9vbGVhbj4ge1xuICAgIHJldHVybiB0aGlzLnN0YWNrTG9hZGluZyQuYXNPYnNlcnZhYmxlKCk7XG4gIH1cblxuICBnZXRTdGFja0xvYWRpbmdQcm9ncmVzcygpOiBPYnNlcnZhYmxlPG51bWJlcj4ge1xuICAgIHJldHVybiB0aGlzLnN0YWNrTG9hZGluZ1Byb2dyZXNzJC5hc09ic2VydmFibGUoKTtcbiAgfVxuXG4gIGdldFRydWVJbWFnZVNpemUoKTogeyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlciB9IHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMuaW1hZ2VXID4gMCAmJiB0aGlzLmltYWdlSCA+IDAgPyB7IHdpZHRoOiB0aGlzLmltYWdlVywgaGVpZ2h0OiB0aGlzLmltYWdlSCB9IDogbnVsbDtcbiAgfVxuXG4gIGdldEN1cnJlbnRJbWFnZSgpOiBQcm9taXNlPEltYWdlIHwgbnVsbD4ge1xuICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUobnVsbCk7XG4gIH1cblxuICBnZXREaXNwbGF5ZWRQaXhlbERhdGEoKTogUGl4ZWxEYXRhIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMubGFzdFBpeGVscztcbiAgfVxuXG4gIGdldERpc3BsYXllZFNvdXJjZVJlY3QoKTogeyB4OiBudW1iZXI7IHk6IG51bWJlcjsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfSB8IG51bGwge1xuICAgIGNvbnN0IHYgPSB0aGlzLnZpZXdlcjtcbiAgICBjb25zdCBzaXplID0gdGhpcy5nZXRUcnVlSW1hZ2VTaXplKCk7XG4gICAgaWYgKCF2IHx8ICFzaXplKSByZXR1cm4gbnVsbDtcbiAgICBjb25zdCByID0gdi52aXNpYmxlV29ybGRSZWN0KCk7XG4gICAgY29uc3QgeCA9IE1hdGgubWF4KDAsIHIueCk7XG4gICAgY29uc3QgeSA9IE1hdGgubWF4KDAsIHIueSk7XG4gICAgcmV0dXJuIHtcbiAgICAgIHgsXG4gICAgICB5LFxuICAgICAgd2lkdGg6IE1hdGgubWluKHNpemUud2lkdGgsIHIueCArIHIud2lkdGgpIC0geCxcbiAgICAgIGhlaWdodDogTWF0aC5taW4oc2l6ZS5oZWlnaHQsIHIueSArIHIuaGVpZ2h0KSAtIHksXG4gICAgfTtcbiAgfVxuXG4gIGRvd25sb2FkSW1hZ2UoKTogdm9pZCB7XG4gICAgdm9pZCB0aGlzLmV4cG9ydENvbXBvc2l0ZSgpO1xuICB9XG5cbiAgc2V0UGxvdFR5cGUocGxvdFR5cGU6IFBsb3RUeXBlKTogdm9pZCB7XG4gICAgdGhpcy5jdXJyZW50UGxvdFR5cGUgPSBwbG90VHlwZTtcbiAgfVxuXG4gIC8qKiBNYXAgYSBQbG90bHktc3R5bGUgM0QgZHJhZyBtb2RlIG9udG8gbmFwYXJpLWpzJ3MgY2FtZXJhIGRyYWcgbW9kZS4gKi9cbiAgc2V0U3VyZmFjZURyYWdNb2RlKG1vZGU6IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICghdGhpcy52aWV3ZXIpIHJldHVybjtcbiAgICBjb25zdCBtID0gbW9kZSA9PT0gJ3BhbicgPyAncGFuJyA6IG1vZGUgPT09ICd6b29tJyA/ICd6b29tJyA6ICdyb3RhdGUnOyAvLyBvcmJpdC90dXJudGFibGUg4oaSIHJvdGF0ZVxuICAgIHRoaXMudmlld2VyLnNldENhbWVyYURyYWdNb2RlKG0pO1xuICB9XG5cbiAgLyoqIFJlLWZyYW1lIHRoZSAzRCBjYW1lcmEgb24gdGhlIGN1cnJlbnQgM0QgbGF5ZXIuIEEgc3VyZmFjZSBtZXNoIHNpdHMgaW4gdGhlIHBvc2l0aXZlIG9jdGFudFxuICAgKiAgKG5vdCBjZW50cmVkKSwgc28gZnJhbWUgaXQgYnkgaXRzIG93biBib3VuZHM7IGEgdm9sdW1lL2lzb3N1cmZhY2UgdXNlcyBpdHMgY2VudHJlZCBib3guICovXG4gIHJlc2V0U3VyZmFjZUNhbWVyYSgpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMudmlld2VyKSByZXR1cm47XG4gICAgY29uc3QgbWVzaExheWVyID0gdGhpcy5zdXJmYWNlTGF5ZXIgPz8gdGhpcy5zY2F0dGVyM2RMYXllcjtcbiAgICBpZiAobWVzaExheWVyKSB7XG4gICAgICBjb25zdCBiID0gbWVzaExheWVyLmJvdW5kcygpO1xuICAgICAgdGhpcy52aWV3ZXIuY2FtZXJhM2QudGFyZ2V0ID0gYi5jZW50ZXI7XG4gICAgICB0aGlzLnZpZXdlci5jYW1lcmEzZC5kaXN0YW5jZSA9IE1hdGgubWF4KGIucmFkaXVzICogMi41LCAxZS0zKTtcbiAgICAgIHRoaXMudmlld2VyLnJlcXVlc3RSZW5kZXIoKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgZCA9IHRoaXMudm9sdW1lRGltcztcbiAgICBpZiAoZCkgdGhpcy52aWV3ZXIuY2FtZXJhM2QuZnJhbWUoZC53aWR0aCwgZC5oZWlnaHQsIGQuZGVwdGgpO1xuICB9XG5cbiAgZ2V0QXV0b3NjYWxlRXZlbnQoKTogT2JzZXJ2YWJsZTx1bmtub3duPiB7XG4gICAgcmV0dXJuIHRoaXMuYXV0b3NjYWxlRXZlbnQkLmFzT2JzZXJ2YWJsZSgpO1xuICB9XG5cbiAgZ2V0UGxvdFR5cGVEZXNjcmlwdG9ycygpOiBQbG90VHlwZURlc2NyaXB0b3JbXSB7XG4gICAgLy8gVGhlIFdlYkdQVSBuYXBhcmktanMgb3B0aW9ucywgb2ZmZXJlZCBhbG9uZ3NpZGUgKG5vdCByZXBsYWNpbmcpIHRoZSBPU0QvUGxvdGx5IHR5cGVzLlxuICAgIHJldHVybiBbXG4gICAgICBQTE9UX1RZUEVfREVTQ1JJUFRPUlNbUGxvdFR5cGUuTkFQQVJJX0lNQUdFXSEsXG4gICAgICBQTE9UX1RZUEVfREVTQ1JJUFRPUlNbUGxvdFR5cGUuTkFQQVJJX1NDQVRURVJdISxcbiAgICAgIFBMT1RfVFlQRV9ERVNDUklQVE9SU1tQbG90VHlwZS5OQVBBUklfU1VSRkFDRV0hLFxuICAgICAgUExPVF9UWVBFX0RFU0NSSVBUT1JTW1Bsb3RUeXBlLk5BUEFSSV9TQ0FUVEVSM0RdISxcbiAgICAgIFBMT1RfVFlQRV9ERVNDUklQVE9SU1tQbG90VHlwZS5OQVBBUklfVk9MVU1FXSEsXG4gICAgICBQTE9UX1RZUEVfREVTQ1JJUFRPUlNbUGxvdFR5cGUuTkFQQVJJX0lTT1NVUkZBQ0VdISxcbiAgICBdO1xuICB9XG5cbiAgLyoqIFRoZSBuYXBhcmkgM0QgZGVjaW1hdGUgZmFjdG9yICgxID0gZnVsbCDigKYgOCA9IOKFmykuIFJlYWQgYnkgdGhlIHRvb2xiYXIgdG8gaW5pdCB0aGUgY29udHJvbC4gKi9cbiAgZ2V0UmVzb2x1dGlvblNjYWxlKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMucmVzb2x1dGlvblNjYWxlO1xuICB9XG5cbiAgLyoqIFNldCB0aGUgZGVjaW1hdGUgZmFjdG9yIGZvciB0aGUgbmFwYXJpIDNEIHR5cGVzLiBUYWtlcyBlZmZlY3Qgb24gdGhlIG5leHQgKHJlKWxvYWQg4oCUIHRoZSBob3N0XG4gICAqICByZS1wbG90cyBhZnRlciBjYWxsaW5nIHRoaXMsIHNpbmNlIGRlY2ltYXRpb24gY2hhbmdlcyB0aGUgZmV0Y2hlZC9hc3NlbWJsZWQgZGF0YS4gKi9cbiAgc2V0UmVzb2x1dGlvblNjYWxlKHNjYWxlOiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLnJlc29sdXRpb25TY2FsZSA9IE1hdGgubWF4KDEsIE1hdGgucm91bmQoc2NhbGUpKTtcbiAgfVxuXG4gIGdldEludGVuc2l0eVByb2ZpbGUkKCk6IE9ic2VydmFibGU8SW50ZW5zaXR5UHJvZmlsZVtdPiB7XG4gICAgcmV0dXJuIHRoaXMuaW50ZW5zaXR5UHJvZmlsZSQuYXNPYnNlcnZhYmxlKCk7XG4gIH1cblxuICByZW5kZXJJbnRlbnNpdHlJbnNldChfZGl2SWQ6IHN0cmluZywgX3Byb2ZpbGVzOiBJbnRlbnNpdHlQcm9maWxlW10pOiB2b2lkIHtcbiAgICAvKiBQbG90bHkgb3ducyB0aGUgaW50ZW5zaXR5IGluc2V0ICovXG4gIH1cblxuICAvKiogUmVhZCB0aGUgZGlzcGxheWVkIGNvbXBvc2l0ZSBpbnRvIGBsYXN0UGl4ZWxzYCAodGhlIHBpeGVsLXRvb2xzJyBzb3VyY2UpICsgZW1pdCB0aGUgY2xhbXBlZFxuICAgKiAgdmlzaWJsZSByZWdpb24uIEEgZnJlc2ggUGl4ZWxEYXRhIG9iamVjdCBtZWFucyBjYWNoZWRJbWFnZURhdGEoKSByZWJ1aWxkcyBhdXRvbWF0aWNhbGx5LiAqL1xuICBwcml2YXRlIGFzeW5jIHJ1blJlYWRiYWNrKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHYgPSB0aGlzLnZpZXdlcjtcbiAgICBpZiAoIXYpIHJldHVybjtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcHggPSBhd2FpdCB2LnJlYWREaXNwbGF5ZWRQaXhlbHMoKTtcbiAgICAgIGlmICh0aGlzLnZpZXdlciAhPT0gdikgcmV0dXJuO1xuICAgICAgY29uc3QgcmVjdCA9IHYudmlzaWJsZVdvcmxkUmVjdCgpOyAvLyBjYXB0dXJlIFdJVEggdGhlIHBpeGVscyAoc2FtZSBjYW1lcmEpXG4gICAgICB0aGlzLmxhc3RQaXhlbHMgPSBweDtcbiAgICAgIHRoaXMubGFzdFBpeGVsc1JlY3QgPSByZWN0O1xuICAgICAgY29uc3Qgc2l6ZSA9IHRoaXMuZ2V0VHJ1ZUltYWdlU2l6ZSgpO1xuICAgICAgaWYgKHNpemUgJiYgcmVjdCkge1xuICAgICAgICBjb25zdCB4ID0gTWF0aC5tYXgoMCwgTWF0aC5taW4oc2l6ZS53aWR0aCwgcmVjdC54KSk7XG4gICAgICAgIGNvbnN0IHkgPSBNYXRoLm1heCgwLCBNYXRoLm1pbihzaXplLmhlaWdodCwgcmVjdC55KSk7XG4gICAgICAgIGNvbnN0IHdpZHRoID0gTWF0aC5tYXgoMSwgTWF0aC5taW4oc2l6ZS53aWR0aCAtIHgsIHJlY3Qud2lkdGgpKTtcbiAgICAgICAgY29uc3QgaGVpZ2h0ID0gTWF0aC5tYXgoMSwgTWF0aC5taW4oc2l6ZS5oZWlnaHQgLSB5LCByZWN0LmhlaWdodCkpO1xuICAgICAgICB0aGlzLnZpZXdwb3J0Q2hhbmdlJC5uZXh0KHsgeCwgeSwgd2lkdGgsIGhlaWdodCB9KTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIHtcbiAgICAgIC8qIHJlYWRiYWNrIHVuYXZhaWxhYmxlICovXG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzY2hlZHVsZVJlYWRiYWNrKCk6IHZvaWQge1xuICAgIGlmICghdGhpcy52aWV3ZXIpIHJldHVybjtcbiAgICBpZiAodHlwZW9mIHJlcXVlc3RBbmltYXRpb25GcmFtZSA9PT0gJ2Z1bmN0aW9uJykgcmVxdWVzdEFuaW1hdGlvbkZyYW1lKCgpID0+IHZvaWQgdGhpcy5ydW5SZWFkYmFjaygpKTtcbiAgICBlbHNlIHNldFRpbWVvdXQoKCkgPT4gdm9pZCB0aGlzLnJ1blJlYWRiYWNrKCksIDApO1xuICB9XG5cbiAgLyoqIERlYm91bmNlZCByZWFkYmFjayDigJQgYXJtZWQgb24gY2FtZXJhIGNoYW5nZXMgc28gYGxhc3RQaXhlbHNgIHRyYWNrcyB0aGUgY3VycmVudCB2aWV3IGFmdGVyIGFcbiAgICogIHBhbi96b29tIHNldHRsZXMgKGFuZCBhZnRlciB0aWxlZCBsZXZlbHMgZmluaXNoIGxvYWRpbmcpLCB3aGljaCB0aGUgb24tY2FudmFzIHBpeGVsIHRvb2xzXG4gICAqICAod2FuZC9icnVzaC9TQU0pIHJlYWQgc3luY2hyb25vdXNseS4gQ29hbGVzY2VzIHJhcGlkIGNoYW5nZXMuICovXG4gIHByaXZhdGUgYXJtUmVhZGJhY2soZGVsYXlNcyA9IDI1MCk6IHZvaWQge1xuICAgIGlmICh0aGlzLnJlYWRiYWNrVGltZXIgIT0gbnVsbCkgY2xlYXJUaW1lb3V0KHRoaXMucmVhZGJhY2tUaW1lcik7XG4gICAgdGhpcy5yZWFkYmFja1RpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICB0aGlzLnJlYWRiYWNrVGltZXIgPSBudWxsO1xuICAgICAgdm9pZCB0aGlzLnJ1blJlYWRiYWNrKCk7XG4gICAgfSwgZGVsYXlNcyk7XG4gIH1cblxuICAvLyDilIDilIAgSVJlZ2lvblN0b3JlICsgY2xhc3NpZmljYXRpb24gY29sb3VycyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgLy8gSW5oZXJpdGVkIGZyb20gQmFzZVN0b3JlVmlzdWFsaXplciDigJQgcHVyZSBkZWxlZ2F0aW9ucyB0byB0aGUgc2hhcmVkXG4gIC8vIFJlZ2lvblN0b3JlIC8gVmlzdWFsaXplclN0b3JlIChpZGVudGljYWwgdG8gdGhlIE9TRCBiYWNrZW5kKS5cblxuICAvLyDilIDilIAgUGl4ZWwgdG9vbHM6IHJldXNlIHRoZSBzaGFyZWQsIGJhY2tlbmQtYWdub3N0aWMgdG9vbCBzZXJ2aWNlcyB3aXRoIGEgbmFwYXJpIGhvc3Qg4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLyoqIEJ1aWxkIHRoZSBjb29yZGluYXRlIHRyYW5zZm9ybSArIHRvb2wgaG9zdHMgZm9yIHRoZSBjdXJyZW50IHZpZXdlciAoY2FsbGVkIGZyb20gcGxvdCgpKS4gVGhlXG4gICAqICBwaXhlbCB0b29scyByZWFkIGRpc3BsYXllZCBwaXhlbHMgc3luY2hyb25vdXNseSBmcm9tIHRoZSBsYXN0IHJlYWRiYWNrIGFuZCBjb252ZXJ0IHBvaW50ZXJcbiAgICogIGNvb3JkcyB2aWEgdGhlIG5hcGFyaSBjYW1lcmEsIGV4YWN0bHkgbWlycm9yaW5nIHRoZSBPU0QgaG9zdC4gKi9cbiAgcHJpdmF0ZSBidWlsZFRvb2xIb3N0cygpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMudmlld2VyKSByZXR1cm47XG4gICAgdGhpcy5jb29yZFRyYW5zZm9ybSA9IG5ldyBOYXBhcmlDb29yZGluYXRlVHJhbnNmb3JtKFxuICAgICAgdGhpcy52aWV3ZXIsXG4gICAgICAoKSA9PiAhIXRoaXMudmlld2VyICYmIHRoaXMuaW1hZ2VXID4gMCxcbiAgICApO1xuICAgIGNvbnN0IHJlZ2lvbnMgPSB0aGlzLnJlZ2lvblN0b3JlO1xuICAgIHRoaXMud2FuZEhvc3QgPSB7XG4gICAgICBnZXRSZWdpb25zOiAoKSA9PiByZWdpb25zLmdldFJlZ2lvbnMoKSxcbiAgICAgIHNldFJlZ2lvbnM6IChyKSA9PiByZWdpb25zLnNldFJlZ2lvbnMociksXG4gICAgICBnZXRDYWNoZWRJbWFnZURhdGE6ICgpID0+IHRoaXMuY2FjaGVkSW1hZ2VEYXRhKCksXG4gICAgICBnZXRBY3RpdmVGcmFtZUluZGV4OiAoKSA9PiB0aGlzLmxvYWRlZD8ueiA/PyAwLFxuICAgICAgZ2V0T3ZlcmxheUNvbnRhaW5lcjogKCkgPT4gdGhpcy5ob3N0LFxuICAgICAgZ2V0Q29vcmRpbmF0ZVRyYW5zZm9ybTogKCkgPT4gdGhpcy5jb29yZFRyYW5zZm9ybSBhcyBJQ29vcmRpbmF0ZVRyYW5zZm9ybSxcbiAgICAgIGdldEZpbGVOYW1lOiAoKSA9PiB0aGlzLmxvYWRlZD8uZmlsZW5hbWUsXG4gICAgICBnZXRTaGFwZUNvbG9yOiAoKSA9PiByZWdpb25zLmdldFNoYXBlQ29sb3IoKSxcbiAgICB9O1xuICAgIHRoaXMuZXJhc2VySG9zdCA9IHtcbiAgICAgIGdldFJlZ2lvbnM6ICgpID0+IHJlZ2lvbnMuZ2V0UmVnaW9ucygpLFxuICAgICAgc2V0UmVnaW9uczogKHIpID0+IHJlZ2lvbnMuc2V0UmVnaW9ucyhyKSxcbiAgICAgIGludmFsaWRhdGVXYW5kUmVnaW9uOiAoKSA9PiB0aGlzLndhbmRUb29sLmNsZWFyQWN0aXZlUmVnaW9uKCksXG4gICAgICBnZXRPdmVybGF5Q29udGFpbmVyOiAoKSA9PiB0aGlzLmhvc3QsXG4gICAgICBnZXRDb29yZGluYXRlVHJhbnNmb3JtOiAoKSA9PiB0aGlzLmNvb3JkVHJhbnNmb3JtIGFzIElDb29yZGluYXRlVHJhbnNmb3JtLFxuICAgICAgZ2V0Q2FjaGVkSW1hZ2VSYXRpbzogKCkgPT4gdGhpcy5jYWNoZWRJbWFnZURhdGEoKT8ucmF0aW9zWzBdID8/IDEsXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgem9vbUJveEhvc3QoKTogWm9vbVRvQm94VG9vbEhvc3Qge1xuICAgIHJldHVybiB7XG4gICAgICBnZXRQbG90RGl2OiAoKSA9PiB0aGlzLnBsb3REaXZJZCxcbiAgICAgIHBpeGVsVG9EYXRhOiAocHgsIHB5KSA9PiB7XG4gICAgICAgIGNvbnN0IHJlY3QgPSB0aGlzLmhvc3Q/LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICBpZiAoIXRoaXMudmlld2VyIHx8ICFyZWN0KSByZXR1cm4geyB4OiAwLCB5OiAwIH07XG4gICAgICAgIGNvbnN0IFt4LCB5XSA9IHRoaXMudmlld2VyLmNhbnZhc1RvV29ybGQocmVjdC5sZWZ0ICsgcHgsIHJlY3QudG9wICsgcHkpO1xuICAgICAgICByZXR1cm4geyB4LCB5IH07XG4gICAgICB9LFxuICAgICAgYXBwbHlab29tVG9Cb3g6IChjb29yZHMpID0+IHRoaXMuYXBwbHlab29tVG9Cb3goY29vcmRzKSxcbiAgICB9O1xuICB9XG5cbiAgLyoqIFpvb20vcGFuIHRoZSBjYW1lcmEgdG8gZml0IGEgZGF0YS1zcGFjZSByZWN0YW5nbGUgYFt4TWluLCB4TWF4LCB5TWF4LCB5TWluXWAuICovXG4gIHByaXZhdGUgYXBwbHlab29tVG9Cb3goY29vcmRpbmF0ZXM6IG51bWJlcltdKTogdm9pZCB7XG4gICAgY29uc3QgdiA9IHRoaXMudmlld2VyO1xuICAgIGlmICghdiB8fCAhdGhpcy5jYW52YXMgfHwgY29vcmRpbmF0ZXMubGVuZ3RoIDwgNCkgcmV0dXJuO1xuICAgIGNvbnN0IFt4TWluLCB4TWF4LCB5QSwgeUJdID0gY29vcmRpbmF0ZXM7XG4gICAgY29uc3QgeDAgPSBNYXRoLm1pbih4TWluLCB4TWF4KTtcbiAgICBjb25zdCB4MSA9IE1hdGgubWF4KHhNaW4sIHhNYXgpO1xuICAgIGNvbnN0IHkwID0gTWF0aC5taW4oeUEsIHlCKTtcbiAgICBjb25zdCB5MSA9IE1hdGgubWF4KHlBLCB5Qik7XG4gICAgY29uc3QgdyA9IE1hdGgubWF4KDEsIHgxIC0geDApO1xuICAgIGNvbnN0IGggPSBNYXRoLm1heCgxLCB5MSAtIHkwKTtcbiAgICBjb25zdCB2dyA9IHRoaXMuY2FudmFzLmNsaWVudFdpZHRoIHx8IHc7XG4gICAgY29uc3QgdmggPSB0aGlzLmNhbnZhcy5jbGllbnRIZWlnaHQgfHwgaDtcbiAgICB2LmNhbWVyYS5jZW50ZXIgPSBbKHgwICsgeDEpIC8gMiwgKHkwICsgeTEpIC8gMl07XG4gICAgdi5jYW1lcmEuem9vbSA9IE1hdGgubWluKHZ3IC8gdywgdmggLyBoKTtcbiAgICB2LnJlcXVlc3RSZW5kZXIoKTtcbiAgfVxuXG4gIC8qKiBCdWlsZCBDYWNoZWRJbWFnZURhdGEgZnJvbSB0aGUgbW9zdCByZWNlbnQgcmVhZGJhY2sgKFJHQkEgZGV2aWNlIHBpeGVscyDihpIgW3ldW3hdPVtyLGcsYl0pLFxuICAgKiAgd2l0aCByYXRpb3Mvb3JpZ2luIG1hcHBpbmcgaW1hZ2UgY29vcmRzIOKGlCByZWFkYmFjayBwaXhlbHMuIENhY2hlZCB1bnRpbCB0aGUgcmVhZGJhY2sgY2hhbmdlcy4gKi9cbiAgcHJpdmF0ZSBjYWNoZWRJbWFnZURhdGEoKTogQ2FjaGVkSW1hZ2VEYXRhIHwgbnVsbCB7XG4gICAgY29uc3QgcHggPSB0aGlzLmxhc3RQaXhlbHM7XG4gICAgaWYgKCFweCB8fCAhdGhpcy52aWV3ZXIpIHJldHVybiBudWxsO1xuICAgIGlmICh0aGlzLmNhY2hlZEltYWdlICYmIHRoaXMuY2FjaGVkSW1hZ2VTb3VyY2UgPT09IHB4KSByZXR1cm4gdGhpcy5jYWNoZWRJbWFnZTtcbiAgICBjb25zdCB3ID0gcHgud2lkdGg7XG4gICAgY29uc3QgaCA9IHB4LmhlaWdodDtcbiAgICBjb25zdCBkYXRhID0gcHguZGF0YTtcbiAgICBjb25zdCBtYXRyaXg6IG51bWJlcltdW11bXSA9IG5ldyBBcnJheShoKTtcbiAgICBmb3IgKGxldCB5ID0gMDsgeSA8IGg7IHkrKykge1xuICAgICAgY29uc3Qgcm93OiBudW1iZXJbXVtdID0gbmV3IEFycmF5KHcpO1xuICAgICAgY29uc3QgYmFzZSA9IHkgKiB3ICogNDtcbiAgICAgIGZvciAobGV0IHggPSAwOyB4IDwgdzsgeCsrKSB7XG4gICAgICAgIGNvbnN0IG8gPSBiYXNlICsgeCAqIDQ7XG4gICAgICAgIHJvd1t4XSA9IFtkYXRhW29dLCBkYXRhW28gKyAxXSwgZGF0YVtvICsgMl1dO1xuICAgICAgfVxuICAgICAgbWF0cml4W3ldID0gcm93O1xuICAgIH1cbiAgICAvLyBVc2UgdGhlIHJlY3QgY2FwdHVyZWQgV0lUSCB0aGlzIHJlYWRiYWNrIChub3QgdGhlIGxpdmUgb25lKSBzbyByYXRpb3Mvb3JpZ2luIG1hdGNoIHRoZVxuICAgIC8vIG1hdHJpeCdzIGNhbWVyYSDigJQgb3RoZXJ3aXNlIGEgcGFuL3pvb20gc2luY2UgdGhlIHJlYWRiYWNrIG1pcy1zY2FsZXMgdGhlIHRyYWNlZCByZWdpb24uXG4gICAgY29uc3QgcmVjdCA9IHRoaXMubGFzdFBpeGVsc1JlY3QgPz8gdGhpcy52aWV3ZXIudmlzaWJsZVdvcmxkUmVjdCgpO1xuICAgIHRoaXMuY2FjaGVkSW1hZ2UgPSB7XG4gICAgICBmcmFtZXM6IFttYXRyaXhdLFxuICAgICAgd2lkdGg6IHcsXG4gICAgICBoZWlnaHQ6IGgsXG4gICAgICByYXRpb3M6IFtyZWN0LndpZHRoIC8gdywgcmVjdC5oZWlnaHQgLyBoXSxcbiAgICAgIGlzR3JheXNjYWxlOiBmYWxzZSxcbiAgICAgIG9yaWdpblg6IHJlY3QueCxcbiAgICAgIG9yaWdpblk6IHJlY3QueSxcbiAgICB9O1xuICAgIHRoaXMuY2FjaGVkSW1hZ2VTb3VyY2UgPSBweDtcbiAgICByZXR1cm4gdGhpcy5jYWNoZWRJbWFnZTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBJVG9vbENvbnRyb2xsZXIg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIC8vIENvbnRyb2wgZ2F0aW5nOiBhIHRvb2wgdGhhdCBBQ1RJVkFURVMgZGlzYWJsZXMgdGhlIGNhbWVyYSBjb250cm9sczsgZGVhY3RpdmF0aW9uIGlzIGEgbm8tb3BcbiAgLy8gYmVjYXVzZSB0aGUgaG9zdCBhbHdheXMgY2FsbHMgdGhlIHJlZ2lvbiBvdmVybGF5J3Mgc2V0TW9kZSBGSVJTVCBpbiBlYWNoIHRvZ2dsZSAod2hpY2ggc2V0c1xuICAvLyB0aGUgYmFzZWxpbmUgZW5hYmxlZC9kaXNhYmxlZCksIHNvIHJlLWVuYWJsaW5nIGhlcmUgd291bGQgZmlnaHQgYSBmcmVzaGx5LWFjdGl2YXRlZCBkcmF3IG1vZGUuXG4gIHNldFdhbmRNb2RlKGFjdGl2ZTogYm9vbGVhbiwgb3B0aW9ucz86IElXYW5kT3B0aW9ucyk6IHZvaWQge1xuICAgIGlmICghdGhpcy52aWV3ZXIpIHJldHVybjsgLy8gbm8gcGxvdCB5ZXQg4oaSIG5vdGhpbmcgdG8gZHJpdmVcbiAgICBpZiAoIWFjdGl2ZSkge1xuICAgICAgdGhpcy53YW5kVG9vbC5zZXRNb2RlKGZhbHNlKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy52aWV3ZXI/LnNldENvbnRyb2xzRW5hYmxlZChmYWxzZSk7XG4gICAgdGhpcy5jYWNoZWRJbWFnZVNvdXJjZSA9IG51bGw7XG4gICAgdGhpcy5hcm1SZWFkYmFjaygwKTtcbiAgICBpZiAodGhpcy53YW5kSG9zdCkgdGhpcy53YW5kVG9vbC5iaW5kSG9zdCh0aGlzLndhbmRIb3N0KTtcbiAgICB0aGlzLndhbmRUb29sLnNldE1vZGUodHJ1ZSwgKG9wdGlvbnMgPz8ge30pIGFzIHVua25vd24gYXMgV2FuZE9wdGlvbnMpO1xuICB9XG4gIHNldFdhbmRPcHRpb25zKG9wdGlvbnM6IElXYW5kT3B0aW9ucyk6IHZvaWQge1xuICAgIHRoaXMud2FuZFRvb2wuc2V0T3B0aW9ucygob3B0aW9ucyA/PyB7fSkgYXMgdW5rbm93biBhcyBXYW5kT3B0aW9ucyk7XG4gIH1cbiAgY2xlYXJBY3RpdmVXYW5kUmVnaW9uKCk6IHZvaWQge1xuICAgIHRoaXMud2FuZFRvb2wuY2xlYXJBY3RpdmVSZWdpb24oKTtcbiAgfVxuICBzZXRCcnVzaE1vZGUoYWN0aXZlOiBib29sZWFuLCBvcHRpb25zPzogSUJydXNoT3B0aW9ucyk6IHZvaWQge1xuICAgIGlmICghdGhpcy52aWV3ZXIpIHJldHVybjtcbiAgICBpZiAoIWFjdGl2ZSkge1xuICAgICAgdGhpcy5icnVzaFRvb2wuc2V0TW9kZShmYWxzZSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMudmlld2VyPy5zZXRDb250cm9sc0VuYWJsZWQoZmFsc2UpO1xuICAgIHRoaXMuY2FjaGVkSW1hZ2VTb3VyY2UgPSBudWxsO1xuICAgIHRoaXMuYXJtUmVhZGJhY2soMCk7XG4gICAgaWYgKHRoaXMud2FuZEhvc3QpIHRoaXMuYnJ1c2hUb29sLmJpbmRIb3N0KHRoaXMud2FuZEhvc3QpO1xuICAgIHRoaXMuYnJ1c2hUb29sLnNldE1vZGUodHJ1ZSwgKG9wdGlvbnMgPz8ge30pIGFzIHVua25vd24gYXMgQnJ1c2hPcHRpb25zKTtcbiAgfVxuICBzZXRCcnVzaE9wdGlvbnMob3B0aW9uczogSUJydXNoT3B0aW9ucyk6IHZvaWQge1xuICAgIGlmIChvcHRpb25zPy5zaXplICE9IG51bGwpIHRoaXMuYnJ1c2hUb29sLnNldFNpemUob3B0aW9ucy5zaXplKTtcbiAgfVxuICBzZXRWZXJ0ZXhFcmFzZXJNb2RlKGFjdGl2ZTogYm9vbGVhbik6IHZvaWQge1xuICAgIGlmICghdGhpcy52aWV3ZXIpIHJldHVybjtcbiAgICBpZiAoYWN0aXZlKSB7XG4gICAgICB0aGlzLnZpZXdlcj8uc2V0Q29udHJvbHNFbmFibGVkKGZhbHNlKTtcbiAgICAgIHRoaXMuY2FjaGVkSW1hZ2VTb3VyY2UgPSBudWxsO1xuICAgICAgaWYgKHRoaXMuZXJhc2VySG9zdCkgdGhpcy5lcmFzZXJUb29sLmJpbmRIb3N0KHRoaXMuZXJhc2VySG9zdCk7XG4gICAgfVxuICAgIHRoaXMuZXJhc2VyVG9vbC5zZXRNb2RlKGFjdGl2ZSk7XG4gIH1cbiAgc2V0VmVydGV4RXJhc2VyUmFkaXVzKHJhZGl1czogbnVtYmVyKTogdm9pZCB7XG4gICAgdGhpcy5lcmFzZXJUb29sLnNldFJhZGl1cyhyYWRpdXMpO1xuICB9XG4gIHNldFpvb21Ub0JveE1vZGUoYWN0aXZlOiBib29sZWFuKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLnZpZXdlcikgcmV0dXJuO1xuICAgIGlmIChhY3RpdmUpIHtcbiAgICAgIHRoaXMudmlld2VyPy5zZXRDb250cm9sc0VuYWJsZWQoZmFsc2UpO1xuICAgICAgdGhpcy56b29tVG9Cb3hUb29sLmJpbmRIb3N0KHRoaXMuem9vbUJveEhvc3QoKSk7XG4gICAgfVxuICAgIHRoaXMuem9vbVRvQm94VG9vbC5zZXRNb2RlKGFjdGl2ZSk7XG4gIH1cbiAgLy8gU0FNIC8gY2VsbHBvc2Ug4oCUIHNlcnZlciByb3VuZC10cmlwcyB0aGF0IHJlYWQgdGhlIGRyYXduIHJlY3RhbmdsZXMgKyBkaXNwbGF5ZWQgcGl4ZWxzIHRocm91Z2hcbiAgLy8gdGhlIHNhbWUgd2FuZCBob3N0IChyZWN0YW5nbGVzIGZyb20gdGhlIFJlZ2lvblN0b3JlLCBpbWFnZSBmcm9tIHRoZSByZWFkYmFjaykuXG4gIGFzeW5jIHNlZ21lbnRSZWN0YW5nbGVzKCk6IFByb21pc2U8bnVtYmVyPiB7XG4gICAgaWYgKCF0aGlzLnZpZXdlciB8fCAhdGhpcy53YW5kSG9zdCkgcmV0dXJuIDA7XG4gICAgYXdhaXQgdGhpcy5ydW5SZWFkYmFjaygpOyAvLyB0aGUgU0FNIGVtYmVkZGluZyBzYW1wbGVzIHRoZSBjdXJyZW50bHktZGlzcGxheWVkIGltYWdlXG4gICAgdGhpcy5jYWNoZWRJbWFnZVNvdXJjZSA9IG51bGw7XG4gICAgdGhpcy5zYW1Ub29sLmJpbmRIb3N0KHRoaXMud2FuZEhvc3QpO1xuICAgIHJldHVybiB0aGlzLnNhbVRvb2wuc2VnbWVudEJveGVzKCk7XG4gIH1cbiAgYXN5bmMgc2VnbWVudFJlY3RhbmdsZXNDZWxscG9zZSgpOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIGlmICghdGhpcy52aWV3ZXIgfHwgIXRoaXMud2FuZEhvc3QgfHwgIXRoaXMuY2VsbFNlZ21lbnRlcikgcmV0dXJuIDA7XG4gICAgYXdhaXQgdGhpcy5ydW5SZWFkYmFjaygpO1xuICAgIHRoaXMuY2FjaGVkSW1hZ2VTb3VyY2UgPSBudWxsO1xuICAgIHRoaXMuY2VsbFNlZ21lbnRUb29sLmJpbmRIb3N0KHRoaXMud2FuZEhvc3QpO1xuICAgIHJldHVybiB0aGlzLmNlbGxTZWdtZW50VG9vbC5zZWdtZW50Qm94ZXModGhpcy5jZWxsU2VnbWVudGVyKTtcbiAgfVxuICBzZXRTYW1Nb2RlbChpZDogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5zYW1Ub29sLnNldE1vZGVsKGlkKTtcbiAgICB0aGlzLnNhbVBvaW50VG9vbC5zZXRNb2RlbChpZCk7XG4gIH1cbiAgc2V0U2FtUG9pbnRNb2RlKGFjdGl2ZTogYm9vbGVhbik6IHZvaWQge1xuICAgIGlmICghdGhpcy52aWV3ZXIpIHJldHVybjtcbiAgICBpZiAoYWN0aXZlKSB7XG4gICAgICB0aGlzLnZpZXdlcj8uc2V0Q29udHJvbHNFbmFibGVkKGZhbHNlKTtcbiAgICAgIHRoaXMuY2FjaGVkSW1hZ2VTb3VyY2UgPSBudWxsO1xuICAgICAgdGhpcy5hcm1SZWFkYmFjaygwKTsgLy8gcmVmcmVzaCB0aGUgcmVhZGJhY2sgdGhlIGNsaWNrJ3MgZW1iZWRkaW5nIHdpbGwgc2FtcGxlXG4gICAgICBpZiAodGhpcy53YW5kSG9zdCkgdGhpcy5zYW1Qb2ludFRvb2wuYmluZEhvc3QodGhpcy53YW5kSG9zdCk7XG4gICAgfVxuICAgIHRoaXMuc2FtUG9pbnRUb29sLnNldE1vZGUoYWN0aXZlKTtcbiAgfVxuICBjb21taXRTYW1Qb2ludHMoKTogdm9pZCB7XG4gICAgdGhpcy5zYW1Qb2ludFRvb2wuY29tbWl0KCk7XG4gIH1cbiAgY2xlYXJTYW1Qb2ludHMoKTogdm9pZCB7XG4gICAgdGhpcy5zYW1Qb2ludFRvb2wuY2xlYXIoKTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBJRGlzcGxheU9wdGlvbnMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIC8vIEluaGVyaXRlZCBmcm9tIEJhc2VTdG9yZVZpc3VhbGl6ZXIg4oCUIHB1cmUgZGVsZWdhdGlvbnMgdG8gdGhlIHNoYXJlZFxuICAvLyBWaXN1YWxpemVyU3RvcmUgKGlkZW50aWNhbCB0byB0aGUgT1NEIGJhY2tlbmQpLlxuXG4gIC8vIOKUgOKUgCBJSW50ZW5zaXR5U2FtcGxpbmc6IFBsb3RseSBvd25zIHNhbXBsaW5nOyBlbWl0IHZpZXdwb3J0IGNoYW5nZXMg4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIGVuc3VyZUludGVuc2l0eVNhbXBsaW5nKF9pbWFnZUluZm86IElJbWFnZUluZm8sIF96SW5kZXg6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcbiAgfVxuICByZWZyZXNoSW50ZW5zaXR5U2FtcGxpbmdGb3JSb2koXG4gICAgX3g6IG51bWJlcixcbiAgICBfeTogbnVtYmVyLFxuICAgIF93aWR0aDogbnVtYmVyLFxuICAgIF9oZWlnaHQ6IG51bWJlcixcbiAgICBfekluZGV4OiBudW1iZXIsXG4gICk6IHZvaWQge31cbiAgZ2V0Vmlld3BvcnRDaGFuZ2UkKCk6IE9ic2VydmFibGU8eyB4OiBudW1iZXI7IHk6IG51bWJlcjsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfT4ge1xuICAgIHJldHVybiB0aGlzLnZpZXdwb3J0Q2hhbmdlJC5hc09ic2VydmFibGUoKTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBJVmlzdWFsaXplciBjb21wb3NpdGUgbWVtYmVycyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgZ2V0UmVnaW9uT3ZlcmxheSgpOiBJUmVnaW9uT3ZlcmxheSB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLnJlZ2lvbk92ZXJsYXk7XG4gIH1cbiAgZ2V0SXNvc3VyZmFjZUNvbnRyb2xzKCk6IElJc29zdXJmYWNlQ29udHJvbHMgfCBudWxsIHtcbiAgICBpZiAoIXRoaXMudm9sdW1lVmlldykgcmV0dXJuIG51bGw7XG4gICAgcmV0dXJuIHtcbiAgICAgIHNldElzb1JhbmdlOiAoaXNvTWluOiBudW1iZXIsIGlzb01heDogbnVtYmVyKTogdm9pZCA9PiB7XG4gICAgICAgIC8vIEFwcGx5IHRvIGV2ZXJ5IGNoYW5uZWwncyB2b2x1bWUgbGF5ZXIuXG4gICAgICAgIGZvciAoY29uc3QgbGF5ZXIgb2YgdGhpcy52b2x1bWVWaWV3Py5sYXllcnMgPz8gW10pIHtcbiAgICAgICAgICBsYXllci5jb250cmFzdExpbWl0cyA9IFtpc29NaW4sIGlzb01heF07XG4gICAgICAgICAgbGF5ZXIucmVuZGVyaW5nID0gJ2lzbyc7XG4gICAgICAgICAgbGF5ZXIuaXNvVGhyZXNob2xkID0gMC41O1xuICAgICAgICB9XG4gICAgICAgIHRoaXMudmlld2VyPy5yZXF1ZXN0UmVuZGVyKCk7XG4gICAgICB9LFxuICAgIH07XG4gIH1cbiAgZ2V0SW50ZW5zaXR5Q29udHJvbHMoKTogSUludGVuc2l0eUNvbnRyb2xzIHwgbnVsbCB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgZ2V0U3VyZmFjZTNkQ29udHJvbHMoKTogSVN1cmZhY2UzZENvbnRyb2xzIHwgbnVsbCB7XG4gICAgaWYgKCF0aGlzLnZvbHVtZVZpZXcgJiYgIXRoaXMuc3VyZmFjZUxheWVyICYmICF0aGlzLnNjYXR0ZXIzZExheWVyKSByZXR1cm4gbnVsbDtcbiAgICByZXR1cm4ge1xuICAgICAgc2V0U3VyZmFjZURyYWdNb2RlOiAobW9kZTogc3RyaW5nKTogdm9pZCA9PiB0aGlzLnNldFN1cmZhY2VEcmFnTW9kZShtb2RlKSxcbiAgICAgIHJlc2V0U3VyZmFjZUNhbWVyYTogKCk6IHZvaWQgPT4gdGhpcy5yZXNldFN1cmZhY2VDYW1lcmEoKSxcbiAgICAgIHNldEF4ZXNWaXNpYmxlOiAodmlzaWJsZTogYm9vbGVhbik6IHZvaWQgPT4ge1xuICAgICAgICB0aGlzLmF4ZXNWaXNpYmxlID0gdmlzaWJsZTtcbiAgICAgICAgdGhpcy5heGVzTGFiZWxzPy5zZXRWaXNpYmxlKHZpc2libGUpO1xuICAgICAgICBpZiAodGhpcy5heGVzTGF5ZXIpIHtcbiAgICAgICAgICB0aGlzLmF4ZXNMYXllci52aXNpYmxlID0gdmlzaWJsZTtcbiAgICAgICAgICB0aGlzLnZpZXdlcj8ucmVxdWVzdFJlbmRlcigpO1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgYXhlc1Zpc2libGU6ICgpOiBib29sZWFuID0+IHRoaXMuYXhlc1Zpc2libGUsXG4gICAgICAvLyBTdXJmYWNlIHdpcmVmcmFtZSAobmFwYXJpLWpzIHN1cmZhY2Ugb25seSkg4oCUIGEgbGl2ZSBsYXllciBwcm9wZXJ0eSwgbm8gcmVidWlsZCBuZWVkZWQuXG4gICAgICBzZXRXaXJlZnJhbWU6IChvbjogYm9vbGVhbik6IHZvaWQgPT4ge1xuICAgICAgICB0aGlzLnN1cmZhY2VXaXJlZnJhbWUgPSBvbjtcbiAgICAgICAgaWYgKHRoaXMuc3VyZmFjZUxheWVyKSB7XG4gICAgICAgICAgdGhpcy5zdXJmYWNlTGF5ZXIud2lyZWZyYW1lID0gb247XG4gICAgICAgICAgdGhpcy52aWV3ZXI/LnJlcXVlc3RSZW5kZXIoKTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIHdpcmVmcmFtZTogKCk6IGJvb2xlYW4gPT4gdGhpcy5zdXJmYWNlV2lyZWZyYW1lLFxuICAgIH07XG4gIH1cbiAgZ2V0SGlzdG9ncmFtKGNoYW5uZWxJbmRleDogbnVtYmVyLCBiaW5zOiBudW1iZXIpOiBJSGlzdG9ncmFtIHwgbnVsbCB7XG4gICAgY29uc3QgdiA9IHRoaXMudmlld2VyO1xuICAgIGlmICghdikgcmV0dXJuIG51bGw7XG4gICAgLy8gVm9sdW1lIC8gaXNvc3VyZmFjZTogaW50ZW5zaXR5IGhpc3RvZ3JhbSBvZiB0aGUgYXNzZW1ibGVkIChkb3duc2FtcGxlZCkgdWludDggdm9sdW1lIGZvciB0aGVcbiAgICAvLyByZXF1ZXN0ZWQgY2hhbm5lbCAobXVsdGljaGFubmVsKSBvciB0aGUgc2luZ2xlIGdyYXlzY2FsZSB2b2x1bWUgKGtleSAwKS5cbiAgICBpZiAodGhpcy52b2x1bWVDaGFubmVsRGF0YS5zaXplKSB7XG4gICAgICBjb25zdCBkYXRhID0gdGhpcy52b2x1bWVDaGFubmVsRGF0YS5nZXQoY2hhbm5lbEluZGV4KSA/PyB0aGlzLnZvbHVtZUNoYW5uZWxEYXRhLmdldCgwKTtcbiAgICAgIHJldHVybiBkYXRhID8gdGhpcy50b0lIaXN0b2dyYW0oaGlzdG9ncmFtU2NhbGFyKGRhdGEsIGJpbnMsIDAsIDI1NSkpIDogbnVsbDtcbiAgICB9XG4gICAgLy8gVGlsZWQgbW9kZSBoYXMgbm8gZnVsbCBpbi1tZW1vcnkgcGl4ZWxzIOKGkiB1c2UgdGhlIGNvYXJzZSBwZXItY2hhbm5lbCBzYW1wbGUgKFJHQjogcmVhZGJhY2spLlxuICAgIGlmICh0aGlzLnRpbGVkKSB7XG4gICAgICBpZiAodGhpcy5pbWFnZU1vZGUgPT09ICdyZ2InKSByZXR1cm4gdGhpcy5yZ2JIaXN0b2dyYW0oY2hhbm5lbEluZGV4LCBiaW5zKTtcbiAgICAgIGNvbnN0IHNhbXBsZSA9IHRoaXMuaGlzdFNhbXBsZXMuZ2V0KHRoaXMuaW1hZ2VNb2RlID09PSAnZ3JheXNjYWxlJyA/IDAgOiBjaGFubmVsSW5kZXgpO1xuICAgICAgcmV0dXJuIHNhbXBsZSA/IHRoaXMudG9JSGlzdG9ncmFtKGhpc3RvZ3JhbVNjYWxhcihzYW1wbGUsIGJpbnMsIDAsIDI1NSkpIDogbnVsbDtcbiAgICB9XG4gICAgLy8gR3JheXNjYWxlL211bHRpY2hhbm5lbCAoc3RpdGNoKTogbmF0aXZlIHBlci1jaGFubmVsIGhpc3RvZ3JhbSBzdHJhaWdodCBmcm9tIHRoZSBpbi1tZW1vcnlcbiAgICAvLyBzY2FsYXIgbGF5ZXIgKG5vIEdQVSByZWFkYmFjaykuIFJHQjogYmluIHRoZSBkaXNwbGF5ZWQgcGl4ZWxzJyBSL0cvQiBieXRlICg4LWJpdCBjbGllbnQgcGF0aCkuXG4gICAgY29uc3QgbGF5ZXIgPSB0aGlzLmNoYW5uZWxWaWV3Py5sYXllcnNbdGhpcy5pbWFnZU1vZGUgPT09ICdncmF5c2NhbGUnID8gMCA6IGNoYW5uZWxJbmRleF07XG4gICAgaWYgKGxheWVyKSB7XG4gICAgICBjb25zdCBoID0gdi5sYXllckhpc3RvZ3JhbShsYXllciwgYmlucyk7XG4gICAgICBpZiAoaCkgcmV0dXJuIHRoaXMudG9JSGlzdG9ncmFtKGgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5pbWFnZU1vZGUgPT09ICdyZ2InKSByZXR1cm4gdGhpcy5yZ2JIaXN0b2dyYW0oY2hhbm5lbEluZGV4LCBiaW5zKTtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBnZXRIaXN0b2dyYW0kKGNoYW5uZWxJbmRleDogbnVtYmVyLCBiaW5zOiBudW1iZXIpOiBPYnNlcnZhYmxlPElIaXN0b2dyYW0gfCBudWxsPiB7XG4gICAgLy8gPjgtYml0IGNoYW5uZWxzOiBmZXRjaCB0aGUgdHJ1ZSBuYXRpdmUgZGlzdHJpYnV0aW9uIGZyb20gdGhlIHNlcnZlciAodGhlIGRpc3BsYXllZCBwaXhlbHNcbiAgICAvLyBhcmUgOC1iaXQsIHNvIHRoZSBjbGllbnQgaGlzdG9ncmFtIHdvdWxkIGJlIGNsaXBwZWQpLiA4LWJpdCBjaGFubmVscyB1c2UgdGhlIGNsaWVudCBwYXRoLlxuICAgIGNvbnN0IGJpdERlcHRoID0gdGhpcy5kZXNjcmlwdG9yPy5jaGFubmVsSW5mbz8uW2NoYW5uZWxJbmRleF0/LmJpdERlcHRoID8/IDg7XG4gICAgaWYgKGJpdERlcHRoID4gOCkge1xuICAgICAgY29uc3QgeiA9IHRoaXMubG9hZGVkPy56ID8/IDA7XG4gICAgICBjb25zdCBrZXkgPSBgJHt6fXwke2NoYW5uZWxJbmRleH1gO1xuICAgICAgY29uc3QgY2FjaGVkID0gdGhpcy5uYXRpdmVIaXN0b2dyYW1zLmdldChrZXkpO1xuICAgICAgaWYgKGNhY2hlZCkgcmV0dXJuIG9mKGNhY2hlZCk7XG4gICAgICByZXR1cm4gZnJvbSh0aGlzLmZldGNoTmF0aXZlSGlzdG9ncmFtKGNoYW5uZWxJbmRleCwgYmlucywgeiwga2V5KSk7XG4gICAgfVxuICAgIHJldHVybiBvZih0aGlzLmdldEhpc3RvZ3JhbShjaGFubmVsSW5kZXgsIGJpbnMpKTtcbiAgfVxuXG4gIC8qKiBGZXRjaCArIGNhY2hlIG9uZSBjaGFubmVsJ3MgbmF0aXZlLWJpdC1kZXB0aCBoaXN0b2dyYW0gZnJvbSBgR0VUIC9oaXN0b2dyYW1gICg+OC1iaXQpLiAqL1xuICBwcml2YXRlIGFzeW5jIGZldGNoTmF0aXZlSGlzdG9ncmFtKFxuICAgIGNoYW5uZWw6IG51bWJlcixcbiAgICBiaW5zOiBudW1iZXIsXG4gICAgejogbnVtYmVyLFxuICAgIGtleTogc3RyaW5nLFxuICApOiBQcm9taXNlPElIaXN0b2dyYW0gfCBudWxsPiB7XG4gICAgY29uc3QgaW5mb0I2NCA9IHRoaXMudGlsZXMuZ2V0U2VsZWN0ZWRJbmZvQjY0KCk7XG4gICAgaWYgKCFpbmZvQjY0KSByZXR1cm4gbnVsbDtcbiAgICBjb25zdCBoZWFkZXJzID0gYXdhaXQgdGhpcy50aWxlc1xuICAgICAgLmdldEF1dGhIZWFkZXJzKClcbiAgICAgIC5jYXRjaCgoKSA9PiAoe30pIGFzIFJlY29yZDxzdHJpbmcsIHN0cmluZz4pO1xuICAgIGNvbnN0IHVybCA9IGAke3RoaXMuYXBpfWhpc3RvZ3JhbT9pbmZvPSR7aW5mb0I2NH0mY2hhbm5lbD0ke2NoYW5uZWx9Jno9JHt6fSZiaW5zPSR7Ymluc31gO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXNwID0gYXdhaXQgZmV0Y2godXJsLCB7IGhlYWRlcnMgfSk7XG4gICAgICBpZiAoIXJlc3Aub2spIHJldHVybiBudWxsOyAvLyAyMDIgKHN0aWxsIGNhY2hpbmcpIG9yIHRyYW5zaWVudCDihpIgbnVsbDsgdGhlIHBhbmUgcmV0cmllc1xuICAgICAgY29uc3QgaGkgPSAoYXdhaXQgcmVzcC5qc29uKCkpIGFzIHtcbiAgICAgICAgYml0RGVwdGg6IG51bWJlcjtcbiAgICAgICAgcmFuZ2VNaW46IG51bWJlcjtcbiAgICAgICAgcmFuZ2VNYXg6IG51bWJlcjtcbiAgICAgICAgb2JzZXJ2ZWRNaW46IG51bWJlcjtcbiAgICAgICAgb2JzZXJ2ZWRNYXg6IG51bWJlcjtcbiAgICAgICAgYmluV2lkdGg6IG51bWJlcjtcbiAgICAgICAgY291bnRzOiBudW1iZXJbXTtcbiAgICAgIH07XG4gICAgICBpZiAoIWhpPy5jb3VudHMpIHJldHVybiBudWxsO1xuICAgICAgY29uc3Qgb3V0OiBJSGlzdG9ncmFtID0ge1xuICAgICAgICBiaW5zOiBoaS5jb3VudHMubWFwKChfLCBpKSA9PiBoaS5yYW5nZU1pbiArIGkgKiBoaS5iaW5XaWR0aCksXG4gICAgICAgIGNvdW50czogaGkuY291bnRzLFxuICAgICAgICBtYXg6IGhpLmNvdW50cy5yZWR1Y2UoKG0sIGMpID0+IChjID4gbSA/IGMgOiBtKSwgMCksXG4gICAgICAgIGJpdERlcHRoOiBoaS5iaXREZXB0aCxcbiAgICAgICAgcmFuZ2VNaW46IGhpLnJhbmdlTWluLFxuICAgICAgICByYW5nZU1heDogaGkucmFuZ2VNYXgsXG4gICAgICAgIG9ic2VydmVkTWluOiBoaS5vYnNlcnZlZE1pbixcbiAgICAgICAgb2JzZXJ2ZWRNYXg6IGhpLm9ic2VydmVkTWF4LFxuICAgICAgfTtcbiAgICAgIHRoaXMubmF0aXZlSGlzdG9ncmFtcy5zZXQoa2V5LCBvdXQpO1xuICAgICAgcmV0dXJuIG91dDtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGNvbnNvbGUud2FybignW25hcGFyaS1qc10gbmF0aXZlIGhpc3RvZ3JhbSBmZXRjaCBmYWlsZWQnLCBlcnIpO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICB9XG5cbiAgLyoqIFBlci1jaGFubmVsIGhpc3RvZ3JhbSBvZiB0aGUgZGlzcGxheWVkIFJHQiBjb21wb3NpdGUgKFIvRy9CIGJ5dGUpLCBmcm9tIHRoZSBsYXN0IHJlYWRiYWNrLiAqL1xuICBwcml2YXRlIHJnYkhpc3RvZ3JhbShjaGFubmVsSW5kZXg6IG51bWJlciwgYmluczogbnVtYmVyKTogSUhpc3RvZ3JhbSB8IG51bGwge1xuICAgIGNvbnN0IHB4ID0gdGhpcy5sYXN0UGl4ZWxzO1xuICAgIGlmICghcHgpIHJldHVybiBudWxsO1xuICAgIGNvbnN0IGJhbmQgPSBNYXRoLm1heCgwLCBNYXRoLm1pbigyLCBjaGFubmVsSW5kZXgpKTtcbiAgICBjb25zdCBuID0gTWF0aC5tYXgoMSwgTWF0aC5taW4oMjU2LCBNYXRoLmZsb29yKGJpbnMpIHx8IDI1NikpO1xuICAgIGNvbnN0IGNvdW50cyA9IG5ldyBBcnJheShuKS5maWxsKDApO1xuICAgIGNvbnN0IGRhdGEgPSBweC5kYXRhO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgZGF0YS5sZW5ndGg7IGkgKz0gNCkge1xuICAgICAgaWYgKGRhdGFbaSArIDNdID09PSAwKSBjb250aW51ZTsgLy8gc2tpcCB0cmFuc3BhcmVudCBwYWRkaW5nXG4gICAgICBjb3VudHNbTWF0aC5taW4obiAtIDEsIChkYXRhW2kgKyBiYW5kXSAqIG4pID4+IDgpXSsrO1xuICAgIH1cbiAgICBjb25zdCBiaW5zQXJyID0gQXJyYXkuZnJvbSh7IGxlbmd0aDogbiB9LCAoXywgaSkgPT4gKGkgKiAyNTYpIC8gbik7XG4gICAgcmV0dXJuIHsgYmluczogYmluc0FyciwgY291bnRzLCBtYXg6IGNvdW50cy5yZWR1Y2UoKG0sIGMpID0+IChjID4gbSA/IGMgOiBtKSwgMCkgfTtcbiAgfVxuICBleHBvcnRDb21wb3NpdGUoKTogdm9pZCB7XG4gICAgY29uc3QgdiA9IHRoaXMudmlld2VyO1xuICAgIGlmICghdikgcmV0dXJuO1xuICAgIHZvaWQgdi5zY3JlZW5zaG90KCkudGhlbigoYmxvYikgPT4ge1xuICAgICAgY29uc3QgdXJsID0gVVJMLmNyZWF0ZU9iamVjdFVSTChibG9iKTtcbiAgICAgIGNvbnN0IGEgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG4gICAgICBhLmhyZWYgPSB1cmw7XG4gICAgICBhLmRvd25sb2FkID0gJ25hcGFyaS1qcy5wbmcnO1xuICAgICAgYS5jbGljaygpO1xuICAgICAgVVJMLnJldm9rZU9iamVjdFVSTCh1cmwpO1xuICAgIH0pO1xuICB9XG4gIC8qKiBOYXRpdmUtYml0LWRlcHRoICgxNi8zMi1iaXQpIG11bHRpLWJhbmQgVElGRiBleHBvcnQgdmlhIHRoZSBzZXJ2ZXIgYC9leHBvcnQvdGlmZmAgZW5kcG9pbnQg4oCUXG4gICAqICB0aGUgZGlzcGxheWVkIFBORyBpcyBhbiA4LWJpdCBmaWd1cmUsIHRoaXMgcHJlc2VydmVzIHRoZSB0cnVlIHBpeGVsIHZhbHVlcy4gVmlzaWJsZSBjaGFubmVsc1xuICAgKiAgb25seSAob21pdHRlZCB3aGVuIGFsbCBhcmUgdmlzaWJsZSDihpIgc2VydmVyIGRlZmF1bHQpLiBNaXJyb3JzIHRoZSBPU0QgYmFja2VuZC4gKi9cbiAgYXN5bmMgZXhwb3J0RGF0YSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBpbmZvQjY0ID0gdGhpcy50aWxlcy5nZXRTZWxlY3RlZEluZm9CNjQoKTtcbiAgICBpZiAoIWluZm9CNjQpIHJldHVybjtcbiAgICBjb25zdCBzdGF0ZXMgPSB0aGlzLnN0b3JlLmN1cnJlbnRDaGFubmVsU3RhdGVzKCk7XG4gICAgY29uc3QgdmlzaWJsZSA9IHN0YXRlcy5maWx0ZXIoKGMpID0+IGMudmlzaWJsZSkubWFwKChjKSA9PiBjLmluZGV4KTtcbiAgICBjb25zdCBjaFBhcmFtID1cbiAgICAgIHZpc2libGUubGVuZ3RoICYmIHZpc2libGUubGVuZ3RoIDwgc3RhdGVzLmxlbmd0aCA/IGAmY2hhbm5lbHM9JHt2aXNpYmxlLmpvaW4oJywnKX1gIDogJyc7XG4gICAgY29uc3QgeiA9IHRoaXMubG9hZGVkPy56ID8/IDA7XG4gICAgY29uc3QgdXJsID0gYCR7dGhpcy5hcGl9ZXhwb3J0L3RpZmY/aW5mbz0ke2luZm9CNjR9Jno9JHt6fSR7Y2hQYXJhbX1gO1xuICAgIGNvbnN0IHN0ZW0gPSAodGhpcy5sb2FkZWQ/LmZpbGVuYW1lIHx8ICdpbWFnZScpLnJlcGxhY2UoL1xcLlteLl0rJC8sICcnKTtcbiAgICBjb25zdCBoZWFkZXJzID0gYXdhaXQgdGhpcy50aWxlc1xuICAgICAgLmdldEF1dGhIZWFkZXJzKClcbiAgICAgIC5jYXRjaCgoKSA9PiAoe30pIGFzIFJlY29yZDxzdHJpbmcsIHN0cmluZz4pO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXNwID0gYXdhaXQgZmV0Y2godXJsLCB7IGhlYWRlcnMgfSk7XG4gICAgICBpZiAocmVzcC5zdGF0dXMgPT09IDIwMikge1xuICAgICAgICBjb25zb2xlLndhcm4oJ1tuYXBhcmktanNdIDE2LWJpdCBleHBvcnQ6IGZpbGUgc3RpbGwgY2FjaGluZyDigJQgdHJ5IGFnYWluIHNob3J0bHkuJyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGlmICghcmVzcC5vaykgdGhyb3cgbmV3IEVycm9yKGBzdGF0dXMgJHtyZXNwLnN0YXR1c31gKTtcbiAgICAgIHNhdmVBcyhhd2FpdCByZXNwLmJsb2IoKSwgYCR7c3RlbX1fMTZiaXQub21lLnRpZmApO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgY29uc29sZS53YXJuKCdbbmFwYXJpLWpzXSAxNi1iaXQgVElGRiBleHBvcnQgZmFpbGVkJywgZXJyKTtcbiAgICB9XG4gIH1cbiAgdW5zdWJzY3JpYmUoKTogdm9pZCB7XG4gICAgdGhpcy5yZXNldCgpO1xuICB9XG59XG4iXX0=