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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (525) hide show
  1. package/README.md +201 -460
  2. package/esm2022/index.mjs +28 -15
  3. package/esm2022/lib/channel-histogram/channel-histogram.component.mjs +163 -91
  4. package/esm2022/lib/contracts/capabilities.contract.mjs +4 -3
  5. package/esm2022/lib/contracts/cell-segmenter.contract.mjs +1 -1
  6. package/esm2022/lib/contracts/channel-histogram-api.contract.mjs +5 -1
  7. package/esm2022/lib/contracts/color.mjs +60 -0
  8. package/esm2022/lib/contracts/colormap-lut.mjs +213 -57
  9. package/esm2022/lib/contracts/coordinate-transform.contract.mjs +1 -1
  10. package/esm2022/lib/contracts/display-types.mjs +11 -1
  11. package/esm2022/lib/contracts/instance-segmenter.contract.mjs +1 -1
  12. package/esm2022/lib/contracts/intensity.mjs +48 -5
  13. package/esm2022/lib/contracts/plot-type-contribution.contract.mjs +1 -1
  14. package/esm2022/lib/contracts/plot-type.mjs +134 -17
  15. package/esm2022/lib/contracts/ports/image-state.port.mjs +4 -1
  16. package/esm2022/lib/contracts/ports/preferences.port.mjs +5 -1
  17. package/esm2022/lib/contracts/ports/region-io.port.mjs +2 -1
  18. package/esm2022/lib/contracts/ports/spatial-data.port.mjs +5 -1
  19. package/esm2022/lib/contracts/ports/tile-access.port.mjs +2 -1
  20. package/esm2022/lib/contracts/region-editor-api.contract.mjs +1 -1
  21. package/esm2022/lib/contracts/region-overlay.contract.mjs +1 -1
  22. package/esm2022/lib/contracts/sam.contract.mjs +1 -1
  23. package/esm2022/lib/contracts/semantic-segmenter.contract.mjs +1 -1
  24. package/esm2022/lib/contracts/spatial-dataset.contract.mjs +3 -1
  25. package/esm2022/lib/contracts/toolbar-tool.contract.mjs +6 -4
  26. package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
  27. package/esm2022/lib/contracts/viz-config.mjs +2 -1
  28. package/esm2022/lib/geometry/contour.mjs +63 -0
  29. package/esm2022/lib/geometry/raster.mjs +98 -0
  30. package/esm2022/lib/geometry/ring.mjs +201 -0
  31. package/esm2022/lib/hex-color-picker/hex-color-picker.component.mjs +202 -102
  32. package/esm2022/lib/implementations/base-store-visualizer.mjs +201 -48
  33. package/esm2022/lib/implementations/napari-js/napari-axes-gizmo.mjs +65 -0
  34. package/esm2022/lib/implementations/napari-js/napari-display-state.mjs +136 -0
  35. package/esm2022/lib/implementations/napari-js/napari-helpers.mjs +199 -0
  36. package/esm2022/lib/implementations/napari-js/napari-image-2d-scene.mjs +602 -0
  37. package/esm2022/lib/implementations/napari-js/napari-loading-badge.mjs +3 -4
  38. package/esm2022/lib/implementations/napari-js/napari-loading-state.mjs +66 -0
  39. package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +157 -591
  40. package/esm2022/lib/implementations/napari-js/napari-scene.mjs +2 -0
  41. package/esm2022/lib/implementations/napari-js/napari-spatial-2d-scene.mjs +614 -0
  42. package/esm2022/lib/implementations/napari-js/napari-spatial-3d-scene.mjs +769 -0
  43. package/esm2022/lib/implementations/napari-js/napari-spatial-encoding.mjs +199 -0
  44. package/esm2022/lib/implementations/napari-js/napari-spatial-hover.mjs +263 -0
  45. package/esm2022/lib/implementations/napari-js/napari-spatial-scene.mjs +159 -0
  46. package/esm2022/lib/implementations/napari-js/napari-spatial-tiles.mjs +106 -1408
  47. package/esm2022/lib/implementations/napari-js/napari-spatial-tooltip.mjs +2 -2
  48. package/esm2022/lib/implementations/napari-js/napari-surface-scene.mjs +271 -0
  49. package/esm2022/lib/implementations/napari-js/napari-tile-client.mjs +562 -0
  50. package/esm2022/lib/implementations/napari-js/napari-tool-bridge.mjs +344 -0
  51. package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +361 -4184
  52. package/esm2022/lib/implementations/napari-js/napari-volume-scene.mjs +439 -0
  53. package/esm2022/lib/implementations/napari-js/napari-volume-z-handle.mjs +6 -6
  54. package/esm2022/lib/implementations/napari-js/region-overlay/overlay-projection.mjs +96 -0
  55. package/esm2022/lib/implementations/napari-js/region-overlay/region-edit.mjs +35 -0
  56. package/esm2022/lib/implementations/napari-js/region-overlay/region-svg-renderer.mjs +182 -0
  57. package/esm2022/lib/implementations/napari-js/spatial-tiles/categorical-lookup.mjs +30 -0
  58. package/esm2022/lib/implementations/napari-js/spatial-tiles/cell-layers.mjs +274 -0
  59. package/esm2022/lib/implementations/napari-js/spatial-tiles/density-layer.mjs +144 -0
  60. package/esm2022/lib/implementations/napari-js/spatial-tiles/layer-groups.mjs +165 -0
  61. package/esm2022/lib/implementations/napari-js/spatial-tiles/plan-context.mjs +78 -0
  62. package/esm2022/lib/implementations/napari-js/spatial-tiles/transcript-hover.mjs +182 -0
  63. package/esm2022/lib/implementations/napari-js/spatial-tiles/transcript-jobs.mjs +369 -0
  64. package/esm2022/lib/implementations/napari-js/spatial-tiles/transcript-layers.mjs +306 -0
  65. package/esm2022/lib/implementations/osd/display-pipeline.mjs +102 -40
  66. package/esm2022/lib/implementations/osd/histogram-sampler.mjs +56 -31
  67. package/esm2022/lib/implementations/osd/openseadragon-viewer-options.mjs +128 -0
  68. package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +333 -1685
  69. package/esm2022/lib/implementations/osd/osd-coordinate-transform.mjs +1 -1
  70. package/esm2022/lib/implementations/osd/osd-coords.mjs +5 -6
  71. package/esm2022/lib/implementations/osd/osd-export.mjs +122 -0
  72. package/esm2022/lib/implementations/osd/osd-lib.mjs +15 -1
  73. package/esm2022/lib/implementations/osd/osd-navigator-chrome.mjs +205 -0
  74. package/esm2022/lib/implementations/osd/osd-pixel-readback.mjs +68 -0
  75. package/esm2022/lib/implementations/osd/osd-region-overlay-draw.mjs +153 -0
  76. package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +185 -718
  77. package/esm2022/lib/implementations/osd/osd-scale-bar.mjs +52 -60
  78. package/esm2022/lib/implementations/osd/osd-tile-source.mjs +105 -0
  79. package/esm2022/lib/implementations/osd/osd-viewer-like.mjs +2 -0
  80. package/esm2022/lib/implementations/osd/osd-viewer-mount.mjs +44 -0
  81. package/esm2022/lib/implementations/osd/osd-viewport.mjs +184 -0
  82. package/esm2022/lib/implementations/osd/osd-zoom.mjs +7 -1
  83. package/esm2022/lib/implementations/osd/simple-source.mjs +395 -0
  84. package/esm2022/lib/implementations/osd/slice-cache.mjs +53 -48
  85. package/esm2022/lib/implementations/osd/tile-client.mjs +32 -11
  86. package/esm2022/lib/implementations/osd/tile-recolor.mjs +330 -0
  87. package/esm2022/lib/implementations/plotly/omics-trace-builders.mjs +161 -73
  88. package/esm2022/lib/implementations/plotly/plotly-coordinate-transform.mjs +1 -1
  89. package/esm2022/lib/implementations/plotly/plotly-image-loader.mjs +112 -0
  90. package/esm2022/lib/implementations/plotly/plotly-image-recolor.mjs +178 -0
  91. package/esm2022/lib/implementations/plotly/plotly-intensity-inset.mjs +33 -0
  92. package/esm2022/lib/implementations/plotly/plotly-isosurface-controls.mjs +83 -0
  93. package/esm2022/lib/implementations/plotly/plotly-layouts.mjs +121 -0
  94. package/esm2022/lib/implementations/plotly/plotly-readback.mjs +117 -0
  95. package/esm2022/lib/implementations/plotly/plotly-region-overlay.mjs +19 -10
  96. package/esm2022/lib/implementations/plotly/plotly-shape-projection.mjs +223 -0
  97. package/esm2022/lib/implementations/plotly/plotly-trace-builders.mjs +79 -19
  98. package/esm2022/lib/implementations/plotly/plotly-zoom-controller.mjs +223 -0
  99. package/esm2022/lib/implementations/plotly/plotly.service.mjs +439 -1655
  100. package/esm2022/lib/implementations/region-centroids.mjs +47 -0
  101. package/esm2022/lib/implementations/simple-slice-access.service.mjs +22 -12
  102. package/esm2022/lib/implementations/spatial-data-http/spatial-data-http.service.mjs +172 -92
  103. package/esm2022/lib/implementations/spatial-data-http/spatial-wire.mjs +103 -22
  104. package/esm2022/lib/implementations/tile-server/index.mjs +6 -0
  105. package/esm2022/lib/implementations/tile-server/native-histogram.mjs +30 -0
  106. package/esm2022/lib/implementations/tile-server/poll-descriptor.mjs +65 -0
  107. package/esm2022/lib/implementations/tile-server/tile-protocol.mjs +42 -0
  108. package/esm2022/lib/implementations/tile-server/transport.mjs +135 -0
  109. package/esm2022/lib/intensity/intensity-profile.mjs +107 -0
  110. package/esm2022/lib/intensity/intensity-profile.service.mjs +223 -0
  111. package/esm2022/lib/intensity-inset/intensity-inset.component.mjs +133 -0
  112. package/esm2022/lib/models/bezier.mjs +1 -11
  113. package/esm2022/lib/models/class-preset.mjs +41 -3
  114. package/esm2022/lib/models/geometry.mjs +27 -164
  115. package/esm2022/lib/models/polygon-edit.mjs +254 -0
  116. package/esm2022/lib/models/polygon-factory.mjs +118 -0
  117. package/esm2022/lib/models/region-clone.mjs +68 -0
  118. package/esm2022/lib/models/region-geojson.mjs +373 -0
  119. package/esm2022/lib/models/region.mjs +34 -33
  120. package/esm2022/lib/models/shape.mjs +18 -16
  121. package/esm2022/lib/plot-mode/contribution-host.mjs +297 -0
  122. package/esm2022/lib/plot-mode/plot-mode-controller.mjs +9 -6
  123. package/esm2022/lib/plot-mode/plot-type-menu.mjs +85 -0
  124. package/esm2022/lib/plot-mode/tool-params-model.mjs +87 -0
  125. package/esm2022/lib/plot.utilities.mjs +296 -473
  126. package/esm2022/lib/processing/image-crop.mjs +1 -1
  127. package/esm2022/lib/processing/processing-image.mjs +28 -15
  128. package/esm2022/lib/provide-visualization.mjs +40 -14
  129. package/esm2022/lib/region-editor/classes-panel/classes-panel.component.mjs +99 -0
  130. package/esm2022/lib/region-editor/file-stem.mjs +13 -0
  131. package/esm2022/lib/region-editor/manage-classes-dialog/manage-classes-dialog.component.mjs +118 -0
  132. package/esm2022/lib/region-editor/mask-export.service.mjs +98 -0
  133. package/esm2022/lib/region-editor/mask-raster.mjs +11 -5
  134. package/esm2022/lib/region-editor/region-color-dialog/region-color-dialog.component.mjs +56 -0
  135. package/esm2022/lib/region-editor/region-editor-help/region-editor-help.component.mjs +28 -0
  136. package/esm2022/lib/region-editor/region-editor.component.mjs +402 -651
  137. package/esm2022/lib/region-editor/region-metrics.mjs +68 -0
  138. package/esm2022/lib/region-editor/region-persistence.service.mjs +134 -0
  139. package/esm2022/lib/region-editor/region-table/region-table.component.mjs +153 -0
  140. package/esm2022/lib/region-editor/save-mask-dialog/save-mask-dialog.component.mjs +91 -0
  141. package/esm2022/lib/region-editor/save-regions-dialog/save-regions-dialog.component.mjs +92 -0
  142. package/esm2022/lib/region-ops.service.mjs +28 -70
  143. package/esm2022/lib/region-overlay/region-geometry.mjs +360 -0
  144. package/esm2022/lib/region-overlay/region-hit-test.mjs +103 -0
  145. package/esm2022/lib/region-overlay/svg-region-renderer.mjs +228 -0
  146. package/esm2022/lib/render-orchestrator.mjs +14 -11
  147. package/esm2022/lib/render-session.mjs +207 -0
  148. package/esm2022/lib/routing-visualizer.service.mjs +459 -411
  149. package/esm2022/lib/spatial/density-raster.mjs +14 -0
  150. package/esm2022/lib/spatial/embedding-compute-coordinator.mjs +239 -0
  151. package/esm2022/lib/spatial/embedding-compute.mjs +43 -10
  152. package/esm2022/lib/spatial/gene-search.mjs +1 -1
  153. package/esm2022/lib/spatial/glyphs.mjs +97 -0
  154. package/esm2022/lib/spatial/lod.mjs +187 -0
  155. package/esm2022/lib/spatial/spatial-controls.facade.mjs +200 -0
  156. package/esm2022/lib/spatial/spatial-density.mjs +25 -16
  157. package/esm2022/lib/spatial/spatial-encoding.mjs +26 -82
  158. package/esm2022/lib/spatial/spatial-expression.mjs +43 -42
  159. package/esm2022/lib/spatial/spatial-framing.mjs +1 -1
  160. package/esm2022/lib/spatial/spatial-heatmap.mjs +6 -3
  161. package/esm2022/lib/spatial/spatial-hover.mjs +5 -33
  162. package/esm2022/lib/spatial/spatial-panel-model.mjs +282 -0
  163. package/esm2022/lib/spatial/spatial-sections.mjs +1 -1
  164. package/esm2022/lib/spatial/spatial-selection.mjs +33 -46
  165. package/esm2022/lib/spatial/spatial-tile-merge.mjs +219 -0
  166. package/esm2022/lib/spatial/spatial-tiles.mjs +12 -648
  167. package/esm2022/lib/spatial/spatial-volume-image.mjs +2 -4
  168. package/esm2022/lib/spatial/stats.mjs +71 -0
  169. package/esm2022/lib/spatial/transcript-grouping.mjs +307 -0
  170. package/esm2022/lib/spatial-controls/spatial-cells-panel/spatial-cells-panel.component.mjs +150 -0
  171. package/esm2022/lib/spatial-controls/spatial-charts/chart-data-model.mjs +309 -0
  172. package/esm2022/lib/spatial-controls/spatial-charts/chart-help.mjs +145 -0
  173. package/esm2022/lib/spatial-controls/spatial-charts/plotly-chart-host.mjs +158 -0
  174. package/esm2022/lib/spatial-controls/spatial-charts/spatial-chart-window/spatial-chart-window.component.mjs +47 -0
  175. package/esm2022/lib/spatial-controls/spatial-charts/spatial-charts.component.mjs +297 -802
  176. package/esm2022/lib/spatial-controls/spatial-charts/spatial-embedding-controls/spatial-embedding-controls.component.mjs +89 -0
  177. package/esm2022/lib/spatial-controls/spatial-charts/spatial-heatmap-controls/spatial-heatmap-controls.component.mjs +52 -0
  178. package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +143 -1238
  179. package/esm2022/lib/spatial-controls/spatial-gene-picker.mjs +153 -0
  180. package/esm2022/lib/spatial-controls/spatial-gene-tree/spatial-gene-tree.component.mjs +241 -0
  181. package/esm2022/lib/spatial-controls/spatial-groups-panel/spatial-groups-panel.component.mjs +193 -0
  182. package/esm2022/lib/spatial-controls/spatial-key/spatial-key.component.mjs +132 -0
  183. package/esm2022/lib/spatial-controls/spatial-key/spatial-key.model.mjs +67 -0
  184. package/esm2022/lib/spatial-controls/spatial-marker-genes-form/spatial-marker-genes-form.component.mjs +147 -0
  185. package/esm2022/lib/spatial-controls/spatial-observations-panel/spatial-observations-panel.component.mjs +300 -0
  186. package/esm2022/lib/spatial-controls/spatial-transcripts-panel/spatial-transcripts-panel.component.mjs +370 -0
  187. package/esm2022/lib/store/class-color.util.mjs +46 -3
  188. package/esm2022/lib/store/region-history.mjs +146 -0
  189. package/esm2022/lib/store/region-scope-cache.mjs +140 -0
  190. package/esm2022/lib/store/region-selection.mjs +82 -0
  191. package/esm2022/lib/store/region-store.service.mjs +279 -675
  192. package/esm2022/lib/store/spatial-selection.service.mjs +2 -2
  193. package/esm2022/lib/store/visualizer-store.service.mjs +49 -71
  194. package/esm2022/lib/toast-outlets.mjs +5 -4
  195. package/esm2022/lib/toolbar/brush/brush-tool.service.mjs +92 -434
  196. package/esm2022/lib/toolbar/canvas-tools.mjs +20 -0
  197. package/esm2022/lib/toolbar/crop/slide-crop.mjs +26 -42
  198. package/esm2022/lib/toolbar/plot-type-selector/plot-type-selector.component.mjs +85 -0
  199. package/esm2022/lib/toolbar/region-tools/region-tool-buttons.mjs +74 -0
  200. package/esm2022/lib/toolbar/region-tools/region-tools.component.mjs +114 -0
  201. package/esm2022/lib/toolbar/segmentation/cell-segment-tool.service.mjs +74 -78
  202. package/esm2022/lib/toolbar/segmentation/cellpose-segmenter.service.mjs +50 -8
  203. package/esm2022/lib/toolbar/segmentation/onnx-sam-session.mjs +61 -42
  204. package/esm2022/lib/toolbar/segmentation/pending-calls.mjs +47 -0
  205. package/esm2022/lib/toolbar/segmentation/sam-model-registry.mjs +12 -4
  206. package/esm2022/lib/toolbar/segmentation/sam-onnx-core.mjs +70 -14
  207. package/esm2022/lib/toolbar/segmentation/sam-point-tool.service.mjs +135 -172
  208. package/esm2022/lib/toolbar/segmentation/sam-prompt.mjs +9 -90
  209. package/esm2022/lib/toolbar/segmentation/sam-session.service.mjs +183 -0
  210. package/esm2022/lib/toolbar/segmentation/sam-tool.service.mjs +95 -150
  211. package/esm2022/lib/toolbar/segmentation/segmentation-commit.mjs +12 -0
  212. package/esm2022/lib/toolbar/segmentation/segmentation-runner.mjs +108 -0
  213. package/esm2022/lib/toolbar/segmentation-tools/segmentation-tools.component.mjs +155 -0
  214. package/esm2022/lib/toolbar/stack-controls/stack-controls.component.mjs +81 -0
  215. package/esm2022/lib/toolbar/tool-kit/async-tool-status.mjs +37 -0
  216. package/esm2022/lib/toolbar/tool-kit/canvas-tool-manager.mjs +63 -0
  217. package/esm2022/lib/toolbar/tool-kit/canvas-tool.mjs +2 -0
  218. package/esm2022/lib/toolbar/tool-kit/frame-pixels.mjs +108 -0
  219. package/esm2022/lib/toolbar/tool-kit/mask-stroke-editor.mjs +306 -0
  220. package/esm2022/lib/toolbar/tool-kit/matrix-frame.mjs +61 -0
  221. package/esm2022/lib/toolbar/tool-kit/tool-overlay.mjs +140 -0
  222. package/esm2022/lib/toolbar/tool-kit/undo-gesture.mjs +24 -0
  223. package/esm2022/lib/toolbar/tool-slider/tool-slider.component.mjs +63 -0
  224. package/esm2022/lib/toolbar/toolbar-help-dialog/toolbar-help-dialog.component.mjs +50 -0
  225. package/esm2022/lib/toolbar/toolbar.component.mjs +68 -117
  226. package/esm2022/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.mjs +93 -115
  227. package/esm2022/lib/toolbar/view-controls/view-controls.component.mjs +107 -0
  228. package/esm2022/lib/toolbar/wand/wand-region-grow.mjs +321 -0
  229. package/esm2022/lib/toolbar/wand/wand-tool.service.mjs +121 -416
  230. package/esm2022/lib/toolbar/wand/wand.service.mjs +30 -894
  231. package/esm2022/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.mjs +48 -90
  232. package/esm2022/lib/util/mark-for-check.mjs +17 -0
  233. package/esm2022/lib/util/supersede.mjs +56 -0
  234. package/esm2022/lib/visualization.module.mjs +39 -141
  235. package/esm2022/lib/visualizer/floating-drag.directive.mjs +58 -0
  236. package/esm2022/lib/visualizer/region-actions.mjs +149 -0
  237. package/esm2022/lib/visualizer/region-load.mjs +60 -0
  238. package/esm2022/lib/visualizer/spatial-dataset-binder.mjs +163 -0
  239. package/esm2022/lib/visualizer/tool-modes.mjs +113 -0
  240. package/esm2022/lib/visualizer/viewer-shortcuts.mjs +167 -0
  241. package/esm2022/lib/visualizer/visualizer-context-menu.mjs +119 -0
  242. package/esm2022/lib/visualizer.component.mjs +480 -1902
  243. package/esm2022/lib/workers/spatial-math-ops.mjs +43 -0
  244. package/esm2022/lib/workers/spatial-math-worker.mjs +10 -0
  245. package/esm2022/lib/workers/spatial-math.mjs +190 -0
  246. package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-Csxlnq7I.mjs +41083 -0
  247. package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-Csxlnq7I.mjs.map +1 -0
  248. package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-Bkujqg7m.mjs +238 -0
  249. package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-Bkujqg7m.mjs.map +1 -0
  250. package/fesm2022/jax-data-science-sci-image-visualizer-spatial-math-worker-BzVFFqGS.mjs +12 -0
  251. package/fesm2022/jax-data-science-sci-image-visualizer-spatial-math-worker-BzVFFqGS.mjs.map +1 -0
  252. package/fesm2022/jax-data-science-sci-image-visualizer.mjs +41 -34430
  253. package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
  254. package/fesm2022/mask.worker.js +36 -917
  255. package/fesm2022/onnx-sam.worker.js +52 -16
  256. package/fesm2022/spatial-math.worker.js +471 -0
  257. package/fesm2022/tsne.worker.js +32 -15
  258. package/index.d.ts +13 -10
  259. package/lib/channel-histogram/channel-histogram.component.d.ts +53 -43
  260. package/lib/contracts/capabilities.contract.d.ts +4 -2
  261. package/lib/contracts/cell-segmenter.contract.d.ts +1 -0
  262. package/lib/contracts/channel-histogram-api.contract.d.ts +4 -0
  263. package/lib/contracts/color.d.ts +27 -0
  264. package/lib/contracts/colormap-lut.d.ts +19 -4
  265. package/lib/contracts/coordinate-transform.contract.d.ts +3 -3
  266. package/lib/contracts/display-types.d.ts +21 -0
  267. package/lib/contracts/instance-segmenter.contract.d.ts +5 -0
  268. package/lib/contracts/intensity.d.ts +13 -2
  269. package/lib/contracts/plot-type.d.ts +3 -1
  270. package/lib/contracts/ports/image-state.port.d.ts +6 -2
  271. package/lib/contracts/ports/preferences.port.d.ts +4 -0
  272. package/lib/contracts/ports/region-io.port.d.ts +1 -0
  273. package/lib/contracts/ports/spatial-data.port.d.ts +4 -10
  274. package/lib/contracts/ports/tile-access.port.d.ts +1 -0
  275. package/lib/contracts/region-editor-api.contract.d.ts +1 -1
  276. package/lib/contracts/region-overlay.contract.d.ts +5 -0
  277. package/lib/contracts/sam.contract.d.ts +7 -0
  278. package/lib/contracts/semantic-segmenter.contract.d.ts +6 -0
  279. package/lib/contracts/spatial-dataset.contract.d.ts +19 -6
  280. package/lib/contracts/toolbar-tool.contract.d.ts +1 -0
  281. package/lib/contracts/visualizer.contract.d.ts +221 -54
  282. package/lib/contracts/viz-config.d.ts +1 -0
  283. package/lib/geometry/contour.d.ts +24 -0
  284. package/lib/geometry/raster.d.ts +42 -0
  285. package/lib/geometry/ring.d.ts +67 -0
  286. package/lib/hex-color-picker/hex-color-picker.component.d.ts +61 -27
  287. package/lib/implementations/base-store-visualizer.d.ts +61 -15
  288. package/lib/implementations/napari-js/napari-axes-gizmo.d.ts +36 -0
  289. package/lib/implementations/napari-js/napari-display-state.d.ts +76 -0
  290. package/lib/implementations/napari-js/napari-helpers.d.ts +129 -0
  291. package/lib/implementations/napari-js/napari-image-2d-scene.d.ts +162 -0
  292. package/lib/implementations/napari-js/napari-loading-badge.d.ts +0 -1
  293. package/lib/implementations/napari-js/napari-loading-state.d.ts +30 -0
  294. package/lib/implementations/napari-js/napari-region-overlay.d.ts +62 -70
  295. package/lib/implementations/napari-js/napari-scene.d.ts +93 -0
  296. package/lib/implementations/napari-js/napari-spatial-2d-scene.d.ts +163 -0
  297. package/lib/implementations/napari-js/napari-spatial-3d-scene.d.ts +212 -0
  298. package/lib/implementations/napari-js/napari-spatial-encoding.d.ts +114 -0
  299. package/lib/implementations/napari-js/napari-spatial-hover.d.ts +89 -0
  300. package/lib/implementations/napari-js/napari-spatial-scene.d.ts +118 -0
  301. package/lib/implementations/napari-js/napari-spatial-tiles.d.ts +56 -263
  302. package/lib/implementations/napari-js/napari-spatial-tooltip.d.ts +1 -1
  303. package/lib/implementations/napari-js/napari-surface-scene.d.ts +76 -0
  304. package/lib/implementations/napari-js/napari-tile-client.d.ts +214 -0
  305. package/lib/implementations/napari-js/napari-tool-bridge.d.ts +179 -0
  306. package/lib/implementations/napari-js/napari-visualizer.service.d.ts +121 -878
  307. package/lib/implementations/napari-js/napari-volume-scene.d.ts +104 -0
  308. package/lib/implementations/napari-js/napari-volume-z-handle.d.ts +8 -12
  309. package/lib/implementations/napari-js/region-overlay/overlay-projection.d.ts +59 -0
  310. package/lib/implementations/napari-js/region-overlay/region-edit.d.ts +15 -0
  311. package/lib/implementations/napari-js/region-overlay/region-svg-renderer.d.ts +54 -0
  312. package/lib/implementations/napari-js/spatial-tiles/categorical-lookup.d.ts +26 -0
  313. package/lib/implementations/napari-js/spatial-tiles/cell-layers.d.ts +70 -0
  314. package/lib/implementations/napari-js/spatial-tiles/density-layer.d.ts +61 -0
  315. package/lib/implementations/napari-js/spatial-tiles/layer-groups.d.ts +67 -0
  316. package/lib/implementations/napari-js/spatial-tiles/plan-context.d.ts +38 -0
  317. package/lib/implementations/napari-js/spatial-tiles/transcript-hover.d.ts +71 -0
  318. package/lib/implementations/napari-js/spatial-tiles/transcript-jobs.d.ts +105 -0
  319. package/lib/implementations/napari-js/spatial-tiles/transcript-layers.d.ts +56 -0
  320. package/lib/implementations/osd/display-pipeline.d.ts +38 -9
  321. package/lib/implementations/osd/histogram-sampler.d.ts +22 -12
  322. package/lib/implementations/osd/openseadragon-viewer-options.d.ts +35 -0
  323. package/lib/implementations/osd/openseadragon-visualizer.service.d.ts +78 -354
  324. package/lib/implementations/osd/osd-coordinate-transform.d.ts +2 -1
  325. package/lib/implementations/osd/osd-coords.d.ts +6 -4
  326. package/lib/implementations/osd/osd-export.d.ts +36 -0
  327. package/lib/implementations/osd/osd-lib.d.ts +7 -0
  328. package/lib/implementations/osd/osd-navigator-chrome.d.ts +69 -0
  329. package/lib/implementations/osd/osd-pixel-readback.d.ts +22 -0
  330. package/lib/implementations/osd/osd-region-overlay-draw.d.ts +37 -0
  331. package/lib/implementations/osd/osd-region-overlay.d.ts +36 -48
  332. package/lib/implementations/osd/osd-scale-bar.d.ts +8 -6
  333. package/lib/implementations/osd/osd-tile-source.d.ts +49 -0
  334. package/lib/implementations/osd/osd-viewer-like.d.ts +100 -0
  335. package/lib/implementations/osd/osd-viewer-mount.d.ts +14 -0
  336. package/lib/implementations/osd/osd-viewport.d.ts +77 -0
  337. package/lib/implementations/osd/osd-zoom.d.ts +6 -0
  338. package/lib/implementations/osd/simple-source.d.ts +161 -0
  339. package/lib/implementations/osd/slice-cache.d.ts +12 -3
  340. package/lib/implementations/osd/tile-client.d.ts +11 -14
  341. package/lib/implementations/osd/tile-recolor.d.ts +141 -0
  342. package/lib/implementations/plotly/omics-trace-builders.d.ts +32 -20
  343. package/lib/implementations/plotly/plotly-image-loader.d.ts +49 -0
  344. package/lib/implementations/plotly/plotly-image-recolor.d.ts +75 -0
  345. package/lib/implementations/plotly/plotly-intensity-inset.d.ts +8 -0
  346. package/lib/implementations/plotly/plotly-isosurface-controls.d.ts +38 -0
  347. package/lib/implementations/plotly/plotly-layouts.d.ts +188 -0
  348. package/lib/implementations/plotly/plotly-readback.d.ts +41 -0
  349. package/lib/implementations/plotly/plotly-region-overlay.d.ts +2 -2
  350. package/lib/implementations/plotly/plotly-shape-projection.d.ts +92 -0
  351. package/lib/implementations/plotly/plotly-trace-builders.d.ts +23 -12
  352. package/lib/implementations/plotly/plotly-zoom-controller.d.ts +86 -0
  353. package/lib/implementations/plotly/plotly.service.d.ts +136 -401
  354. package/lib/implementations/region-centroids.d.ts +14 -0
  355. package/lib/implementations/simple-slice-access.service.d.ts +7 -4
  356. package/lib/implementations/spatial-data-http/spatial-data-http.service.d.ts +42 -18
  357. package/lib/implementations/spatial-data-http/spatial-wire.d.ts +34 -9
  358. package/lib/implementations/tile-server/index.d.ts +5 -0
  359. package/lib/implementations/tile-server/native-histogram.d.ts +25 -0
  360. package/lib/implementations/tile-server/poll-descriptor.d.ts +39 -0
  361. package/lib/implementations/tile-server/tile-protocol.d.ts +80 -0
  362. package/lib/implementations/tile-server/transport.d.ts +62 -0
  363. package/lib/intensity/intensity-profile.d.ts +66 -0
  364. package/lib/intensity/intensity-profile.service.d.ts +108 -0
  365. package/lib/intensity-inset/intensity-inset.component.d.ts +58 -0
  366. package/lib/models/bezier.d.ts +0 -5
  367. package/lib/models/class-preset.d.ts +18 -0
  368. package/lib/models/geometry.d.ts +14 -48
  369. package/lib/models/polygon-edit.d.ts +72 -0
  370. package/lib/models/polygon-factory.d.ts +40 -0
  371. package/lib/models/region-clone.d.ts +20 -0
  372. package/lib/models/region-geojson.d.ts +17 -0
  373. package/lib/models/region.d.ts +40 -4
  374. package/lib/models/shape.d.ts +8 -0
  375. package/lib/plot-mode/contribution-host.d.ts +125 -0
  376. package/lib/plot-mode/plot-type-menu.d.ts +60 -0
  377. package/lib/plot-mode/tool-params-model.d.ts +49 -0
  378. package/lib/plot.utilities.d.ts +19 -30
  379. package/lib/processing/processing-image.d.ts +5 -2
  380. package/lib/provide-visualization.d.ts +19 -2
  381. package/lib/region-editor/classes-panel/classes-panel.component.d.ts +56 -0
  382. package/lib/region-editor/file-stem.d.ts +7 -0
  383. package/lib/region-editor/manage-classes-dialog/manage-classes-dialog.component.d.ts +45 -0
  384. package/lib/region-editor/mask-export.service.d.ts +54 -0
  385. package/lib/region-editor/mask-raster.d.ts +2 -8
  386. package/lib/region-editor/region-color-dialog/region-color-dialog.component.d.ts +29 -0
  387. package/lib/region-editor/region-editor-help/region-editor-help.component.d.ts +11 -0
  388. package/lib/region-editor/region-editor.component.d.ts +166 -199
  389. package/lib/region-editor/region-metrics.d.ts +26 -0
  390. package/lib/region-editor/region-persistence.service.d.ts +69 -0
  391. package/lib/region-editor/region-table/region-table.component.d.ts +83 -0
  392. package/lib/region-editor/save-mask-dialog/save-mask-dialog.component.d.ts +36 -0
  393. package/lib/region-editor/save-regions-dialog/save-regions-dialog.component.d.ts +37 -0
  394. package/lib/region-ops.service.d.ts +1 -7
  395. package/lib/region-overlay/region-geometry.d.ts +157 -0
  396. package/lib/region-overlay/region-hit-test.d.ts +38 -0
  397. package/lib/region-overlay/svg-region-renderer.d.ts +110 -0
  398. package/lib/render-orchestrator.d.ts +6 -2
  399. package/lib/render-session.d.ts +75 -0
  400. package/lib/routing-visualizer.service.d.ts +110 -58
  401. package/lib/spatial/density-raster.d.ts +6 -0
  402. package/lib/spatial/embedding-compute-coordinator.d.ts +123 -0
  403. package/lib/spatial/embedding-compute.d.ts +21 -2
  404. package/lib/spatial/glyphs.d.ts +16 -0
  405. package/lib/spatial/lod.d.ts +94 -0
  406. package/lib/spatial/spatial-controls.facade.d.ts +64 -0
  407. package/lib/spatial/spatial-density.d.ts +15 -5
  408. package/lib/spatial/spatial-encoding.d.ts +11 -44
  409. package/lib/spatial/spatial-expression.d.ts +40 -22
  410. package/lib/spatial/spatial-framing.d.ts +1 -0
  411. package/lib/spatial/spatial-heatmap.d.ts +2 -0
  412. package/lib/spatial/spatial-hover.d.ts +4 -16
  413. package/lib/spatial/spatial-panel-model.d.ts +135 -0
  414. package/lib/spatial/spatial-selection.d.ts +21 -26
  415. package/lib/spatial/spatial-tile-merge.d.ts +41 -0
  416. package/lib/spatial/spatial-tiles.d.ts +9 -256
  417. package/lib/spatial/stats.d.ts +38 -0
  418. package/lib/spatial/transcript-grouping.d.ts +119 -0
  419. package/lib/spatial-controls/spatial-cells-panel/spatial-cells-panel.component.d.ts +64 -0
  420. package/lib/spatial-controls/spatial-charts/chart-data-model.d.ts +153 -0
  421. package/lib/spatial-controls/spatial-charts/chart-help.d.ts +69 -0
  422. package/lib/spatial-controls/spatial-charts/plotly-chart-host.d.ts +98 -0
  423. package/lib/spatial-controls/spatial-charts/spatial-chart-window/spatial-chart-window.component.d.ts +26 -0
  424. package/lib/spatial-controls/spatial-charts/spatial-charts.component.d.ts +101 -260
  425. package/lib/spatial-controls/spatial-charts/spatial-embedding-controls/spatial-embedding-controls.component.d.ts +43 -0
  426. package/lib/spatial-controls/spatial-charts/spatial-heatmap-controls/spatial-heatmap-controls.component.d.ts +27 -0
  427. package/lib/spatial-controls/spatial-controls.component.d.ts +68 -448
  428. package/lib/spatial-controls/spatial-gene-picker.d.ts +86 -0
  429. package/lib/spatial-controls/spatial-gene-tree/spatial-gene-tree.component.d.ts +102 -0
  430. package/lib/spatial-controls/spatial-groups-panel/spatial-groups-panel.component.d.ts +72 -0
  431. package/lib/spatial-controls/spatial-key/spatial-key.component.d.ts +66 -0
  432. package/lib/spatial-controls/spatial-key/spatial-key.model.d.ts +33 -0
  433. package/lib/spatial-controls/spatial-marker-genes-form/spatial-marker-genes-form.component.d.ts +64 -0
  434. package/lib/spatial-controls/spatial-observations-panel/spatial-observations-panel.component.d.ts +117 -0
  435. package/lib/spatial-controls/spatial-transcripts-panel/spatial-transcripts-panel.component.d.ts +142 -0
  436. package/lib/store/class-color.util.d.ts +35 -1
  437. package/lib/store/region-history.d.ts +66 -0
  438. package/lib/store/region-scope-cache.d.ts +63 -0
  439. package/lib/store/region-selection.d.ts +36 -0
  440. package/lib/store/region-store.service.d.ts +104 -112
  441. package/lib/store/visualizer-store.service.d.ts +22 -33
  442. package/lib/toast-outlets.d.ts +4 -3
  443. package/lib/toolbar/brush/brush-tool.service.d.ts +31 -98
  444. package/lib/toolbar/canvas-tools.d.ts +20 -0
  445. package/lib/toolbar/crop/slide-crop.d.ts +10 -4
  446. package/lib/toolbar/plot-type-selector/plot-type-selector.component.d.ts +38 -0
  447. package/lib/toolbar/region-tools/region-tool-buttons.d.ts +21 -0
  448. package/lib/toolbar/region-tools/region-tools.component.d.ts +60 -0
  449. package/lib/toolbar/segmentation/cell-segment-tool.service.d.ts +10 -14
  450. package/lib/toolbar/segmentation/cellpose-segmenter.service.d.ts +12 -2
  451. package/lib/toolbar/segmentation/onnx-sam-session.d.ts +22 -3
  452. package/lib/toolbar/segmentation/pending-calls.d.ts +31 -0
  453. package/lib/toolbar/segmentation/sam-model-registry.d.ts +9 -3
  454. package/lib/toolbar/segmentation/sam-onnx-core.d.ts +18 -5
  455. package/lib/toolbar/segmentation/sam-point-tool.service.d.ts +46 -32
  456. package/lib/toolbar/segmentation/sam-prompt.d.ts +7 -31
  457. package/lib/toolbar/segmentation/sam-session.service.d.ts +74 -0
  458. package/lib/toolbar/segmentation/sam-tool.service.d.ts +23 -30
  459. package/lib/toolbar/segmentation/segmentation-commit.d.ts +8 -0
  460. package/lib/toolbar/segmentation/segmentation-runner.d.ts +52 -0
  461. package/lib/toolbar/segmentation-tools/segmentation-tools.component.d.ts +87 -0
  462. package/lib/toolbar/stack-controls/stack-controls.component.d.ts +40 -0
  463. package/lib/toolbar/tool-kit/async-tool-status.d.ts +21 -0
  464. package/lib/toolbar/tool-kit/canvas-tool-manager.d.ts +36 -0
  465. package/lib/toolbar/tool-kit/canvas-tool.d.ts +86 -0
  466. package/lib/toolbar/tool-kit/frame-pixels.d.ts +45 -0
  467. package/lib/toolbar/tool-kit/mask-stroke-editor.d.ts +125 -0
  468. package/lib/toolbar/tool-kit/matrix-frame.d.ts +40 -0
  469. package/lib/toolbar/tool-kit/tool-overlay.d.ts +60 -0
  470. package/lib/toolbar/tool-kit/undo-gesture.d.ts +17 -0
  471. package/lib/toolbar/tool-slider/tool-slider.component.d.ts +33 -0
  472. package/lib/toolbar/toolbar-help-dialog/toolbar-help-dialog.component.d.ts +24 -0
  473. package/lib/toolbar/toolbar.component.d.ts +47 -83
  474. package/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.d.ts +33 -40
  475. package/lib/toolbar/view-controls/view-controls.component.d.ts +54 -0
  476. package/lib/toolbar/wand/wand-region-grow.d.ts +49 -0
  477. package/lib/toolbar/wand/wand-tool.service.d.ts +39 -124
  478. package/lib/toolbar/wand/wand.service.d.ts +22 -116
  479. package/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.d.ts +17 -37
  480. package/lib/util/mark-for-check.d.ts +11 -0
  481. package/lib/util/supersede.d.ts +53 -0
  482. package/lib/visualization.module.d.ts +18 -39
  483. package/lib/visualizer/floating-drag.directive.d.ts +30 -0
  484. package/lib/visualizer/region-actions.d.ts +54 -0
  485. package/lib/visualizer/region-load.d.ts +27 -0
  486. package/lib/visualizer/spatial-dataset-binder.d.ts +74 -0
  487. package/lib/visualizer/tool-modes.d.ts +49 -0
  488. package/lib/visualizer/viewer-shortcuts.d.ts +71 -0
  489. package/lib/visualizer/visualizer-context-menu.d.ts +55 -0
  490. package/lib/visualizer.component.d.ts +201 -447
  491. package/lib/workers/spatial-math-ops.d.ts +65 -0
  492. package/lib/workers/spatial-math-worker.d.ts +7 -0
  493. package/lib/workers/spatial-math.d.ts +74 -0
  494. package/package.json +12 -3
  495. package/src/lib/styles/_viz-icons-rules.scss +73 -0
  496. package/src/lib/styles/viz-icons.scss +14 -0
  497. package/esm2022/lib/implementations/napari-js/napari-axes-labels.mjs +0 -97
  498. package/esm2022/lib/implementations/napari-js/napari-navigator.mjs +0 -195
  499. package/esm2022/lib/implementations/napari-js/napari-scale-bar.mjs +0 -92
  500. package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-BwP-A11m.mjs +0 -329
  501. package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-BwP-A11m.mjs.map +0 -1
  502. package/lib/implementations/napari-js/napari-axes-labels.d.ts +0 -49
  503. package/lib/implementations/napari-js/napari-navigator.d.ts +0 -72
  504. package/lib/implementations/napari-js/napari-scale-bar.d.ts +0 -35
  505. package/src/lib/assets/cell-detection.svg +0 -37
  506. package/src/lib/assets/click.svg +0 -1
  507. package/src/lib/assets/colormap-luts.json +0 -1
  508. package/src/lib/assets/edit-selection-white.svg +0 -47
  509. package/src/lib/assets/icons/colormap-earth.png +0 -0
  510. package/src/lib/assets/icons/colormap-placeholder.png +0 -0
  511. package/src/lib/assets/icons/colormap-pubu.png +0 -0
  512. package/src/lib/assets/interactive-segmentation-cursor.svg +0 -1
  513. package/src/lib/assets/picture-segmentation.svg +0 -43
  514. package/src/lib/assets/test/download-379.svg +0 -1
  515. package/src/lib/assets/test/download-98.svg +0 -1
  516. package/src/lib/assets/test/json-1.svg +0 -1
  517. package/src/lib/assets/test/load-53.svg +0 -1
  518. package/src/lib/assets/test/mask-44.svg +0 -1
  519. package/src/lib/assets/test/mask-download-44.svg +0 -15
  520. package/src/lib/assets/test/mask-save-44.svg +0 -11
  521. package/src/lib/assets/test/overlay-1.svg +0 -1
  522. package/src/lib/assets/test/overlay-4.svg +0 -1
  523. package/src/lib/assets/test/region-26.svg +0 -1
  524. package/src/lib/assets/test/save-56.svg +0 -1
  525. package/src/lib/assets/test/upload-43.svg +0 -1
@@ -1,24 +1,32 @@
1
- import { Component, Inject, Input, } from '@angular/core';
2
- import { Subscription, combineLatest } from 'rxjs';
3
- import * as Plotly from 'plotly.js-dist-min';
1
+ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, Inject, Input, inject, NgZone, } from '@angular/core';
2
+ import { markForCheckInZone } from '../../util/mark-for-check';
3
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
+ import { CommonModule } from '@angular/common';
5
+ import { FormsModule } from '@angular/forms';
6
+ import { DropdownModule } from 'primeng/dropdown';
7
+ import { SelectButtonModule } from 'primeng/selectbutton';
8
+ import { TooltipModule } from 'primeng/tooltip';
9
+ import { combineLatest } from 'rxjs';
4
10
  import { VISUALIZER } from '../../contracts/visualizer.contract';
5
11
  import { DEFAULT_SPATIAL_VIEW } from '../../contracts/display-types';
6
- import { emptySelection, maskToIndices, } from '../../spatial/spatial-selection';
7
- import { cellsAsGroups, heatmapMatrix } from '../../spatial/spatial-heatmap';
8
- import { geneOptionsFor } from '../../spatial/gene-search';
9
- import { EmbeddingComputeRun } from '../../spatial/embedding-compute';
10
- import { benefitsFromGrouping, buildCountTraces, buildHeatmapTraces, buildOmicsTraces, buildEmbeddingTraces, countsLayout, heatmapLayout, omicsLayout, embeddingLayout, } from '../../implementations/plotly/omics-trace-builders';
12
+ import { emptySelection, maskToIndices } from '../../spatial/spatial-selection';
13
+ import { cellsAsGroups } from '../../spatial/spatial-heatmap';
14
+ import { computeHeatmapMatrixAsync } from '../../workers/spatial-math';
15
+ import { Supersede } from '../../util/supersede';
16
+ import { BROWSER_TSNE_MAX_OBSERVATIONS, EmbeddingComputeCoordinator, } from '../../spatial/embedding-compute-coordinator';
17
+ import { ChartDataModel } from './chart-data-model';
18
+ import { EMBEDDING_CONFIG, PlotlyChartHost } from './plotly-chart-host';
19
+ import { chartKindOptions, embeddingNote, heatmapNote, kindHelp } from './chart-help';
20
+ import { benefitsFromGrouping, buildCountTraces, buildHeatmapTraces, countByCategory, buildOmicsTraces, buildEmbeddingTraces, countsLayout, heatmapLayout, omicsLayout, embeddingLayout, } from '../../implementations/plotly/omics-trace-builders';
21
+ import { SpatialHeatmapControlsComponent } from './spatial-heatmap-controls/spatial-heatmap-controls.component';
22
+ import { SpatialEmbeddingControlsComponent } from './spatial-embedding-controls/spatial-embedding-controls.component';
23
+ import { SpatialChartWindowComponent } from './spatial-chart-window/spatial-chart-window.component';
11
24
  import * as i0 from "@angular/core";
12
25
  import * as i1 from "@angular/common";
13
26
  import * as i2 from "@angular/forms";
14
- import * as i3 from "primeng/button";
15
- import * as i4 from "primeng/tooltip";
16
- import * as i5 from "primeng/dropdown";
17
- import * as i6 from "primeng/dialog";
18
- import * as i7 from "primeng/progressbar";
19
- import * as i8 from "primeng/checkbox";
20
- import * as i9 from "primeng/multiselect";
21
- import * as i10 from "primeng/selectbutton";
27
+ import * as i3 from "primeng/dropdown";
28
+ import * as i4 from "primeng/selectbutton";
29
+ import * as i5 from "primeng/tooltip";
22
30
  /**
23
31
  * Widen the help tooltip, once per document.
24
32
  *
@@ -37,31 +45,13 @@ function ensureHelpTipStyle() {
37
45
  return;
38
46
  const style = document.createElement('style');
39
47
  style.id = HELP_TIP_STYLE_ID;
40
- style.textContent = '.sx-help-tip .p-tooltip-text { max-width: none; width: 24rem; '
41
- + 'line-height: 1.45; }\n.sx-help-tip { max-width: none; }';
48
+ style.textContent =
49
+ '.sx-help-tip .p-tooltip-text { max-width: none; width: 24rem; ' +
50
+ 'line-height: 1.45; }\n.sx-help-tip { max-width: none; }';
42
51
  document.head.appendChild(style);
43
52
  }
44
53
  /** Per-instance chart-div id source — see {@link SpatialChartsComponent.chartDiv}. */
45
54
  let chartInstanceSeq = 0;
46
- /** Plotly config: a static-ish analysis chart, not an editable figure. */
47
- const CHART_CONFIG = {
48
- displaylogo: false,
49
- responsive: true,
50
- modeBarButtonsToRemove: ['lasso2d', 'select2d', 'autoScale2d'],
51
- };
52
- /**
53
- * Config for the embedding, which unlike the distributions is a plot you SELECT in.
54
- *
55
- * The lasso and box-select are kept — drawing round a cluster is how a population gets
56
- * picked out of a UMAP — and the wheel zooms, because exploring an embedding means
57
- * zooming into a cluster and reaching for a toolbar button to do it breaks that.
58
- */
59
- const EMBEDDING_CONFIG = {
60
- displaylogo: false,
61
- responsive: true,
62
- scrollZoom: true,
63
- modeBarButtonsToRemove: ['autoScale2d'],
64
- };
65
55
  /**
66
56
  * Distribution charts over the spatial-omics values — histogram, violin, box —
67
57
  * **linked to the map**: they chart whatever the map is coloured by, and narrow
@@ -81,6 +71,10 @@ const EMBEDDING_CONFIG = {
81
71
  * Depends only on {@link ISpatialControls} through the `VISUALIZER` contract
82
72
  * token, like `SpatialControlsComponent`. Trace building lives in the pure
83
73
  * `omics-trace-builders`; this component only moves data and owns the div.
74
+ *
75
+ * OnPush: its state lives in collaborators that change asynchronously (the data loads,
76
+ * the t-SNE run, the plot host), so the controls' streams, the run's state, every
77
+ * finished draw and every load start mark it for check.
84
78
  */
85
79
  export class SpatialChartsComponent {
86
80
  /**
@@ -103,70 +97,83 @@ export class SpatialChartsComponent {
103
97
  get active() {
104
98
  return this.isActive;
105
99
  }
106
- static { this.CONTINUOUS_KINDS = [
107
- { label: 'Histogram', value: 'histogram' },
108
- { label: 'Violin', value: 'violin' },
109
- { label: 'Box', value: 'box' },
110
- ]; }
111
- static { this.CATEGORICAL_KINDS = [
112
- { label: 'Counts', value: 'counts' },
113
- ]; }
114
- /** Available whatever the map is coloured by: the heatmap's subject is a GENE
115
- * LIST crossed with a grouping, not the active colour source. */
116
- static { this.ALWAYS_KINDS = [
117
- { label: 'Heatmap', value: 'heatmap' },
118
- ]; }
119
- /**
120
- * A t-SNE the dataset does not publish, offered so it can be computed here.
121
- *
122
- * A dropped-in `.h5ad` typically carries one UMAP and a PCA, and no t-SNE — the two
123
- * views answer different questions, so having only one is a real gap. Computing it
124
- * needs no expression data: t-SNE runs on the PCA scores, which is why this can happen
125
- * in the browser at all while PCA cannot.
126
- */
127
- static { this.COMPUTABLE = [
128
- { name: 'local:tsne', label: 't-SNE (compute)', dims: 2, derived: true },
129
- { name: 'local:tsne3d', label: 't-SNE 3D (compute)', dims: 3, derived: true },
130
- ]; }
131
- /** The menu suffix that says a click will start work; not part of the name. */
132
- static { this.COMPUTE_SUFFIX = ' (compute)'; }
133
100
  /**
134
- * Largest dataset this will offer to embed in the browser.
135
- *
136
- * t-SNE is O(N²) per iteration — vectorised on the GPU, but not approximated — so the
137
- * cost is quadratic in observations. Measured end to end in Firefox on WebGPU: 2,688
138
- * points took 95 s. That scales to roughly five and a half minutes at this threshold,
139
- * and to about 76 minutes at seqFISH's 19,416, which was also measured offline.
140
- *
141
- * Past this the option is WITHDRAWN rather than offered with a warning. An hour-long
142
- * job started from a button is not a choice a reader can meaningfully consent to in a
143
- * dialog, and a t-SNE that large belongs in the offline pipeline, where it can run
144
- * once and be served to everyone.
101
+ * Largest dataset this will offer to embed in the browser — see
102
+ * {@link BROWSER_TSNE_MAX_OBSERVATIONS}.
145
103
  */
146
- static { this.BROWSER_TSNE_MAX_OBSERVATIONS = 5000; }
147
- /** Measured anchor for the estimate: seconds for 2,688 points on WebGPU. */
148
- static { this.TSNE_SECONDS_AT = { seconds: 95, observations: 2688 }; }
149
- /** Offered only when the dataset publishes an embedding to draw. */
150
- // Labelled for what it is rather than for one instance of it: this view draws whatever
151
- // embedding the dataset publishes — a UMAP, a PCA, a t-SNE — and calling the tab "UMAP"
152
- // while it showed a PCA would be a lie the picker beneath it immediately contradicts.
153
- static { this.EMBEDDING_KINDS = [
154
- { label: 'Embedding', value: 'embedding' },
155
- ]; }
104
+ static { this.BROWSER_TSNE_MAX_OBSERVATIONS = BROWSER_TSNE_MAX_OBSERVATIONS; }
105
+ /** 0..1 while running, or null before the first report. Drives the progress bar. */
106
+ get computeFraction() {
107
+ return this.compute.state.fraction;
108
+ }
109
+ /** Which backend the worker got — 'webgpu', 'wasm' or 'cpu'. Shown while running. */
110
+ get computeBackend() {
111
+ return this.compute.state.backend;
112
+ }
113
+ get computeMessage() {
114
+ return this.compute.state.message;
115
+ }
116
+ get computeError() {
117
+ return this.compute.state.error;
118
+ }
119
+ /** Where a computation stands, for the embedding controls. */
120
+ get computeState() {
121
+ return this.compute.state;
122
+ }
123
+ /** True when a t-SNE is missing but the dataset is too big to embed here. */
124
+ get tsneTooLarge() {
125
+ return this.compute.tooLarge;
126
+ }
156
127
  /** The kinds the ACTIVE subject can be drawn as. A category code is a label,
157
128
  * not a magnitude, so a histogram of it would be meaningless — what a
158
- * categorical column has is a frequency distribution. */
129
+ * categorical column has is a frequency distribution.
130
+ *
131
+ * Bound to a `p-selectButton`, so the SAME array is returned until what it is built
132
+ * from changes: a fresh array per change-detection pass makes PrimeNG re-render the
133
+ * buttons, and a button re-rendered under the pointer swallows the click. */
159
134
  get kindOptions() {
160
- return [
161
- ...(this.categorical
162
- ? SpatialChartsComponent.CATEGORICAL_KINDS
163
- : SpatialChartsComponent.CONTINUOUS_KINDS),
164
- ...SpatialChartsComponent.ALWAYS_KINDS,
165
- // An embedding is a property of the DATASET, not of the active colour source, so
166
- // it is offered whenever one is published and never otherwise — a tab that draws
167
- // nothing is worse than an absent one.
168
- ...(this.embeddings.length > 0 ? SpatialChartsComponent.EMBEDDING_KINDS : []),
169
- ];
135
+ const categorical = this.data.isCategorical;
136
+ const embeddings = this.embeddings.length > 0;
137
+ const hit = this.kindOptionsMemo;
138
+ if (hit && hit.categorical === categorical && hit.embeddings === embeddings) {
139
+ return hit.options;
140
+ }
141
+ const options = chartKindOptions(categorical, embeddings);
142
+ this.kindOptionsMemo = { categorical, embeddings, options };
143
+ return options;
144
+ }
145
+ /** Embeddings the dataset offers, and which of them is drawn. */
146
+ get embeddings() {
147
+ return this.data.embeddings;
148
+ }
149
+ get embedding() {
150
+ return this.data.embedding;
151
+ }
152
+ /** Genes the heatmap's rows are. */
153
+ get heatmapGenes() {
154
+ return this.data.heatmapGenes;
155
+ }
156
+ /** The heatmap gene picker's options. */
157
+ get geneOptions() {
158
+ return this.data.geneOptions;
159
+ }
160
+ /** Categorical column the violin/box splits by; null = one trace for all. */
161
+ get groupBy() {
162
+ return this.data.groupBy;
163
+ }
164
+ get groupOptions() {
165
+ return this.data.groupOptions;
166
+ }
167
+ /** Set when the active colour source cannot be charted, for an inline hint. */
168
+ get notice() {
169
+ return this.data.notice;
170
+ }
171
+ set notice(text) {
172
+ this.data.notice = text;
173
+ }
174
+ /** A load is in flight. */
175
+ get busy() {
176
+ return this.data.busy;
170
177
  }
171
178
  /** Past this many selected cells the per-cell view is a texture, not a
172
179
  * readable panel, so the columns go back to being classes. */
@@ -181,17 +188,21 @@ export class SpatialChartsComponent {
181
188
  * were dropped. See `maxCols` in `spatial-heatmap.ts`.
182
189
  */
183
190
  static { this.HEATMAP_MAX_COLUMNS = 40; }
191
+ /** Re-render this OnPush view, re-entering the zone when the change came from outside it. */
192
+ changed() {
193
+ markForCheckInZone(this.cdr, this.zone);
194
+ }
184
195
  constructor(viz) {
185
196
  this.viz = viz;
186
197
  this.isActive = true;
187
- /** Per-instance, for the same reason the visualizer's plot div is
188
- * (`visualizer.component.ts`): two mounted charts sharing one DOM id means
189
- * `getElementById` hands both of them the first element, so one instance draws
190
- * into — or purges — the other's canvas. */
191
198
  /** One increment per INSTANCE — a static initializer would run once per class and
192
199
  * hand every instance the same id, which is the bug this id exists to avoid. Declared
193
200
  * before the ids because field initializers run in order. */
194
201
  this.seq = ++chartInstanceSeq;
202
+ /** Per-instance, for the same reason the visualizer's plot div is
203
+ * (`visualizer.component.ts`): two mounted charts sharing one DOM id means
204
+ * `getElementById` hands both of them the first element, so one instance draws
205
+ * into — or purges — the other's canvas. */
195
206
  this.chartDiv = `spatial-charts-plot-${this.seq}`;
196
207
  /**
197
208
  * The div inside the detached window.
@@ -217,96 +228,32 @@ export class SpatialChartsComponent {
217
228
  * window shows, not yank it back into the dialog and make them detach it again.
218
229
  */
219
230
  this.detached = false;
220
- /** Coordinates computed in this browser, by name. Not persisted: a reload recomputes. */
221
- this.computed = new Map();
222
- /** The live run, when one is going. */
223
- this.computeRun = null;
224
- /**
225
- * Bumped by every dataset change and by teardown, so a computation in progress can tell
226
- * that what it is computing is no longer what is on screen.
227
- *
228
- * A run is several awaits long — the PCA scores arrive over HTTP, then the worker takes
229
- * a minute or more — and the dataset can change at any of them, including BEFORE
230
- * `computeRun` exists to be terminated. Without a token, the scores fetched for one
231
- * dataset get embedded and drawn over another's observations: a plot of two datasets at
232
- * once, which reads as a strange-looking t-SNE rather than as a bug.
233
- */
234
- this.computeToken = 0;
235
- /** 0..1 while running, or null before the first report. Drives the progress bar. */
236
- this.computeFraction = null;
237
- /** Which backend the worker got — 'webgpu', 'wasm' or 'cpu'. Shown while running. */
238
- this.computeBackend = null;
239
- this.computeMessage = null;
240
- this.computeError = null;
241
- /** True when a t-SNE is missing but the dataset is too big to embed here. */
242
- this.tsneTooLarge = false;
243
- /** Observations in the live dataset, kept for the cost estimate. */
244
- this.observationCount = 0;
231
+ /** The t-SNE computed in this browser: what to offer, the run, its progress, its results. */
232
+ this.compute = new EmbeddingComputeCoordinator();
233
+ this.kindOptionsMemo = null;
245
234
  this.controls = null;
246
235
  this.kind = 'histogram';
247
- /** Embeddings the dataset publishes, and which of them is drawn. */
248
- this.embeddings = [];
249
- this.embedding = null;
250
- this.embeddingCoords = null;
251
- /** The categorical colouring behind the embedding's colours, kept so a redraw for a
252
- * selection change does not refetch the column. */
253
- this.embeddingCodes = null;
254
- /**
255
- * Genes the heatmap's rows are, and the vectors behind them.
256
- *
257
- * Local rather than in `SpatialViewState`, matching how the chart KIND is
258
- * held: this is what the panel is charting, not what the map is drawing, and
259
- * the renderer has no use for it.
260
- */
261
- this.heatmapGenes = [];
236
+ /** The data charted, and its loading — see {@link ChartDataModel}. */
237
+ this.data = new ChartDataModel();
262
238
  /** Z-score each gene across the groups. On by default: without it one loud
263
239
  * gene saturates the scale and the rest of the panel reads as blank. */
264
240
  this.heatmapZScore = true;
265
241
  /** Columns the cap dropped from the last render, for the note. */
266
242
  this.heatmapHidden = 0;
267
- this.geneOptions = [];
268
- /** Every gene the dataset inlined. `geneOptions` is only ever the best few hundred of
269
- * these — see `gene-search.ts` for why a whole-transcriptome list cannot be handed to
270
- * the control whole. */
271
- this.geneNames = [];
272
- /** What is typed in the picker's filter box, so the options can be rebuilt when the
273
- * selection changes without losing the query. */
274
- this.geneQuery = '';
275
- /** Fetched vectors by gene name, so adding a fourth gene does not refetch the
276
- * first three — each is a full per-observation Float32Array. */
277
- this.geneCache = new Map();
278
- /** Categorical column the violin/box splits by; null = one trace for all. */
279
- this.groupBy = null;
280
- this.groupOptions = [];
281
243
  /** What the map is coloured by — the chart's subject. */
282
244
  this.colorBy = null;
283
245
  this.selectionCount = 0;
284
- /** Set when the active colour source cannot be charted, for an inline hint. */
285
- this.notice = null;
286
- this.busy = false;
287
246
  this.view = { ...DEFAULT_SPATIAL_VIEW };
288
247
  this.selection = emptySelection();
289
- this.values = null;
290
- /** Set instead of `values` when the colour source is a categorical column: its
291
- * distribution is counts per category, not a histogram of its codes. */
292
- this.categorical = null;
293
- this.grouping = null;
294
- /** Guards the async value fetch: a fast colour-source change can resolve out
295
- * of order, and a stale vector would be charted against the new label. */
296
- this.token = 0;
297
- /** The same guard for the grouping fetch, which the user can change as fast. */
298
- this.groupToken = 0;
299
248
  /** Whether the first view emission has been handled. */
300
249
  this.primed = false;
301
- this.subs = new Subscription();
302
- /**
303
- * The height the drawn layout asked for, or null where it autosizes.
304
- *
305
- * {@link resize} needs this: the counts and heatmap layouts size their height
306
- * to their content — one band per bar, per gene row — while the distribution
307
- * kinds want to fill whatever height they are given.
308
- */
309
- this.drawnHeight = null;
250
+ this.destroyRef = inject(DestroyRef);
251
+ this.cdr = inject(ChangeDetectorRef);
252
+ this.zone = inject(NgZone);
253
+ /** Latest wins among heatmap draws, whose matrix may be computed in a worker. */
254
+ this.heatmapRender = new Supersede();
255
+ /** Plotly I/O for both the inline and the detached div. */
256
+ this.plot = new PlotlyChartHost();
310
257
  }
311
258
  ngOnInit() {
312
259
  // Before the port check: the help icon is in the template either way, and a panel
@@ -315,11 +262,12 @@ export class SpatialChartsComponent {
315
262
  this.controls = this.viz.getSpatialControls?.() ?? null;
316
263
  if (!this.controls)
317
264
  return;
318
- this.subs.add(combineLatest([
319
- this.controls.getViewState$(), this.controls.getSelection$(),
320
- ]).subscribe(([view, selection]) => {
321
- const sourceChanged = view.colorBy?.kind !== this.view.colorBy?.kind
322
- || view.colorBy?.name !== this.view.colorBy?.name;
265
+ // The run's progress, backend and errors, for the embedding controls.
266
+ this.compute.state$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.changed());
267
+ combineLatest([this.controls.getViewState$(), this.controls.getSelection$()])
268
+ .pipe(takeUntilDestroyed(this.destroyRef))
269
+ .subscribe(([view, selection]) => {
270
+ const sourceChanged = view.colorBy?.kind !== this.view.colorBy?.kind || view.colorBy?.name !== this.view.colorBy?.name;
323
271
  this.view = view;
324
272
  this.colorBy = view.colorBy;
325
273
  this.selection = selection;
@@ -336,52 +284,28 @@ export class SpatialChartsComponent {
336
284
  // A selection or log-scale change needs only a redraw of the same vector.
337
285
  void this.render();
338
286
  }
339
- }));
340
- this.subs.add(this.controls.getDataset$().subscribe((dataset) => {
341
- this.groupOptions = [
342
- { label: 'No grouping', value: null },
343
- ...(this.controls?.categoricalColumns() ?? []).map((n) => ({ label: n, value: n })),
344
- ];
345
- // A new dataset's columns are different; a carried-over group is meaningless.
346
- if (this.groupBy && !this.groupOptions.some((o) => o.value === this.groupBy)) {
347
- this.groupBy = null;
348
- this.grouping = null;
349
- }
350
- // The heatmap's rows come from the dataset's gene list. A dataset too wide
351
- // to inline its names offers none here — the panel's typeahead is the way
352
- // in for those, and the heatmap needs names it can list.
353
- this.geneNames = [...(dataset?.features?.names ?? [])];
354
- this.geneQuery = '';
355
- // Validated against the WHOLE list, not the visible options: those are capped, and
356
- // filtering the selection by them would drop genes the dataset still has.
357
- const known = new Set(this.geneNames);
358
- this.heatmapGenes = this.heatmapGenes.filter((n) => known.has(n));
359
- this.refreshGeneOptions();
360
- this.geneCache.clear();
361
- // Embeddings belong to the dataset, so they are re-read with it and the loaded
362
- // coordinates dropped: a UMAP from the previous dataset over these observations
363
- // would be a plot of two different things at once.
364
- this.computed.clear();
365
- this.abandonCompute();
366
- const published = dataset?.embeddings ? [...dataset.embeddings] : [];
287
+ this.changed();
288
+ });
289
+ this.controls
290
+ .getDataset$()
291
+ .pipe(takeUntilDestroyed(this.destroyRef))
292
+ .subscribe((dataset) => {
367
293
  // Offer to compute a t-SNE only where the dataset has PCA to embed and no t-SNE of
368
- // its own — otherwise the menu advertises work that cannot start, or duplicates
369
- // what is already served.
370
- const hasPca = published.some((e) => /pca/i.test(e.label ?? e.name));
371
- const hasTsne = published.some((e) => /tsne|t-sne/i.test(e.label ?? e.name));
372
- this.observationCount = dataset?.observations.count ?? 0;
373
- this.tsneTooLarge = hasPca && !hasTsne
374
- && this.observationCount > SpatialChartsComponent.BROWSER_TSNE_MAX_OBSERVATIONS;
375
- this.embeddings = hasPca && !hasTsne && !this.tsneTooLarge
376
- ? [...published, ...SpatialChartsComponent.COMPUTABLE]
377
- : published;
378
- this.embedding = this.embeddings[0] ?? null;
379
- this.embeddingCoords = null;
380
- this.embeddingCodes = null;
294
+ // its own; anything computed or computing for the previous dataset is dropped.
295
+ const embeddings = this.compute.setDataset(dataset?.embeddings ?? [], dataset?.observations.count ?? 0);
296
+ const switched = this.data.onDatasetChanged(dataset, this.controls?.categoricalColumns() ?? [], embeddings);
381
297
  // Nothing to draw for the kind that was selected; fall back rather than sit blank.
382
- if (this.kind === 'embedding' && this.embeddings.length === 0)
298
+ if (this.kind === 'embedding' && embeddings.length === 0)
383
299
  this.kind = 'histogram';
384
- }));
300
+ // Everything loaded was the previous dataset's: fetch afresh, even when the view
301
+ // does not re-emit (a colour source of the same name).
302
+ if (switched) {
303
+ if (this.groupBy)
304
+ void this.onGroupBy(this.groupBy);
305
+ void this.reload();
306
+ }
307
+ this.changed();
308
+ });
385
309
  }
386
310
  /**
387
311
  * Re-fit the plot to the panel's current width.
@@ -391,50 +315,20 @@ export class SpatialChartsComponent {
391
315
  * resizes only, so dragging a dialog edge reaches it through nothing at all.
392
316
  */
393
317
  resize() {
394
- this.refit(this.chartDiv);
395
- }
396
- /**
397
- * Re-fit whatever is plotted in `div` to its container.
398
- *
399
- * Shared by the panel and the detached window because the rule is the same and getting
400
- * it wrong is invisible until it is not: `autosize` takes BOTH dimensions from the
401
- * container, which is right only where the layout did not fix its own height. Measured:
402
- * autosizing a layout with an explicit height of 500 replaced it with the container's
403
- * 400. The counts and heatmap layouts size themselves to their row count, so the
404
- * window resize has to set the width ALONE for them and let the height stand.
405
- */
406
- refit(div) {
407
- const el = document.getElementById(div);
408
- if (!el)
409
- return;
410
- const width = Math.round(el.clientWidth);
411
- // Zero while the section is collapsed or the dialog is closed; relaying out
412
- // to a zero box makes Plotly compute a layout it does not recover from.
413
- if (width <= 0)
414
- return;
415
- try {
416
- Plotly.relayout(el, this.drawnHeight === null ? { autosize: true } : { width });
417
- }
418
- catch {
419
- // Nothing plotted yet.
420
- }
318
+ this.plot.refit(this.chartDiv);
421
319
  }
320
+ /** Stops the t-SNE run and heatmap worker and frees both plots; the streams end
321
+ * through `takeUntilDestroyed`. */
422
322
  ngOnDestroy() {
423
- this.subs.unsubscribe();
424
323
  // A worker outlives the component that started it: closing the panel mid-run would
425
324
  // otherwise leave a t-SNE saturating a GPU for minutes with nothing left to receive
426
325
  // the answer.
427
- this.abandonCompute();
326
+ this.compute.abandon();
327
+ this.heatmapRender.cancel();
428
328
  // BOTH divs: a chart left detached at teardown holds its WebGL context in the
429
329
  // window's div, which the inline id would never reach.
430
- for (const div of [this.chartDiv, this.detachedDiv]) {
431
- try {
432
- Plotly.purge(div);
433
- }
434
- catch {
435
- // The div may already be gone with the dialog; nothing to clean up.
436
- }
437
- }
330
+ for (const div of [this.chartDiv, this.detachedDiv])
331
+ this.plot.purge(div);
438
332
  }
439
333
  ngAfterViewInit() {
440
334
  // The div exists only now, so this is the earliest a first draw can land.
@@ -446,7 +340,7 @@ export class SpatialChartsComponent {
446
340
  // Seed with the gene already on screen, so the chart says something the
447
341
  // moment it opens rather than showing an empty grid and a prompt.
448
342
  if (this.heatmapGenes.length === 0 && this.colorBy?.kind === 'feature') {
449
- this.heatmapGenes = [this.colorBy.name];
343
+ this.data.setHeatmapGenes([this.colorBy.name]);
450
344
  }
451
345
  void (async () => {
452
346
  await this.ensureHeatmapGrouping();
@@ -463,7 +357,7 @@ export class SpatialChartsComponent {
463
357
  * is a sensible answer for a violin but not for this.
464
358
  */
465
359
  async ensureHeatmapGrouping() {
466
- if (this.grouping || !this.controls)
360
+ if (this.data.hasGrouping || !this.controls)
467
361
  return;
468
362
  const first = this.groupOptions.find((o) => o.value)?.value;
469
363
  if (!first)
@@ -476,93 +370,33 @@ export class SpatialChartsComponent {
476
370
  }
477
371
  /** What the heatmap is currently showing, said plainly. */
478
372
  get heatmapNote() {
479
- if (this.heatmapGenes.length === 0)
480
- return 'Pick one or more genes for the rows.';
481
- const perCell = this.selectionCount > 0
482
- && this.selectionCount <= SpatialChartsComponent.HEATMAP_CELL_COLUMNS;
483
- if (perCell) {
484
- return `One column per selected cell (${this.selectionCount}). `
485
- + 'Mean expression per cell, so the columns are cells rather than classes.';
486
- }
487
- const scope = this.selectionCount > 0 ? 'the selected cells' : 'all cells';
488
- const scaled = this.heatmapZScore
489
- ? ' Each gene is z-scored across the columns, so the colour is above or below that gene\'s own average.'
490
- : ' Raw means, so a highly-expressed gene dominates the scale.';
491
- // Saying so matters: without it the panel looks like the whole column.
492
- const capped = this.heatmapHidden > 0
493
- ? ` Showing the ${SpatialChartsComponent.HEATMAP_MAX_COLUMNS} strongest of `
494
- + `${SpatialChartsComponent.HEATMAP_MAX_COLUMNS + this.heatmapHidden}, `
495
- + 'ranked by the largest value any picked gene reaches.'
496
- : '';
497
- return `Mean expression of each gene within each ${this.groupBy ?? 'group'}, over ${scope}.`
498
- + `${scaled}${capped}`;
373
+ return heatmapNote({
374
+ geneCount: this.heatmapGenes.length,
375
+ selectionCount: this.selectionCount,
376
+ zScore: this.heatmapZScore,
377
+ hidden: this.heatmapHidden,
378
+ groupBy: this.groupBy,
379
+ maxColumns: SpatialChartsComponent.HEATMAP_MAX_COLUMNS,
380
+ cellColumns: SpatialChartsComponent.HEATMAP_CELL_COLUMNS,
381
+ });
499
382
  }
500
383
  /** Gene rows for the heatmap. */
501
384
  async onHeatmapGenes(names) {
502
- this.heatmapGenes = names ?? [];
503
- // Chosen genes must stay in the options or the control cannot label its chips.
504
- this.refreshGeneOptions();
385
+ this.data.setHeatmapGenes(names);
505
386
  await this.loadHeatmapGenes();
506
387
  void this.render();
507
388
  }
508
- /**
509
- * Fetch any gene the heatmap needs and does not already hold.
510
- *
511
- * Sequenced on the same token the colour-source load uses: each vector is a
512
- * separate request, and a slower one must not paint rows for a gene list the
513
- * user has already moved on from.
514
- */
515
- async loadHeatmapGenes() {
516
- const controls = this.controls;
517
- if (!controls)
518
- return;
519
- const missing = this.heatmapGenes.filter((n) => !this.geneCache.has(n));
520
- if (missing.length === 0)
521
- return;
522
- const mine = ++this.token;
523
- this.busy = true;
524
- try {
525
- for (const name of missing) {
526
- const values = await controls.continuousValues({ kind: 'feature', name });
527
- if (mine !== this.token)
528
- return;
529
- this.geneCache.set(name, values);
530
- }
531
- this.notice = null;
532
- }
533
- catch (err) {
534
- if (mine !== this.token)
535
- return;
536
- this.notice = `A gene could not be charted: ${err?.message ?? err}`;
537
- }
538
- finally {
539
- if (mine === this.token)
540
- this.busy = false;
541
- }
389
+ /** Fetch any gene the heatmap needs and does not already hold. */
390
+ loadHeatmapGenes() {
391
+ return this.data.loadHeatmapGenes(this.controls);
542
392
  }
543
393
  async onGroupBy(name) {
544
- this.groupBy = name;
545
- if (!name || !this.controls) {
546
- this.grouping = null;
394
+ const loading = this.data.loadGrouping(this.controls, name);
395
+ this.changed(); // the busy spinner
396
+ if (await loading)
547
397
  void this.render();
548
- return;
549
- }
550
- // Sequenced: pick A then B and a slower A would otherwise land last, charting
551
- // A's categories under a dropdown that says B.
552
- const mine = ++this.groupToken;
553
- try {
554
- const view = await this.controls.categoricalView(name);
555
- if (mine !== this.groupToken)
556
- return;
557
- this.grouping = { codes: view.codes, categories: view.categories, colors: view.colors };
558
- }
559
- catch {
560
- if (mine !== this.groupToken)
561
- return;
562
- this.grouping = null;
563
- this.groupBy = null;
564
- }
565
- void this.render();
398
+ else
399
+ this.changed();
566
400
  }
567
401
  /** True when the current kind would read better with a grouping chosen. */
568
402
  get suggestsGrouping() {
@@ -576,56 +410,17 @@ export class SpatialChartsComponent {
576
410
  // ── internals ───────────────────────────────────────────────────────────
577
411
  /** Fetch the active colour source's vector, then draw. */
578
412
  async reload() {
579
- const controls = this.controls;
580
- const source = this.view.colorBy;
581
- const mine = ++this.token;
582
- if (!controls || !source) {
583
- this.values = null;
584
- this.categorical = null;
585
- this.notice = controls
586
- ? 'Colour the map by a column or a gene to chart its distribution.'
587
- : null;
588
- void this.render();
413
+ const loading = this.data.loadValues(this.controls, this.view.colorBy);
414
+ this.changed(); // the busy spinner
415
+ const loaded = await loading;
416
+ if (loaded === 'superseded')
589
417
  return;
590
- }
591
- this.busy = true;
592
- // A categorical column charts as COUNTS per category — asked for by name
593
- // rather than discovered by catching the continuous fetch's error, so a
594
- // genuine failure still reads as a failure.
595
- const isCategorical = source.kind === 'column'
596
- && (controls.categoricalColumns() ?? []).includes(source.name);
597
- try {
598
- if (isCategorical) {
599
- const view = await controls.categoricalView(source.name);
600
- if (mine !== this.token)
601
- return; // superseded
602
- this.categorical = { codes: view.codes, categories: view.categories, colors: view.colors };
603
- this.values = null;
604
- this.kind = 'counts';
605
- this.notice = null;
606
- }
607
- else {
608
- const values = await controls.continuousValues(source);
609
- if (mine !== this.token)
610
- return;
611
- this.values = values;
612
- this.categorical = null;
613
- if (this.kind === 'counts')
614
- this.kind = 'histogram';
615
- this.notice = null;
616
- }
617
- }
618
- catch (err) {
619
- if (mine !== this.token)
620
- return;
621
- this.values = null;
622
- this.categorical = null;
623
- this.notice = `"${source.name}" could not be charted: ${err?.message ?? err}`;
624
- }
625
- finally {
626
- if (mine === this.token)
627
- this.busy = false;
628
- }
418
+ // Only off the distribution tabs. The heatmap does not chart the colour source, and the
419
+ // embedding is coloured BY it — its own caption asks for exactly this.
420
+ if (loaded === 'categorical' && this.kind !== 'embedding' && this.kind !== 'heatmap')
421
+ this.kind = 'counts';
422
+ if (loaded === 'continuous' && this.kind === 'counts')
423
+ this.kind = 'histogram';
629
424
  void this.render();
630
425
  }
631
426
  /**
@@ -634,7 +429,7 @@ export class SpatialChartsComponent {
634
429
  * Answers what the map cannot: the map says WHERE a population sits, this says which
635
430
  * populations there are and how close they are in expression.
636
431
  *
637
- * Linked to the map without asking for anything: it reuses `this.categorical`, the same
432
+ * Linked to the map without asking for anything: it reuses the loaded categories, the same
638
433
  * loaded colouring the counts chart draws from, so the two cannot disagree about what a
639
434
  * colour means and no second fetch happens. A selection dims the rest rather than
640
435
  * dropping it — the embedding's shape is the context that makes a selection legible.
@@ -651,51 +446,37 @@ export class SpatialChartsComponent {
651
446
  this.purgePlot();
652
447
  return;
653
448
  }
654
- // Sequenced on the shared token: the coordinates are a round-trip, and a slower one
655
- // must not paint over a kind the user has already moved on from.
656
- const mine = ++this.token;
657
449
  // Computed here, not served: a local result is the same shape as a fetched one, so
658
450
  // everything downstream — colouring, lasso selection, the camera — is unchanged.
659
- const local = this.computed.get(meta.name);
451
+ const local = this.compute.result(meta.name);
660
452
  if (local) {
661
- this.embeddingCoords = local;
453
+ this.data.supersedeEmbeddingLoad();
454
+ this.data.embeddingCoords = local;
662
455
  }
663
456
  else if (this.isComputable) {
664
457
  // Offered but not yet computed. The panel shows the Compute button; drawing nothing
665
458
  // is right, and an error would be wrong — there is no failure here.
459
+ this.data.supersedeEmbeddingLoad();
666
460
  this.purgePlot();
667
461
  this.notice = null;
668
462
  return;
669
463
  }
670
- else if (!this.embeddingCoords || this.embeddingCoords.meta.name !== meta.name) {
671
- this.busy = true;
672
- try {
673
- const loaded = await controls.getEmbedding(meta.name);
674
- if (mine !== this.token)
675
- return;
676
- this.embeddingCoords = loaded;
677
- }
678
- catch (err) {
679
- if (mine !== this.token)
680
- return;
681
- this.notice = `Could not load ${meta.label ?? meta.name}: `
682
- + `${err?.message ?? err}`;
464
+ else {
465
+ const loaded = await this.data.loadEmbedding((name) => controls.getEmbedding(name), meta);
466
+ if (loaded === 'superseded')
467
+ return;
468
+ if (loaded === 'failed') {
683
469
  this.purgePlot();
684
470
  return;
685
471
  }
686
- finally {
687
- if (mine === this.token)
688
- this.busy = false;
689
- }
690
472
  }
691
- const coords = this.embeddingCoords;
473
+ const { embeddingCoords: coords, categorical } = this.data.snapshot();
692
474
  if (!coords)
693
475
  return;
694
476
  const target = this.plotTarget;
695
- const el = document.getElementById(target);
696
- if (!el)
477
+ if (!document.getElementById(target))
697
478
  return; // the window is closed, or not rendered yet
698
- const view = this.liveView(el);
479
+ const view = this.plot.liveView(target);
699
480
  this.notice = null;
700
481
  const input = {
701
482
  x: coords.x,
@@ -708,98 +489,21 @@ export class SpatialChartsComponent {
708
489
  derived: meta.derived,
709
490
  // Present for a PCA, absent for a UMAP — see `varianceRatio` on the meta.
710
491
  ...(meta.varianceRatio ? { varianceRatio: meta.varianceRatio } : {}),
711
- ...(this.categorical
712
- ? {
713
- categories: {
714
- codes: this.categorical.codes,
715
- names: this.categorical.categories,
716
- colors: this.categorical.colors,
717
- },
718
- }
492
+ ...(categorical
493
+ ? { categories: { codes: categorical.codes, names: categorical.categories, colors: categorical.colors } }
719
494
  : {}),
720
495
  ...(this.selection.count > 0 ? { selection: this.selection.mask } : {}),
721
496
  // Whatever view the user has set up, read from the LIVE plot — which is where
722
497
  // rotating and zooming leave it.
723
498
  ...(view ? { view } : {}),
724
499
  };
725
- await this.draw(buildEmbeddingTraces(input), embeddingLayout(input), EMBEDDING_CONFIG, target);
726
- this.bindEmbeddingSelection(target);
727
- }
728
- /**
729
- * Turn a lasso in the embedding into a selection of observations.
730
- *
731
- * This is what makes two views of one dataset worth having side by side: draw round a
732
- * cluster here and those cells light up on the tissue, because every view reads the
733
- * same mask.
734
- *
735
- * Bound after each draw, and the previous listener removed first: `Plotly.react`
736
- * preserves handlers, so re-binding without removing would fire the selection once per
737
- * redraw the plot had ever had.
738
- */
739
- bindEmbeddingSelection(div) {
740
- const el = document.getElementById(div);
741
- if (!el?.on)
742
- return;
743
- el.removeAllListeners?.('plotly_selected');
744
- el.removeAllListeners?.('plotly_deselect');
745
- el.on('plotly_selected', (ev) => {
746
- // A lasso that selects nothing arrives as an event with no points; treat it as a
747
- // clear, which is what dragging an empty patch looks like it should do.
748
- const points = ev?.points ?? [];
749
- const indices = [];
750
- for (const p of points) {
751
- // The observation index rides on `customdata` — see `buildEmbeddingTraces`. A point's
752
- // index within its trace is not the observation once the points are split by
753
- // category, so this is the only correct source.
754
- const id = p.customdata;
755
- if (typeof id === 'number')
756
- indices.push(id);
757
- }
758
- if (indices.length === 0) {
759
- this.controls?.clearSelection();
760
- return;
761
- }
762
- this.controls?.selectIndices(indices);
500
+ await this.plot.draw(target, buildEmbeddingTraces(input), embeddingLayout(input), EMBEDDING_CONFIG);
501
+ // Draw round a cluster here and those cells light up on the tissue: every view reads
502
+ // the same mask.
503
+ this.plot.bindSelection(target, {
504
+ selected: (indices) => this.controls?.selectIndices(indices),
505
+ deselected: () => this.controls?.clearSelection(),
763
506
  });
764
- // The modebar's deselect, and a plain click on empty space.
765
- el.on('plotly_deselect', () => this.controls?.clearSelection());
766
- }
767
- /**
768
- * Drop the embedding's selection handlers from a div another kind is about to use.
769
- *
770
- * The detached window outlives any one kind — switching tabs swaps its content and
771
- * keeps the div — so without this the lasso handlers bound for a UMAP stay live under
772
- * the counts plot that replaces it. `plotly_deselect` fires on a plain click in ANY
773
- * Plotly chart, so the first click on a bar would silently clear the map's selection.
774
- */
775
- unbindEmbeddingSelection(div) {
776
- const el = document.getElementById(div);
777
- if (!el?.removeAllListeners)
778
- return;
779
- el.removeAllListeners('plotly_selected');
780
- el.removeAllListeners('plotly_deselect');
781
- }
782
- /**
783
- * The view the user has set up on the live plot, to carry across a redraw.
784
- *
785
- * `Plotly.react` resets a 3D scene camera and any zoomed 2D range unless the layout
786
- * carries them, so selecting a category or recolouring would throw away an orientation
787
- * that took work to find. Rotating a cloud to see a structure and losing it on the next
788
- * click makes the plot useless for what it is for.
789
- */
790
- liveView(el) {
791
- const full = el._fullLayout;
792
- if (!full)
793
- return null; // nothing drawn here yet
794
- if (full.scene?.camera)
795
- return { camera: full.scene.camera };
796
- // 2D: only once the user has actually zoomed. Passing an autoranged range back would
797
- // freeze the axes and stop the plot re-fitting when the data changes.
798
- const zoomed = full.xaxis?.autorange === false && full.yaxis?.autorange === false;
799
- if (zoomed && full.xaxis?.range && full.yaxis?.range) {
800
- return { ranges: { x: full.xaxis.range, y: full.yaxis.range } };
801
- }
802
- return null;
803
507
  }
804
508
  /**
805
509
  * Whether the plot on screen fixed its own height.
@@ -810,7 +514,7 @@ export class SpatialChartsComponent {
810
514
  * which, because the two want opposite `flex` and `overflow`.
811
515
  */
812
516
  get hasFixedHeight() {
813
- return this.drawnHeight !== null;
517
+ return this.plot.hasFixedHeight;
814
518
  }
815
519
  /** Where the active kind draws: its own window, or the shared chart div. */
816
520
  get plotTarget() {
@@ -839,12 +543,7 @@ export class SpatialChartsComponent {
839
543
  toggleDetached() {
840
544
  const leaving = this.plotTarget;
841
545
  this.detached = !this.detached;
842
- try {
843
- Plotly.purge(leaving);
844
- }
845
- catch {
846
- // Nothing was plotted there.
847
- }
546
+ this.plot.purge(leaving);
848
547
  // Going back INLINE, the div appears with the next change detection, so a task is
849
548
  // enough. Going OUT, the window's div does not exist yet — PrimeNG mounts the dialog
850
549
  // with a transition — so the dialog's own `onShow` drives that draw instead. A single
@@ -858,313 +557,83 @@ export class SpatialChartsComponent {
858
557
  }
859
558
  /** Re-fit the detached window's plot after it is resized. */
860
559
  onDetachedResizeEnd() {
861
- this.refit(this.detachedDiv);
862
- }
863
- /**
864
- * Rebuild the visible options: the best matches for what is typed, plus what is chosen.
865
- *
866
- * A capped multi-select that drops its own selections cannot resolve their labels, and
867
- * the model loses them on the next change — the chips vanish for no reason the user
868
- * can see.
869
- */
870
- refreshGeneOptions() {
871
- this.geneOptions = geneOptionsFor(this.geneNames, this.geneQuery, this.heatmapGenes)
872
- .map((n) => ({ label: n, value: n }));
560
+ this.plot.refit(this.detachedDiv);
873
561
  }
874
562
  /** The gene picker's filter box changed: search the resident names, show the best. */
875
563
  onGeneFilter(query) {
876
- this.geneQuery = query ?? '';
877
- this.refreshGeneOptions();
564
+ this.data.filterGenes(query);
878
565
  }
879
- /**
880
- * Roughly how long a run will take, in seconds, from the measured anchor.
881
- *
882
- * Shown before the click. A progress bar tells you a job is going; only an estimate
883
- * tells you whether to start it.
884
- */
566
+ /** Roughly how long a run will take, in seconds, from the measured anchor. */
885
567
  get computeEstimateSeconds() {
886
- const n = this.observationCount;
887
- const { seconds, observations } = SpatialChartsComponent.TSNE_SECONDS_AT;
888
- return Math.max(1, Math.round(seconds * (n / observations) ** 2));
568
+ return this.compute.estimateSeconds;
889
569
  }
890
570
  /** That estimate as something to put on a button. */
891
571
  get computeEstimateLabel() {
892
- const secs = this.computeEstimateSeconds;
893
- if (secs < 90)
894
- return `~${secs}s`;
895
- return `~${Math.round(secs / 60)} min`;
572
+ return this.compute.estimateLabel;
896
573
  }
897
574
  /** Why the option is absent, when a dataset is past the threshold. */
898
575
  get tsneTooLargeNote() {
899
- const n = this.observationCount;
900
- return `t-SNE is not offered here for ${n.toLocaleString()} observations — it is `
901
- + `quadratic, so it would take roughly ${this.computeEstimateLabel} in the browser. `
902
- + 'Compute it offline and serve it with the dataset.';
576
+ return this.compute.tooLargeNote;
903
577
  }
904
578
  /** Whether the selected embedding is one this browser would have to compute. */
905
579
  get isComputable() {
906
- return !!this.embedding && this.embedding.name.startsWith('local:');
580
+ return this.compute.isComputable(this.embedding);
907
581
  }
908
582
  /** Whether it has already been computed in this session. */
909
583
  get isComputed() {
910
- return !!this.embedding && this.computed.has(this.embedding.name);
584
+ return this.compute.isComputed(this.embedding);
911
585
  }
912
586
  get isComputing() {
913
- return !!this.computeRun?.running;
587
+ return this.compute.running;
914
588
  }
915
- /**
916
- * Compute the selected embedding here, in a worker.
917
- *
918
- * The PCA scores come from the port as an ordinary embedding — the server derives them,
919
- * because PCA needs the whole expression matrix (185 MB for a Visium dataset) while
920
- * t-SNE needs only the scores (0.51 MB). Sending the matrix to the browser to save a
921
- * few seconds of arithmetic would be a far slower answer.
922
- */
589
+ /** Compute the selected embedding here, in a worker, then draw it. */
923
590
  async computeEmbedding() {
924
591
  const meta = this.embedding;
925
592
  const controls = this.controls;
926
593
  if (!meta || !controls?.getEmbedding || this.isComputing)
927
594
  return;
928
- this.computeError = null;
929
- this.computeMessage = null;
930
- this.computeFraction = null;
931
- this.computeBackend = null;
932
- // Every early return and every await below is checked against this. `run` is held
933
- // locally as well as on the instance: after an await `this.computeRun` may already be
934
- // a newer run's, and clearing or terminating that one is how a fresh computation gets
935
- // killed by the tail of the one it replaced.
936
- const mine = ++this.computeToken;
937
- const superseded = () => mine !== this.computeToken;
938
- let run = null;
939
- try {
940
- // Prefer the 3-D scores: they carry a third component for free, and t-SNE on more
941
- // components than it needs is not better — the PCA basis is the input either way.
942
- const source = await this.loadPcaScores(controls);
943
- if (superseded())
944
- return;
945
- if (!source) {
946
- this.computeError = 'This dataset serves no PCA to embed.';
947
- return;
948
- }
949
- run = new EmbeddingComputeRun();
950
- this.computeRun = run;
951
- const result = await run.run({
952
- scores: source.scores,
953
- nObs: source.nObs,
954
- nDims: source.nDims,
955
- dims: meta.dims,
956
- }, {
957
- ...meta,
958
- label: (meta.label ?? meta.name)
959
- .replace(SpatialChartsComponent.COMPUTE_SUFFIX, ''),
960
- }, (progress) => {
961
- // Progress from a superseded run must not drive the bar a newer one is using.
962
- if (superseded())
963
- return;
964
- this.computeFraction = progress.fraction;
965
- this.computeBackend = progress.backend ?? this.computeBackend;
966
- if (progress.message)
967
- this.computeMessage = progress.message;
968
- });
969
- // A terminated run resolves null, so this covers abandonment as well as Cancel —
970
- // but the token is still checked, because the dataset can change between the
971
- // worker's `done` and this line.
972
- if (result && !superseded()) {
973
- this.computed.set(meta.name, result);
974
- this.embeddingCoords = result;
975
- await this.render();
976
- }
977
- }
978
- catch (err) {
979
- if (!superseded())
980
- this.computeError = err?.message ?? String(err);
981
- }
982
- finally {
983
- run?.terminate();
984
- // Only if it is still ours: a dataset switch has already terminated this run and
985
- // may have started another, and `this.computeRun = null` here would orphan it —
986
- // leaving a worker nothing can cancel and a Cancel button that does nothing.
987
- if (this.computeRun === run) {
988
- this.computeRun = null;
989
- this.computeFraction = null;
990
- }
991
- }
595
+ const result = await this.compute.start(meta, this.embeddings, (name) => controls.getEmbedding(name));
596
+ if (!result)
597
+ return;
598
+ this.data.embeddingCoords = result;
599
+ await this.render();
992
600
  }
993
601
  /** Stop a run. It settles shortly after, leaving no embedding. */
994
602
  cancelCompute() {
995
- this.computeRun?.cancel();
996
- }
997
- /**
998
- * Drop any computation in progress, because what it is computing no longer applies.
999
- *
1000
- * Distinct from {@link cancelCompute}, which is the user's Cancel and asks the worker to
1001
- * stop politely. This one does not wait: the run is settled and the worker killed, so a
1002
- * dataset switch or a teardown cannot leave a t-SNE grinding on in the background over
1003
- * observations nothing is showing any more.
1004
- */
1005
- abandonCompute() {
1006
- this.computeToken++;
1007
- this.computeRun?.terminate();
1008
- this.computeRun = null;
1009
- this.computeFraction = null;
1010
- this.computeBackend = null;
1011
- this.computeMessage = null;
1012
- }
1013
- /**
1014
- * The dataset's PCA, as row-major scores.
1015
- *
1016
- * Assembled from whichever PCA the source offers, widest first — a 3-D one gives t-SNE
1017
- * three components instead of two for the same request.
1018
- */
1019
- async loadPcaScores(controls) {
1020
- const candidates = this.embeddings
1021
- .filter((e) => /pca/i.test(e.label ?? e.name) && !e.name.startsWith('local:'))
1022
- .sort((a, b) => b.dims - a.dims);
1023
- for (const candidate of candidates) {
1024
- try {
1025
- const pca = await controls.getEmbedding(candidate.name);
1026
- const planes = [pca.x, pca.y, ...(pca.z ? [pca.z] : [])];
1027
- const nObs = pca.x.length;
1028
- const nDims = planes.length;
1029
- const scores = new Float32Array(nObs * nDims);
1030
- for (let i = 0; i < nObs; i++) {
1031
- for (let d = 0; d < nDims; d++)
1032
- scores[i * nDims + d] = planes[d][i];
1033
- }
1034
- return { scores, nObs, nDims };
1035
- }
1036
- catch {
1037
- // Try the next; a source may advertise one it cannot actually serve.
1038
- }
1039
- }
1040
- return null;
603
+ this.compute.cancel();
1041
604
  }
1042
605
  /** Which embedding to draw, when the dataset publishes more than one. */
1043
606
  onEmbedding(name) {
1044
- const next = this.embeddings.find((e) => e.name === name);
1045
- if (!next || next.name === this.embedding?.name)
1046
- return;
1047
- this.embedding = next;
1048
- this.embeddingCoords = null;
1049
- void this.render();
607
+ if (this.data.selectEmbedding(name))
608
+ void this.render();
1050
609
  }
1051
- /**
1052
- * What the chart on screen actually shows, and what it cannot be read for.
1053
- *
1054
- * Shown on hover from a `?` beside the tabs. Each of these plots answers a different
1055
- * question and two of them are routinely over-read — a UMAP's distances and a heatmap's
1056
- * unscaled colours — so the caveat is part of the explanation rather than a footnote.
1057
- *
1058
- * HTML, because the tooltip renders with `[escape]="false"`: a paragraph and a caveat
1059
- * read as two thoughts, and a single run-on line is skipped rather than read.
1060
- */
610
+ /** What the chart on screen shows, and what it cannot be read for — see {@link kindHelp}. */
1061
611
  get kindHelp() {
1062
- switch (this.kind) {
1063
- case 'counts':
1064
- return '<b>Counts</b> — how many observations fall in each category of the column '
1065
- + 'the map is coloured by, largest first.<br><br>A category code is a label, not '
1066
- + 'a magnitude, so a frequency is the only distribution it has: there is no '
1067
- + 'histogram of a cell type.';
1068
- case 'histogram':
1069
- return '<b>Histogram</b> — how the active value is distributed over all '
1070
- + 'observations.<br><br>With a selection, it is overlaid on the full '
1071
- + 'distribution rather than replacing it, so you can see where the selected '
1072
- + 'cells sit within the whole.';
1073
- case 'violin':
1074
- return '<b>Violin</b> — the active value\'s distribution within each category of '
1075
- + 'the grouping column, drawn as a smoothed density.<br><br>Shows shape a box '
1076
- + 'plot hides: two groups with the same median can be one peak or two.';
1077
- case 'box':
1078
- return '<b>Box</b> — median, quartiles and range of the active value within each '
1079
- + 'category of the grouping column.<br><br>Compact and comparable across many '
1080
- + 'groups, at the cost of hiding whether a group is bimodal.';
1081
- case 'heatmap':
1082
- return '<b>Heatmap</b> — mean expression of each picked gene within each group: '
1083
- + 'genes down, groups across.<br><br>Each gene is <b>z-scored across the '
1084
- + 'groups</b> by default, so a colour says "above or below this gene\'s own '
1085
- + 'average", not "highly expressed". Without that one loud gene saturates the '
1086
- + 'scale and the rest of the panel reads as blank. Turning it off compares '
1087
- + 'genes on their raw scale instead.';
1088
- case 'embedding':
1089
- return this.embeddingHelp;
1090
- default:
1091
- return '';
1092
- }
1093
- }
1094
- /**
1095
- * What the embedding on screen is, and how far its geometry can be trusted.
1096
- *
1097
- * Per METHOD, because that is the part people get wrong: a PCA's axes are ordered and
1098
- * measurable while a UMAP's are neither, and the same picture read the two ways supports
1099
- * opposite conclusions.
1100
- */
1101
- get embeddingHelp() {
1102
- const shared = '<br><br>Every cell is in all views at once: lasso a group here and '
1103
- + 'those cells light up on the tissue, because both read the same selection.';
1104
- const name = (this.embedding?.label ?? this.embedding?.name ?? '').toLowerCase();
1105
- if (name.includes('pca')) {
1106
- return '<b>PCA</b> — a <b>linear</b> projection onto the directions of greatest '
1107
- + 'variance, in order.<br><br>Alone among these, its axes mean something '
1108
- + 'measurable: each reports the share of total variance it explains, which is why '
1109
- + 'the labels carry a percentage. Distances are real, and a low percentage tells '
1110
- + 'you the picture is a thin slice of the variation.' + shared;
1111
- }
1112
- if (name.includes('t-sne') || name.includes('tsne')) {
1113
- return '<b>t-SNE</b> — cells placed so that close neighbours in expression stay '
1114
- + 'close.<br><br>Stricter about local neighbourhoods than UMAP and less '
1115
- + 'trustworthy about anything global: it tends to spread clusters into '
1116
- + 'evenly-sized islands whose sizes and separations mean little. Read which cells '
1117
- + 'group together, not how far apart the groups are.' + shared;
1118
- }
1119
- return '<b>UMAP</b> — cells placed so that close neighbours in expression stay '
1120
- + 'close.<br><br>The axes are arbitrary: unordered, unitless, and reproducible only '
1121
- + 'up to a rotation, which is why they carry no percentage. Read which cells group '
1122
- + 'together and which groups touch; do not read the distance between distant '
1123
- + 'clusters, or the direction of an axis.' + shared;
612
+ return kindHelp(this.kind, this.embedding);
1124
613
  }
1125
614
  /** What the embedding view is showing, said plainly. */
1126
615
  get embeddingNote() {
1127
- const meta = this.embeddingCoords?.meta ?? this.embedding;
1128
- if (!meta)
1129
- return 'This dataset publishes no embedding.';
1130
- // A derived embedding says so, and says HOW when the parameters are known: a t-SNE or
1131
- // UMAP at different settings is a different picture of the same cells, so "computed
1132
- // here" alone leaves a reader unable to reproduce or compare it.
1133
- const derived = meta.derived
1134
- ? ` Computed here${meta.params ? ` (${meta.params})` : ''}, `
1135
- + 'not published with the dataset.'
1136
- : '';
1137
- const coloured = this.categorical
1138
- ? ' Coloured to match the map.'
1139
- : ' Colour the map by a categorical column to colour these points.';
1140
- const sel = this.selection.count > 0
1141
- ? ` ${this.selection.count.toLocaleString()} selected are highlighted.`
1142
- : '';
1143
- return `${meta.label ?? meta.name}.${coloured}${sel}${derived}`;
616
+ return embeddingNote(this.data.embeddingCoords?.meta ?? this.embedding, this.data.isCategorical, this.selection.count);
1144
617
  }
618
+ /** Purge the ACTIVE target: purging the inline div while detached would leave the window
619
+ * showing a plot the component believes it has cleared. */
1145
620
  purgePlot() {
621
+ this.plot.purge(this.plotTarget);
622
+ }
623
+ /** Draw into the active target. */
624
+ draw(traces, layout) {
625
+ return this.plot.draw(this.plotTarget, traces, layout);
626
+ }
627
+ /** Draw the active kind, then re-render the panel around it (notices, height, notes). */
628
+ async render() {
1146
629
  try {
1147
- // The ACTIVE target: purging the inline div while detached would leave the
1148
- // window showing a plot the component believes it has cleared.
1149
- Plotly.purge(this.plotTarget);
630
+ await this.renderChart();
1150
631
  }
1151
- catch {
1152
- // Nothing plotted.
632
+ finally {
633
+ this.changed();
1153
634
  }
1154
635
  }
1155
- /**
1156
- * Draw, remembering whether this layout fixed its own height.
1157
- *
1158
- * The layout builders return `unknown` on purpose — they keep Plotly's layout
1159
- * types out of the pure module — so the height is read back by narrowing
1160
- * rather than declared. `height` is Plotly's own key, not ours.
1161
- */
1162
- async draw(traces, layout, config = CHART_CONFIG, div = this.plotTarget) {
1163
- const height = layout?.height;
1164
- this.drawnHeight = typeof height === 'number' ? height : null;
1165
- await Plotly.react(div, traces, layout, config);
1166
- }
1167
- async render() {
636
+ async renderChart() {
1168
637
  if (!this.isActive)
1169
638
  return;
1170
639
  // Guard on the div the ACTIVE kind will draw into, not always the shared one. While a
@@ -1175,7 +644,10 @@ export class SpatialChartsComponent {
1175
644
  return;
1176
645
  // Anything but the embedding inherits a div the embedding may have bound handlers on.
1177
646
  if (this.kind !== 'embedding')
1178
- this.unbindEmbeddingSelection(target);
647
+ this.plot.unbindSelection(target);
648
+ // A heatmap still computing must not draw over the kind that replaced it.
649
+ if (this.kind !== 'heatmap')
650
+ this.heatmapRender.cancel();
1179
651
  // The heatmap answers a different question from the other kinds — which
1180
652
  // genes distinguish which groups — so it is driven by its own gene list and
1181
653
  // grouping rather than by whatever the map is coloured by.
@@ -1189,25 +661,28 @@ export class SpatialChartsComponent {
1189
661
  await this.renderEmbedding();
1190
662
  return;
1191
663
  }
1192
- if (!this.colorBy || (!this.values && !this.categorical)) {
664
+ const { values, categorical, grouping } = this.data.snapshot();
665
+ if (!this.colorBy || (!values && !categorical)) {
1193
666
  this.purgePlot();
1194
667
  return;
1195
668
  }
1196
- if (this.categorical) {
669
+ if (categorical) {
1197
670
  const counts = {
1198
- group: this.categorical,
671
+ group: categorical,
1199
672
  selection: this.selection.count > 0 ? this.selection.mask : null,
1200
673
  name: this.colorBy.name,
1201
674
  };
1202
- await this.draw(buildCountTraces(counts), countsLayout(counts));
675
+ // Counted once for both: the layout's height follows the bar count.
676
+ const tally = countByCategory(counts);
677
+ await this.draw(buildCountTraces(counts, tally), countsLayout(counts, tally));
1203
678
  return;
1204
679
  }
1205
- if (!this.values)
680
+ if (!values)
1206
681
  return;
1207
682
  const input = {
1208
- values: this.values,
1209
- name: this.colorBy.kind === 'feature' ? this.colorBy.name : this.colorBy.name,
1210
- group: benefitsFromGrouping(this.kind) ? this.grouping : null,
683
+ values,
684
+ name: this.colorBy.name,
685
+ group: benefitsFromGrouping(this.kind) ? grouping : null,
1211
686
  selection: this.selection.count > 0 ? this.selection.mask : null,
1212
687
  log: this.view.logScale,
1213
688
  };
@@ -1223,29 +698,40 @@ export class SpatialChartsComponent {
1223
698
  * question there, and each cell earns a column.
1224
699
  */
1225
700
  async renderHeatmap() {
1226
- const controls = this.controls;
1227
- const genes = this.heatmapGenes
1228
- .map((name) => ({ name, values: this.geneCache.get(name) }))
1229
- .filter((g) => !!g.values);
1230
- if (!controls || genes.length === 0 || !this.grouping) {
701
+ const { heatmapRows: genes, grouping } = this.data.snapshot();
702
+ if (!this.controls || genes.length === 0 || !grouping) {
1231
703
  this.purgePlot();
1232
704
  return;
1233
705
  }
1234
706
  const selected = this.selection.count > 0 ? maskToIndices(this.selection.mask) : null;
1235
707
  const perCell = !!selected && selected.length <= SpatialChartsComponent.HEATMAP_CELL_COLUMNS;
1236
708
  const cells = perCell && selected
1237
- ? cellsAsGroups(selected, this.grouping.codes.length, SpatialChartsComponent.HEATMAP_CELL_COLUMNS)
709
+ ? cellsAsGroups(selected, grouping.codes.length, SpatialChartsComponent.HEATMAP_CELL_COLUMNS)
1238
710
  : null;
1239
- const matrix = heatmapMatrix(genes, cells ? cells.groups : { codes: this.grouping.codes, categories: this.grouping.categories }, {
1240
- ...(cells ? { indices: cells.indices, minCells: 1 } : {}),
1241
- // Outside the per-cell view a selection still narrows the means, the way
1242
- // the violin and box narrow rather than overlay.
1243
- ...(!cells && selected ? { indices: selected } : {}),
1244
- // Only the grouped view: `cellsAsGroups` already caps by even thinning,
1245
- // and re-ranking those columns would lose the selection's spread.
1246
- ...(cells ? {} : { maxCols: SpatialChartsComponent.HEATMAP_MAX_COLUMNS }),
1247
- zScore: this.heatmapZScore,
1248
- });
711
+ // Off the main thread for a big dataset (genes × cells past the worker threshold), and
712
+ // superseded by the next heatmap draw: a slow matrix for an earlier gene list or
713
+ // selection must not land over the current one, and its worker stops.
714
+ const task = this.heatmapRender.next();
715
+ let matrix;
716
+ try {
717
+ matrix = await computeHeatmapMatrixAsync(genes, cells ? cells.groups : { codes: grouping.codes, categories: grouping.categories }, {
718
+ ...(cells ? { indices: cells.indices, minCells: 1 } : {}),
719
+ // Outside the per-cell view a selection still narrows the means, the way
720
+ // the violin and box narrow rather than overlay.
721
+ ...(!cells && selected ? { indices: selected } : {}),
722
+ // Only the grouped view: `cellsAsGroups` already caps by even thinning,
723
+ // and re-ranking those columns would lose the selection's spread.
724
+ ...(cells ? {} : { maxCols: SpatialChartsComponent.HEATMAP_MAX_COLUMNS }),
725
+ zScore: this.heatmapZScore,
726
+ }, { signal: task.signal });
727
+ }
728
+ catch (err) {
729
+ if (!task.isCurrent())
730
+ return;
731
+ throw err;
732
+ }
733
+ if (!task.isCurrent())
734
+ return;
1249
735
  if (!matrix) {
1250
736
  this.purgePlot();
1251
737
  this.notice = 'No group has enough measured cells for a mean.';
@@ -1264,15 +750,24 @@ export class SpatialChartsComponent {
1264
750
  await this.draw(buildHeatmapTraces(input), heatmapLayout(input));
1265
751
  }
1266
752
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: SpatialChartsComponent, deps: [{ token: VISUALIZER }], target: i0.ɵɵFactoryTarget.Component }); }
1267
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: SpatialChartsComponent, selector: "spatial-charts", inputs: { active: "active" }, ngImport: i0, template: "<div class=\"sx-body\" *ngIf=\"controls\">\n\n <!-- The chart follows whatever the map is coloured by, so the two can never\n disagree about what is being shown. -->\n <div class=\"sx-head\">\n <span class=\"sx-subject\" *ngIf=\"subject\">{{ subject }}</span>\n <span class=\"sx-subject sx-muted\" *ngIf=\"!subject\">nothing selected to chart</span>\n <span class=\"sx-badge\" *ngIf=\"selectionCount\">\n {{ selectionCount | number }} selected\n </span>\n <i class=\"pi pi-spin pi-spinner sx-busy\" *ngIf=\"busy\" aria-label=\"loading\"></i>\n </div>\n\n <div class=\"sx-controls\">\n <p-selectButton [options]=\"kindOptions\" optionLabel=\"label\" optionValue=\"value\"\n [ngModel]=\"kind\" (ngModelChange)=\"onKind($event)\"\n styleClass=\"sx-kind\"></p-selectButton>\n <p-dropdown [options]=\"groupOptions\" optionLabel=\"label\" optionValue=\"value\"\n [ngModel]=\"groupBy\" (ngModelChange)=\"onGroupBy($event)\"\n placeholder=\"No grouping\" appendTo=\"body\"\n [disabled]=\"kind === 'histogram'\"\n [pTooltip]=\"kind === 'heatmap'\n ? 'The categorical column the heatmap\\'s columns come from'\n : 'Split a violin or box by a categorical column'\"\n tooltipPosition=\"top\"></p-dropdown>\n <!-- What the chart on screen shows, and what it must NOT be read for. Each of these\n answers a different question and two are routinely over-read, so the caveat\n travels with the explanation. -->\n <i class=\"pi pi-question-circle sx-help\"\n [pTooltip]=\"kindHelp\" [escape]=\"false\" tooltipPosition=\"left\"\n tooltipStyleClass=\"sx-help-tip\"\n [attr.aria-label]=\"'About the ' + kind + ' chart'\" tabindex=\"0\"></i>\n </div>\n\n <!-- The heatmap's rows are a gene LIST, so it needs its own picker: the other\n kinds chart whatever single source the map is coloured by. -->\n <div class=\"sx-controls\" *ngIf=\"kind === 'heatmap'\">\n <p-multiSelect [options]=\"geneOptions\" optionLabel=\"label\" optionValue=\"value\"\n [ngModel]=\"heatmapGenes\" (ngModelChange)=\"onHeatmapGenes($event)\"\n [filter]=\"true\" filterBy=\"label\" (onFilter)=\"onGeneFilter($event.filter)\"\n [virtualScroll]=\"true\" [virtualScrollItemSize]=\"30\" appendTo=\"body\"\n [showToggleAll]=\"false\" [maxSelectedLabels]=\"3\"\n selectedItemsLabel=\"{0} genes\"\n placeholder=\"Pick genes for the rows\"\n pTooltip=\"One row per gene, averaged within each group\"\n tooltipPosition=\"top\" styleClass=\"sx-genes\"></p-multiSelect>\n <p-checkbox [binary]=\"true\" [ngModel]=\"heatmapZScore\"\n (onChange)=\"onHeatmapZScore($event.checked)\"\n label=\"z-score\"\n pTooltip=\"Scale each gene across the groups. Without it one highly-expressed gene saturates the colour scale and the rest reads as blank\"\n tooltipPosition=\"top\"></p-checkbox>\n </div>\n\n <!-- Detach applies to EVERY kind: the dialog is only so wide, and a counts plot read\n against a heatmap is the same argument as a UMAP read against the map. -->\n <div class=\"sx-controls\">\n <!-- A picker only when the dataset publishes more than one embedding. -->\n <p-dropdown *ngIf=\"kind === 'embedding' && embeddings.length > 1\"\n [options]=\"embeddings\" optionLabel=\"label\" optionValue=\"name\"\n [ngModel]=\"embedding?.name\" (ngModelChange)=\"onEmbedding($event)\"\n appendTo=\"body\"\n pTooltip=\"Which embedding to draw\" tooltipPosition=\"top\"></p-dropdown>\n <!-- Offered only for an embedding this dataset does not publish. It runs in a Web\n Worker on the GPU, so the panel stays usable while it goes. -->\n <p-button *ngIf=\"kind === 'embedding' && isComputable && !isComputed && !isComputing\"\n icon=\"pi pi-play\" size=\"small\" [text]=\"true\"\n [label]=\"'Compute (' + computeEstimateLabel + ')'\"\n pTooltip=\"Run t-SNE here, in a background worker on the GPU, from this dataset's PCA. The panel stays usable, and it can be cancelled.\"\n tooltipPosition=\"top\"\n (onClick)=\"computeEmbedding()\"></p-button>\n <p-button *ngIf=\"isComputing\"\n icon=\"pi pi-times\" size=\"small\" [text]=\"true\" severity=\"secondary\"\n label=\"Cancel\" (onClick)=\"cancelCompute()\"></p-button>\n <p-button [icon]=\"detached ? 'pi pi-window-minimize' : 'pi pi-external-link'\"\n size=\"small\" [text]=\"true\"\n [label]=\"detached ? 'Put back' : 'Detach'\"\n [pTooltip]=\"detached\n ? 'Show this chart in the panel again'\n : 'Open this chart in its own window, alongside this panel'\"\n tooltipPosition=\"top\"\n (onClick)=\"toggleDetached()\"></p-button>\n </div>\n\n <!-- Progress while a computation runs. `mode=\"indeterminate\"` until the first report,\n because the run starts with a neighbour graph and there is nothing honest to show\n as a percentage yet. -->\n <div class=\"sx-compute\" *ngIf=\"isComputing\">\n <p-progressBar\n [mode]=\"computeFraction === null ? 'indeterminate' : 'determinate'\"\n [value]=\"computeFraction === null ? 0 : (computeFraction * 100)\"\n [showValue]=\"computeFraction !== null\"\n [style]=\"{ height: '0.6rem' }\"></p-progressBar>\n <small class=\"sx-hint\">\n {{ computeFraction === null ? 'Building the neighbour graph\u2026' : 'Optimising\u2026' }}\n <ng-container *ngIf=\"computeBackend\"> on {{ computeBackend }}</ng-container>\n </small>\n <small class=\"sx-hint sx-warn\" *ngIf=\"computeMessage\">{{ computeMessage }}</small>\n </div>\n <small class=\"sx-hint sx-warn\" *ngIf=\"computeError\">{{ computeError }}</small>\n <!-- Withheld rather than offered with a warning: an hour-long job is not a choice a\n reader can meaningfully consent to from a dialog. -->\n <small class=\"sx-hint\" *ngIf=\"kind === 'embedding' && tsneTooLarge\">{{ tsneTooLargeNote }}</small>\n\n <div [id]=\"chartDiv\" class=\"sx-plot\" *ngIf=\"!detached\"></div>\n\n <!-- Detached: the chart keeps drawing, in its own window. Says so rather than\n leaving a blank panel that looks broken. -->\n <div class=\"sx-detached\" *ngIf=\"detached\">\n Shown in its own window.\n </div>\n\n <ng-container *ngTemplateOutlet=\"hints\"></ng-container>\n\n</div>\n\n<!-- Declared once and rendered in both the panel and the detached window: the ladder is\n deliberately not mutually exclusive (a heatmap still needs its grouping prompt), so\n collapsing it to a single expression would drop a hint rather than move it. -->\n<ng-template #hints>\n <small class=\"sx-hint\" *ngIf=\"notice\">{{ notice }}</small>\n <small class=\"sx-hint\" *ngIf=\"!notice && suggestsGrouping\">\n Pick a grouping to compare categories.\n </small>\n <small class=\"sx-hint\" *ngIf=\"!notice && selectionCount && kind === 'histogram'\">\n The selection is overlaid on the full distribution.\n </small>\n <small class=\"sx-hint\" *ngIf=\"!notice && selectionCount && kind !== 'histogram' && kind !== 'heatmap'\">\n Showing the selection only.\n </small>\n <small class=\"sx-hint\" *ngIf=\"!notice && kind === 'heatmap'\">{{ heatmapNote }}</small>\n <small class=\"sx-hint\" *ngIf=\"!notice && kind === 'embedding'\">{{ embeddingNote }}</small>\n</ng-template>\n\n<!-- The detached chart. Its own non-modal, draggable, resizable window so it can be put\n beside the spatial-omics dialog. `appendTo=\"body\"` keeps it out of the dialog's\n stacking context, or it would be clipped by the panel it is meant to sit next to. -->\n<p-dialog *ngIf=\"detached\"\n [header]=\"detachedTitle\"\n [visible]=\"true\" (visibleChange)=\"toggleDetached()\"\n [modal]=\"false\" [resizable]=\"true\" [draggable]=\"true\"\n position=\"right\" appendTo=\"body\"\n (onShow)=\"onDetachedWindowShown()\"\n (onResizeEnd)=\"onDetachedResizeEnd()\"\n [style]=\"{ width: '34rem', height: '30rem' }\"\n [contentStyle]=\"{\n display: 'flex', flexDirection: 'column', gap: '0.25rem',\n padding: '0.4rem 0.6rem 0.5rem', flex: '1 1 auto', minHeight: '0',\n overflow: 'auto'\n }\"\n styleClass=\"spatial-detached-dialog\">\n <div [id]=\"detachedDiv\" class=\"sx-detached-plot\"\n [class.sx-plot-fixed]=\"hasFixedHeight\"></div>\n <ng-container *ngTemplateOutlet=\"hints\"></ng-container>\n</p-dialog>\n", styles: [":host ::ng-deep .spatial-charts-dialog .p-dialog-content{padding:.75rem 1rem 1rem}.sx-empty{padding:.5rem 0;font-size:.85rem;color:var(--text-color-secondary, #6c757d);line-height:1.4}.sx-body{display:flex;flex-direction:column;gap:.6rem}.sx-head{display:flex;align-items:center;gap:.5rem;padding-bottom:.4rem;border-bottom:1px solid var(--surface-border, #dee2e6)}.sx-subject{font-weight:600;font-size:.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sx-muted{font-weight:400;color:var(--text-color-secondary, #6c757d)}.sx-badge{margin-left:auto;padding:.1rem .4rem;border-radius:10px;font-size:.7rem;font-variant-numeric:tabular-nums;background:var(--surface-hover, rgba(213, 94, 0, .12));color:#a8480a;white-space:nowrap}.sx-busy{font-size:.8rem;color:var(--text-color-secondary, #6c757d)}.sx-controls{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.sx-controls p-dropdown{flex:1 1 8rem;min-width:0}:host ::ng-deep .sx-controls p-dropdown .p-dropdown{width:100%}:host ::ng-deep .sx-kind .p-button{padding:.25rem .6rem;font-size:.78rem}.sx-plot{width:100%;min-height:15rem;flex:1}.sx-hint{font-size:.75rem;color:var(--text-color-secondary, #6c757d);line-height:1.35}.sx-detached{flex:1;min-height:6rem;display:flex;align-items:center;justify-content:center;font-size:.78rem;color:var(--text-color-secondary, #6c757d)}.sx-detached-plot{flex:1;width:100%;min-height:0;overflow:hidden}.sx-detached-plot.sx-plot-fixed{flex:0 0 auto;min-height:auto;overflow:visible}.sx-help{margin-left:auto;align-self:center;font-size:.95rem;color:var(--text-color-secondary, #6c757d);cursor:help}.sx-help:hover,.sx-help:focus-visible{color:var(--primary-color, #3b82f6)}.sx-compute{display:flex;flex-direction:column;gap:.3rem;padding:.5rem 0}.sx-warn{color:var(--orange-600, #c2410c)}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "style", "styleClass", "badgeClass", "ariaLabel", "autofocus"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i4.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: i5.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "autoShowPanelOnPrintableCharacterKeyDown", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i6.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "visible", "style", "position"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "component", type: i7.ProgressBar, selector: "p-progressBar", inputs: ["value", "showValue", "styleClass", "style", "unit", "mode", "color"] }, { kind: "component", type: i8.Checkbox, selector: "p-checkbox", inputs: ["value", "name", "disabled", "binary", "label", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "styleClass", "labelStyleClass", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i9.MultiSelect, selector: "p-multiSelect", inputs: ["id", "ariaLabel", "style", "styleClass", "panelStyle", "panelStyleClass", "inputId", "disabled", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "variant", "appendTo", "dataKey", "name", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "defaultLabel", "placeholder", "options", "filterValue", "itemSize", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "component", type: i10.SelectButton, selector: "p-selectButton", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "style", "styleClass", "ariaLabelledBy", "disabled", "dataKey", "autofocus"], outputs: ["onOptionClick", "onChange"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] }); }
753
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: SpatialChartsComponent, isStandalone: true, selector: "spatial-charts", inputs: { active: "active" }, ngImport: i0, template: "<div class=\"sx-body\" *ngIf=\"controls\">\n <!-- The chart follows whatever the map is coloured by, so the two can never\n disagree about what is being shown. -->\n <div class=\"sx-head\">\n <span class=\"sx-subject\" *ngIf=\"subject\">{{ subject }}</span>\n <span class=\"sx-subject sx-muted\" *ngIf=\"!subject\">nothing selected to chart</span>\n <span class=\"sx-badge\" *ngIf=\"selectionCount\"> {{ selectionCount | number }} selected </span>\n <i class=\"pi pi-spin pi-spinner sx-busy\" *ngIf=\"busy\" aria-label=\"loading\"></i>\n </div>\n\n <div class=\"sx-controls\">\n <p-selectButton\n [options]=\"kindOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"kind\"\n (ngModelChange)=\"onKind($event)\"\n styleClass=\"sx-kind\"\n ></p-selectButton>\n <p-dropdown\n [options]=\"groupOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"groupBy\"\n (ngModelChange)=\"onGroupBy($event)\"\n placeholder=\"No grouping\"\n appendTo=\"body\"\n [disabled]=\"kind === 'histogram'\"\n [pTooltip]=\"\n kind === 'heatmap'\n ? 'The categorical column the heatmap\\'s columns come from'\n : 'Split a violin or box by a categorical column'\n \"\n tooltipPosition=\"top\"\n ></p-dropdown>\n <!-- What the chart on screen shows, and what it must NOT be read for. Each of these\n answers a different question and two are routinely over-read, so the caveat\n travels with the explanation. -->\n <i\n class=\"pi pi-question-circle sx-help\"\n [pTooltip]=\"kindHelp\"\n [escape]=\"false\"\n tooltipPosition=\"left\"\n tooltipStyleClass=\"sx-help-tip\"\n [attr.aria-label]=\"'About the ' + kind + ' chart'\"\n tabindex=\"0\"\n ></i>\n </div>\n\n <spatial-heatmap-controls\n *ngIf=\"kind === 'heatmap'\"\n [geneOptions]=\"geneOptions\"\n [genes]=\"heatmapGenes\"\n [zScore]=\"heatmapZScore\"\n (genesChange)=\"onHeatmapGenes($event)\"\n (filterChange)=\"onGeneFilter($event)\"\n (zScoreChange)=\"onHeatmapZScore($event)\"\n ></spatial-heatmap-controls>\n\n <spatial-embedding-controls\n [isEmbedding]=\"kind === 'embedding'\"\n [embeddings]=\"embeddings\"\n [embedding]=\"embedding\"\n [computable]=\"isComputable\"\n [computed]=\"isComputed\"\n [running]=\"isComputing\"\n [state]=\"computeState\"\n [estimateLabel]=\"computeEstimateLabel\"\n [tooLargeNote]=\"tsneTooLarge ? tsneTooLargeNote : null\"\n [detached]=\"detached\"\n (embeddingChange)=\"onEmbedding($event)\"\n (compute)=\"computeEmbedding()\"\n (cancel)=\"cancelCompute()\"\n (detachedToggle)=\"toggleDetached()\"\n >\n </spatial-embedding-controls>\n\n <div [id]=\"chartDiv\" class=\"sx-plot\" *ngIf=\"!detached\"></div>\n\n <!-- Detached: the chart keeps drawing, in its own window. Says so rather than\n leaving a blank panel that looks broken. -->\n <div class=\"sx-detached\" *ngIf=\"detached\">Shown in its own window.</div>\n\n <ng-container *ngTemplateOutlet=\"hints\"></ng-container>\n</div>\n\n<!-- Declared once and rendered in both the panel and the detached window: the ladder is\n deliberately not mutually exclusive (a heatmap still needs its grouping prompt), so\n collapsing it to a single expression would drop a hint rather than move it. -->\n<ng-template #hints>\n <small class=\"sx-hint\" *ngIf=\"notice\">{{ notice }}</small>\n <small class=\"sx-hint\" *ngIf=\"!notice && suggestsGrouping\"> Pick a grouping to compare categories. </small>\n <small class=\"sx-hint\" *ngIf=\"!notice && selectionCount && kind === 'histogram'\">\n The selection is overlaid on the full distribution.\n </small>\n <small class=\"sx-hint\" *ngIf=\"!notice && selectionCount && kind !== 'histogram' && kind !== 'heatmap'\">\n Showing the selection only.\n </small>\n <small class=\"sx-hint\" *ngIf=\"!notice && kind === 'heatmap'\">{{ heatmapNote }}</small>\n <small class=\"sx-hint\" *ngIf=\"!notice && kind === 'embedding'\">{{ embeddingNote }}</small>\n</ng-template>\n\n<spatial-chart-window\n *ngIf=\"detached\"\n [title]=\"detachedTitle\"\n [divId]=\"detachedDiv\"\n [fixedHeight]=\"hasFixedHeight\"\n (closed)=\"toggleDetached()\"\n (shown)=\"onDetachedWindowShown()\"\n (resizeEnd)=\"onDetachedResizeEnd()\"\n>\n <ng-container *ngTemplateOutlet=\"hints\"></ng-container>\n</spatial-chart-window>\n", styles: [".sx-controls{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.sx-controls p-dropdown{flex:1 1 8rem;min-width:0}:host ::ng-deep .sx-controls p-dropdown .p-dropdown{width:100%}.sx-hint{font-size:.75rem;color:var(--text-color-secondary, #6c757d);line-height:1.35}.sx-warn{color:var(--orange-600, #c2410c)}:host ::ng-deep .spatial-charts-dialog .p-dialog-content{padding:.75rem 1rem 1rem}.sx-empty{padding:.5rem 0;font-size:.85rem;color:var(--text-color-secondary, #6c757d);line-height:1.4}.sx-body{display:flex;flex-direction:column;gap:.6rem}.sx-head{display:flex;align-items:center;gap:.5rem;padding-bottom:.4rem;border-bottom:1px solid var(--surface-border, #dee2e6)}.sx-subject{font-weight:600;font-size:.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sx-muted{font-weight:400;color:var(--text-color-secondary, #6c757d)}.sx-badge{margin-left:auto;padding:.1rem .4rem;border-radius:10px;font-size:.7rem;font-variant-numeric:tabular-nums;background:var(--surface-hover, rgba(213, 94, 0, .12));color:#a8480a;white-space:nowrap}.sx-busy{font-size:.8rem;color:var(--text-color-secondary, #6c757d)}:host ::ng-deep .sx-kind .p-button{padding:.25rem .6rem;font-size:.78rem}.sx-plot{width:100%;min-height:15rem;flex:1}.sx-detached{flex:1;min-height:6rem;display:flex;align-items:center;justify-content:center;font-size:.78rem;color:var(--text-color-secondary, #6c757d)}.sx-help{margin-left:auto;align-self:center;font-size:.95rem;color:var(--text-color-secondary, #6c757d);cursor:help}.sx-help:hover,.sx-help:focus-visible{color:var(--primary-color, #3b82f6)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i3.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "autoShowPanelOnPrintableCharacterKeyDown", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i4.SelectButton, selector: "p-selectButton", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "style", "styleClass", "ariaLabelledBy", "disabled", "dataKey", "autofocus"], outputs: ["onOptionClick", "onChange"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: SpatialHeatmapControlsComponent, selector: "spatial-heatmap-controls", inputs: ["geneOptions", "genes", "zScore"], outputs: ["genesChange", "filterChange", "zScoreChange"] }, { kind: "component", type: SpatialEmbeddingControlsComponent, selector: "spatial-embedding-controls", inputs: ["isEmbedding", "embeddings", "embedding", "computable", "computed", "running", "state", "estimateLabel", "tooLargeNote", "detached"], outputs: ["embeddingChange", "compute", "cancel", "detachedToggle"] }, { kind: "component", type: SpatialChartWindowComponent, selector: "spatial-chart-window", inputs: ["title", "divId", "fixedHeight"], outputs: ["closed", "shown", "resizeEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1268
754
  }
1269
755
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: SpatialChartsComponent, decorators: [{
1270
756
  type: Component,
1271
- args: [{ selector: 'spatial-charts', template: "<div class=\"sx-body\" *ngIf=\"controls\">\n\n <!-- The chart follows whatever the map is coloured by, so the two can never\n disagree about what is being shown. -->\n <div class=\"sx-head\">\n <span class=\"sx-subject\" *ngIf=\"subject\">{{ subject }}</span>\n <span class=\"sx-subject sx-muted\" *ngIf=\"!subject\">nothing selected to chart</span>\n <span class=\"sx-badge\" *ngIf=\"selectionCount\">\n {{ selectionCount | number }} selected\n </span>\n <i class=\"pi pi-spin pi-spinner sx-busy\" *ngIf=\"busy\" aria-label=\"loading\"></i>\n </div>\n\n <div class=\"sx-controls\">\n <p-selectButton [options]=\"kindOptions\" optionLabel=\"label\" optionValue=\"value\"\n [ngModel]=\"kind\" (ngModelChange)=\"onKind($event)\"\n styleClass=\"sx-kind\"></p-selectButton>\n <p-dropdown [options]=\"groupOptions\" optionLabel=\"label\" optionValue=\"value\"\n [ngModel]=\"groupBy\" (ngModelChange)=\"onGroupBy($event)\"\n placeholder=\"No grouping\" appendTo=\"body\"\n [disabled]=\"kind === 'histogram'\"\n [pTooltip]=\"kind === 'heatmap'\n ? 'The categorical column the heatmap\\'s columns come from'\n : 'Split a violin or box by a categorical column'\"\n tooltipPosition=\"top\"></p-dropdown>\n <!-- What the chart on screen shows, and what it must NOT be read for. Each of these\n answers a different question and two are routinely over-read, so the caveat\n travels with the explanation. -->\n <i class=\"pi pi-question-circle sx-help\"\n [pTooltip]=\"kindHelp\" [escape]=\"false\" tooltipPosition=\"left\"\n tooltipStyleClass=\"sx-help-tip\"\n [attr.aria-label]=\"'About the ' + kind + ' chart'\" tabindex=\"0\"></i>\n </div>\n\n <!-- The heatmap's rows are a gene LIST, so it needs its own picker: the other\n kinds chart whatever single source the map is coloured by. -->\n <div class=\"sx-controls\" *ngIf=\"kind === 'heatmap'\">\n <p-multiSelect [options]=\"geneOptions\" optionLabel=\"label\" optionValue=\"value\"\n [ngModel]=\"heatmapGenes\" (ngModelChange)=\"onHeatmapGenes($event)\"\n [filter]=\"true\" filterBy=\"label\" (onFilter)=\"onGeneFilter($event.filter)\"\n [virtualScroll]=\"true\" [virtualScrollItemSize]=\"30\" appendTo=\"body\"\n [showToggleAll]=\"false\" [maxSelectedLabels]=\"3\"\n selectedItemsLabel=\"{0} genes\"\n placeholder=\"Pick genes for the rows\"\n pTooltip=\"One row per gene, averaged within each group\"\n tooltipPosition=\"top\" styleClass=\"sx-genes\"></p-multiSelect>\n <p-checkbox [binary]=\"true\" [ngModel]=\"heatmapZScore\"\n (onChange)=\"onHeatmapZScore($event.checked)\"\n label=\"z-score\"\n pTooltip=\"Scale each gene across the groups. Without it one highly-expressed gene saturates the colour scale and the rest reads as blank\"\n tooltipPosition=\"top\"></p-checkbox>\n </div>\n\n <!-- Detach applies to EVERY kind: the dialog is only so wide, and a counts plot read\n against a heatmap is the same argument as a UMAP read against the map. -->\n <div class=\"sx-controls\">\n <!-- A picker only when the dataset publishes more than one embedding. -->\n <p-dropdown *ngIf=\"kind === 'embedding' && embeddings.length > 1\"\n [options]=\"embeddings\" optionLabel=\"label\" optionValue=\"name\"\n [ngModel]=\"embedding?.name\" (ngModelChange)=\"onEmbedding($event)\"\n appendTo=\"body\"\n pTooltip=\"Which embedding to draw\" tooltipPosition=\"top\"></p-dropdown>\n <!-- Offered only for an embedding this dataset does not publish. It runs in a Web\n Worker on the GPU, so the panel stays usable while it goes. -->\n <p-button *ngIf=\"kind === 'embedding' && isComputable && !isComputed && !isComputing\"\n icon=\"pi pi-play\" size=\"small\" [text]=\"true\"\n [label]=\"'Compute (' + computeEstimateLabel + ')'\"\n pTooltip=\"Run t-SNE here, in a background worker on the GPU, from this dataset's PCA. The panel stays usable, and it can be cancelled.\"\n tooltipPosition=\"top\"\n (onClick)=\"computeEmbedding()\"></p-button>\n <p-button *ngIf=\"isComputing\"\n icon=\"pi pi-times\" size=\"small\" [text]=\"true\" severity=\"secondary\"\n label=\"Cancel\" (onClick)=\"cancelCompute()\"></p-button>\n <p-button [icon]=\"detached ? 'pi pi-window-minimize' : 'pi pi-external-link'\"\n size=\"small\" [text]=\"true\"\n [label]=\"detached ? 'Put back' : 'Detach'\"\n [pTooltip]=\"detached\n ? 'Show this chart in the panel again'\n : 'Open this chart in its own window, alongside this panel'\"\n tooltipPosition=\"top\"\n (onClick)=\"toggleDetached()\"></p-button>\n </div>\n\n <!-- Progress while a computation runs. `mode=\"indeterminate\"` until the first report,\n because the run starts with a neighbour graph and there is nothing honest to show\n as a percentage yet. -->\n <div class=\"sx-compute\" *ngIf=\"isComputing\">\n <p-progressBar\n [mode]=\"computeFraction === null ? 'indeterminate' : 'determinate'\"\n [value]=\"computeFraction === null ? 0 : (computeFraction * 100)\"\n [showValue]=\"computeFraction !== null\"\n [style]=\"{ height: '0.6rem' }\"></p-progressBar>\n <small class=\"sx-hint\">\n {{ computeFraction === null ? 'Building the neighbour graph\u2026' : 'Optimising\u2026' }}\n <ng-container *ngIf=\"computeBackend\"> on {{ computeBackend }}</ng-container>\n </small>\n <small class=\"sx-hint sx-warn\" *ngIf=\"computeMessage\">{{ computeMessage }}</small>\n </div>\n <small class=\"sx-hint sx-warn\" *ngIf=\"computeError\">{{ computeError }}</small>\n <!-- Withheld rather than offered with a warning: an hour-long job is not a choice a\n reader can meaningfully consent to from a dialog. -->\n <small class=\"sx-hint\" *ngIf=\"kind === 'embedding' && tsneTooLarge\">{{ tsneTooLargeNote }}</small>\n\n <div [id]=\"chartDiv\" class=\"sx-plot\" *ngIf=\"!detached\"></div>\n\n <!-- Detached: the chart keeps drawing, in its own window. Says so rather than\n leaving a blank panel that looks broken. -->\n <div class=\"sx-detached\" *ngIf=\"detached\">\n Shown in its own window.\n </div>\n\n <ng-container *ngTemplateOutlet=\"hints\"></ng-container>\n\n</div>\n\n<!-- Declared once and rendered in both the panel and the detached window: the ladder is\n deliberately not mutually exclusive (a heatmap still needs its grouping prompt), so\n collapsing it to a single expression would drop a hint rather than move it. -->\n<ng-template #hints>\n <small class=\"sx-hint\" *ngIf=\"notice\">{{ notice }}</small>\n <small class=\"sx-hint\" *ngIf=\"!notice && suggestsGrouping\">\n Pick a grouping to compare categories.\n </small>\n <small class=\"sx-hint\" *ngIf=\"!notice && selectionCount && kind === 'histogram'\">\n The selection is overlaid on the full distribution.\n </small>\n <small class=\"sx-hint\" *ngIf=\"!notice && selectionCount && kind !== 'histogram' && kind !== 'heatmap'\">\n Showing the selection only.\n </small>\n <small class=\"sx-hint\" *ngIf=\"!notice && kind === 'heatmap'\">{{ heatmapNote }}</small>\n <small class=\"sx-hint\" *ngIf=\"!notice && kind === 'embedding'\">{{ embeddingNote }}</small>\n</ng-template>\n\n<!-- The detached chart. Its own non-modal, draggable, resizable window so it can be put\n beside the spatial-omics dialog. `appendTo=\"body\"` keeps it out of the dialog's\n stacking context, or it would be clipped by the panel it is meant to sit next to. -->\n<p-dialog *ngIf=\"detached\"\n [header]=\"detachedTitle\"\n [visible]=\"true\" (visibleChange)=\"toggleDetached()\"\n [modal]=\"false\" [resizable]=\"true\" [draggable]=\"true\"\n position=\"right\" appendTo=\"body\"\n (onShow)=\"onDetachedWindowShown()\"\n (onResizeEnd)=\"onDetachedResizeEnd()\"\n [style]=\"{ width: '34rem', height: '30rem' }\"\n [contentStyle]=\"{\n display: 'flex', flexDirection: 'column', gap: '0.25rem',\n padding: '0.4rem 0.6rem 0.5rem', flex: '1 1 auto', minHeight: '0',\n overflow: 'auto'\n }\"\n styleClass=\"spatial-detached-dialog\">\n <div [id]=\"detachedDiv\" class=\"sx-detached-plot\"\n [class.sx-plot-fixed]=\"hasFixedHeight\"></div>\n <ng-container *ngTemplateOutlet=\"hints\"></ng-container>\n</p-dialog>\n", styles: [":host ::ng-deep .spatial-charts-dialog .p-dialog-content{padding:.75rem 1rem 1rem}.sx-empty{padding:.5rem 0;font-size:.85rem;color:var(--text-color-secondary, #6c757d);line-height:1.4}.sx-body{display:flex;flex-direction:column;gap:.6rem}.sx-head{display:flex;align-items:center;gap:.5rem;padding-bottom:.4rem;border-bottom:1px solid var(--surface-border, #dee2e6)}.sx-subject{font-weight:600;font-size:.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sx-muted{font-weight:400;color:var(--text-color-secondary, #6c757d)}.sx-badge{margin-left:auto;padding:.1rem .4rem;border-radius:10px;font-size:.7rem;font-variant-numeric:tabular-nums;background:var(--surface-hover, rgba(213, 94, 0, .12));color:#a8480a;white-space:nowrap}.sx-busy{font-size:.8rem;color:var(--text-color-secondary, #6c757d)}.sx-controls{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.sx-controls p-dropdown{flex:1 1 8rem;min-width:0}:host ::ng-deep .sx-controls p-dropdown .p-dropdown{width:100%}:host ::ng-deep .sx-kind .p-button{padding:.25rem .6rem;font-size:.78rem}.sx-plot{width:100%;min-height:15rem;flex:1}.sx-hint{font-size:.75rem;color:var(--text-color-secondary, #6c757d);line-height:1.35}.sx-detached{flex:1;min-height:6rem;display:flex;align-items:center;justify-content:center;font-size:.78rem;color:var(--text-color-secondary, #6c757d)}.sx-detached-plot{flex:1;width:100%;min-height:0;overflow:hidden}.sx-detached-plot.sx-plot-fixed{flex:0 0 auto;min-height:auto;overflow:visible}.sx-help{margin-left:auto;align-self:center;font-size:.95rem;color:var(--text-color-secondary, #6c757d);cursor:help}.sx-help:hover,.sx-help:focus-visible{color:var(--primary-color, #3b82f6)}.sx-compute{display:flex;flex-direction:column;gap:.3rem;padding:.5rem 0}.sx-warn{color:var(--orange-600, #c2410c)}\n"] }]
757
+ args: [{ selector: 'spatial-charts', standalone: true, imports: [
758
+ CommonModule,
759
+ FormsModule,
760
+ DropdownModule,
761
+ SelectButtonModule,
762
+ TooltipModule,
763
+ SpatialHeatmapControlsComponent,
764
+ SpatialEmbeddingControlsComponent,
765
+ SpatialChartWindowComponent,
766
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"sx-body\" *ngIf=\"controls\">\n <!-- The chart follows whatever the map is coloured by, so the two can never\n disagree about what is being shown. -->\n <div class=\"sx-head\">\n <span class=\"sx-subject\" *ngIf=\"subject\">{{ subject }}</span>\n <span class=\"sx-subject sx-muted\" *ngIf=\"!subject\">nothing selected to chart</span>\n <span class=\"sx-badge\" *ngIf=\"selectionCount\"> {{ selectionCount | number }} selected </span>\n <i class=\"pi pi-spin pi-spinner sx-busy\" *ngIf=\"busy\" aria-label=\"loading\"></i>\n </div>\n\n <div class=\"sx-controls\">\n <p-selectButton\n [options]=\"kindOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"kind\"\n (ngModelChange)=\"onKind($event)\"\n styleClass=\"sx-kind\"\n ></p-selectButton>\n <p-dropdown\n [options]=\"groupOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"groupBy\"\n (ngModelChange)=\"onGroupBy($event)\"\n placeholder=\"No grouping\"\n appendTo=\"body\"\n [disabled]=\"kind === 'histogram'\"\n [pTooltip]=\"\n kind === 'heatmap'\n ? 'The categorical column the heatmap\\'s columns come from'\n : 'Split a violin or box by a categorical column'\n \"\n tooltipPosition=\"top\"\n ></p-dropdown>\n <!-- What the chart on screen shows, and what it must NOT be read for. Each of these\n answers a different question and two are routinely over-read, so the caveat\n travels with the explanation. -->\n <i\n class=\"pi pi-question-circle sx-help\"\n [pTooltip]=\"kindHelp\"\n [escape]=\"false\"\n tooltipPosition=\"left\"\n tooltipStyleClass=\"sx-help-tip\"\n [attr.aria-label]=\"'About the ' + kind + ' chart'\"\n tabindex=\"0\"\n ></i>\n </div>\n\n <spatial-heatmap-controls\n *ngIf=\"kind === 'heatmap'\"\n [geneOptions]=\"geneOptions\"\n [genes]=\"heatmapGenes\"\n [zScore]=\"heatmapZScore\"\n (genesChange)=\"onHeatmapGenes($event)\"\n (filterChange)=\"onGeneFilter($event)\"\n (zScoreChange)=\"onHeatmapZScore($event)\"\n ></spatial-heatmap-controls>\n\n <spatial-embedding-controls\n [isEmbedding]=\"kind === 'embedding'\"\n [embeddings]=\"embeddings\"\n [embedding]=\"embedding\"\n [computable]=\"isComputable\"\n [computed]=\"isComputed\"\n [running]=\"isComputing\"\n [state]=\"computeState\"\n [estimateLabel]=\"computeEstimateLabel\"\n [tooLargeNote]=\"tsneTooLarge ? tsneTooLargeNote : null\"\n [detached]=\"detached\"\n (embeddingChange)=\"onEmbedding($event)\"\n (compute)=\"computeEmbedding()\"\n (cancel)=\"cancelCompute()\"\n (detachedToggle)=\"toggleDetached()\"\n >\n </spatial-embedding-controls>\n\n <div [id]=\"chartDiv\" class=\"sx-plot\" *ngIf=\"!detached\"></div>\n\n <!-- Detached: the chart keeps drawing, in its own window. Says so rather than\n leaving a blank panel that looks broken. -->\n <div class=\"sx-detached\" *ngIf=\"detached\">Shown in its own window.</div>\n\n <ng-container *ngTemplateOutlet=\"hints\"></ng-container>\n</div>\n\n<!-- Declared once and rendered in both the panel and the detached window: the ladder is\n deliberately not mutually exclusive (a heatmap still needs its grouping prompt), so\n collapsing it to a single expression would drop a hint rather than move it. -->\n<ng-template #hints>\n <small class=\"sx-hint\" *ngIf=\"notice\">{{ notice }}</small>\n <small class=\"sx-hint\" *ngIf=\"!notice && suggestsGrouping\"> Pick a grouping to compare categories. </small>\n <small class=\"sx-hint\" *ngIf=\"!notice && selectionCount && kind === 'histogram'\">\n The selection is overlaid on the full distribution.\n </small>\n <small class=\"sx-hint\" *ngIf=\"!notice && selectionCount && kind !== 'histogram' && kind !== 'heatmap'\">\n Showing the selection only.\n </small>\n <small class=\"sx-hint\" *ngIf=\"!notice && kind === 'heatmap'\">{{ heatmapNote }}</small>\n <small class=\"sx-hint\" *ngIf=\"!notice && kind === 'embedding'\">{{ embeddingNote }}</small>\n</ng-template>\n\n<spatial-chart-window\n *ngIf=\"detached\"\n [title]=\"detachedTitle\"\n [divId]=\"detachedDiv\"\n [fixedHeight]=\"hasFixedHeight\"\n (closed)=\"toggleDetached()\"\n (shown)=\"onDetachedWindowShown()\"\n (resizeEnd)=\"onDetachedResizeEnd()\"\n>\n <ng-container *ngTemplateOutlet=\"hints\"></ng-container>\n</spatial-chart-window>\n", styles: [".sx-controls{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.sx-controls p-dropdown{flex:1 1 8rem;min-width:0}:host ::ng-deep .sx-controls p-dropdown .p-dropdown{width:100%}.sx-hint{font-size:.75rem;color:var(--text-color-secondary, #6c757d);line-height:1.35}.sx-warn{color:var(--orange-600, #c2410c)}:host ::ng-deep .spatial-charts-dialog .p-dialog-content{padding:.75rem 1rem 1rem}.sx-empty{padding:.5rem 0;font-size:.85rem;color:var(--text-color-secondary, #6c757d);line-height:1.4}.sx-body{display:flex;flex-direction:column;gap:.6rem}.sx-head{display:flex;align-items:center;gap:.5rem;padding-bottom:.4rem;border-bottom:1px solid var(--surface-border, #dee2e6)}.sx-subject{font-weight:600;font-size:.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sx-muted{font-weight:400;color:var(--text-color-secondary, #6c757d)}.sx-badge{margin-left:auto;padding:.1rem .4rem;border-radius:10px;font-size:.7rem;font-variant-numeric:tabular-nums;background:var(--surface-hover, rgba(213, 94, 0, .12));color:#a8480a;white-space:nowrap}.sx-busy{font-size:.8rem;color:var(--text-color-secondary, #6c757d)}:host ::ng-deep .sx-kind .p-button{padding:.25rem .6rem;font-size:.78rem}.sx-plot{width:100%;min-height:15rem;flex:1}.sx-detached{flex:1;min-height:6rem;display:flex;align-items:center;justify-content:center;font-size:.78rem;color:var(--text-color-secondary, #6c757d)}.sx-help{margin-left:auto;align-self:center;font-size:.95rem;color:var(--text-color-secondary, #6c757d);cursor:help}.sx-help:hover,.sx-help:focus-visible{color:var(--primary-color, #3b82f6)}\n"] }]
1272
767
  }], ctorParameters: () => [{ type: undefined, decorators: [{
1273
768
  type: Inject,
1274
769
  args: [VISUALIZER]
1275
770
  }] }], propDecorators: { active: [{
1276
771
  type: Input
1277
772
  }] } });
1278
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BhdGlhbC1jaGFydHMuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9zcGF0aWFsLWNvbnRyb2xzL3NwYXRpYWwtY2hhcnRzL3NwYXRpYWwtY2hhcnRzLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvc3BhdGlhbC1jb250cm9scy9zcGF0aWFsLWNoYXJ0cy9zcGF0aWFsLWNoYXJ0cy5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ1UsU0FBUyxFQUFFLE1BQU0sRUFBRSxLQUFLLEdBQ3hDLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxZQUFZLEVBQUUsYUFBYSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ25ELE9BQU8sS0FBSyxNQUFNLE1BQU0sb0JBQW9CLENBQUM7QUFFN0MsT0FBTyxFQUFFLFVBQVUsRUFBaUMsTUFBTSxxQ0FBcUMsQ0FBQztBQUtoRyxPQUFPLEVBQW9DLG9CQUFvQixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDdkcsT0FBTyxFQUNpQixjQUFjLEVBQUUsYUFBYSxHQUNwRCxNQUFNLGlDQUFpQyxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxhQUFhLEVBQUUsYUFBYSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDN0UsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQzNELE9BQU8sRUFBbUIsbUJBQW1CLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUN2RixPQUFPLEVBQzBCLG9CQUFvQixFQUFFLGdCQUFnQixFQUFFLGtCQUFrQixFQUN6RixnQkFBZ0IsRUFBRSxvQkFBb0IsRUFBRSxZQUFZLEVBQUUsYUFBYSxFQUFFLFdBQVcsRUFBRSxlQUFlLEdBQ2xHLE1BQU0sbURBQW1ELENBQUM7Ozs7Ozs7Ozs7OztBQUUzRDs7Ozs7Ozs7O0dBU0c7QUFDSCxNQUFNLGlCQUFpQixHQUFHLG1CQUFtQixDQUFDO0FBQzlDLFNBQVMsa0JBQWtCO0lBQ3pCLElBQUksT0FBTyxRQUFRLEtBQUssV0FBVztRQUFFLE9BQU87SUFDNUMsSUFBSSxRQUFRLENBQUMsY0FBYyxDQUFDLGlCQUFpQixDQUFDO1FBQUUsT0FBTztJQUN2RCxNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzlDLEtBQUssQ0FBQyxFQUFFLEdBQUcsaUJBQWlCLENBQUM7SUFDN0IsS0FBSyxDQUFDLFdBQVcsR0FBRyxnRUFBZ0U7VUFDaEYseURBQXlELENBQUM7SUFDOUQsUUFBUSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDbkMsQ0FBQztBQUVELHNGQUFzRjtBQUN0RixJQUFJLGdCQUFnQixHQUFHLENBQUMsQ0FBQztBQUd6QiwwRUFBMEU7QUFDMUUsTUFBTSxZQUFZLEdBQUc7SUFDbkIsV0FBVyxFQUFFLEtBQUs7SUFDbEIsVUFBVSxFQUFFLElBQUk7SUFDaEIsc0JBQXNCLEVBQUUsQ0FBQyxTQUFTLEVBQUUsVUFBVSxFQUFFLGFBQWEsQ0FBQztDQUMvRCxDQUFDO0FBRUY7Ozs7OztHQU1HO0FBQ0gsTUFBTSxnQkFBZ0IsR0FBRztJQUN2QixXQUFXLEVBQUUsS0FBSztJQUNsQixVQUFVLEVBQUUsSUFBSTtJQUNoQixVQUFVLEVBQUUsSUFBSTtJQUNoQixzQkFBc0IsRUFBRSxDQUFDLGFBQWEsQ0FBQztDQUN4QyxDQUFDO0FBRUY7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FtQkc7QUFNSCxNQUFNLE9BQU8sc0JBQXNCO0lBQ2pDOzs7OztPQUtHO0lBQ0gsSUFBYSxNQUFNLENBQUMsRUFBVztRQUM3QixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDO1FBQzFCLElBQUksQ0FBQyxRQUFRLEdBQUcsRUFBRSxDQUFDO1FBQ25CLDBFQUEwRTtRQUMxRSwwRUFBMEU7UUFDMUUsOEJBQThCO1FBQzlCLElBQUksRUFBRSxJQUFJLENBQUMsR0FBRztZQUFFLFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxLQUFLLElBQUksQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQzthQUNuRCxJQUFJLEVBQUU7WUFBRSxLQUFLLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztJQUNsQyxDQUFDO0lBQ0QsSUFBSSxNQUFNO1FBQ1IsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDO0lBQ3ZCLENBQUM7YUFxQ3VCLHFCQUFnQixHQUErQztRQUNyRixFQUFFLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLFdBQVcsRUFBRTtRQUMxQyxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRTtRQUNwQyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRTtLQUMvQixBQUp1QyxDQUl0QzthQUNzQixzQkFBaUIsR0FBK0M7UUFDdEYsRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUU7S0FDckMsQUFGd0MsQ0FFdkM7SUFFRjtzRUFDa0U7YUFDMUMsaUJBQVksR0FBK0M7UUFDakYsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUU7S0FDdkMsQUFGbUMsQ0FFbEM7SUFFRjs7Ozs7OztPQU9HO2FBQ3FCLGVBQVUsR0FBMkI7UUFDM0QsRUFBRSxJQUFJLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUU7UUFDeEUsRUFBRSxJQUFJLEVBQUUsY0FBYyxFQUFFLEtBQUssRUFBRSxvQkFBb0IsRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUU7S0FDOUUsQUFIaUMsQ0FHaEM7SUFFRiwrRUFBK0U7YUFDdkQsbUJBQWMsR0FBRyxZQUFZLEFBQWYsQ0FBZ0I7SUFFdEQ7Ozs7Ozs7Ozs7OztPQVlHO2FBQ2Esa0NBQTZCLEdBQUcsSUFBSSxBQUFQLENBQVE7SUFFckQsNEVBQTRFO2FBQ3BELG9CQUFlLEdBQUcsRUFBRSxPQUFPLEVBQUUsRUFBRSxFQUFFLFlBQVksRUFBRSxJQUFJLEVBQUUsQUFBdEMsQ0FBdUM7SUFvQzlFLG9FQUFvRTtJQUNwRSx1RkFBdUY7SUFDdkYsd0ZBQXdGO0lBQ3hGLHNGQUFzRjthQUM5RCxvQkFBZSxHQUErQztRQUNwRixFQUFFLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLFdBQVcsRUFBRTtLQUMzQyxBQUZzQyxDQUVyQztJQUVGOzs4REFFMEQ7SUFDMUQsSUFBSSxXQUFXO1FBQ2IsT0FBTztZQUNMLEdBQUcsQ0FBQyxJQUFJLENBQUMsV0FBVztnQkFDbEIsQ0FBQyxDQUFDLHNCQUFzQixDQUFDLGlCQUFpQjtnQkFDMUMsQ0FBQyxDQUFDLHNCQUFzQixDQUFDLGdCQUFnQixDQUFDO1lBQzVDLEdBQUcsc0JBQXNCLENBQUMsWUFBWTtZQUN0QyxpRkFBaUY7WUFDakYsaUZBQWlGO1lBQ2pGLHVDQUF1QztZQUN2QyxHQUFHLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxzQkFBc0IsQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztTQUM5RSxDQUFDO0lBQ0osQ0FBQztJQXNCRDttRUFDK0Q7YUFDdkMseUJBQW9CLEdBQUcsR0FBRyxBQUFOLENBQU87SUFDbkQ7Ozs7Ozs7O09BUUc7YUFDcUIsd0JBQW1CLEdBQUcsRUFBRSxBQUFMLENBQU07SUF5Q2pELFlBQWlELEdBQWdCO1FBQWhCLFFBQUcsR0FBSCxHQUFHLENBQWE7UUF4TnpELGFBQVEsR0FBRyxJQUFJLENBQUM7UUFFeEI7OztxREFHNkM7UUFDN0M7O3NFQUU4RDtRQUM3QyxRQUFHLEdBQUcsRUFBRSxnQkFBZ0IsQ0FBQztRQUNqQyxhQUFRLEdBQUcsdUJBQXVCLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUN0RDs7Ozs7Ozs7O1dBU0c7UUFDTSxnQkFBVyxHQUFHLDJCQUEyQixJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFFN0Q7Ozs7Ozs7Ozs7O1dBV0c7UUFDSCxhQUFRLEdBQUcsS0FBSyxDQUFDO1FBa0RqQix5RkFBeUY7UUFDeEUsYUFBUSxHQUFHLElBQUksR0FBRyxFQUE0QixDQUFDO1FBRWhFLHVDQUF1QztRQUMvQixlQUFVLEdBQStCLElBQUksQ0FBQztRQUV0RDs7Ozs7Ozs7O1dBU0c7UUFDSyxpQkFBWSxHQUFHLENBQUMsQ0FBQztRQUV6QixvRkFBb0Y7UUFDcEYsb0JBQWUsR0FBa0IsSUFBSSxDQUFDO1FBRXRDLHFGQUFxRjtRQUNyRixtQkFBYyxHQUFrQixJQUFJLENBQUM7UUFFckMsbUJBQWMsR0FBa0IsSUFBSSxDQUFDO1FBRXJDLGlCQUFZLEdBQWtCLElBQUksQ0FBQztRQUVuQyw2RUFBNkU7UUFDN0UsaUJBQVksR0FBRyxLQUFLLENBQUM7UUFFckIsb0VBQW9FO1FBQzVELHFCQUFnQixHQUFHLENBQUMsQ0FBQztRQTBCN0IsYUFBUSxHQUE0QixJQUFJLENBQUM7UUFDekMsU0FBSSxHQUFtQixXQUFXLENBQUM7UUFDbkMsb0VBQW9FO1FBQ3BFLGVBQVUsR0FBMkIsRUFBRSxDQUFDO1FBQ3hDLGNBQVMsR0FBZ0MsSUFBSSxDQUFDO1FBQ3RDLG9CQUFlLEdBQTRCLElBQUksQ0FBQztRQUN4RDs0REFDb0Q7UUFDNUMsbUJBQWMsR0FBcUUsSUFBSSxDQUFDO1FBQ2hHOzs7Ozs7V0FNRztRQUNILGlCQUFZLEdBQWEsRUFBRSxDQUFDO1FBQzVCO2lGQUN5RTtRQUN6RSxrQkFBYSxHQUFHLElBQUksQ0FBQztRQWNyQixrRUFBa0U7UUFDMUQsa0JBQWEsR0FBRyxDQUFDLENBQUM7UUFDMUIsZ0JBQVcsR0FBdUMsRUFBRSxDQUFDO1FBQ3JEOztpQ0FFeUI7UUFDakIsY0FBUyxHQUFhLEVBQUUsQ0FBQztRQUNqQzswREFDa0Q7UUFDMUMsY0FBUyxHQUFHLEVBQUUsQ0FBQztRQUN2Qjt5RUFDaUU7UUFDaEQsY0FBUyxHQUFHLElBQUksR0FBRyxFQUF3QixDQUFDO1FBQzdELDZFQUE2RTtRQUM3RSxZQUFPLEdBQWtCLElBQUksQ0FBQztRQUM5QixpQkFBWSxHQUE4QyxFQUFFLENBQUM7UUFFN0QseURBQXlEO1FBQ3pELFlBQU8sR0FBMEIsSUFBSSxDQUFDO1FBQ3RDLG1CQUFjLEdBQUcsQ0FBQyxDQUFDO1FBQ25CLCtFQUErRTtRQUMvRSxXQUFNLEdBQWtCLElBQUksQ0FBQztRQUM3QixTQUFJLEdBQUcsS0FBSyxDQUFDO1FBRUwsU0FBSSxHQUFxQixFQUFFLEdBQUcsb0JBQW9CLEVBQUUsQ0FBQztRQUNyRCxjQUFTLEdBQXlCLGNBQWMsRUFBRSxDQUFDO1FBQ25ELFdBQU0sR0FBd0IsSUFBSSxDQUFDO1FBQzNDO2lGQUN5RTtRQUNqRSxnQkFBVyxHQUF5QixJQUFJLENBQUM7UUFDekMsYUFBUSxHQUF5QixJQUFJLENBQUM7UUFDOUM7bUZBQzJFO1FBQ25FLFVBQUssR0FBRyxDQUFDLENBQUM7UUFDbEIsZ0ZBQWdGO1FBQ3hFLGVBQVUsR0FBRyxDQUFDLENBQUM7UUFDdkIsd0RBQXdEO1FBQ2hELFdBQU0sR0FBRyxLQUFLLENBQUM7UUFDTixTQUFJLEdBQUcsSUFBSSxZQUFZLEVBQUUsQ0FBQztRQWdGM0M7Ozs7OztXQU1HO1FBQ0ssZ0JBQVcsR0FBa0IsSUFBSSxDQUFDO0lBckYwQixDQUFDO0lBRXJFLFFBQVE7UUFDTixrRkFBa0Y7UUFDbEYseURBQXlEO1FBQ3pELGtCQUFrQixFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLGtCQUFrQixFQUFFLEVBQUUsSUFBSSxJQUFJLENBQUM7UUFDeEQsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRO1lBQUUsT0FBTztRQUUzQixJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUM7WUFDMUIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxhQUFhLEVBQUUsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLGFBQWEsRUFBRTtTQUM3RCxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsU0FBUyxDQUFDLEVBQUUsRUFBRTtZQUNqQyxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsT0FBTyxFQUFFLElBQUksS0FBSyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxJQUFJO21CQUMvRCxJQUFJLENBQUMsT0FBTyxFQUFFLElBQUksS0FBSyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUM7WUFDcEQsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7WUFDakIsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDO1lBQzVCLElBQUksQ0FBQyxTQUFTLEdBQUcsU0FBUyxDQUFDO1lBQzNCLElBQUksQ0FBQyxjQUFjLEdBQUcsU0FBUyxDQUFDLEtBQUssQ0FBQztZQUN0Qyx3RUFBd0U7WUFDeEUscUVBQXFFO1lBQ3JFLHVFQUF1RTtZQUN2RSw4REFBOEQ7WUFDOUQsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLElBQUksYUFBYSxFQUFFLENBQUM7Z0JBQ2xDLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDO2dCQUNuQixLQUFLLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNyQixDQUFDO2lCQUFNLENBQUM7Z0JBQ04sMEVBQTBFO2dCQUMxRSxLQUFLLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNyQixDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUVKLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsV0FBVyxFQUFFLENBQUMsU0FBUyxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUU7WUFDOUQsSUFBSSxDQUFDLFlBQVksR0FBRztnQkFDbEIsRUFBRSxLQUFLLEVBQUUsYUFBYSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUU7Z0JBQ3JDLEdBQUcsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLGtCQUFrQixFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsS0FBSyxFQUFFLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQzthQUNwRixDQUFDO1lBQ0YsOEVBQThFO1lBQzlFLElBQUksSUFBSSxDQUFDLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxLQUFLLElBQUksQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO2dCQUM3RSxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQztnQkFDcEIsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUM7WUFDdkIsQ0FBQztZQUNELDJFQUEyRTtZQUMzRSwwRUFBMEU7WUFDMUUseURBQXlEO1lBQ3pELElBQUksQ0FBQyxTQUFTLEdBQUcsQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLFFBQVEsRUFBRSxLQUFLLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztZQUN2RCxJQUFJLENBQUMsU0FBUyxHQUFHLEVBQUUsQ0FBQztZQUNwQixtRkFBbUY7WUFDbkYsMEVBQTBFO1lBQzFFLE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUN0QyxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDbEUsSUFBSSxDQUFDLGtCQUFrQixFQUFFLENBQUM7WUFDMUIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUV2QiwrRUFBK0U7WUFDL0UsZ0ZBQWdGO1lBQ2hGLG1EQUFtRDtZQUNuRCxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ3RCLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUN0QixNQUFNLFNBQVMsR0FBRyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7WUFDckUsbUZBQW1GO1lBQ25GLGdGQUFnRjtZQUNoRiwwQkFBMEI7WUFDMUIsTUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsS0FBSyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ3JFLE1BQU0sT0FBTyxHQUFHLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEtBQUssSUFBSSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztZQUM3RSxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsT0FBTyxFQUFFLFlBQVksQ0FBQyxLQUFLLElBQUksQ0FBQyxDQUFDO1lBQ3pELElBQUksQ0FBQyxZQUFZLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTzttQkFDakMsSUFBSSxDQUFDLGdCQUFnQixHQUFHLHNCQUFzQixDQUFDLDZCQUE2QixDQUFDO1lBQ2xGLElBQUksQ0FBQyxVQUFVLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVk7Z0JBQ3hELENBQUMsQ0FBQyxDQUFDLEdBQUcsU0FBUyxFQUFFLEdBQUcsc0JBQXNCLENBQUMsVUFBVSxDQUFDO2dCQUN0RCxDQUFDLENBQUMsU0FBUyxDQUFDO1lBQ2QsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQztZQUM1QyxJQUFJLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQztZQUM1QixJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksQ0FBQztZQUMzQixtRkFBbUY7WUFDbkYsSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLFdBQVcsSUFBSSxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sS0FBSyxDQUFDO2dCQUFFLElBQUksQ0FBQyxJQUFJLEdBQUcsV0FBVyxDQUFDO1FBQ3pGLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDTixDQUFDO0lBV0Q7Ozs7OztPQU1HO0lBQ0gsTUFBTTtRQUNKLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQzVCLENBQUM7SUFFRDs7Ozs7Ozs7O09BU0c7SUFDSyxLQUFLLENBQUMsR0FBVztRQUN2QixNQUFNLEVBQUUsR0FBRyxRQUFRLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3hDLElBQUksQ0FBQyxFQUFFO1lBQUUsT0FBTztRQUNoQixNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUN6Qyw0RUFBNEU7UUFDNUUsd0VBQXdFO1FBQ3hFLElBQUksS0FBSyxJQUFJLENBQUM7WUFBRSxPQUFPO1FBQ3ZCLElBQUksQ0FBQztZQUNILE1BQU0sQ0FBQyxRQUFRLENBQUMsRUFBRSxFQUFFLElBQUksQ0FBQyxXQUFXLEtBQUssSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBQ2xGLENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCx1QkFBdUI7UUFDekIsQ0FBQztJQUNILENBQUM7SUFFRCxXQUFXO1FBQ1QsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUN4QixtRkFBbUY7UUFDbkYsb0ZBQW9GO1FBQ3BGLGNBQWM7UUFDZCxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDdEIsOEVBQThFO1FBQzlFLHVEQUF1RDtRQUN2RCxLQUFLLE1BQU0sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQztZQUNwRCxJQUFJLENBQUM7Z0JBQ0gsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNwQixDQUFDO1lBQUMsTUFBTSxDQUFDO2dCQUNQLG9FQUFvRTtZQUN0RSxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFRCxlQUFlO1FBQ2IsMEVBQTBFO1FBQzFFLEtBQUssSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO0lBQ3JCLENBQUM7SUFFRCxNQUFNLENBQUMsSUFBb0I7UUFDekIsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7UUFDakIsSUFBSSxJQUFJLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDdkIsd0VBQXdFO1lBQ3hFLGtFQUFrRTtZQUNsRSxJQUFJLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxLQUFLLENBQUMsSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLElBQUksS0FBSyxTQUFTLEVBQUUsQ0FBQztnQkFDdkUsSUFBSSxDQUFDLFlBQVksR0FBRyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDMUMsQ0FBQztZQUNELEtBQUssQ0FBQyxLQUFLLElBQUksRUFBRTtnQkFDZixNQUFNLElBQUksQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO2dCQUNuQyxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO2dCQUM5QixLQUFLLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNyQixDQUFDLENBQUMsRUFBRSxDQUFDO1lBQ0wsT0FBTztRQUNULENBQUM7UUFDRCxLQUFLLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztJQUNyQixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNLLEtBQUssQ0FBQyxxQkFBcUI7UUFDakMsSUFBSSxJQUFJLENBQUMsUUFBUSxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVE7WUFBRSxPQUFPO1FBQzVDLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEVBQUUsS0FBSyxDQUFDO1FBQzVELElBQUksQ0FBQyxLQUFLO1lBQUUsT0FBTztRQUNuQixNQUFNLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDOUIsQ0FBQztJQUVELGVBQWUsQ0FBQyxFQUFXO1FBQ3pCLElBQUksQ0FBQyxhQUFhLEdBQUcsRUFBRSxDQUFDO1FBQ3hCLEtBQUssSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO0lBQ3JCLENBQUM7SUFFRCwyREFBMkQ7SUFDM0QsSUFBSSxXQUFXO1FBQ2IsSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLE1BQU0sS0FBSyxDQUFDO1lBQUUsT0FBTyxzQ0FBc0MsQ0FBQztRQUNsRixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsY0FBYyxHQUFHLENBQUM7ZUFDbEMsSUFBSSxDQUFDLGNBQWMsSUFBSSxzQkFBc0IsQ0FBQyxvQkFBb0IsQ0FBQztRQUN4RSxJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQ1osT0FBTyxpQ0FBaUMsSUFBSSxDQUFDLGNBQWMsS0FBSztrQkFDNUQseUVBQXlFLENBQUM7UUFDaEYsQ0FBQztRQUNELE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxjQUFjLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDO1FBQzNFLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxhQUFhO1lBQy9CLENBQUMsQ0FBQyxzR0FBc0c7WUFDeEcsQ0FBQyxDQUFDLDZEQUE2RCxDQUFDO1FBQ2xFLHVFQUF1RTtRQUN2RSxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsYUFBYSxHQUFHLENBQUM7WUFDbkMsQ0FBQyxDQUFDLGdCQUFnQixzQkFBc0IsQ0FBQyxtQkFBbUIsZ0JBQWdCO2tCQUN4RSxHQUFHLHNCQUFzQixDQUFDLG1CQUFtQixHQUFHLElBQUksQ0FBQyxhQUFhLElBQUk7a0JBQ3RFLHNEQUFzRDtZQUMxRCxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ1AsT0FBTyw0Q0FBNEMsSUFBSSxDQUFDLE9BQU8sSUFBSSxPQUFPLFVBQVUsS0FBSyxHQUFHO2NBQ3hGLEdBQUcsTUFBTSxHQUFHLE1BQU0sRUFBRSxDQUFDO0lBQzNCLENBQUM7SUFFRCxpQ0FBaUM7SUFDakMsS0FBSyxDQUFDLGNBQWMsQ0FBQyxLQUFlO1FBQ2xDLElBQUksQ0FBQyxZQUFZLEdBQUcsS0FBSyxJQUFJLEVBQUUsQ0FBQztRQUNoQywrRUFBK0U7UUFDL0UsSUFBSSxDQUFDLGtCQUFrQixFQUFFLENBQUM7UUFDMUIsTUFBTSxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztRQUM5QixLQUFLLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztJQUNyQixDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0ssS0FBSyxDQUFDLGdCQUFnQjtRQUM1QixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDO1FBQy9CLElBQUksQ0FBQyxRQUFRO1lBQUUsT0FBTztRQUN0QixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3hFLElBQUksT0FBTyxDQUFDLE1BQU0sS0FBSyxDQUFDO1lBQUUsT0FBTztRQUNqQyxNQUFNLElBQUksR0FBRyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUM7UUFDMUIsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7UUFDakIsSUFBSSxDQUFDO1lBQ0gsS0FBSyxNQUFNLElBQUksSUFBSSxPQUFPLEVBQUUsQ0FBQztnQkFDM0IsTUFBTSxNQUFNLEdBQUcsTUFBTSxRQUFRLENBQUMsZ0JBQWdCLENBQUMsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7Z0JBQzFFLElBQUksSUFBSSxLQUFLLElBQUksQ0FBQyxLQUFLO29CQUFFLE9BQU87Z0JBQ2hDLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxNQUFNLENBQUMsQ0FBQztZQUNuQyxDQUFDO1lBQ0QsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUM7UUFDckIsQ0FBQztRQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7WUFDYixJQUFJLElBQUksS0FBSyxJQUFJLENBQUMsS0FBSztnQkFBRSxPQUFPO1lBQ2hDLElBQUksQ0FBQyxNQUFNLEdBQUcsZ0NBQWlDLEdBQWEsRUFBRSxPQUFPLElBQUksR0FBRyxFQUFFLENBQUM7UUFDakYsQ0FBQztnQkFBUyxDQUFDO1lBQ1QsSUFBSSxJQUFJLEtBQUssSUFBSSxDQUFDLEtBQUs7Z0JBQUUsSUFBSSxDQUFDLElBQUksR0FBRyxLQUFLLENBQUM7UUFDN0MsQ0FBQztJQUNILENBQUM7SUFFRCxLQUFLLENBQUMsU0FBUyxDQUFDLElBQW1CO1FBQ2pDLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDO1FBQ3BCLElBQUksQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDNUIsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUM7WUFDckIsS0FBSyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDbkIsT0FBTztRQUNULENBQUM7UUFDRCw4RUFBOEU7UUFDOUUsK0NBQStDO1FBQy9DLE1BQU0sSUFBSSxHQUFHLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQztRQUMvQixJQUFJLENBQUM7WUFDSCxNQUFNLElBQUksR0FBRyxNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3ZELElBQUksSUFBSSxLQUFLLElBQUksQ0FBQyxVQUFVO2dCQUFFLE9BQU87WUFDckMsSUFBSSxDQUFDLFFBQVEsR0FBRyxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLFVBQVUsRUFBRSxJQUFJLENBQUMsVUFBVSxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDMUYsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLElBQUksSUFBSSxLQUFLLElBQUksQ0FBQyxVQUFVO2dCQUFFLE9BQU87WUFDckMsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUM7WUFDckIsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUM7UUFDdEIsQ0FBQztRQUNELEtBQUssSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO0lBQ3JCLENBQUM7SUFFRCwyRUFBMkU7SUFDM0UsSUFBSSxnQkFBZ0I7UUFDbEIsT0FBTyxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztJQUMxRixDQUFDO0lBRUQsSUFBSSxPQUFPO1FBQ1QsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPO1lBQUUsT0FBTyxFQUFFLENBQUM7UUFDN0IsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLFFBQVEsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUM7SUFDM0YsQ0FBQztJQUVELDJFQUEyRTtJQUUzRSwwREFBMEQ7SUFDbEQsS0FBSyxDQUFDLE1BQU07UUFDbEIsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQztRQUMvQixNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQztRQUNqQyxNQUFNLElBQUksR0FBRyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUM7UUFDMUIsSUFBSSxDQUFDLFFBQVEsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ3pCLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDO1lBQ25CLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDO1lBQ3hCLElBQUksQ0FBQyxNQUFNLEdBQUcsUUFBUTtnQkFDcEIsQ0FBQyxDQUFDLGlFQUFpRTtnQkFDbkUsQ0FBQyxDQUFDLElBQUksQ0FBQztZQUNULEtBQUssSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ25CLE9BQU87UUFDVCxDQUFDO1FBQ0QsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7UUFDakIseUVBQXlFO1FBQ3pFLHdFQUF3RTtRQUN4RSw0Q0FBNEM7UUFDNUMsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFDLElBQUksS0FBSyxRQUFRO2VBQ3pDLENBQUMsUUFBUSxDQUFDLGtCQUFrQixFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNqRSxJQUFJLENBQUM7WUFDSCxJQUFJLGFBQWEsRUFBRSxDQUFDO2dCQUNsQixNQUFNLElBQUksR0FBRyxNQUFNLFFBQVEsQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUN6RCxJQUFJLElBQUksS0FBSyxJQUFJLENBQUMsS0FBSztvQkFBRSxPQUFPLENBQUMsYUFBYTtnQkFDOUMsSUFBSSxDQUFDLFdBQVcsR0FBRyxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLFVBQVUsRUFBRSxJQUFJLENBQUMsVUFBVSxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7Z0JBQzNGLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDO2dCQUNuQixJQUFJLENBQUMsSUFBSSxHQUFHLFFBQVEsQ0FBQztnQkFDckIsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUM7WUFDckIsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLE1BQU0sTUFBTSxHQUFHLE1BQU0sUUFBUSxDQUFDLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUN2RCxJQUFJLElBQUksS0FBSyxJQUFJLENBQUMsS0FBSztvQkFBRSxPQUFPO2dCQUNoQyxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztnQkFDckIsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUM7Z0JBQ3hCLElBQUksSUFBSSxDQUFDLElBQUksS0FBSyxRQUFRO29CQUFFLElBQUksQ0FBQyxJQUFJLEdBQUcsV0FBVyxDQUFDO2dCQUNwRCxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztZQUNyQixDQUFDO1FBQ0gsQ0FBQztRQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7WUFDYixJQUFJLElBQUksS0FBSyxJQUFJLENBQUMsS0FBSztnQkFBRSxPQUFPO1lBQ2hDLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDO1lBQ25CLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDO1lBQ3hCLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxNQUFNLENBQUMsSUFBSSwyQkFBNEIsR0FBYSxFQUFFLE9BQU8sSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUMzRixDQUFDO2dCQUFTLENBQUM7WUFDVCxJQUFJLElBQUksS0FBSyxJQUFJLENBQUMsS0FBSztnQkFBRSxJQUFJLENBQUMsSUFBSSxHQUFHLEtBQUssQ0FBQztRQUM3QyxDQUFDO1FBQ0QsS0FBSyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7SUFDckIsQ0FBQztJQUVEOzs7Ozs7Ozs7O09BVUc7SUFDSyxLQUFLLENBQUMsZUFBZTtRQUMzQixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDO1FBQy9CLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUM7UUFDNUIsSUFBSSxDQUFDLFFBQVEsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ3ZCLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUNqQixPQUFPO1FBQ1QsQ0FBQztRQUNELElBQUksQ0FBQyxRQUFRLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDM0IsSUFBSSxDQUFDLE1BQU0sR0FBRyw2Q0FBNkMsQ0FBQztZQUM1RCxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDakIsT0FBTztRQUNULENBQUM7UUFFRCxvRkFBb0Y7UUFDcEYsaUVBQWlFO1FBQ2pFLE1BQU0sSUFBSSxHQUFHLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQztRQUMxQixtRkFBbUY7UUFDbkYsaUZBQWlGO1FBQ2pGLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUMzQyxJQUFJLEtBQUssRUFBRSxDQUFDO1lBQ1YsSUFBSSxDQUFDLGVBQWUsR0FBRyxLQUFLLENBQUM7UUFDL0IsQ0FBQzthQUFNLElBQUksSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1lBQzdCLG9GQUFvRjtZQUNwRixvRUFBb0U7WUFDcEUsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQ2pCLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDO1lBQ25CLE9BQU87UUFDVCxDQUFDO2FBQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxlQUFlLElBQUksSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsSUFBSSxLQUFLLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNqRixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztZQUNqQixJQUFJLENBQUM7Z0JBQ0gsTUFBTSxNQUFNLEdBQUcsTUFBTSxRQUFRLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDdEQsSUFBSSxJQUFJLEtBQUssSUFBSSxDQUFDLEtBQUs7b0JBQUUsT0FBTztnQkFDaEMsSUFBSSxDQUFDLGVBQWUsR0FBRyxNQUFNLENBQUM7WUFDaEMsQ0FBQztZQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7Z0JBQ2IsSUFBSSxJQUFJLEtBQUssSUFBSSxDQUFDLEtBQUs7b0JBQUUsT0FBTztnQkFDaEMsSUFBSSxDQUFDLE1BQU0sR0FBRyxrQkFBa0IsSUFBSSxDQUFDLEtBQUssSUFBSSxJQUFJLENBQUMsSUFBSSxJQUFJO3NCQUN2RCxHQUFJLEdBQWEsRUFBRSxPQUFPLElBQUksR0FBRyxFQUFFLENBQUM7Z0JBQ3hDLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztnQkFDakIsT0FBTztZQUNULENBQUM7b0JBQVMsQ0FBQztnQkFDVCxJQUFJLElBQUksS0FBSyxJQUFJLENBQUMsS0FBSztvQkFBRSxJQUFJLENBQUMsSUFBSSxHQUFHLEtBQUssQ0FBQztZQUM3QyxDQUFDO1FBQ0gsQ0FBQztRQUNELE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUM7UUFDcEMsSUFBSSxDQUFDLE1BQU07WUFBRSxPQUFPO1FBRXBCLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUM7UUFDL0IsTUFBTSxFQUFFLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMzQyxJQUFJLENBQUMsRUFBRTtZQUFFLE9BQU8sQ0FBQyw0Q0FBNEM7UUFDN0QsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUUvQixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztRQUNuQixNQUFNLEtBQUssR0FBRztZQUNaLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQztZQUNYLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQztZQUNYLCtFQUErRTtZQUMvRSxpRkFBaUY7WUFDakYsNkNBQTZDO1lBQzdDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUNwQyxLQUFLLEVBQUUsSUFBSSxDQUFDLEtBQUssSUFBSSxJQUFJLENBQUMsSUFBSTtZQUM5QixPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQU87WUFDckIsMEVBQTBFO1lBQzFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxFQUFFLGFBQWEsRUFBRSxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUNwRSxHQUFHLENBQUMsSUFBSSxDQUFDLFdBQVc7Z0JBQ2xCLENBQUMsQ0FBQztvQkFDQSxVQUFVLEVBQUU7d0JBQ1YsS0FBSyxFQUFFLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSzt3QkFDN0IsS0FBSyxFQUFFLElBQUksQ0FBQyxXQUFXLENBQUMsVUFBVTt3QkFDbEMsTUFBTSxFQUFFLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTTtxQkFDaEM7aUJBQ0Y7Z0JBQ0QsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUNQLEdBQUcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUN2RSw4RUFBOEU7WUFDOUUsaUNBQWlDO1lBQ2pDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztTQUMxQixDQUFDO1FBQ0YsTUFBTSxJQUFJLENBQUMsSUFBSSxDQUFDLG9CQUFvQixDQUFDLEtBQUssQ0FBQyxFQUFFLGVBQWUsQ0FBQyxLQUFLLENBQUMsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUMvRixJQUFJLENBQUMsc0JBQXNCLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdEMsQ0FBQztJQUVEOzs7Ozs7Ozs7O09BVUc7SUFDSyxzQkFBc0IsQ0FBQyxHQUFXO1FBQ3hDLE1BQU0sRUFBRSxHQUFHLFFBQVEsQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFzQyxDQUFDO1FBQzdFLElBQUksQ0FBQyxFQUFFLEVBQUUsRUFBRTtZQUFFLE9BQU87UUFDcEIsRUFBRSxDQUFDLGtCQUFrQixFQUFFLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUMzQyxFQUFFLENBQUMsa0JBQWtCLEVBQUUsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQzNDLEVBQUUsQ0FBQyxFQUFFLENBQUMsaUJBQWlCLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRTtZQUM5QixpRkFBaUY7WUFDakYsd0VBQXdFO1lBQ3hFLE1BQU0sTUFBTSxHQUFJLEVBQWtELEVBQUUsTUFBTSxJQUFJLEVBQUUsQ0FBQztZQUNqRixNQUFNLE9BQU8sR0FBYSxFQUFFLENBQUM7WUFDN0IsS0FBSyxNQUFNLENBQUMsSUFBSSxNQUFNLEVBQUUsQ0FBQztnQkFDdkIsc0ZBQXNGO2dCQUN0Riw2RUFBNkU7Z0JBQzdFLGdEQUFnRDtnQkFDaEQsTUFBTSxFQUFFLEdBQUksQ0FBOEIsQ0FBQyxVQUFVLENBQUM7Z0JBQ3RELElBQUksT0FBTyxFQUFFLEtBQUssUUFBUTtvQkFBRSxPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQy9DLENBQUM7WUFDRCxJQUFJLE9BQU8sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQ3pCLElBQUksQ0FBQyxRQUFRLEVBQUUsY0FBYyxFQUFFLENBQUM7Z0JBQ2hDLE9BQU87WUFDVCxDQUFDO1lBQ0QsSUFBSSxDQUFDLFFBQVEsRUFBRSxhQUFhLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDeEMsQ0FBQyxDQUFDLENBQUM7UUFDSCw0REFBNEQ7UUFDNUQsRUFBRSxDQUFDLEVBQUUsQ0FBQyxpQkFBaUIsRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLGNBQWMsRUFBRSxDQUFDLENBQUM7SUFDbEUsQ0FBQztJQUVEOzs7Ozs7O09BT0c7SUFDSyx3QkFBd0IsQ0FBQyxHQUFXO1FBQzFDLE1BQU0sRUFBRSxHQUFHLFFBQVEsQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFzQyxDQUFDO1FBQzdFLElBQUksQ0FBQyxFQUFFLEVBQUUsa0JBQWtCO1lBQUUsT0FBTztRQUNwQyxFQUFFLENBQUMsa0JBQWtCLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUN6QyxFQUFFLENBQUMsa0JBQWtCLENBQUMsaUJBQWlCLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBRUQ7Ozs7Ozs7T0FPRztJQUNLLFFBQVEsQ0FDZCxFQUFlO1FBRWYsTUFBTSxJQUFJLEdBQUksRUFNWixDQUFDLFdBQVcsQ0FBQztRQUNmLElBQUksQ0FBQyxJQUFJO1lBQUUsT0FBTyxJQUFJLENBQUMsQ0FBQyx5QkFBeUI7UUFDakQsSUFBSSxJQUFJLENBQUMsS0FBSyxFQUFFLE1BQU07WUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDN0QscUZBQXFGO1FBQ3JGLHNFQUFzRTtRQUN0RSxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsS0FBSyxFQUFFLFNBQVMsS0FBSyxLQUFLLElBQUksSUFBSSxDQUFDLEtBQUssRUFBRSxTQUFTLEtBQUssS0FBSyxDQUFDO1FBQ2xGLElBQUksTUFBTSxJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUUsS0FBSyxJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUUsS0FBSyxFQUFFLENBQUM7WUFDckQsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFLENBQUMsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssRUFBRSxDQUFDLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDO1FBQ2xFLENBQUM7UUFDRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0gsSUFBSSxjQUFjO1FBQ2hCLE9BQU8sSUFBSSxDQUFDLFdBQVcsS0FBSyxJQUFJLENBQUM7SUFDbkMsQ0FBQztJQUVELDRFQUE0RTtJQUM1RSxJQUFJLFVBQVU7UUFDWixPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUM7SUFDMUQsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0gsSUFBSSxhQUFhO1FBQ2YsSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLFdBQVcsRUFBRSxDQUFDO1lBQzlCLE9BQU8sSUFBSSxDQUFDLFNBQVMsRUFBRSxLQUFLLElBQUksSUFBSSxDQUFDLFNBQVMsRUFBRSxJQUFJLElBQUksV0FBVyxDQUFDO1FBQ3RFLENBQUM7UUFDRCxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLEtBQUssS0FBSyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxJQUFJLE9BQU8sQ0FBQztRQUNwRixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEdBQUcsS0FBSyxNQUFNLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDO0lBQzdELENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxjQUFjO1FBQ1osTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQztRQUNoQyxJQUFJLENBQUMsUUFBUSxHQUFHLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQztRQUMvQixJQUFJLENBQUM7WUFDSCxNQUFNLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ3hCLENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCw2QkFBNkI7UUFDL0IsQ0FBQztRQUNELGtGQUFrRjtRQUNsRixxRkFBcUY7UUFDckYsc0ZBQXNGO1FBQ3RGLG1EQUFtRDtRQUNuRCxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVE7WUFBRSxVQUFVLENBQUMsR0FBRyxFQUFFLENBQUMsS0FBSyxJQUFJLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDOUQsQ0FBQztJQUVELHdGQUF3RjtJQUN4RixxQkFBcUI7UUFDbkIsS0FBSyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7SUFDckIsQ0FBQztJQUVELDZEQUE2RDtJQUM3RCxtQkFBbUI7UUFDakIsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDL0IsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNLLGtCQUFrQjtRQUN4QixJQUFJLENBQUMsV0FBVyxHQUFHLGNBQWMsQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLElBQUksQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLFlBQVksQ0FBQzthQUNqRixHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDMUMsQ0FBQztJQUVELHNGQUFzRjtJQUN0RixZQUFZLENBQUMsS0FBYTtRQUN4QixJQUFJLENBQUMsU0FBUyxHQUFHLEtBQUssSUFBSSxFQUFFLENBQUM7UUFDN0IsSUFBSSxDQUFDLGtCQUFrQixFQUFFLENBQUM7SUFDNUIsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0gsSUFBSSxzQkFBc0I7UUFDeEIsTUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixDQUFDO1FBQ2hDLE1BQU0sRUFBRSxPQUFPLEVBQUUsWUFBWSxFQUFFLEdBQUcsc0JBQXNCLENBQUMsZUFBZSxDQUFDO1FBQ3pFLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLEdBQUcsQ0FBQyxDQUFDLEdBQUcsWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNwRSxDQUFDO0lBRUQscURBQXFEO0lBQ3JELElBQUksb0JBQW9CO1FBQ3RCLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxzQkFBc0IsQ0FBQztRQUN6QyxJQUFJLElBQUksR0FBRyxFQUFFO1lBQUUsT0FBTyxJQUFJLElBQUksR0FBRyxDQUFDO1FBQ2xDLE9BQU8sSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDO0lBQ3pDLENBQUM7SUFFRCxzRUFBc0U7SUFDdEUsSUFBSSxnQkFBZ0I7UUFDbEIsTUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixDQUFDO1FBQ2hDLE9BQU8saUNBQWlDLENBQUMsQ0FBQyxjQUFjLEVBQUUsd0JBQXdCO2NBQzlFLHVDQUF1QyxJQUFJLENBQUMsb0JBQW9CLG1CQUFtQjtjQUNuRixtREFBbUQsQ0FBQztJQUMxRCxDQUFDO0lBRUQsZ0ZBQWdGO0lBQ2hGLElBQUksWUFBWTtRQUNkLE9BQU8sQ0FBQyxDQUFDLElBQUksQ0FBQyxTQUFTLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3RFLENBQUM7SUFFRCw0REFBNEQ7SUFDNUQsSUFBSSxVQUFVO1FBQ1osT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDLFNBQVMsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3BFLENBQUM7SUFFRCxJQUFJLFdBQVc7UUFDYixPQUFPLENBQUMsQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLE9BQU8sQ0FBQztJQUNwQyxDQUFDO0lBRUQ7Ozs7Ozs7T0FPRztJQUNILEtBQUssQ0FBQyxnQkFBZ0I7UUFDcEIsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQztRQUM1QixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDO1FBQy9CLElBQUksQ0FBQyxJQUFJLElBQUksQ0FBQyxRQUFRLEVBQUUsWUFBWSxJQUFJLElBQUksQ0FBQyxXQUFXO1lBQUUsT0FBTztRQUNqRSxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQztRQUN6QixJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksQ0FBQztRQUMzQixJQUFJLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQztRQUM1QixJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksQ0FBQztRQUUzQixrRkFBa0Y7UUFDbEYsc0ZBQXNGO1FBQ3RGLHNGQUFzRjtRQUN0Riw2Q0FBNkM7UUFDN0MsTUFBTSxJQUFJLEdBQUcsRUFBRSxJQUFJLENBQUMsWUFBWSxDQUFDO1FBQ2pDLE1BQU0sVUFBVSxHQUFHLEdBQUcsRUFBRSxDQUFDLElBQUksS0FBSyxJQUFJLENBQUMsWUFBWSxDQUFDO1FBQ3BELElBQUksR0FBRyxHQUErQixJQUFJLENBQUM7UUFFM0MsSUFBSSxDQUFDO1lBQ0gsa0ZBQWtGO1lBQ2xGLGtGQUFrRjtZQUNsRixNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDbEQsSUFBSSxVQUFVLEVBQUU7Z0JBQUUsT0FBTztZQUN6QixJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7Z0JBQ1osSUFBSSxDQUFDLFlBQVksR0FBRyxzQ0FBc0MsQ0FBQztnQkFDM0QsT0FBTztZQUNULENBQUM7WUFDRCxHQUFHLEdBQUcsSUFBSSxtQkFBbUIsRUFBRSxDQUFDO1lBQ2hDLElBQUksQ0FBQyxVQUFVLEdBQUcsR0FBRyxDQUFDO1lBQ3RCLE1BQU0sTUFBTSxHQUFHLE1BQU0sR0FBRyxDQUFDLEdBQUcsQ0FDMUI7Z0JBQ0UsTUFBTSxFQUFFLE1BQU0sQ0FBQyxNQUFNO2dCQUNyQixJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUk7Z0JBQ2pCLEtBQUssRUFBRSxNQUFNLENBQUMsS0FBSztnQkFDbkIsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJO2FBQ2hCLEVBQ0Q7Z0JBQ0UsR0FBRyxJQUFJO2dCQUNQLEtBQUssRUFBRSxDQUFDLElBQUksQ0FBQyxLQUFLLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQztxQkFDN0IsT0FBTyxDQUFDLHNCQUFzQixDQUFDLGNBQWMsRUFBRSxFQUFFLENBQUM7YUFDdEQsRUFDRCxDQUFDLFFBQXlCLEVBQUUsRUFBRTtnQkFDNUIsOEVBQThFO2dCQUM5RSxJQUFJLFVBQVUsRUFBRTtvQkFBRSxPQUFPO2dCQUN6QixJQUFJLENBQUMsZUFBZSxHQUFHLFFBQVEsQ0FBQyxRQUFRLENBQUM7Z0JBQ3pDLElBQUksQ0FBQyxjQUFjLEdBQUcsUUFBUSxDQUFDLE9BQU8sSUFBSSxJQUFJLENBQUMsY0FBYyxDQUFDO2dCQUM5RCxJQUFJLFFBQVEsQ0FBQyxPQUFPO29CQUFFLElBQUksQ0FBQyxjQUFjLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQztZQUMvRCxDQUFDLENBQ0YsQ0FBQztZQUNGLGlGQUFpRjtZQUNqRiw2RUFBNkU7WUFDN0UsaUNBQWlDO1lBQ2pDLElBQUksTUFBTSxJQUFJLENBQUMsVUFBVSxFQUFFLEVBQUUsQ0FBQztnQkFDNUIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxNQUFNLENBQUMsQ0FBQztnQkFDckMsSUFBSSxDQUFDLGVBQWUsR0FBRyxNQUFNLENBQUM7Z0JBQzlCLE1BQU0sSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ3RCLENBQUM7UUFDSCxDQUFDO1FBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztZQUNiLElBQUksQ0FBQyxVQUFVLEVBQUU7Z0JBQUUsSUFBSSxDQUFDLFlBQVksR0FBSSxHQUFhLEVBQUUsT0FBTyxJQUFJLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNoRixDQUFDO2dCQUFTLENBQUM7WUFDVCxHQUFHLEVBQUUsU0FBUyxFQUFFLENBQUM7WUFDakIsaUZBQWlGO1lBQ2pGLGdGQUFnRjtZQUNoRiw2RUFBNkU7WUFDN0UsSUFBSSxJQUFJLENBQUMsVUFBVSxLQUFLLEdBQUcsRUFBRSxDQUFDO2dCQUM1QixJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQztnQkFDdkIsSUFBSSxDQUFDLGVBQWUsR0FBRyxJQUFJLENBQUM7WUFDOUIsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBRUQsa0VBQWtFO0lBQ2xFLGFBQWE7UUFDWCxJQUFJLENBQUMsVUFBVSxFQUFFLE1BQU0sRUFBRSxDQUFDO0lBQzVCLENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0ssY0FBYztRQUNwQixJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDcEIsSUFBSSxDQUFDLFVBQVUsRUFBRSxTQUFTLEVBQUUsQ0FBQztRQUM3QixJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQztRQUN2QixJQUFJLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQztRQUM1QixJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksQ0FBQztRQUMzQixJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksQ0FBQztJQUM3QixDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSyxLQUFLLENBQUMsYUFBYSxDQUN6QixRQUEwQjtRQUUxQixNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsVUFBVTthQUMvQixNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEtBQUssSUFBSSxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQzthQUM3RSxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNuQyxLQUFLLE1BQU0sU0FBUyxJQUFJLFVBQVUsRUFBRSxDQUFDO1lBQ25DLElBQUksQ0FBQztnQkFDSCxNQUFNLEdBQUcsR0FBRyxNQUFNLFFBQVEsQ0FBQyxZQUFhLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUN6RCxNQUFNLE1BQU0sR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQ3pELE1BQU0sSUFBSSxHQUFHLEdBQUcsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDO2dCQUMxQixNQUFNLEtBQUssR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDO2dCQUM1QixNQUFNLE1BQU0sR0FBRyxJQUFJLFlBQVksQ0FBQyxJQUFJLEdBQUcsS0FBSyxDQUFDLENBQUM7Z0JBQzlDLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxJQUFJLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztvQkFDOUIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEtBQUssRUFBRSxDQUFDLEVBQUU7d0JBQUUsTUFBTSxDQUFDLENBQUMsR0FBRyxLQUFLLEdBQUcsQ0FBQyxDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUN2RSxDQUFDO2dCQUNELE9BQU8sRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxDQUFDO1lBQ2pDLENBQUM7WUFBQyxNQUFNLENBQUM7Z0JBQ1AscUVBQXFFO1lBQ3ZFLENBQUM7UUFDSCxDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQseUVBQXlFO0lBQ3pFLFdBQVcsQ0FBQyxJQUFZO1FBQ3RCLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxLQUFLLElBQUksQ0FBQyxDQUFDO1FBQzFELElBQUksQ0FBQyxJQUFJLElBQUksSUFBSSxDQUFDLElBQUksS0FBSyxJQUFJLENBQUMsU0FBUyxFQUFFLElBQUk7WUFBRSxPQUFPO1FBQ3hELElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDO1FBQ3RCLElBQUksQ0FBQyxlQUFlLEdBQUcsSUFBSSxDQUFDO1FBQzVCLEtBQUssSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO0lBQ3JCLENBQUM7SUFFRDs7Ozs7Ozs7O09BU0c7SUFDSCxJQUFJLFFBQVE7UUFDVixRQUFRLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNsQixLQUFLLFFBQVE7Z0JBQ1gsT0FBTyw0RUFBNEU7c0JBQy9FLGdGQUFnRjtzQkFDaEYsMkVBQTJFO3NCQUMzRSwyQkFBMkIsQ0FBQztZQUNsQyxLQUFLLFdBQVc7Z0JBQ2QsT0FBTyxrRUFBa0U7c0JBQ3JFLG9FQUFvRTtzQkFDcEUsMkVBQTJFO3NCQUMzRSw2QkFBNkIsQ0FBQztZQUNwQyxLQUFLLFFBQVE7Z0JBQ1gsT0FBTywyRUFBMkU7c0JBQzlFLDZFQUE2RTtzQkFDN0UscUVBQXFFLENBQUM7WUFDNUUsS0FBSyxLQUFLO2dCQUNSLE9BQU8sMkVBQTJFO3NCQUM5RSw2RUFBNkU7c0JBQzdFLDJEQUEyRCxDQUFDO1lBQ2xFLEtBQUssU0FBUztnQkFDWixPQUFPLDBFQUEwRTtzQkFDN0Usd0VBQXdFO3NCQUN4RSwyRUFBMkU7c0JBQzNFLDZFQUE2RTtzQkFDN0UsMEVBQTBFO3NCQUMxRSxtQ0FBbUMsQ0FBQztZQUMxQyxLQUFLLFdBQVc7Z0JBQ2QsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDO1lBQzVCO2dCQUNFLE9BQU8sRUFBRSxDQUFDO1FBQ2QsQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxJQUFZLGFBQWE7UUFDdkIsTUFBTSxNQUFNLEdBQUcscUVBQXFFO2NBQ2hGLDJFQUEyRSxDQUFDO1FBQ2hGLE1BQU0sSUFBSSxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxLQUFLLElBQUksSUFBSSxDQUFDLFNBQVMsRUFBRSxJQUFJLElBQUksRUFBRSxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDakYsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDekIsT0FBTywwRUFBMEU7a0JBQzdFLHdFQUF3RTtrQkFDeEUsaUZBQWlGO2tCQUNqRixnRkFBZ0Y7a0JBQ2hGLG1EQUFtRCxHQUFHLE1BQU0sQ0FBQztRQUNuRSxDQUFDO1FBQ0QsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztZQUNwRCxPQUFPLDBFQUEwRTtrQkFDN0UsdUVBQXVFO2tCQUN2RSxzRUFBc0U7a0JBQ3RFLGlGQUFpRjtrQkFDakYsbURBQW1ELEdBQUcsTUFBTSxDQUFDO1FBQ25FLENBQUM7UUFDRCxPQUFPLHlFQUF5RTtjQUM1RSxtRkFBbUY7Y0FDbkYsa0ZBQWtGO2NBQ2xGLDRFQUE0RTtjQUM1RSx3Q0FBd0MsR0FBRyxNQUFNLENBQUM7SUFDeEQsQ0FBQztJQUVELHdEQUF3RDtJQUN4RCxJQUFJLGFBQWE7UUFDZixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsZUFBZSxFQUFFLElBQUksSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDO1FBQzFELElBQUksQ0FBQyxJQUFJO1lBQUUsT0FBTyxzQ0FBc0MsQ0FBQztRQUN6RCxzRkFBc0Y7UUFDdEYsb0ZBQW9GO1FBQ3BGLGlFQUFpRTtRQUNqRSxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsT0FBTztZQUMxQixDQUFDLENBQUMsaUJBQWlCLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEtBQUssSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLElBQUk7a0JBQ3pELGlDQUFpQztZQUNyQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ1AsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLFdBQVc7WUFDL0IsQ0FBQyxDQUFDLDZCQUE2QjtZQUMvQixDQUFDLENBQUMsaUVBQWlFLENBQUM7UUFDdEUsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLEdBQUcsQ0FBQztZQUNsQyxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxjQUFjLEVBQUUsNEJBQTRCO1lBQ3ZFLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDUCxPQUFPLEdBQUcsSUFBSSxDQUFDLEtBQUssSUFBSSxJQUFJLENBQUMsSUFBSSxJQUFJLFFBQVEsR0FBRyxHQUFHLEdBQUcsT0FBTyxFQUFFLENBQUM7SUFDbEUsQ0FBQztJQUVPLFNBQVM7UUFDZixJQUFJLENBQUM7WUFDSCwyRUFBMkU7WUFDM0UsK0RBQStEO1lBQy9ELE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ2hDLENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCxtQkFBbUI7UUFDckIsQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSyxLQUFLLENBQUMsSUFBSSxDQUNoQixNQUFlLEVBQUUsTUFBZSxFQUFFLFNBQWtCLFlBQVksRUFBRSxHQUFHLEdBQUcsSUFBSSxDQUFDLFVBQVU7UUFFdkYsTUFBTSxNQUFNLEdBQUksTUFBc0MsRUFBRSxNQUFNLENBQUM7UUFDL0QsSUFBSSxDQUFDLFdBQVcsR0FBRyxPQUFPLE1BQU0sS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO1FBQzlELE1BQU0sTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsTUFBZSxFQUFFLE1BQWUsRUFBRSxNQUFlLENBQUMsQ0FBQztJQUM3RSxDQUFDO0lBRU8sS0FBSyxDQUFDLE1BQU07UUFDbEIsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRO1lBQUUsT0FBTztRQUMzQixzRkFBc0Y7UUFDdEYsa0ZBQWtGO1FBQ2xGLHdEQUF3RDtRQUN4RCxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDO1FBQy9CLElBQUksQ0FBQyxRQUFRLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQztZQUFFLE9BQU87UUFDN0Msc0ZBQXNGO1FBQ3RGLElBQUksSUFBSSxDQUFDLElBQUksS0FBSyxXQUFXO1lBQUUsSUFBSSxDQUFDLHdCQUF3QixDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3JFLHdFQUF3RTtRQUN4RSw0RUFBNEU7UUFDNUUsMkRBQTJEO1FBQzNELElBQUksSUFBSSxDQUFDLElBQUksS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUM1QixNQUFNLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztZQUMzQixPQUFPO1FBQ1QsQ0FBQztRQUNELG1GQUFtRjtRQUNuRiw4REFBOEQ7UUFDOUQsSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLFdBQVcsRUFBRSxDQUFDO1lBQzlCLE1BQU0sSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1lBQzdCLE9BQU87UUFDVCxDQUFDO1FBQ0QsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQztZQUN6RCxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDakIsT0FBTztRQUNULENBQUM7UUFDRCxJQUFJLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUNyQixNQUFNLE1BQU0sR0FBRztnQkFDYixLQUFLLEVBQUUsSUFBSSxDQUFDLFdBQVc7Z0JBQ3ZCLFNBQVMsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJO2dCQUNoRSxJQUFJLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJO2FBQ3hCLENBQUM7WUFDRixNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsTUFBTSxDQUFDLEVBQUUsWUFBWSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7WUFDaEUsT0FBTztRQUNULENBQUM7UUFDRCxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU07WUFBRSxPQUFPO1FBQ3pCLE1BQU0sS0FBSyxHQUFHO1lBQ1osTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFNO1lBQ25CLElBQUksRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUk7WUFDN0UsS0FBSyxFQUFFLG9CQUFvQixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsSUFBSTtZQUM3RCxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSTtZQUNoRSxHQUFHLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRO1NBQ3hCLENBQUM7UUFDRixNQUFNLE1BQU0sR0FBRyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ2xELE1BQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsV0FBVyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUN6RCxDQUFDO0lBRUQ7Ozs7Ozs7T0FPRztJQUNLLEtBQUssQ0FBQyxhQUFhO1FBQ3pCLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUM7UUFDL0IsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLFlBQVk7YUFDNUIsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7YUFDM0QsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUErQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMxRSxJQUFJLENBQUMsUUFBUSxJQUFJLEtBQUssQ0FBQyxNQUFNLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ3RELElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUNqQixPQUFPO1FBQ1QsQ0FBQztRQUVELE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztRQUN0RixNQUFNLE9BQU8sR0FBRyxDQUFDLENBQUMsUUFBUSxJQUFJLFFBQVEsQ0FBQyxNQUFNLElBQUksc0JBQXNCLENBQUMsb0JBQW9CLENBQUM7UUFDN0YsTUFBTSxLQUFLLEdBQUcsT0FBTyxJQUFJLFFBQVE7WUFDL0IsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsTUFBTSxFQUNsRCxzQkFBc0IsQ0FBQyxvQkFBb0IsQ0FBQztZQUM5QyxDQUFDLENBQUMsSUFBSSxDQUFDO1FBRVQsTUFBTSxNQUFNLEdBQUcsYUFBYSxDQUMxQixLQUFLLEVBQ0wsS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssRUFBRSxVQUFVLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLEVBQUUsRUFDM0Y7WUFDRSxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxLQUFLLENBQUMsT0FBTyxFQUFFLFFBQVEsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQ3pELHlFQUF5RTtZQUN6RSxpREFBaUQ7WUFDakQsR0FBRyxDQUFDLENBQUMsS0FBSyxJQUFJLFFBQVEsQ0FBQyxDQUFDLENBQUMsRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUNwRCx3RUFBd0U7WUFDeEUsa0VBQWtFO1lBQ2xFLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxPQUFPLEVBQUUsc0JBQXNCLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztZQUN6RSxNQUFNLEVBQUUsSUFBSSxDQUFDLGFBQWE7U0FDM0IsQ0FDRixDQUFDO1FBQ0YsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ1osSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQ2pCLElBQUksQ0FBQyxNQUFNLEdBQUcsZ0RBQWdELENBQUM7WUFDL0QsT0FBTztRQUNULENBQUM7UUFDRCxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztRQUNuQixJQUFJLENBQUMsYUFBYSxHQUFHLE1BQU0sQ0FBQyxVQUFVLENBQUM7UUFDdkMsTUFBTSxLQUFLLEdBQUc7WUFDWixJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUk7WUFDakIsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJO1lBQ2pCLE1BQU0sRUFBRSxNQUFNLENBQUMsTUFBTTtZQUNyQixNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU07WUFDckIsT0FBTyxFQUFFLElBQUksQ0FBQyxhQUFhO1lBQzNCLFVBQVUsRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLElBQUksRUFBRSxDQUFDO1NBQzVELENBQUM7UUFDRixNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsS0FBSyxDQUFDLEVBQUUsYUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7SUFDbkUsQ0FBQzs4R0E5ckNVLHNCQUFzQixrQkEyT2IsVUFBVTtrR0EzT25CLHNCQUFzQixvRkM5Rm5DLHNyUkEwSkE7OzJGRDVEYSxzQkFBc0I7a0JBTGxDLFNBQVM7K0JBQ0UsZ0JBQWdCOzswQkErT2IsTUFBTTsyQkFBQyxVQUFVO3lDQXBPakIsTUFBTTtzQkFBbEIsS0FBSyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIEFmdGVyVmlld0luaXQsIENvbXBvbmVudCwgSW5qZWN0LCBJbnB1dCwgT25EZXN0cm95LCBPbkluaXQsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgU3Vic2NyaXB0aW9uLCBjb21iaW5lTGF0ZXN0IH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgKiBhcyBQbG90bHkgZnJvbSAncGxvdGx5LmpzLWRpc3QtbWluJztcblxuaW1wb3J0IHsgVklTVUFMSVpFUiwgSVZpc3VhbGl6ZXIsIElTcGF0aWFsQ29udHJvbHMgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvdmlzdWFsaXplci5jb250cmFjdCc7XG5pbXBvcnQge1xuICBTcGF0aWFsRW1iZWRkaW5nLFxuICBTcGF0aWFsRW1iZWRkaW5nTWV0YSxcbn0gZnJvbSAnLi4vLi4vY29udHJhY3RzL3NwYXRpYWwtZGF0YXNldC5jb250cmFjdCc7XG5pbXBvcnQgeyBTcGF0aWFsQ29sb3JCeSwgU3BhdGlhbFZpZXdTdGF0ZSwgREVGQVVMVF9TUEFUSUFMX1ZJRVcgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvZGlzcGxheS10eXBlcyc7XG5pbXBvcnQge1xuICBTcGF0aWFsU2VsZWN0aW9uTWFzaywgZW1wdHlTZWxlY3Rpb24sIG1hc2tUb0luZGljZXMsXG59IGZyb20gJy4uLy4uL3NwYXRpYWwvc3BhdGlhbC1zZWxlY3Rpb24nO1xuaW1wb3J0IHsgY2VsbHNBc0dyb3VwcywgaGVhdG1hcE1hdHJpeCB9IGZyb20gJy4uLy4uL3NwYXRpYWwvc3BhdGlhbC1oZWF0bWFwJztcbmltcG9ydCB7IGdlbmVPcHRpb25zRm9yIH0gZnJvbSAnLi4vLi4vc3BhdGlhbC9nZW5lLXNlYXJjaCc7XG5pbXBvcnQgeyBDb21wdXRlUHJvZ3Jlc3MsIEVtYmVkZGluZ0NvbXB1dGVSdW4gfSBmcm9tICcuLi8uLi9zcGF0aWFsL2VtYmVkZGluZy1jb21wdXRlJztcbmltcG9ydCB7XG4gIE9taWNzQ2hhcnRLaW5kLCBPbWljc0dyb3VwaW5nLCBiZW5lZml0c0Zyb21Hcm91cGluZywgYnVpbGRDb3VudFRyYWNlcywgYnVpbGRIZWF0bWFwVHJhY2VzLFxuICBidWlsZE9taWNzVHJhY2VzLCBidWlsZEVtYmVkZGluZ1RyYWNlcywgY291bnRzTGF5b3V0LCBoZWF0bWFwTGF5b3V0LCBvbWljc0xheW91dCwgZW1iZWRkaW5nTGF5b3V0LFxufSBmcm9tICcuLi8uLi9pbXBsZW1lbnRhdGlvbnMvcGxvdGx5L29taWNzLXRyYWNlLWJ1aWxkZXJzJztcblxuLyoqXG4gKiBXaWRlbiB0aGUgaGVscCB0b29sdGlwLCBvbmNlIHBlciBkb2N1bWVudC5cbiAqXG4gKiBQcmltZU5HIGNhcHMgYC5wLXRvb2x0aXBgIGF0IDEyLjVyZW0gYW5kIGFwcGVuZHMgaXQgdG8gYDxib2R5PmAsIHNvIGEgY29tcG9uZW50XG4gKiBzdHlsZXNoZWV0IGNhbm5vdCByZWFjaCBpdCDigJQgdGhlIHNhbWUgcmVhc29uIHRoZSBkZXRhY2hlZCBkaWFsb2cgaXMgc2l6ZWQgaW5saW5lLiBBdFxuICogMjAwIHB4IHRoZSBjaGFydCBleHBsYW5hdGlvbnMgcmVuZGVyIGFzIGEgdGFsbCB0aHJlYWQgb2YgdHdvLXdvcmQgbGluZXMsIHdoaWNoIGlzIHdvcnNlXG4gKiB0aGFuIG5vdCBzaG93aW5nIHRoZW0uIFRoZSBsaWJyYXJ5IHNoaXBzIG5vIGdsb2JhbCBzdHlsZXNoZWV0IHRvIHB1dCB0aGlzIGluLCBzbyBvbmVcbiAqIHJ1bGUgaXMgaW5qZWN0ZWQgaW5zdGVhZDogc2NvcGVkIHRvIHRoaXMgY29tcG9uZW50J3Mgb3duIHRvb2x0aXAgY2xhc3MsIGlkZW1wb3RlbnQsIGFuZFxuICogc2tpcHBlZCB3aGVyZSB0aGVyZSBpcyBubyBkb2N1bWVudC5cbiAqL1xuY29uc3QgSEVMUF9USVBfU1RZTEVfSUQgPSAnc3gtaGVscC10aXAtc3R5bGUnO1xuZnVuY3Rpb24gZW5zdXJlSGVscFRpcFN0eWxlKCk6IHZvaWQge1xuICBpZiAodHlwZW9mIGRvY3VtZW50ID09PSAndW5kZWZpbmVkJykgcmV0dXJuO1xuICBpZiAoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoSEVMUF9USVBfU1RZTEVfSUQpKSByZXR1cm47XG4gIGNvbnN0IHN0eWxlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3R5bGUnKTtcbiAgc3R5bGUuaWQgPSBIRUxQX1RJUF9TVFlMRV9JRDtcbiAgc3R5bGUudGV4dENvbnRlbnQgPSAnLnN4LWhlbHAtdGlwIC5wLXRvb2x0aXAtdGV4dCB7IG1heC13aWR0aDogbm9uZTsgd2lkdGg6IDI0cmVtOyAnXG4gICAgKyAnbGluZS1oZWlnaHQ6IDEuNDU7IH1cXG4uc3gtaGVscC10aXAgeyBtYXgtd2lkdGg6IG5vbmU7IH0nO1xuICBkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKHN0eWxlKTtcbn1cblxuLyoqIFBlci1pbnN0YW5jZSBjaGFydC1kaXYgaWQgc291cmNlIOKAlCBzZWUge0BsaW5rIFNwYXRpYWxDaGFydHNDb21wb25lbnQuY2hhcnREaXZ9LiAqL1xubGV0IGNoYXJ0SW5zdGFuY2VTZXEgPSAwO1xuXG5cbi8qKiBQbG90bHkgY29uZmlnOiBhIHN0YXRpYy1pc2ggYW5hbHlzaXMgY2hhcnQsIG5vdCBhbiBlZGl0YWJsZSBmaWd1cmUuICovXG5jb25zdCBDSEFSVF9DT05GSUcgPSB7XG4gIGRpc3BsYXlsb2dvOiBmYWxzZSxcbiAgcmVzcG9uc2l2ZTogdHJ1ZSxcbiAgbW9kZUJhckJ1dHRvbnNUb1JlbW92ZTogWydsYXNzbzJkJywgJ3NlbGVjdDJkJywgJ2F1dG9TY2FsZTJkJ10sXG59O1xuXG4vKipcbiAqIENvbmZpZyBmb3IgdGhlIGVtYmVkZGluZywgd2hpY2ggdW5saWtlIHRoZSBkaXN0cmlidXRpb25zIGlzIGEgcGxvdCB5b3UgU0VMRUNUIGluLlxuICpcbiAqIFRoZSBsYXNzbyBhbmQgYm94LXNlbGVjdCBhcmUga2VwdCDigJQgZHJhd2luZyByb3VuZCBhIGNsdXN0ZXIgaXMgaG93IGEgcG9wdWxhdGlvbiBnZXRzXG4gKiBwaWNrZWQgb3V0IG9mIGEgVU1BUCDigJQgYW5kIHRoZSB3aGVlbCB6b29tcywgYmVjYXVzZSBleHBsb3JpbmcgYW4gZW1iZWRkaW5nIG1lYW5zXG4gKiB6b29taW5nIGludG8gYSBjbHVzdGVyIGFuZCByZWFjaGluZyBmb3IgYSB0b29sYmFyIGJ1dHRvbiB0byBkbyBpdCBicmVha3MgdGhhdC5cbiAqL1xuY29uc3QgRU1CRURESU5HX0NPTkZJRyA9IHtcbiAgZGlzcGxheWxvZ286IGZhbHNlLFxuICByZXNwb25zaXZlOiB0cnVlLFxuICBzY3JvbGxab29tOiB0cnVlLFxuICBtb2RlQmFyQnV0dG9uc1RvUmVtb3ZlOiBbJ2F1dG9TY2FsZTJkJ10sXG59O1xuXG4vKipcbiAqIERpc3RyaWJ1dGlvbiBjaGFydHMgb3ZlciB0aGUgc3BhdGlhbC1vbWljcyB2YWx1ZXMg4oCUIGhpc3RvZ3JhbSwgdmlvbGluLCBib3gg4oCUXG4gKiAqKmxpbmtlZCB0byB0aGUgbWFwKio6IHRoZXkgY2hhcnQgd2hhdGV2ZXIgdGhlIG1hcCBpcyBjb2xvdXJlZCBieSwgYW5kIG5hcnJvd1xuICogdG8gdGhlIGN1cnJlbnQgc2VsZWN0aW9uLlxuICpcbiAqIEVtYmVkZGVkIElOU0lERSBgPHNwYXRpYWwtY29udHJvbHM+YCByYXRoZXIgdGhhbiBvd25pbmcgYSBkaWFsb2cgb2YgaXRzIG93bjpcbiAqIHRoZSB0d28gYXJlIG9uZSB3b3JrZmxvdyAoY2hhbmdlIHRoZSBjb2xvdXIgc291cmNlLCB3YXRjaCB0aGUgZGlzdHJpYnV0aW9uXG4gKiBtb3ZlKSwgYW5kIHNwbGl0dGluZyB0aGVtIGFjcm9zcyB0d28gZmxvYXRpbmcgd2luZG93cyBtYWRlIHRoZSBsaW5rIGhhcmRlciB0b1xuICogc2VlLCBub3QgZWFzaWVyLiBJdCBzdGF5cyBhIHNlcGFyYXRlIGNvbXBvbmVudCBzbyB0aGUgcHVyZSB0cmFjZSBidWlsZGVycyBhbmRcbiAqIGl0cyBvd24gdGVzdHMga2VlcCB0aGVpciBib3VuZGFyeS5cbiAqXG4gKiBDaGFydGluZyB0aGUgYWN0aXZlIGNvbG91ciBzb3VyY2UgcmF0aGVyIHRoYW4gb2ZmZXJpbmcgaXRzIG93biB2YWx1ZSBwaWNrZXIgaXNcbiAqIGRlbGliZXJhdGU6IGl0IGtlZXBzIG9uZSBzb3VyY2Ugb2YgdHJ1dGgsIHNvIHdoYXQgeW91IHNlZSBvbiB0aGUgbWFwIGFuZCB3aGF0XG4gKiB5b3Ugc2VlIGluIHRoZSBjaGFydCBjYW5ub3QgZGlzYWdyZWUsIGFuZCBpdCBtYWtlcyB0aGUgbGluayBsZWdpYmxlIHdpdGhvdXRcbiAqIGV4cGxhaW5pbmcgaXQuXG4gKlxuICogRGVwZW5kcyBvbmx5IG9uIHtAbGluayBJU3BhdGlhbENvbnRyb2xzfSB0aHJvdWdoIHRoZSBgVklTVUFMSVpFUmAgY29udHJhY3RcbiAqIHRva2VuLCBsaWtlIGBTcGF0aWFsQ29udHJvbHNDb21wb25lbnRgLiBUcmFjZSBidWlsZGluZyBsaXZlcyBpbiB0aGUgcHVyZVxuICogYG9taWNzLXRyYWNlLWJ1aWxkZXJzYDsgdGhpcyBjb21wb25lbnQgb25seSBtb3ZlcyBkYXRhIGFuZCBvd25zIHRoZSBkaXYuXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ3NwYXRpYWwtY2hhcnRzJyxcbiAgdGVtcGxhdGVVcmw6ICcuL3NwYXRpYWwtY2hhcnRzLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vc3BhdGlhbC1jaGFydHMuY29tcG9uZW50LnNjc3MnXSxcbn0pXG5leHBvcnQgY2xhc3MgU3BhdGlhbENoYXJ0c0NvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgQWZ0ZXJWaWV3SW5pdCwgT25EZXN0cm95IHtcbiAgLyoqXG4gICAqIFdoZXRoZXIgdGhlIGhvc3QgcGFuZWwgaXMgb24gc2NyZWVuLiBUaGUgY2hhcnQgb25seSBkcmF3cyB3aGVuIGl0IGlzOiB0aGVcbiAgICogZW5jbG9zaW5nIGRpYWxvZyBjcmVhdGVzIGFuZCBkZXN0cm95cyBpdHMgY29udGVudCwgc28gd2l0aG91dCB0aGlzIHRoZVxuICAgKiBjb21wb25lbnQgd291bGQgbW91bnQgd2l0aCBhIGRpdiBwcmVzZW50IGJ1dCBubyBzdGF0ZSBjaGFuZ2UgdG8gdHJpZ2dlciBhXG4gICAqIGZpcnN0IGRyYXcuXG4gICAqL1xuICBASW5wdXQoKSBzZXQgYWN0aXZlKG9uOiBib29sZWFuKSB7XG4gICAgY29uc3Qgd2FzID0gdGhpcy5pc0FjdGl2ZTtcbiAgICB0aGlzLmlzQWN0aXZlID0gb247XG4gICAgLy8gRGVmZXJyZWQgYnkgYSB0YXNrOiB3aGVuIGEgY29sbGFwc2VkIHNlY3Rpb24gZXhwYW5kcywgdGhlIGhvc3QgaXMgc3RpbGxcbiAgICAvLyBgaGlkZGVuYCBhdCB0aGUgbW9tZW50IHRoaXMgc2V0dGVyIHJ1bnMsIHNvIHBsb3R0aW5nIG5vdyB3b3VsZCBzaXplIHRoZVxuICAgIC8vIGNoYXJ0IHRvIGEgemVyby1oZWlnaHQgZGl2LlxuICAgIGlmIChvbiAmJiAhd2FzKSBzZXRUaW1lb3V0KCgpID0+IHZvaWQgdGhpcy5yZWxvYWQoKSwgMCk7XG4gICAgZWxzZSBpZiAob24pIHZvaWQgdGhpcy5yZWxvYWQoKTtcbiAgfVxuICBnZXQgYWN0aXZlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmlzQWN0aXZlO1xuICB9XG4gIHByaXZhdGUgaXNBY3RpdmUgPSB0cnVlO1xuXG4gIC8qKiBQZXItaW5zdGFuY2UsIGZvciB0aGUgc2FtZSByZWFzb24gdGhlIHZpc3VhbGl6ZXIncyBwbG90IGRpdiBpc1xuICAgKiAgKGB2aXN1YWxpemVyLmNvbXBvbmVudC50c2ApOiB0d28gbW91bnRlZCBjaGFydHMgc2hhcmluZyBvbmUgRE9NIGlkIG1lYW5zXG4gICAqICBgZ2V0RWxlbWVudEJ5SWRgIGhhbmRzIGJvdGggb2YgdGhlbSB0aGUgZmlyc3QgZWxlbWVudCwgc28gb25lIGluc3RhbmNlIGRyYXdzXG4gICAqICBpbnRvIOKAlCBvciBwdXJnZXMg4oCUIHRoZSBvdGhlcidzIGNhbnZhcy4gKi9cbiAgLyoqIE9uZSBpbmNyZW1lbnQgcGVyIElOU1RBTkNFIOKAlCBhIHN0YXRpYyBpbml0aWFsaXplciB3b3VsZCBydW4gb25jZSBwZXIgY2xhc3MgYW5kXG4gICAqICBoYW5kIGV2ZXJ5IGluc3RhbmNlIHRoZSBzYW1lIGlkLCB3aGljaCBpcyB0aGUgYnVnIHRoaXMgaWQgZXhpc3RzIHRvIGF2b2lkLiBEZWNsYXJlZFxuICAgKiAgYmVmb3JlIHRoZSBpZHMgYmVjYXVzZSBmaWVsZCBpbml0aWFsaXplcnMgcnVuIGluIG9yZGVyLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IHNlcSA9ICsrY2hhcnRJbnN0YW5jZVNlcTtcbiAgcmVhZG9ubHkgY2hhcnREaXYgPSBgc3BhdGlhbC1jaGFydHMtcGxvdC0ke3RoaXMuc2VxfWA7XG4gIC8qKlxuICAgKiBUaGUgZGl2IGluc2lkZSB0aGUgZGV0YWNoZWQgd2luZG93LlxuICAgKlxuICAgKiBBIHNlcGFyYXRlIGlkIHJhdGhlciB0aGFuIG1vdmluZyB0aGUgZXhpc3RpbmcgZGl2OiBBbmd1bGFyIGRlc3Ryb3lzIGFuZCByZWNyZWF0ZXMgYVxuICAgKiBkaXYgYWNyb3NzIGFuIGAqbmdJZmAsIHNvIFBsb3RseSB3b3VsZCBiZSBsZWZ0IGhvbGRpbmcgYSBkZXRhY2hlZCBub2RlLiBUd28gZGl2cyBhbmRcbiAgICogYSByZWRyYXcgaXMgYm90aCBzaW1wbGVyIGFuZCBjb3JyZWN0LlxuICAgKlxuICAgKiBPTkUgaXMgZW5vdWdoIGZvciBldmVyeSBraW5kLCBiZWNhdXNlIG9ubHkgdGhlIGFjdGl2ZSBraW5kIGlzIGV2ZXIgcGxvdHRlZC4gRGV0YWNoaW5nXG4gICAqIGlzIHJlbWVtYmVyZWQgcGVyIGtpbmQsIGJ1dCBhdCBtb3N0IG9uZSB3aW5kb3cgaXMgb3BlbiBhdCBhIHRpbWUuXG4gICAqL1xuICByZWFkb25seSBkZXRhY2hlZERpdiA9IGBzcGF0aWFsLWNoYXJ0cy1kZXRhY2hlZC0ke3RoaXMuc2VxfWA7XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdGhlIGNoYXJ0IGlzIHNob3duIGluIGl0cyBvd24gd2luZG93IHJhdGhlciB0aGFuIGlubGluZS5cbiAgICpcbiAgICogRGV0YWNoZWQsIGl0IHNpdHMgQUxPTkdTSURFIHRoZSBzcGF0aWFsLW9taWNzIGRpYWxvZyBpbnN0ZWFkIG9mIGluc2lkZSBpdCDigJQgd2hpY2ggaXNcbiAgICogdGhlIHBvaW50OiB0aGVzZSBhcmUgcmVhZCBhZ2FpbnN0IHRoZSBtYXAsIG5vdCBpbnN0ZWFkIG9mIGl0LiBUaGUgZGlhbG9nIGlzIG9ubHkgc29cbiAgICogd2lkZS5cbiAgICpcbiAgICogT25lIGZsYWcgZm9yIHRoZSBQQU5FTCwgbm90IG9uZSBwZXIga2luZC4gV2hlcmUgdGhlIHdpbmRvdyBpcywgaXMgYSBwcm9wZXJ0eSBvZiB0aGVcbiAgICogd29ya3NwYWNlIHNvbWVvbmUgaGFzIGFycmFuZ2VkLCBub3Qgb2YgdGhlIHRhYiB0aGV5IGhhcHBlbiB0byBiZSBvbjogaGF2aW5nIHB1dCB0aGVcbiAgICogY2hhcnRzIGJlc2lkZSB0aGUgbWFwLCBzd2l0Y2hpbmcgZnJvbSB0aGUgaGVhdG1hcCB0byB0aGUgY291bnRzIHNob3VsZCBzd2FwIHdoYXQgdGhlXG4gICAqIHdpbmRvdyBzaG93cywgbm90IHlhbmsgaXQgYmFjayBpbnRvIHRoZSBkaWFsb2cgYW5kIG1ha2UgdGhlbSBkZXRhY2ggaXQgYWdhaW4uXG4gICAqL1xuICBkZXRhY2hlZCA9IGZhbHNlO1xuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBDT05USU5VT1VTX0tJTkRTOiB7IGxhYmVsOiBzdHJpbmc7IHZhbHVlOiBPbWljc0NoYXJ0S2luZCB9W10gPSBbXG4gICAgeyBsYWJlbDogJ0hpc3RvZ3JhbScsIHZhbHVlOiAnaGlzdG9ncmFtJyB9LFxuICAgIHsgbGFiZWw6ICdWaW9saW4nLCB2YWx1ZTogJ3Zpb2xpbicgfSxcbiAgICB7IGxhYmVsOiAnQm94JywgdmFsdWU6ICdib3gnIH0sXG4gIF07XG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IENBVEVHT1JJQ0FMX0tJTkRTOiB7IGxhYmVsOiBzdHJpbmc7IHZhbHVlOiBPbWljc0NoYXJ0S2luZCB9W10gPSBbXG4gICAgeyBsYWJlbDogJ0NvdW50cycsIHZhbHVlOiAnY291bnRzJyB9LFxuICBdO1xuXG4gIC8qKiBBdmFpbGFibGUgd2hhdGV2ZXIgdGhlIG1hcCBpcyBjb2xvdXJlZCBieTogdGhlIGhlYXRtYXAncyBzdWJqZWN0IGlzIGEgR0VORVxuICAgKiAgTElTVCBjcm9zc2VkIHdpdGggYSBncm91cGluZywgbm90IHRoZSBhY3RpdmUgY29sb3VyIHNvdXJjZS4gKi9cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgQUxXQVlTX0tJTkRTOiB7IGxhYmVsOiBzdHJpbmc7IHZhbHVlOiBPbWljc0NoYXJ0S2luZCB9W10gPSBbXG4gICAgeyBsYWJlbDogJ0hlYXRtYXAnLCB2YWx1ZTogJ2hlYXRtYXAnIH0sXG4gIF07XG5cbiAgLyoqXG4gICAqIEEgdC1TTkUgdGhlIGRhdGFzZXQgZG9lcyBub3QgcHVibGlzaCwgb2ZmZXJlZCBzbyBpdCBjYW4gYmUgY29tcHV0ZWQgaGVyZS5cbiAgICpcbiAgICogQSBkcm9wcGVkLWluIGAuaDVhZGAgdHlwaWNhbGx5IGNhcnJpZXMgb25lIFVNQVAgYW5kIGEgUENBLCBhbmQgbm8gdC1TTkUg4oCUIHRoZSB0d29cbiAgICogdmlld3MgYW5zd2VyIGRpZmZlcmVudCBxdWVzdGlvbnMsIHNvIGhhdmluZyBvbmx5IG9uZSBpcyBhIHJlYWwgZ2FwLiBDb21wdXRpbmcgaXRcbiAgICogbmVlZHMgbm8gZXhwcmVzc2lvbiBkYXRhOiB0LVNORSBydW5zIG9uIHRoZSBQQ0Egc2NvcmVzLCB3aGljaCBpcyB3aHkgdGhpcyBjYW4gaGFwcGVuXG4gICAqIGluIHRoZSBicm93c2VyIGF0IGFsbCB3aGlsZSBQQ0EgY2Fubm90LlxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgQ09NUFVUQUJMRTogU3BhdGlhbEVtYmVkZGluZ01ldGFbXSA9IFtcbiAgICB7IG5hbWU6ICdsb2NhbDp0c25lJywgbGFiZWw6ICd0LVNORSAoY29tcHV0ZSknLCBkaW1zOiAyLCBkZXJpdmVkOiB0cnVlIH0sXG4gICAgeyBuYW1lOiAnbG9jYWw6dHNuZTNkJywgbGFiZWw6ICd0LVNORSAzRCAoY29tcHV0ZSknLCBkaW1zOiAzLCBkZXJpdmVkOiB0cnVlIH0sXG4gIF07XG5cbiAgLyoqIFRoZSBtZW51IHN1ZmZpeCB0aGF0IHNheXMgYSBjbGljayB3aWxsIHN0YXJ0IHdvcms7IG5vdCBwYXJ0IG9mIHRoZSBuYW1lLiAqL1xuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBDT01QVVRFX1NVRkZJWCA9ICcgKGNvbXB1dGUpJztcblxuICAvKipcbiAgICogTGFyZ2VzdCBkYXRhc2V0IHRoaXMgd2lsbCBvZmZlciB0byBlbWJlZCBpbiB0aGUgYnJvd3Nlci5cbiAgICpcbiAgICogdC1TTkUgaXMgTyhOwrIpIHBlciBpdGVyYXRpb24g4oCUIHZlY3RvcmlzZWQgb24gdGhlIEdQVSwgYnV0IG5vdCBhcHByb3hpbWF0ZWQg4oCUIHNvIHRoZVxuICAgKiBjb3N0IGlzIHF1YWRyYXRpYyBpbiBvYnNlcnZhdGlvbnMuIE1lYXN1cmVkIGVuZCB0byBlbmQgaW4gRmlyZWZveCBvbiBXZWJHUFU6IDIsNjg4XG4gICAqIHBvaW50cyB0b29rIDk1IHMuIFRoYXQgc2NhbGVzIHRvIHJvdWdobHkgZml2ZSBhbmQgYSBoYWxmIG1pbnV0ZXMgYXQgdGhpcyB0aHJlc2hvbGQsXG4gICAqIGFuZCB0byBhYm91dCA3NiBtaW51dGVzIGF0IHNlcUZJU0gncyAxOSw0MTYsIHdoaWNoIHdhcyBhbHNvIG1lYXN1cmVkIG9mZmxpbmUuXG4gICAqXG4gICAqIFBhc3QgdGhpcyB0aGUgb3B0aW9uIGlzIFdJVEhEUkFXTiByYXRoZXIgdGhhbiBvZmZlcmVkIHdpdGggYSB3YXJuaW5nLiBBbiBob3VyLWxvbmdcbiAgICogam9iIHN0YXJ0ZWQgZnJvbSBhIGJ1dHRvbiBpcyBub3QgYSBjaG9pY2UgYSByZWFkZXIgY2FuIG1lYW5pbmdmdWxseSBjb25zZW50IHRvIGluIGFcbiAgICogZGlhbG9nLCBhbmQgYSB0LVNORSB0aGF0IGxhcmdlIGJlbG9uZ3MgaW4gdGhlIG9mZmxpbmUgcGlwZWxpbmUsIHdoZXJlIGl0IGNhbiBydW5cbiAgICogb25jZSBhbmQgYmUgc2VydmVkIHRvIGV2ZXJ5b25lLlxuICAgKi9cbiAgc3RhdGljIHJlYWRvbmx5IEJST1dTRVJfVFNORV9NQVhfT0JTRVJWQVRJT05TID0gNTAwMDtcblxuICAvKiogTWVhc3VyZWQgYW5jaG9yIGZvciB0aGUgZXN0aW1hdGU6IHNlY29uZHMgZm9yIDIsNjg4IHBvaW50cyBvbiBXZWJHUFUuICovXG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IFRTTkVfU0VDT05EU19BVCA9IHsgc2Vjb25kczogOTUsIG9ic2VydmF0aW9uczogMjY4OCB9O1xuXG4gIC8qKiBDb29yZGluYXRlcyBjb21wdXRlZCBpbiB0aGlzIGJyb3dzZXIsIGJ5IG5hbWUuIE5vdCBwZXJzaXN0ZWQ6IGEgcmVsb2FkIHJlY29tcHV0ZXMuICovXG4gIHByaXZhdGUgcmVhZG9ubHkgY29tcHV0ZWQgPSBuZXcgTWFwPHN0cmluZywgU3BhdGlhbEVtYmVkZGluZz4oKTtcblxuICAvKiogVGhlIGxpdmUgcnVuLCB3aGVuIG9uZSBpcyBnb2luZy4gKi9cbiAgcHJpdmF0ZSBjb21wdXRlUnVuOiBFbWJlZGRpbmdDb21wdXRlUnVuIHwgbnVsbCA9IG51bGw7XG5cbiAgLyoqXG4gICAqIEJ1bXBlZCBieSBldmVyeSBkYXRhc2V0IGNoYW5nZSBhbmQgYnkgdGVhcmRvd24sIHNvIGEgY29tcHV0YXRpb24gaW4gcHJvZ3Jlc3MgY2FuIHRlbGxcbiAgICogdGhhdCB3aGF0IGl0IGlzIGNvbXB1dGluZyBpcyBubyBsb25nZXIgd2hhdCBpcyBvbiBzY3JlZW4uXG4gICAqXG4gICAqIEEgcnVuIGlzIHNldmVyYWwgYXdhaXRzIGxvbmcg4oCUIHRoZSBQQ0Egc2NvcmVzIGFycml2ZSBvdmVyIEhUVFAsIHRoZW4gdGhlIHdvcmtlciB0YWtlc1xuICAgKiBhIG1pbnV0ZSBvciBtb3JlIOKAlCBhbmQgdGhlIGRhdGFzZXQgY2FuIGNoYW5nZSBhdCBhbnkgb2YgdGhlbSwgaW5jbHVkaW5nIEJFRk9SRVxuICAgKiBgY29tcHV0ZVJ1bmAgZXhpc3RzIHRvIGJlIHRlcm1pbmF0ZWQuIFdpdGhvdXQgYSB0b2tlbiwgdGhlIHNjb3JlcyBmZXRjaGVkIGZvciBvbmVcbiAgICogZGF0YXNldCBnZXQgZW1iZWRkZWQgYW5kIGRyYXduIG92ZXIgYW5vdGhlcidzIG9ic2VydmF0aW9uczogYSBwbG90IG9mIHR3byBkYXRhc2V0cyBhdFxuICAgKiBvbmNlLCB3aGljaCByZWFkcyBhcyBhIHN0cmFuZ2UtbG9va2luZyB0LVNORSByYXRoZXIgdGhhbiBhcyBhIGJ1Zy5cbiAgICovXG4gIHByaXZhdGUgY29tcHV0ZVRva2VuID0gMDtcblxuICAvKiogMC4uMSB3aGlsZSBydW5uaW5nLCBvciBudWxsIGJlZm9yZSB0aGUgZmlyc3QgcmVwb3J0LiBEcml2ZXMgdGhlIHByb2dyZXNzIGJhci4gKi9cbiAgY29tcHV0ZUZyYWN0aW9uOiBudW1iZXIgfCBudWxsID0gbnVsbDtcblxuICAvKiogV2hpY2ggYmFja2VuZCB0aGUgd29ya2VyIGdvdCDigJQgJ3dlYmdwdScsICd3YXNtJyBvciAnY3B1Jy4gU2hvd24gd2hpbGUgcnVubmluZy4gKi9cbiAgY29tcHV0ZUJhY2tlbmQ6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuXG4gIGNvbXB1dGVNZXNzYWdlOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcblxuICBjb21wdXRlRXJyb3I6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuXG4gIC8qKiBUcnVlIHdoZW4gYSB0LVNORSBpcyBtaXNzaW5nIGJ1dCB0aGUgZGF0YXNldCBpcyB0b28gYmlnIHRvIGVtYmVkIGhlcmUuICovXG4gIHRzbmVUb29MYXJnZSA9IGZhbHNlO1xuXG4gIC8qKiBPYnNlcnZhdGlvbnMgaW4gdGhlIGxpdmUgZGF0YXNldCwga2VwdCBmb3IgdGhlIGNvc3QgZXN0aW1hdGUuICovXG4gIHByaXZhdGUgb2JzZXJ2YXRpb25Db3VudCA9IDA7XG5cbiAgLyoqIE9mZmVyZWQgb25seSB3aGVuIHRoZSBkYXRhc2V0IHB1Ymxpc2hlcyBhbiBlbWJlZGRpbmcgdG8gZHJhdy4gKi9cbiAgLy8gTGFiZWxsZWQgZm9yIHdoYXQgaXQgaXMgcmF0aGVyIHRoYW4gZm9yIG9uZSBpbnN0YW5jZSBvZiBpdDogdGhpcyB2aWV3IGRyYXdzIHdoYXRldmVyXG4gIC8vIGVtYmVkZGluZyB0aGUgZGF0YXNldCBwdWJsaXNoZXMg4oCUIGEgVU1BUCwgYSBQQ0EsIGEgdC1TTkUg4oCUIGFuZCBjYWxsaW5nIHRoZSB0YWIgXCJVTUFQXCJcbiAgLy8gd2hpbGUgaXQgc2hvd2VkIGEgUENBIHdvdWxkIGJlIGEgbGllIHRoZSBwaWNrZXIgYmVuZWF0aCBpdCBpbW1lZGlhdGVseSBjb250cmFkaWN0cy5cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgRU1CRURESU5HX0tJTkRTOiB7IGxhYmVsOiBzdHJpbmc7IHZhbHVlOiBPbWljc0NoYXJ0S2luZCB9W10gPSBbXG4gICAgeyBsYWJlbDogJ0VtYmVkZGluZycsIHZhbHVlOiAnZW1iZWRkaW5nJyB9LFxuICBdO1xuXG4gIC8qKiBUaGUga2luZHMgdGhlIEFDVElWRSBzdWJqZWN0IGNhbiBiZSBkcmF3biBhcy4gQSBjYXRlZ29yeSBjb2RlIGlzIGEgbGFiZWwsXG4gICAqICBub3QgYSBtYWduaXR1ZGUsIHNvIGEgaGlzdG9ncmFtIG9mIGl0IHdvdWxkIGJlIG1lYW5pbmdsZXNzIOKAlCB3aGF0IGFcbiAgICogIGNhdGVnb3JpY2FsIGNvbHVtbiBoYXMgaXMgYSBmcmVxdWVuY3kgZGlzdHJpYnV0aW9uLiAqL1xuICBnZXQga2luZE9wdGlvbnMoKTogeyBsYWJlbDogc3RyaW5nOyB2YWx1ZTogT21pY3NDaGFydEtpbmQgfVtdIHtcbiAgICByZXR1cm4gW1xuICAgICAgLi4uKHRoaXMuY2F0ZWdvcmljYWxcbiAgICAgICAgPyBTcGF0aWFsQ2hhcnRzQ29tcG9uZW50LkNBVEVHT1JJQ0FMX0tJTkRTXG4gICAgICAgIDogU3BhdGlhbENoYXJ0c0NvbXBvbmVudC5DT05USU5VT1VTX0tJTkRTKSxcbiAgICAgIC4uLlNwYXRpYWxDaGFydHNDb21wb25lbnQuQUxXQVlTX0tJTkRTLFxuICAgICAgLy8gQW4gZW1iZWRkaW5nIGlzIGEgcHJvcGVydHkgb2YgdGhlIERBVEFTRVQsIG5vdCBvZiB0aGUgYWN0aXZlIGNvbG91ciBzb3VyY2UsIHNvXG4gICAgICAvLyBpdCBpcyBvZmZlcmVkIHdoZW5ldmVyIG9uZSBpcyBwdWJsaXNoZWQgYW5kIG5ldmVyIG90aGVyd2lzZSDigJQgYSB0YWIgdGhhdCBkcmF3c1xuICAgICAgLy8gbm90aGluZyBpcyB3b3JzZSB0aGFuIGFuIGFic2VudCBvbmUuXG4gICAgICAuLi4odGhpcy5lbWJlZGRpbmdzLmxlbmd0aCA+IDAgPyBTcGF0aWFsQ2hhcnRzQ29tcG9uZW50LkVNQkVERElOR19LSU5EUyA6IFtdKSxcbiAgICBdO1xuICB9XG5cbiAgY29udHJvbHM6IElTcGF0aWFsQ29udHJvbHMgfCBudWxsID0gbnVsbDtcbiAga2luZDogT21pY3NDaGFydEtpbmQgPSAnaGlzdG9ncmFtJztcbiAgLyoqIEVtYmVkZGluZ3MgdGhlIGRhdGFzZXQgcHVibGlzaGVzLCBhbmQgd2hpY2ggb2YgdGhlbSBpcyBkcmF3bi4gKi9cbiAgZW1iZWRkaW5nczogU3BhdGlhbEVtYmVkZGluZ01ldGFbXSA9IFtdO1xuICBlbWJlZGRpbmc6IFNwYXRpYWxFbWJlZGRpbmdNZXRhIHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgZW1iZWRkaW5nQ29vcmRzOiBTcGF0aWFsRW1iZWRkaW5nIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBUaGUgY2F0ZWdvcmljYWwgY29sb3VyaW5nIGJlaGluZCB0aGUgZW1iZWRkaW5nJ3MgY29sb3Vycywga2VwdCBzbyBhIHJlZHJhdyBmb3IgYVxuICAgKiAgc2VsZWN0aW9uIGNoYW5nZSBkb2VzIG5vdCByZWZldGNoIHRoZSBjb2x1bW4uICovXG4gIHByaXZhdGUgZW1iZWRkaW5nQ29kZXM6IHsgY29kZXM6IFVpbnQxNkFycmF5OyBuYW1lczogc3RyaW5nW107IGNvbG9yczogc3RyaW5nW10gfSB8IG51bGwgPSBudWxsO1xuICAvKipcbiAgICogR2VuZXMgdGhlIGhlYXRtYXAncyByb3dzIGFyZSwgYW5kIHRoZSB2ZWN0b3JzIGJlaGluZCB0aGVtLlxuICAgKlxuICAgKiBMb2NhbCByYXRoZXIgdGhhbiBpbiBgU3BhdGlhbFZpZXdTdGF0ZWAsIG1hdGNoaW5nIGhvdyB0aGUgY2hhcnQgS0lORCBpc1xuICAgKiBoZWxkOiB0aGlzIGlzIHdoYXQgdGhlIHBhbmVsIGlzIGNoYXJ0aW5nLCBub3Qgd2hhdCB0aGUgbWFwIGlzIGRyYXdpbmcsIGFuZFxuICAgKiB0aGUgcmVuZGVyZXIgaGFzIG5vIHVzZSBmb3IgaXQuXG4gICAqL1xuICBoZWF0bWFwR2VuZXM6IHN0cmluZ1tdID0gW107XG4gIC8qKiBaLXNjb3JlIGVhY2ggZ2VuZSBhY3Jvc3MgdGhlIGdyb3Vwcy4gT24gYnkgZGVmYXVsdDogd2l0aG91dCBpdCBvbmUgbG91ZFxuICAgKiAgZ2VuZSBzYXR1cmF0ZXMgdGhlIHNjYWxlIGFuZCB0aGUgcmVzdCBvZiB0aGUgcGFuZWwgcmVhZHMgYXMgYmxhbmsuICovXG4gIGhlYXRtYXBaU2NvcmUgPSB0cnVlO1xuICAvKiogUGFzdCB0aGlzIG1hbnkgc2VsZWN0ZWQgY2VsbHMgdGhlIHBlci1jZWxsIHZpZXcgaXMgYSB0ZXh0dXJlLCBub3QgYVxuICAgKiAgcmVhZGFibGUgcGFuZWwsIHNvIHRoZSBjb2x1bW5zIGdvIGJhY2sgdG8gYmVpbmcgY2xhc3Nlcy4gKi9cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgSEVBVE1BUF9DRUxMX0NPTFVNTlMgPSAyMDA7XG4gIC8qKlxuICAgKiBDb2x1bW4gY2FwIGZvciB0aGUgZ3JvdXBlZCBoZWF0bWFwLlxuICAgKlxuICAgKiBBbnkgY2F0ZWdvcmljYWwgY29sdW1uIGNhbiBiZSB0aGUgeCBheGlzLCBhbmQgdGhleSBhcmUgbm90IHRoZSBzYW1lIHNpemU6XG4gICAqIGBuZXVyb3RyYW5zbWl0dGVyYCBoYXMgOSBjYXRlZ29yaWVzLCBgY2xhc3NgIDM0LCBgc3ViY2xhc3NgIDMzOC4gQXQgMzM4IHRoZVxuICAgKiBwYW5lbCBpcyBhIHRleHR1cmUg4oCUIGNvbHVtbnMgYSBjb3VwbGUgb2YgcGl4ZWxzIHdpZGUsIG5vIHJlYWRhYmxlIGxhYmVsIOKAlFxuICAgKiBzbyBwYXN0IHRoaXMgbWFueSB0aGUgc3Ryb25nZXN0IGNvbHVtbnMgYXJlIGtlcHQgYW5kIHRoZSBub3RlIHNheXMgaG93IG1hbnlcbiAgICogd2VyZSBkcm9wcGVkLiBTZWUgYG1heENvbHNgIGluIGBzcGF0aWFsLWhlYXRtYXAudHNgLlxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgSEVBVE1BUF9NQVhfQ09MVU1OUyA9IDQwO1xuICAvKiogQ29sdW1ucyB0aGUgY2FwIGRyb3BwZWQgZnJvbSB0aGUgbGFzdCByZW5kZXIsIGZvciB0aGUgbm90ZS4gKi9cbiAgcHJpdmF0ZSBoZWF0bWFwSGlkZGVuID0gMDtcbiAgZ2VuZU9wdGlvbnM6IHsgbGFiZWw6IHN0cmluZzsgdmFsdWU6IHN0cmluZyB9W10gPSBbXTtcbiAgLyoqIEV2ZXJ5IGdlbmUgdGhlIGRhdGFzZXQgaW5saW5lZC4gYGdlbmVPcHRpb25zYCBpcyBvbmx5IGV2ZXIgdGhlIGJlc3QgZmV3IGh1bmRyZWQgb2ZcbiAgICogIHRoZXNlIOKAlCBzZWUgYGdlbmUtc2VhcmNoLnRzYCBmb3Igd2h5IGEgd2hvbGUtdHJhbnNjcmlwdG9tZSBsaXN0IGNhbm5vdCBiZSBoYW5kZWQgdG9cbiAgICogIHRoZSBjb250cm9sIHdob2xlLiAqL1xuICBwcml2YXRlIGdlbmVOYW1lczogc3RyaW5nW10gPSBbXTtcbiAgLyoqIFdoYXQgaXMgdHlwZWQgaW4gdGhlIHBpY2tlcidzIGZpbHRlciBib3gsIHNvIHRoZSBvcHRpb25zIGNhbiBiZSByZWJ1aWx0IHdoZW4gdGhlXG4gICAqICBzZWxlY3Rpb24gY2hhbmdlcyB3aXRob3V0IGxvc2luZyB0aGUgcXVlcnkuICovXG4gIHByaXZhdGUgZ2VuZVF1ZXJ5ID0gJyc7XG4gIC8qKiBGZXRjaGVkIHZlY3RvcnMgYnkgZ2VuZSBuYW1lLCBzbyBhZGRpbmcgYSBmb3VydGggZ2VuZSBkb2VzIG5vdCByZWZldGNoIHRoZVxuICAgKiAgZmlyc3QgdGhyZWUg4oCUIGVhY2ggaXMgYSBmdWxsIHBlci1vYnNlcnZhdGlvbiBGbG9hdDMyQXJyYXkuICovXG4gIHByaXZhdGUgcmVhZG9ubHkgZ2VuZUNhY2hlID0gbmV3IE1hcDxzdHJpbmcsIEZsb2F0MzJBcnJheT4oKTtcbiAgLyoqIENhdGVnb3JpY2FsIGNvbHVtbiB0aGUgdmlvbGluL2JveCBzcGxpdHMgYnk7IG51bGwgPSBvbmUgdHJhY2UgZm9yIGFsbC4gKi9cbiAgZ3JvdXBCeTogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG4gIGdyb3VwT3B0aW9uczogeyBsYWJlbDogc3RyaW5nOyB2YWx1ZTogc3RyaW5nIHwgbnVsbCB9W10gPSBbXTtcblxuICAvKiogV2hhdCB0aGUgbWFwIGlzIGNvbG91cmVkIGJ5IOKAlCB0aGUgY2hhcnQncyBzdWJqZWN0LiAqL1xuICBjb2xvckJ5OiBTcGF0aWFsQ29sb3JCeSB8IG51bGwgPSBudWxsO1xuICBzZWxlY3Rpb25Db3VudCA9IDA7XG4gIC8qKiBTZXQgd2hlbiB0aGUgYWN0aXZlIGNvbG91ciBzb3VyY2UgY2Fubm90IGJlIGNoYXJ0ZWQsIGZvciBhbiBpbmxpbmUgaGludC4gKi9cbiAgbm90aWNlOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgYnVzeSA9IGZhbHNlO1xuXG4gIHByaXZhdGUgdmlldzogU3BhdGlhbFZpZXdTdGF0ZSA9IHsgLi4uREVGQVVMVF9TUEFUSUFMX1ZJRVcgfTtcbiAgcHJpdmF0ZSBzZWxlY3Rpb246IFNwYXRpYWxTZWxlY3Rpb25NYXNrID0gZW1wdHlTZWxlY3Rpb24oKTtcbiAgcHJpdmF0ZSB2YWx1ZXM6IEZsb2F0MzJBcnJheSB8IG51bGwgPSBudWxsO1xuICAvKiogU2V0IGluc3RlYWQgb2YgYHZhbHVlc2Agd2hlbiB0aGUgY29sb3VyIHNvdXJjZSBpcyBhIGNhdGVnb3JpY2FsIGNvbHVtbjogaXRzXG4gICAqICBkaXN0cmlidXRpb24gaXMgY291bnRzIHBlciBjYXRlZ29yeSwgbm90IGEgaGlzdG9ncmFtIG9mIGl0cyBjb2Rlcy4gKi9cbiAgcHJpdmF0ZSBjYXRlZ29yaWNhbDogT21pY3NHcm91cGluZyB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGdyb3VwaW5nOiBPbWljc0dyb3VwaW5nIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBHdWFyZHMgdGhlIGFzeW5jIHZhbHVlIGZldGNoOiBhIGZhc3QgY29sb3VyLXNvdXJjZSBjaGFuZ2UgY2FuIHJlc29sdmUgb3V0XG4gICAqICBvZiBvcmRlciwgYW5kIGEgc3RhbGUgdmVjdG9yIHdvdWxkIGJlIGNoYXJ0ZWQgYWdhaW5zdCB0aGUgbmV3IGxhYmVsLiAqL1xuICBwcml2YXRlIHRva2VuID0gMDtcbiAgLyoqIFRoZSBzYW1lIGd1YXJkIGZvciB0aGUgZ3JvdXBpbmcgZmV0Y2gsIHdoaWNoIHRoZSB1c2VyIGNhbiBjaGFuZ2UgYXMgZmFzdC4gKi9cbiAgcHJpdmF0ZSBncm91cFRva2VuID0gMDtcbiAgLyoqIFdoZXRoZXIgdGhlIGZpcnN0IHZpZXcgZW1pc3Npb24gaGFzIGJlZW4gaGFuZGxlZC4gKi9cbiAgcHJpdmF0ZSBwcmltZWQgPSBmYWxzZTtcbiAgcHJpdmF0ZSByZWFkb25seSBzdWJzID0gbmV3IFN1YnNjcmlwdGlvbigpO1xuXG4gIGNvbnN0cnVjdG9yKEBJbmplY3QoVklTVUFMSVpFUikgcHJpdmF0ZSByZWFkb25seSB2aXo6IElWaXN1YWxpemVyKSB7fVxuXG4gIG5nT25Jbml0KCk6IHZvaWQge1xuICAgIC8vIEJlZm9yZSB0aGUgcG9ydCBjaGVjazogdGhlIGhlbHAgaWNvbiBpcyBpbiB0aGUgdGVtcGxhdGUgZWl0aGVyIHdheSwgYW5kIGEgcGFuZWxcbiAgICAvLyB3aXRoIG5vIGRhdGEgc291cmNlIGlzIGV4YWN0bHkgd2hlcmUgc29tZW9uZSByZWFkcyBpdC5cbiAgICBlbnN1cmVIZWxwVGlwU3R5bGUoKTtcbiAgICB0aGlzLmNvbnRyb2xzID0gdGhpcy52aXouZ2V0U3BhdGlhbENvbnRyb2xzPy4oKSA/PyBudWxsO1xuICAgIGlmICghdGhpcy5jb250cm9scykgcmV0dXJuO1xuXG4gICAgdGhpcy5zdWJzLmFkZChjb21iaW5lTGF0ZXN0KFtcbiAgICAgIHRoaXMuY29udHJvbHMuZ2V0Vmlld1N0YXRlJCgpLCB0aGlzLmNvbnRyb2xzLmdldFNlbGVjdGlvbiQoKSxcbiAgICBdKS5zdWJzY3JpYmUoKFt2aWV3LCBzZWxlY3Rpb25dKSA9PiB7XG4gICAgICBjb25zdCBzb3VyY2VDaGFuZ2VkID0gdmlldy5jb2xvckJ5Py5raW5kICE9PSB0aGlzLnZpZXcuY29sb3JCeT8ua2luZFxuICAgICAgICB8fCB2aWV3LmNvbG9yQnk/Lm5hbWUgIT09IHRoaXMudmlldy5jb2xvckJ5Py5uYW1lO1xuICAgICAgdGhpcy52aWV3ID0gdmlldztcbiAgICAgIHRoaXMuY29sb3JCeSA9IHZpZXcuY29sb3JCeTtcbiAgICAgIHRoaXMuc2VsZWN0aW9uID0gc2VsZWN0aW9uO1xuICAgICAgdGhpcy5zZWxlY3Rpb25Db3VudCA9IHNlbGVjdGlvbi5jb3VudDtcbiAgICAgIC8vIEEgc2VsZWN0aW9uIG9yIGxvZyBjaGFuZ2Ugb25seSBuZWVkcyBhIHJlLXJlbmRlcjsgYSBuZXcgY29sb3VyIHNvdXJjZVxuICAgICAgLy8gbmVlZHMgaXRzIHZlY3RvciBmZXRjaGVkIGZpcnN0LiBUaGUgRklSU1QgZW1pc3Npb24gYWx3YXlzIHJlbG9hZHM6XG4gICAgICAvLyBvdGhlcndpc2UgYG51bGwgLT4gbnVsbGAgcmVhZHMgYXMgXCJ1bmNoYW5nZWRcIiBhbmQgdGhlIGNvbXBvbmVudCBzaXRzXG4gICAgICAvLyB3aXRoIG5vIGRhdGEgYW5kIG5vIGV4cGxhbmF0aW9uIHVudGlsIHNvbWV0aGluZyBlbHNlIG1vdmVzLlxuICAgICAgaWYgKCF0aGlzLnByaW1lZCB8fCBzb3VyY2VDaGFuZ2VkKSB7XG4gICAgICAgIHRoaXMucHJpbWVkID0gdHJ1ZTtcbiAgICAgICAgdm9pZCB0aGlzLnJlbG9hZCgpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gQSBzZWxlY3Rpb24gb3IgbG9nLXNjYWxlIGNoYW5nZSBuZWVkcyBvbmx5IGEgcmVkcmF3IG9mIHRoZSBzYW1lIHZlY3Rvci5cbiAgICAgICAgdm9pZCB0aGlzLnJlbmRlcigpO1xuICAgICAgfVxuICAgIH0pKTtcblxuICAgIHRoaXMuc3Vicy5hZGQodGhpcy5jb250cm9scy5nZXREYXRhc2V0JCgpLnN1YnNjcmliZSgoZGF0YXNldCkgPT4ge1xuICAgICAgdGhpcy5ncm91cE9wdGlvbnMgPSBbXG4gICAgICAgIHsgbGFiZWw6ICdObyBncm91cGluZycsIHZhbHVlOiBudWxsIH0sXG4gICAgICAgIC4uLih0aGlzLmNvbnRyb2xzPy5jYXRlZ29yaWNhbENvbHVtbnMoKSA/PyBbXSkubWFwKChuKSA9PiAoeyBsYWJlbDogbiwgdmFsdWU6IG4gfSkpLFxuICAgICAgXTtcbiAgICAgIC8vIEEgbmV3IGRhdGFzZXQncyBjb2x1bW5zIGFyZSBkaWZmZXJlbnQ7IGEgY2FycmllZC1vdmVyIGdyb3VwIGlzIG1lYW5pbmdsZXNzLlxuICAgICAgaWYgKHRoaXMuZ3JvdXBCeSAmJiAhdGhpcy5ncm91cE9wdGlvbnMuc29tZSgobykgPT4gby52YWx1ZSA9PT0gdGhpcy5ncm91cEJ5KSkge1xuICAgICAgICB0aGlzLmdyb3VwQnkgPSBudWxsO1xuICAgICAgICB0aGlzLmdyb3VwaW5nID0gbnVsbDtcbiAgICAgIH1cbiAgICAgIC8vIFRoZSBoZWF0bWFwJ3Mgcm93cyBjb21lIGZyb20gdGhlIGRhdGFzZXQncyBnZW5lIGxpc3QuIEEgZGF0YXNldCB0b28gd2lkZVxuICAgICAgLy8gdG8gaW5saW5lIGl0cyBuYW1lcyBvZmZlcnMgbm9uZSBoZXJlIOKAlCB0aGUgcGFuZWwncyB0eXBlYWhlYWQgaXMgdGhlIHdheVxuICAgICAgLy8gaW4gZm9yIHRob3NlLCBhbmQgdGhlIGhlYXRtYXAgbmVlZHMgbmFtZXMgaXQgY2FuIGxpc3QuXG4gICAgICB0aGlzLmdlbmVOYW1lcyA9IFsuLi4oZGF0YXNldD8uZmVhdHVyZXM/Lm5hbWVzID8/IFtdKV07XG4gICAgICB0aGlzLmdlbmVRdWVyeSA9ICcnO1xuICAgICAgLy8gVmFsaWRhdGVkIGFnYWluc3QgdGhlIFdIT0xFIGxpc3QsIG5vdCB0aGUgdmlzaWJsZSBvcHRpb25zOiB0aG9zZSBhcmUgY2FwcGVkLCBhbmRcbiAgICAgIC8vIGZpbHRlcmluZyB0aGUgc2VsZWN0aW9uIGJ5IHRoZW0gd291bGQgZHJvcCBnZW5lcyB0aGUgZGF0YXNldCBzdGlsbCBoYXMuXG4gICAgICBjb25zdCBrbm93biA9IG5ldyBTZXQodGhpcy5nZW5lTmFtZXMpO1xuICAgICAgdGhpcy5oZWF0bWFwR2VuZXMgPSB0aGlzLmhlYXRtYXBHZW5lcy5maWx0ZXIoKG4pID0+IGtub3duLmhhcyhuKSk7XG4gICAgICB0aGlzLnJlZnJlc2hHZW5lT3B0aW9ucygpO1xuICAgICAgdGhpcy5nZW5lQ2FjaGUuY2xlYXIoKTtcblxuICAgICAgLy8gRW1iZWRkaW5ncyBiZWxvbmcgdG8gdGhlIGRhdGFzZXQsIHNvIHRoZXkgYXJlIHJlLXJlYWQgd2l0aCBpdCBhbmQgdGhlIGxvYWRlZFxuICAgICAgLy8gY29vcmRpbmF0ZXMgZHJvcHBlZDogYSBVTUFQIGZyb20gdGhlIHByZXZpb3VzIGRhdGFzZXQgb3ZlciB0aGVzZSBvYnNlcnZhdGlvbnNcbiAgICAgIC8vIHdvdWxkIGJlIGEgcGxvdCBvZiB0d28gZGlmZmVyZW50IHRoaW5ncyBhdCBvbmNlLlxuICAgICAgdGhpcy5jb21wdXRlZC5jbGVhcigpO1xuICAgICAgdGhpcy5hYmFuZG9uQ29tcHV0ZSgpO1xuICAgICAgY29uc3QgcHVibGlzaGVkID0gZGF0YXNldD8uZW1iZWRkaW5ncyA/IFsuLi5kYXRhc2V0LmVtYmVkZGluZ3NdIDogW107XG4gICAgICAvLyBPZmZlciB0byBjb21wdXRlIGEgdC1TTkUgb25seSB3aGVyZSB0aGUgZGF0YXNldCBoYXMgUENBIHRvIGVtYmVkIGFuZCBubyB0LVNORSBvZlxuICAgICAgLy8gaXRzIG93biDigJQgb3RoZXJ3aXNlIHRoZSBtZW51IGFkdmVydGlzZXMgd29yayB0aGF0IGNhbm5vdCBzdGFydCwgb3IgZHVwbGljYXRlc1xuICAgICAgLy8gd2hhdCBpcyBhbHJlYWR5IHNlcnZlZC5cbiAgICAgIGNvbnN0IGhhc1BjYSA9IHB1Ymxpc2hlZC5zb21lKChlKSA9PiAvcGNhL2kudGVzdChlLmxhYmVsID8/IGUubmFtZSkpO1xuICAgICAgY29uc3QgaGFzVHNuZSA9IHB1Ymxpc2hlZC5zb21lKChlKSA9PiAvdHNuZXx0LXNuZS9pLnRlc3QoZS5sYWJlbCA/PyBlLm5hbWUpKTtcbiAgICAgIHRoaXMub2JzZXJ2YXRpb25Db3VudCA9IGRhdGFzZXQ/Lm9ic2VydmF0aW9ucy5jb3VudCA/PyAwO1xuICAgICAgdGhpcy50c25lVG9vTGFyZ2UgPSBoYXNQY2EgJiYgIWhhc1RzbmVcbiAgICAgICAgJiYgdGhpcy5vYnNlcnZhdGlvbkNvdW50ID4gU3BhdGlhbENoYXJ0c0NvbXBvbmVudC5CUk9XU0VSX1RTTkVfTUFYX09CU0VSVkFUSU9OUztcbiAgICAgIHRoaXMuZW1iZWRkaW5ncyA9IGhhc1BjYSAmJiAhaGFzVHNuZSAmJiAhdGhpcy50c25lVG9vTGFyZ2VcbiAgICAgICAgPyBbLi4ucHVibGlzaGVkLCAuLi5TcGF0aWFsQ2hhcnRzQ29tcG9uZW50LkNPTVBVVEFCTEVdXG4gICAgICAgIDogcHVibGlzaGVkO1xuICAgICAgdGhpcy5lbWJlZGRpbmcgPSB0aGlzLmVtYmVkZGluZ3NbMF0gPz8gbnVsbDtcbiAgICAgIHRoaXMuZW1iZWRkaW5nQ29vcmRzID0gbnVsbDtcbiAgICAgIHRoaXMuZW1iZWRkaW5nQ29kZXMgPSBudWxsO1xuICAgICAgLy8gTm90aGluZyB0byBkcmF3IGZvciB0aGUga2luZCB0aGF0IHdhcyBzZWxlY3RlZDsgZmFsbCBiYWNrIHJhdGhlciB0aGFuIHNpdCBibGFuay5cbiAgICAgIGlmICh0aGlzLmtpbmQgPT09ICdlbWJlZGRpbmcnICYmIHRoaXMuZW1iZWRkaW5ncy5sZW5ndGggPT09IDApIHRoaXMua2luZCA9ICdoaXN0b2dyYW0nO1xuICAgIH0pKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgaGVpZ2h0IHRoZSBkcmF3biBsYXlvdXQgYXNrZWQgZm9yLCBvciBudWxsIHdoZXJlIGl0IGF1dG9zaXplcy5cbiAgICpcbiAgICoge0BsaW5rIHJlc2l6ZX0gbmVlZHMgdGhpczogdGhlIGNvdW50cyBhbmQgaGVhdG1hcCBsYXlvdXRzIHNpemUgdGhlaXIgaGVpZ2h0XG4gICAqIHRvIHRoZWlyIGNvbnRlbnQg4oCUIG9uZSBiYW5kIHBlciBiYXIsIHBlciBnZW5lIHJvdyDigJQgd2hpbGUgdGhlIGRpc3RyaWJ1dGlvblxuICAgKiBraW5kcyB3YW50IHRvIGZpbGwgd2hhdGV2ZXIgaGVpZ2h0IHRoZXkgYXJlIGdpdmVuLlxuICAgKi9cbiAgcHJpdmF0ZSBkcmF3bkhlaWdodDogbnVtYmVyIHwgbnVsbCA9IG51bGw7XG5cbiAgLyoqXG4gICAqIFJlLWZpdCB0aGUgcGxvdCB0byB0aGUgcGFuZWwncyBjdXJyZW50IHdpZHRoLlxuICAgKlxuICAgKiBDYWxsZWQgYnkgdGhlIGhvc3Qgd2hlbiB0aGUgZGlhbG9nIGZpbmlzaGVzIHJlc2l6aW5nLiBQbG90bHkgZG9lcyBub3Qgbm90aWNlXG4gICAqIGEgY29udGFpbmVyIHJlc2l6ZSBvbiBpdHMgb3duIOKAlCBpdHMgYHJlc3BvbnNpdmVgIG9wdGlvbiBsaXN0ZW5zIGZvciBXSU5ET1dcbiAgICogcmVzaXplcyBvbmx5LCBzbyBkcmFnZ2luZyBhIGRpYWxvZyBlZGdlIHJlYWNoZXMgaXQgdGhyb3VnaCBub3RoaW5nIGF0IGFsbC5cbiAgICovXG4gIHJlc2l6ZSgpOiB2b2lkIHtcbiAgICB0aGlzLnJlZml0KHRoaXMuY2hhcnREaXYpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlLWZpdCB3aGF0ZXZlciBpcyBwbG90dGVkIGluIGBkaXZgIHRvIGl0cyBjb250YWluZXIuXG4gICAqXG4gICAqIFNoYXJlZCBieSB0aGUgcGFuZWwgYW5kIHRoZSBkZXRhY2hlZCB3aW5kb3cgYmVjYXVzZSB0aGUgcnVsZSBpcyB0aGUgc2FtZSBhbmQgZ2V0dGluZ1xuICAgKiBpdCB3cm9uZyBpcyBpbnZpc2libGUgdW50aWwgaXQgaXMgbm90OiBgYXV0b3NpemVgIHRha2VzIEJPVEggZGltZW5zaW9ucyBmcm9tIHRoZVxuICAgKiBjb250YWluZXIsIHdoaWNoIGlzIHJpZ2h0IG9ubHkgd2hlcmUgdGhlIGxheW91dCBkaWQgbm90IGZpeCBpdHMgb3duIGhlaWdodC4gTWVhc3VyZWQ6XG4gICAqIGF1dG9zaXppbmcgYSBsYXlvdXQgd2l0aCBhbiBleHBsaWNpdCBoZWlnaHQgb2YgNTAwIHJlcGxhY2VkIGl0IHdpdGggdGhlIGNvbnRhaW5lcidzXG4gICAqIDQwMC4gVGhlIGNvdW50cyBhbmQgaGVhdG1hcCBsYXlvdXRzIHNpemUgdGhlbXNlbHZlcyB0byB0aGVpciByb3cgY291bnQsIHNvIHRoZVxuICAgKiB3aW5kb3cgcmVzaXplIGhhcyB0byBzZXQgdGhlIHdpZHRoIEFMT05FIGZvciB0aGVtIGFuZCBsZXQgdGhlIGhlaWdodCBzdGFuZC5cbiAgICovXG4gIHByaXZhdGUgcmVmaXQoZGl2OiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCBlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGRpdik7XG4gICAgaWYgKCFlbCkgcmV0dXJuO1xuICAgIGNvbnN0IHdpZHRoID0gTWF0aC5yb3VuZChlbC5jbGllbnRXaWR0aCk7XG4gICAgLy8gWmVybyB3aGlsZSB0aGUgc2VjdGlvbiBpcyBjb2xsYXBzZWQgb3IgdGhlIGRpYWxvZyBpcyBjbG9zZWQ7IHJlbGF5aW5nIG91dFxuICAgIC8vIHRvIGEgemVybyBib3ggbWFrZXMgUGxvdGx5IGNvbXB1dGUgYSBsYXlvdXQgaXQgZG9lcyBub3QgcmVjb3ZlciBmcm9tLlxuICAgIGlmICh3aWR0aCA8PSAwKSByZXR1cm47XG4gICAgdHJ5IHtcbiAgICAgIFBsb3RseS5yZWxheW91dChlbCwgdGhpcy5kcmF3bkhlaWdodCA9PT0gbnVsbCA/IHsgYXV0b3NpemU6IHRydWUgfSA6IHsgd2lkdGggfSk7XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBOb3RoaW5nIHBsb3R0ZWQgeWV0LlxuICAgIH1cbiAgfVxuXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIHRoaXMuc3Vicy51bnN1YnNjcmliZSgpO1xuICAgIC8vIEEgd29ya2VyIG91dGxpdmVzIHRoZSBjb21wb25lbnQgdGhhdCBzdGFydGVkIGl0OiBjbG9zaW5nIHRoZSBwYW5lbCBtaWQtcnVuIHdvdWxkXG4gICAgLy8gb3RoZXJ3aXNlIGxlYXZlIGEgdC1TTkUgc2F0dXJhdGluZyBhIEdQVSBmb3IgbWludXRlcyB3aXRoIG5vdGhpbmcgbGVmdCB0byByZWNlaXZlXG4gICAgLy8gdGhlIGFuc3dlci5cbiAgICB0aGlzLmFiYW5kb25Db21wdXRlKCk7XG4gICAgLy8gQk9USCBkaXZzOiBhIGNoYXJ0IGxlZnQgZGV0YWNoZWQgYXQgdGVhcmRvd24gaG9sZHMgaXRzIFdlYkdMIGNvbnRleHQgaW4gdGhlXG4gICAgLy8gd2luZG93J3MgZGl2LCB3aGljaCB0aGUgaW5saW5lIGlkIHdvdWxkIG5ldmVyIHJlYWNoLlxuICAgIGZvciAoY29uc3QgZGl2IG9mIFt0aGlzLmNoYXJ0RGl2LCB0aGlzLmRldGFjaGVkRGl2XSkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgUGxvdGx5LnB1cmdlKGRpdik7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLy8gVGhlIGRpdiBtYXkgYWxyZWFkeSBiZSBnb25lIHdpdGggdGhlIGRpYWxvZzsgbm90aGluZyB0byBjbGVhbiB1cC5cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBuZ0FmdGVyVmlld0luaXQoKTogdm9pZCB7XG4gICAgLy8gVGhlIGRpdiBleGlzdHMgb25seSBub3csIHNvIHRoaXMgaXMgdGhlIGVhcmxpZXN0IGEgZmlyc3QgZHJhdyBjYW4gbGFuZC5cbiAgICB2b2lkIHRoaXMucmVsb2FkKCk7XG4gIH1cblxuICBvbktpbmQoa2luZDogT21pY3NDaGFydEtpbmQpOiB2b2lkIHtcbiAgICB0aGlzLmtpbmQgPSBraW5kO1xuICAgIGlmIChraW5kID09PSAnaGVhdG1hcCcpIHtcbiAgICAgIC8vIFNlZWQgd2l0aCB0aGUgZ2VuZSBhbHJlYWR5IG9uIHNjcmVlbiwgc28gdGhlIGNoYXJ0IHNheXMgc29tZXRoaW5nIHRoZVxuICAgICAgLy8gbW9tZW50IGl0IG9wZW5zIHJhdGhlciB0aGFuIHNob3dpbmcgYW4gZW1wdHkgZ3JpZCBhbmQgYSBwcm9tcHQuXG4gICAgICBpZiAodGhpcy5oZWF0bWFwR2VuZXMubGVuZ3RoID09PSAwICYmIHRoaXMuY29sb3JCeT8ua2luZCA9PT0gJ2ZlYXR1cmUnKSB7XG4gICAgICAgIHRoaXMuaGVhdG1hcEdlbmVzID0gW3RoaXMuY29sb3JCeS5uYW1lXTtcbiAgICAgIH1cbiAgICAgIHZvaWQgKGFzeW5jICgpID0+IHtcbiAgICAgICAgYXdhaXQgdGhpcy5lbnN1cmVIZWF0bWFwR3JvdXBpbmcoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5sb2FkSGVhdG1hcEdlbmVzKCk7XG4gICAgICAgIHZvaWQgdGhpcy5yZW5kZXIoKTtcbiAgICAgIH0pKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHZvaWQgdGhpcy5yZW5kZXIoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgZ3JvdXBpbmcgdGhlIGhlYXRtYXAncyBjb2x1bW5zIGNvbWUgZnJvbSwgZGVmYXVsdGluZyByYXRoZXIgdGhhblxuICAgKiBkZW1hbmRpbmcgb25lOiBhIGhlYXRtYXAgd2l0aCBubyBjb2x1bW5zIGlzIG5vdCBhIGNoYXJ0LCBhbmQgXCJObyBncm91cGluZ1wiXG4gICAqIGlzIGEgc2Vuc2libGUgYW5zd2VyIGZvciBhIHZpb2xpbiBidXQgbm90IGZvciB0aGlzLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBlbnN1cmVIZWF0bWFwR3JvdXBpbmcoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMuZ3JvdXBpbmcgfHwgIXRoaXMuY29udHJvbHMpIHJldHVybjtcbiAgICBjb25zdCBmaXJzdCA9IHRoaXMuZ3JvdXBPcHRpb25zLmZpbmQoKG8pID0+IG8udmFsdWUpPy52YWx1ZTtcbiAgICBpZiAoIWZpcnN0KSByZXR1cm47XG4gICAgYXdhaXQgdGhpcy5vbkdyb3VwQnkoZmlyc3QpO1xuICB9XG5cbiAgb25IZWF0bWFwWlNjb3JlKG9uOiBib29sZWFuKTogdm9pZCB7XG4gICAgdGhpcy5oZWF0bWFwWlNjb3JlID0gb247XG4gICAgdm9pZCB0aGlzLnJlbmRlcigpO1xuICB9XG5cbiAgLyoqIFdoYXQgdGhlIGhlYXRtYXAgaXMgY3VycmVudGx5IHNob3dpbmcsIHNhaWQgcGxhaW5seS4gKi9cbiAgZ2V0IGhlYXRtYXBOb3RlKCk6IHN0cmluZyB7XG4gICAgaWYgKHRoaXMuaGVhdG1hcEdlbmVzLmxlbmd0aCA9PT0gMCkgcmV0dXJuICdQaWNrIG9uZSBvciBtb3JlIGdlbmVzIGZvciB0aGUgcm93cy4nO1xuICAgIGNvbnN0IHBlckNlbGwgPSB0aGlzLnNlbGVjdGlvbkNvdW50ID4gMFxuICAgICAgJiYgdGhpcy5zZWxlY3Rpb25Db3VudCA8PSBTcGF0aWFsQ2hhcnRzQ29tcG9uZW50LkhFQVRNQVBfQ0VMTF9DT0xVTU5TO1xuICAgIGlmIChwZXJDZWxsKSB7XG4gICAgICByZXR1cm4gYE9uZSBjb2x1bW4gcGVyIHNlbGVjdGVkIGNlbGwgKCR7dGhpcy5zZWxlY3Rpb25Db3VudH0pLiBgXG4gICAgICAgICsgJ01lYW4gZXhwcmVzc2lvbiBwZXIgY2VsbCwgc28gdGhlIGNvbHVtbnMgYXJlIGNlbGxzIHJhdGhlciB0aGFuIGNsYXNzZXMuJztcbiAgICB9XG4gICAgY29uc3Qgc2NvcGUgPSB0aGlzLnNlbGVjdGlvbkNvdW50ID4gMCA/ICd0aGUgc2VsZWN0ZWQgY2VsbHMnIDogJ2FsbCBjZWxscyc7XG4gICAgY29uc3Qgc2NhbGVkID0gdGhpcy5oZWF0bWFwWlNjb3JlXG4gICAgICA/ICcgRWFjaCBnZW5lIGlzIHotc2NvcmVkIGFjcm9zcyB0aGUgY29sdW1ucywgc28gdGhlIGNvbG91ciBpcyBhYm92ZSBvciBiZWxvdyB0aGF0IGdlbmVcXCdzIG93biBhdmVyYWdlLidcbiAgICAgIDogJyBSYXcgbWVhbnMsIHNvIGEgaGlnaGx5LWV4cHJlc3NlZCBnZW5lIGRvbWluYXRlcyB0aGUgc2NhbGUuJztcbiAgICAvLyBTYXlpbmcgc28gbWF0dGVyczogd2l0aG91dCBpdCB0aGUgcGFuZWwgbG9va3MgbGlrZSB0aGUgd2hvbGUgY29sdW1uLlxuICAgIGNvbnN0IGNhcHBlZCA9IHRoaXMuaGVhdG1hcEhpZGRlbiA+IDBcbiAgICAgID8gYCBTaG93aW5nIHRoZSAke1NwYXRpYWxDaGFydHNDb21wb25lbnQuSEVBVE1BUF9NQVhfQ09MVU1OU30gc3Ryb25nZXN0IG9mIGBcbiAgICAgICAgKyBgJHtTcGF0aWFsQ2hhcnRzQ29tcG9uZW50LkhFQVRNQVBfTUFYX0NPTFVNTlMgKyB0aGlzLmhlYXRtYXBIaWRkZW59LCBgXG4gICAgICAgICsgJ3JhbmtlZCBieSB0aGUgbGFyZ2VzdCB2YWx1ZSBhbnkgcGlja2VkIGdlbmUgcmVhY2hlcy4nXG4gICAgICA6ICcnO1xuICAgIHJldHVybiBgTWVhbiBleHByZXNzaW9uIG9mIGVhY2ggZ2VuZSB3aXRoaW4gZWFjaCAke3RoaXMuZ3JvdXBCeSA/PyAnZ3JvdXAnfSwgb3ZlciAke3Njb3BlfS5gXG4gICAgICArIGAke3NjYWxlZH0ke2NhcHBlZH1gO1xuICB9XG5cbiAgLyoqIEdlbmUgcm93cyBmb3IgdGhlIGhlYXRtYXAuICovXG4gIGFzeW5jIG9uSGVhdG1hcEdlbmVzKG5hbWVzOiBzdHJpbmdbXSk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuaGVhdG1hcEdlbmVzID0gbmFtZXMgPz8gW107XG4gICAgLy8gQ2hvc2VuIGdlbmVzIG11c3Qgc3RheSBpbiB0aGUgb3B0aW9ucyBvciB0aGUgY29udHJvbCBjYW5ub3QgbGFiZWwgaXRzIGNoaXBzLlxuICAgIHRoaXMucmVmcmVzaEdlbmVPcHRpb25zKCk7XG4gICAgYXdhaXQgdGhpcy5sb2FkSGVhdG1hcEdlbmVzKCk7XG4gICAgdm9pZCB0aGlzLnJlbmRlcigpO1xuICB9XG5cbiAgLyoqXG4gICAqIEZldGNoIGFueSBnZW5lIHRoZSBoZWF0bWFwIG5lZWRzIGFuZCBkb2VzIG5vdCBhbHJlYWR5IGhvbGQuXG4gICAqXG4gICAqIFNlcXVlbmNlZCBvbiB0aGUgc2FtZSB0b2tlbiB0aGUgY29sb3VyLXNvdXJjZSBsb2FkIHVzZXM6IGVhY2ggdmVjdG9yIGlzIGFcbiAgICogc2VwYXJhdGUgcmVxdWVzdCwgYW5kIGEgc2xvd2VyIG9uZSBtdXN0IG5vdCBwYWludCByb3dzIGZvciBhIGdlbmUgbGlzdCB0aGVcbiAgICogdXNlciBoYXMgYWxyZWFkeSBtb3ZlZCBvbiBmcm9tLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBsb2FkSGVhdG1hcEdlbmVzKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGNvbnRyb2xzID0gdGhpcy5jb250cm9scztcbiAgICBpZiAoIWNvbnRyb2xzKSByZXR1cm47XG4gICAgY29uc3QgbWlzc2luZyA9IHRoaXMuaGVhdG1hcEdlbmVzLmZpbHRlcigobikgPT4gIXRoaXMuZ2VuZUNhY2hlLmhhcyhuKSk7XG4gICAgaWYgKG1pc3NpbmcubGVuZ3RoID09PSAwKSByZXR1cm47XG4gICAgY29uc3QgbWluZSA9ICsrdGhpcy50b2tlbjtcbiAgICB0aGlzLmJ1c3kgPSB0cnVlO1xuICAgIHRyeSB7XG4gICAgICBmb3IgKGNvbnN0IG5hbWUgb2YgbWlzc2luZykge1xuICAgICAgICBjb25zdCB2YWx1ZXMgPSBhd2FpdCBjb250cm9scy5jb250aW51b3VzVmFsdWVzKHsga2luZDogJ2ZlYXR1cmUnLCBuYW1lIH0pO1xuICAgICAgICBpZiAobWluZSAhPT0gdGhpcy50b2tlbikgcmV0dXJuO1xuICAgICAgICB0aGlzLmdlbmVDYWNoZS5zZXQobmFtZSwgdmFsdWVzKTtcbiAgICAgIH1cbiAgICAgIHRoaXMubm90aWNlID0gbnVsbDtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGlmIChtaW5lICE9PSB0aGlzLnRva2VuKSByZXR1cm47XG4gICAgICB0aGlzLm5vdGljZSA9IGBBIGdlbmUgY291bGQgbm90IGJlIGNoYXJ0ZWQ6ICR7KGVyciBhcyBFcnJvcik/Lm1lc3NhZ2UgPz8gZXJyfWA7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIGlmIChtaW5lID09PSB0aGlzLnRva2VuKSB0aGlzLmJ1c3kgPSBmYWxzZTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBvbkdyb3VwQnkobmFtZTogc3RyaW5nIHwgbnVsbCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuZ3JvdXBCeSA9IG5hbWU7XG4gICAgaWYgKCFuYW1lIHx8ICF0aGlzLmNvbnRyb2xzKSB7XG4gICAgICB0aGlzLmdyb3VwaW5nID0gbnVsbDtcbiAgICAgIHZvaWQgdGhpcy5yZW5kZXIoKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gU2VxdWVuY2VkOiBwaWNrIEEgdGhlbiBCIGFuZCBhIHNsb3dlciBBIHdvdWxkIG90aGVyd2lzZSBsYW5kIGxhc3QsIGNoYXJ0aW5nXG4gICAgLy8gQSdzIGNhdGVnb3JpZXMgdW5kZXIgYSBkcm9wZG93biB0aGF0IHNheXMgQi5cbiAgICBjb25zdCBtaW5lID0gKyt0aGlzLmdyb3VwVG9rZW47XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHZpZXcgPSBhd2FpdCB0aGlzLmNvbnRyb2xzLmNhdGVnb3JpY2FsVmlldyhuYW1lKTtcbiAgICAgIGlmIChtaW5lICE9PSB0aGlzLmdyb3VwVG9rZW4pIHJldHVybjtcbiAgICAgIHRoaXMuZ3JvdXBpbmcgPSB7IGNvZGVzOiB2aWV3LmNvZGVzLCBjYXRlZ29yaWVzOiB2aWV3LmNhdGVnb3JpZXMsIGNvbG9yczogdmlldy5jb2xvcnMgfTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIGlmIChtaW5lICE9PSB0aGlzLmdyb3VwVG9rZW4pIHJldHVybjtcbiAgICAgIHRoaXMuZ3JvdXBpbmcgPSBudWxsO1xuICAgICAgdGhpcy5ncm91cEJ5ID0gbnVsbDtcbiAgICB9XG4gICAgdm9pZCB0aGlzLnJlbmRlcigpO1xuICB9XG5cbiAgLyoqIFRydWUgd2hlbiB0aGUgY3VycmVudCBraW5kIHdvdWxkIHJlYWQgYmV0dGVyIHdpdGggYSBncm91cGluZyBjaG9zZW4uICovXG4gIGdldCBzdWdnZXN0c0dyb3VwaW5nKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBiZW5lZml0c0Zyb21Hcm91cGluZyh0aGlzLmtpbmQpICYmICF0aGlzLmdyb3VwQnkgJiYgdGhpcy5ncm91cE9wdGlvbnMubGVuZ3RoID4gMTtcbiAgfVxuXG4gIGdldCBzdWJqZWN0KCk6IHN0cmluZyB7XG4gICAgaWYgKCF0aGlzLmNvbG9yQnkpIHJldHVybiAnJztcbiAgICByZXR1cm4gdGhpcy5jb2xvckJ5LmtpbmQgPT09ICdmZWF0dXJlJyA/IGBnZW5lICR7dGhpcy5jb2xvckJ5Lm5hbWV9YCA6IHRoaXMuY29sb3JCeS5uYW1lO1xuICB9XG5cbiAgLy8g4pSA4pSAIGludGVybmFscyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICAvKiogRmV0Y2ggdGhlIGFjdGl2ZSBjb2xvdXIgc291cmNlJ3MgdmVjdG9yLCB0aGVuIGRyYXcuICovXG4gIHByaXZhdGUgYXN5bmMgcmVsb2FkKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGNvbnRyb2xzID0gdGhpcy5jb250cm9scztcbiAgICBjb25zdCBzb3VyY2UgPSB0aGlzLnZpZXcuY29sb3JCeTtcbiAgICBjb25zdCBtaW5lID0gKyt0aGlzLnRva2VuO1xuICAgIGlmICghY29udHJvbHMgfHwgIXNvdXJjZSkge1xuICAgICAgdGhpcy52YWx1ZXMgPSBudWxsO1xuICAgICAgdGhpcy5jYXRlZ29yaWNhbCA9IG51bGw7XG4gICAgICB0aGlzLm5vdGljZSA9IGNvbnRyb2xzXG4gICAgICAgID8gJ0NvbG91ciB0aGUgbWFwIGJ5IGEgY29sdW1uIG9yIGEgZ2VuZSB0byBjaGFydCBpdHMgZGlzdHJpYnV0aW9uLidcbiAgICAgICAgOiBudWxsO1xuICAgICAgdm9pZCB0aGlzLnJlbmRlcigpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmJ1c3kgPSB0cnVlO1xuICAgIC8vIEEgY2F0ZWdvcmljYWwgY29sdW1uIGNoYXJ0cyBhcyBDT1VOVFMgcGVyIGNhdGVnb3J5IOKAlCBhc2tlZCBmb3IgYnkgbmFtZVxuICAgIC8vIHJhdGhlciB0aGFuIGRpc2NvdmVyZWQgYnkgY2F0Y2hpbmcgdGhlIGNvbnRpbnVvdXMgZmV0Y2gncyBlcnJvciwgc28gYVxuICAgIC8vIGdlbnVpbmUgZmFpbHVyZSBzdGlsbCByZWFkcyBhcyBhIGZhaWx1cmUuXG4gICAgY29uc3QgaXNDYXRlZ29yaWNhbCA9IHNvdXJjZS5raW5kID09PSAnY29sdW1uJ1xuICAgICAgJiYgKGNvbnRyb2xzLmNhdGVnb3JpY2FsQ29sdW1ucygpID8/IFtdKS5pbmNsdWRlcyhzb3VyY2UubmFtZSk7XG4gICAgdHJ5IHtcbiAgICAgIGlmIChpc0NhdGVnb3JpY2FsKSB7XG4gICAgICAgIGNvbnN0IHZpZXcgPSBhd2FpdCBjb250cm9scy5jYXRlZ29yaWNhbFZpZXcoc291cmNlLm5hbWUpO1xuICAgICAgICBpZiAobWluZSAhPT0gdGhpcy50b2tlbikgcmV0dXJuOyAvLyBzdXBlcnNlZGVkXG4gICAgICAgIHRoaXMuY2F0ZWdvcmljYWwgPSB7IGNvZGVzOiB2aWV3LmNvZGVzLCBjYXRlZ29yaWVzOiB2aWV3LmNhdGVnb3JpZXMsIGNvbG9yczogdmlldy5jb2xvcnMgfTtcbiAgICAgICAgdGhpcy52YWx1ZXMgPSBudWxsO1xuICAgICAgICB0aGlzLmtpbmQgPSAnY291bnRzJztcbiAgICAgICAgdGhpcy5ub3RpY2UgPSBudWxsO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3QgdmFsdWVzID0gYXdhaXQgY29udHJvbHMuY29udGludW91c1ZhbHVlcyhzb3VyY2UpO1xuICAgICAgICBpZiAobWluZSAhPT0gdGhpcy50b2tlbikgcmV0dXJuO1xuICAgICAgICB0aGlzLnZhbHVlcyA9IHZhbHVlcztcbiAgICAgICAgdGhpcy5jYXRlZ29yaWNhbCA9IG51bGw7XG4gICAgICAgIGlmICh0aGlzLmtpbmQgPT09ICdjb3VudHMnKSB0aGlzLmtpbmQgPSAnaGlzdG9ncmFtJztcbiAgICAgICAgdGhpcy5ub3RpY2UgPSBudWxsO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgaWYgKG1pbmUgIT09IHRoaXMudG9rZW4pIHJldHVybjtcbiAgICAgIHRoaXMudmFsdWVzID0gbnVsbDtcbiAgICAgIHRoaXMuY2F0ZWdvcmljYWwgPSBudWxsO1xuICAgICAgdGhpcy5ub3RpY2UgPSBgXCIke3NvdXJjZS5uYW1lfVwiIGNvdWxkIG5vdCBiZSBjaGFydGVkOiAkeyhlcnIgYXMgRXJyb3IpPy5tZXNzYWdlID8/IGVycn1gO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBpZiAobWluZSA9PT0gdGhpcy50b2tlbikgdGhpcy5idXN5ID0gZmFsc2U7XG4gICAgfVxuICAgIHZvaWQgdGhpcy5yZW5kZXIoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgZW1iZWRkaW5nIHNjYXR0ZXIg4oCUIGEgVU1BUCBvdmVyIHRoZSBzYW1lIG9ic2VydmF0aW9ucy5cbiAgICpcbiAgICogQW5zd2VycyB3aGF0IHRoZSBtYXAgY2Fubm90OiB0aGUgbWFwIHNheXMgV0hFUkUgYSBwb3B1bGF0aW9uIHNpdHMsIHRoaXMgc2F5cyB3aGljaFxuICAgKiBwb3B1bGF0aW9ucyB0aGVyZSBhcmUgYW5kIGhvdyBjbG9zZSB0aGV5IGFyZSBpbiBleHByZXNzaW9uLlxuICAgKlxuICAgKiBMaW5rZWQgdG8gdGhlIG1hcCB3aXRob3V0IGFza2luZyBmb3IgYW55dGhpbmc6IGl0IHJldXNlcyBgdGhpcy5jYXRlZ29yaWNhbGAsIHRoZSBzYW1lXG4gICAqIGxvYWRlZCBjb2xvdXJpbmcgdGhlIGNvdW50cyBjaGFydCBkcmF3cyBmcm9tLCBzbyB0aGUgdHdvIGNhbm5vdCBkaXNhZ3JlZSBhYm91dCB3aGF0IGFcbiAgICogY29sb3VyIG1lYW5zIGFuZCBubyBzZWNvbmQgZmV0Y2ggaGFwcGVucy4gQSBzZWxlY3Rpb24gZGltcyB0aGUgcmVzdCByYXRoZXIgdGhhblxuICAgKiBkcm9wcGluZyBpdCDigJQgdGhlIGVtYmVkZGluZydzIHNoYXBlIGlzIHRoZSBjb250ZXh0IHRoYXQgbWFrZXMgYSBzZWxlY3Rpb24gbGVnaWJsZS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcmVuZGVyRW1iZWRkaW5nKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGNvbnRyb2xzID0gdGhpcy5jb250cm9scztcbiAgICBjb25zdCBtZXRhID0gdGhpcy5lbWJlZGRpbmc7XG4gICAgaWYgKCFjb250cm9scyB8fCAhbWV0YSkge1xuICAgICAgdGhpcy5wdXJnZVBsb3QoKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKCFjb250cm9scy5nZXRFbWJlZGRpbmcpIHtcbiAgICAgIHRoaXMubm90aWNlID0gJ1RoaXMgZGF0YSBzb3VyY2UgZG9lcyBub3Qgc2VydmUgZW1iZWRkaW5ncy4nO1xuICAgICAgdGhpcy5wdXJnZVBsb3QoKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBTZXF1ZW5jZWQgb24gdGhlIHNoYXJlZCB0b2tlbjogdGhlIGNvb3JkaW5hdGVzIGFyZSBhIHJvdW5kLXRyaXAsIGFuZCBhIHNsb3dlciBvbmVcbiAgICAvLyBtdXN0IG5vdCBwYWludCBvdmVyIGEga2luZCB0aGUgdXNlciBoYXMgYWxyZWFkeSBtb3ZlZCBvbiBmcm9tLlxuICAgIGNvbnN0IG1pbmUgPSArK3RoaXMudG9rZW47XG4gICAgLy8gQ29tcHV0ZWQgaGVyZSwgbm90IHNlcnZlZDogYSBsb2NhbCByZXN1bHQgaXMgdGhlIHNhbWUgc2hhcGUgYXMgYSBmZXRjaGVkIG9uZSwgc29cbiAgICAvLyBldmVyeXRoaW5nIGRvd25zdHJlYW0g4oCUIGNvbG91cmluZywgbGFzc28gc2VsZWN0aW9uLCB0aGUgY2FtZXJhIOKAlCBpcyB1bmNoYW5nZWQuXG4gICAgY29uc3QgbG9jYWwgPSB0aGlzLmNvbXB1dGVkLmdldChtZXRhLm5hbWUpO1xuICAgIGlmIChsb2NhbCkge1xuICAgICAgdGhpcy5lbWJlZGRpbmdDb29yZHMgPSBsb2NhbDtcbiAgICB9IGVsc2UgaWYgKHRoaXMuaXNDb21wdXRhYmxlKSB7XG4gICAgICAvLyBPZmZlcmVkIGJ1dCBub3QgeWV0IGNvbXB1dGVkLiBUaGUgcGFuZWwgc2hvd3MgdGhlIENvbXB1dGUgYnV0dG9uOyBkcmF3aW5nIG5vdGhpbmdcbiAgICAgIC8vIGlzIHJpZ2h0LCBhbmQgYW4gZXJyb3Igd291bGQgYmUgd3Jvbmcg4oCUIHRoZXJlIGlzIG5vIGZhaWx1cmUgaGVyZS5cbiAgICAgIHRoaXMucHVyZ2VQbG90KCk7XG4gICAgICB0aGlzLm5vdGljZSA9IG51bGw7XG4gICAgICByZXR1cm47XG4gICAgfSBlbHNlIGlmICghdGhpcy5lbWJlZGRpbmdDb29yZHMgfHwgdGhpcy5lbWJlZGRpbmdDb29yZHMubWV0YS5uYW1lICE9PSBtZXRhLm5hbWUpIHtcbiAgICAgIHRoaXMuYnVzeSA9IHRydWU7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBsb2FkZWQgPSBhd2FpdCBjb250cm9scy5nZXRFbWJlZGRpbmcobWV0YS5uYW1lKTtcbiAgICAgICAgaWYgKG1pbmUgIT09IHRoaXMudG9rZW4pIHJldHVybjtcbiAgICAgICAgdGhpcy5lbWJlZGRpbmdDb29yZHMgPSBsb2FkZWQ7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgaWYgKG1pbmUgIT09IHRoaXMudG9rZW4pIHJldHVybjtcbiAgICAgICAgdGhpcy5ub3RpY2UgPSBgQ291bGQgbm90IGxvYWQgJHttZXRhLmxhYmVsID8/IG1ldGEubmFtZX06IGBcbiAgICAgICAgICArIGAkeyhlcnIgYXMgRXJyb3IpPy5tZXNzYWdlID8/IGVycn1gO1xuICAgICAgICB0aGlzLnB1cmdlUGxvdCgpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICBpZiAobWluZSA9PT0gdGhpcy50b2tlbikgdGhpcy5idXN5ID0gZmFsc2U7XG4gICAgICB9XG4gICAgfVxuICAgIGNvbnN0IGNvb3JkcyA9IHRoaXMuZW1iZWRkaW5nQ29vcmRzO1xuICAgIGlmICghY29vcmRzKSByZXR1cm47XG5cbiAgICBjb25zdCB0YXJnZXQgPSB0aGlzLnBsb3RUYXJnZXQ7XG4gICAgY29uc3QgZWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0YXJnZXQpO1xuICAgIGlmICghZWwpIHJldHVybjsgLy8gdGhlIHdpbmRvdyBpcyBjbG9zZWQsIG9yIG5vdCByZW5kZXJlZCB5ZXRcbiAgICBjb25zdCB2aWV3ID0gdGhpcy5saXZlVmlldyhlbCk7XG5cbiAgICB0aGlzLm5vdGljZSA9IG51bGw7XG4gICAgY29uc3QgaW5wdXQgPSB7XG4gICAgICB4OiBjb29yZHMueCxcbiAgICAgIHk6IGNvb3Jkcy55LFxuICAgICAgLy8gVGhlIHRoaXJkIGRpbWVuc2lvbiB3aGVuIHRoZSBlbWJlZGRpbmcgaGFzIG9uZSDigJQgd2hhdCBzd2l0Y2hlcyB0aGUgcGxvdCB0byBhXG4gICAgICAvLyByb3RhdGFibGUgM0Qgc2NhdHRlci4gRGVjb2RlZCBvbmx5IGZvciBhIDMtZGltIGVtYmVkZGluZywgc28gaXRzIHByZXNlbmNlIGhlcmVcbiAgICAgIC8vIGlzIHRoZSBzYW1lIHF1ZXN0aW9uIGFzIGBtZXRhLmRpbXMgPT09IDNgLlxuICAgICAgLi4uKGNvb3Jkcy56ID8geyB6OiBjb29yZHMueiB9IDoge30pLFxuICAgICAgbGFiZWw6IG1ldGEubGFiZWwgPz8gbWV0YS5uYW1lLFxuICAgICAgZGVyaXZlZDogbWV0YS5kZXJpdmVkLFxuICAgICAgLy8gUHJlc2VudCBmb3IgYSBQQ0EsIGFic2VudCBmb3IgYSBVTUFQIOKAlCBzZWUgYHZhcmlhbmNlUmF0aW9gIG9uIHRoZSBtZXRhLlxuICAgICAgLi4uKG1ldGEudmFyaWFuY2VSYXRpbyA/IHsgdmFyaWFuY2VSYXRpbzogbWV0YS52YXJpYW5jZVJhdGlvIH0gOiB7fSksXG4gICAgICAuLi4odGhpcy5jYXRlZ29yaWNhbFxuICAgICAgICA/IHtcbiAgICAgICAgICBjYXRlZ29yaWVzOiB7XG4gICAgICAgICAgICBjb2RlczogdGhpcy5jYXRlZ29yaWNhbC5jb2RlcyxcbiAgICAgICAgICAgIG5hbWVzOiB0aGlzLmNhdGVnb3JpY2FsLmNhdGVnb3JpZXMsXG4gICAgICAgICAgICBjb2xvcnM6IHRoaXMuY2F0ZWdvcmljYWwuY29sb3JzLFxuICAgICAgICAgIH0sXG4gICAgICAgIH1cbiAgICAgICAgOiB7fSksXG4gICAgICAuLi4odGhpcy5zZWxlY3Rpb24uY291bnQgPiAwID8geyBzZWxlY3Rpb246IHRoaXMuc2VsZWN0aW9uLm1hc2sgfSA6IHt9KSxcbiAgICAgIC8vIFdoYXRldmVyIHZpZXcgdGhlIHVzZXIgaGFzIHNldCB1cCwgcmVhZCBmcm9tIHRoZSBMSVZFIHBsb3Qg4oCUIHdoaWNoIGlzIHdoZXJlXG4gICAgICAvLyByb3RhdGluZyBhbmQgem9vbWluZyBsZWF2ZSBpdC5cbiAgICAgIC4uLih2aWV3ID8geyB2aWV3IH0gOiB7fSksXG4gICAgfTtcbiAgICBhd2FpdCB0aGlzLmRyYXcoYnVpbGRFbWJlZGRpbmdUcmFjZXMoaW5wdXQpLCBlbWJlZGRpbmdMYXlvdXQoaW5wdXQpLCBFTUJFRERJTkdfQ09ORklHLCB0YXJnZXQpO1xuICAgIHRoaXMuYmluZEVtYmVkZGluZ1NlbGVjdGlvbih0YXJnZXQpO1xuICB9XG5cbiAgLyoqXG4gICAqIFR1cm4gYSBsYXNzbyBpbiB0aGUgZW1iZWRkaW5nIGludG8gYSBzZWxlY3Rpb24gb2Ygb2JzZXJ2YXRpb25zLlxuICAgKlxuICAgKiBUaGlzIGlzIHdoYXQgbWFrZXMgdHdvIHZpZXdzIG9mIG9uZSBkYXRhc2V0IHdvcnRoIGhhdmluZyBzaWRlIGJ5IHNpZGU6IGRyYXcgcm91bmQgYVxuICAgKiBjbHVzdGVyIGhlcmUgYW5kIHRob3NlIGNlbGxzIGxpZ2h0IHVwIG9uIHRoZSB0aXNzdWUsIGJlY2F1c2UgZXZlcnkgdmlldyByZWFkcyB0aGVcbiAgICogc2FtZSBtYXNrLlxuICAgKlxuICAgKiBCb3VuZCBhZnRlciBlYWNoIGRyYXcsIGFuZCB0aGUgcHJldmlvdXMgbGlzdGVuZXIgcmVtb3ZlZCBmaXJzdDogYFBsb3RseS5yZWFjdGBcbiAgICogcHJlc2VydmVzIGhhbmRsZXJzLCBzbyByZS1iaW5kaW5nIHdpdGhvdXQgcmVtb3Zpbmcgd291bGQgZmlyZSB0aGUgc2VsZWN0aW9uIG9uY2UgcGVyXG4gICAqIHJlZHJhdyB0aGUgcGxvdCBoYWQgZXZlciBoYWQuXG4gICAqL1xuICBwcml2YXRlIGJpbmRFbWJlZGRpbmdTZWxlY3Rpb24oZGl2OiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCBlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGRpdikgYXMgKFBsb3RseS5QbG90bHlIVE1MRWxlbWVudCB8IG51bGwpO1xuICAgIGlmICghZWw/Lm9uKSByZXR1cm47XG4gICAgZWwucmVtb3ZlQWxsTGlzdGVuZXJzPy4oJ3Bsb3RseV9zZWxlY3RlZCcpO1xuICAgIGVsLnJlbW92ZUFsbExpc3RlbmVycz8uKCdwbG90bHlfZGVzZWxlY3QnKTtcbiAgICBlbC5vbigncGxvdGx5X3NlbGVjdGVkJywgKGV2KSA9PiB7XG4gICAgICAvLyBBIGxhc3NvIHRoYXQgc2VsZWN0cyBub3RoaW5nIGFycml2ZXMgYXMgYW4gZXZlbnQgd2l0aCBubyBwb2ludHM7IHRyZWF0IGl0IGFzIGFcbiAgICAgIC8vIGNsZWFyLCB3aGljaCBpcyB3aGF0IGRyYWdnaW5nIGFuIGVtcHR5IHBhdGNoIGxvb2tzIGxpa2UgaXQgc2hvdWxkIGRvLlxuICAgICAgY29uc3QgcG9pbnRzID0gKGV2IGFzIHsgcG9pbnRzPzogcmVhZG9ubHkgdW5rbm93bltdIH0gfCB1bmRlZmluZWQpPy5wb2ludHMgPz8gW107XG4gICAgICBjb25zdCBpbmRpY2VzOiBudW1iZXJbXSA9IFtdO1xuICAgICAgZm9yIChjb25zdCBwIG9mIHBvaW50cykge1xuICAgICAgICAvLyBUaGUgb2JzZXJ2YXRpb24gaW5kZXggcmlkZXMgb24gYGN1c3RvbWRhdGFgIOKAlCBzZWUgYGJ1aWxkRW1iZWRkaW5nVHJhY2VzYC4gQSBwb2ludCdzXG4gICAgICAgIC8vIGluZGV4IHdpdGhpbiBpdHMgdHJhY2UgaXMgbm90IHRoZSBvYnNlcnZhdGlvbiBvbmNlIHRoZSBwb2ludHMgYXJlIHNwbGl0IGJ5XG4gICAgICAgIC8vIGNhdGVnb3J5LCBzbyB0aGlzIGlzIHRoZSBvbmx5IGNvcnJlY3Qgc291cmNlLlxuICAgICAgICBjb25zdCBpZCA9IChwIGFzIHsgY3VzdG9tZGF0YT86IHVua25vd24gfSkuY3VzdG9tZGF0YTtcbiAgICAgICAgaWYgKHR5cGVvZiBpZCA9PT0gJ251bWJlcicpIGluZGljZXMucHVzaChpZCk7XG4gICAgICB9XG4gICAgICBpZiAoaW5kaWNlcy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgdGhpcy5jb250cm9scz8uY2xlYXJTZWxlY3Rpb24oKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhpcy5jb250cm9scz8uc2VsZWN0SW5kaWNlcyhpbmRpY2VzKTtcbiAgICB9KTtcbiAgICAvLyBUaGUgbW9kZWJhcidzIGRlc2VsZWN0LCBhbmQgYSBwbGFpbiBjbGljayBvbiBlbXB0eSBzcGFjZS5cbiAgICBlbC5vbigncGxvdGx5X2Rlc2VsZWN0JywgKCkgPT4gdGhpcy5jb250cm9scz8uY2xlYXJTZWxlY3Rpb24oKSk7XG4gIH1cblxuICAvKipcbiAgICogRHJvcCB0aGUgZW1iZWRkaW5nJ3Mgc2VsZWN0aW9uIGhhbmRsZXJzIGZyb20gYSBkaXYgYW5vdGhlciBraW5kIGlzIGFib3V0IHRvIHVzZS5cbiAgICpcbiAgICogVGhlIGRldGFjaGVkIHdpbmRvdyBvdXRsaXZlcyBhbnkgb25lIGtpbmQg4oCUIHN3aXRjaGluZyB0YWJzIHN3YXBzIGl0cyBjb250ZW50IGFuZFxuICAgKiBrZWVwcyB0aGUgZGl2IOKAlCBzbyB3aXRob3V0IHRoaXMgdGhlIGxhc3NvIGhhbmRsZXJzIGJvdW5kIGZvciBhIFVNQVAgc3RheSBsaXZlIHVuZGVyXG4gICAqIHRoZSBjb3VudHMgcGxvdCB0aGF0IHJlcGxhY2VzIGl0LiBgcGxvdGx5X2Rlc2VsZWN0YCBmaXJlcyBvbiBhIHBsYWluIGNsaWNrIGluIEFOWVxuICAgKiBQbG90bHkgY2hhcnQsIHNvIHRoZSBmaXJzdCBjbGljayBvbiBhIGJhciB3b3VsZCBzaWxlbnRseSBjbGVhciB0aGUgbWFwJ3Mgc2VsZWN0aW9uLlxuICAgKi9cbiAgcHJpdmF0ZSB1bmJpbmRFbWJlZGRpbmdTZWxlY3Rpb24oZGl2OiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCBlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGRpdikgYXMgKFBsb3RseS5QbG90bHlIVE1MRWxlbWVudCB8IG51bGwpO1xuICAgIGlmICghZWw/LnJlbW92ZUFsbExpc3RlbmVycykgcmV0dXJuO1xuICAgIGVsLnJlbW92ZUFsbExpc3RlbmVycygncGxvdGx5X3NlbGVjdGVkJyk7XG4gICAgZWwucmVtb3ZlQWxsTGlzdGVuZXJzKCdwbG90bHlfZGVzZWxlY3QnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgdmlldyB0aGUgdXNlciBoYXMgc2V0IHVwIG9uIHRoZSBsaXZlIHBsb3QsIHRvIGNhcnJ5IGFjcm9zcyBhIHJlZHJhdy5cbiAgICpcbiAgICogYFBsb3RseS5yZWFjdGAgcmVzZXRzIGEgM0Qgc2NlbmUgY2FtZXJhIGFuZCBhbnkgem9vbWVkIDJEIHJhbmdlIHVubGVzcyB0aGUgbGF5b3V0XG4gICAqIGNhcnJpZXMgdGhlbSwgc28gc2VsZWN0aW5nIGEgY2F0ZWdvcnkgb3IgcmVjb2xvdXJpbmcgd291bGQgdGhyb3cgYXdheSBhbiBvcmllbnRhdGlvblxuICAgKiB0aGF0IHRvb2sgd29yayB0byBmaW5kLiBSb3RhdGluZyBhIGNsb3VkIHRvIHNlZSBhIHN0cnVjdHVyZSBhbmQgbG9zaW5nIGl0IG9uIHRoZSBuZXh0XG4gICAqIGNsaWNrIG1ha2VzIHRoZSBwbG90IHVzZWxlc3MgZm9yIHdoYXQgaXQgaXMgZm9yLlxuICAgKi9cbiAgcHJpdmF0ZSBsaXZlVmlldyhcbiAgICBlbDogSFRNTEVsZW1lbnQsXG4gICk6IHsgY2FtZXJhPzogdW5rbm93bjsgcmFuZ2VzPzogeyB4OiB1bmtub3duOyB5OiB1bmtub3duIH0gfSB8IG51bGwge1xuICAgIGNvbnN0IGZ1bGwgPSAoZWwgYXMge1xuICAgICAgX2Z1bGxMYXlvdXQ/OiB7XG4gICAgICAgIHNjZW5lPzogeyBjYW1lcmE/OiB1bmtub3duIH07XG4gICAgICAgIHhheGlzPzogeyByYW5nZT86IHVua25vd247IGF1dG9yYW5nZT86IGJvb2xlYW4gfTtcbiAgICAgICAgeWF4aXM/OiB7IHJhbmdlPzogdW5rbm93bjsgYXV0b3JhbmdlPzogYm9vbGVhbiB9O1xuICAgICAgfTtcbiAgICB9KS5fZnVsbExheW91dDtcbiAgICBpZiAoIWZ1bGwpIHJldHVybiBudWxsOyAvLyBub3RoaW5nIGRyYXduIGhlcmUgeWV0XG4gICAgaWYgKGZ1bGwuc2NlbmU/LmNhbWVyYSkgcmV0dXJuIHsgY2FtZXJhOiBmdWxsLnNjZW5lLmNhbWVyYSB9O1xuICAgIC8vIDJEOiBvbmx5IG9uY2UgdGhlIHVzZXIgaGFzIGFjdHVhbGx5IHpvb21lZC4gUGFzc2luZyBhbiBhdXRvcmFuZ2VkIHJhbmdlIGJhY2sgd291bGRcbiAgICAvLyBmcmVlemUgdGhlIGF4ZXMgYW5kIHN0b3AgdGhlIHBsb3QgcmUtZml0dGluZyB3aGVuIHRoZSBkYXRhIGNoYW5nZXMuXG4gICAgY29uc3Qgem9vbWVkID0gZnVsbC54YXhpcz8uYXV0b3JhbmdlID09PSBmYWxzZSAmJiBmdWxsLnlheGlzPy5hdXRvcmFuZ2UgPT09IGZhbHNlO1xuICAgIGlmICh6b29tZWQgJiYgZnVsbC54YXhpcz8ucmFuZ2UgJiYgZnVsbC55YXhpcz8ucmFuZ2UpIHtcbiAgICAgIHJldHVybiB7IHJhbmdlczogeyB4OiBmdWxsLnhheGlzLnJhbmdlLCB5OiBmdWxsLnlheGlzLnJhbmdlIH0gfTtcbiAgICB9XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvKipcbiAgICogV2hldGhlciB0aGUgcGxvdCBvbiBzY3JlZW4gZml4ZWQgaXRzIG93biBoZWlnaHQuXG4gICAqXG4gICAqIFRoZSBjb3VudHMgYW5kIGhlYXRtYXAgbGF5b3V0cyBzaXplIHRoZW1zZWx2ZXMgdG8gdGhlaXIgcm93IGNvdW50LCBzbyBpbiB0aGUgZGV0YWNoZWRcbiAgICogd2luZG93IHRoZXkgY2FuIGJlIFRBTExFUiB0aGFuIHRoZSB3aW5kb3cg4oCUIHdoaWNoIGhhcyB0byBzY3JvbGwgcmF0aGVyIHRoYW4gY2xpcC4gQW5cbiAgICogZW1iZWRkaW5nIGZpeGVzIG5vIGhlaWdodCBhbmQgZmlsbHMgdGhlIHdpbmRvdyBpbnN0ZWFkLiBUaGUgdGVtcGxhdGUgbmVlZHMgdG8ga25vd1xuICAgKiB3aGljaCwgYmVjYXVzZSB0aGUgdHdvIHdhbnQgb3Bwb3NpdGUgYGZsZXhgIGFuZCBgb3ZlcmZsb3dgLlxuICAgKi9cbiAgZ2V0IGhhc0ZpeGVkSGVpZ2h0KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmRyYXduSGVpZ2h0ICE9PSBudWxsO1xuICB9XG5cbiAgLyoqIFdoZXJlIHRoZSBhY3RpdmUga2luZCBkcmF3czogaXRzIG93biB3aW5kb3csIG9yIHRoZSBzaGFyZWQgY2hhcnQgZGl2LiAqL1xuICBnZXQgcGxvdFRhcmdldCgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLmRldGFjaGVkID8gdGhpcy5kZXRhY2hlZERpdiA6IHRoaXMuY2hhcnREaXY7XG4gIH1cblxuICAvKipcbiAgICogSGVhZGVyIGZvciB0aGUgZGV0YWNoZWQgd2luZG93IOKAlCB3aGF0IGl0IGlzIHNob3dpbmcsIG5hbWVkIGFzIGl0cyB0YWIgaXMuXG4gICAqXG4gICAqIFRoZSBlbWJlZGRpbmcgbmFtZXMgdGhlIGVtYmVkZGluZyByYXRoZXIgdGhhbiB0aGUgdGFiLCBiZWNhdXNlIFwiRW1iZWRkaW5nXCIgYmVzaWRlIGFcbiAgICogd2luZG93IGRyYXdpbmcgYSB0LVNORSBzYXlzIGxlc3MgdGhhbiBcInQtU05FXCIgZG9lcy5cbiAgICovXG4gIGdldCBkZXRhY2hlZFRpdGxlKCk6IHN0cmluZyB7XG4gICAgaWYgKHRoaXMua2luZCA9PT0gJ2VtYmVkZGluZycpIHtcbiAgICAgIHJldHVybiB0aGlzLmVtYmVkZGluZz8ubGFiZWwgPz8gdGhpcy5lbWJlZGRpbmc/Lm5hbWUgPz8gJ0VtYmVkZGluZyc7XG4gICAgfVxuICAgIGNvbnN0IGxhYmVsID0gdGhpcy5raW5kT3B0aW9ucy5maW5kKChrKSA9PiBrLnZhbHVlID09PSB0aGlzLmtpbmQpPy5sYWJlbCA/PyAnQ2hhcnQnO1xuICAgIHJldHVybiB0aGlzLnN1YmplY3QgPyBgJHtsYWJlbH0gwrcgJHt0aGlzLnN1YmplY3R9YCA6IGxhYmVsO1xuICB9XG5cbiAgLyoqXG4gICAqIE1vdmUgdGhlIGFjdGl2ZSBraW5kIGJldHdlZW4gaXRzIG93biB3aW5kb3cgYW5kIHRoZSBwYW5lbC5cbiAgICpcbiAgICogUHVyZ2VzIHRoZSBkaXYgaXQgaXMgTEVBVklORyBmaXJzdC4gUGxvdGx5IGtlZXBzIHBlci1kaXYgc3RhdGUsIGFuZCBhIGdyYXBoIGxlZnRcbiAgICogYmVoaW5kIGluIGEgZGl2IHRoYXQgQW5ndWxhciB0aGVuIHJlbW92ZXMgbGVha3MgaXRzIFdlYkdMIGNvbnRleHQg4oCUIGFuZCBpZiB0aGUgZGl2XG4gICAqIGNvbWVzIGJhY2ssIHJlYWN0IHdvdWxkIHJlc2l6ZSBhIHBsb3Qgd2hvc2UgZGF0YSBiZWxvbmdzIHRvIHRoZSBvdGhlciBwbGFjZS5cbiAgICovXG4gIHRvZ2dsZURldGFjaGVkKCk6IHZvaWQge1xuICAgIGNvbnN0IGxlYXZpbmcgPSB0aGlzLnBsb3RUYXJnZXQ7XG4gICAgdGhpcy5kZXRhY2hlZCA9ICF0aGlzLmRldGFjaGVkO1xuICAgIHRyeSB7XG4gICAgICBQbG90bHkucHVyZ2UobGVhdmluZyk7XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBOb3RoaW5nIHdhcyBwbG90dGVkIHRoZXJlLlxuICAgIH1cbiAgICAvLyBHb2luZyBiYWNrIElOTElORSwgdGhlIGRpdiBhcHBlYXJzIHdpdGggdGhlIG5leHQgY2hhbmdlIGRldGVjdGlvbiwgc28gYSB0YXNrIGlzXG4gICAgLy8gZW5vdWdoLiBHb2luZyBPVVQsIHRoZSB3aW5kb3cncyBkaXYgZG9lcyBub3QgZXhpc3QgeWV0IOKAlCBQcmltZU5HIG1vdW50cyB0aGUgZGlhbG9nXG4gICAgLy8gd2l0aCBhIHRyYW5zaXRpb24g4oCUIHNvIHRoZSBkaWFsb2cncyBvd24gYG9uU2hvd2AgZHJpdmVzIHRoYXQgZHJhdyBpbnN0ZWFkLiBBIHNpbmdsZVxuICAgIC8vIGRlZmVycmVkIGF0dGVtcHQgZHJldyBub3RoaW5nIGFuZCBuZXZlciByZXRyaWVkLlxuICAgIGlmICghdGhpcy5kZXRhY2hlZCkgc2V0VGltZW91dCgoKSA9PiB2b2lkIHRoaXMucmVuZGVyKCksIDApO1xuICB9XG5cbiAgLyoqIFRoZSBkZXRhY2hlZCB3aW5kb3cgaXMgdXAgYW5kIGl0cyBkaXYgZXhpc3RzIOKAlCBub3cgdGhlIHBsb3QgY2FuIGJlIGRyYXduIGludG8gaXQuICovXG4gIG9uRGV0YWNoZWRXaW5kb3dTaG93bigpOiB2b2lkIHtcbiAgICB2b2lkIHRoaXMucmVuZGVyKCk7XG4gIH1cblxuICAvKiogUmUtZml0IHRoZSBkZXRhY2hlZCB3aW5kb3cncyBwbG90IGFmdGVyIGl0IGlzIHJlc2l6ZWQuICovXG4gIG9uRGV0YWNoZWRSZXNpemVFbmQoKTogdm9pZCB7XG4gICAgdGhpcy5yZWZpdCh0aGlzLmRldGFjaGVkRGl2KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWJ1aWxkIHRoZSB2aXNpYmxlIG9wdGlvbnM6IHRoZSBiZXN0IG1hdGNoZXMgZm9yIHdoYXQgaXMgdHlwZWQsIHBsdXMgd2hhdCBpcyBjaG9zZW4uXG4gICAqXG4gICAqIEEgY2FwcGVkIG11bHRpLXNlbGVjdCB0aGF0IGRyb3BzIGl0cyBvd24gc2VsZWN0aW9ucyBjYW5ub3QgcmVzb2x2ZSB0aGVpciBsYWJlbHMsIGFuZFxuICAgKiB0aGUgbW9kZWwgbG9zZXMgdGhlbSBvbiB0aGUgbmV4dCBjaGFuZ2Ug4oCUIHRoZSBjaGlwcyB2YW5pc2ggZm9yIG5vIHJlYXNvbiB0aGUgdXNlclxuICAgKiBjYW4gc2VlLlxuICAgKi9cbiAgcHJpdmF0ZSByZWZyZXNoR2VuZU9wdGlvbnMoKTogdm9pZCB7XG4gICAgdGhpcy5nZW5lT3B0aW9ucyA9IGdlbmVPcHRpb25zRm9yKHRoaXMuZ2VuZU5hbWVzLCB0aGlzLmdlbmVRdWVyeSwgdGhpcy5oZWF0bWFwR2VuZXMpXG4gICAgICAubWFwKChuKSA9PiAoeyBsYWJlbDogbiwgdmFsdWU6IG4gfSkpO1xuICB9XG5cbiAgLyoqIFRoZSBnZW5lIHBpY2tlcidzIGZpbHRlciBib3ggY2hhbmdlZDogc2VhcmNoIHRoZSByZXNpZGVudCBuYW1lcywgc2hvdyB0aGUgYmVzdC4gKi9cbiAgb25HZW5lRmlsdGVyKHF1ZXJ5OiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLmdlbmVRdWVyeSA9IHF1ZXJ5ID8/ICcnO1xuICAgIHRoaXMucmVmcmVzaEdlbmVPcHRpb25zKCk7XG4gIH1cblxuICAvKipcbiAgICogUm91Z2hseSBob3cgbG9uZyBhIHJ1biB3aWxsIHRha2UsIGluIHNlY29uZHMsIGZyb20gdGhlIG1lYXN1cmVkIGFuY2hvci5cbiAgICpcbiAgICogU2hvd24gYmVmb3JlIHRoZSBjbGljay4gQSBwcm9ncmVzcyBiYXIgdGVsbHMgeW91IGEgam9iIGlzIGdvaW5nOyBvbmx5IGFuIGVzdGltYXRlXG4gICAqIHRlbGxzIHlvdSB3aGV0aGVyIHRvIHN0YXJ0IGl0LlxuICAgKi9cbiAgZ2V0IGNvbXB1dGVFc3RpbWF0ZVNlY29uZHMoKTogbnVtYmVyIHtcbiAgICBjb25zdCBuID0gdGhpcy5vYnNlcnZhdGlvbkNvdW50O1xuICAgIGNvbnN0IHsgc2Vjb25kcywgb2JzZXJ2YXRpb25zIH0gPSBTcGF0aWFsQ2hhcnRzQ29tcG9uZW50LlRTTkVfU0VDT05EU19BVDtcbiAgICByZXR1cm4gTWF0aC5tYXgoMSwgTWF0aC5yb3VuZChzZWNvbmRzICogKG4gLyBvYnNlcnZhdGlvbnMpICoqIDIpKTtcbiAgfVxuXG4gIC8qKiBUaGF0IGVzdGltYXRlIGFzIHNvbWV0aGluZyB0byBwdXQgb24gYSBidXR0b24uICovXG4gIGdldCBjb21wdXRlRXN0aW1hdGVMYWJlbCgpOiBzdHJpbmcge1xuICAgIGNvbnN0IHNlY3MgPSB0aGlzLmNvbXB1dGVFc3RpbWF0ZVNlY29uZHM7XG4gICAgaWYgKHNlY3MgPCA5MCkgcmV0dXJuIGB+JHtzZWNzfXNgO1xuICAgIHJldHVybiBgfiR7TWF0aC5yb3VuZChzZWNzIC8gNjApfSBtaW5gO1xuICB9XG5cbiAgLyoqIFdoeSB0aGUgb3B0aW9uIGlzIGFic2VudCwgd2hlbiBhIGRhdGFzZXQgaXMgcGFzdCB0aGUgdGhyZXNob2xkLiAqL1xuICBnZXQgdHNuZVRvb0xhcmdlTm90ZSgpOiBzdHJpbmcge1xuICAgIGNvbnN0IG4gPSB0aGlzLm9ic2VydmF0aW9uQ291bnQ7XG4gICAgcmV0dXJuIGB0LVNORSBpcyBub3Qgb2ZmZXJlZCBoZXJlIGZvciAke24udG9Mb2NhbGVTdHJpbmcoKX0gb2JzZXJ2YXRpb25zIOKAlCBpdCBpcyBgXG4gICAgICArIGBxdWFkcmF0aWMsIHNvIGl0IHdvdWxkIHRha2Ugcm91Z2hseSAke3RoaXMuY29tcHV0ZUVzdGltYXRlTGFiZWx9IGluIHRoZSBicm93c2VyLiBgXG4gICAgICArICdDb21wdXRlIGl0IG9mZmxpbmUgYW5kIHNlcnZlIGl0IHdpdGggdGhlIGRhdGFzZXQuJztcbiAgfVxuXG4gIC8qKiBXaGV0aGVyIHRoZSBzZWxlY3RlZCBlbWJlZGRpbmcgaXMgb25lIHRoaXMgYnJvd3NlciB3b3VsZCBoYXZlIHRvIGNvbXB1dGUuICovXG4gIGdldCBpc0NvbXB1dGFibGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuICEhdGhpcy5lbWJlZGRpbmcgJiYgdGhpcy5lbWJlZGRpbmcubmFtZS5zdGFydHNXaXRoKCdsb2NhbDonKTtcbiAgfVxuXG4gIC8qKiBXaGV0aGVyIGl0IGhhcyBhbHJlYWR5IGJlZW4gY29tcHV0ZWQgaW4gdGhpcyBzZXNzaW9uLiAqL1xuICBnZXQgaXNDb21wdXRlZCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gISF0aGlzLmVtYmVkZGluZyAmJiB0aGlzLmNvbXB1dGVkLmhhcyh0aGlzLmVtYmVkZGluZy5uYW1lKTtcbiAgfVxuXG4gIGdldCBpc0NvbXB1dGluZygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gISF0aGlzLmNvbXB1dGVSdW4/LnJ1bm5pbmc7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZSB0aGUgc2VsZWN0ZWQgZW1iZWRkaW5nIGhlcmUsIGluIGEgd29ya2VyLlxuICAgKlxuICAgKiBUaGUgUENBIHNjb3JlcyBjb21lIGZyb20gdGhlIHBvcnQgYXMgYW4gb3JkaW5hcnkgZW1iZWRkaW5nIOKAlCB0aGUgc2VydmVyIGRlcml2ZXMgdGhlbSxcbiAgICogYmVjYXVzZSBQQ0EgbmVlZHMgdGhlIHdob2xlIGV4cHJlc3Npb24gbWF0cml4ICgxODUgTUIgZm9yIGEgVmlzaXVtIGRhdGFzZXQpIHdoaWxlXG4gICAqIHQtU05FIG5lZWRzIG9ubHkgdGhlIHNjb3JlcyAoMC41MSBNQikuIFNlbmRpbmcgdGhlIG1hdHJpeCB0byB0aGUgYnJvd3NlciB0byBzYXZlIGFcbiAgICogZmV3IHNlY29uZHMgb2YgYXJpdGhtZXRpYyB3b3VsZCBiZSBhIGZhciBzbG93ZXIgYW5zd2VyLlxuICAgKi9cbiAgYXN5bmMgY29tcHV0ZUVtYmVkZGluZygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBtZXRhID0gdGhpcy5lbWJlZGRpbmc7XG4gICAgY29uc3QgY29udHJvbHMgPSB0aGlzLmNvbnRyb2xzO1xuICAgIGlmICghbWV0YSB8fCAhY29udHJvbHM/LmdldEVtYmVkZGluZyB8fCB0aGlzLmlzQ29tcHV0aW5nKSByZXR1cm47XG4gICAgdGhpcy5jb21wdXRlRXJyb3IgPSBudWxsO1xuICAgIHRoaXMuY29tcHV0ZU1lc3NhZ2UgPSBudWxsO1xuICAgIHRoaXMuY29tcHV0ZUZyYWN0aW9uID0gbnVsbDtcbiAgICB0aGlzLmNvbXB1dGVCYWNrZW5kID0gbnVsbDtcblxuICAgIC8vIEV2ZXJ5IGVhcmx5IHJldHVybiBhbmQgZXZlcnkgYXdhaXQgYmVsb3cgaXMgY2hlY2tlZCBhZ2FpbnN0IHRoaXMuIGBydW5gIGlzIGhlbGRcbiAgICAvLyBsb2NhbGx5IGFzIHdlbGwgYXMgb24gdGhlIGluc3RhbmNlOiBhZnRlciBhbiBhd2FpdCBgdGhpcy5jb21wdXRlUnVuYCBtYXkgYWxyZWFkeSBiZVxuICAgIC8vIGEgbmV3ZXIgcnVuJ3MsIGFuZCBjbGVhcmluZyBvciB0ZXJtaW5hdGluZyB0aGF0IG9uZSBpcyBob3cgYSBmcmVzaCBjb21wdXRhdGlvbiBnZXRzXG4gICAgLy8ga2lsbGVkIGJ5IHRoZSB0YWlsIG9mIHRoZSBvbmUgaXQgcmVwbGFjZWQuXG4gICAgY29uc3QgbWluZSA9ICsrdGhpcy5jb21wdXRlVG9rZW47XG4gICAgY29uc3Qgc3VwZXJzZWRlZCA9ICgpID0+IG1pbmUgIT09IHRoaXMuY29tcHV0ZVRva2VuO1xuICAgIGxldCBydW46IEVtYmVkZGluZ0NvbXB1dGVSdW4gfCBudWxsID0gbnVsbDtcblxuICAgIHRyeSB7XG4gICAgICAvLyBQcmVmZXIgdGhlIDMtRCBzY29yZXM6IHRoZXkgY2FycnkgYSB0aGlyZCBjb21wb25lbnQgZm9yIGZyZWUsIGFuZCB0LVNORSBvbiBtb3JlXG4gICAgICAvLyBjb21wb25lbnRzIHRoYW4gaXQgbmVlZHMgaXMgbm90IGJldHRlciDigJQgdGhlIFBDQSBiYXNpcyBpcyB0aGUgaW5wdXQgZWl0aGVyIHdheS5cbiAgICAgIGNvbnN0IHNvdXJjZSA9IGF3YWl0IHRoaXMubG9hZFBjYVNjb3Jlcyhjb250cm9scyk7XG4gICAgICBpZiAoc3VwZXJzZWRlZCgpKSByZXR1cm47XG4gICAgICBpZiAoIXNvdXJjZSkge1xuICAgICAgICB0aGlzLmNvbXB1dGVFcnJvciA9ICdUaGlzIGRhdGFzZXQgc2VydmVzIG5vIFBDQSB0byBlbWJlZC4nO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBydW4gPSBuZXcgRW1iZWRkaW5nQ29tcHV0ZVJ1bigpO1xuICAgICAgdGhpcy5jb21wdXRlUnVuID0gcnVuO1xuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcnVuLnJ1bihcbiAgICAgICAge1xuICAgICAgICAgIHNjb3Jlczogc291cmNlLnNjb3JlcyxcbiAgICAgICAgICBuT2JzOiBzb3VyY2Uubk9icyxcbiAgICAgICAgICBuRGltczogc291cmNlLm5EaW1zLFxuICAgICAgICAgIGRpbXM6IG1ldGEuZGltcyxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIC4uLm1ldGEsXG4gICAgICAgICAgbGFiZWw6IChtZXRhLmxhYmVsID8/IG1ldGEubmFtZSlcbiAgICAgICAgICAgIC5yZXBsYWNlKFNwYXRpYWxDaGFydHNDb21wb25lbnQuQ09NUFVURV9TVUZGSVgsICcnKSxcbiAgICAgICAgfSxcbiAgICAgICAgKHByb2dyZXNzOiBDb21wdXRlUHJvZ3Jlc3MpID0+IHtcbiAgICAgICAgICAvLyBQcm9ncmVzcyBmcm9tIGEgc3VwZXJzZWRlZCBydW4gbXVzdCBub3QgZHJpdmUgdGhlIGJhciBhIG5ld2VyIG9uZSBpcyB1c2luZy5cbiAgICAgICAgICBpZiAoc3VwZXJzZWRlZCgpKSByZXR1cm47XG4gICAgICAgICAgdGhpcy5jb21wdXRlRnJhY3Rpb24gPSBwcm9ncmVzcy5mcmFjdGlvbjtcbiAgICAgICAgICB0aGlzLmNvbXB1dGVCYWNrZW5kID0gcHJvZ3Jlc3MuYmFja2VuZCA/PyB0aGlzLmNvbXB1dGVCYWNrZW5kO1xuICAgICAgICAgIGlmIChwcm9ncmVzcy5tZXNzYWdlKSB0aGlzLmNvbXB1dGVNZXNzYWdlID0gcHJvZ3Jlc3MubWVzc2FnZTtcbiAgICAgICAgfSxcbiAgICAgICk7XG4gICAgICAvLyBBIHRlcm1pbmF0ZWQgcnVuIHJlc29sdmVzIG51bGwsIHNvIHRoaXMgY292ZXJzIGFiYW5kb25tZW50IGFzIHdlbGwgYXMgQ2FuY2VsIOKAlFxuICAgICAgLy8gYnV0IHRoZSB0b2tlbiBpcyBzdGlsbCBjaGVja2VkLCBiZWNhdXNlIHRoZSBkYXRhc2V0IGNhbiBjaGFuZ2UgYmV0d2VlbiB0aGVcbiAgICAgIC8vIHdvcmtlcidzIGBkb25lYCBhbmQgdGhpcyBsaW5lLlxuICAgICAgaWYgKHJlc3VsdCAmJiAhc3VwZXJzZWRlZCgpKSB7XG4gICAgICAgIHRoaXMuY29tcHV0ZWQuc2V0KG1ldGEubmFtZSwgcmVzdWx0KTtcbiAgICAgICAgdGhpcy5lbWJlZGRpbmdDb29yZHMgPSByZXN1bHQ7XG4gICAgICAgIGF3YWl0IHRoaXMucmVuZGVyKCk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBpZiAoIXN1cGVyc2VkZWQoKSkgdGhpcy5jb21wdXRlRXJyb3IgPSAoZXJyIGFzIEVycm9yKT8ubWVzc2FnZSA/PyBTdHJpbmcoZXJyKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgcnVuPy50ZXJtaW5hdGUoKTtcbiAgICAgIC8vIE9ubHkgaWYgaXQgaXMgc3RpbGwgb3VyczogYSBkYXRhc2V0IHN3aXRjaCBoYXMgYWxyZWFkeSB0ZXJtaW5hdGVkIHRoaXMgcnVuIGFuZFxuICAgICAgLy8gbWF5IGhhdmUgc3RhcnRlZCBhbm90aGVyLCBhbmQgYHRoaXMuY29tcHV0ZVJ1biA9IG51bGxgIGhlcmUgd291bGQgb3JwaGFuIGl0IOKAlFxuICAgICAgLy8gbGVhdmluZyBhIHdvcmtlciBub3RoaW5nIGNhbiBjYW5jZWwgYW5kIGEgQ2FuY2VsIGJ1dHRvbiB0aGF0IGRvZXMgbm90aGluZy5cbiAgICAgIGlmICh0aGlzLmNvbXB1dGVSdW4gPT09IHJ1bikge1xuICAgICAgICB0aGlzLmNvbXB1dGVSdW4gPSBudWxsO1xuICAgICAgICB0aGlzLmNvbXB1dGVGcmFjdGlvbiA9IG51bGw7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqIFN0b3AgYSBydW4uIEl0IHNldHRsZXMgc2hvcnRseSBhZnRlciwgbGVhdmluZyBubyBlbWJlZGRpbmcuICovXG4gIGNhbmNlbENvbXB1dGUoKTogdm9pZCB7XG4gICAgdGhpcy5jb21wdXRlUnVuPy5jYW5jZWwoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEcm9wIGFueSBjb21wdXRhdGlvbiBpbiBwcm9ncmVzcywgYmVjYXVzZSB3aGF0IGl0IGlzIGNvbXB1dGluZyBubyBsb25nZXIgYXBwbGllcy5cbiAgICpcbiAgICogRGlzdGluY3QgZnJvbSB7QGxpbmsgY2FuY2VsQ29tcHV0ZX0sIHdoaWNoIGlzIHRoZSB1c2VyJ3MgQ2FuY2VsIGFuZCBhc2tzIHRoZSB3b3JrZXIgdG9cbiAgICogc3RvcCBwb2xpdGVseS4gVGhpcyBvbmUgZG9lcyBub3Qgd2FpdDogdGhlIHJ1biBpcyBzZXR0bGVkIGFuZCB0aGUgd29ya2VyIGtpbGxlZCwgc28gYVxuICAgKiBkYXRhc2V0IHN3aXRjaCBvciBhIHRlYXJkb3duIGNhbm5vdCBsZWF2ZSBhIHQtU05FIGdyaW5kaW5nIG9uIGluIHRoZSBiYWNrZ3JvdW5kIG92ZXJcbiAgICogb2JzZXJ2YXRpb25zIG5vdGhpbmcgaXMgc2hvd2luZyBhbnkgbW9yZS5cbiAgICovXG4gIHByaXZhdGUgYWJhbmRvbkNvbXB1dGUoKTogdm9pZCB7XG4gICAgdGhpcy5jb21wdXRlVG9rZW4rKztcbiAgICB0aGlzLmNvbXB1dGVSdW4/LnRlcm1pbmF0ZSgpO1xuICAgIHRoaXMuY29tcHV0ZVJ1biA9IG51bGw7XG4gICAgdGhpcy5jb21wdXRlRnJhY3Rpb24gPSBudWxsO1xuICAgIHRoaXMuY29tcHV0ZUJhY2tlbmQgPSBudWxsO1xuICAgIHRoaXMuY29tcHV0ZU1lc3NhZ2UgPSBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBkYXRhc2V0J3MgUENBLCBhcyByb3ctbWFqb3Igc2NvcmVzLlxuICAgKlxuICAgKiBBc3NlbWJsZWQgZnJvbSB3aGljaGV2ZXIgUENBIHRoZSBzb3VyY2Ugb2ZmZXJzLCB3aWRlc3QgZmlyc3Qg4oCUIGEgMy1EIG9uZSBnaXZlcyB0LVNORVxuICAgKiB0aHJlZSBjb21wb25lbnRzIGluc3RlYWQgb2YgdHdvIGZvciB0aGUgc2FtZSByZXF1ZXN0LlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBsb2FkUGNhU2NvcmVzKFxuICAgIGNvbnRyb2xzOiBJU3BhdGlhbENvbnRyb2xzLFxuICApOiBQcm9taXNlPHsgc2NvcmVzOiBGbG9hdDMyQXJyYXk7IG5PYnM6IG51bWJlcjsgbkRpbXM6IG51bWJlciB9IHwgbnVsbD4ge1xuICAgIGNvbnN0IGNhbmRpZGF0ZXMgPSB0aGlzLmVtYmVkZGluZ3NcbiAgICAgIC5maWx0ZXIoKGUpID0+IC9wY2EvaS50ZXN0KGUubGFiZWwgPz8gZS5uYW1lKSAmJiAhZS5uYW1lLnN0YXJ0c1dpdGgoJ2xvY2FsOicpKVxuICAgICAgLnNvcnQoKGEsIGIpID0+IGIuZGltcyAtIGEuZGltcyk7XG4gICAgZm9yIChjb25zdCBjYW5kaWRhdGUgb2YgY2FuZGlkYXRlcykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcGNhID0gYXdhaXQgY29udHJvbHMuZ2V0RW1iZWRkaW5nIShjYW5kaWRhdGUubmFtZSk7XG4gICAgICAgIGNvbnN0IHBsYW5lcyA9IFtwY2EueCwgcGNhLnksIC4uLihwY2EueiA/IFtwY2Euel0gOiBbXSldO1xuICAgICAgICBjb25zdCBuT2JzID0gcGNhLngubGVuZ3RoO1xuICAgICAgICBjb25zdCBuRGltcyA9IHBsYW5lcy5sZW5ndGg7XG4gICAgICAgIGNvbnN0IHNjb3JlcyA9IG5ldyBGbG9hdDMyQXJyYXkobk9icyAqIG5EaW1zKTtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuT2JzOyBpKyspIHtcbiAgICAgICAgICBmb3IgKGxldCBkID0gMDsgZCA8IG5EaW1zOyBkKyspIHNjb3Jlc1tpICogbkRpbXMgKyBkXSA9IHBsYW5lc1tkXVtpXTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4geyBzY29yZXMsIG5PYnMsIG5EaW1zIH07XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLy8gVHJ5IHRoZSBuZXh0OyBhIHNvdXJjZSBtYXkgYWR2ZXJ0aXNlIG9uZSBpdCBjYW5ub3QgYWN0dWFsbHkgc2VydmUuXG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLyoqIFdoaWNoIGVtYmVkZGluZyB0byBkcmF3LCB3aGVuIHRoZSBkYXRhc2V0IHB1Ymxpc2hlcyBtb3JlIHRoYW4gb25lLiAqL1xuICBvbkVtYmVkZGluZyhuYW1lOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCBuZXh0ID0gdGhpcy5lbWJlZGRpbmdzLmZpbmQoKGUpID0+IGUubmFtZSA9PT0gbmFtZSk7XG4gICAgaWYgKCFuZXh0IHx8IG5leHQubmFtZSA9PT0gdGhpcy5lbWJlZGRpbmc/Lm5hbWUpIHJldHVybjtcbiAgICB0aGlzLmVtYmVkZGluZyA9IG5leHQ7XG4gICAgdGhpcy5lbWJlZGRpbmdDb29yZHMgPSBudWxsO1xuICAgIHZvaWQgdGhpcy5yZW5kZXIoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXaGF0IHRoZSBjaGFydCBvbiBzY3JlZW4gYWN0dWFsbHkgc2hvd3MsIGFuZCB3aGF0IGl0IGNhbm5vdCBiZSByZWFkIGZvci5cbiAgICpcbiAgICogU2hvd24gb24gaG92ZXIgZnJvbSBhIGA/YCBiZXNpZGUgdGhlIHRhYnMuIEVhY2ggb2YgdGhlc2UgcGxvdHMgYW5zd2VycyBhIGRpZmZlcmVudFxuICAgKiBxdWVzdGlvbiBhbmQgdHdvIG9mIHRoZW0gYXJlIHJvdXRpbmVseSBvdmVyLXJlYWQg4oCUIGEgVU1BUCdzIGRpc3RhbmNlcyBhbmQgYSBoZWF0bWFwJ3NcbiAgICogdW5zY2FsZWQgY29sb3VycyDigJQgc28gdGhlIGNhdmVhdCBpcyBwYXJ0IG9mIHRoZSBleHBsYW5hdGlvbiByYXRoZXIgdGhhbiBhIGZvb3Rub3RlLlxuICAgKlxuICAgKiBIVE1MLCBiZWNhdXNlIHRoZSB0b29sdGlwIHJlbmRlcnMgd2l0aCBgW2VzY2FwZV09XCJmYWxzZVwiYDogYSBwYXJhZ3JhcGggYW5kIGEgY2F2ZWF0XG4gICAqIHJlYWQgYXMgdHdvIHRob3VnaHRzLCBhbmQgYSBzaW5nbGUgcnVuLW9uIGxpbmUgaXMgc2tpcHBlZCByYXRoZXIgdGhhbiByZWFkLlxuICAgKi9cbiAgZ2V0IGtpbmRIZWxwKCk6IHN0cmluZyB7XG4gICAgc3dpdGNoICh0aGlzLmtpbmQpIHtcbiAgICAgIGNhc2UgJ2NvdW50cyc6XG4gICAgICAgIHJldHVybiAnPGI+Q291bnRzPC9iPiDigJQgaG93IG1hbnkgb2JzZXJ2YXRpb25zIGZhbGwgaW4gZWFjaCBjYXRlZ29yeSBvZiB0aGUgY29sdW1uICdcbiAgICAgICAgICArICd0aGUgbWFwIGlzIGNvbG91cmVkIGJ5LCBsYXJnZXN0IGZpcnN0Ljxicj48YnI+QSBjYXRlZ29yeSBjb2RlIGlzIGEgbGFiZWwsIG5vdCAnXG4gICAgICAgICAgKyAnYSBtYWduaXR1ZGUsIHNvIGEgZnJlcXVlbmN5IGlzIHRoZSBvbmx5IGRpc3RyaWJ1dGlvbiBpdCBoYXM6IHRoZXJlIGlzIG5vICdcbiAgICAgICAgICArICdoaXN0b2dyYW0gb2YgYSBjZWxsIHR5cGUuJztcbiAgICAgIGNhc2UgJ2hpc3RvZ3JhbSc6XG4gICAgICAgIHJldHVybiAnPGI+SGlzdG9ncmFtPC9iPiDigJQgaG93IHRoZSBhY3RpdmUgdmFsdWUgaXMgZGlzdHJpYnV0ZWQgb3ZlciBhbGwgJ1xuICAgICAgICAgICsgJ29ic2VydmF0aW9ucy48YnI+PGJyPldpdGggYSBzZWxlY3Rpb24sIGl0IGlzIG92ZXJsYWlkIG9uIHRoZSBmdWxsICdcbiAgICAgICAgICArICdkaXN0cmlidXRpb24gcmF0aGVyIHRoYW4gcmVwbGFjaW5nIGl0LCBzbyB5b3UgY2FuIHNlZSB3aGVyZSB0aGUgc2VsZWN0ZWQgJ1xuICAgICAgICAgICsgJ2NlbGxzIHNpdCB3aXRoaW4gdGhlIHdob2xlLic7XG4gICAgICBjYXNlICd2aW9saW4nOlxuICAgICAgICByZXR1cm4gJzxiPlZpb2xpbjwvYj4g4oCUIHRoZSBhY3RpdmUgdmFsdWVcXCdzIGRpc3RyaWJ1dGlvbiB3aXRoaW4gZWFjaCBjYXRlZ29yeSBvZiAnXG4gICAgICAgICAgKyAndGhlIGdyb3VwaW5nIGNvbHVtbiwgZHJhd24gYXMgYSBzbW9vdGhlZCBkZW5zaXR5Ljxicj48YnI+U2hvd3Mgc2hhcGUgYSBib3ggJ1xuICAgICAgICAgICsgJ3Bsb3QgaGlkZXM6IHR3byBncm91cHMgd2l0aCB0aGUgc2FtZSBtZWRpYW4gY2FuIGJlIG9uZSBwZWFrIG9yIHR3by4nO1xuICAgICAgY2FzZSAnYm94JzpcbiAgICAgICAgcmV0dXJuICc8Yj5Cb3g8L2I+IOKAlCBtZWRpYW4sIHF1YXJ0aWxlcyBhbmQgcmFuZ2Ugb2YgdGhlIGFjdGl2ZSB2YWx1ZSB3aXRoaW4gZWFjaCAnXG4gICAgICAgICAgKyAnY2F0ZWdvcnkgb2YgdGhlIGdyb3VwaW5nIGNvbHVtbi48YnI+PGJyPkNvbXBhY3QgYW5kIGNvbXBhcmFibGUgYWNyb3NzIG1hbnkgJ1xuICAgICAgICAgICsgJ2dyb3VwcywgYXQgdGhlIGNvc3Qgb2YgaGlkaW5nIHdoZXRoZXIgYSBncm91cCBpcyBiaW1vZGFsLic7XG4gICAgICBjYXNlICdoZWF0bWFwJzpcbiAgICAgICAgcmV0dXJuICc8Yj5IZWF0bWFwPC9iPiDigJQgbWVhbiBleHByZXNzaW9uIG9mIGVhY2ggcGlja2VkIGdlbmUgd2l0aGluIGVhY2ggZ3JvdXA6ICdcbiAgICAgICAgICArICdnZW5lcyBkb3duLCBncm91cHMgYWNyb3NzLjxicj48YnI+RWFjaCBnZW5lIGlzIDxiPnotc2NvcmVkIGFjcm9zcyB0aGUgJ1xuICAgICAgICAgICsgJ2dyb3VwczwvYj4gYnkgZGVmYXVsdCwgc28gYSBjb2xvdXIgc2F5cyBcImFib3ZlIG9yIGJlbG93IHRoaXMgZ2VuZVxcJ3Mgb3duICdcbiAgICAgICAgICArICdhdmVyYWdlXCIsIG5vdCBcImhpZ2hseSBleHByZXNzZWRcIi4gV2l0aG91dCB0aGF0IG9uZSBsb3VkIGdlbmUgc2F0dXJhdGVzIHRoZSAnXG4gICAgICAgICAgKyAnc2NhbGUgYW5kIHRoZSByZXN0IG9mIHRoZSBwYW5lbCByZWFkcyBhcyBibGFuay4gVHVybmluZyBpdCBvZmYgY29tcGFyZXMgJ1xuICAgICAgICAgICsgJ2dlbmVzIG9uIHRoZWlyIHJhdyBzY2FsZSBpbnN0ZWFkLic7XG4gICAgICBjYXNlICdlbWJlZGRpbmcnOlxuICAgICAgICByZXR1cm4gdGhpcy5lbWJlZGRpbmdIZWxwO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuICcnO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBXaGF0IHRoZSBlbWJlZGRpbmcgb24gc2NyZWVuIGlzLCBhbmQgaG93IGZhciBpdHMgZ2VvbWV0cnkgY2FuIGJlIHRydXN0ZWQuXG4gICAqXG4gICAqIFBlciBNRVRIT0QsIGJlY2F1c2UgdGhhdCBpcyB0aGUgcGFydCBwZW9wbGUgZ2V0IHdyb25nOiBhIFBDQSdzIGF4ZXMgYXJlIG9yZGVyZWQgYW5kXG4gICAqIG1lYXN1cmFibGUgd2hpbGUgYSBVTUFQJ3MgYXJlIG5laXRoZXIsIGFuZCB0aGUgc2FtZSBwaWN0dXJlIHJlYWQgdGhlIHR3byB3YXlzIHN1cHBvcnRzXG4gICAqIG9wcG9zaXRlIGNvbmNsdXNpb25zLlxuICAgKi9cbiAgcHJpdmF0ZSBnZXQgZW1iZWRkaW5nSGVscCgpOiBzdHJpbmcge1xuICAgIGNvbnN0IHNoYXJlZCA9ICc8YnI+PGJyPkV2ZXJ5IGNlbGwgaXMgaW4gYWxsIHZpZXdzIGF0IG9uY2U6IGxhc3NvIGEgZ3JvdXAgaGVyZSBhbmQgJ1xuICAgICAgKyAndGhvc2UgY2VsbHMgbGlnaHQgdXAgb24gdGhlIHRpc3N1ZSwgYmVjYXVzZSBib3RoIHJlYWQgdGhlIHNhbWUgc2VsZWN0aW9uLic7XG4gICAgY29uc3QgbmFtZSA9ICh0aGlzLmVtYmVkZGluZz8ubGFiZWwgPz8gdGhpcy5lbWJlZGRpbmc/Lm5hbWUgPz8gJycpLnRvTG93ZXJDYXNlKCk7XG4gICAgaWYgKG5hbWUuaW5jbHVkZXMoJ3BjYScpKSB7XG4gICAgICByZXR1cm4gJzxiPlBDQTwvYj4g4oCUIGEgPGI+bGluZWFyPC9iPiBwcm9qZWN0aW9uIG9udG8gdGhlIGRpcmVjdGlvbnMgb2YgZ3JlYXRlc3QgJ1xuICAgICAgICArICd2YXJpYW5jZSwgaW4gb3JkZXIuPGJyPjxicj5BbG9uZSBhbW9uZyB0aGVzZSwgaXRzIGF4ZXMgbWVhbiBzb21ldGhpbmcgJ1xuICAgICAgICArICdtZWFzdXJhYmxlOiBlYWNoIHJlcG9ydHMgdGhlIHNoYXJlIG9mIHRvdGFsIHZhcmlhbmNlIGl0IGV4cGxhaW5zLCB3aGljaCBpcyB3aHkgJ1xuICAgICAgICArICd0aGUgbGFiZWxzIGNhcnJ5IGEgcGVyY2VudGFnZS4gRGlzdGFuY2VzIGFyZSByZWFsLCBhbmQgYSBsb3cgcGVyY2VudGFnZSB0ZWxscyAnXG4gICAgICAgICsgJ3lvdSB0aGUgcGljdHVyZSBpcyBhIHRoaW4gc2xpY2Ugb2YgdGhlIHZhcmlhdGlvbi4nICsgc2hhcmVkO1xuICAgIH1cbiAgICBpZiAobmFtZS5pbmNsdWRlcygndC1zbmUnKSB8fCBuYW1lLmluY2x1ZGVzKCd0c25lJykpIHtcbiAgICAgIHJldHVybiAnPGI+dC1TTkU8L2I+IOKAlCBjZWxscyBwbGFjZWQgc28gdGhhdCBjbG9zZSBuZWlnaGJvdXJzIGluIGV4cHJlc3Npb24gc3RheSAnXG4gICAgICAgICsgJ2Nsb3NlLjxicj48YnI+U3RyaWN0ZXIgYWJvdXQgbG9jYWwgbmVpZ2hib3VyaG9vZHMgdGhhbiBVTUFQIGFuZCBsZXNzICdcbiAgICAgICAgKyAndHJ1c3R3b3J0aHkgYWJvdXQgYW55dGhpbmcgZ2xvYmFsOiBpdCB0ZW5kcyB0byBzcHJlYWQgY2x1c3RlcnMgaW50byAnXG4gICAgICAgICsgJ2V2ZW5seS1zaXplZCBpc2xhbmRzIHdob3NlIHNpemVzIGFuZCBzZXBhcmF0aW9ucyBtZWFuIGxpdHRsZS4gUmVhZCB3aGljaCBjZWxscyAnXG4gICAgICAgICsgJ2dyb3VwIHRvZ2V0aGVyLCBub3QgaG93IGZhciBhcGFydCB0aGUgZ3JvdXBzIGFyZS4nICsgc2hhcmVkO1xuICAgIH1cbiAgICByZXR1cm4gJzxiPlVNQVA8L2I+IOKAlCBjZWxscyBwbGFjZWQgc28gdGhhdCBjbG9zZSBuZWlnaGJvdXJzIGluIGV4cHJlc3Npb24gc3RheSAnXG4gICAgICArICdjbG9zZS48YnI+PGJyPlRoZSBheGVzIGFyZSBhcmJpdHJhcnk6IHVub3JkZXJlZCwgdW5pdGxlc3MsIGFuZCByZXByb2R1Y2libGUgb25seSAnXG4gICAgICArICd1cCB0byBhIHJvdGF0aW9uLCB3aGljaCBpcyB3aHkgdGhleSBjYXJyeSBubyBwZXJjZW50YWdlLiBSZWFkIHdoaWNoIGNlbGxzIGdyb3VwICdcbiAgICAgICsgJ3RvZ2V0aGVyIGFuZCB3aGljaCBncm91cHMgdG91Y2g7IGRvIG5vdCByZWFkIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIGRpc3RhbnQgJ1xuICAgICAgKyAnY2x1c3RlcnMsIG9yIHRoZSBkaXJlY3Rpb24gb2YgYW4gYXhpcy4nICsgc2hhcmVkO1xuICB9XG5cbiAgLyoqIFdoYXQgdGhlIGVtYmVkZGluZyB2aWV3IGlzIHNob3dpbmcsIHNhaWQgcGxhaW5seS4gKi9cbiAgZ2V0IGVtYmVkZGluZ05vdGUoKTogc3RyaW5nIHtcbiAgICBjb25zdCBtZXRhID0gdGhpcy5lbWJlZGRpbmdDb29yZHM/Lm1ldGEgPz8gdGhpcy5lbWJlZGRpbmc7XG4gICAgaWYgKCFtZXRhKSByZXR1cm4gJ1RoaXMgZGF0YXNldCBwdWJsaXNoZXMgbm8gZW1iZWRkaW5nLic7XG4gICAgLy8gQSBkZXJpdmVkIGVtYmVkZGluZyBzYXlzIHNvLCBhbmQgc2F5cyBIT1cgd2hlbiB0aGUgcGFyYW1ldGVycyBhcmUga25vd246IGEgdC1TTkUgb3JcbiAgICAvLyBVTUFQIGF0IGRpZmZlcmVudCBzZXR0aW5ncyBpcyBhIGRpZmZlcmVudCBwaWN0dXJlIG9mIHRoZSBzYW1lIGNlbGxzLCBzbyBcImNvbXB1dGVkXG4gICAgLy8gaGVyZVwiIGFsb25lIGxlYXZlcyBhIHJlYWRlciB1bmFibGUgdG8gcmVwcm9kdWNlIG9yIGNvbXBhcmUgaXQuXG4gICAgY29uc3QgZGVyaXZlZCA9IG1ldGEuZGVyaXZlZFxuICAgICAgPyBgIENvbXB1dGVkIGhlcmUke21ldGEucGFyYW1zID8gYCAoJHttZXRhLnBhcmFtc30pYCA6ICcnfSwgYFxuICAgICAgICArICdub3QgcHVibGlzaGVkIHdpdGggdGhlIGRhdGFzZXQuJ1xuICAgICAgOiAnJztcbiAgICBjb25zdCBjb2xvdXJlZCA9IHRoaXMuY2F0ZWdvcmljYWxcbiAgICAgID8gJyBDb2xvdXJlZCB0byBtYXRjaCB0aGUgbWFwLidcbiAgICAgIDogJyBDb2xvdXIgdGhlIG1hcCBieSBhIGNhdGVnb3JpY2FsIGNvbHVtbiB0byBjb2xvdXIgdGhlc2UgcG9pbnRzLic7XG4gICAgY29uc3Qgc2VsID0gdGhpcy5zZWxlY3Rpb24uY291bnQgPiAwXG4gICAgICA/IGAgJHt0aGlzLnNlbGVjdGlvbi5jb3VudC50b0xvY2FsZVN0cmluZygpfSBzZWxlY3RlZCBhcmUgaGlnaGxpZ2h0ZWQuYFxuICAgICAgOiAnJztcbiAgICByZXR1cm4gYCR7bWV0YS5sYWJlbCA/PyBtZXRhLm5hbWV9LiR7Y29sb3VyZWR9JHtzZWx9JHtkZXJpdmVkfWA7XG4gIH1cblxuICBwcml2YXRlIHB1cmdlUGxvdCgpOiB2b2lkIHtcbiAgICB0cnkge1xuICAgICAgLy8gVGhlIEFDVElWRSB0YXJnZXQ6IHB1cmdpbmcgdGhlIGlubGluZSBkaXYgd2hpbGUgZGV0YWNoZWQgd291bGQgbGVhdmUgdGhlXG4gICAgICAvLyB3aW5kb3cgc2hvd2luZyBhIHBsb3QgdGhlIGNvbXBvbmVudCBiZWxpZXZlcyBpdCBoYXMgY2xlYXJlZC5cbiAgICAgIFBsb3RseS5wdXJnZSh0aGlzLnBsb3RUYXJnZXQpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gTm90aGluZyBwbG90dGVkLlxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBEcmF3LCByZW1lbWJlcmluZyB3aGV0aGVyIHRoaXMgbGF5b3V0IGZpeGVkIGl0cyBvd24gaGVpZ2h0LlxuICAgKlxuICAgKiBUaGUgbGF5b3V0IGJ1aWxkZXJzIHJldHVybiBgdW5rbm93bmAgb24gcHVycG9zZSDigJQgdGhleSBrZWVwIFBsb3RseSdzIGxheW91dFxuICAgKiB0eXBlcyBvdXQgb2YgdGhlIHB1cmUgbW9kdWxlIOKAlCBzbyB0aGUgaGVpZ2h0IGlzIHJlYWQgYmFjayBieSBuYXJyb3dpbmdcbiAgICogcmF0aGVyIHRoYW4gZGVjbGFyZWQuIGBoZWlnaHRgIGlzIFBsb3RseSdzIG93biBrZXksIG5vdCBvdXJzLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBkcmF3KFxuICAgIHRyYWNlczogdW5rbm93biwgbGF5b3V0OiB1bmtub3duLCBjb25maWc6IHVua25vd24gPSBDSEFSVF9DT05GSUcsIGRpdiA9IHRoaXMucGxvdFRhcmdldCxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgaGVpZ2h0ID0gKGxheW91dCBhcyB7IGhlaWdodD86IHVua25vd24gfSB8IG51bGwpPy5oZWlnaHQ7XG4gICAgdGhpcy5kcmF3bkhlaWdodCA9IHR5cGVvZiBoZWlnaHQgPT09ICdudW1iZXInID8gaGVpZ2h0IDogbnVsbDtcbiAgICBhd2FpdCBQbG90bHkucmVhY3QoZGl2LCB0cmFjZXMgYXMgbmV2ZXIsIGxheW91dCBhcyBuZXZlciwgY29uZmlnIGFzIG5ldmVyKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgcmVuZGVyKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICghdGhpcy5pc0FjdGl2ZSkgcmV0dXJuO1xuICAgIC8vIEd1YXJkIG9uIHRoZSBkaXYgdGhlIEFDVElWRSBraW5kIHdpbGwgZHJhdyBpbnRvLCBub3QgYWx3YXlzIHRoZSBzaGFyZWQgb25lLiBXaGlsZSBhXG4gICAgLy8ga2luZCBpcyBkZXRhY2hlZCB0aGUgaW5saW5lIGRpdiBpcyByZW1vdmVkIGJ5IGl0cyBgKm5nSWZgLCBzbyBjaGVja2luZyB0aGF0IG9uZVxuICAgIC8vIGJhaWxlZCBoZXJlIGFuZCB0aGUgZGV0YWNoZWQgd2luZG93IG5ldmVyIGdvdCBhIHBsb3QuXG4gICAgY29uc3QgdGFyZ2V0ID0gdGhpcy5wbG90VGFyZ2V0O1xuICAgIGlmICghZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGFyZ2V0KSkgcmV0dXJuO1xuICAgIC8vIEFueXRoaW5nIGJ1dCB0aGUgZW1iZWRkaW5nIGluaGVyaXRzIGEgZGl2IHRoZSBlbWJlZGRpbmcgbWF5IGhhdmUgYm91bmQgaGFuZGxlcnMgb24uXG4gICAgaWYgKHRoaXMua2luZCAhPT0gJ2VtYmVkZGluZycpIHRoaXMudW5iaW5kRW1iZWRkaW5nU2VsZWN0aW9uKHRhcmdldCk7XG4gICAgLy8gVGhlIGhlYXRtYXAgYW5zd2VycyBhIGRpZmZlcmVudCBxdWVzdGlvbiBmcm9tIHRoZSBvdGhlciBraW5kcyDigJQgd2hpY2hcbiAgICAvLyBnZW5lcyBkaXN0aW5ndWlzaCB3aGljaCBncm91cHMg4oCUIHNvIGl0IGlzIGRyaXZlbiBieSBpdHMgb3duIGdlbmUgbGlzdCBhbmRcbiAgICAvLyBncm91cGluZyByYXRoZXIgdGhhbiBieSB3aGF0ZXZlciB0aGUgbWFwIGlzIGNvbG91cmVkIGJ5LlxuICAgIGlmICh0aGlzLmtpbmQgPT09ICdoZWF0bWFwJykge1xuICAgICAgYXdhaXQgdGhpcy5yZW5kZXJIZWF0bWFwKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIEFsc28gaW5kZXBlbmRlbnQgb2YgdGhlIGFjdGl2ZSBjb2xvdXIgc291cmNlOiB0aGUgY29vcmRpbmF0ZXMgYXJlIHRoZSBkYXRhc2V0J3MsXG4gICAgLy8gYW5kIHRoZSBjb2xvdXJpbmcgbWVyZWx5IGZvbGxvd3Mgd2hhdGV2ZXIgdGhlIG1hcCBpcyB1c2luZy5cbiAgICBpZiAodGhpcy5raW5kID09PSAnZW1iZWRkaW5nJykge1xuICAgICAgYXdhaXQgdGhpcy5yZW5kZXJFbWJlZGRpbmcoKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKCF0aGlzLmNvbG9yQnkgfHwgKCF0aGlzLnZhbHVlcyAmJiAhdGhpcy5jYXRlZ29yaWNhbCkpIHtcbiAgICAgIHRoaXMucHVyZ2VQbG90KCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICh0aGlzLmNhdGVnb3JpY2FsKSB7XG4gICAgICBjb25zdCBjb3VudHMgPSB7XG4gICAgICAgIGdyb3VwOiB0aGlzLmNhdGVnb3JpY2FsLFxuICAgICAgICBzZWxlY3Rpb246IHRoaXMuc2VsZWN0aW9uLmNvdW50ID4gMCA/IHRoaXMuc2VsZWN0aW9uLm1hc2sgOiBudWxsLFxuICAgICAgICBuYW1lOiB0aGlzLmNvbG9yQnkubmFtZSxcbiAgICAgIH07XG4gICAgICBhd2FpdCB0aGlzLmRyYXcoYnVpbGRDb3VudFRyYWNlcyhjb3VudHMpLCBjb3VudHNMYXlvdXQoY291bnRzKSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICghdGhpcy52YWx1ZXMpIHJldHVybjtcbiAgICBjb25zdCBpbnB1dCA9IHtcbiAgICAgIHZhbHVlczogdGhpcy52YWx1ZXMsXG4gICAgICBuYW1lOiB0aGlzLmNvbG9yQnkua2luZCA9PT0gJ2ZlYXR1cmUnID8gdGhpcy5jb2xvckJ5Lm5hbWUgOiB0aGlzLmNvbG9yQnkubmFtZSxcbiAgICAgIGdyb3VwOiBiZW5lZml0c0Zyb21Hcm91cGluZyh0aGlzLmtpbmQpID8gdGhpcy5ncm91cGluZyA6IG51bGwsXG4gICAgICBzZWxlY3Rpb246IHRoaXMuc2VsZWN0aW9uLmNvdW50ID4gMCA/IHRoaXMuc2VsZWN0aW9uLm1hc2sgOiBudWxsLFxuICAgICAgbG9nOiB0aGlzLnZpZXcubG9nU2NhbGUsXG4gICAgfTtcbiAgICBjb25zdCB0cmFjZXMgPSBidWlsZE9taWNzVHJhY2VzKHRoaXMua2luZCwgaW5wdXQpO1xuICAgIGF3YWl0IHRoaXMuZHJhdyh0cmFjZXMsIG9taWNzTGF5b3V0KHRoaXMua2luZCwgaW5wdXQpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZW5lcyDDlyBncm91cHMgbWVhbiBleHByZXNzaW9uLlxuICAgKlxuICAgKiBDb2x1bW5zIGFyZSB0aGUgZ3JvdXBpbmcgY29sdW1uJ3MgY2F0ZWdvcmllcyDigJQgb3IsIGluc2lkZSBhIFNNQUxMXG4gICAqIHNlbGVjdGlvbiwgdGhlIHNlbGVjdGVkIGNlbGxzIHRoZW1zZWx2ZXMuIEEgdHdvLWNvbHVtbiBjbGFzcyBtYXRyaXggaXMgYVxuICAgKiBwb29yIGFuc3dlciBmb3IgYW4gUk9JIGhvbGRpbmcgZm9ydHkgY2VsbHM7IFwid2hhdCBpcyBpbiBmcm9udCBvZiBtZVwiIGlzIHRoZVxuICAgKiBxdWVzdGlvbiB0aGVyZSwgYW5kIGVhY2ggY2VsbCBlYXJucyBhIGNvbHVtbi5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcmVuZGVySGVhdG1hcCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBjb250cm9scyA9IHRoaXMuY29udHJvbHM7XG4gICAgY29uc3QgZ2VuZXMgPSB0aGlzLmhlYXRtYXBHZW5lc1xuICAgICAgLm1hcCgobmFtZSkgPT4gKHsgbmFtZSwgdmFsdWVzOiB0aGlzLmdlbmVDYWNoZS5nZXQobmFtZSkgfSkpXG4gICAgICAuZmlsdGVyKChnKTogZyBpcyB7IG5hbWU6IHN0cmluZzsgdmFsdWVzOiBGbG9hdDMyQXJyYXkgfSA9PiAhIWcudmFsdWVzKTtcbiAgICBpZiAoIWNvbnRyb2xzIHx8IGdlbmVzLmxlbmd0aCA9PT0gMCB8fCAhdGhpcy5ncm91cGluZykge1xuICAgICAgdGhpcy5wdXJnZVBsb3QoKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBzZWxlY3RlZCA9IHRoaXMuc2VsZWN0aW9uLmNvdW50ID4gMCA/IG1hc2tUb0luZGljZXModGhpcy5zZWxlY3Rpb24ubWFzaykgOiBudWxsO1xuICAgIGNvbnN0IHBlckNlbGwgPSAhIXNlbGVjdGVkICYmIHNlbGVjdGVkLmxlbmd0aCA8PSBTcGF0aWFsQ2hhcnRzQ29tcG9uZW50LkhFQVRNQVBfQ0VMTF9DT0xVTU5TO1xuICAgIGNvbnN0IGNlbGxzID0gcGVyQ2VsbCAmJiBzZWxlY3RlZFxuICAgICAgPyBjZWxsc0FzR3JvdXBzKHNlbGVjdGVkLCB0aGlzLmdyb3VwaW5nLmNvZGVzLmxlbmd0aCxcbiAgICAgICAgU3BhdGlhbENoYXJ0c0NvbXBvbmVudC5IRUFUTUFQX0NFTExfQ09MVU1OUylcbiAgICAgIDogbnVsbDtcblxuICAgIGNvbnN0IG1hdHJpeCA9IGhlYXRtYXBNYXRyaXgoXG4gICAgICBnZW5lcyxcbiAgICAgIGNlbGxzID8gY2VsbHMuZ3JvdXBzIDogeyBjb2RlczogdGhpcy5ncm91cGluZy5jb2RlcywgY2F0ZWdvcmllczogdGhpcy5ncm91cGluZy5jYXRlZ29yaWVzIH0sXG4gICAgICB7XG4gICAgICAgIC4uLihjZWxscyA/IHsgaW5kaWNlczogY2VsbHMuaW5kaWNlcywgbWluQ2VsbHM6IDEgfSA6IHt9KSxcbiAgICAgICAgLy8gT3V0c2lkZSB0aGUgcGVyLWNlbGwgdmlldyBhIHNlbGVjdGlvbiBzdGlsbCBuYXJyb3dzIHRoZSBtZWFucywgdGhlIHdheVxuICAgICAgICAvLyB0aGUgdmlvbGluIGFuZCBib3ggbmFycm93IHJhdGhlciB0aGFuIG92ZXJsYXkuXG4gICAgICAgIC4uLighY2VsbHMgJiYgc2VsZWN0ZWQgPyB7IGluZGljZXM6IHNlbGVjdGVkIH0gOiB7fSksXG4gICAgICAgIC8vIE9ubHkgdGhlIGdyb3VwZWQgdmlldzogYGNlbGxzQXNHcm91cHNgIGFscmVhZHkgY2FwcyBieSBldmVuIHRoaW5uaW5nLFxuICAgICAgICAvLyBhbmQgcmUtcmFua2luZyB0aG9zZSBjb2x1bW5zIHdvdWxkIGxvc2UgdGhlIHNlbGVjdGlvbidzIHNwcmVhZC5cbiAgICAgICAgLi4uKGNlbGxzID8ge30gOiB7IG1heENvbHM6IFNwYXRpYWxDaGFydHNDb21wb25lbnQuSEVBVE1BUF9NQVhfQ09MVU1OUyB9KSxcbiAgICAgICAgelNjb3JlOiB0aGlzLmhlYXRtYXBaU2NvcmUsXG4gICAgICB9LFxuICAgICk7XG4gICAgaWYgKCFtYXRyaXgpIHtcbiAgICAgIHRoaXMucHVyZ2VQbG90KCk7XG4gICAgICB0aGlzLm5vdGljZSA9ICdObyBncm91cCBoYXMgZW5vdWdoIG1lYXN1cmVkIGNlbGxzIGZvciBhIG1lYW4uJztcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5ub3RpY2UgPSBudWxsO1xuICAgIHRoaXMuaGVhdG1hcEhpZGRlbiA9IG1hdHJpeC5oaWRkZW5Db2xzO1xuICAgIGNvbnN0IGlucHV0ID0ge1xuICAgICAgcm93czogbWF0cml4LnJvd3MsXG4gICAgICBjb2xzOiBtYXRyaXguY29scyxcbiAgICAgIHZhbHVlczogbWF0cml4LnZhbHVlcyxcbiAgICAgIGNvdW50czogbWF0cml4LmNvdW50cyxcbiAgICAgIHpTY29yZWQ6IHRoaXMuaGVhdG1hcFpTY29yZSxcbiAgICAgIGdyb3VwTGFiZWw6IGNlbGxzID8gJ3NlbGVjdGVkIGNlbGxzJyA6ICh0aGlzLmdyb3VwQnkgPz8gJycpLFxuICAgIH07XG4gICAgYXdhaXQgdGhpcy5kcmF3KGJ1aWxkSGVhdG1hcFRyYWNlcyhpbnB1dCksIGhlYXRtYXBMYXlvdXQoaW5wdXQpKTtcbiAgfVxufVxuIiwiPGRpdiBjbGFzcz1cInN4LWJvZHlcIiAqbmdJZj1cImNvbnRyb2xzXCI+XG5cbiAgPCEtLSBUaGUgY2hhcnQgZm9sbG93cyB3aGF0ZXZlciB0aGUgbWFwIGlzIGNvbG91cmVkIGJ5LCBzbyB0aGUgdHdvIGNhbiBuZXZlclxuICAgICAgIGRpc2FncmVlIGFib3V0IHdoYXQgaXMgYmVpbmcgc2hvd24uIC0tPlxuICA8ZGl2IGNsYXNzPVwic3gtaGVhZFwiPlxuICAgIDxzcGFuIGNsYXNzPVwic3gtc3ViamVjdFwiICpuZ0lmPVwic3ViamVjdFwiPnt7IHN1YmplY3QgfX08L3NwYW4+XG4gICAgPHNwYW4gY2xhc3M9XCJzeC1zdWJqZWN0IHN4LW11dGVkXCIgKm5nSWY9XCIhc3ViamVjdFwiPm5vdGhpbmcgc2VsZWN0ZWQgdG8gY2hhcnQ8L3NwYW4+XG4gICAgPHNwYW4gY2xhc3M9XCJzeC1iYWRnZVwiICpuZ0lmPVwic2VsZWN0aW9uQ291bnRcIj5cbiAgICAgIHt7IHNlbGVjdGlvbkNvdW50IHwgbnVtYmVyIH19IHNlbGVjdGVkXG4gICAgPC9zcGFuPlxuICAgIDxpIGNsYXNzPVwicGkgcGktc3BpbiBwaS1zcGlubmVyIHN4LWJ1c3lcIiAqbmdJZj1cImJ1c3lcIiBhcmlhLWxhYmVsPVwibG9hZGluZ1wiPjwvaT5cbiAgPC9kaXY+XG5cbiAgPGRpdiBjbGFzcz1cInN4LWNvbnRyb2xzXCI+XG4gICAgPHAtc2VsZWN0QnV0dG9uIFtvcHRpb25zXT1cImtpbmRPcHRpb25zXCIgb3B0aW9uTGFiZWw9XCJsYWJlbFwiIG9wdGlvblZhbHVlPVwidmFsdWVcIlxuICAgICAgICAgICAgICAgICAgICBbbmdNb2RlbF09XCJraW5kXCIgKG5nTW9kZWxDaGFuZ2UpPVwib25LaW5kKCRldmVudClcIlxuICAgICAgICAgICAgICAgICAgICBzdHlsZUNsYXNzPVwic3gta2luZFwiPjwvcC1zZWxlY3RCdXR0b24+XG4gICAgPHAtZHJvcGRvd24gW29wdGlvbnNdPVwiZ3JvdXBPcHRpb25zXCIgb3B0aW9uTGFiZWw9XCJsYWJlbFwiIG9wdGlvblZhbHVlPVwidmFsdWVcIlxuICAgICAgICAgICAgICAgIFtuZ01vZGVsXT1cImdyb3VwQnlcIiAobmdNb2RlbENoYW5nZSk9XCJvbkdyb3VwQnkoJGV2ZW50KVwiXG4gICAgICAgICAgICAgICAgcGxhY2Vob2xkZXI9XCJObyBncm91cGluZ1wiIGFwcGVuZFRvPVwiYm9keVwiXG4gICAgICAgICAgICAgICAgW2Rpc2FibGVkXT1cImtpbmQgPT09ICdoaXN0b2dyYW0nXCJcbiAgICAgICAgICAgICAgICBbcFRvb2x0aXBdPVwia2luZCA9PT0gJ2hlYXRtYXAnXG4gICAgICAgICAgICAgICAgICA/ICdUaGUgY2F0ZWdvcmljYWwgY29sdW1uIHRoZSBoZWF0bWFwXFwncyBjb2x1bW5zIGNvbWUgZnJvbSdcbiAgICAgICAgICAgICAgICAgIDogJ1NwbGl0IGEgdmlvbGluIG9yIGJveCBieSBhIGNhdGVnb3JpY2FsIGNvbHVtbidcIlxuICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cInRvcFwiPjwvcC1kcm9wZG93bj5cbiAgICA8IS0tIFdoYXQgdGhlIGNoYXJ0IG9uIHNjcmVlbiBzaG93cywgYW5kIHdoYXQgaXQgbXVzdCBOT1QgYmUgcmVhZCBmb3IuIEVhY2ggb2YgdGhlc2VcbiAgICAgICAgIGFuc3dlcnMgYSBkaWZmZXJlbnQgcXVlc3Rpb24gYW5kIHR3byBhcmUgcm91dGluZWx5IG92ZXItcmVhZCwgc28gdGhlIGNhdmVhdFxuICAgICAgICAgdHJhdmVscyB3aXRoIHRoZSBleHBsYW5hdGlvbi4gLS0+XG4gICAgPGkgY2xhc3M9XCJwaSBwaS1xdWVzdGlvbi1jaXJjbGUgc3gtaGVscFwiXG4gICAgICAgW3BUb29sdGlwXT1cImtpbmRIZWxwXCIgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImxlZnRcIlxuICAgICAgIHRvb2x0aXBTdHlsZUNsYXNzPVwic3gtaGVscC10aXBcIlxuICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwiJ0Fib3V0IHRoZSAnICsga2luZCArICcgY2hhcnQnXCIgdGFiaW5kZXg9XCIwXCI+PC9pPlxuICA8L2Rpdj5cblxuICA8IS0tIFRoZSBoZWF0bWFwJ3Mgcm93cyBhcmUgYSBnZW5lIExJU1QsIHNvIGl0IG5lZWRzIGl0cyBvd24gcGlja2VyOiB0aGUgb3RoZXJcbiAgICAgICBraW5kcyBjaGFydCB3aGF0ZXZlciBzaW5nbGUgc291cmNlIHRoZSBtYXAgaXMgY29sb3VyZWQgYnkuIC0tPlxuICA8ZGl2IGNsYXNzPVwic3gtY29udHJvbHNcIiAqbmdJZj1cImtpbmQgPT09ICdoZWF0bWFwJ1wiPlxuICAgIDxwLW11bHRpU2VsZWN0IFtvcHRpb25zXT1cImdlbmVPcHRpb25zXCIgb3B0aW9uTGFiZWw9XCJsYWJlbFwiIG9wdGlvblZhbHVlPVwidmFsdWVcIlxuICAgICAgICAgICAgICAgICAgIFtuZ01vZGVsXT1cImhlYXRtYXBHZW5lc1wiIChuZ01vZGVsQ2hhbmdlKT1cIm9uSGVhdG1hcEdlbmVzKCRldmVudClcIlxuICAgICAgICAgICAgICAgICAgIFtmaWx0ZXJdPVwidHJ1ZVwiIGZpbHRlckJ5PVwibGFiZWxcIiAob25GaWx0ZXIpPVwib25HZW5lRmlsdGVyKCRldmVudC5maWx0ZXIpXCJcbiAgICAgICAgICAgICAgICAgICBbdmlydHVhbFNjcm9sbF09XCJ0cnVlXCIgW3ZpcnR1YWxTY3JvbGxJdGVtU2l6ZV09XCIzMFwiIGFwcGVuZFRvPVwiYm9keVwiXG4gICAgICAgICAgICAgICAgICAgW3Nob3dUb2dnbGVBbGxdPVwiZmFsc2VcIiBbbWF4U2VsZWN0ZWRMYWJlbHNdPVwiM1wiXG4gICAgICAgICAgICAgICAgICAgc2VsZWN0ZWRJdGVtc0xhYmVsPVwiezB9IGdlbmVzXCJcbiAgICAgICAgICAgICAgICAgICBwbGFjZWhvbGRlcj1cIlBpY2sgZ2VuZXMgZm9yIHRoZSByb3dzXCJcbiAgICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIk9uZSByb3cgcGVyIGdlbmUsIGF2ZXJhZ2VkIHdpdGhpbiBlYWNoIGdyb3VwXCJcbiAgICAgICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJ0b3BcIiBzdHlsZUNsYXNzPVwic3gtZ2VuZXNcIj48L3AtbXVsdGlTZWxlY3Q+XG4gICAgPHAtY2hlY2tib3ggW2JpbmFyeV09XCJ0cnVlXCIgW25nTW9kZWxdPVwiaGVhdG1hcFpTY29yZVwiXG4gICAgICAgICAgICAgICAgKG9uQ2hhbmdlKT1cIm9uSGVhdG1hcFpTY29yZSgkZXZlbnQuY2hlY2tlZClcIlxuICAgICAgICAgICAgICAgIGxhYmVsPVwiei1zY29yZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTY2FsZSBlYWNoIGdlbmUgYWNyb3NzIHRoZSBncm91cHMuIFdpdGhvdXQgaXQgb25lIGhpZ2hseS1leHByZXNzZWQgZ2VuZSBzYXR1cmF0ZXMgdGhlIGNvbG91ciBzY2FsZSBhbmQgdGhlIHJlc3QgcmVhZHMgYXMgYmxhbmtcIlxuICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cInRvcFwiPjwvcC1jaGVja2JveD5cbiAgPC9kaXY+XG5cbiAgPCEtLSBEZXRhY2ggYXBwbGllcyB0byBFVkVSWSBraW5kOiB0aGUgZGlhbG9nIGlzIG9ubHkgc28gd2lkZSwgYW5kIGEgY291bnRzIHBsb3QgcmVhZFxuICAgICAgIGFnYWluc3QgYSBoZWF0bWFwIGlzIHRoZSBzYW1lIGFyZ3VtZW50IGFzIGEgVU1BUCByZWFkIGFnYWluc3QgdGhlIG1hcC4gLS0+XG4gIDxkaXYgY2xhc3M9XCJzeC1jb250cm9sc1wiPlxuICAgIDwhLS0gQSBwaWNrZXIgb25seSB3aGVuIHRoZSBkYXRhc2V0IHB1Ymxpc2hlcyBtb3JlIHRoYW4gb25lIGVtYmVkZGluZy4gLS0+XG4gICAgPHAtZHJvcGRvd24gKm5nSWY9XCJraW5kID09PSAnZW1iZWRkaW5nJyAmJiBlbWJlZGRpbmdzLmxlbmd0aCA+IDFcIlxuICAgICAgICAgICAgICAgIFtvcHRpb25zXT1cImVtYmVkZGluZ3NcIiBvcHRpb25MYWJlbD1cImxhYmVsXCIgb3B0aW9uVmFsdWU9XCJuYW1lXCJcbiAgICAgICAgICAgICAgICBbbmdNb2RlbF09XCJlbWJlZGRpbmc/Lm5hbWVcIiAobmdNb2RlbENoYW5nZSk9XCJvbkVtYmVkZGluZygkZXZlbnQpXCJcbiAgICAgICAgICAgICAgICBhcHBlbmRUbz1cImJvZHlcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiV2hpY2ggZW1iZWRkaW5nIHRvIGRyYXdcIiB0b29sdGlwUG9zaXRpb249XCJ0b3BcIj48L3AtZHJvcGRvd24+XG4gICAgPCEtLSBPZmZlcmVkIG9ubHkgZm9yIGFuIGVtYmVkZGluZyB0aGlzIGRhdGFzZXQgZG9lcyBub3QgcHVibGlzaC4gSXQgcnVucyBpbiBhIFdlYlxuICAgICAgICAgV29ya2VyIG9uIHRoZSBHUFUsIHNvIHRoZSBwYW5lbCBzdGF5cyB1c2FibGUgd2hpbGUgaXQgZ29lcy4gLS0+XG4gICAgPHAtYnV0dG9uICpuZ0lmPVwia2luZCA9PT0gJ2VtYmVkZGluZycgJiYgaXNDb21wdXRhYmxlICYmICFpc0NvbXB1dGVkICYmICFpc0NvbXB1dGluZ1wiXG4gICAgICAgICAgICAgIGljb249XCJwaSBwaS1wbGF5XCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgIFtsYWJlbF09XCInQ29tcHV0ZSAoJyArIGNvbXB1dGVFc3RpbWF0ZUxhYmVsICsgJyknXCJcbiAgICAgICAgICAgICAgcFRvb2x0aXA9XCJSdW4gdC1TTkUgaGVyZSwgaW4gYSBiYWNrZ3JvdW5kIHdvcmtlciBvbiB0aGUgR1BVLCBmcm9tIHRoaXMgZGF0YXNldCdzIFBDQS4gVGhlIHBhbmVsIHN0YXlzIHVzYWJsZSwgYW5kIGl0IGNhbiBiZSBjYW5jZWxsZWQuXCJcbiAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwidG9wXCJcbiAgICAgICAgICAgICAgKG9uQ2xpY2spPVwiY29tcHV0ZUVtYmVkZGluZygpXCI+PC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b24gKm5nSWY9XCJpc0NvbXB1dGluZ1wiXG4gICAgICAgICAgICAgIGljb249XCJwaSBwaS10aW1lc1wiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIiBzZXZlcml0eT1cInNlY29uZGFyeVwiXG4gICAgICAgICAgICAgIGxhYmVsPVwiQ2FuY2VsXCIgKG9uQ2xpY2spPVwiY2FuY2VsQ29tcHV0ZSgpXCI+PC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b24gW2ljb25dPVwiZGV0YWNoZWQgPyAncGkgcGktd2luZG93LW1pbmltaXplJyA6ICdwaSBwaS1leHRlcm5hbC1saW5rJ1wiXG4gICAgICAgICAgICAgIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICBbbGFiZWxdPVwiZGV0YWNoZWQgPyAnUHV0IGJhY2snIDogJ0RldGFjaCdcIlxuICAgICAgICAgICAgICBbcFRvb2x0aXBdPVwiZGV0YWNoZWRcbiAgICAgICAgICAgICAgICA/ICdTaG93IHRoaXMgY2hhcnQgaW4gdGhlIHBhbmVsIGFnYWluJ1xuICAgICAgICAgICAgICAgIDogJ09wZW4gdGhpcyBjaGFydCBpbiBpdHMgb3duIHdpbmRvdywgYWxvbmdzaWRlIHRoaXMgcGFuZWwnXCJcbiAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwidG9wXCJcbiAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRGV0YWNoZWQoKVwiPjwvcC1idXR0b24+XG4gIDwvZGl2PlxuXG4gIDwhLS0gUHJvZ3Jlc3Mgd2hpbGUgYSBjb21wdXRhdGlvbiBydW5zLiBgbW9kZT1cImluZGV0ZXJtaW5hdGVcImAgdW50aWwgdGhlIGZpcnN0IHJlcG9ydCxcbiAgICAgICBiZWNhdXNlIHRoZSBydW4gc3RhcnRzIHdpdGggYSBuZWlnaGJvdXIgZ3JhcGggYW5kIHRoZXJlIGlzIG5vdGhpbmcgaG9uZXN0IHRvIHNob3dcbiAgICAgICBhcyBhIHBlcmNlbnRhZ2UgeWV0LiAtLT5cbiAgPGRpdiBjbGFzcz1cInN4LWNvbXB1dGVcIiAqbmdJZj1cImlzQ29tcHV0aW5nXCI+XG4gICAgPHAtcHJvZ3Jlc3NCYXJcbiAgICAgIFttb2RlXT1cImNvbXB1dGVGcmFjdGlvbiA9PT0gbnVsbCA/ICdpbmRldGVybWluYXRlJyA6ICdkZXRlcm1pbmF0ZSdcIlxuICAgICAgW3ZhbHVlXT1cImNvbXB1dGVGcmFjdGlvbiA9PT0gbnVsbCA/IDAgOiAoY29tcHV0ZUZyYWN0aW9uICogMTAwKVwiXG4gICAgICBbc2hvd1ZhbHVlXT1cImNvbXB1dGVGcmFjdGlvbiAhPT0gbnVsbFwiXG4gICAgICBbc3R5bGVdPVwieyBoZWlnaHQ6ICcwLjZyZW0nIH1cIj48L3AtcHJvZ3Jlc3NCYXI+XG4gICAgPHNtYWxsIGNsYXNzPVwic3gtaGludFwiPlxuICAgICAge3sgY29tcHV0ZUZyYWN0aW9uID09PSBudWxsID8gJ0J1aWxkaW5nIHRoZSBuZWlnaGJvdXIgZ3JhcGjigKYnIDogJ09wdGltaXNpbmfigKYnIH19XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiY29tcHV0ZUJhY2tlbmRcIj4gb24ge3sgY29tcHV0ZUJhY2tlbmQgfX08L25nLWNvbnRhaW5lcj5cbiAgICA8L3NtYWxsPlxuICAgIDxzbWFsbCBjbGFzcz1cInN4LWhpbnQgc3gtd2FyblwiICpuZ0lmPVwiY29tcHV0ZU1lc3NhZ2VcIj57eyBjb21wdXRlTWVzc2FnZSB9fTwvc21hbGw+XG4gIDwvZGl2PlxuICA8c21hbGwgY2xhc3M9XCJzeC1oaW50IHN4LXdhcm5cIiAqbmdJZj1cImNvbXB1dGVFcnJvclwiPnt7IGNvbXB1dGVFcnJvciB9fTwvc21hbGw+XG4gIDwhLS0gV2l0aGhlbGQgcmF0aGVyIHRoYW4gb2ZmZXJlZCB3aXRoIGEgd2FybmluZzogYW4gaG91ci1sb25nIGpvYiBpcyBub3QgYSBjaG9pY2UgYVxuICAgICAgIHJlYWRlciBjYW4gbWVhbmluZ2Z1bGx5IGNvbnNlbnQgdG8gZnJvbSBhIGRpYWxvZy4gLS0+XG4gIDxzbWFsbCBjbGFzcz1cInN4LWhpbnRcIiAqbmdJZj1cImtpbmQgPT09ICdlbWJlZGRpbmcnICYmIHRzbmVUb29MYXJnZVwiPnt7IHRzbmVUb29MYXJnZU5vdGUgfX08L3NtYWxsPlxuXG4gIDxkaXYgW2lkXT1cImNoYXJ0RGl2XCIgY2xhc3M9XCJzeC1wbG90XCIgKm5nSWY9XCIhZGV0YWNoZWRcIj48L2Rpdj5cblxuICA8IS0tIERldGFjaGVkOiB0aGUgY2hhcnQga2VlcHMgZHJhd2luZywgaW4gaXRzIG93biB3aW5kb3cuIFNheXMgc28gcmF0aGVyIHRoYW5cbiAgICAgICBsZWF2aW5nIGEgYmxhbmsgcGFuZWwgdGhhdCBsb29rcyBicm9rZW4uIC0tPlxuICA8ZGl2IGNsYXNzPVwic3gtZGV0YWNoZWRcIiAqbmdJZj1cImRldGFjaGVkXCI+XG4gICAgU2hvd24gaW4gaXRzIG93biB3aW5kb3cuXG4gIDwvZGl2PlxuXG4gIDxuZy1jb250YWluZXIgKm5nVGVtcGxhdGVPdXRsZXQ9XCJoaW50c1wiPjwvbmctY29udGFpbmVyPlxuXG48L2Rpdj5cblxuPCEtLSBEZWNsYXJlZCBvbmNlIGFuZCByZW5kZXJlZCBpbiBib3RoIHRoZSBwYW5lbCBhbmQgdGhlIGRldGFjaGVkIHdpbmRvdzogdGhlIGxhZGRlciBpc1xuICAgICBkZWxpYmVyYXRlbHkgbm90IG11dHVhbGx5IGV4Y2x1c2l2ZSAoYSBoZWF0bWFwIHN0aWxsIG5lZWRzIGl0cyBncm91cGluZyBwcm9tcHQpLCBzb1xuICAgICBjb2xsYXBzaW5nIGl0IHRvIGEgc2luZ2xlIGV4cHJlc3Npb24gd291bGQgZHJvcCBhIGhpbnQgcmF0aGVyIHRoYW4gbW92ZSBpdC4gLS0+XG48bmctdGVtcGxhdGUgI2hpbnRzPlxuICA8c21hbGwgY2xhc3M9XCJzeC1oaW50XCIgKm5nSWY9XCJub3RpY2VcIj57eyBub3RpY2UgfX08L3NtYWxsPlxuICA8c21hbGwgY2xhc3M9XCJzeC1oaW50XCIgKm5nSWY9XCIhbm90aWNlICYmIHN1Z2dlc3RzR3JvdXBpbmdcIj5cbiAgICBQaWNrIGEgZ3JvdXBpbmcgdG8gY29tcGFyZSBjYXRlZ29yaWVzLlxuICA8L3NtYWxsPlxuICA8c21hbGwgY2xhc3M9XCJzeC1oaW50XCIgKm5nSWY9XCIhbm90aWNlICYmIHNlbGVjdGlvbkNvdW50ICYmIGtpbmQgPT09ICdoaXN0b2dyYW0nXCI+XG4gICAgVGhlIHNlbGVjdGlvbiBpcyBvdmVybGFpZCBvbiB0aGUgZnVsbCBkaXN0cmlidXRpb24uXG4gIDwvc21hbGw+XG4gIDxzbWFsbCBjbGFzcz1cInN4LWhpbnRcIiAqbmdJZj1cIiFub3RpY2UgJiYgc2VsZWN0aW9uQ291bnQgJiYga2luZCAhPT0gJ2hpc3RvZ3JhbScgJiYga2luZCAhPT0gJ2hlYXRtYXAnXCI+XG4gICAgU2hvd2luZyB0aGUgc2VsZWN0aW9uIG9ubHkuXG4gIDwvc21hbGw+XG4gIDxzbWFsbCBjbGFzcz1cInN4LWhpbnRcIiAqbmdJZj1cIiFub3RpY2UgJiYga2luZCA9PT0gJ2hlYXRtYXAnXCI+e3sgaGVhdG1hcE5vdGUgfX08L3NtYWxsPlxuICA8c21hbGwgY2xhc3M9XCJzeC1oaW50XCIgKm5nSWY9XCIhbm90aWNlICYmIGtpbmQgPT09ICdlbWJlZGRpbmcnXCI+e3sgZW1iZWRkaW5nTm90ZSB9fTwvc21hbGw+XG48L25nLXRlbXBsYXRlPlxuXG48IS0tIFRoZSBkZXRhY2hlZCBjaGFydC4gSXRzIG93biBub24tbW9kYWwsIGRyYWdnYWJsZSwgcmVzaXphYmxlIHdpbmRvdyBzbyBpdCBjYW4gYmUgcHV0XG4gICAgIGJlc2lkZSB0aGUgc3BhdGlhbC1vbWljcyBkaWFsb2cuIGBhcHBlbmRUbz1cImJvZHlcImAga2VlcHMgaXQgb3V0IG9mIHRoZSBkaWFsb2cnc1xuICAgICBzdGFja2luZyBjb250ZXh0LCBvciBpdCB3b3VsZCBiZSBjbGlwcGVkIGJ5IHRoZSBwYW5lbCBpdCBpcyBtZWFudCB0byBzaXQgbmV4dCB0by4gLS0+XG48cC1kaWFsb2cgKm5nSWY9XCJkZXRhY2hlZFwiXG4gICAgICAgICAgW2hlYWRlcl09XCJkZXRhY2hlZFRpdGxlXCJcbiAgICAgICAgICBbdmlzaWJsZV09XCJ0cnVlXCIgKHZpc2libGVDaGFuZ2UpPVwidG9nZ2xlRGV0YWNoZWQoKVwiXG4gICAgICAgICAgW21vZGFsXT1cImZhbHNlXCIgW3Jlc2l6YWJsZV09XCJ0cnVlXCIgW2RyYWdnYWJsZV09XCJ0cnVlXCJcbiAgICAgICAgICBwb3NpdGlvbj1cInJpZ2h0XCIgYXBwZW5kVG89XCJib2R5XCJcbiAgICAgICAgICAob25TaG93KT1cIm9uRGV0YWNoZWRXaW5kb3dTaG93bigpXCJcbiAgICAgICAgICAob25SZXNpemVFbmQpPVwib25EZXRhY2hlZFJlc2l6ZUVuZCgpXCJcbiAgICAgICAgICBbc3R5bGVdPVwieyB3aWR0aDogJzM0cmVtJywgaGVpZ2h0OiAnMzByZW0nIH1cIlxuICAgICAgICAgIFtjb250ZW50U3R5bGVdPVwie1xuICAgICAgICAgICAgZGlzcGxheTogJ2ZsZXgnLCBmbGV4RGlyZWN0aW9uOiAnY29sdW1uJywgZ2FwOiAnMC4yNXJlbScsXG4gICAgICAgICAgICBwYWRkaW5nOiAnMC40cmVtIDAuNnJlbSAwLjVyZW0nLCBmbGV4OiAnMSAxIGF1dG8nLCBtaW5IZWlnaHQ6ICcwJyxcbiAgICAgICAgICAgIG92ZXJmbG93OiAnYXV0bydcbiAgICAgICAgICB9XCJcbiAgICAgICAgICBzdHlsZUNsYXNzPVwic3BhdGlhbC1kZXRhY2hlZC1kaWFsb2dcIj5cbiAgPGRpdiBbaWRdPVwiZGV0YWNoZWREaXZcIiBjbGFzcz1cInN4LWRldGFjaGVkLXBsb3RcIlxuICAgICAgIFtjbGFzcy5zeC1wbG90LWZpeGVkXT1cImhhc0ZpeGVkSGVpZ2h0XCI+PC9kaXY+XG4gIDxuZy1jb250YWluZXIgKm5nVGVtcGxhdGVPdXRsZXQ9XCJoaW50c1wiPjwvbmctY29udGFpbmVyPlxuPC9wLWRpYWxvZz5cbiJdfQ==
773
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BhdGlhbC1jaGFydHMuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9zcGF0aWFsLWNvbnRyb2xzL3NwYXRpYWwtY2hhcnRzL3NwYXRpYWwtY2hhcnRzLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvc3BhdGlhbC1jb250cm9scy9zcGF0aWFsLWNoYXJ0cy9zcGF0aWFsLWNoYXJ0cy5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBRUwsdUJBQXVCLEVBQ3ZCLGlCQUFpQixFQUNqQixTQUFTLEVBQ1QsVUFBVSxFQUNWLE1BQU0sRUFDTixLQUFLLEVBR0wsTUFBTSxFQUNOLE1BQU0sR0FDUCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUMvRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUNoRSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzdDLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNsRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUMxRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDaEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUVyQyxPQUFPLEVBQUUsVUFBVSxFQUFpQyxNQUFNLHFDQUFxQyxDQUFDO0FBRWhHLE9BQU8sRUFBb0Msb0JBQW9CLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUN2RyxPQUFPLEVBQXdCLGNBQWMsRUFBRSxhQUFhLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUN0RyxPQUFPLEVBQWlCLGFB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QUFPLEVBQUUsSUFBSSxDQUFDLGFBQWE7WUFDM0IsVUFBVSxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUMsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sSUFBSSxFQUFFLENBQUM7U0FDNUQsQ0FBQztRQUNGLE1BQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxLQUFLLENBQUMsRUFBRSxhQUFhLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUNuRSxDQUFDOzhHQTVzQlUsc0JBQXNCLGtCQXlMYixVQUFVO2tHQXpMbkIsc0JBQXNCLHdHQ3hIbkMsMjVJQWlIQSxvbURETkksWUFBWSxpV0FDWixXQUFXLDhWQUNYLGNBQWMsdXRDQUNkLGtCQUFrQix3VkFDbEIsYUFBYSxzWEFDYiwrQkFBK0IsMktBQy9CLGlDQUFpQywyUkFDakMsMkJBQTJCOzsyRkFNbEIsc0JBQXNCO2tCQWpCbEMsU0FBUzsrQkFDRSxnQkFBZ0IsY0FDZCxJQUFJLFdBQ1A7d0JBQ1AsWUFBWTt3QkFDWixXQUFXO3dCQUNYLGNBQWM7d0JBQ2Qsa0JBQWtCO3dCQUNsQixhQUFhO3dCQUNiLCtCQUErQjt3QkFDL0IsaUNBQWlDO3dCQUNqQywyQkFBMkI7cUJBQzVCLG1CQUdnQix1QkFBdUIsQ0FBQyxNQUFNOzswQkEyTGxDLE1BQU07MkJBQUMsVUFBVTt5Q0FsTGpCLE1BQU07c0JBQWxCLEtBQUsiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBBZnRlclZpZXdJbml0LFxuICBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSxcbiAgQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gIENvbXBvbmVudCxcbiAgRGVzdHJveVJlZixcbiAgSW5qZWN0LFxuICBJbnB1dCxcbiAgT25EZXN0cm95LFxuICBPbkluaXQsXG4gIGluamVjdCxcbiAgTmdab25lLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IG1hcmtGb3JDaGVja0luWm9uZSB9IGZyb20gJy4uLy4uL3V0aWwvbWFyay1mb3ItY2hlY2snO1xuaW1wb3J0IHsgdGFrZVVudGlsRGVzdHJveWVkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZS9yeGpzLWludGVyb3AnO1xuaW1wb3J0IHsgQ29tbW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcbmltcG9ydCB7IEZvcm1zTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHsgRHJvcGRvd25Nb2R1bGUgfSBmcm9tICdwcmltZW5nL2Ryb3Bkb3duJztcbmltcG9ydCB7IFNlbGVjdEJ1dHRvbk1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvc2VsZWN0YnV0dG9uJztcbmltcG9ydCB7IFRvb2x0aXBNb2R1bGUgfSBmcm9tICdwcmltZW5nL3Rvb2x0aXAnO1xuaW1wb3J0IHsgY29tYmluZUxhdGVzdCB9IGZyb20gJ3J4anMnO1xuXG5pbXBvcnQgeyBWSVNVQUxJWkVSLCBJVmlzdWFsaXplciwgSVNwYXRpYWxDb250cm9scyB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy92aXN1YWxpemVyLmNvbnRyYWN0JztcbmltcG9ydCB7IFNwYXRpYWxFbWJlZGRpbmdNZXRhIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL3NwYXRpYWwtZGF0YXNldC5jb250cmFjdCc7XG5pbXBvcnQgeyBTcGF0aWFsQ29sb3JCeSwgU3BhdGlhbFZpZXdTdGF0ZSwgREVGQVVMVF9TUEFUSUFMX1ZJRVcgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvZGlzcGxheS10eXBlcyc7XG5pbXBvcnQgeyBTcGF0aWFsU2VsZWN0aW9uTWFzaywgZW1wdHlTZWxlY3Rpb24sIG1hc2tUb0luZGljZXMgfSBmcm9tICcuLi8uLi9zcGF0aWFsL3NwYXRpYWwtc2VsZWN0aW9uJztcbmltcG9ydCB7IEhlYXRtYXBNYXRyaXgsIGNlbGxzQXNHcm91cHMgfSBmcm9tICcuLi8uLi9zcGF0aWFsL3NwYXRpYWwtaGVhdG1hcCc7XG5pbXBvcnQgeyBjb21wdXRlSGVhdG1hcE1hdHJpeEFzeW5jIH0gZnJvbSAnLi4vLi4vd29ya2Vycy9zcGF0aWFsLW1hdGgnO1xuaW1wb3J0IHsgU3VwZXJzZWRlIH0gZnJvbSAnLi4vLi4vdXRpbC9zdXBlcnNlZGUnO1xuaW1wb3J0IHtcbiAgQlJPV1NFUl9UU05FX01BWF9PQlNFUlZBVElPTlMsXG4gIEVtYmVkZGluZ0NvbXB1dGVDb29yZGluYXRvcixcbiAgRW1iZWRkaW5nQ29tcHV0ZVN0YXRlLFxufSBmcm9tICcuLi8uLi9zcGF0aWFsL2VtYmVkZGluZy1jb21wdXRlLWNvb3JkaW5hdG9yJztcbmltcG9ydCB7IENoYXJ0RGF0YU1vZGVsIH0gZnJvbSAnLi9jaGFydC1kYXRhLW1vZGVsJztcbmltcG9ydCB7IEVNQkVERElOR19DT05GSUcsIFBsb3RseUNoYXJ0SG9zdCB9IGZyb20gJy4vcGxvdGx5LWNoYXJ0LWhvc3QnO1xuaW1wb3J0IHsgQ2hhcnRLaW5kT3B0aW9uLCBjaGFydEtpbmRPcHRpb25zLCBlbWJlZGRpbmdOb3RlLCBoZWF0bWFwTm90ZSwga2luZEhlbHAgfSBmcm9tICcuL2NoYXJ0LWhlbHAnO1xuaW1wb3J0IHtcbiAgT21pY3NDaGFydEtpbmQsXG4gIGJlbmVmaXRzRnJvbUdyb3VwaW5nLFxuICBidWlsZENvdW50VHJhY2VzLFxuICBidWlsZEhlYXRtYXBUcmFjZXMsXG4gIGNvdW50QnlDYXRlZ29yeSxcbiAgYnVpbGRPbWljc1RyYWNlcyxcbiAgYnVpbGRFbWJlZGRpbmdUcmFjZXMsXG4gIGNvdW50c0xheW91dCxcbiAgaGVhdG1hcExheW91dCxcbiAgb21pY3NMYXlvdXQsXG4gIGVtYmVkZGluZ0xheW91dCxcbn0gZnJvbSAnLi4vLi4vaW1wbGVtZW50YXRpb25zL3Bsb3RseS9vbWljcy10cmFjZS1idWlsZGVycyc7XG5pbXBvcnQgeyBTcGF0aWFsSGVhdG1hcENvbnRyb2xzQ29tcG9uZW50IH0gZnJvbSAnLi9zcGF0aWFsLWhlYXRtYXAtY29udHJvbHMvc3BhdGlhbC1oZWF0bWFwLWNvbnRyb2xzLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBTcGF0aWFsRW1iZWRkaW5nQ29udHJvbHNDb21wb25lbnQgfSBmcm9tICcuL3NwYXRpYWwtZW1iZWRkaW5nLWNvbnRyb2xzL3NwYXRpYWwtZW1iZWRkaW5nLWNvbnRyb2xzLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBTcGF0aWFsQ2hhcnRXaW5kb3dDb21wb25lbnQgfSBmcm9tICcuL3NwYXRpYWwtY2hhcnQtd2luZG93L3NwYXRpYWwtY2hhcnQtd2luZG93LmNvbXBvbmVudCc7XG5cbi8qKlxuICogV2lkZW4gdGhlIGhlbHAgdG9vbHRpcCwgb25jZSBwZXIgZG9jdW1lbnQuXG4gKlxuICogUHJpbWVORyBjYXBzIGAucC10b29sdGlwYCBhdCAxMi41cmVtIGFuZCBhcHBlbmRzIGl0IHRvIGA8Ym9keT5gLCBzbyBhIGNvbXBvbmVudFxuICogc3R5bGVzaGVldCBjYW5ub3QgcmVhY2ggaXQg4oCUIHRoZSBzYW1lIHJlYXNvbiB0aGUgZGV0YWNoZWQgZGlhbG9nIGlzIHNpemVkIGlubGluZS4gQXRcbiAqIDIwMCBweCB0aGUgY2hhcnQgZXhwbGFuYXRpb25zIHJlbmRlciBhcyBhIHRhbGwgdGhyZWFkIG9mIHR3by13b3JkIGxpbmVzLCB3aGljaCBpcyB3b3JzZVxuICogdGhhbiBub3Qgc2hvd2luZyB0aGVtLiBUaGUgbGlicmFyeSBzaGlwcyBubyBnbG9iYWwgc3R5bGVzaGVldCB0byBwdXQgdGhpcyBpbiwgc28gb25lXG4gKiBydWxlIGlzIGluamVjdGVkIGluc3RlYWQ6IHNjb3BlZCB0byB0aGlzIGNvbXBvbmVudCdzIG93biB0b29sdGlwIGNsYXNzLCBpZGVtcG90ZW50LCBhbmRcbiAqIHNraXBwZWQgd2hlcmUgdGhlcmUgaXMgbm8gZG9jdW1lbnQuXG4gKi9cbmNvbnN0IEhFTFBfVElQX1NUWUxFX0lEID0gJ3N4LWhlbHAtdGlwLXN0eWxlJztcbmZ1bmN0aW9uIGVuc3VyZUhlbHBUaXBTdHlsZSgpOiB2b2lkIHtcbiAgaWYgKHR5cGVvZiBkb2N1bWVudCA9PT0gJ3VuZGVmaW5lZCcpIHJldHVybjtcbiAgaWYgKGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKEhFTFBfVElQX1NUWUxFX0lEKSkgcmV0dXJuO1xuICBjb25zdCBzdHlsZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N0eWxlJyk7XG4gIHN0eWxlLmlkID0gSEVMUF9USVBfU1RZTEVfSUQ7XG4gIHN0eWxlLnRleHRDb250ZW50ID1cbiAgICAnLnN4LWhlbHAtdGlwIC5wLXRvb2x0aXAtdGV4dCB7IG1heC13aWR0aDogbm9uZTsgd2lkdGg6IDI0cmVtOyAnICtcbiAgICAnbGluZS1oZWlnaHQ6IDEuNDU7IH1cXG4uc3gtaGVscC10aXAgeyBtYXgtd2lkdGg6IG5vbmU7IH0nO1xuICBkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKHN0eWxlKTtcbn1cblxuLyoqIFBlci1pbnN0YW5jZSBjaGFydC1kaXYgaWQgc291cmNlIOKAlCBzZWUge0BsaW5rIFNwYXRpYWxDaGFydHNDb21wb25lbnQuY2hhcnREaXZ9LiAqL1xubGV0IGNoYXJ0SW5zdGFuY2VTZXEgPSAwO1xuXG4vKipcbiAqIERpc3RyaWJ1dGlvbiBjaGFydHMgb3ZlciB0aGUgc3BhdGlhbC1vbWljcyB2YWx1ZXMg4oCUIGhpc3RvZ3JhbSwgdmlvbGluLCBib3gg4oCUXG4gKiAqKmxpbmtlZCB0byB0aGUgbWFwKio6IHRoZXkgY2hhcnQgd2hhdGV2ZXIgdGhlIG1hcCBpcyBjb2xvdXJlZCBieSwgYW5kIG5hcnJvd1xuICogdG8gdGhlIGN1cnJlbnQgc2VsZWN0aW9uLlxuICpcbiAqIEVtYmVkZGVkIElOU0lERSBgPHNwYXRpYWwtY29udHJvbHM+YCByYXRoZXIgdGhhbiBvd25pbmcgYSBkaWFsb2cgb2YgaXRzIG93bjpcbiAqIHRoZSB0d28gYXJlIG9uZSB3b3JrZmxvdyAoY2hhbmdlIHRoZSBjb2xvdXIgc291cmNlLCB3YXRjaCB0aGUgZGlzdHJpYnV0aW9uXG4gKiBtb3ZlKSwgYW5kIHNwbGl0dGluZyB0aGVtIGFjcm9zcyB0d28gZmxvYXRpbmcgd2luZG93cyBtYWRlIHRoZSBsaW5rIGhhcmRlciB0b1xuICogc2VlLCBub3QgZWFzaWVyLiBJdCBzdGF5cyBhIHNlcGFyYXRlIGNvbXBvbmVudCBzbyB0aGUgcHVyZSB0cmFjZSBidWlsZGVycyBhbmRcbiAqIGl0cyBvd24gdGVzdHMga2VlcCB0aGVpciBib3VuZGFyeS5cbiAqXG4gKiBDaGFydGluZyB0aGUgYWN0aXZlIGNvbG91ciBzb3VyY2UgcmF0aGVyIHRoYW4gb2ZmZXJpbmcgaXRzIG93biB2YWx1ZSBwaWNrZXIgaXNcbiAqIGRlbGliZXJhdGU6IGl0IGtlZXBzIG9uZSBzb3VyY2Ugb2YgdHJ1dGgsIHNvIHdoYXQgeW91IHNlZSBvbiB0aGUgbWFwIGFuZCB3aGF0XG4gKiB5b3Ugc2VlIGluIHRoZSBjaGFydCBjYW5ub3QgZGlzYWdyZWUsIGFuZCBpdCBtYWtlcyB0aGUgbGluayBsZWdpYmxlIHdpdGhvdXRcbiAqIGV4cGxhaW5pbmcgaXQuXG4gKlxuICogRGVwZW5kcyBvbmx5IG9uIHtAbGluayBJU3BhdGlhbENvbnRyb2xzfSB0aHJvdWdoIHRoZSBgVklTVUFMSVpFUmAgY29udHJhY3RcbiAqIHRva2VuLCBsaWtlIGBTcGF0aWFsQ29udHJvbHNDb21wb25lbnRgLiBUcmFjZSBidWlsZGluZyBsaXZlcyBpbiB0aGUgcHVyZVxuICogYG9taWNzLXRyYWNlLWJ1aWxkZXJzYDsgdGhpcyBjb21wb25lbnQgb25seSBtb3ZlcyBkYXRhIGFuZCBvd25zIHRoZSBkaXYuXG4gKlxuICogT25QdXNoOiBpdHMgc3RhdGUgbGl2ZXMgaW4gY29sbGFib3JhdG9ycyB0aGF0IGNoYW5nZSBhc3luY2hyb25vdXNseSAodGhlIGRhdGEgbG9hZHMsXG4gKiB0aGUgdC1TTkUgcnVuLCB0aGUgcGxvdCBob3N0KSwgc28gdGhlIGNvbnRyb2xzJyBzdHJlYW1zLCB0aGUgcnVuJ3Mgc3RhdGUsIGV2ZXJ5XG4gKiBmaW5pc2hlZCBkcmF3IGFuZCBldmVyeSBsb2FkIHN0YXJ0IG1hcmsgaXQgZm9yIGNoZWNrLlxuICovXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdzcGF0aWFsLWNoYXJ0cycsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtcbiAgICBDb21tb25Nb2R1bGUsXG4gICAgRm9ybXNNb2R1bGUsXG4gICAgRHJvcGRvd25Nb2R1bGUsXG4gICAgU2VsZWN0QnV0dG9uTW9kdWxlLFxuICAgIFRvb2x0aXBNb2R1bGUsXG4gICAgU3BhdGlhbEhlYXRtYXBDb250cm9sc0NvbXBvbmVudCxcbiAgICBTcGF0aWFsRW1iZWRkaW5nQ29udHJvbHNDb21wb25lbnQsXG4gICAgU3BhdGlhbENoYXJ0V2luZG93Q29tcG9uZW50LFxuICBdLFxuICB0ZW1wbGF0ZVVybDogJy4vc3BhdGlhbC1jaGFydHMuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi9zcGF0aWFsLWNoYXJ0cy5jb21wb25lbnQuc2NzcyddLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbn0pXG5leHBvcnQgY2xhc3MgU3BhdGlhbENoYXJ0c0NvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgQWZ0ZXJWaWV3SW5pdCwgT25EZXN0cm95IHtcbiAgLyoqXG4gICAqIFdoZXRoZXIgdGhlIGhvc3QgcGFuZWwgaXMgb24gc2NyZWVuLiBUaGUgY2hhcnQgb25seSBkcmF3cyB3aGVuIGl0IGlzOiB0aGVcbiAgICogZW5jbG9zaW5nIGRpYWxvZyBjcmVhdGVzIGFuZCBkZXN0cm95cyBpdHMgY29udGVudCwgc28gd2l0aG91dCB0aGlzIHRoZVxuICAgKiBjb21wb25lbnQgd291bGQgbW91bnQgd2l0aCBhIGRpdiBwcmVzZW50IGJ1dCBubyBzdGF0ZSBjaGFuZ2UgdG8gdHJpZ2dlciBhXG4gICAqIGZpcnN0IGRyYXcuXG4gICAqL1xuICBASW5wdXQoKSBzZXQgYWN0aXZlKG9uOiBib29sZWFuKSB7XG4gICAgY29uc3Qgd2FzID0gdGhpcy5pc0FjdGl2ZTtcbiAgICB0aGlzLmlzQWN0aXZlID0gb247XG4gICAgLy8gRGVmZXJyZWQgYnkgYSB0YXNrOiB3aGVuIGEgY29sbGFwc2VkIHNlY3Rpb24gZXhwYW5kcywgdGhlIGhvc3QgaXMgc3RpbGxcbiAgICAvLyBgaGlkZGVuYCBhdCB0aGUgbW9tZW50IHRoaXMgc2V0dGVyIHJ1bnMsIHNvIHBsb3R0aW5nIG5vdyB3b3VsZCBzaXplIHRoZVxuICAgIC8vIGNoYXJ0IHRvIGEgemVyby1oZWlnaHQgZGl2LlxuICAgIGlmIChvbiAmJiAhd2FzKSBzZXRUaW1lb3V0KCgpID0+IHZvaWQgdGhpcy5yZWxvYWQoKSwgMCk7XG4gICAgZWxzZSBpZiAob24pIHZvaWQgdGhpcy5yZWxvYWQoKTtcbiAgfVxuICBnZXQgYWN0aXZlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmlzQWN0aXZlO1xuICB9XG4gIHByaXZhdGUgaXNBY3RpdmUgPSB0cnVlO1xuXG4gIC8qKiBPbmUgaW5jcmVtZW50IHBlciBJTlNUQU5DRSDigJQgYSBzdGF0aWMgaW5pdGlhbGl6ZXIgd291bGQgcnVuIG9uY2UgcGVyIGNsYXNzIGFuZFxuICAgKiAgaGFuZCBldmVyeSBpbnN0YW5jZSB0aGUgc2FtZSBpZCwgd2hpY2ggaXMgdGhlIGJ1ZyB0aGlzIGlkIGV4aXN0cyB0byBhdm9pZC4gRGVjbGFyZWRcbiAgICogIGJlZm9yZSB0aGUgaWRzIGJlY2F1c2UgZmllbGQgaW5pdGlhbGl6ZXJzIHJ1biBpbiBvcmRlci4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBzZXEgPSArK2NoYXJ0SW5zdGFuY2VTZXE7XG4gIC8qKiBQZXItaW5zdGFuY2UsIGZvciB0aGUgc2FtZSByZWFzb24gdGhlIHZpc3VhbGl6ZXIncyBwbG90IGRpdiBpc1xuICAgKiAgKGB2aXN1YWxpemVyLmNvbXBvbmVudC50c2ApOiB0d28gbW91bnRlZCBjaGFydHMgc2hhcmluZyBvbmUgRE9NIGlkIG1lYW5zXG4gICAqICBgZ2V0RWxlbWVudEJ5SWRgIGhhbmRzIGJvdGggb2YgdGhlbSB0aGUgZmlyc3QgZWxlbWVudCwgc28gb25lIGluc3RhbmNlIGRyYXdzXG4gICAqICBpbnRvIOKAlCBvciBwdXJnZXMg4oCUIHRoZSBvdGhlcidzIGNhbnZhcy4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGNoYXJ0RGl2ID0gYHNwYXRpYWwtY2hhcnRzLXBsb3QtJHt0aGlzLnNlcX1gO1xuICAvKipcbiAgICogVGhlIGRpdiBpbnNpZGUgdGhlIGRldGFjaGVkIHdpbmRvdy5cbiAgICpcbiAgICogQSBzZXBhcmF0ZSBpZCByYXRoZXIgdGhhbiBtb3ZpbmcgdGhlIGV4aXN0aW5nIGRpdjogQW5ndWxhciBkZXN0cm95cyBhbmQgcmVjcmVhdGVzIGFcbiAgICogZGl2IGFjcm9zcyBhbiBgKm5nSWZgLCBzbyBQbG90bHkgd291bGQgYmUgbGVmdCBob2xkaW5nIGEgZGV0YWNoZWQgbm9kZS4gVHdvIGRpdnMgYW5kXG4gICAqIGEgcmVkcmF3IGlzIGJvdGggc2ltcGxlciBhbmQgY29ycmVjdC5cbiAgICpcbiAgICogT05FIGlzIGVub3VnaCBmb3IgZXZlcnkga2luZCwgYmVjYXVzZSBvbmx5IHRoZSBhY3RpdmUga2luZCBpcyBldmVyIHBsb3R0ZWQuIERldGFjaGluZ1xuICAgKiBpcyByZW1lbWJlcmVkIHBlciBraW5kLCBidXQgYXQgbW9zdCBvbmUgd2luZG93IGlzIG9wZW4gYXQgYSB0aW1lLlxuICAgKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGRldGFjaGVkRGl2ID0gYHNwYXRpYWwtY2hhcnRzLWRldGFjaGVkLSR7dGhpcy5zZXF9YDtcblxuICAvKipcbiAgICogV2hldGhlciB0aGUgY2hhcnQgaXMgc2hvd24gaW4gaXRzIG93biB3aW5kb3cgcmF0aGVyIHRoYW4gaW5saW5lLlxuICAgKlxuICAgKiBEZXRhY2hlZCwgaXQgc2l0cyBBTE9OR1NJREUgdGhlIHNwYXRpYWwtb21pY3MgZGlhbG9nIGluc3RlYWQgb2YgaW5zaWRlIGl0IOKAlCB3aGljaCBpc1xuICAgKiB0aGUgcG9pbnQ6IHRoZXNlIGFyZSByZWFkIGFnYWluc3QgdGhlIG1hcCwgbm90IGluc3RlYWQgb2YgaXQuIFRoZSBkaWFsb2cgaXMgb25seSBzb1xuICAgKiB3aWRlLlxuICAgKlxuICAgKiBPbmUgZmxhZyBmb3IgdGhlIFBBTkVMLCBub3Qgb25lIHBlciBraW5kLiBXaGVyZSB0aGUgd2luZG93IGlzLCBpcyBhIHByb3BlcnR5IG9mIHRoZVxuICAgKiB3b3Jrc3BhY2Ugc29tZW9uZSBoYXMgYXJyYW5nZWQsIG5vdCBvZiB0aGUgdGFiIHRoZXkgaGFwcGVuIHRvIGJlIG9uOiBoYXZpbmcgcHV0IHRoZVxuICAgKiBjaGFydHMgYmVzaWRlIHRoZSBtYXAsIHN3aXRjaGluZyBmcm9tIHRoZSBoZWF0bWFwIHRvIHRoZSBjb3VudHMgc2hvdWxkIHN3YXAgd2hhdCB0aGVcbiAgICogd2luZG93IHNob3dzLCBub3QgeWFuayBpdCBiYWNrIGludG8gdGhlIGRpYWxvZyBhbmQgbWFrZSB0aGVtIGRldGFjaCBpdCBhZ2Fpbi5cbiAgICovXG4gIHByb3RlY3RlZCBkZXRhY2hlZCA9IGZhbHNlO1xuICAvKipcbiAgICogTGFyZ2VzdCBkYXRhc2V0IHRoaXMgd2lsbCBvZmZlciB0byBlbWJlZCBpbiB0aGUgYnJvd3NlciDigJQgc2VlXG4gICAqIHtAbGluayBCUk9XU0VSX1RTTkVfTUFYX09CU0VSVkFUSU9OU30uXG4gICAqL1xuICBzdGF0aWMgcmVhZG9ubHkgQlJPV1NFUl9UU05FX01BWF9PQlNFUlZBVElPTlMgPSBCUk9XU0VSX1RTTkVfTUFYX09CU0VSVkFUSU9OUztcblxuICAvKiogVGhlIHQtU05FIGNvbXB1dGVkIGluIHRoaXMgYnJvd3Nlcjogd2hhdCB0byBvZmZlciwgdGhlIHJ1biwgaXRzIHByb2dyZXNzLCBpdHMgcmVzdWx0cy4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBjb21wdXRlID0gbmV3IEVtYmVkZGluZ0NvbXB1dGVDb29yZGluYXRvcigpO1xuXG4gIC8qKiAwLi4xIHdoaWxlIHJ1bm5pbmcsIG9yIG51bGwgYmVmb3JlIHRoZSBmaXJzdCByZXBvcnQuIERyaXZlcyB0aGUgcHJvZ3Jlc3MgYmFyLiAqL1xuICBnZXQgY29tcHV0ZUZyYWN0aW9uKCk6IG51bWJlciB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLmNvbXB1dGUuc3RhdGUuZnJhY3Rpb247XG4gIH1cbiAgLyoqIFdoaWNoIGJhY2tlbmQgdGhlIHdvcmtlciBnb3Qg4oCUICd3ZWJncHUnLCAnd2FzbScgb3IgJ2NwdScuIFNob3duIHdoaWxlIHJ1bm5pbmcuICovXG4gIGdldCBjb21wdXRlQmFja2VuZCgpOiBzdHJpbmcgfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5jb21wdXRlLnN0YXRlLmJhY2tlbmQ7XG4gIH1cbiAgZ2V0IGNvbXB1dGVNZXNzYWdlKCk6IHN0cmluZyB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLmNvbXB1dGUuc3RhdGUubWVzc2FnZTtcbiAgfVxuICBnZXQgY29tcHV0ZUVycm9yKCk6IHN0cmluZyB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLmNvbXB1dGUuc3RhdGUuZXJyb3I7XG4gIH1cbiAgLyoqIFdoZXJlIGEgY29tcHV0YXRpb24gc3RhbmRzLCBmb3IgdGhlIGVtYmVkZGluZyBjb250cm9scy4gKi9cbiAgcHJvdGVjdGVkIGdldCBjb21wdXRlU3RhdGUoKTogRW1iZWRkaW5nQ29tcHV0ZVN0YXRlIHtcbiAgICByZXR1cm4gdGhpcy5jb21wdXRlLnN0YXRlO1xuICB9XG4gIC8qKiBUcnVlIHdoZW4gYSB0LVNORSBpcyBtaXNzaW5nIGJ1dCB0aGUgZGF0YXNldCBpcyB0b28gYmlnIHRvIGVtYmVkIGhlcmUuICovXG4gIHByb3RlY3RlZCBnZXQgdHNuZVRvb0xhcmdlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmNvbXB1dGUudG9vTGFyZ2U7XG4gIH1cblxuICAvKiogVGhlIGtpbmRzIHRoZSBBQ1RJVkUgc3ViamVjdCBjYW4gYmUgZHJhd24gYXMuIEEgY2F0ZWdvcnkgY29kZSBpcyBhIGxhYmVsLFxuICAgKiAgbm90IGEgbWFnbml0dWRlLCBzbyBhIGhpc3RvZ3JhbSBvZiBpdCB3b3VsZCBiZSBtZWFuaW5nbGVzcyDigJQgd2hhdCBhXG4gICAqICBjYXRlZ29yaWNhbCBjb2x1bW4gaGFzIGlzIGEgZnJlcXVlbmN5IGRpc3RyaWJ1dGlvbi5cbiAgICpcbiAgICogIEJvdW5kIHRvIGEgYHAtc2VsZWN0QnV0dG9uYCwgc28gdGhlIFNBTUUgYXJyYXkgaXMgcmV0dXJuZWQgdW50aWwgd2hhdCBpdCBpcyBidWlsdFxuICAgKiAgZnJvbSBjaGFuZ2VzOiBhIGZyZXNoIGFycmF5IHBlciBjaGFuZ2UtZGV0ZWN0aW9uIHBhc3MgbWFrZXMgUHJpbWVORyByZS1yZW5kZXIgdGhlXG4gICAqICBidXR0b25zLCBhbmQgYSBidXR0b24gcmUtcmVuZGVyZWQgdW5kZXIgdGhlIHBvaW50ZXIgc3dhbGxvd3MgdGhlIGNsaWNrLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IGtpbmRPcHRpb25zKCk6IENoYXJ0S2luZE9wdGlvbltdIHtcbiAgICBjb25zdCBjYXRlZ29yaWNhbCA9IHRoaXMuZGF0YS5pc0NhdGVnb3JpY2FsO1xuICAgIGNvbnN0IGVtYmVkZGluZ3MgPSB0aGlzLmVtYmVkZGluZ3MubGVuZ3RoID4gMDtcbiAgICBjb25zdCBoaXQgPSB0aGlzLmtpbmRPcHRpb25zTWVtbztcbiAgICBpZiAoaGl0ICYmIGhpdC5jYXRlZ29yaWNhbCA9PT0gY2F0ZWdvcmljYWwgJiYgaGl0LmVtYmVkZGluZ3MgPT09IGVtYmVkZGluZ3MpIHtcbiAgICAgIHJldHVybiBoaXQub3B0aW9ucztcbiAgICB9XG4gICAgY29uc3Qgb3B0aW9ucyA9IGNoYXJ0S2luZE9wdGlvbnMoY2F0ZWdvcmljYWwsIGVtYmVkZGluZ3MpO1xuICAgIHRoaXMua2luZE9wdGlvbnNNZW1vID0geyBjYXRlZ29yaWNhbCwgZW1iZWRkaW5ncywgb3B0aW9ucyB9O1xuICAgIHJldHVybiBvcHRpb25zO1xuICB9XG4gIHByaXZhdGUga2luZE9wdGlvbnNNZW1vOiB7XG4gICAgY2F0ZWdvcmljYWw6IGJvb2xlYW47XG4gICAgZW1iZWRkaW5nczogYm9vbGVhbjtcbiAgICBvcHRpb25zOiBDaGFydEtpbmRPcHRpb25bXTtcbiAgfSB8IG51bGwgPSBudWxsO1xuXG4gIHByb3RlY3RlZCBjb250cm9sczogSVNwYXRpYWxDb250cm9scyB8IG51bGwgPSBudWxsO1xuICBwcm90ZWN0ZWQga2luZDogT21pY3NDaGFydEtpbmQgPSAnaGlzdG9ncmFtJztcbiAgLyoqIFRoZSBkYXRhIGNoYXJ0ZWQsIGFuZCBpdHMgbG9hZGluZyDigJQgc2VlIHtAbGluayBDaGFydERhdGFNb2RlbH0uICovXG4gIHByaXZhdGUgcmVhZG9ubHkgZGF0YSA9IG5ldyBDaGFydERhdGFNb2RlbCgpO1xuXG4gIC8qKiBFbWJlZGRpbmdzIHRoZSBkYXRhc2V0IG9mZmVycywgYW5kIHdoaWNoIG9mIHRoZW0gaXMgZHJhd24uICovXG4gIHByb3RlY3RlZCBnZXQgZW1iZWRkaW5ncygpOiBTcGF0aWFsRW1iZWRkaW5nTWV0YVtdIHtcbiAgICByZXR1cm4gdGhpcy5kYXRhLmVtYmVkZGluZ3M7XG4gIH1cbiAgcHJvdGVjdGVkIGdldCBlbWJlZGRpbmcoKTogU3BhdGlhbEVtYmVkZGluZ01ldGEgfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5kYXRhLmVtYmVkZGluZztcbiAgfVxuICAvKiogR2VuZXMgdGhlIGhlYXRtYXAncyByb3dzIGFyZS4gKi9cbiAgcHJvdGVjdGVkIGdldCBoZWF0bWFwR2VuZXMoKTogc3RyaW5nW10ge1xuICAgIHJldHVybiB0aGlzLmRhdGEuaGVhdG1hcEdlbmVzO1xuICB9XG4gIC8qKiBUaGUgaGVhdG1hcCBnZW5lIHBpY2tlcidzIG9wdGlvbnMuICovXG4gIHByb3RlY3RlZCBnZXQgZ2VuZU9wdGlvbnMoKTogeyBsYWJlbDogc3RyaW5nOyB2YWx1ZTogc3RyaW5nIH1bXSB7XG4gICAgcmV0dXJuIHRoaXMuZGF0YS5nZW5lT3B0aW9ucztcbiAgfVxuICAvKiogQ2F0ZWdvcmljYWwgY29sdW1uIHRoZSB2aW9saW4vYm94IHNwbGl0cyBieTsgbnVsbCA9IG9uZSB0cmFjZSBmb3IgYWxsLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IGdyb3VwQnkoKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMuZGF0YS5ncm91cEJ5O1xuICB9XG4gIHByb3RlY3RlZCBnZXQgZ3JvdXBPcHRpb25zKCk6IHsgbGFiZWw6IHN0cmluZzsgdmFsdWU6IHN0cmluZyB8IG51bGwgfVtdIHtcbiAgICByZXR1cm4gdGhpcy5kYXRhLmdyb3VwT3B0aW9ucztcbiAgfVxuICAvKiogU2V0IHdoZW4gdGhlIGFjdGl2ZSBjb2xvdXIgc291cmNlIGNhbm5vdCBiZSBjaGFydGVkLCBmb3IgYW4gaW5saW5lIGhpbnQuICovXG4gIHByb3RlY3RlZCBnZXQgbm90aWNlKCk6IHN0cmluZyB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLmRhdGEubm90aWNlO1xuICB9XG4gIHByb3RlY3RlZCBzZXQgbm90aWNlKHRleHQ6IHN0cmluZyB8IG51bGwpIHtcbiAgICB0aGlzLmRhdGEubm90aWNlID0gdGV4dDtcbiAgfVxuICAvKiogQSBsb2FkIGlzIGluIGZsaWdodC4gKi9cbiAgcHJvdGVjdGVkIGdldCBidXN5KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmRhdGEuYnVzeTtcbiAgfVxuICAvKiogWi1zY29yZSBlYWNoIGdlbmUgYWNyb3NzIHRoZSBncm91cHMuIE9uIGJ5IGRlZmF1bHQ6IHdpdGhvdXQgaXQgb25lIGxvdWRcbiAgICogIGdlbmUgc2F0dXJhdGVzIHRoZSBzY2FsZSBhbmQgdGhlIHJlc3Qgb2YgdGhlIHBhbmVsIHJlYWRzIGFzIGJsYW5rLiAqL1xuICBwcm90ZWN0ZWQgaGVhdG1hcFpTY29yZSA9IHRydWU7XG4gIC8qKiBQYXN0IHRoaXMgbWFueSBzZWxlY3RlZCBjZWxscyB0aGUgcGVyLWNlbGwgdmlldyBpcyBhIHRleHR1cmUsIG5vdCBhXG4gICAqICByZWFkYWJsZSBwYW5lbCwgc28gdGhlIGNvbHVtbnMgZ28gYmFjayB0byBiZWluZyBjbGFzc2VzLiAqL1xuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBIRUFUTUFQX0NFTExfQ09MVU1OUyA9IDIwMDtcbiAgLyoqXG4gICAqIENvbHVtbiBjYXAgZm9yIHRoZSBncm91cGVkIGhlYXRtYXAuXG4gICAqXG4gICAqIEFueSBjYXRlZ29yaWNhbCBjb2x1bW4gY2FuIGJlIHRoZSB4IGF4aXMsIGFuZCB0aGV5IGFyZSBub3QgdGhlIHNhbWUgc2l6ZTpcbiAgICogYG5ldXJvdHJhbnNtaXR0ZXJgIGhhcyA5IGNhdGVnb3JpZXMsIGBjbGFzc2AgMzQsIGBzdWJjbGFzc2AgMzM4LiBBdCAzMzggdGhlXG4gICAqIHBhbmVsIGlzIGEgdGV4dHVyZSDigJQgY29sdW1ucyBhIGNvdXBsZSBvZiBwaXhlbHMgd2lkZSwgbm8gcmVhZGFibGUgbGFiZWwg4oCUXG4gICAqIHNvIHBhc3QgdGhpcyBtYW55IHRoZSBzdHJvbmdlc3QgY29sdW1ucyBhcmUga2VwdCBhbmQgdGhlIG5vdGUgc2F5cyBob3cgbWFueVxuICAgKiB3ZXJlIGRyb3BwZWQuIFNlZSBgbWF4Q29sc2AgaW4gYHNwYXRpYWwtaGVhdG1hcC50c2AuXG4gICAqL1xuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBIRUFUTUFQX01BWF9DT0xVTU5TID0gNDA7XG4gIC8qKiBDb2x1bW5zIHRoZSBjYXAgZHJvcHBlZCBmcm9tIHRoZSBsYXN0IHJlbmRlciwgZm9yIHRoZSBub3RlLiAqL1xuICBwcml2YXRlIGhlYXRtYXBIaWRkZW4gPSAwO1xuXG4gIC8qKiBXaGF0IHRoZSBtYXAgaXMgY29sb3VyZWQgYnkg4oCUIHRoZSBjaGFydCdzIHN1YmplY3QuICovXG4gIGNvbG9yQnk6IFNwYXRpYWxDb2xvckJ5IHwgbnVsbCA9IG51bGw7XG4gIHByb3RlY3RlZCBzZWxlY3Rpb25Db3VudCA9IDA7XG5cbiAgcHJpdmF0ZSB2aWV3OiBTcGF0aWFsVmlld1N0YXRlID0geyAuLi5ERUZBVUxUX1NQQVRJQUxfVklFVyB9O1xuICBwcml2YXRlIHNlbGVjdGlvbjogU3BhdGlhbFNlbGVjdGlvbk1hc2sgPSBlbXB0eVNlbGVjdGlvbigpO1xuICAvKiogV2hldGhlciB0aGUgZmlyc3QgdmlldyBlbWlzc2lvbiBoYXMgYmVlbiBoYW5kbGVkLiAqL1xuICBwcml2YXRlIHByaW1lZCA9IGZhbHNlO1xuICBwcml2YXRlIHJlYWRvbmx5IGRlc3Ryb3lSZWYgPSBpbmplY3QoRGVzdHJveVJlZik7XG4gIHByaXZhdGUgcmVhZG9ubHkgY2RyID0gaW5qZWN0KENoYW5nZURldGVjdG9yUmVmKTtcbiAgcHJpdmF0ZSByZWFkb25seSB6b25lID0gaW5qZWN0KE5nWm9uZSk7XG5cbiAgLyoqIFJlLXJlbmRlciB0aGlzIE9uUHVzaCB2aWV3LCByZS1lbnRlcmluZyB0aGUgem9uZSB3aGVuIHRoZSBjaGFuZ2UgY2FtZSBmcm9tIG91dHNpZGUgaXQuICovXG4gIHByaXZhdGUgY2hhbmdlZCgpOiB2b2lkIHtcbiAgICBtYXJrRm9yQ2hlY2tJblpvbmUodGhpcy5jZHIsIHRoaXMuem9uZSk7XG4gIH1cblxuICBjb25zdHJ1Y3RvcihASW5qZWN0KFZJU1VBTElaRVIpIHByaXZhdGUgcmVhZG9ubHkgdml6OiBJVmlzdWFsaXplcikge31cblxuICBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICAvLyBCZWZvcmUgdGhlIHBvcnQgY2hlY2s6IHRoZSBoZWxwIGljb24gaXMgaW4gdGhlIHRlbXBsYXRlIGVpdGhlciB3YXksIGFuZCBhIHBhbmVsXG4gICAgLy8gd2l0aCBubyBkYXRhIHNvdXJjZSBpcyBleGFjdGx5IHdoZXJlIHNvbWVvbmUgcmVhZHMgaXQuXG4gICAgZW5zdXJlSGVscFRpcFN0eWxlKCk7XG4gICAgdGhpcy5jb250cm9scyA9IHRoaXMudml6LmdldFNwYXRpYWxDb250cm9scz8uKCkgPz8gbnVsbDtcbiAgICBpZiAoIXRoaXMuY29udHJvbHMpIHJldHVybjtcblxuICAgIC8vIFRoZSBydW4ncyBwcm9ncmVzcywgYmFja2VuZCBhbmQgZXJyb3JzLCBmb3IgdGhlIGVtYmVkZGluZyBjb250cm9scy5cbiAgICB0aGlzLmNvbXB1dGUuc3RhdGUkLnBpcGUodGFrZVVudGlsRGVzdHJveWVkKHRoaXMuZGVzdHJveVJlZikpLnN1YnNjcmliZSgoKSA9PiB0aGlzLmNoYW5nZWQoKSk7XG5cbiAgICBjb21iaW5lTGF0ZXN0KFt0aGlzLmNvbnRyb2xzLmdldFZpZXdTdGF0ZSQoKSwgdGhpcy5jb250cm9scy5nZXRTZWxlY3Rpb24kKCldKVxuICAgICAgLnBpcGUodGFrZVVudGlsRGVzdHJveWVkKHRoaXMuZGVzdHJveVJlZikpXG4gICAgICAuc3Vic2NyaWJlKChbdmlldywgc2VsZWN0aW9uXSkgPT4ge1xuICAgICAgICBjb25zdCBzb3VyY2VDaGFuZ2VkID1cbiAgICAgICAgICB2aWV3LmNvbG9yQnk/LmtpbmQgIT09IHRoaXMudmlldy5jb2xvckJ5Py5raW5kIHx8IHZpZXcuY29sb3JCeT8ubmFtZSAhPT0gdGhpcy52aWV3LmNvbG9yQnk/Lm5hbWU7XG4gICAgICAgIHRoaXMudmlldyA9IHZpZXc7XG4gICAgICAgIHRoaXMuY29sb3JCeSA9IHZpZXcuY29sb3JCeTtcbiAgICAgICAgdGhpcy5zZWxlY3Rpb24gPSBzZWxlY3Rpb247XG4gICAgICAgIHRoaXMuc2VsZWN0aW9uQ291bnQgPSBzZWxlY3Rpb24uY291bnQ7XG4gICAgICAgIC8vIEEgc2VsZWN0aW9uIG9yIGxvZyBjaGFuZ2Ugb25seSBuZWVkcyBhIHJlLXJlbmRlcjsgYSBuZXcgY29sb3VyIHNvdXJjZVxuICAgICAgICAvLyBuZWVkcyBpdHMgdmVjdG9yIGZldGNoZWQgZmlyc3QuIFRoZSBGSVJTVCBlbWlzc2lvbiBhbHdheXMgcmVsb2FkczpcbiAgICAgICAgLy8gb3RoZXJ3aXNlIGBudWxsIC0+IG51bGxgIHJlYWRzIGFzIFwidW5jaGFuZ2VkXCIgYW5kIHRoZSBjb21wb25lbnQgc2l0c1xuICAgICAgICAvLyB3aXRoIG5vIGRhdGEgYW5kIG5vIGV4cGxhbmF0aW9uIHVudGlsIHNvbWV0aGluZyBlbHNlIG1vdmVzLlxuICAgICAgICBpZiAoIXRoaXMucHJpbWVkIHx8IHNvdXJjZUNoYW5nZWQpIHtcbiAgICAgICAgICB0aGlzLnByaW1lZCA9IHRydWU7XG4gICAgICAgICAgdm9pZCB0aGlzLnJlbG9hZCgpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIEEgc2VsZWN0aW9uIG9yIGxvZy1zY2FsZSBjaGFuZ2UgbmVlZHMgb25seSBhIHJlZHJhdyBvZiB0aGUgc2FtZSB2ZWN0b3IuXG4gICAgICAgICAgdm9pZCB0aGlzLnJlbmRlcigpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuY2hhbmdlZCgpO1xuICAgICAgfSk7XG5cbiAgICB0aGlzLmNvbnRyb2xzXG4gICAgICAuZ2V0RGF0YXNldCQoKVxuICAgICAgLnBpcGUodGFrZVVudGlsRGVzdHJveWVkKHRoaXMuZGVzdHJveVJlZikpXG4gICAgICAuc3Vic2NyaWJlKChkYXRhc2V0KSA9PiB7XG4gICAgICAgIC8vIE9mZmVyIHRvIGNvbXB1dGUgYSB0LVNORSBvbmx5IHdoZXJlIHRoZSBkYXRhc2V0IGhhcyBQQ0EgdG8gZW1iZWQgYW5kIG5vIHQtU05FIG9mXG4gICAgICAgIC8vIGl0cyBvd247IGFueXRoaW5nIGNvbXB1dGVkIG9yIGNvbXB1dGluZyBmb3IgdGhlIHByZXZpb3VzIGRhdGFzZXQgaXMgZHJvcHBlZC5cbiAgICAgICAgY29uc3QgZW1iZWRkaW5ncyA9IHRoaXMuY29tcHV0ZS5zZXREYXRhc2V0KGRhdGFzZXQ/LmVtYmVkZGluZ3MgPz8gW10sIGRhdGFzZXQ/Lm9ic2VydmF0aW9ucy5jb3VudCA/PyAwKTtcbiAgICAgICAgY29uc3Qgc3dpdGNoZWQgPSB0aGlzLmRhdGEub25EYXRhc2V0Q2hhbmdlZChcbiAgICAgICAgICBkYXRhc2V0LFxuICAgICAgICAgIHRoaXMuY29udHJvbHM/LmNhdGVnb3JpY2FsQ29sdW1ucygpID8/IFtdLFxuICAgICAgICAgIGVtYmVkZGluZ3MsXG4gICAgICAgICk7XG4gICAgICAgIC8vIE5vdGhpbmcgdG8gZHJhdyBmb3IgdGhlIGtpbmQgdGhhdCB3YXMgc2VsZWN0ZWQ7IGZhbGwgYmFjayByYXRoZXIgdGhhbiBzaXQgYmxhbmsuXG4gICAgICAgIGlmICh0aGlzLmtpbmQgPT09ICdlbWJlZGRpbmcnICYmIGVtYmVkZGluZ3MubGVuZ3RoID09PSAwKSB0aGlzLmtpbmQgPSAnaGlzdG9ncmFtJztcbiAgICAgICAgLy8gRXZlcnl0aGluZyBsb2FkZWQgd2FzIHRoZSBwcmV2aW91cyBkYXRhc2V0J3M6IGZldGNoIGFmcmVzaCwgZXZlbiB3aGVuIHRoZSB2aWV3XG4gICAgICAgIC8vIGRvZXMgbm90IHJlLWVtaXQgKGEgY29sb3VyIHNvdXJjZSBvZiB0aGUgc2FtZSBuYW1lKS5cbiAgICAgICAgaWYgKHN3aXRjaGVkKSB7XG4gICAgICAgICAgaWYgKHRoaXMuZ3JvdXBCeSkgdm9pZCB0aGlzLm9uR3JvdXBCeSh0aGlzLmdyb3VwQnkpO1xuICAgICAgICAgIHZvaWQgdGhpcy5yZWxvYWQoKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmNoYW5nZWQoKTtcbiAgICAgIH0pO1xuICB9XG5cbiAgLyoqIExhdGVzdCB3aW5zIGFtb25nIGhlYXRtYXAgZHJhd3MsIHdob3NlIG1hdHJpeCBtYXkgYmUgY29tcHV0ZWQgaW4gYSB3b3JrZXIuICovXG4gIHByaXZhdGUgcmVhZG9ubHkgaGVhdG1hcFJlbmRlciA9IG5ldyBTdXBlcnNlZGUoKTtcblxuICAvKiogUGxvdGx5IEkvTyBmb3IgYm90aCB0aGUgaW5saW5lIGFuZCB0aGUgZGV0YWNoZWQgZGl2LiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IHBsb3QgPSBuZXcgUGxvdGx5Q2hhcnRIb3N0KCk7XG5cbiAgLyoqXG4gICAqIFJlLWZpdCB0aGUgcGxvdCB0byB0aGUgcGFuZWwncyBjdXJyZW50IHdpZHRoLlxuICAgKlxuICAgKiBDYWxsZWQgYnkgdGhlIGhvc3Qgd2hlbiB0aGUgZGlhbG9nIGZpbmlzaGVzIHJlc2l6aW5nLiBQbG90bHkgZG9lcyBub3Qgbm90aWNlXG4gICAqIGEgY29udGFpbmVyIHJlc2l6ZSBvbiBpdHMgb3duIOKAlCBpdHMgYHJlc3BvbnNpdmVgIG9wdGlvbiBsaXN0ZW5zIGZvciBXSU5ET1dcbiAgICogcmVzaXplcyBvbmx5LCBzbyBkcmFnZ2luZyBhIGRpYWxvZyBlZGdlIHJlYWNoZXMgaXQgdGhyb3VnaCBub3RoaW5nIGF0IGFsbC5cbiAgICovXG4gIHJlc2l6ZSgpOiB2b2lkIHtcbiAgICB0aGlzLnBsb3QucmVmaXQodGhpcy5jaGFydERpdik7XG4gIH1cblxuICAvKiogU3RvcHMgdGhlIHQtU05FIHJ1biBhbmQgaGVhdG1hcCB3b3JrZXIgYW5kIGZyZWVzIGJvdGggcGxvdHM7IHRoZSBzdHJlYW1zIGVuZFxuICAgKiAgdGhyb3VnaCBgdGFrZVVudGlsRGVzdHJveWVkYC4gKi9cbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgLy8gQSB3b3JrZXIgb3V0bGl2ZXMgdGhlIGNvbXBvbmVudCB0aGF0IHN0YXJ0ZWQgaXQ6IGNsb3NpbmcgdGhlIHBhbmVsIG1pZC1ydW4gd291bGRcbiAgICAvLyBvdGhlcndpc2UgbGVhdmUgYSB0LVNORSBzYXR1cmF0aW5nIGEgR1BVIGZvciBtaW51dGVzIHdpdGggbm90aGluZyBsZWZ0IHRvIHJlY2VpdmVcbiAgICAvLyB0aGUgYW5zd2VyLlxuICAgIHRoaXMuY29tcHV0ZS5hYmFuZG9uKCk7XG4gICAgdGhpcy5oZWF0bWFwUmVuZGVyLmNhbmNlbCgpO1xuICAgIC8vIEJPVEggZGl2czogYSBjaGFydCBsZWZ0IGRldGFjaGVkIGF0IHRlYXJkb3duIGhvbGRzIGl0cyBXZWJHTCBjb250ZXh0IGluIHRoZVxuICAgIC8vIHdpbmRvdydzIGRpdiwgd2hpY2ggdGhlIGlubGluZSBpZCB3b3VsZCBuZXZlciByZWFjaC5cbiAgICBmb3IgKGNvbnN0IGRpdiBvZiBbdGhpcy5jaGFydERpdiwgdGhpcy5kZXRhY2hlZERpdl0pIHRoaXMucGxvdC5wdXJnZShkaXYpO1xuICB9XG5cbiAgbmdBZnRlclZpZXdJbml0KCk6IHZvaWQge1xuICAgIC8vIFRoZSBkaXYgZXhpc3RzIG9ubHkgbm93LCBzbyB0aGlzIGlzIHRoZSBlYXJsaWVzdCBhIGZpcnN0IGRyYXcgY2FuIGxhbmQuXG4gICAgdm9pZCB0aGlzLnJlbG9hZCgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIG9uS2luZChraW5kOiBPbWljc0NoYXJ0S2luZCk6IHZvaWQge1xuICAgIHRoaXMua2luZCA9IGtpbmQ7XG4gICAgaWYgKGtpbmQgPT09ICdoZWF0bWFwJykge1xuICAgICAgLy8gU2VlZCB3aXRoIHRoZSBnZW5lIGFscmVhZHkgb24gc2NyZWVuLCBzbyB0aGUgY2hhcnQgc2F5cyBzb21ldGhpbmcgdGhlXG4gICAgICAvLyBtb21lbnQgaXQgb3BlbnMgcmF0aGVyIHRoYW4gc2hvd2luZyBhbiBlbXB0eSBncmlkIGFuZCBhIHByb21wdC5cbiAgICAgIGlmICh0aGlzLmhlYXRtYXBHZW5lcy5sZW5ndGggPT09IDAgJiYgdGhpcy5jb2xvckJ5Py5raW5kID09PSAnZmVhdHVyZScpIHtcbiAgICAgICAgdGhpcy5kYXRhLnNldEhlYXRtYXBHZW5lcyhbdGhpcy5jb2xvckJ5Lm5hbWVdKTtcbiAgICAgIH1cbiAgICAgIHZvaWQgKGFzeW5jICgpID0+IHtcbiAgICAgICAgYXdhaXQgdGhpcy5lbnN1cmVIZWF0bWFwR3JvdXBpbmcoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5sb2FkSGVhdG1hcEdlbmVzKCk7XG4gICAgICAgIHZvaWQgdGhpcy5yZW5kZXIoKTtcbiAgICAgIH0pKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHZvaWQgdGhpcy5yZW5kZXIoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgZ3JvdXBpbmcgdGhlIGhlYXRtYXAncyBjb2x1bW5zIGNvbWUgZnJvbSwgZGVmYXVsdGluZyByYXRoZXIgdGhhblxuICAgKiBkZW1hbmRpbmcgb25lOiBhIGhlYXRtYXAgd2l0aCBubyBjb2x1bW5zIGlzIG5vdCBhIGNoYXJ0LCBhbmQgXCJObyBncm91cGluZ1wiXG4gICAqIGlzIGEgc2Vuc2libGUgYW5zd2VyIGZvciBhIHZpb2xpbiBidXQgbm90IGZvciB0aGlzLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBlbnN1cmVIZWF0bWFwR3JvdXBpbmcoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMuZGF0YS5oYXNHcm91cGluZyB8fCAhdGhpcy5jb250cm9scykgcmV0dXJuO1xuICAgIGNvbnN0IGZpcnN0ID0gdGhpcy5ncm91cE9wdGlvbnMuZmluZCgobykgPT4gby52YWx1ZSk/LnZhbHVlO1xuICAgIGlmICghZmlyc3QpIHJldHVybjtcbiAgICBhd2FpdCB0aGlzLm9uR3JvdXBCeShmaXJzdCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgb25IZWF0bWFwWlNjb3JlKG9uOiBib29sZWFuKTogdm9pZCB7XG4gICAgdGhpcy5oZWF0bWFwWlNjb3JlID0gb247XG4gICAgdm9pZCB0aGlzLnJlbmRlcigpO1xuICB9XG5cbiAgLyoqIFdoYXQgdGhlIGhlYXRtYXAgaXMgY3VycmVudGx5IHNob3dpbmcsIHNhaWQgcGxhaW5seS4gKi9cbiAgcHJvdGVjdGVkIGdldCBoZWF0bWFwTm90ZSgpOiBzdHJpbmcge1xuICAgIHJldHVybiBoZWF0bWFwTm90ZSh7XG4gICAgICBnZW5lQ291bnQ6IHRoaXMuaGVhdG1hcEdlbmVzLmxlbmd0aCxcbiAgICAgIHNlbGVjdGlvbkNvdW50OiB0aGlzLnNlbGVjdGlvbkNvdW50LFxuICAgICAgelNjb3JlOiB0aGlzLmhlYXRtYXBaU2NvcmUsXG4gICAgICBoaWRkZW46IHRoaXMuaGVhdG1hcEhpZGRlbixcbiAgICAgIGdyb3VwQnk6IHRoaXMuZ3JvdXBCeSxcbiAgICAgIG1heENvbHVtbnM6IFNwYXRpYWxDaGFydHNDb21wb25lbnQuSEVBVE1BUF9NQVhfQ09MVU1OUyxcbiAgICAgIGNlbGxDb2x1bW5zOiBTcGF0aWFsQ2hhcnRzQ29tcG9uZW50LkhFQVRNQVBfQ0VMTF9DT0xVTU5TLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqIEdlbmUgcm93cyBmb3IgdGhlIGhlYXRtYXAuICovXG4gIHByb3RlY3RlZCBhc3luYyBvbkhlYXRtYXBHZW5lcyhuYW1lczogc3RyaW5nW10pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmRhdGEuc2V0SGVhdG1hcEdlbmVzKG5hbWVzKTtcbiAgICBhd2FpdCB0aGlzLmxvYWRIZWF0bWFwR2VuZXMoKTtcbiAgICB2b2lkIHRoaXMucmVuZGVyKCk7XG4gIH1cblxuICAvKiogRmV0Y2ggYW55IGdlbmUgdGhlIGhlYXRtYXAgbmVlZHMgYW5kIGRvZXMgbm90IGFscmVhZHkgaG9sZC4gKi9cbiAgcHJpdmF0ZSBsb2FkSGVhdG1hcEdlbmVzKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmRhdGEubG9hZEhlYXRtYXBHZW5lcyh0aGlzLmNvbnRyb2xzKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhc3luYyBvbkdyb3VwQnkobmFtZTogc3RyaW5nIHwgbnVsbCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGxvYWRpbmcgPSB0aGlzLmRhdGEubG9hZEdyb3VwaW5nKHRoaXMuY29udHJvbHMsIG5hbWUpO1xuICAgIHRoaXMuY2hhbmdlZCgpOyAvLyB0aGUgYnVzeSBzcGlubmVyXG4gICAgaWYgKGF3YWl0IGxvYWRpbmcpIHZvaWQgdGhpcy5yZW5kZXIoKTtcbiAgICBlbHNlIHRoaXMuY2hhbmdlZCgpO1xuICB9XG5cbiAgLyoqIFRydWUgd2hlbiB0aGUgY3VycmVudCBraW5kIHdvdWxkIHJlYWQgYmV0dGVyIHdpdGggYSBncm91cGluZyBjaG9zZW4uICovXG4gIHByb3RlY3RlZCBnZXQgc3VnZ2VzdHNHcm91cGluZygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gYmVuZWZpdHNGcm9tR3JvdXBpbmcodGhpcy5raW5kKSAmJiAhdGhpcy5ncm91cEJ5ICYmIHRoaXMuZ3JvdXBPcHRpb25zLmxlbmd0aCA+IDE7XG4gIH1cblxuICBwcm90ZWN0ZWQgZ2V0IHN1YmplY3QoKTogc3RyaW5nIHtcbiAgICBpZiAoIXRoaXMuY29sb3JCeSkgcmV0dXJuICcnO1xuICAgIHJldHVybiB0aGlzLmNvbG9yQnkua2luZCA9PT0gJ2ZlYXR1cmUnID8gYGdlbmUgJHt0aGlzLmNvbG9yQnkubmFtZX1gIDogdGhpcy5jb2xvckJ5Lm5hbWU7XG4gIH1cblxuICAvLyDilIDilIAgaW50ZXJuYWxzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBGZXRjaCB0aGUgYWN0aXZlIGNvbG91ciBzb3VyY2UncyB2ZWN0b3IsIHRoZW4gZHJhdy4gKi9cbiAgcHJpdmF0ZSBhc3luYyByZWxvYWQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgbG9hZGluZyA9IHRoaXMuZGF0YS5sb2FkVmFsdWVzKHRoaXMuY29udHJvbHMsIHRoaXMudmlldy5jb2xvckJ5KTtcbiAgICB0aGlzLmNoYW5nZWQoKTsgLy8gdGhlIGJ1c3kgc3Bpbm5lclxuICAgIGNvbnN0IGxvYWRlZCA9IGF3YWl0IGxvYWRpbmc7XG4gICAgaWYgKGxvYWRlZCA9PT0gJ3N1cGVyc2VkZWQnKSByZXR1cm47XG4gICAgLy8gT25seSBvZmYgdGhlIGRpc3RyaWJ1dGlvbiB0YWJzLiBUaGUgaGVhdG1hcCBkb2VzIG5vdCBjaGFydCB0aGUgY29sb3VyIHNvdXJjZSwgYW5kIHRoZVxuICAgIC8vIGVtYmVkZGluZyBpcyBjb2xvdXJlZCBCWSBpdCDigJQgaXRzIG93biBjYXB0aW9uIGFza3MgZm9yIGV4YWN0bHkgdGhpcy5cbiAgICBpZiAobG9hZGVkID09PSAnY2F0ZWdvcmljYWwnICYmIHRoaXMua2luZCAhPT0gJ2VtYmVkZGluZycgJiYgdGhpcy5raW5kICE9PSAnaGVhdG1hcCcpIHRoaXMua2luZCA9ICdjb3VudHMnO1xuICAgIGlmIChsb2FkZWQgPT09ICdjb250aW51b3VzJyAmJiB0aGlzLmtpbmQgPT09ICdjb3VudHMnKSB0aGlzLmtpbmQgPSAnaGlzdG9ncmFtJztcbiAgICB2b2lkIHRoaXMucmVuZGVyKCk7XG4gIH1cblxuICAvKipcbiAgICogVGhlIGVtYmVkZGluZyBzY2F0dGVyIOKAlCBhIFVNQVAgb3ZlciB0aGUgc2FtZSBvYnNlcnZhdGlvbnMuXG4gICAqXG4gICAqIEFuc3dlcnMgd2hhdCB0aGUgbWFwIGNhbm5vdDogdGhlIG1hcCBzYXlzIFdIRVJFIGEgcG9wdWxhdGlvbiBzaXRzLCB0aGlzIHNheXMgd2hpY2hcbiAgICogcG9wdWxhdGlvbnMgdGhlcmUgYXJlIGFuZCBob3cgY2xvc2UgdGhleSBhcmUgaW4gZXhwcmVzc2lvbi5cbiAgICpcbiAgICogTGlua2VkIHRvIHRoZSBtYXAgd2l0aG91dCBhc2tpbmcgZm9yIGFueXRoaW5nOiBpdCByZXVzZXMgdGhlIGxvYWRlZCBjYXRlZ29yaWVzLCB0aGUgc2FtZVxuICAgKiBsb2FkZWQgY29sb3VyaW5nIHRoZSBjb3VudHMgY2hhcnQgZHJhd3MgZnJvbSwgc28gdGhlIHR3byBjYW5ub3QgZGlzYWdyZWUgYWJvdXQgd2hhdCBhXG4gICAqIGNvbG91ciBtZWFucyBhbmQgbm8gc2Vjb25kIGZldGNoIGhhcHBlbnMuIEEgc2VsZWN0aW9uIGRpbXMgdGhlIHJlc3QgcmF0aGVyIHRoYW5cbiAgICogZHJvcHBpbmcgaXQg4oCUIHRoZSBlbWJlZGRpbmcncyBzaGFwZSBpcyB0aGUgY29udGV4dCB0aGF0IG1ha2VzIGEgc2VsZWN0aW9uIGxlZ2libGUuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHJlbmRlckVtYmVkZGluZygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBjb250cm9scyA9IHRoaXMuY29udHJvbHM7XG4gICAgY29uc3QgbWV0YSA9IHRoaXMuZW1iZWRkaW5nO1xuICAgIGlmICghY29udHJvbHMgfHwgIW1ldGEpIHtcbiAgICAgIHRoaXMucHVyZ2VQbG90KCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICghY29udHJvbHMuZ2V0RW1iZWRkaW5nKSB7XG4gICAgICB0aGlzLm5vdGljZSA9ICdUaGlzIGRhdGEgc291cmNlIGRvZXMgbm90IHNlcnZlIGVtYmVkZGluZ3MuJztcbiAgICAgIHRoaXMucHVyZ2VQbG90KCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gQ29tcHV0ZWQgaGVyZSwgbm90IHNlcnZlZDogYSBsb2NhbCByZXN1bHQgaXMgdGhlIHNhbWUgc2hhcGUgYXMgYSBmZXRjaGVkIG9uZSwgc29cbiAgICAvLyBldmVyeXRoaW5nIGRvd25zdHJlYW0g4oCUIGNvbG91cmluZywgbGFzc28gc2VsZWN0aW9uLCB0aGUgY2FtZXJhIOKAlCBpcyB1bmNoYW5nZWQuXG4gICAgY29uc3QgbG9jYWwgPSB0aGlzLmNvbXB1dGUucmVzdWx0KG1ldGEubmFtZSk7XG4gICAgaWYgKGxvY2FsKSB7XG4gICAgICB0aGlzLmRhdGEuc3VwZXJzZWRlRW1iZWRkaW5nTG9hZCgpO1xuICAgICAgdGhpcy5kYXRhLmVtYmVkZGluZ0Nvb3JkcyA9IGxvY2FsO1xuICAgIH0gZWxzZSBpZiAodGhpcy5pc0NvbXB1dGFibGUpIHtcbiAgICAgIC8vIE9mZmVyZWQgYnV0IG5vdCB5ZXQgY29tcHV0ZWQuIFRoZSBwYW5lbCBzaG93cyB0aGUgQ29tcHV0ZSBidXR0b247IGRyYXdpbmcgbm90aGluZ1xuICAgICAgLy8gaXMgcmlnaHQsIGFuZCBhbiBlcnJvciB3b3VsZCBiZSB3cm9uZyDigJQgdGhlcmUgaXMgbm8gZmFpbHVyZSBoZXJlLlxuICAgICAgdGhpcy5kYXRhLnN1cGVyc2VkZUVtYmVkZGluZ0xvYWQoKTtcbiAgICAgIHRoaXMucHVyZ2VQbG90KCk7XG4gICAgICB0aGlzLm5vdGljZSA9IG51bGw7XG4gICAgICByZXR1cm47XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGxvYWRlZCA9IGF3YWl0IHRoaXMuZGF0YS5sb2FkRW1iZWRkaW5nKChuYW1lKSA9PiBjb250cm9scy5nZXRFbWJlZGRpbmchKG5hbWUpLCBtZXRhKTtcbiAgICAgIGlmIChsb2FkZWQgPT09ICdzdXBlcnNlZGVkJykgcmV0dXJuO1xuICAgICAgaWYgKGxvYWRlZCA9PT0gJ2ZhaWxlZCcpIHtcbiAgICAgICAgdGhpcy5wdXJnZVBsb3QoKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH1cbiAgICBjb25zdCB7IGVtYmVkZGluZ0Nvb3JkczogY29vcmRzLCBjYXRlZ29yaWNhbCB9ID0gdGhpcy5kYXRhLnNuYXBzaG90KCk7XG4gICAgaWYgKCFjb29yZHMpIHJldHVybjtcblxuICAgIGNvbnN0IHRhcmdldCA9IHRoaXMucGxvdFRhcmdldDtcbiAgICBpZiAoIWRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRhcmdldCkpIHJldHVybjsgLy8gdGhlIHdpbmRvdyBpcyBjbG9zZWQsIG9yIG5vdCByZW5kZXJlZCB5ZXRcbiAgICBjb25zdCB2aWV3ID0gdGhpcy5wbG90LmxpdmVWaWV3KHRhcmdldCk7XG5cbiAgICB0aGlzLm5vdGljZSA9IG51bGw7XG4gICAgY29uc3QgaW5wdXQgPSB7XG4gICAgICB4OiBjb29yZHMueCxcbiAgICAgIHk6IGNvb3Jkcy55LFxuICAgICAgLy8gVGhlIHRoaXJkIGRpbWVuc2lvbiB3aGVuIHRoZSBlbWJlZGRpbmcgaGFzIG9uZSDigJQgd2hhdCBzd2l0Y2hlcyB0aGUgcGxvdCB0byBhXG4gICAgICAvLyByb3RhdGFibGUgM0Qgc2NhdHRlci4gRGVjb2RlZCBvbmx5IGZvciBhIDMtZGltIGVtYmVkZGluZywgc28gaXRzIHByZXNlbmNlIGhlcmVcbiAgICAgIC8vIGlzIHRoZSBzYW1lIHF1ZXN0aW9uIGFzIGBtZXRhLmRpbXMgPT09IDNgLlxuICAgICAgLi4uKGNvb3Jkcy56ID8geyB6OiBjb29yZHMueiB9IDoge30pLFxuICAgICAgbGFiZWw6IG1ldGEubGFiZWwgPz8gbWV0YS5uYW1lLFxuICAgICAgZGVyaXZlZDogbWV0YS5kZXJpdmVkLFxuICAgICAgLy8gUHJlc2VudCBmb3IgYSBQQ0EsIGFic2VudCBmb3IgYSBVTUFQIOKAlCBzZWUgYHZhcmlhbmNlUmF0aW9gIG9uIHRoZSBtZXRhLlxuICAgICAgLi4uKG1ldGEudmFyaWFuY2VSYXRpbyA/IHsgdmFyaWFuY2VSYXRpbzogbWV0YS52YXJpYW5jZVJhdGlvIH0gOiB7fSksXG4gICAgICAuLi4oY2F0ZWdvcmljYWxcbiAgICAgICAgPyB7IGNhdGVnb3JpZXM6IHsgY29kZXM6IGNhdGVnb3JpY2FsLmNvZGVzLCBuYW1lczogY2F0ZWdvcmljYWwuY2F0ZWdvcmllcywgY29sb3JzOiBjYXRlZ29yaWNhbC5jb2xvcnMgfSB9XG4gICAgICAgIDoge30pLFxuICAgICAgLi4uKHRoaXMuc2VsZWN0aW9uLmNvdW50ID4gMCA/IHsgc2VsZWN0aW9uOiB0aGlzLnNlbGVjdGlvbi5tYXNrIH0gOiB7fSksXG4gICAgICAvLyBXaGF0ZXZlciB2aWV3IHRoZSB1c2VyIGhhcyBzZXQgdXAsIHJlYWQgZnJvbSB0aGUgTElWRSBwbG90IOKAlCB3aGljaCBpcyB3aGVyZVxuICAgICAgLy8gcm90YXRpbmcgYW5kIHpvb21pbmcgbGVhdmUgaXQuXG4gICAgICAuLi4odmlldyA/IHsgdmlldyB9IDoge30pLFxuICAgIH07XG4gICAgYXdhaXQgdGhpcy5wbG90LmRyYXcodGFyZ2V0LCBidWlsZEVtYmVkZGluZ1RyYWNlcyhpbnB1dCksIGVtYmVkZGluZ0xheW91dChpbnB1dCksIEVNQkVERElOR19DT05GSUcpO1xuICAgIC8vIERyYXcgcm91bmQgYSBjbHVzdGVyIGhlcmUgYW5kIHRob3NlIGNlbGxzIGxpZ2h0IHVwIG9uIHRoZSB0aXNzdWU6IGV2ZXJ5IHZpZXcgcmVhZHNcbiAgICAvLyB0aGUgc2FtZSBtYXNrLlxuICAgIHRoaXMucGxvdC5iaW5kU2VsZWN0aW9uKHRhcmdldCwge1xuICAgICAgc2VsZWN0ZWQ6IChpbmRpY2VzKSA9PiB0aGlzLmNvbnRyb2xzPy5zZWxlY3RJbmRpY2VzKGluZGljZXMpLFxuICAgICAgZGVzZWxlY3RlZDogKCkgPT4gdGhpcy5jb250cm9scz8uY2xlYXJTZWxlY3Rpb24oKSxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHRoZSBwbG90IG9uIHNjcmVlbiBmaXhlZCBpdHMgb3duIGhlaWdodC5cbiAgICpcbiAgICogVGhlIGNvdW50cyBhbmQgaGVhdG1hcCBsYXlvdXRzIHNpemUgdGhlbXNlbHZlcyB0byB0aGVpciByb3cgY291bnQsIHNvIGluIHRoZSBkZXRhY2hlZFxuICAgKiB3aW5kb3cgdGhleSBjYW4gYmUgVEFMTEVSIHRoYW4gdGhlIHdpbmRvdyDigJQgd2hpY2ggaGFzIHRvIHNjcm9sbCByYXRoZXIgdGhhbiBjbGlwLiBBblxuICAgKiBlbWJlZGRpbmcgZml4ZXMgbm8gaGVpZ2h0IGFuZCBmaWxscyB0aGUgd2luZG93IGluc3RlYWQuIFRoZSB0ZW1wbGF0ZSBuZWVkcyB0byBrbm93XG4gICAqIHdoaWNoLCBiZWNhdXNlIHRoZSB0d28gd2FudCBvcHBvc2l0ZSBgZmxleGAgYW5kIGBvdmVyZmxvd2AuXG4gICAqL1xuICBwcm90ZWN0ZWQgZ2V0IGhhc0ZpeGVkSGVpZ2h0KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnBsb3QuaGFzRml4ZWRIZWlnaHQ7XG4gIH1cblxuICAvKiogV2hlcmUgdGhlIGFjdGl2ZSBraW5kIGRyYXdzOiBpdHMgb3duIHdpbmRvdywgb3IgdGhlIHNoYXJlZCBjaGFydCBkaXYuICovXG4gIGdldCBwbG90VGFyZ2V0KCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuZGV0YWNoZWQgPyB0aGlzLmRldGFjaGVkRGl2IDogdGhpcy5jaGFydERpdjtcbiAgfVxuXG4gIC8qKlxuICAgKiBIZWFkZXIgZm9yIHRoZSBkZXRhY2hlZCB3aW5kb3cg4oCUIHdoYXQgaXQgaXMgc2hvd2luZywgbmFtZWQgYXMgaXRzIHRhYiBpcy5cbiAgICpcbiAgICogVGhlIGVtYmVkZGluZyBuYW1lcyB0aGUgZW1iZWRkaW5nIHJhdGhlciB0aGFuIHRoZSB0YWIsIGJlY2F1c2UgXCJFbWJlZGRpbmdcIiBiZXNpZGUgYVxuICAgKiB3aW5kb3cgZHJhd2luZyBhIHQtU05FIHNheXMgbGVzcyB0aGFuIFwidC1TTkVcIiBkb2VzLlxuICAgKi9cbiAgcHJvdGVjdGVkIGdldCBkZXRhY2hlZFRpdGxlKCk6IHN0cmluZyB7XG4gICAgaWYgKHRoaXMua2luZCA9PT0gJ2VtYmVkZGluZycpIHtcbiAgICAgIHJldHVybiB0aGlzLmVtYmVkZGluZz8ubGFiZWwgPz8gdGhpcy5lbWJlZGRpbmc/Lm5hbWUgPz8gJ0VtYmVkZGluZyc7XG4gICAgfVxuICAgIGNvbnN0IGxhYmVsID0gdGhpcy5raW5kT3B0aW9ucy5maW5kKChrKSA9PiBrLnZhbHVlID09PSB0aGlzLmtpbmQpPy5sYWJlbCA/PyAnQ2hhcnQnO1xuICAgIHJldHVybiB0aGlzLnN1YmplY3QgPyBgJHtsYWJlbH0gwrcgJHt0aGlzLnN1YmplY3R9YCA6IGxhYmVsO1xuICB9XG5cbiAgLyoqXG4gICAqIE1vdmUgdGhlIGFjdGl2ZSBraW5kIGJldHdlZW4gaXRzIG93biB3aW5kb3cgYW5kIHRoZSBwYW5lbC5cbiAgICpcbiAgICogUHVyZ2VzIHRoZSBkaXYgaXQgaXMgTEVBVklORyBmaXJzdC4gUGxvdGx5IGtlZXBzIHBlci1kaXYgc3RhdGUsIGFuZCBhIGdyYXBoIGxlZnRcbiAgICogYmVoaW5kIGluIGEgZGl2IHRoYXQgQW5ndWxhciB0aGVuIHJlbW92ZXMgbGVha3MgaXRzIFdlYkdMIGNvbnRleHQg4oCUIGFuZCBpZiB0aGUgZGl2XG4gICAqIGNvbWVzIGJhY2ssIHJlYWN0IHdvdWxkIHJlc2l6ZSBhIHBsb3Qgd2hvc2UgZGF0YSBiZWxvbmdzIHRvIHRoZSBvdGhlciBwbGFjZS5cbiAgICovXG4gIHByb3RlY3RlZCB0b2dnbGVEZXRhY2hlZCgpOiB2b2lkIHtcbiAgICBjb25zdCBsZWF2aW5nID0gdGhpcy5wbG90VGFyZ2V0O1xuICAgIHRoaXMuZGV0YWNoZWQgPSAhdGhpcy5kZXRhY2hlZDtcbiAgICB0aGlzLnBsb3QucHVyZ2UobGVhdmluZyk7XG4gICAgLy8gR29pbmcgYmFjayBJTkxJTkUsIHRoZSBkaXYgYXBwZWFycyB3aXRoIHRoZSBuZXh0IGNoYW5nZSBkZXRlY3Rpb24sIHNvIGEgdGFzayBpc1xuICAgIC8vIGVub3VnaC4gR29pbmcgT1VULCB0aGUgd2luZG93J3MgZGl2IGRvZXMgbm90IGV4aXN0IHlldCDigJQgUHJpbWVORyBtb3VudHMgdGhlIGRpYWxvZ1xuICAgIC8vIHdpdGggYSB0cmFuc2l0aW9uIOKAlCBzbyB0aGUgZGlhbG9nJ3Mgb3duIGBvblNob3dgIGRyaXZlcyB0aGF0IGRyYXcgaW5zdGVhZC4gQSBzaW5nbGVcbiAgICAvLyBkZWZlcnJlZCBhdHRlbXB0IGRyZXcgbm90aGluZyBhbmQgbmV2ZXIgcmV0cmllZC5cbiAgICBpZiAoIXRoaXMuZGV0YWNoZWQpIHNldFRpbWVvdXQoKCkgPT4gdm9pZCB0aGlzLnJlbmRlcigpLCAwKTtcbiAgfVxuXG4gIC8qKiBUaGUgZGV0YWNoZWQgd2luZG93IGlzIHVwIGFuZCBpdHMgZGl2IGV4aXN0cyDigJQgbm93IHRoZSBwbG90IGNhbiBiZSBkcmF3biBpbnRvIGl0LiAqL1xuICBwcm90ZWN0ZWQgb25EZXRhY2hlZFdpbmRvd1Nob3duKCk6IHZvaWQge1xuICAgIHZvaWQgdGhpcy5yZW5kZXIoKTtcbiAgfVxuXG4gIC8qKiBSZS1maXQgdGhlIGRldGFjaGVkIHdpbmRvdydzIHBsb3QgYWZ0ZXIgaXQgaXMgcmVzaXplZC4gKi9cbiAgcHJvdGVjdGVkIG9uRGV0YWNoZWRSZXNpemVFbmQoKTogdm9pZCB7XG4gICAgdGhpcy5wbG90LnJlZml0KHRoaXMuZGV0YWNoZWREaXYpO1xuICB9XG5cbiAgLyoqIFRoZSBnZW5lIHBpY2tlcidzIGZpbHRlciBib3ggY2hhbmdlZDogc2VhcmNoIHRoZSByZXNpZGVudCBuYW1lcywgc2hvdyB0aGUgYmVzdC4gKi9cbiAgcHJvdGVjdGVkIG9uR2VuZUZpbHRlcihxdWVyeTogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5kYXRhLmZpbHRlckdlbmVzKHF1ZXJ5KTtcbiAgfVxuXG4gIC8qKiBSb3VnaGx5IGhvdyBsb25nIGEgcnVuIHdpbGwgdGFrZSwgaW4gc2Vjb25kcywgZnJvbSB0aGUgbWVhc3VyZWQgYW5jaG9yLiAqL1xuICBnZXQgY29tcHV0ZUVzdGltYXRlU2Vjb25kcygpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLmNvbXB1dGUuZXN0aW1hdGVTZWNvbmRzO1xuICB9XG5cbiAgLyoqIFRoYXQgZXN0aW1hdGUgYXMgc29tZXRoaW5nIHRvIHB1dCBvbiBhIGJ1dHRvbi4gKi9cbiAgcHJvdGVjdGVkIGdldCBjb21wdXRlRXN0aW1hdGVMYWJlbCgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLmNvbXB1dGUuZXN0aW1hdGVMYWJlbDtcbiAgfVxuXG4gIC8qKiBXaHkgdGhlIG9wdGlvbiBpcyBhYnNlbnQsIHdoZW4gYSBkYXRhc2V0IGlzIHBhc3QgdGhlIHRocmVzaG9sZC4gKi9cbiAgcHJvdGVjdGVkIGdldCB0c25lVG9vTGFyZ2VOb3RlKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuY29tcHV0ZS50b29MYXJnZU5vdGU7XG4gIH1cblxuICAvKiogV2hldGhlciB0aGUgc2VsZWN0ZWQgZW1iZWRkaW5nIGlzIG9uZSB0aGlzIGJyb3dzZXIgd291bGQgaGF2ZSB0byBjb21wdXRlLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IGlzQ29tcHV0YWJsZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5jb21wdXRlLmlzQ29tcHV0YWJsZSh0aGlzLmVtYmVkZGluZyk7XG4gIH1cblxuICAvKiogV2hldGhlciBpdCBoYXMgYWxyZWFkeSBiZWVuIGNvbXB1dGVkIGluIHRoaXMgc2Vzc2lvbi4gKi9cbiAgcHJvdGVjdGVkIGdldCBpc0NvbXB1dGVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmNvbXB1dGUuaXNDb21wdXRlZCh0aGlzLmVtYmVkZGluZyk7XG4gIH1cblxuICBwcm90ZWN0ZWQgZ2V0IGlzQ29tcHV0aW5nKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmNvbXB1dGUucnVubmluZztcbiAgfVxuXG4gIC8qKiBDb21wdXRlIHRoZSBzZWxlY3RlZCBlbWJlZGRpbmcgaGVyZSwgaW4gYSB3b3JrZXIsIHRoZW4gZHJhdyBpdC4gKi9cbiAgcHJvdGVjdGVkIGFzeW5jIGNvbXB1dGVFbWJlZGRpbmcoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgbWV0YSA9IHRoaXMuZW1iZWRkaW5nO1xuICAgIGNvbnN0IGNvbnRyb2xzID0gdGhpcy5jb250cm9scztcbiAgICBpZiAoIW1ldGEgfHwgIWNvbnRyb2xzPy5nZXRFbWJlZGRpbmcgfHwgdGhpcy5pc0NvbXB1dGluZykgcmV0dXJuO1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMuY29tcHV0ZS5zdGFydChtZXRhLCB0aGlzLmVtYmVkZGluZ3MsIChuYW1lKSA9PiBjb250cm9scy5nZXRFbWJlZGRpbmchKG5hbWUpKTtcbiAgICBpZiAoIXJlc3VsdCkgcmV0dXJuO1xuICAgIHRoaXMuZGF0YS5lbWJlZGRpbmdDb29yZHMgPSByZXN1bHQ7XG4gICAgYXdhaXQgdGhpcy5yZW5kZXIoKTtcbiAgfVxuXG4gIC8qKiBTdG9wIGEgcnVuLiBJdCBzZXR0bGVzIHNob3J0bHkgYWZ0ZXIsIGxlYXZpbmcgbm8gZW1iZWRkaW5nLiAqL1xuICBwcm90ZWN0ZWQgY2FuY2VsQ29tcHV0ZSgpOiB2b2lkIHtcbiAgICB0aGlzLmNvbXB1dGUuY2FuY2VsKCk7XG4gIH1cblxuICAvKiogV2hpY2ggZW1iZWRkaW5nIHRvIGRyYXcsIHdoZW4gdGhlIGRhdGFzZXQgcHVibGlzaGVzIG1vcmUgdGhhbiBvbmUuICovXG4gIHByb3RlY3RlZCBvbkVtYmVkZGluZyhuYW1lOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5kYXRhLnNlbGVjdEVtYmVkZGluZyhuYW1lKSkgdm9pZCB0aGlzLnJlbmRlcigpO1xuICB9XG5cbiAgLyoqIFdoYXQgdGhlIGNoYXJ0IG9uIHNjcmVlbiBzaG93cywgYW5kIHdoYXQgaXQgY2Fubm90IGJlIHJlYWQgZm9yIOKAlCBzZWUge0BsaW5rIGtpbmRIZWxwfS4gKi9cbiAgcHJvdGVjdGVkIGdldCBraW5kSGVscCgpOiBzdHJpbmcge1xuICAgIHJldHVybiBraW5kSGVscCh0aGlzLmtpbmQsIHRoaXMuZW1iZWRkaW5nKTtcbiAgfVxuXG4gIC8qKiBXaGF0IHRoZSBlbWJlZGRpbmcgdmlldyBpcyBzaG93aW5nLCBzYWlkIHBsYWlubHkuICovXG4gIHByb3RlY3RlZCBnZXQgZW1iZWRkaW5nTm90ZSgpOiBzdHJpbmcge1xuICAgIHJldHVybiBlbWJlZGRpbmdOb3RlKFxuICAgICAgdGhpcy5kYXRhLmVtYmVkZGluZ0Nvb3Jkcz8ubWV0YSA/PyB0aGlzLmVtYmVkZGluZyxcbiAgICAgIHRoaXMuZGF0YS5pc0NhdGVnb3JpY2FsLFxuICAgICAgdGhpcy5zZWxlY3Rpb24uY291bnQsXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBQdXJnZSB0aGUgQUNUSVZFIHRhcmdldDogcHVyZ2luZyB0aGUgaW5saW5lIGRpdiB3aGlsZSBkZXRhY2hlZCB3b3VsZCBsZWF2ZSB0aGUgd2luZG93XG4gICAqICBzaG93aW5nIGEgcGxvdCB0aGUgY29tcG9uZW50IGJlbGlldmVzIGl0IGhhcyBjbGVhcmVkLiAqL1xuICBwcml2YXRlIHB1cmdlUGxvdCgpOiB2b2lkIHtcbiAgICB0aGlzLnBsb3QucHVyZ2UodGhpcy5wbG90VGFyZ2V0KTtcbiAgfVxuXG4gIC8qKiBEcmF3IGludG8gdGhlIGFjdGl2ZSB0YXJnZXQuICovXG4gIHByaXZhdGUgZHJhdyh0cmFjZXM6IHVua25vd24sIGxheW91dDogdW5rbm93bik6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLnBsb3QuZHJhdyh0aGlzLnBsb3RUYXJnZXQsIHRyYWNlcywgbGF5b3V0KTtcbiAgfVxuXG4gIC8qKiBEcmF3IHRoZSBhY3RpdmUga2luZCwgdGhlbiByZS1yZW5kZXIgdGhlIHBhbmVsIGFyb3VuZCBpdCAobm90aWNlcywgaGVpZ2h0LCBub3RlcykuICovXG4gIHByaXZhdGUgYXN5bmMgcmVuZGVyKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLnJlbmRlckNoYXJ0KCk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIHRoaXMuY2hhbmdlZCgpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgcmVuZGVyQ2hhcnQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKCF0aGlzLmlzQWN0aXZlKSByZXR1cm47XG4gICAgLy8gR3VhcmQgb24gdGhlIGRpdiB0aGUgQUNUSVZFIGtpbmQgd2lsbCBkcmF3IGludG8sIG5vdCBhbHdheXMgdGhlIHNoYXJlZCBvbmUuIFdoaWxlIGFcbiAgICAvLyBraW5kIGlzIGRldGFjaGVkIHRoZSBpbmxpbmUgZGl2IGlzIHJlbW92ZWQgYnkgaXRzIGAqbmdJZmAsIHNvIGNoZWNraW5nIHRoYXQgb25lXG4gICAgLy8gYmFpbGVkIGhlcmUgYW5kIHRoZSBkZXRhY2hlZCB3aW5kb3cgbmV2ZXIgZ290IGEgcGxvdC5cbiAgICBjb25zdCB0YXJnZXQgPSB0aGlzLnBsb3RUYXJnZXQ7XG4gICAgaWYgKCFkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0YXJnZXQpKSByZXR1cm47XG4gICAgLy8gQW55dGhpbmcgYnV0IHRoZSBlbWJlZGRpbmcgaW5oZXJpdHMgYSBkaXYgdGhlIGVtYmVkZGluZyBtYXkgaGF2ZSBib3VuZCBoYW5kbGVycyBvbi5cbiAgICBpZiAodGhpcy5raW5kICE9PSAnZW1iZWRkaW5nJykgdGhpcy5wbG90LnVuYmluZFNlbGVjdGlvbih0YXJnZXQpO1xuICAgIC8vIEEgaGVhdG1hcCBzdGlsbCBjb21wdXRpbmcgbXVzdCBub3QgZHJhdyBvdmVyIHRoZSBraW5kIHRoYXQgcmVwbGFjZWQgaXQuXG4gICAgaWYgKHRoaXMua2luZCAhPT0gJ2hlYXRtYXAnKSB0aGlzLmhlYXRtYXBSZW5kZXIuY2FuY2VsKCk7XG4gICAgLy8gVGhlIGhlYXRtYXAgYW5zd2VycyBhIGRpZmZlcmVudCBxdWVzdGlvbiBmcm9tIHRoZSBvdGhlciBraW5kcyDigJQgd2hpY2hcbiAgICAvLyBnZW5lcyBkaXN0aW5ndWlzaCB3aGljaCBncm91cHMg4oCUIHNvIGl0IGlzIGRyaXZlbiBieSBpdHMgb3duIGdlbmUgbGlzdCBhbmRcbiAgICAvLyBncm91cGluZyByYXRoZXIgdGhhbiBieSB3aGF0ZXZlciB0aGUgbWFwIGlzIGNvbG91cmVkIGJ5LlxuICAgIGlmICh0aGlzLmtpbmQgPT09ICdoZWF0bWFwJykge1xuICAgICAgYXdhaXQgdGhpcy5yZW5kZXJIZWF0bWFwKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIEFsc28gaW5kZXBlbmRlbnQgb2YgdGhlIGFjdGl2ZSBjb2xvdXIgc291cmNlOiB0aGUgY29vcmRpbmF0ZXMgYXJlIHRoZSBkYXRhc2V0J3MsXG4gICAgLy8gYW5kIHRoZSBjb2xvdXJpbmcgbWVyZWx5IGZvbGxvd3Mgd2hhdGV2ZXIgdGhlIG1hcCBpcyB1c2luZy5cbiAgICBpZiAodGhpcy5raW5kID09PSAnZW1iZWRkaW5nJykge1xuICAgICAgYXdhaXQgdGhpcy5yZW5kZXJFbWJlZGRpbmcoKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgeyB2YWx1ZXMsIGNhdGVnb3JpY2FsLCBncm91cGluZyB9ID0gdGhpcy5kYXRhLnNuYXBzaG90KCk7XG4gICAgaWYgKCF0aGlzLmNvbG9yQnkgfHwgKCF2YWx1ZXMgJiYgIWNhdGVnb3JpY2FsKSkge1xuICAgICAgdGhpcy5wdXJnZVBsb3QoKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGNhdGVnb3JpY2FsKSB7XG4gICAgICBjb25zdCBjb3VudHMgPSB7XG4gICAgICAgIGdyb3VwOiBjYXRlZ29yaWNhbCxcbiAgICAgICAgc2VsZWN0aW9uOiB0aGlzLnNlbGVjdGlvbi5jb3VudCA+IDAgPyB0aGlzLnNlbGVjdGlvbi5tYXNrIDogbnVsbCxcbiAgICAgICAgbmFtZTogdGhpcy5jb2xvckJ5Lm5hbWUsXG4gICAgICB9O1xuICAgICAgLy8gQ291bnRlZCBvbmNlIGZvciBib3RoOiB0aGUgbGF5b3V0J3MgaGVpZ2h0IGZvbGxvd3MgdGhlIGJhciBjb3VudC5cbiAgICAgIGNvbnN0IHRhbGx5ID0gY291bnRCeUNhdGVnb3J5KGNvdW50cyk7XG4gICAgICBhd2FpdCB0aGlzLmRyYXcoYnVpbGRDb3VudFRyYWNlcyhjb3VudHMsIHRhbGx5KSwgY291bnRzTGF5b3V0KGNvdW50cywgdGFsbHkpKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKCF2YWx1ZXMpIHJldHVybjtcbiAgICBjb25zdCBpbnB1dCA9IHtcbiAgICAgIHZhbHVlcyxcbiAgICAgIG5hbWU6IHRoaXMuY29sb3JCeS5uYW1lLFxuICAgICAgZ3JvdXA6IGJlbmVmaXRzRnJvbUdyb3VwaW5nKHRoaXMua2luZCkgPyBncm91cGluZyA6IG51bGwsXG4gICAgICBzZWxlY3Rpb246IHRoaXMuc2VsZWN0aW9uLmNvdW50ID4gMCA/IHRoaXMuc2VsZWN0aW9uLm1hc2sgOiBudWxsLFxuICAgICAgbG9nOiB0aGlzLnZpZXcubG9nU2NhbGUsXG4gICAgfTtcbiAgICBjb25zdCB0cmFjZXMgPSBidWlsZE9taWNzVHJhY2VzKHRoaXMua2luZCwgaW5wdXQpO1xuICAgIGF3YWl0IHRoaXMuZHJhdyh0cmFjZXMsIG9taWNzTGF5b3V0KHRoaXMua2luZCwgaW5wdXQpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZW5lcyDDlyBncm91cHMgbWVhbiBleHByZXNzaW9uLlxuICAgKlxuICAgKiBDb2x1bW5zIGFyZSB0aGUgZ3JvdXBpbmcgY29sdW1uJ3MgY2F0ZWdvcmllcyDigJQgb3IsIGluc2lkZSBhIFNNQUxMXG4gICAqIHNlbGVjdGlvbiwgdGhlIHNlbGVjdGVkIGNlbGxzIHRoZW1zZWx2ZXMuIEEgdHdvLWNvbHVtbiBjbGFzcyBtYXRyaXggaXMgYVxuICAgKiBwb29yIGFuc3dlciBmb3IgYW4gUk9JIGhvbGRpbmcgZm9ydHkgY2VsbHM7IFwid2hhdCBpcyBpbiBmcm9udCBvZiBtZVwiIGlzIHRoZVxuICAgKiBxdWVzdGlvbiB0aGVyZSwgYW5kIGVhY2ggY2VsbCBlYXJucyBhIGNvbHVtbi5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcmVuZGVySGVhdG1hcCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB7IGhlYXRtYXBSb3dzOiBnZW5lcywgZ3JvdXBpbmcgfSA9IHRoaXMuZGF0YS5zbmFwc2hvdCgpO1xuICAgIGlmICghdGhpcy5jb250cm9scyB8fCBnZW5lcy5sZW5ndGggPT09IDAgfHwgIWdyb3VwaW5nKSB7XG4gICAgICB0aGlzLnB1cmdlUGxvdCgpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHNlbGVjdGVkID0gdGhpcy5zZWxlY3Rpb24uY291bnQgPiAwID8gbWFza1RvSW5kaWNlcyh0aGlzLnNlbGVjdGlvbi5tYXNrKSA6IG51bGw7XG4gICAgY29uc3QgcGVyQ2VsbCA9ICEhc2VsZWN0ZWQgJiYgc2VsZWN0ZWQubGVuZ3RoIDw9IFNwYXRpYWxDaGFydHNDb21wb25lbnQuSEVBVE1BUF9DRUxMX0NPTFVNTlM7XG4gICAgY29uc3QgY2VsbHMgPVxuICAgICAgcGVyQ2VsbCAmJiBzZWxlY3RlZFxuICAgICAgICA/IGNlbGxzQXNHcm91cHMoc2VsZWN0ZWQsIGdyb3VwaW5nLmNvZGVzLmxlbmd0aCwgU3BhdGlhbENoYXJ0c0NvbXBvbmVudC5IRUFUTUFQX0NFTExfQ09MVU1OUylcbiAgICAgICAgOiBudWxsO1xuXG4gICAgLy8gT2ZmIHRoZSBtYWluIHRocmVhZCBmb3IgYSBiaWcgZGF0YXNldCAoZ2VuZXMgw5cgY2VsbHMgcGFzdCB0aGUgd29ya2VyIHRocmVzaG9sZCksIGFuZFxuICAgIC8vIHN1cGVyc2VkZWQgYnkgdGhlIG5leHQgaGVhdG1hcCBkcmF3OiBhIHNsb3cgbWF0cml4IGZvciBhbiBlYXJsaWVyIGdlbmUgbGlzdCBvclxuICAgIC8vIHNlbGVjdGlvbiBtdXN0IG5vdCBsYW5kIG92ZXIgdGhlIGN1cnJlbnQgb25lLCBhbmQgaXRzIHdvcmtlciBzdG9wcy5cbiAgICBjb25zdCB0YXNrID0gdGhpcy5oZWF0bWFwUmVuZGVyLm5leHQoKTtcbiAgICBsZXQgbWF0cml4OiBIZWF0bWFwTWF0cml4IHwgbnVsbDtcbiAgICB0cnkge1xuICAgICAgbWF0cml4ID0gYXdhaXQgY29tcHV0ZUhlYXRtYXBNYXRyaXhBc3luYyhcbiAgICAgICAgZ2VuZXMsXG4gICAgICAgIGNlbGxzID8gY2VsbHMuZ3JvdXBzIDogeyBjb2RlczogZ3JvdXBpbmcuY29kZXMsIGNhdGVnb3JpZXM6IGdyb3VwaW5nLmNhdGVnb3JpZXMgfSxcbiAgICAgICAge1xuICAgICAgICAgIC4uLihjZWxscyA/IHsgaW5kaWNlczogY2VsbHMuaW5kaWNlcywgbWluQ2VsbHM6IDEgfSA6IHt9KSxcbiAgICAgICAgICAvLyBPdXRzaWRlIHRoZSBwZXItY2VsbCB2aWV3IGEgc2VsZWN0aW9uIHN0aWxsIG5hcnJvd3MgdGhlIG1lYW5zLCB0aGUgd2F5XG4gICAgICAgICAgLy8gdGhlIHZpb2xpbiBhbmQgYm94IG5hcnJvdyByYXRoZXIgdGhhbiBvdmVybGF5LlxuICAgICAgICAgIC4uLighY2VsbHMgJiYgc2VsZWN0ZWQgPyB7IGluZGljZXM6IHNlbGVjdGVkIH0gOiB7fSksXG4gICAgICAgICAgLy8gT25seSB0aGUgZ3JvdXBlZCB2aWV3OiBgY2VsbHNBc0dyb3Vwc2AgYWxyZWFkeSBjYXBzIGJ5IGV2ZW4gdGhpbm5pbmcsXG4gICAgICAgICAgLy8gYW5kIHJlLXJhbmtpbmcgdGhvc2UgY29sdW1ucyB3b3VsZCBsb3NlIHRoZSBzZWxlY3Rpb24ncyBzcHJlYWQuXG4gICAgICAgICAgLi4uKGNlbGxzID8ge30gOiB7IG1heENvbHM6IFNwYXRpYWxDaGFydHNDb21wb25lbnQuSEVBVE1BUF9NQVhfQ09MVU1OUyB9KSxcbiAgICAgICAgICB6U2NvcmU6IHRoaXMuaGVhdG1hcFpTY29yZSxcbiAgICAgICAgfSxcbiAgICAgICAgeyBzaWduYWw6IHRhc2suc2lnbmFsIH0sXG4gICAgICApO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgaWYgKCF0YXNrLmlzQ3VycmVudCgpKSByZXR1cm47XG4gICAgICB0aHJvdyBlcnI7XG4gICAgfVxuICAgIGlmICghdGFzay5pc0N1cnJlbnQoKSkgcmV0dXJuO1xuICAgIGlmICghbWF0cml4KSB7XG4gICAgICB0aGlzLnB1cmdlUGxvdCgpO1xuICAgICAgdGhpcy5ub3RpY2UgPSAnTm8gZ3JvdXAgaGFzIGVub3VnaCBtZWFzdXJlZCBjZWxscyBmb3IgYSBtZWFuLic7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMubm90aWNlID0gbnVsbDtcbiAgICB0aGlzLmhlYXRtYXBIaWRkZW4gPSBtYXRyaXguaGlkZGVuQ29scztcbiAgICBjb25zdCBpbnB1dCA9IHtcbiAgICAgIHJvd3M6IG1hdHJpeC5yb3dzLFxuICAgICAgY29sczogbWF0cml4LmNvbHMsXG4gICAgICB2YWx1ZXM6IG1hdHJpeC52YWx1ZXMsXG4gICAgICBjb3VudHM6IG1hdHJpeC5jb3VudHMsXG4gICAgICB6U2NvcmVkOiB0aGlzLmhlYXRtYXBaU2NvcmUsXG4gICAgICBncm91cExhYmVsOiBjZWxscyA/ICdzZWxlY3RlZCBjZWxscycgOiAodGhpcy5ncm91cEJ5ID8/ICcnKSxcbiAgICB9O1xuICAgIGF3YWl0IHRoaXMuZHJhdyhidWlsZEhlYXRtYXBUcmFjZXMoaW5wdXQpLCBoZWF0bWFwTGF5b3V0KGlucHV0KSk7XG4gIH1cbn1cbiIsIjxkaXYgY2xhc3M9XCJzeC1ib2R5XCIgKm5nSWY9XCJjb250cm9sc1wiPlxuICA8IS0tIFRoZSBjaGFydCBmb2xsb3dzIHdoYXRldmVyIHRoZSBtYXAgaXMgY29sb3VyZWQgYnksIHNvIHRoZSB0d28gY2FuIG5ldmVyXG4gICAgICAgZGlzYWdyZWUgYWJvdXQgd2hhdCBpcyBiZWluZyBzaG93bi4gLS0+XG4gIDxkaXYgY2xhc3M9XCJzeC1oZWFkXCI+XG4gICAgPHNwYW4gY2xhc3M9XCJzeC1zdWJqZWN0XCIgKm5nSWY9XCJzdWJqZWN0XCI+e3sgc3ViamVjdCB9fTwvc3Bhbj5cbiAgICA8c3BhbiBjbGFzcz1cInN4LXN1YmplY3Qgc3gtbXV0ZWRcIiAqbmdJZj1cIiFzdWJqZWN0XCI+bm90aGluZyBzZWxlY3RlZCB0byBjaGFydDwvc3Bhbj5cbiAgICA8c3BhbiBjbGFzcz1cInN4LWJhZGdlXCIgKm5nSWY9XCJzZWxlY3Rpb25Db3VudFwiPiB7eyBzZWxlY3Rpb25Db3VudCB8IG51bWJlciB9fSBzZWxlY3RlZCA8L3NwYW4+XG4gICAgPGkgY2xhc3M9XCJwaSBwaS1zcGluIHBpLXNwaW5uZXIgc3gtYnVzeVwiICpuZ0lmPVwiYnVzeVwiIGFyaWEtbGFiZWw9XCJsb2FkaW5nXCI+PC9pPlxuICA8L2Rpdj5cblxuICA8ZGl2IGNsYXNzPVwic3gtY29udHJvbHNcIj5cbiAgICA8cC1zZWxlY3RCdXR0b25cbiAgICAgIFtvcHRpb25zXT1cImtpbmRPcHRpb25zXCJcbiAgICAgIG9wdGlvbkxhYmVsPVwibGFiZWxcIlxuICAgICAgb3B0aW9uVmFsdWU9XCJ2YWx1ZVwiXG4gICAgICBbbmdNb2RlbF09XCJraW5kXCJcbiAgICAgIChuZ01vZGVsQ2hhbmdlKT1cIm9uS2luZCgkZXZlbnQpXCJcbiAgICAgIHN0eWxlQ2xhc3M9XCJzeC1raW5kXCJcbiAgICA+PC9wLXNlbGVjdEJ1dHRvbj5cbiAgICA8cC1kcm9wZG93blxuICAgICAgW29wdGlvbnNdPVwiZ3JvdXBPcHRpb25zXCJcbiAgICAgIG9wdGlvbkxhYmVsPVwibGFiZWxcIlxuICAgICAgb3B0aW9uVmFsdWU9XCJ2YWx1ZVwiXG4gICAgICBbbmdNb2RlbF09XCJncm91cEJ5XCJcbiAgICAgIChuZ01vZGVsQ2hhbmdlKT1cIm9uR3JvdXBCeSgkZXZlbnQpXCJcbiAgICAgIHBsYWNlaG9sZGVyPVwiTm8gZ3JvdXBpbmdcIlxuICAgICAgYXBwZW5kVG89XCJib2R5XCJcbiAgICAgIFtkaXNhYmxlZF09XCJraW5kID09PSAnaGlzdG9ncmFtJ1wiXG4gICAgICBbcFRvb2x0aXBdPVwiXG4gICAgICAgIGtpbmQgPT09ICdoZWF0bWFwJ1xuICAgICAgICAgID8gJ1RoZSBjYXRlZ29yaWNhbCBjb2x1bW4gdGhlIGhlYXRtYXBcXCdzIGNvbHVtbnMgY29tZSBmcm9tJ1xuICAgICAgICAgIDogJ1NwbGl0IGEgdmlvbGluIG9yIGJveCBieSBhIGNhdGVnb3JpY2FsIGNvbHVtbidcbiAgICAgIFwiXG4gICAgICB0b29sdGlwUG9zaXRpb249XCJ0b3BcIlxuICAgID48L3AtZHJvcGRvd24+XG4gICAgPCEtLSBXaGF0IHRoZSBjaGFydCBvbiBzY3JlZW4gc2hvd3MsIGFuZCB3aGF0IGl0IG11c3QgTk9UIGJlIHJlYWQgZm9yLiBFYWNoIG9mIHRoZXNlXG4gICAgICAgICBhbnN3ZXJzIGEgZGlmZmVyZW50IHF1ZXN0aW9uIGFuZCB0d28gYXJlIHJvdXRpbmVseSBvdmVyLXJlYWQsIHNvIHRoZSBjYXZlYXRcbiAgICAgICAgIHRyYXZlbHMgd2l0aCB0aGUgZXhwbGFuYXRpb24uIC0tPlxuICAgIDxpXG4gICAgICBjbGFzcz1cInBpIHBpLXF1ZXN0aW9uLWNpcmNsZSBzeC1oZWxwXCJcbiAgICAgIFtwVG9vbHRpcF09XCJraW5kSGVscFwiXG4gICAgICBbZXNjYXBlXT1cImZhbHNlXCJcbiAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImxlZnRcIlxuICAgICAgdG9vbHRpcFN0eWxlQ2xhc3M9XCJzeC1oZWxwLXRpcFwiXG4gICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cIidBYm91dCB0aGUgJyArIGtpbmQgKyAnIGNoYXJ0J1wiXG4gICAgICB0YWJpbmRleD1cIjBcIlxuICAgID48L2k+XG4gIDwvZGl2PlxuXG4gIDxzcGF0aWFsLWhlYXRtYXAtY29udHJvbHNcbiAgICAqbmdJZj1cImtpbmQgPT09ICdoZWF0bWFwJ1wiXG4gICAgW2dlbmVPcHRpb25zXT1cImdlbmVPcHRpb25zXCJcbiAgICBbZ2VuZXNdPVwiaGVhdG1hcEdlbmVzXCJcbiAgICBbelNjb3JlXT1cImhlYXRtYXBaU2NvcmVcIlxuICAgIChnZW5lc0NoYW5nZSk9XCJvbkhlYXRtYXBHZW5lcygkZXZlbnQpXCJcbiAgICAoZmlsdGVyQ2hhbmdlKT1cIm9uR2VuZUZpbHRlcigkZXZlbnQpXCJcbiAgICAoelNjb3JlQ2hhbmdlKT1cIm9uSGVhdG1hcFpTY29yZSgkZXZlbnQpXCJcbiAgPjwvc3BhdGlhbC1oZWF0bWFwLWNvbnRyb2xzPlxuXG4gIDxzcGF0aWFsLWVtYmVkZGluZy1jb250cm9sc1xuICAgIFtpc0VtYmVkZGluZ109XCJraW5kID09PSAnZW1iZWRkaW5nJ1wiXG4gICAgW2VtYmVkZGluZ3NdPVwiZW1iZWRkaW5nc1wiXG4gICAgW2VtYmVkZGluZ109XCJlbWJlZGRpbmdcIlxuICAgIFtjb21wdXRhYmxlXT1cImlzQ29tcHV0YWJsZVwiXG4gICAgW2NvbXB1dGVkXT1cImlzQ29tcHV0ZWRcIlxuICAgIFtydW5uaW5nXT1cImlzQ29tcHV0aW5nXCJcbiAgICBbc3RhdGVdPVwiY29tcHV0ZVN0YXRlXCJcbiAgICBbZXN0aW1hdGVMYWJlbF09XCJjb21wdXRlRXN0aW1hdGVMYWJlbFwiXG4gICAgW3Rvb0xhcmdlTm90ZV09XCJ0c25lVG9vTGFyZ2UgPyB0c25lVG9vTGFyZ2VOb3RlIDogbnVsbFwiXG4gICAgW2RldGFjaGVkXT1cImRldGFjaGVkXCJcbiAgICAoZW1iZWRkaW5nQ2hhbmdlKT1cIm9uRW1iZWRkaW5nKCRldmVudClcIlxuICAgIChjb21wdXRlKT1cImNvbXB1dGVFbWJlZGRpbmcoKVwiXG4gICAgKGNhbmNlbCk9XCJjYW5jZWxDb21wdXRlKClcIlxuICAgIChkZXRhY2hlZFRvZ2dsZSk9XCJ0b2dnbGVEZXRhY2hlZCgpXCJcbiAgPlxuICA8L3NwYXRpYWwtZW1iZWRkaW5nLWNvbnRyb2xzPlxuXG4gIDxkaXYgW2lkXT1cImNoYXJ0RGl2XCIgY2xhc3M9XCJzeC1wbG90XCIgKm5nSWY9XCIhZGV0YWNoZWRcIj48L2Rpdj5cblxuICA8IS0tIERldGFjaGVkOiB0aGUgY2hhcnQga2VlcHMgZHJhd2luZywgaW4gaXRzIG93biB3aW5kb3cuIFNheXMgc28gcmF0aGVyIHRoYW5cbiAgICAgICBsZWF2aW5nIGEgYmxhbmsgcGFuZWwgdGhhdCBsb29rcyBicm9rZW4uIC0tPlxuICA8ZGl2IGNsYXNzPVwic3gtZGV0YWNoZWRcIiAqbmdJZj1cImRldGFjaGVkXCI+U2hvd24gaW4gaXRzIG93biB3aW5kb3cuPC9kaXY+XG5cbiAgPG5nLWNvbnRhaW5lciAqbmdUZW1wbGF0ZU91dGxldD1cImhpbnRzXCI+PC9uZy1jb250YWluZXI+XG48L2Rpdj5cblxuPCEtLSBEZWNsYXJlZCBvbmNlIGFuZCByZW5kZXJlZCBpbiBib3RoIHRoZSBwYW5lbCBhbmQgdGhlIGRldGFjaGVkIHdpbmRvdzogdGhlIGxhZGRlciBpc1xuICAgICBkZWxpYmVyYXRlbHkgbm90IG11dHVhbGx5IGV4Y2x1c2l2ZSAoYSBoZWF0bWFwIHN0aWxsIG5lZWRzIGl0cyBncm91cGluZyBwcm9tcHQpLCBzb1xuICAgICBjb2xsYXBzaW5nIGl0IHRvIGEgc2luZ2xlIGV4cHJlc3Npb24gd291bGQgZHJvcCBhIGhpbnQgcmF0aGVyIHRoYW4gbW92ZSBpdC4gLS0+XG48bmctdGVtcGxhdGUgI2hpbnRzPlxuICA8c21hbGwgY2xhc3M9XCJzeC1oaW50XCIgKm5nSWY9XCJub3RpY2VcIj57eyBub3RpY2UgfX08L3NtYWxsPlxuICA8c21hbGwgY2xhc3M9XCJzeC1oaW50XCIgKm5nSWY9XCIhbm90aWNlICYmIHN1Z2dlc3RzR3JvdXBpbmdcIj4gUGljayBhIGdyb3VwaW5nIHRvIGNvbXBhcmUgY2F0ZWdvcmllcy4gPC9zbWFsbD5cbiAgPHNtYWxsIGNsYXNzPVwic3gtaGludFwiICpuZ0lmPVwiIW5vdGljZSAmJiBzZWxlY3Rpb25Db3VudCAmJiBraW5kID09PSAnaGlzdG9ncmFtJ1wiPlxuICAgIFRoZSBzZWxlY3Rpb24gaXMgb3ZlcmxhaWQgb24gdGhlIGZ1bGwgZGlzdHJpYnV0aW9uLlxuICA8L3NtYWxsPlxuICA8c21hbGwgY2xhc3M9XCJzeC1oaW50XCIgKm5nSWY9XCIhbm90aWNlICYmIHNlbGVjdGlvbkNvdW50ICYmIGtpbmQgIT09ICdoaXN0b2dyYW0nICYmIGtpbmQgIT09ICdoZWF0bWFwJ1wiPlxuICAgIFNob3dpbmcgdGhlIHNlbGVjdGlvbiBvbmx5LlxuICA8L3NtYWxsPlxuICA8c21hbGwgY2xhc3M9XCJzeC1oaW50XCIgKm5nSWY9XCIhbm90aWNlICYmIGtpbmQgPT09ICdoZWF0bWFwJ1wiPnt7IGhlYXRtYXBOb3RlIH19PC9zbWFsbD5cbiAgPHNtYWxsIGNsYXNzPVwic3gtaGludFwiICpuZ0lmPVwiIW5vdGljZSAmJiBraW5kID09PSAnZW1iZWRkaW5nJ1wiPnt7IGVtYmVkZGluZ05vdGUgfX08L3NtYWxsPlxuPC9uZy10ZW1wbGF0ZT5cblxuPHNwYXRpYWwtY2hhcnQtd2luZG93XG4gICpuZ0lmPVwiZGV0YWNoZWRcIlxuICBbdGl0bGVdPVwiZGV0YWNoZWRUaXRsZVwiXG4gIFtkaXZJZF09XCJkZXRhY2hlZERpdlwiXG4gIFtmaXhlZEhlaWdodF09XCJoYXNGaXhlZEhlaWdodFwiXG4gIChjbG9zZWQpPVwidG9nZ2xlRGV0YWNoZWQoKVwiXG4gIChzaG93bik9XCJvbkRldGFjaGVkV2luZG93U2hvd24oKVwiXG4gIChyZXNpemVFbmQpPVwib25EZXRhY2hlZFJlc2l6ZUVuZCgpXCJcbj5cbiAgPG5nLWNvbnRhaW5lciAqbmdUZW1wbGF0ZU91dGxldD1cImhpbnRzXCI+PC9uZy1jb250YWluZXI+XG48L3NwYXRpYWwtY2hhcnQtd2luZG93PlxuIl19