speechrecorderng 3.6.0 → 3.8.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 (220) hide show
  1. package/esm2022/lib/action/action.mjs +73 -0
  2. package/{esm2020 → esm2022}/lib/audio/array_audio_buffer.mjs +163 -163
  3. package/{esm2020 → esm2022}/lib/audio/array_audio_buffer_input_stream.mjs +85 -85
  4. package/{esm2020 → esm2022}/lib/audio/array_audio_buffer_random_access_stream.mjs +15 -15
  5. package/esm2022/lib/audio/audio_data_holder.mjs +264 -0
  6. package/{esm2020 → esm2022}/lib/audio/audio_display.mjs +93 -93
  7. package/{esm2020 → esm2022}/lib/audio/audio_player.mjs +213 -213
  8. package/esm2022/lib/audio/capture/capture.mjs +855 -0
  9. package/esm2022/lib/audio/context.mjs +79 -0
  10. package/{esm2020 → esm2022}/lib/audio/dsp/level_measure.mjs +515 -515
  11. package/{esm2020 → esm2022}/lib/audio/format.mjs +16 -16
  12. package/esm2022/lib/audio/impl/wavformat.mjs +6 -0
  13. package/{esm2020 → esm2022}/lib/audio/impl/wavreader.mjs +133 -133
  14. package/esm2022/lib/audio/impl/wavwriter.mjs +105 -0
  15. package/esm2022/lib/audio/inddb_audio_buffer.mjs +508 -0
  16. package/{esm2020 → esm2022}/lib/audio/io/stream.mjs +58 -58
  17. package/esm2022/lib/audio/net_audio_buffer.mjs +293 -0
  18. package/{esm2020 → esm2022}/lib/audio/persistor.mjs +80 -80
  19. package/{esm2020 → esm2022}/lib/audio/playback/array_audio_buffer_source_node.mjs +125 -125
  20. package/esm2022/lib/audio/playback/audio_source_node.mjs +18 -0
  21. package/esm2022/lib/audio/playback/audio_source_worklet_module_loader.mjs +167 -0
  22. package/{esm2020 → esm2022}/lib/audio/playback/inddb_audio_buffer_source_node.mjs +166 -166
  23. package/{esm2020 → esm2022}/lib/audio/playback/net_audio_buffer_source_node.mjs +217 -217
  24. package/esm2022/lib/audio/playback/player.mjs +402 -0
  25. package/esm2022/lib/audio/ui/audio_canvas_layer_comp.mjs +347 -0
  26. package/{esm2020 → esm2022}/lib/audio/ui/audio_display_control.mjs +65 -65
  27. package/{esm2020 → esm2022}/lib/audio/ui/audio_display_scroll_pane.mjs +139 -139
  28. package/{esm2020 → esm2022}/lib/audio/ui/audiosignal.mjs +524 -524
  29. package/{esm2020 → esm2022}/lib/audio/ui/common.mjs +18 -18
  30. package/esm2022/lib/audio/ui/container.mjs +414 -0
  31. package/{esm2020 → esm2022}/lib/audio/ui/livelevel.mjs +352 -352
  32. package/esm2022/lib/audio/ui/scroll_pane_horizontal.mjs +11 -0
  33. package/esm2022/lib/audio/ui/sonagram.mjs +900 -0
  34. package/esm2022/lib/db/inddb.mjs +120 -0
  35. package/esm2022/lib/dsp/utils.mjs +48 -0
  36. package/{esm2020 → esm2022}/lib/io/BinaryReader.mjs +84 -84
  37. package/esm2022/lib/io/BinaryWriter.mjs +74 -0
  38. package/{esm2020 → esm2022}/lib/io/stream.mjs +258 -258
  39. package/{esm2020 → esm2022}/lib/math/2d/geometry.mjs +27 -27
  40. package/esm2022/lib/math/complex.mjs +58 -0
  41. package/{esm2020 → esm2022}/lib/math/dft.mjs +195 -195
  42. package/{esm2020 → esm2022}/lib/media/utils.mjs +13 -13
  43. package/esm2022/lib/net/uploader.mjs +367 -0
  44. package/{esm2020 → esm2022}/lib/recorder_component.mjs +64 -64
  45. package/esm2022/lib/speechrecorder/project/project.mjs +49 -0
  46. package/esm2022/lib/speechrecorder/project/project.service.mjs +64 -0
  47. package/{esm2020 → esm2022}/lib/speechrecorder/recording.mjs +123 -123
  48. package/esm2022/lib/speechrecorder/recordings/basic_recording.service.mjs +218 -0
  49. package/esm2022/lib/speechrecorder/recordings/recordings.service.mjs +1014 -0
  50. package/{esm2020 → esm2022}/lib/speechrecorder/script/script.mjs +114 -114
  51. package/{esm2020 → esm2022}/lib/speechrecorder/script/script.service.mjs +47 -47
  52. package/esm2022/lib/speechrecorder/session/audiorecorder.mjs +1179 -0
  53. package/esm2022/lib/speechrecorder/session/basicrecorder.mjs +668 -0
  54. package/esm2022/lib/speechrecorder/session/controlpanel.mjs +416 -0
  55. package/{esm2020 → esm2022}/lib/speechrecorder/session/item.mjs +29 -29
  56. package/{esm2020 → esm2022}/lib/speechrecorder/session/progress.mjs +69 -69
  57. package/{esm2020 → esm2022}/lib/speechrecorder/session/prompting.mjs +571 -571
  58. package/{esm2020 → esm2022}/lib/speechrecorder/session/recorder_combi_pane.mjs +65 -65
  59. package/esm2022/lib/speechrecorder/session/recording_file_cache.mjs +195 -0
  60. package/{esm2020 → esm2022}/lib/speechrecorder/session/recording_list.mjs +103 -103
  61. package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file-meta.component.mjs +63 -63
  62. package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file-navi.component.mjs +51 -51
  63. package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file-u-i.component.mjs +104 -104
  64. package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file-view.component.mjs +381 -381
  65. package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file.mjs +67 -67
  66. package/esm2022/lib/speechrecorder/session/recordingfile/recordingfile-service.mjs +288 -0
  67. package/{esm2020 → esm2022}/lib/speechrecorder/session/session.mjs +1 -1
  68. package/esm2022/lib/speechrecorder/session/session.service.mjs +69 -0
  69. package/{esm2020 → esm2022}/lib/speechrecorder/session/session_finished_dialog.mjs +29 -29
  70. package/esm2022/lib/speechrecorder/session/sessionmanager.mjs +1386 -0
  71. package/{esm2020 → esm2022}/lib/speechrecorder/session/warning_bar.mjs +28 -28
  72. package/{esm2020 → esm2022}/lib/speechrecorder/spruploader.mjs +22 -22
  73. package/{esm2020 → esm2022}/lib/speechrecorder/startstopsignal/startstopsignal.mjs +1 -1
  74. package/esm2022/lib/speechrecorder/startstopsignal/ui/simpletrafficlight.mjs +57 -0
  75. package/{esm2020 → esm2022}/lib/speechrecorderng.component.mjs +388 -388
  76. package/{esm2020 → esm2022}/lib/speechrecorderng.module.mjs +100 -100
  77. package/{esm2020 → esm2022}/lib/spr.config.mjs +26 -26
  78. package/{esm2020 → esm2022}/lib/spr.module.version.mjs +2 -2
  79. package/{esm2020 → esm2022}/lib/ui/canvas_layer_comp.mjs +38 -38
  80. package/{esm2020 → esm2022}/lib/ui/message_dialog.mjs +30 -30
  81. package/esm2022/lib/ui/recordingitem_display.mjs +253 -0
  82. package/{esm2020 → esm2022}/lib/ui/responsive_component.mjs +24 -24
  83. package/{esm2020 → esm2022}/lib/utils/scrollIntoViewToBottom.mjs +23 -23
  84. package/esm2022/lib/utils/ua-parser.mjs +190 -0
  85. package/esm2022/lib/utils/utils.mjs +105 -0
  86. package/esm2022/lib/utils/wake_lock.mjs +114 -0
  87. package/{esm2020 → esm2022}/lib/utils/wake_lock_media.mjs +1 -1
  88. package/{esm2020 → esm2022}/public-api.mjs +33 -33
  89. package/{esm2020 → esm2022}/speechrecorderng.mjs +4 -4
  90. package/{fesm2020 → fesm2022}/speechrecorderng.mjs +16518 -16454
  91. package/fesm2022/speechrecorderng.mjs.map +1 -0
  92. package/index.d.ts +5 -5
  93. package/lib/action/action.d.ts +27 -27
  94. package/lib/audio/array_audio_buffer.d.ts +31 -31
  95. package/lib/audio/array_audio_buffer_input_stream.d.ts +14 -14
  96. package/lib/audio/array_audio_buffer_random_access_stream.d.ts +9 -9
  97. package/lib/audio/audio_data_holder.d.ts +84 -84
  98. package/lib/audio/audio_display.d.ts +36 -36
  99. package/lib/audio/audio_player.d.ts +47 -47
  100. package/lib/audio/capture/capture.d.ts +67 -67
  101. package/lib/audio/context.d.ts +6 -6
  102. package/lib/audio/dsp/level_measure.d.ts +60 -60
  103. package/lib/audio/format.d.ts +11 -11
  104. package/lib/audio/impl/wavformat.d.ts +5 -5
  105. package/lib/audio/impl/wavreader.d.ts +16 -16
  106. package/lib/audio/impl/wavwriter.d.ts +13 -13
  107. package/lib/audio/inddb_audio_buffer.d.ts +68 -68
  108. package/lib/audio/io/stream.d.ts +28 -28
  109. package/lib/audio/net_audio_buffer.d.ts +57 -57
  110. package/lib/audio/persistor.d.ts +29 -29
  111. package/lib/audio/playback/array_audio_buffer_source_node.d.ts +18 -18
  112. package/lib/audio/playback/audio_source_node.d.ts +10 -10
  113. package/lib/audio/playback/audio_source_worklet_module_loader.d.ts +4 -4
  114. package/lib/audio/playback/inddb_audio_buffer_source_node.d.ts +21 -21
  115. package/lib/audio/playback/net_audio_buffer_source_node.d.ts +27 -27
  116. package/lib/audio/playback/player.d.ts +65 -65
  117. package/lib/audio/ui/audio_canvas_layer_comp.d.ts +68 -68
  118. package/lib/audio/ui/audio_display_control.d.ts +25 -25
  119. package/lib/audio/ui/audio_display_scroll_pane.d.ts +26 -26
  120. package/lib/audio/ui/audiosignal.d.ts +30 -30
  121. package/lib/audio/ui/common.d.ts +11 -11
  122. package/lib/audio/ui/container.d.ts +63 -63
  123. package/lib/audio/ui/livelevel.d.ts +60 -60
  124. package/lib/audio/ui/scroll_pane_horizontal.d.ts +5 -5
  125. package/lib/audio/ui/sonagram.d.ts +37 -37
  126. package/lib/db/inddb.d.ts +21 -21
  127. package/lib/dsp/utils.d.ts +19 -19
  128. package/lib/io/BinaryReader.d.ts +18 -18
  129. package/lib/io/BinaryWriter.d.ts +15 -15
  130. package/lib/io/stream.d.ts +59 -59
  131. package/lib/math/2d/geometry.d.ts +18 -18
  132. package/lib/math/complex.d.ts +17 -17
  133. package/lib/math/dft.d.ts +22 -22
  134. package/lib/media/utils.d.ts +3 -3
  135. package/lib/net/uploader.d.ts +81 -81
  136. package/lib/recorder_component.d.ts +16 -16
  137. package/lib/speechrecorder/project/project.d.ts +59 -59
  138. package/lib/speechrecorder/project/project.service.d.ts +21 -21
  139. package/lib/speechrecorder/recording.d.ts +51 -51
  140. package/lib/speechrecorder/recordings/basic_recording.service.d.ts +27 -27
  141. package/lib/speechrecorder/recordings/recordings.service.d.ts +48 -48
  142. package/lib/speechrecorder/script/script.d.ts +80 -79
  143. package/lib/speechrecorder/script/script.service.d.ts +16 -16
  144. package/lib/speechrecorder/session/audiorecorder.d.ts +122 -122
  145. package/lib/speechrecorder/session/basicrecorder.d.ts +139 -139
  146. package/lib/speechrecorder/session/controlpanel.d.ts +109 -107
  147. package/lib/speechrecorder/session/item.d.ts +12 -12
  148. package/lib/speechrecorder/session/progress.d.ts +17 -17
  149. package/lib/speechrecorder/session/prompting.d.ts +121 -121
  150. package/lib/speechrecorder/session/recorder_combi_pane.d.ts +32 -32
  151. package/lib/speechrecorder/session/recording_file_cache.d.ts +33 -33
  152. package/lib/speechrecorder/session/recording_list.d.ts +24 -24
  153. package/lib/speechrecorder/session/recordingfile/recording-file-meta.component.d.ts +15 -15
  154. package/lib/speechrecorder/session/recordingfile/recording-file-navi.component.d.ts +20 -20
  155. package/lib/speechrecorder/session/recordingfile/recording-file-u-i.component.d.ts +31 -31
  156. package/lib/speechrecorder/session/recordingfile/recording-file-view.component.d.ts +55 -55
  157. package/lib/speechrecorder/session/recordingfile/recording-file.d.ts +7 -7
  158. package/lib/speechrecorder/session/recordingfile/recordingfile-service.d.ts +24 -24
  159. package/lib/speechrecorder/session/session.d.ts +15 -15
  160. package/lib/speechrecorder/session/session.service.d.ts +20 -20
  161. package/lib/speechrecorder/session/session_finished_dialog.d.ts +10 -10
  162. package/lib/speechrecorder/session/sessionmanager.d.ts +129 -123
  163. package/lib/speechrecorder/session/warning_bar.d.ts +8 -8
  164. package/lib/speechrecorder/spruploader.d.ts +11 -11
  165. package/lib/speechrecorder/startstopsignal/startstopsignal.d.ts +10 -10
  166. package/lib/speechrecorder/startstopsignal/ui/simpletrafficlight.d.ts +11 -11
  167. package/lib/speechrecorderng.component.d.ts +61 -61
  168. package/lib/speechrecorderng.module.d.ts +56 -56
  169. package/lib/spr.config.d.ts +17 -17
  170. package/lib/spr.module.version.d.ts +1 -1
  171. package/lib/ui/canvas_layer_comp.d.ts +13 -13
  172. package/lib/ui/message_dialog.d.ts +10 -10
  173. package/lib/ui/recordingitem_display.d.ts +84 -84
  174. package/lib/ui/responsive_component.d.ts +9 -9
  175. package/lib/utils/scrollIntoViewToBottom.d.ts +9 -9
  176. package/lib/utils/ua-parser.d.ts +43 -43
  177. package/lib/utils/utils.d.ts +19 -19
  178. package/lib/utils/wake_lock.d.ts +23 -23
  179. package/lib/utils/wake_lock_media.d.ts +1 -1
  180. package/package.json +17 -23
  181. package/public-api.d.ts +33 -33
  182. package/esm2020/lib/action/action.mjs +0 -73
  183. package/esm2020/lib/audio/audio_data_holder.mjs +0 -264
  184. package/esm2020/lib/audio/capture/capture.mjs +0 -855
  185. package/esm2020/lib/audio/context.mjs +0 -79
  186. package/esm2020/lib/audio/impl/wavformat.mjs +0 -6
  187. package/esm2020/lib/audio/impl/wavwriter.mjs +0 -105
  188. package/esm2020/lib/audio/inddb_audio_buffer.mjs +0 -508
  189. package/esm2020/lib/audio/net_audio_buffer.mjs +0 -293
  190. package/esm2020/lib/audio/playback/audio_source_node.mjs +0 -18
  191. package/esm2020/lib/audio/playback/audio_source_worklet_module_loader.mjs +0 -167
  192. package/esm2020/lib/audio/playback/player.mjs +0 -402
  193. package/esm2020/lib/audio/ui/audio_canvas_layer_comp.mjs +0 -347
  194. package/esm2020/lib/audio/ui/container.mjs +0 -414
  195. package/esm2020/lib/audio/ui/scroll_pane_horizontal.mjs +0 -11
  196. package/esm2020/lib/audio/ui/sonagram.mjs +0 -900
  197. package/esm2020/lib/db/inddb.mjs +0 -120
  198. package/esm2020/lib/dsp/utils.mjs +0 -48
  199. package/esm2020/lib/io/BinaryWriter.mjs +0 -74
  200. package/esm2020/lib/math/complex.mjs +0 -58
  201. package/esm2020/lib/net/uploader.mjs +0 -367
  202. package/esm2020/lib/speechrecorder/project/project.mjs +0 -49
  203. package/esm2020/lib/speechrecorder/project/project.service.mjs +0 -64
  204. package/esm2020/lib/speechrecorder/recordings/basic_recording.service.mjs +0 -216
  205. package/esm2020/lib/speechrecorder/recordings/recordings.service.mjs +0 -1014
  206. package/esm2020/lib/speechrecorder/session/audiorecorder.mjs +0 -1179
  207. package/esm2020/lib/speechrecorder/session/basicrecorder.mjs +0 -666
  208. package/esm2020/lib/speechrecorder/session/controlpanel.mjs +0 -409
  209. package/esm2020/lib/speechrecorder/session/recording_file_cache.mjs +0 -195
  210. package/esm2020/lib/speechrecorder/session/recordingfile/recordingfile-service.mjs +0 -288
  211. package/esm2020/lib/speechrecorder/session/session.service.mjs +0 -69
  212. package/esm2020/lib/speechrecorder/session/sessionmanager.mjs +0 -1333
  213. package/esm2020/lib/speechrecorder/startstopsignal/ui/simpletrafficlight.mjs +0 -57
  214. package/esm2020/lib/ui/recordingitem_display.mjs +0 -253
  215. package/esm2020/lib/utils/ua-parser.mjs +0 -190
  216. package/esm2020/lib/utils/utils.mjs +0 -105
  217. package/esm2020/lib/utils/wake_lock.mjs +0 -114
  218. package/fesm2015/speechrecorderng.mjs +0 -17708
  219. package/fesm2015/speechrecorderng.mjs.map +0 -1
  220. package/fesm2020/speechrecorderng.mjs.map +0 -1
@@ -0,0 +1,855 @@
1
+ import { Browser, Platform, UserAgentBuilder } from "../../utils/ua-parser";
2
+ import { AudioStorageType, Platform as CfgPlatform } from "../../speechrecorder/project/project";
3
+ import { ArrayAudioBuffer } from "../array_audio_buffer";
4
+ import { UUID } from "../../utils/utils";
5
+ import { IndexedDbAudioBuffer } from "../inddb_audio_buffer";
6
+ import { AudioContextProvider } from "../context";
7
+ export const CHROME_ACTIVATE_ECHO_CANCELLATION_WITH_AGC = false;
8
+ const DEBUG_TRACE_LEVEL = 0;
9
+ // Dirty way to load this module
10
+ // Copy content of interceptor_worklet.js to this string
11
+ const awpStr = "class AudioCaptureInterceptorProcessor extends AudioWorkletProcessor{\n" +
12
+ "\n" +
13
+ " BUFFER_QUANTUMS=64;\n" +
14
+ " QUANTUM_FRAME_LEN=128;\n" +
15
+ " BUFFER_FRAME_LEN=this.QUANTUM_FRAME_LEN*this.BUFFER_QUANTUMS;\n" +
16
+ " buffer=null;\n" +
17
+ " bufferPos=0;\n" +
18
+ " bufferPosBytes=0;\n" +
19
+ " constructor() {\n" +
20
+ " super();\n" +
21
+ "\n" +
22
+ " }\n" +
23
+ "\n" +
24
+ " process(\n" +
25
+ " inputs,\n" +
26
+ " outputs,\n" +
27
+ " parameters\n" +
28
+ " ){\n" +
29
+ "\n" +
30
+ " let inputsCnt=inputs.length;\n" +
31
+ " let channelCount=0;\n" +
32
+ " let inputLen=0;\n" +
33
+ " let inputLenBytes=0;\n" +
34
+ " if(inputsCnt>0) {\n" +
35
+ " let input0 = inputs[0];\n" +
36
+ " channelCount = input0.length;\n" +
37
+ " if (channelCount > 0) {\n" +
38
+ " let input0ch0=input0[0];\n" +
39
+ " inputLen=input0ch0.length;\n" +
40
+ " inputLenBytes=input0ch0.buffer.length;\n" +
41
+ " }\n" +
42
+ " }\n" +
43
+ " if (!this.buffer || this.buffer.length < channelCount) {\n" +
44
+ " this.buffer = new Array(channelCount);\n" +
45
+ " this.bufferPos = 0\n" +
46
+ " for (let bch = 0; bch < channelCount; bch++) {\n" +
47
+ " this.buffer[bch] = new Float32Array(this.BUFFER_FRAME_LEN);\n" +
48
+ " this.bufferPos = 0;\n" +
49
+ " this.bufferPosBytes=0;\n" +
50
+ " }\n" +
51
+ " }\n" +
52
+ " let bufAvail = this.BUFFER_FRAME_LEN - this.bufferPos;\n" +
53
+ " // check if buffer has to be transferred\n" +
54
+ " if (inputLen > bufAvail) {\n" +
55
+ " let ada=new Array(channelCount);\n" +
56
+ " for (let ch = 0; ch < channelCount; ch++) {\n" +
57
+ " ada[ch]=this.buffer[ch].buffer.slice(0);\n" +
58
+ " }\n" +
59
+ " this.port.postMessage({\n" +
60
+ " data: ada,\n" +
61
+ " chs: channelCount,\n" +
62
+ " len: this.bufferPos\n" +
63
+ " }, ada);\n" +
64
+ " // buffer transferred, reset\n" +
65
+ " this.bufferPos = 0;\n" +
66
+ " this.bufferPosBytes=0;\n" +
67
+ " }\n" +
68
+ "\n" +
69
+ " for(let ii=0;ii<inputsCnt;ii++) {\n" +
70
+ " for (let ch = 0; ch < channelCount; ch++) {\n" +
71
+ " // Mute outputs\n" +
72
+ " //outputs[ii][ch].fill(0);\n" +
73
+ " let chSamples = inputs[ii][ch];\n" +
74
+ " this.buffer[ch].set(chSamples,this.bufferPos);\n" +
75
+ " }\n" +
76
+ " this.bufferPos+=inputLen;\n" +
77
+ " this.bufferPosBytes+=inputLenBytes;\n" +
78
+ " }\n" +
79
+ " \n" +
80
+ " return true;\n" +
81
+ " }\n" +
82
+ "}\n" +
83
+ "\n" +
84
+ "registerProcessor('capture-interceptor',AudioCaptureInterceptorProcessor);\n";
85
+ export class AudioCapture {
86
+ get maxAutoNetMemStoreSamples() {
87
+ return this._maxAutoNetMemStoreSamples;
88
+ }
89
+ set maxAutoNetMemStoreSamples(value) {
90
+ this._maxAutoNetMemStoreSamples = value;
91
+ }
92
+ set recUUID(value) {
93
+ this._recUUID = value;
94
+ }
95
+ get recUUID() {
96
+ return this._recUUID;
97
+ }
98
+ get audioStorageType() {
99
+ return this._audioStorageType;
100
+ }
101
+ set audioStorageType(value) {
102
+ this._audioStorageType = value;
103
+ }
104
+ get persistentAudioStorageTarget() {
105
+ return this._persistentAudioStorageTarget;
106
+ }
107
+ set persistentAudioStorageTarget(value) {
108
+ this._persistentAudioStorageTarget = value;
109
+ }
110
+ get opened() {
111
+ return this._opened;
112
+ }
113
+ static { this.BUFFER_SIZE = 8192; }
114
+ static { this.DEFAULT_MAX_NET_AUTO_MEM_STORE_SAMPLES = 2880000 * 5; } // Default 5 minute at 48kHz
115
+ static { this.captureInterceptorModuleRegistered = false; }
116
+ //private context:AudioContext|null=null;
117
+ constructor() {
118
+ this._maxAutoNetMemStoreSamples = AudioCapture.DEFAULT_MAX_NET_AUTO_MEM_STORE_SAMPLES;
119
+ this.context = null;
120
+ this._recUUID = null;
121
+ this.agcStatus = null;
122
+ this.bufferingNode = null;
123
+ this.data = null;
124
+ this.audioOutStream = null;
125
+ this.disconnectStreams = true;
126
+ this._opened = false;
127
+ this.capturing = false;
128
+ this.framesRecorded = 0;
129
+ this._audioStorageType = AudioStorageType.MEM_ENTIRE;
130
+ this._persistentAudioStorageTarget = null;
131
+ this.persisted = true;
132
+ this.persistError = null;
133
+ this.inddbAudioBuffer = null;
134
+ this.n = navigator;
135
+ }
136
+ _audioContext() {
137
+ if (!this.context) {
138
+ this.context = AudioContextProvider.audioContextInstance();
139
+ if (this.context) {
140
+ this.context.addEventListener('statechange', () => {
141
+ if (this.context && this.context.state !== 'running') {
142
+ this.close();
143
+ }
144
+ });
145
+ }
146
+ }
147
+ return this.context;
148
+ }
149
+ initData() {
150
+ if (!this._recUUID) {
151
+ this._recUUID = UUID.generate();
152
+ }
153
+ this.persistError = null;
154
+ //console.debug("Audio capture initialize storage for type: "+this._audioStorageType);
155
+ if (AudioStorageType.DB_CHUNKED === this._audioStorageType && this._persistentAudioStorageTarget && this._recUUID) {
156
+ //console.debug("Create indexed db audio buffer.");
157
+ this.inddbAudioBuffer = new IndexedDbAudioBuffer(this._persistentAudioStorageTarget, this.channelCount, this.currentSampleRate, AudioCapture.BUFFER_SIZE, 0, this._recUUID);
158
+ }
159
+ if (!(AudioStorageType.NET_CHUNKED === this._audioStorageType)) {
160
+ // Initialize audio data array except for net audio buffer mode
161
+ this.data = new Array();
162
+ for (let i = 0; i < this.channelCount; i++) {
163
+ this.data.push(new Array());
164
+ }
165
+ }
166
+ this.framesRecorded = 0;
167
+ }
168
+ listDevices() {
169
+ navigator.mediaDevices.enumerateDevices().then((l) => this.printDevices(l));
170
+ }
171
+ dummySession() {
172
+ // workaround to request permissions:
173
+ // Start a dummy session
174
+ let mediaStrCnstrs = { audio: { echoCancelation: false }
175
+ };
176
+ return navigator.mediaDevices.getUserMedia(mediaStrCnstrs);
177
+ }
178
+ stopAllSessionTracks(mediaStream) {
179
+ let ats = mediaStream.getTracks();
180
+ for (let atIdx = 0; atIdx < ats.length; atIdx++) {
181
+ //console.debug("Stop dummy session track: #" + atIdx)
182
+ ats[atIdx].stop();
183
+ }
184
+ }
185
+ deviceInfos(cb, retry = true, dummyStream) {
186
+ navigator.mediaDevices.enumerateDevices().then((l) => {
187
+ let labelsAvailable = false;
188
+ for (let i = 0; i < l.length; i++) {
189
+ let di = l[i];
190
+ if (di.label) {
191
+ labelsAvailable = true;
192
+ }
193
+ }
194
+ if (!labelsAvailable) {
195
+ //console.debug("Media device enumeration: No labels.")
196
+ if (retry) {
197
+ console.info("Starting dummy session to request audio permissions...");
198
+ this.dummySession().then((s) => {
199
+ // and stop it immediately
200
+ if (s) {
201
+ //console.debug("Got dummy session stream: " + s + " .")
202
+ }
203
+ else {
204
+ //console.debug("No dummy stream")
205
+ }
206
+ // retry (only once)
207
+ this.deviceInfos(cb, false, s);
208
+ }, reason => {
209
+ //console.debug("Dummy session rejected.")
210
+ // TODO error callback
211
+ cb(null);
212
+ });
213
+ }
214
+ else {
215
+ cb(null);
216
+ }
217
+ }
218
+ else {
219
+ // success
220
+ cb(l);
221
+ }
222
+ if (dummyStream) {
223
+ this.stopAllSessionTracks(dummyStream);
224
+ }
225
+ }, (reason) => {
226
+ //rejected
227
+ //console.debug("Media device enumeration rejected.")
228
+ if (retry) {
229
+ //console.debug("Starting dummy session to request audio permissions...")
230
+ this.dummySession().then((s) => {
231
+ // and stop it immediately
232
+ //console.debug("Dummy session.")
233
+ if (s) {
234
+ //console.debug("Got dummy session stream: " + s + " .")
235
+ }
236
+ else {
237
+ //console.debug("No dummy stream")
238
+ }
239
+ // retry (only once)
240
+ this.deviceInfos(cb, false, s);
241
+ }, reason => {
242
+ //console.debug("Dummy session rejected.")
243
+ // TODO error callback
244
+ cb(null);
245
+ });
246
+ }
247
+ else {
248
+ cb(null);
249
+ }
250
+ if (dummyStream) {
251
+ this.stopAllSessionTracks(dummyStream);
252
+ }
253
+ });
254
+ }
255
+ printDevices(l) {
256
+ //let selDeviceId = '___dummy___';
257
+ for (let i = 0; i < l.length; i++) {
258
+ let di = l[i];
259
+ console.log("Audio device: Id: " + di.deviceId + " groupId: " + di.groupId + " label: " + di.label + " kind: " + di.kind);
260
+ }
261
+ }
262
+ addCaptureInterceptor() {
263
+ if (this.context) {
264
+ const awn = new AudioWorkletNode(this.context, 'capture-interceptor');
265
+ awn.onprocessorerror = (ev) => {
266
+ let msg = 'Unknwon error';
267
+ if (ev instanceof ErrorEvent) {
268
+ msg = ev.message;
269
+ }
270
+ console.error("Capture audio worklet error: " + msg);
271
+ if (this.listener) {
272
+ this.listener.error(msg);
273
+ }
274
+ };
275
+ let awnPt = awn.port;
276
+ if (awnPt) {
277
+ awnPt.onmessage = (ev) => {
278
+ if (this.capturing) {
279
+ let dt = ev.data;
280
+ let chs = dt.chs;
281
+ let adaLen = dt.data.length;
282
+ if (DEBUG_TRACE_LEVEL > 8) {
283
+ console.debug('Received data from worklet: ' + chs + ' ' + dt.len + ' Data chs: ' + adaLen);
284
+ }
285
+ let chunk = new Array(chs);
286
+ const samples = this.framesRecorded * chs;
287
+ if ((AudioStorageType.MEM_ENTIRE_AUTO_NET_CHUNKED === this.audioStorageType || AudioStorageType.MEM_CHUNKED_AUTO_NET_CHUNKED === this.audioStorageType) && this.data && samples > this._maxAutoNetMemStoreSamples) {
288
+ this.data = null;
289
+ }
290
+ //console.debug("Data initialized: "+(this.data!=null));
291
+ for (let ch = 0; ch < chs; ch++) {
292
+ //console.debug("Data ch initialized: "+(this.data !=null && this.data[ch] !=null));
293
+ if (ch < this.channelCount) {
294
+ if (dt.data[ch]) {
295
+ let fa = new Float32Array(dt.data[ch]);
296
+ if (this.data && this.data[ch]) {
297
+ this.data[ch].push(fa);
298
+ }
299
+ chunk[ch] = fa;
300
+ // Use samples of channel 0 to count frames (samples)
301
+ if (ch == 0) {
302
+ this.framesRecorded += fa.length;
303
+ }
304
+ }
305
+ }
306
+ }
307
+ if (this.audioOutStream) {
308
+ try {
309
+ this.audioOutStream.write(chunk);
310
+ // // Random test error:
311
+ // if(Math.random()>0.98) {
312
+ // throw new Error('Test');
313
+ // }
314
+ }
315
+ catch (err) {
316
+ if (err instanceof Error) {
317
+ this.persistError = err;
318
+ }
319
+ else {
320
+ this.persistError = new Error('Error handling recorded audio data');
321
+ }
322
+ console.error("Capture error: " + err);
323
+ try {
324
+ this.stop();
325
+ }
326
+ catch (err2) {
327
+ console.error("Capture next error (ignored): " + err2);
328
+ }
329
+ finally {
330
+ if (this.listener) {
331
+ let errExpl = '';
332
+ if (err instanceof DOMException) {
333
+ errExpl = ': ' + err.name + ': ' + err.message;
334
+ }
335
+ this.listener.error("Could not handle recorded audio data" + errExpl, "Please try to record again.");
336
+ }
337
+ else {
338
+ this.close();
339
+ }
340
+ }
341
+ }
342
+ }
343
+ if (AudioStorageType.DB_CHUNKED === this._audioStorageType && this._persistentAudioStorageTarget) {
344
+ this.store();
345
+ }
346
+ }
347
+ };
348
+ }
349
+ // Tried to fix that Safari does not record the second channel
350
+ // Does not help
351
+ //awn.channelCount=this.channelCount;
352
+ //awn.channelCountMode='explicit';
353
+ //console.debug('Channel count explicitly set to '+this.channelCount);
354
+ this.bufferingNode = awn;
355
+ //this.bufferingNode.channelCount=this.channelCount;
356
+ this._opened = true;
357
+ if (this.listener) {
358
+ this.listener.opened();
359
+ }
360
+ }
361
+ }
362
+ open(channelCount, selDeviceId, autoGainControlConfigs, allowEchoCancellation) {
363
+ //console.debug("Capture open: ctx state: "+this.context.state);
364
+ this.context = this._audioContext();
365
+ if (!this.context) {
366
+ throw new Error("Could not get audio context!");
367
+ }
368
+ if (this.context.state === 'suspended') {
369
+ //console.debug("Capture open: Resume context");
370
+ this.context.resume().then(() => {
371
+ //console.debug("Capture open (ctx resumed): ctx state: "+this.context.state);
372
+ this._open(channelCount, selDeviceId, autoGainControlConfigs, allowEchoCancellation);
373
+ }).catch((err) => {
374
+ console.error(err.message);
375
+ throw err;
376
+ });
377
+ }
378
+ else if (this.context.state === 'closed') {
379
+ const msg = 'Error on start capture: The audio context is already closed.';
380
+ console.error(msg);
381
+ throw new Error(msg);
382
+ }
383
+ else {
384
+ this._open(channelCount, selDeviceId, autoGainControlConfigs, allowEchoCancellation);
385
+ }
386
+ }
387
+ _open(channelCount, selDeviceId, autoGainControlConfigs, allowEchoCancellation) {
388
+ this.channelCount = channelCount;
389
+ this.framesRecorded = 0;
390
+ this.context = this._audioContext();
391
+ if (!this.context) {
392
+ throw new Error("Could not get audio context!");
393
+ }
394
+ //var msc = new AudioStreamConstr();
395
+ // var msc={};
396
+ //msc.video = false;
397
+ //msc.audio = true;
398
+ // Chrome and Firefox stereo channels are identical !!
399
+ // And even worse: The data coming from the source is already preprocessed on FF and Chrome. It contains DSP artifacts!!
400
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=387737
401
+ // The workaround to set these constraints does _NOT_ help:
402
+ //var msc={audio: {echoCancellation: false,channelCount: 2, googAudioMirroring: false},video: false};
403
+ // Safari at least version 11: Support for media streams
404
+ // TODO test if input is unprocessed
405
+ let msc;
406
+ console.info('User agent: ' + navigator.userAgent);
407
+ let ua = UserAgentBuilder.userAgent();
408
+ // ua.components.forEach((c)=>{
409
+ // console.info("UA_Comp: "+c.toString());
410
+ // })
411
+ let agcCfg = null;
412
+ let autoGainControl = false;
413
+ let chromeEchoCancellation = false;
414
+ if (autoGainControlConfigs) {
415
+ for (let agcc of autoGainControlConfigs) {
416
+ if (agcc.platform === CfgPlatform.Android && ua.detectedPlatform === Platform.Android) {
417
+ agcCfg = agcc;
418
+ break;
419
+ }
420
+ if (agcc.platform === CfgPlatform.Windows && ua.detectedPlatform === Platform.Windows) {
421
+ agcCfg = agcc;
422
+ break;
423
+ }
424
+ }
425
+ if (agcCfg) {
426
+ // TODO use EXACT/IDEAL constraint
427
+ autoGainControl = agcCfg.value;
428
+ if (CHROME_ACTIVATE_ECHO_CANCELLATION_WITH_AGC) {
429
+ chromeEchoCancellation = agcCfg.value;
430
+ }
431
+ // TODO query real AGC status
432
+ this.agcStatus = agcCfg.value;
433
+ }
434
+ else {
435
+ this.agcStatus = false;
436
+ }
437
+ }
438
+ // default
439
+ msc = {
440
+ audio: {
441
+ deviceId: selDeviceId,
442
+ echoCancellation: false,
443
+ channelCount: channelCount,
444
+ autoGainControl: autoGainControl
445
+ },
446
+ video: false
447
+ };
448
+ if (ua.detectedBrowser === Browser.Edge) {
449
+ // Microsoft Edge sends unmodified audio
450
+ // The constraint can follow the specification
451
+ console.info("Setting media track constraints for Microsoft Edge.");
452
+ msc = {
453
+ audio: {
454
+ deviceId: selDeviceId,
455
+ echoCancellation: false,
456
+ channelCount: channelCount,
457
+ autoGainControl: autoGainControl
458
+ },
459
+ video: false
460
+ };
461
+ }
462
+ else if (ua.detectedBrowser === Browser.Chrome) {
463
+ // Google Chrome: we need to switch of each of the preprocessing units including the
464
+ console.info("Setting media track constraints for Google Chrome.");
465
+ // Chrome 60 -> 61 changed
466
+ // it works now without mandatory/optional sub-objects
467
+ // Requires at least Chrome 61
468
+ msc = {
469
+ audio: {
470
+ deviceId: selDeviceId,
471
+ channelCount: channelCount,
472
+ echoCancellation: { exact: chromeEchoCancellation },
473
+ autoGainControl: { exact: autoGainControl },
474
+ sampleSize: { min: 16 },
475
+ },
476
+ video: false,
477
+ };
478
+ }
479
+ else if (ua.detectedBrowser === Browser.Firefox) {
480
+ console.info("Setting media track constraints for Mozilla Firefox.");
481
+ // Firefox
482
+ msc = {
483
+ audio: {
484
+ deviceId: selDeviceId,
485
+ channelCount: channelCount,
486
+ echoCancellation: false,
487
+ autoGainControl: autoGainControl,
488
+ noiseSuppression: false
489
+ },
490
+ video: false,
491
+ };
492
+ }
493
+ else if (ua.detectedBrowser === Browser.Safari) {
494
+ console.info("Setting media track constraints for Safari browser.");
495
+ //console.info("Apply workaround for Safari: Avoid disconnect of streams.");
496
+ this.disconnectStreams = true;
497
+ msc = {
498
+ audio: {
499
+ deviceId: selDeviceId,
500
+ channelCount: channelCount,
501
+ echoCancellation: allowEchoCancellation ? undefined : false
502
+ },
503
+ video: false,
504
+ };
505
+ }
506
+ else {
507
+ // TODO default constraints or error Browser not supported
508
+ }
509
+ console.debug("Audio capture, AGC: " + this.agcStatus);
510
+ let ump = navigator.mediaDevices.getUserMedia(msc);
511
+ ump.then((s) => {
512
+ if (this.context) {
513
+ this.stream = s;
514
+ let aTracks = s.getAudioTracks();
515
+ for (let i = 0; i < aTracks.length; i++) {
516
+ let aTrack = aTracks[i];
517
+ console.info("Track audio info: id: " + aTrack.id + " kind: " + aTrack.kind + " label: \"" + aTrack.label + "\"");
518
+ let mtrSts = aTrack.getSettings();
519
+ // Typescript lib.dom.ts MediaTrackSettings.channelCount is missing
520
+ // https://github.com/mdn/browser-compat-data/blob/5493d8f937e05b2ddbd41b99f5bdfad4a1f2ed85/api/MediaTrackSettings.json
521
+ //@ts-ignore
522
+ console.info("Track audio settings: Ch cnt: " + mtrSts.channelCount + ", AGC: " + mtrSts.autoGainControl + ", Echo cancell.: " + mtrSts.echoCancellation);
523
+ if (mtrSts.autoGainControl) {
524
+ this.agcStatus = mtrSts.autoGainControl;
525
+ }
526
+ console.debug("Echo cancellation: " + mtrSts.echoCancellation);
527
+ }
528
+ let vTracks = s.getVideoTracks();
529
+ for (let i = 0; i < vTracks.length; i++) {
530
+ let vTrack = vTracks[i];
531
+ console.info("Track video info: id: " + vTrack.id + " kind: " + vTrack.kind + " label: " + vTrack.label);
532
+ }
533
+ this.mediaStream = this.context.createMediaStreamSource(s);
534
+ // stream channel count ( is always 2 !)
535
+ let streamChannelCount = this.mediaStream.channelCount;
536
+ console.info("Stream channel count: " + streamChannelCount);
537
+ // is not set!!
538
+ //this.currentSampleRate = this.mediaStream.sampleRate;
539
+ this.currentSampleRate = this.context.sampleRate;
540
+ console.info("Source audio node: channels: " + streamChannelCount + " samplerate: " + this.currentSampleRate);
541
+ if (this.audioOutStream) {
542
+ this.audioOutStream.setFormat(this.channelCount, this.currentSampleRate);
543
+ }
544
+ // W3C -> new name is createScriptProcessor
545
+ //
546
+ // Again deprecated, but AudioWorker not yet implemented in stable releases (June 2016)
547
+ // AudioWorker is now AudioWorkletProcessor ... (May 2017)
548
+ // Update 12-2020:
549
+ // The ScriptProcessorNode Interface - DEPRECATED
550
+ // Update 06-2021
551
+ // AudioWorkletProcessor is here to stay. Web Audio API has now Recommendation status !
552
+ if (this.context.audioWorklet) {
553
+ //const workletFileName = ('file-loader!./interceptor_worklet.js');
554
+ //const workletFileName = 'http://localhost:4200/assets/interceptor_worklet.js';
555
+ //console.log(awpStr);
556
+ if (AudioCapture.captureInterceptorModuleRegistered) {
557
+ // Required capture interceptor module already registered
558
+ this.addCaptureInterceptor();
559
+ }
560
+ else {
561
+ // Register capture interceptor module
562
+ let audioWorkletModuleBlob = new Blob([awpStr], { type: 'text/javascript' });
563
+ let audioWorkletModuleBlobUrl = window.URL.createObjectURL(audioWorkletModuleBlob);
564
+ this.context.audioWorklet.addModule(audioWorkletModuleBlobUrl).then(() => {
565
+ AudioCapture.captureInterceptorModuleRegistered = true;
566
+ this.addCaptureInterceptor();
567
+ }).catch((error) => {
568
+ console.log('Could not add module ' + error);
569
+ });
570
+ }
571
+ }
572
+ else if (this.context.createScriptProcessor) {
573
+ // The ScriptProcessorNode Interface - DEPRECATED Only as fallback
574
+ // TODO should we use streamChannelCount or channelCount here ?
575
+ let scriptProcessorNode = this.context.createScriptProcessor(AudioCapture.BUFFER_SIZE, streamChannelCount, streamChannelCount);
576
+ this.bufferingNode = scriptProcessorNode;
577
+ let c = 0;
578
+ if (scriptProcessorNode.onaudioprocess) {
579
+ scriptProcessorNode.onaudioprocess = (e) => {
580
+ if (this.capturing) {
581
+ let inBuffer = e.inputBuffer;
582
+ // only process requested count of channels
583
+ let currentBuffers = new Array(channelCount);
584
+ for (let ch = 0; ch < channelCount; ch++) {
585
+ let chSamples = inBuffer.getChannelData(ch);
586
+ let chSamplesCopy = chSamples.slice(0);
587
+ currentBuffers[ch] = chSamplesCopy.slice(0);
588
+ if (this.data) {
589
+ this.data[ch].push(chSamplesCopy);
590
+ }
591
+ if (DEBUG_TRACE_LEVEL > 8) {
592
+ console.debug("Process " + chSamplesCopy.length + " samples.");
593
+ }
594
+ this.framesRecorded += chSamplesCopy.length;
595
+ }
596
+ c++;
597
+ if (this.audioOutStream) {
598
+ this.audioOutStream.write(currentBuffers);
599
+ }
600
+ }
601
+ };
602
+ this._opened = true;
603
+ if (this.listener) {
604
+ this.listener.opened();
605
+ }
606
+ }
607
+ else {
608
+ this.listener.error('Browser does not support audio processing (ScriptProcessor.onaudioprocess method not found)!');
609
+ }
610
+ }
611
+ else {
612
+ this.listener.error('Browser does not support audio processing (neither AudioWorkletProcessor nor ScriptProcessor)!');
613
+ }
614
+ }
615
+ }, (e) => {
616
+ console.error(e + " Error name: " + e.name);
617
+ if (this.listener) {
618
+ if ('NotAllowedError' === e.name) {
619
+ this.listener.error('Not allowed to use your microphone.', 'Please make sure that microphone access is allowed for this web page and reload the page.');
620
+ }
621
+ else if ('NotReadableError' === e.name) {
622
+ this.listener.error('Could not read from your audio device.', 'Please make sure your audio device is working.');
623
+ }
624
+ else if ('OverconstrainedError' === e.name) {
625
+ let eMsg = e.msg ? e.msg : 'Overconstrained media device request error.';
626
+ this.listener.error(eMsg);
627
+ }
628
+ else {
629
+ this.listener.error();
630
+ }
631
+ }
632
+ });
633
+ }
634
+ _start() {
635
+ if (this.context) {
636
+ this.initData();
637
+ if (this.audioOutStream) {
638
+ this.audioOutStream.nextStream();
639
+ }
640
+ this.capturing = true;
641
+ if (this.bufferingNode) {
642
+ this.mediaStream.connect(this.bufferingNode);
643
+ this.bufferingNode.connect(this.context.destination);
644
+ }
645
+ if (this.listener) {
646
+ this.listener.started();
647
+ }
648
+ }
649
+ }
650
+ start() {
651
+ if (this.context) {
652
+ const aSt = this.context.state;
653
+ if (aSt === 'running') {
654
+ this._start();
655
+ }
656
+ else {
657
+ console.debug("Capture start: audio context not running, state: " + aSt + ", resuming...");
658
+ this.context.resume().then(() => {
659
+ console.debug("Capture start: audio context resumed, starting...");
660
+ this._start();
661
+ });
662
+ }
663
+ }
664
+ }
665
+ stop() {
666
+ if (this.disconnectStreams && this.bufferingNode) {
667
+ this.mediaStream.disconnect(this.bufferingNode);
668
+ if (this.context) {
669
+ this.bufferingNode.disconnect(this.context.destination);
670
+ }
671
+ }
672
+ try {
673
+ if (this.audioOutStream) {
674
+ this.audioOutStream.flush();
675
+ }
676
+ }
677
+ catch (err) {
678
+ console.error("Could not flush capture stream.");
679
+ throw err;
680
+ }
681
+ finally {
682
+ this.capturing = false;
683
+ if (this.inddbAudioBuffer && this.persistError) {
684
+ // Delete invalid persistent audio data and hope that it will be completely uploaded to the server
685
+ this.inddbAudioBuffer.releaseAudioData();
686
+ this.inddbAudioBuffer = null;
687
+ }
688
+ if (this.listener && (this.persistError || this.persisted)) {
689
+ //console.debug("Stopped by stop() method call");
690
+ this.listener.stopped();
691
+ }
692
+ }
693
+ }
694
+ store() {
695
+ // if(this.db && this.recUUID){
696
+ // let tr= this.db.transaction(SprDb.RECORDING_FILE_CHUNKS_OBJECT_STORE_NAME, 'readwrite');
697
+ // let recFileObjStore = tr.objectStore(SprDb.RECORDING_FILE_CHUNKS_OBJECT_STORE_NAME);
698
+ //
699
+ // try {
700
+ // let ch0Data=this.data[0];
701
+ // let dataChkCnt=ch0Data.length;
702
+ // let pos = 0;
703
+ // for (let chCkIdx = 0; chCkIdx < dataChkCnt; chCkIdx++) {
704
+ // let bufLen=0;
705
+ // for (let ch = 0; ch < this.channelCount; ch++) {
706
+ // let chChk = this.data[ch][chCkIdx];
707
+ // bufLen = chChk.length;
708
+ // //let cacheId = uuid + '_' + ch + '_' + chCkIdx;
709
+ // let chkDbId = [this.recUUID, this.indDbChkIdx + chCkIdx, ch];
710
+ // let cr = recFileObjStore.add(chChk, chkDbId);
711
+ // //console.debug("Added: "+ch+" "+(this.indDbChkIdx+chCkIdx));
712
+ // cr.onsuccess=()=>{
713
+ // //console.debug("Stored audio data to indexed db");
714
+ // }
715
+ // cr.onerror=()=>{
716
+ // console.error("Error storing audio data to indexed db");
717
+ // }
718
+ // }
719
+ // pos += bufLen;
720
+ // }
721
+ // this.indDbChkIdx+=dataChkCnt;
722
+ //
723
+ // tr.onerror = (err) => {
724
+ // console.error('Failed to cache audio data to indexed db: ' + err)
725
+ // }
726
+ // tr.oncomplete = () => {
727
+ // //console.debug('Transferred capture audio data to indexed db, deleting original data from memory...');
728
+ //
729
+ // /// Audio data saved to index db delete from in memory data array
730
+ // for (let ch = 0; ch < this.channelCount; ch++) {
731
+ // this.data[ch].splice(0);
732
+ // //console.debug("Spliced/removed ch: "+ch);
733
+ // }
734
+ //
735
+ // this.persisted=true;
736
+ // if(this.listener && !this.capturing){
737
+ // //console.debug("Stopped by indexed db hook");
738
+ // this.listener.stopped();
739
+ // }
740
+ // }
741
+ // // Commit chunks
742
+ // this.persisted=false;
743
+ // tr.commit();
744
+ // } catch (err) {
745
+ // console.error('Transfer capture audio data error: '+err);
746
+ // }
747
+ // }
748
+ // if(!this.inddbAudioBuffer && this._persistentAudioStorageTarget && this.recUUID) {
749
+ // this.inddbAudioBuffer = new IndexedDbAudioBuffer(this._persistentAudioStorageTarget, this.channelCount,this.currentSampleRate,AudioCapture.BUFFER_SIZE,0,this.recUUID)
750
+ // }
751
+ if (this.inddbAudioBuffer && this.data) {
752
+ // Try to append to
753
+ this.inddbAudioBuffer.appendRawAudioData(this.data).subscribe({
754
+ complete: () => {
755
+ //console.debug('Transferred capture audio data to indexed db, deleting original data from memory...');
756
+ /// Audio data saved to index db delete from in memory data array
757
+ if (this.data) {
758
+ for (let ch = 0; ch < this.channelCount; ch++) {
759
+ this.data[ch].splice(0);
760
+ //console.debug("Spliced/removed ch: "+ch);
761
+ }
762
+ }
763
+ this.persisted = true;
764
+ if (this.listener && !this.capturing) {
765
+ //console.debug("Stopped by indexed db hook");
766
+ this.listener.stopped();
767
+ }
768
+ }, error: (err) => {
769
+ // Only log the first error
770
+ if (!this.persistError) {
771
+ this.persistError = err;
772
+ console.error("Error persisting audio data: " + err);
773
+ }
774
+ }
775
+ });
776
+ this.persisted = false;
777
+ }
778
+ }
779
+ close() {
780
+ if (this.mediaStream) {
781
+ this.mediaStream.disconnect();
782
+ }
783
+ if (this.stream) {
784
+ const mts = this.stream.getTracks();
785
+ for (let i = 0; i < mts.length; i++) {
786
+ mts[i].stop();
787
+ }
788
+ }
789
+ this._opened = false;
790
+ }
791
+ audioBuffer() {
792
+ let ab = null;
793
+ if (this.context && this.data) {
794
+ let frameLen = 0;
795
+ let ch0Data = this.data[0];
796
+ for (let ch0Chk of ch0Data) {
797
+ frameLen += ch0Chk.length;
798
+ }
799
+ try {
800
+ ab = this.context.createBuffer(this.channelCount, frameLen, this.context.sampleRate);
801
+ }
802
+ catch (err) {
803
+ if (err instanceof DOMException) {
804
+ if (err.name === 'NotSupportedError') {
805
+ if (frameLen == 0) {
806
+ // Empty buffers are not supported by Chromium
807
+ // Create dummy buffer with one sample
808
+ ab = this.context.createBuffer(this.channelCount, 1, this.context.sampleRate);
809
+ }
810
+ else {
811
+ throw err;
812
+ }
813
+ }
814
+ else {
815
+ throw err;
816
+ }
817
+ }
818
+ else if (err instanceof RangeError) {
819
+ // Out of memory
820
+ // TODO What to do ??
821
+ throw err;
822
+ }
823
+ else {
824
+ throw err;
825
+ }
826
+ }
827
+ for (let ch = 0; ch < this.channelCount; ch++) {
828
+ let chD = ab.getChannelData(ch);
829
+ let pos = 0;
830
+ for (let chChk of this.data[ch]) {
831
+ let bufLen = chChk.length;
832
+ chD.set(chChk, pos);
833
+ pos += bufLen;
834
+ }
835
+ }
836
+ }
837
+ return ab;
838
+ }
839
+ audioBufferArray() {
840
+ let arrAb = null;
841
+ if (this.data) {
842
+ arrAb = new ArrayAudioBuffer(this.channelCount, this.currentSampleRate, this.data);
843
+ }
844
+ return arrAb;
845
+ }
846
+ inddbAudioBufferArray() {
847
+ if (this.persistError) {
848
+ return null;
849
+ }
850
+ else {
851
+ return this.inddbAudioBuffer;
852
+ }
853
+ }
854
+ }
855
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FwdHVyZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3NwZWVjaHJlY29yZGVybmcvc3JjL2xpYi9hdWRpby9jYXB0dXJlL2NhcHR1cmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFDLE9BQU8sRUFBRSxRQUFRLEVBQUUsZ0JBQWdCLEVBQUMsTUFBTSx1QkFBdUIsQ0FBQztBQUUxRSxPQUFPLEVBQUMsZ0JBQWdCLEVBQXlCLFFBQVEsSUFBSSxXQUFXLEVBQUMsTUFBTSxzQ0FBc0MsQ0FBQztBQUN0SCxPQUFPLEVBQUMsZ0JBQWdCLEVBQUMsTUFBTSx1QkFBdUIsQ0FBQztBQUN2RCxPQUFPLEVBQUMsSUFBSSxFQUFDLE1BQU0sbUJBQW1CLENBQUM7QUFDdkMsT0FBTyxFQUFDLG9CQUFvQixFQUErQixNQUFNLHVCQUF1QixDQUFDO0FBQ3pGLE9BQU8sRUFBQyxvQkFBb0IsRUFBQyxNQUFNLFlBQVksQ0FBQztBQUdoRCxNQUFNLENBQUMsTUFBTSwwQ0FBMEMsR0FBQyxLQUFLLENBQUM7QUFFOUQsTUFBTSxpQkFBaUIsR0FBQyxDQUFDLENBQUM7QUFFMUIsZ0NBQWdDO0FBQ2hDLHdEQUF3RDtBQUN4RCxNQUFNLE1BQU0sR0FBQyx5RUFBeUU7SUFDbEYsSUFBSTtJQUNKLDJCQUEyQjtJQUMzQiw4QkFBOEI7SUFDOUIscUVBQXFFO0lBQ3JFLG9CQUFvQjtJQUNwQixvQkFBb0I7SUFDcEIseUJBQXlCO0lBQ3pCLHVCQUF1QjtJQUN2QixvQkFBb0I7SUFDcEIsSUFBSTtJQUNKLFNBQVM7SUFDVCxJQUFJO0lBQ0osYUFBYTtJQUNiLGlCQUFpQjtJQUNqQixrQkFBa0I7SUFDbEIsb0JBQW9CO0lBQ3BCLFFBQVE7SUFDUixJQUFJO0lBQ0oscUNBQXFDO0lBQ3JDLDRCQUE0QjtJQUM1Qix3QkFBd0I7SUFDeEIsNkJBQTZCO0lBQzdCLDBCQUEwQjtJQUMxQixvQ0FBb0M7SUFDcEMsMENB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Y2VBdWRpb0Zsb2F0MzJPdXRTdHJlYW19IGZyb20gXCIuLi9pby9zdHJlYW1cIjtcclxuaW1wb3J0IHtCcm93c2VyLCBQbGF0Zm9ybSwgVXNlckFnZW50QnVpbGRlcn0gZnJvbSBcIi4uLy4uL3V0aWxzL3VhLXBhcnNlclwiO1xyXG5cclxuaW1wb3J0IHtBdWRpb1N0b3JhZ2VUeXBlLCBBdXRvR2FpbkNvbnRyb2xDb25maWcsIFBsYXRmb3JtIGFzIENmZ1BsYXRmb3JtfSBmcm9tIFwiLi4vLi4vc3BlZWNocmVjb3JkZXIvcHJvamVjdC9wcm9qZWN0XCI7XHJcbmltcG9ydCB7QXJyYXlBdWRpb0J1ZmZlcn0gZnJvbSBcIi4uL2FycmF5X2F1ZGlvX2J1ZmZlclwiO1xyXG5pbXBvcnQge1VVSUR9IGZyb20gXCIuLi8uLi91dGlscy91dGlsc1wiO1xyXG5pbXBvcnQge0luZGV4ZWREYkF1ZGlvQnVmZmVyLCBQZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0fSBmcm9tIFwiLi4vaW5kZGJfYXVkaW9fYnVmZmVyXCI7XHJcbmltcG9ydCB7QXVkaW9Db250ZXh0UHJvdmlkZXJ9IGZyb20gXCIuLi9jb250ZXh0XCI7XHJcblxyXG5cclxuZXhwb3J0IGNvbnN0IENIUk9NRV9BQ1RJVkFURV9FQ0hPX0NBTkNFTExBVElPTl9XSVRIX0FHQz1mYWxzZTtcclxuXHJcbmNvbnN0IERFQlVHX1RSQUNFX0xFVkVMPTA7XHJcblxyXG4vLyBEaXJ0eSB3YXkgdG8gbG9hZCB0aGlzIG1vZHVsZVxyXG4vLyBDb3B5IGNvbnRlbnQgb2YgaW50ZXJjZXB0b3Jfd29ya2xldC5qcyB0byB0aGlzIHN0cmluZ1xyXG5jb25zdCBhd3BTdHI9XCJjbGFzcyBBdWRpb0NhcHR1cmVJbnRlcmNlcHRvclByb2Nlc3NvciBleHRlbmRzIEF1ZGlvV29ya2xldFByb2Nlc3NvcntcXG5cIiArXHJcbiAgICBcIlxcblwiICtcclxuICAgIFwiICAgIEJVRkZFUl9RVUFOVFVNUz02NDtcXG5cIiArXHJcbiAgICBcIiAgICBRVUFOVFVNX0ZSQU1FX0xFTj0xMjg7XFxuXCIgK1xyXG4gICAgXCIgICAgQlVGRkVSX0ZSQU1FX0xFTj10aGlzLlFVQU5UVU1fRlJBTUVfTEVOKnRoaXMuQlVGRkVSX1FVQU5UVU1TO1xcblwiICtcclxuICAgIFwiICAgIGJ1ZmZlcj1udWxsO1xcblwiICtcclxuICAgIFwiICAgIGJ1ZmZlclBvcz0wO1xcblwiICtcclxuICAgIFwiICAgIGJ1ZmZlclBvc0J5dGVzPTA7XFxuXCIgK1xyXG4gICAgXCIgICAgY29uc3RydWN0b3IoKSB7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgIHN1cGVyKCk7XFxuXCIgK1xyXG4gICAgXCJcXG5cIiArXHJcbiAgICBcIiAgICB9XFxuXCIgK1xyXG4gICAgXCJcXG5cIiArXHJcbiAgICBcIiBwcm9jZXNzKFxcblwiICtcclxuICAgIFwiICAgICAgaW5wdXRzLFxcblwiICtcclxuICAgIFwiICAgICAgb3V0cHV0cyxcXG5cIiArXHJcbiAgICBcIiAgICAgIHBhcmFtZXRlcnNcXG5cIiArXHJcbiAgICBcIiAgKXtcXG5cIiArXHJcbiAgICBcIlxcblwiICtcclxuICAgIFwiICAgICBsZXQgaW5wdXRzQ250PWlucHV0cy5sZW5ndGg7XFxuXCIgK1xyXG4gICAgXCIgICAgIGxldCBjaGFubmVsQ291bnQ9MDtcXG5cIiArXHJcbiAgICBcIiAgICAgbGV0IGlucHV0TGVuPTA7XFxuXCIgK1xyXG4gICAgXCIgICAgIGxldCBpbnB1dExlbkJ5dGVzPTA7XFxuXCIgK1xyXG4gICAgXCIgICAgIGlmKGlucHV0c0NudD4wKSB7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICBsZXQgaW5wdXQwID0gaW5wdXRzWzBdO1xcblwiICtcclxuICAgIFwiICAgICAgICAgY2hhbm5lbENvdW50ID0gaW5wdXQwLmxlbmd0aDtcXG5cIiArXHJcbiAgICBcIiAgICAgICAgIGlmIChjaGFubmVsQ291bnQgPiAwKSB7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICAgICAgbGV0IGlucHV0MGNoMD1pbnB1dDBbMF07XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICAgICAgaW5wdXRMZW49aW5wdXQwY2gwLmxlbmd0aDtcXG5cIiArXHJcbiAgICBcIiAgICAgICAgICAgICBpbnB1dExlbkJ5dGVzPWlucHV0MGNoMC5idWZmZXIubGVuZ3RoO1xcblwiICtcclxuICAgIFwiICAgICAgICAgfVxcblwiICtcclxuICAgIFwiICAgICB9XFxuXCIgK1xyXG4gICAgXCIgICAgIGlmICghdGhpcy5idWZmZXIgfHwgdGhpcy5idWZmZXIubGVuZ3RoIDwgY2hhbm5lbENvdW50KSB7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICB0aGlzLmJ1ZmZlciA9IG5ldyBBcnJheShjaGFubmVsQ291bnQpO1xcblwiICtcclxuICAgIFwiICAgICAgICAgdGhpcy5idWZmZXJQb3MgPSAwXFxuXCIgK1xyXG4gICAgXCIgICAgICAgICBmb3IgKGxldCBiY2ggPSAwOyBiY2ggPCBjaGFubmVsQ291bnQ7IGJjaCsrKSB7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICAgICAgdGhpcy5idWZmZXJbYmNoXSA9IG5ldyBGbG9hdDMyQXJyYXkodGhpcy5CVUZGRVJfRlJBTUVfTEVOKTtcXG5cIiArXHJcbiAgICBcIiAgICAgICAgICAgICB0aGlzLmJ1ZmZlclBvcyA9IDA7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICAgICAgdGhpcy5idWZmZXJQb3NCeXRlcz0wO1xcblwiICtcclxuICAgIFwiICAgICAgICAgfVxcblwiICtcclxuICAgIFwiICAgICB9XFxuXCIgK1xyXG4gICAgXCIgICAgIGxldCBidWZBdmFpbCA9IHRoaXMuQlVGRkVSX0ZSQU1FX0xFTiAtIHRoaXMuYnVmZmVyUG9zO1xcblwiICtcclxuICAgIFwiICAgICAvLyBjaGVjayBpZiBidWZmZXIgaGFzIHRvIGJlIHRyYW5zZmVycmVkXFxuXCIgK1xyXG4gICAgXCIgICAgIGlmIChpbnB1dExlbiA+IGJ1ZkF2YWlsKSB7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICBsZXQgYWRhPW5ldyBBcnJheShjaGFubmVsQ291bnQpO1xcblwiICtcclxuICAgIFwiICAgICAgICAgZm9yIChsZXQgY2ggPSAwOyBjaCA8IGNoYW5uZWxDb3VudDsgY2grKykge1xcblwiICtcclxuICAgIFwiICAgICAgICAgICAgIGFkYVtjaF09dGhpcy5idWZmZXJbY2hdLmJ1ZmZlci5zbGljZSgwKTtcXG5cIiArXHJcbiAgICBcIiAgICAgICAgIH1cXG5cIiArXHJcbiAgICBcIiAgICAgICAgIHRoaXMucG9ydC5wb3N0TWVzc2FnZSh7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICAgICAgZGF0YTogYWRhLFxcblwiICtcclxuICAgIFwiICAgICAgICAgICAgIGNoczogY2hhbm5lbENvdW50LFxcblwiICtcclxuICAgIFwiICAgICAgICAgICAgIGxlbjogdGhpcy5idWZmZXJQb3NcXG5cIiArXHJcbiAgICBcIiAgICAgICAgIH0sIGFkYSk7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICAvLyBidWZmZXIgdHJhbnNmZXJyZWQsIHJlc2V0XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICB0aGlzLmJ1ZmZlclBvcyA9IDA7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICB0aGlzLmJ1ZmZlclBvc0J5dGVzPTA7XFxuXCIgK1xyXG4gICAgXCIgICAgIH1cXG5cIiArXHJcbiAgICBcIlxcblwiICtcclxuICAgIFwiICAgICBmb3IobGV0IGlpPTA7aWk8aW5wdXRzQ250O2lpKyspIHtcXG5cIiArXHJcbiAgICBcIiAgICAgICAgIGZvciAobGV0IGNoID0gMDsgY2ggPCBjaGFubmVsQ291bnQ7IGNoKyspIHtcXG5cIiArXHJcbiAgICBcIiAgICAgICAgICAgICAvLyBNdXRlIG91dHB1dHNcXG5cIiArXHJcbiAgICBcIiAgICAgICAgICAgICAvL291dHB1dHNbaWldW2NoXS5maWxsKDApO1xcblwiICtcclxuICAgIFwiICAgICAgICAgICAgIGxldCBjaFNhbXBsZXMgPSBpbnB1dHNbaWldW2NoXTtcXG5cIiArXHJcbiAgICBcIiAgICAgICAgICAgICB0aGlzLmJ1ZmZlcltjaF0uc2V0KGNoU2FtcGxlcyx0aGlzLmJ1ZmZlclBvcyk7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICB9XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICB0aGlzLmJ1ZmZlclBvcys9aW5wdXRMZW47XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICB0aGlzLmJ1ZmZlclBvc0J5dGVzKz1pbnB1dExlbkJ5dGVzO1xcblwiICtcclxuICAgIFwiICAgICB9XFxuXCIgK1xyXG4gICAgXCIgICAgXFxuXCIgK1xyXG4gICAgXCIgICAgIHJldHVybiB0cnVlO1xcblwiICtcclxuICAgIFwiICB9XFxuXCIgK1xyXG4gICAgXCJ9XFxuXCIgK1xyXG4gICAgXCJcXG5cIiArXHJcbiAgICBcInJlZ2lzdGVyUHJvY2Vzc29yKCdjYXB0dXJlLWludGVyY2VwdG9yJyxBdWRpb0NhcHR1cmVJbnRlcmNlcHRvclByb2Nlc3Nvcik7XFxuXCI7XHJcblxyXG5cclxuXHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIEF1ZGlvQ2FwdHVyZUxpc3RlbmVyIHtcclxuICBvcGVuZWQoKTogdm9pZDtcclxuXHJcbiAgc3RhcnRlZCgpOiB2b2lkO1xyXG5cclxuICBzdG9wcGVkKCk6IHZvaWQ7XHJcblxyXG4gIGNsb3NlZCgpOiB2b2lkO1xyXG5cclxuICBlcnJvcihtc2c/OnN0cmluZyxhZHZpY2U/OnN0cmluZyk6IHZvaWQ7XHJcbn1cclxuXHJcbmV4cG9ydCBjbGFzcyBBdWRpb0NhcHR1cmUge1xyXG5cclxuICBnZXQgbWF4QXV0b05ldE1lbVN0b3JlU2FtcGxlcygpOiBudW1iZXIge1xyXG4gICAgcmV0dXJuIHRoaXMuX21heEF1dG9OZXRNZW1TdG9yZVNhbXBsZXM7XHJcbiAgfVxyXG5cclxuICBzZXQgbWF4QXV0b05ldE1lbVN0b3JlU2FtcGxlcyh2YWx1ZTogbnVtYmVyKSB7XHJcbiAgICB0aGlzLl9tYXhBdXRvTmV0TWVtU3RvcmVTYW1wbGVzID0gdmFsdWU7XHJcbiAgfVxyXG4gIHNldCByZWNVVUlEKHZhbHVlOiBzdHJpbmd8bnVsbCkge1xyXG4gICAgdGhpcy5fcmVjVVVJRCA9IHZhbHVlO1xyXG4gIH1cclxuICBnZXQgcmVjVVVJRCgpOiBzdHJpbmcgfG51bGx7XHJcbiAgICByZXR1cm4gdGhpcy5fcmVjVVVJRDtcclxuICB9XHJcbiAgZ2V0IGF1ZGlvU3RvcmFnZVR5cGUoKTogQXVkaW9TdG9yYWdlVHlwZSB7XHJcbiAgICByZXR1cm4gdGhpcy5fYXVkaW9TdG9yYWdlVHlwZTtcclxuICB9XHJcblxyXG4gIHNldCBhdWRpb1N0b3JhZ2VUeXBlKHZhbHVlOiBBdWRpb1N0b3JhZ2VUeXBlKSB7XHJcbiAgICB0aGlzLl9hdWRpb1N0b3JhZ2VUeXBlID0gdmFsdWU7XHJcbiAgfVxyXG4gIGdldCBwZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0KCk6IFBlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQgfCBudWxsIHtcclxuICAgIHJldHVybiB0aGlzLl9wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0O1xyXG4gIH1cclxuXHJcbiAgc2V0IHBlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQodmFsdWU6IFBlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQgfCBudWxsKSB7XHJcbiAgICB0aGlzLl9wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0ID0gdmFsdWU7XHJcbiAgfVxyXG5cclxuXHJcbiAgZ2V0IG9wZW5lZCgpOiBib29sZWFuIHtcclxuICAgIHJldHVybiB0aGlzLl9vcGVuZWQ7XHJcbiAgfVxyXG5cclxuICBzdGF0aWMgQlVGRkVSX1NJWkU6IG51bWJlciA9IDgxOTI7XHJcbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgREVGQVVMVF9NQVhfTkVUX0FVVE9fTUVNX1NUT1JFX1NBTVBMRVM6bnVtYmVyPTI4ODAwMDAqNTsgLy8gRGVmYXVsdCA1IG1pbnV0ZSBhdCA0OGtIelxyXG4gIHByaXZhdGUgX21heEF1dG9OZXRNZW1TdG9yZVNhbXBsZXM6bnVtYmVyPUF1ZGlvQ2FwdHVyZS5ERUZBVUxUX01BWF9ORVRfQVVUT19NRU1fU1RPUkVfU0FNUExFUztcclxuICBwcml2YXRlIHN0YXRpYyBjYXB0dXJlSW50ZXJjZXB0b3JNb2R1bGVSZWdpc3RlcmVkPWZhbHNlO1xyXG4gIGNvbnRleHQ6IEF1ZGlvQ29udGV4dHxudWxsPW51bGw7XHJcbiAgc3RyZWFtITogTWVkaWFTdHJlYW07XHJcbiAgY2hhbm5lbENvdW50ITogbnVtYmVyO1xyXG4gIHByaXZhdGUgX3JlY1VVSUQ6c3RyaW5nfG51bGw9bnVsbDtcclxuICBtZWRpYVN0cmVhbTogYW55O1xyXG4gIGFnY1N0YXR1czpib29sZWFufG51bGw9bnVsbDtcclxuICBidWZmZXJpbmdOb2RlOiBBdWRpb05vZGV8bnVsbD1udWxsO1xyXG4gIGxpc3RlbmVyITogQXVkaW9DYXB0dXJlTGlzdGVuZXI7XHJcbiAgZGF0YTogQXJyYXk8QXJyYXk8RmxvYXQzMkFycmF5Pj58bnVsbD1udWxsO1xyXG4gIGN1cnJlbnRTYW1wbGVSYXRlITogbnVtYmVyO1xyXG4gIG46IE5hdmlnYXRvcjtcclxuICBhdWRpb091dFN0cmVhbTogU2VxdWVuY2VBdWRpb0Zsb2F0MzJPdXRTdHJlYW0gfCBudWxsPW51bGw7XHJcbiAgcHJpdmF0ZSBkaXNjb25uZWN0U3RyZWFtcyA9IHRydWU7XHJcbiAgcHJpdmF0ZSBfb3BlbmVkPWZhbHNlO1xyXG4gIHByaXZhdGUgY2FwdHVyaW5nID0gZmFsc2U7XHJcblxyXG4gIGZyYW1lc1JlY29yZGVkOiBudW1iZXI9MDtcclxuXHJcbiAgcHJpdmF0ZSBfYXVkaW9TdG9yYWdlVHlwZTpBdWRpb1N0b3JhZ2VUeXBlPUF1ZGlvU3RvcmFnZVR5cGUuTUVNX0VOVElSRTtcclxuICBwcml2YXRlIF9wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0OlBlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXR8bnVsbD1udWxsO1xyXG5cclxuICBwcml2YXRlIHBlcnNpc3RlZD10cnVlO1xyXG4gIHByaXZhdGUgcGVyc2lzdEVycm9yOkVycm9yfG51bGw9bnVsbDtcclxuICBwcml2YXRlIGluZGRiQXVkaW9CdWZmZXI6SW5kZXhlZERiQXVkaW9CdWZmZXJ8bnVsbD1udWxsO1xyXG5cclxuICAvL3ByaXZhdGUgY29udGV4dDpBdWRpb0NvbnRleHR8bnVsbD1udWxsO1xyXG5cclxuICBjb25zdHJ1Y3RvcigpIHtcclxuICAgIHRoaXMubiA9IG5hdmlnYXRvcjtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgX2F1ZGlvQ29udGV4dCgpOkF1ZGlvQ29udGV4dHxudWxse1xyXG4gICAgaWYoIXRoaXMuY29udGV4dCl7XHJcbiAgICAgIHRoaXMuY29udGV4dD1BdWRpb0NvbnRleHRQcm92aWRlci5hdWRpb0NvbnRleHRJbnN0YW5jZSgpO1xyXG4gICAgICBpZih0aGlzLmNvbnRleHQpIHtcclxuICAgICAgICB0aGlzLmNvbnRleHQuYWRkRXZlbnRMaXN0ZW5lcignc3RhdGVjaGFuZ2UnLCAoKSA9PiB7XHJcbiAgICAgICAgICBpZiAodGhpcy5jb250ZXh0ICYmIHRoaXMuY29udGV4dC5zdGF0ZSAhPT0gJ3J1bm5pbmcnKSB7XHJcbiAgICAgICAgICAgIHRoaXMuY2xvc2UoKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgcmV0dXJuIHRoaXMuY29udGV4dDtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgaW5pdERhdGEoKSB7XHJcbiAgICBpZighdGhpcy5fcmVjVVVJRCkge1xyXG4gICAgICB0aGlzLl9yZWNVVUlEID0gVVVJRC5nZW5lcmF0ZSgpO1xyXG4gICAgfVxyXG4gICAgdGhpcy5wZXJzaXN0RXJyb3I9bnVsbDtcclxuICAgIC8vY29uc29sZS5kZWJ1ZyhcIkF1ZGlvIGNhcHR1cmUgaW5pdGlhbGl6ZSBzdG9yYWdlIGZvciB0eXBlOiBcIit0aGlzLl9hdWRpb1N0b3JhZ2VUeXBlKTtcclxuICAgIGlmKEF1ZGlvU3RvcmFnZVR5cGUuREJfQ0hVTktFRCA9PT0gdGhpcy5fYXVkaW9TdG9yYWdlVHlwZSAmJiB0aGlzLl9wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0ICYmIHRoaXMuX3JlY1VVSUQpIHtcclxuICAgICAgLy9jb25zb2xlLmRlYnVnKFwiQ3JlYXRlIGluZGV4ZWQgZGIgYXVkaW8gYnVmZmVyLlwiKTtcclxuICAgICAgdGhpcy5pbmRkYkF1ZGlvQnVmZmVyID0gbmV3IEluZGV4ZWREYkF1ZGlvQnVmZmVyKHRoaXMuX3BlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQsIHRoaXMuY2hhbm5lbENvdW50LHRoaXMuY3VycmVudFNhbXBsZVJhdGUsQXVkaW9DYXB0dXJlLkJVRkZFUl9TSVpFLDAsdGhpcy5fcmVjVVVJRClcclxuICAgIH1cclxuICAgIGlmKCEoQXVkaW9TdG9yYWdlVHlwZS5ORVRfQ0hVTktFRCA9PT0gdGhpcy5fYXVkaW9TdG9yYWdlVHlwZSkpIHtcclxuICAgICAgLy8gSW5pdGlhbGl6ZSBhdWRpbyBkYXRhIGFycmF5IGV4Y2VwdCBmb3IgbmV0IGF1ZGlvIGJ1ZmZlciBtb2RlXHJcbiAgICAgIHRoaXMuZGF0YSA9IG5ldyBBcnJheTxBcnJheTxGbG9hdDMyQXJyYXk+PigpO1xyXG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuY2hhbm5lbENvdW50OyBpKyspIHtcclxuICAgICAgICB0aGlzLmRhdGEucHVzaChuZXcgQXJyYXk8RmxvYXQzMkFycmF5PigpKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgdGhpcy5mcmFtZXNSZWNvcmRlZCA9IDA7XHJcbiAgfVxyXG5cclxuICBsaXN0RGV2aWNlcygpIHtcclxuICAgIG5hdmlnYXRvci5tZWRpYURldmljZXMuZW51bWVyYXRlRGV2aWNlcygpLnRoZW4oKGw6IE1lZGlhRGV2aWNlSW5mb1tdKSA9PiB0aGlzLnByaW50RGV2aWNlcyhsKSk7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIGR1bW15U2Vzc2lvbigpOlByb21pc2U8TWVkaWFTdHJlYW0+e1xyXG4gICAgLy8gd29ya2Fyb3VuZCB0byByZXF1ZXN0IHBlcm1pc3Npb25zOlxyXG4gICAgLy8gU3RhcnQgYSBkdW1teSBzZXNzaW9uXHJcbiAgICBsZXQgbWVkaWFTdHJDbnN0cnMgPSA8TWVkaWFTdHJlYW1Db25zdHJhaW50cz57YXVkaW86XHJcbiAgICAgICAge2VjaG9DYW5jZWxhdGlvbjogZmFsc2V9XHJcbiAgICB9O1xyXG4gICAgcmV0dXJuIG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhKG1lZGlhU3RyQ25zdHJzKTtcclxuXHJcbiAgfVxyXG5cclxuXHJcbiAgcHJpdmF0ZSBzdG9wQWxsU2Vzc2lvblRyYWNrcyhtZWRpYVN0cmVhbTpNZWRpYVN0cmVhbSl7XHJcbiAgICAgIGxldCBhdHMgPSBtZWRpYVN0cmVhbS5nZXRUcmFja3MoKTtcclxuICAgICAgZm9yIChsZXQgYXRJZHggPSAwOyBhdElkeCA8IGF0cy5sZW5ndGg7IGF0SWR4KyspIHtcclxuICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJTdG9wIGR1bW15IHNlc3Npb24gdHJhY2s6ICNcIiArIGF0SWR4KVxyXG4gICAgICAgIGF0c1thdElkeF0uc3RvcCgpO1xyXG4gICAgICB9XHJcbiAgfVxyXG5cclxuICBkZXZpY2VJbmZvcyhjYjogKGRldmljZUluZm9zOiBNZWRpYURldmljZUluZm9bXSB8IG51bGwpID0+IGFueSwgcmV0cnkgPSB0cnVlLGR1bW15U3RyZWFtPzpNZWRpYVN0cmVhbSkge1xyXG5cclxuICAgIG5hdmlnYXRvci5tZWRpYURldmljZXMuZW51bWVyYXRlRGV2aWNlcygpLnRoZW4oKGw6IE1lZGlhRGV2aWNlSW5mb1tdKSA9PiB7XHJcbiAgICAgIGxldCBsYWJlbHNBdmFpbGFibGUgPSBmYWxzZTtcclxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBsLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgbGV0IGRpID0gbFtpXTtcclxuICAgICAgICBpZiAoZGkubGFiZWwpIHtcclxuICAgICAgICAgIGxhYmVsc0F2YWlsYWJsZSA9IHRydWU7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICAgIGlmICghbGFiZWxzQXZhaWxhYmxlKSB7XHJcbiAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiTWVkaWEgZGV2aWNlIGVudW1lcmF0aW9uOiBObyBsYWJlbHMuXCIpXHJcbiAgICAgICAgaWYgKHJldHJ5KSB7XHJcbiAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIlN0YXJ0aW5nIGR1bW15IHNlc3Npb24gdG8gcmVxdWVzdCBhdWRpbyBwZXJtaXNzaW9ucy4uLlwiKVxyXG5cclxuICAgICAgICAgICAgdGhpcy5kdW1teVNlc3Npb24oKS50aGVuKChzOiBNZWRpYVN0cmVhbSkgPT4ge1xyXG4gICAgICAgICAgICAvLyBhbmQgc3RvcCBpdCBpbW1lZGlhdGVseVxyXG5cclxuICAgICAgICAgICAgaWYocykge1xyXG4gICAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIkdvdCBkdW1teSBzZXNzaW9uIHN0cmVhbTogXCIgKyBzICsgXCIgLlwiKVxyXG4gICAgICAgICAgICB9ZWxzZXtcclxuICAgICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJObyBkdW1teSBzdHJlYW1cIilcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAvLyByZXRyeSAob25seSBvbmNlKVxyXG4gICAgICAgICAgICB0aGlzLmRldmljZUluZm9zKGNiLCBmYWxzZSxzKTtcclxuICAgICAgICAgIH0scmVhc29uID0+IHtcclxuICAgICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiRHVtbXkgc2Vzc2lvbiByZWplY3RlZC5cIilcclxuICAgICAgICAgICAgLy8gVE9ETyBlcnJvciBjYWxsYmFja1xyXG4gICAgICAgICAgICBjYihudWxsKTtcclxuICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICBjYihudWxsKTtcclxuICAgICAgICB9XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gc3VjY2Vzc1xyXG4gICAgICAgIGNiKGwpO1xyXG4gICAgICB9XHJcbiAgICAgIGlmKGR1bW15U3RyZWFtKXtcclxuICAgICAgICB0aGlzLnN0b3BBbGxTZXNzaW9uVHJhY2tzKGR1bW15U3RyZWFtKTtcclxuICAgICAgfVxyXG4gICAgfSwocmVhc29uKT0+IHtcclxuICAgICAgLy9yZWplY3RlZFxyXG4gICAgICAvL2NvbnNvbGUuZGVidWcoXCJNZWRpYSBkZXZpY2UgZW51bWVyYXRpb24gcmVqZWN0ZWQuXCIpXHJcbiAgICAgIGlmIChyZXRyeSkge1xyXG4gICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIlN0YXJ0aW5nIGR1bW15IHNlc3Npb24gdG8gcmVxdWVzdCBhdWRpbyBwZXJtaXNzaW9ucy4uLlwiKVxyXG4gICAgICAgIHRoaXMuZHVtbXlTZXNzaW9uKCkudGhlbigoczogTWVkaWFTdHJlYW0pID0+IHtcclxuICAgICAgICAgIC8vIGFuZCBzdG9wIGl0IGltbWVkaWF0ZWx5XHJcbiAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJEdW1teSBzZXNzaW9uLlwiKVxyXG4gICAgICAgICAgaWYocykge1xyXG4gICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJHb3QgZHVtbXkgc2Vzc2lvbiBzdHJlYW06IFwiICsgcyArIFwiIC5cIilcclxuICAgICAgICAgIH1lbHNle1xyXG4gICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJObyBkdW1teSBzdHJlYW1cIilcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIC8vIHJldHJ5IChvbmx5IG9uY2UpXHJcbiAgICAgICAgICB0aGlzLmRldmljZUluZm9zKGNiLCBmYWxzZSxzKTtcclxuICAgICAgICB9LCByZWFzb24gPT4ge1xyXG4gICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiRHVtbXkgc2Vzc2lvbiByZWplY3RlZC5cIilcclxuICAgICAgICAgIC8vIFRPRE8gZXJyb3IgY2FsbGJhY2tcclxuICAgICAgICAgIGNiKG51bGwpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIGNiKG51bGwpO1xyXG4gICAgICB9XHJcbiAgICAgIGlmKGR1bW15U3RyZWFtKXtcclxuICAgICAgICB0aGlzLnN0b3BBbGxTZXNzaW9uVHJhY2tzKGR1bW15U3RyZWFtKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG5cclxuXHJcbiAgfVxyXG5cclxuXHJcbiAgcHJpbnREZXZpY2VzKGw6IE1lZGlhRGV2aWNlSW5mb1tdKTogdm9pZCB7XHJcbiAgICAvL2xldCBzZWxEZXZpY2VJZCA9ICdfX19kdW1teV9fXyc7XHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGwubGVuZ3RoOyBpKyspIHtcclxuICAgICAgbGV0IGRpID0gbFtpXTtcclxuICAgICAgY29uc29sZS5sb2coXCJBdWRpbyBkZXZpY2U6IElkOiBcIiArIGRpLmRldmljZUlkICsgXCIgZ3JvdXBJZDogXCIgKyBkaS5ncm91cElkICsgXCIgbGFiZWw6IFwiICsgZGkubGFiZWwgKyBcIiBraW5kOiBcIiArIGRpLmtpbmQpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgYWRkQ2FwdHVyZUludGVyY2VwdG9yKCkge1xyXG4gICAgaWYgKHRoaXMuY29udGV4dCkge1xyXG5cclxuICAgIGNvbnN0IGF3biA9IG5ldyBBdWRpb1dvcmtsZXROb2RlKHRoaXMuY29udGV4dCwgJ2NhcHR1cmUtaW50ZXJjZXB0b3InKTtcclxuICAgIGF3bi5vbnByb2Nlc3NvcmVycm9yID0gKGV2OiBFdmVudCkgPT4ge1xyXG4gICAgICBsZXQgbXNnID0gJ1Vua253b24gZXJyb3InO1xyXG4gICAgICBpZiAoZXYgaW5zdGFuY2VvZiBFcnJvckV2ZW50KSB7XHJcbiAgICAgICAgbXNnID0gZXYubWVzc2FnZTtcclxuICAgICAgfVxyXG4gICAgICBjb25zb2xlLmVycm9yKFwiQ2FwdHVyZSBhdWRpbyB3b3JrbGV0IGVycm9yOiBcIiArIG1zZyk7XHJcbiAgICAgIGlmICh0aGlzLmxpc3RlbmVyKSB7XHJcbiAgICAgICAgdGhpcy5saXN0ZW5lci5lcnJvcihtc2cpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBsZXQgYXduUHQgPSBhd24ucG9ydDtcclxuICAgIGlmIChhd25QdCkge1xyXG4gICAgICBhd25QdC5vbm1lc3NhZ2UgPSAoZXY6IE1lc3NhZ2VFdmVudCkgPT4ge1xyXG4gICAgICAgIGlmICh0aGlzLmNhcHR1cmluZykge1xyXG4gICAgICAgICAgbGV0IGR0ID0gZXYuZGF0YTtcclxuICAgICAgICAgIGxldCBjaHMgPSBkdC5jaHM7XHJcbiAgICAgICAgICBsZXQgYWRhTGVuID0gZHQuZGF0YS5sZW5ndGg7XHJcbiAgICAgICAgICBpZiAoREVCVUdfVFJBQ0VfTEVWRUwgPiA4KSB7XHJcbiAgICAgICAgICAgIGNvbnNvbGUuZGVidWcoJ1JlY2VpdmVkIGRhdGEgZnJvbSB3b3JrbGV0OiAnICsgY2hzICsgJyAnICsgZHQubGVuICsgJyBEYXRhIGNoczogJyArIGFkYUxlbik7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICBsZXQgY2h1bmsgPSBuZXcgQXJyYXk8RmxvYXQzMkFycmF5PihjaHMpO1xyXG4gICAgICAgICAgY29uc3Qgc2FtcGxlcyA9IHRoaXMuZnJhbWVzUmVjb3JkZWQgKiBjaHM7XHJcbiAgICAgICAgICBpZiAoKEF1ZGlvU3RvcmFnZVR5cGUuTUVNX0VOVElSRV9BVVRPX05FVF9DSFVOS0VEID09PSB0aGlzLmF1ZGlvU3RvcmFnZVR5cGUgfHwgQXVkaW9TdG9yYWdlVHlwZS5NRU1fQ0hVTktFRF9BVVRPX05FVF9DSFVOS0VEID09PSB0aGlzLmF1ZGlvU3RvcmFnZVR5cGUpICYmIHRoaXMuZGF0YSAmJiBzYW1wbGVzID4gdGhpcy5fbWF4QXV0b05ldE1lbVN0b3JlU2FtcGxlcykge1xyXG4gICAgICAgICAgICB0aGlzLmRhdGEgPSBudWxsO1xyXG4gICAgICAgICAgfVxyXG5cclxuICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIkRhdGEgaW5pdGlhbGl6ZWQ6IFwiKyh0aGlzLmRhdGEhPW51bGwpKTtcclxuICAgICAgICAgIGZvciAobGV0IGNoID0gMDsgY2ggPCBjaHM7IGNoKyspIHtcclxuICAgICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiRGF0YSBjaCBpbml0aWFsaXplZDogXCIrKHRoaXMuZGF0YSAhPW51bGwgJiYgdGhpcy5kYXRhW2NoXSAhPW51bGwpKTtcclxuICAgICAgICAgICAgaWYgKGNoIDwgdGhpcy5jaGFubmVsQ291bnQpIHtcclxuICAgICAgICAgICAgICBpZiAoZHQuZGF0YVtjaF0pIHtcclxuICAgICAgICAgICAgICAgIGxldCBmYSA9IG5ldyBGbG9hdDMyQXJyYXkoZHQuZGF0YVtjaF0pO1xyXG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuZGF0YSAmJiB0aGlzLmRhdGFbY2hdKSB7XHJcbiAgICAgICAgICAgICAgICAgIHRoaXMuZGF0YVtjaF0ucHVzaChmYSk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBjaHVua1tjaF0gPSBmYTtcclxuICAgICAgICAgICAgICAgIC8vIFVzZSBzYW1wbGVzIG9mIGNoYW5uZWwgMCB0byBjb3VudCBmcmFtZXMgKHNhbXBsZXMpXHJcbiAgICAgICAgICAgICAgICBpZiAoY2ggPT0gMCkge1xyXG4gICAgICAgICAgICAgICAgICB0aGlzLmZyYW1lc1JlY29yZGVkICs9IGZhLmxlbmd0aDtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICAgIGlmICh0aGlzLmF1ZGlvT3V0U3RyZWFtKSB7XHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgdGhpcy5hdWRpb091dFN0cmVhbS53cml0ZShjaHVuayk7XHJcbiAgICAgICAgICAgICAgLy8gLy8gUmFuZG9tIHRlc3QgZXJyb3I6XHJcbiAgICAgICAgICAgICAgLy8gaWYoTWF0aC5yYW5kb20oKT4wLjk4KSB7XHJcbiAgICAgICAgICAgICAgLy8gICB0aHJvdyBuZXcgRXJyb3IoJ1Rlc3QnKTtcclxuICAgICAgICAgICAgICAvLyB9XHJcbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xyXG4gICAgICAgICAgICAgIGlmIChlcnIgaW5zdGFuY2VvZiBFcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5wZXJzaXN0RXJyb3IgPSBlcnI7XHJcbiAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgIHRoaXMucGVyc2lzdEVycm9yID0gbmV3IEVycm9yKCdFcnJvciBoYW5kbGluZyByZWNvcmRlZCBhdWRpbyBkYXRhJyk7XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiQ2FwdHVyZSBlcnJvcjogXCIgKyBlcnIpO1xyXG4gICAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLnN0b3AoKTtcclxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnIyKSB7XHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiQ2FwdHVyZSBuZXh0IGVycm9yIChpZ25vcmVkKTogXCIgKyBlcnIyKTtcclxuICAgICAgICAgICAgICB9IGZpbmFsbHkge1xyXG4gICAgICAgICAgICAgICAgaWYgKHRoaXMubGlzdGVuZXIpIHtcclxuICAgICAgICAgICAgICAgICAgbGV0IGVyckV4cGwgPSAnJztcclxuICAgICAgICAgICAgICAgICAgaWYgKGVyciBpbnN0YW5jZW9mIERPTUV4Y2VwdGlvbikge1xyXG4gICAgICAgICAgICAgICAgICAgIGVyckV4cGwgPSAnOiAnICsgZXJyLm5hbWUgKyAnOiAnICsgZXJyLm1lc3NhZ2U7XHJcbiAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5lcnJvcihcIkNvdWxkIG5vdCBoYW5kbGUgcmVjb3JkZWQgYXVkaW8gZGF0YVwiICsgZXJyRXhwbCwgXCJQbGVhc2UgdHJ5IHRvIHJlY29yZCBhZ2Fpbi5cIik7XHJcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICB0aGlzLmNsb3NlKCk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICBpZiAoQXVkaW9TdG9yYWdlVHlwZS5EQl9DSFVOS0VEID09PSB0aGlzLl9hdWRpb1N0b3JhZ2VUeXBlICYmIHRoaXMuX3BlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQpIHtcclxuICAgICAgICAgICAgdGhpcy5zdG9yZSgpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfTtcclxuICAgIH1cclxuICAgIC8vIFRyaWVkIHRvIGZpeCB0aGF0IFNhZmFyaSBkb2VzIG5vdCByZWNvcmQgdGhlIHNlY29uZCBjaGFubmVsXHJcbiAgICAvLyBEb2VzIG5vdCBoZWxwXHJcbiAgICAvL2F3bi5jaGFubmVsQ291bnQ9dGhpcy5jaGFubmVsQ291bnQ7XHJcbiAgICAvL2F3bi5jaGFubmVsQ291bnRNb2RlPSdleHBsaWNpdCc7XHJcbiAgICAvL2NvbnNvbGUuZGVidWcoJ0NoYW5uZWwgY291bnQgZXhwbGljaXRseSBzZXQgdG8gJyt0aGlzLmNoYW5uZWxDb3VudCk7XHJcblxyXG4gICAgdGhpcy5idWZmZXJpbmdOb2RlID0gYXduO1xyXG4gICAgLy90aGlzLmJ1ZmZlcmluZ05vZGUuY2hhbm5lbENvdW50PXRoaXMuY2hhbm5lbENvdW50O1xyXG4gICAgdGhpcy5fb3BlbmVkID0gdHJ1ZTtcclxuICAgIGlmICh0aGlzLmxpc3RlbmVyKSB7XHJcbiAgICAgIHRoaXMubGlzdGVuZXIub3BlbmVkKCk7XHJcbiAgICB9XHJcbiAgfVxyXG4gIH1cclxuXHJcbiAgb3BlbihjaGFubmVsQ291bnQ6IG51bWJlciwgc2VsRGV2aWNlSWQ/OiBDb25zdHJhaW5ET01TdHJpbmd8dW5kZWZpbmVkLGF1dG9HYWluQ29udHJvbENvbmZpZ3M/OkFycmF5PEF1dG9HYWluQ29udHJvbENvbmZpZz58bnVsbHx1bmRlZmluZWQsYWxsb3dFY2hvQ2FuY2VsbGF0aW9uPzpib29sZWFuKXtcclxuICAgIC8vY29uc29sZS5kZWJ1ZyhcIkNhcHR1cmUgb3BlbjogY3R4IHN0YXRlOiBcIit0aGlzLmNvbnRleHQuc3RhdGUpO1xyXG4gICAgdGhpcy5jb250ZXh0PXRoaXMuX2F1ZGlvQ29udGV4dCgpO1xyXG4gICAgaWYoIXRoaXMuY29udGV4dCl7XHJcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIkNvdWxkIG5vdCBnZXQgYXVkaW8gY29udGV4dCFcIik7XHJcbiAgICB9XHJcbiAgICBpZih0aGlzLmNvbnRleHQuc3RhdGU9PT0nc3VzcGVuZGVkJyl7XHJcbiAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIkNhcHR1cmUgb3BlbjogUmVzdW1lIGNvbnRleHRcIik7XHJcbiAgICAgIHRoaXMuY29udGV4dC5yZXN1bWUoKS50aGVuKCgpPT57XHJcbiAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiQ2FwdHVyZSBvcGVuIChjdHggcmVzdW1lZCk6IGN0eCBzdGF0ZTogXCIrdGhpcy5jb250ZXh0LnN0YXRlKTtcclxuICAgICAgICB0aGlzLl9vcGVuKGNoYW5uZWxDb3VudCwgc2VsRGV2aWNlSWQsIGF1dG9HYWluQ29udHJvbENvbmZpZ3MsYWxsb3dFY2hvQ2FuY2VsbGF0aW9uKTtcclxuICAgICAgfSkuY2F0Y2goKGVycik9PntcclxuICAgICAgICBjb25zb2xlLmVycm9yKGVyci5tZXNzYWdlKTtcclxuICAgICAgICB0aHJvdyBlcnI7XHJcbiAgICAgIH0pXHJcbiAgICB9ZWxzZSBpZih0aGlzLmNvbnRleHQuc3RhdGU9PT0nY2xvc2VkJykge1xyXG4gICAgICAgIGNvbnN0IG1zZz0nRXJyb3Igb24gc3RhcnQgY2FwdHVyZTogVGhlIGF1ZGlvIGNvbnRleHQgaXMgYWxyZWFkeSBjbG9zZWQuJztcclxuICAgICAgICBjb25zb2xlLmVycm9yKG1zZyk7XHJcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKG1zZyk7XHJcbiAgICB9ZWxzZSB7XHJcbiAgICAgIHRoaXMuX29wZW4oY2hhbm5lbENvdW50LCBzZWxEZXZpY2VJZCwgYXV0b0dhaW5Db250cm9sQ29uZmlncyxhbGxvd0VjaG9DYW5jZWxsYXRpb24pO1xyXG4gICAgfVxyXG5cclxuICB9XHJcblxyXG4gIF9vcGVuKGNoYW5uZWxDb3VudDogbnVtYmVyLCBzZWxEZXZpY2VJZD86IENvbnN0cmFpbkRPTVN0cmluZ3x1bmRlZmluZWQsYXV0b0dhaW5Db250cm9sQ29uZmlncz86QXJyYXk8QXV0b0dhaW5Db250cm9sQ29uZmlnPnxudWxsfHVuZGVmaW5lZCxhbGxvd0VjaG9DYW5jZWxsYXRpb24/OmJvb2xlYW4pIHtcclxuICAgIHRoaXMuY2hhbm5lbENvdW50ID0gY2hhbm5lbENvdW50O1xyXG4gICAgdGhpcy5mcmFtZXNSZWNvcmRlZCA9IDA7XHJcblxyXG4gICAgdGhpcy5jb250ZXh0PXRoaXMuX2F1ZGlvQ29udGV4dCgpO1xyXG5cclxuICAgIGlmKCF0aGlzLmNvbnRleHQpe1xyXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJDb3VsZCBub3QgZ2V0IGF1ZGlvIGNvbnRleHQhXCIpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vdmFyIG1zYyA9IG5ldyBBdWRpb1N0cmVhbUNvbnN0cigpO1xyXG4gICAgLy8gdmFyIG1zYz17fTtcclxuICAgIC8vbXNjLnZpZGVvID0gZmFsc2U7XHJcbiAgICAvL21zYy5hdWRpbyA9IHRydWU7XHJcblxyXG4gICAgLy8gQ2hyb21lIGFuZCBGaXJlZm94IHN0ZXJlbyBjaGFubmVscyBhcmUgaWRlbnRpY2FsICEhXHJcbiAgICAvLyBBbmQgZXZlbiB3b3JzZTogVGhlIGRhdGEgY29taW5nIGZyb20gdGhlIHNvdXJjZSBpcyBhbHJlYWR5IHByZXByb2Nlc3NlZCBvbiBGRiBhbmQgQ2hyb21lLiBJdCBjb250YWlucyBEU1AgYXJ0aWZhY3RzISFcclxuXHJcbiAgICAvLyBodHRwczovL2J1Z3MuY2hyb21pdW0ub3JnL3AvY2hyb21pdW0vaXNzdWVzL2RldGFpbD9pZD0zODc3MzdcclxuICAgIC8vIFRoZSB3b3JrYXJvdW5kIHRvIHNldCB0aGVzZSBjb25zdHJhaW50cyBkb2VzIF9OT1RfIGhlbHA6XHJcbiAgICAvL3ZhciBtc2M9e2F1ZGlvOiB7ZWNob0NhbmNlbGxhdGlvbjogZmFsc2UsY2hhbm5lbENvdW50OiAyLCBnb29nQXVkaW9NaXJyb3Jpbmc6IGZhbHNlfSx2aWRlbzogZmFsc2V9O1xyXG5cclxuICAgIC8vIFNhZmFyaSBhdCBsZWFzdCB2ZXJzaW9uIDExOiBTdXBwb3J0IGZvciBtZWRpYSBzdHJlYW1zXHJcbiAgICAvLyBUT0RPIHRlc3QgaWYgaW5wdXQgaXMgdW5wcm9jZXNzZWRcclxuXHJcbiAgICBsZXQgbXNjOk1lZGlhU3RyZWFtQ29uc3RyYWludHM7XHJcbiAgICBjb25zb2xlLmluZm8oJ1VzZXIgYWdlbnQ6ICcrbmF2aWdhdG9yLnVzZXJBZ2VudCk7XHJcblxyXG5cclxuICAgICAgbGV0IHVhPVVzZXJBZ2VudEJ1aWxkZXIudXNlckFnZW50KCk7XHJcblxyXG4gICAgICAvLyB1YS5jb21wb25lbnRzLmZvckVhY2goKGMpPT57XHJcbiAgICAgIC8vICAgY29uc29sZS5pbmZvKFwiVUFfQ29tcDogXCIrYy50b1N0cmluZygpKTtcclxuICAgICAgLy8gfSlcclxuXHJcbiAgICAgbGV0IGFnY0NmZzpBdXRvR2FpbkNvbnRyb2xDb25maWd8bnVsbD1udWxsO1xyXG5cclxuICAgIGxldCBhdXRvR2FpbkNvbnRyb2w9ZmFsc2U7XHJcbiAgICBsZXQgY2hyb21lRWNob0NhbmNlbGxhdGlvbj1mYWxzZTtcclxuICAgIGlmKGF1dG9HYWluQ29udHJvbENvbmZpZ3Mpe1xyXG4gICAgICBmb3IobGV0IGFnY2Mgb2YgYXV0b0dhaW5Db250cm9sQ29uZmlncyl7XHJcbiAgICAgICAgaWYoYWdjYy5wbGF0Zm9ybT09PUNmZ1BsYXRmb3JtLkFuZHJvaWQgJiYgdWEuZGV0ZWN0ZWRQbGF0Zm9ybT09PVBsYXRmb3JtLkFuZHJvaWQpe1xyXG4gICAgICAgICAgICBhZ2NDZmc9YWdjYztcclxuICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmKGFnY2MucGxhdGZvcm09PT1DZmdQbGF0Zm9ybS5XaW5kb3dzICYmIHVhLmRldGVjdGVkUGxhdGZvcm09PT1QbGF0Zm9ybS5XaW5kb3dzKXtcclxuICAgICAgICAgIGFnY0NmZz1hZ2NjO1xyXG4gICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICAgIGlmKGFnY0NmZyl7XHJcbiAgICAgICAgLy8gVE9ETyB1c2UgRVhBQ1QvSURFQUwgY29uc3RyYWludFxyXG4gICAgICAgIGF1dG9HYWluQ29udHJvbD1hZ2NDZmcudmFsdWU7XHJcbiAgICAgICAgaWYoQ0hST01FX0FDVElWQVRFX0VDSE9fQ0FOQ0VMTEFUSU9OX1dJVEhfQUdDKXtcclxuICAgICAgICAgIGNocm9tZUVjaG9DYW5jZWxsYXRpb249YWdjQ2ZnLnZhbHVlO1xyXG4gICAgICAgIH1cclxuICAgICAgICAvLyBUT0RPIHF1ZXJ5IHJlYWwgQUdDIHN0YXR1c1xyXG4gICAgICAgIHRoaXMuYWdjU3RhdHVzPWFnY0NmZy52YWx1ZTtcclxuICAgICAgfWVsc2V7XHJcbiAgICAgICAgdGhpcy5hZ2NTdGF0dXM9ZmFsc2U7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBkZWZhdWx0XHJcbiAgICBtc2MgPSB7XHJcbiAgICAgIGF1ZGlvOiB7XHJcbiAgICAgICAgZGV2aWNlSWQ6IHNlbERldmljZUlkLFxyXG4gICAgICAgIGVjaG9DYW5jZWxsYXRpb246IGZhbHNlLFxyXG4gICAgICAgIGNoYW5uZWxDb3VudDogY2hhbm5lbENvdW50LFxyXG4gICAgICAgIGF1dG9HYWluQ29udHJvbDogYXV0b0dhaW5Db250cm9sXHJcbiAgICAgIH0sXHJcbiAgICAgIHZpZGVvOiBmYWxzZVxyXG4gICAgfTtcclxuXHJcbiAgICBpZiAodWEuZGV0ZWN0ZWRCcm93c2VyPT09QnJvd3Nlci5FZGdlKSB7XHJcblxyXG4gICAgICAvLyBNaWNyb3NvZnQgRWRnZSBzZW5kcyB1bm1vZGlmaWVkIGF1ZGlvXHJcbiAgICAgIC8vIFRoZSBjb25zdHJhaW50IGNhbiBmb2xsb3cgdGhlIHNwZWNpZmljYXRpb25cclxuICAgICAgY29uc29sZS5pbmZvKFwiU2V0dGluZyBtZWRpYSB0cmFjayBjb25zdHJhaW50cyBmb3IgTWljcm9zb2Z0IEVkZ2UuXCIpO1xyXG4gICAgICBtc2MgPSB7XHJcbiAgICAgICAgYXVkaW86IHtcclxuICAgICAgICAgIGRldmljZUlkOiBzZWxEZXZpY2VJZCxcclxuICAgICAgICAgIGVjaG9DYW5jZWxsYXRpb246IGZhbHNlLFxyXG4gICAgICAgICAgY2hhbm5lbENvdW50OiBjaGFubmVsQ291bnQsXHJcbiAgICAgICAgICBhdXRvR2FpbkNvbnRyb2w6IGF1dG9HYWluQ29udHJvbFxyXG4gICAgICAgIH0sXHJcbiAgICAgICAgdmlkZW86IGZhbHNlXHJcbiAgICAgIH07XHJcbiAgICB9IGVsc2UgaWYgKHVhLmRldGVjdGVkQnJvd3Nlcj09PUJyb3dzZXIuQ2hyb21lKSB7XHJcbiAgICAgIC8vIEdvb2dsZSBDaHJvbWU6IHdlIG5lZWQgdG8gc3dpdGNoIG9mIGVhY2ggb2YgdGhlIHByZXByb2Nlc3NpbmcgdW5pdHMgaW5jbHVkaW5nIHRoZVxyXG4gICAgICBjb25zb2xlLmluZm8oXCJTZXR0aW5nIG1lZGlhIHRyYWNrIGNvbnN0cmFpbnRzIGZvciBHb29nbGUgQ2hyb21lLlwiKTtcclxuXHJcbiAgICAgIC8vIENocm9tZSA2MCAtPiA2MSBjaGFuZ2VkXHJcbiAgICAgIC8vIGl0IHdvcmtzIG5vdyB3aXRob3V0IG1hbmRhdG9yeS9vcHRpb25hbCBzdWItb2JqZWN0c1xyXG5cclxuXHJcbiAgICAgIC8vIFJlcXVpcmVzIGF0IGxlYXN0IENocm9tZSA2MVxyXG4gICAgICBtc2MgPSB7XHJcbiAgICAgICAgYXVkaW86IHtcclxuICAgICAgICAgIGRldmljZUlkOiBzZWxEZXZpY2VJZCxcclxuICAgICAgICAgIGNoYW5uZWxDb3VudDogY2hhbm5lbENvdW50LFxyXG4gICAgICAgICAgZWNob0NhbmNlbGxhdGlvbjoge2V4YWN0OmNocm9tZUVjaG9DYW5jZWxsYXRpb259LFxyXG4gICAgICAgICAgYXV0b0dhaW5Db250cm9sOiB7ZXhhY3Q6YXV0b0dhaW5Db250cm9sfSxcclxuICAgICAgICAgIHNhbXBsZVNpemU6e21pbjogMTZ9LFxyXG4gICAgICAgIH0sXHJcbiAgICAgICAgdmlkZW86IGZhbHNlLFxyXG4gICAgICB9XHJcblxyXG4gICAgfSBlbHNlIGlmICh1YS5kZXRlY3RlZEJyb3dzZXI9PT1Ccm93c2VyLkZpcmVmb3gpIHtcclxuICAgICAgY29uc29sZS5pbmZvKFwiU2V0dGluZyBtZWRpYSB0cmFjayBjb25zdHJhaW50cyBmb3IgTW96aWxsYSBGaXJlZm94LlwiKTtcclxuICAgICAgLy8gRmlyZWZveFxyXG4gICAgICBtc2MgPSB7XHJcbiAgICAgICAgYXVkaW86IHtcclxuICAgICAgICAgICAgZGV2aWNlSWQ6IHNlbERldmljZUlkLFxyXG4gICAgICAgICAgICBjaGFubmVsQ291bnQ6IGNoYW5uZWxDb3VudCxcclxuICAgICAgICAgIGVjaG9DYW5jZWxsYXRpb246IGZhbHNlLFxyXG4gICAgICAgICAgICBhdXRvR2FpbkNvbnRyb2w6IGF1dG9HYWluQ29udHJvbCxcclxuICAgICAgICAgIG5vaXNlU3VwcHJlc3Npb246IGZhbHNlXHJcbiAgICAgICAgfSxcclxuICAgICAgICB2aWRlbzogZmFsc2UsXHJcbiAgICAgIH1cclxuXHJcbiAgICB9IGVsc2UgaWYgKHVhLmRldGVjdGVkQnJvd3Nlcj09PUJyb3dzZXIuU2FmYXJpKSB7XHJcbiAgICAgIGNvbnNvbGUuaW5mbyhcIlNldHRpbmcgbWVkaWEgdHJhY2sgY29uc3RyYWludHMgZm9yIFNhZmFyaSBicm93c2VyLlwiKVxyXG4gICAgICAvL2NvbnNvbGUuaW5mbyhcIkFwcGx5IHdvcmthcm91bmQgZm9yIFNhZmFyaTogQXZvaWQgZGlzY29ubmVjdCBvZiBzdHJlYW1zLlwiKTtcclxuXHJcbiAgICAgIHRoaXMuZGlzY29ubmVjdFN0cmVhbXMgPSB0cnVlO1xyXG5cclxuICAgICAgbXNjID0ge1xyXG4gICAgICAgIGF1ZGlvOiB7XHJcbiAgICAgICAgICBkZXZpY2VJZDogc2VsRGV2aWNlSWQsXHJcbiAgICAgICAgICBjaGFubmVsQ291bnQ6IGNoYW5uZWxDb3VudCxcclxuICAgICAgICAgIGVjaG9DYW5jZWxsYXRpb246IGFsbG93RWNob0NhbmNlbGxhdGlvbj91bmRlZmluZWQ6ZmFsc2VcclxuICAgICAgICB9LFxyXG4gICAgICAgIHZpZGVvOiBmYWxzZSxcclxuICAgICAgfVxyXG5cclxuICAgIH0gZWxzZSB7XHJcblxyXG4gICAgICAvLyBUT0RPIGRlZmF1bHQgY29uc3RyYWludHMgb3IgZXJyb3IgQnJvd3NlciBub3Qgc3VwcG9ydGVkXHJcbiAgICB9XHJcblxyXG5cclxuXHJcbiAgICBjb25zb2xlLmRlYnVnKFwiQXVkaW8gY2FwdHVyZSwgQUdDOiBcIit0aGlzLmFnY1N0YXR1cylcclxuXHJcbiAgICBsZXQgdW1wID0gbmF2aWdhdG9yLm1lZGlhRGV2aWNlcy5nZXRVc2VyTWVkaWEobXNjKTtcclxuICAgIHVtcC50aGVuKChzKSA9PiB7XHJcblxyXG4gICAgICBpZiAodGhpcy5jb250ZXh0KSB7XHJcbiAgICAgIHRoaXMuc3RyZWFtID0gcztcclxuXHJcbiAgICAgIGxldCBhVHJhY2tzID0gcy5nZXRBdWRpb1RyYWNrcygpO1xyXG5cclxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhVHJhY2tzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgbGV0IGFUcmFjayA9IGFUcmFja3NbaV07XHJcblxyXG4gICAgICAgIGNvbnNvbGUuaW5mbyhcIlRyYWNrIGF1ZGlvIGluZm86IGlkOiBcIiArIGFUcmFjay5pZCArIFwiIGtpbmQ6IFwiICsgYVRyYWNrLmtpbmQgKyBcIiBsYWJlbDogXFxcIlwiICsgYVRyYWNrLmxhYmVsICsgXCJcXFwiXCIpO1xyXG4gICAgICAgIGxldCBtdHJTdHMgPSBhVHJhY2suZ2V0U2V0dGluZ3MoKTtcclxuXHJcbiAgICAgICAgLy8gVHlwZXNjcmlwdCBsaWIuZG9tLnRzIE1lZGlhVHJhY2tTZXR0aW5ncy5jaGFubmVsQ291bnQgaXMgbWlzc2luZ1xyXG4gICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9tZG4vYnJvd3Nlci1jb21wYXQtZGF0YS9ibG9iLzU0OTNkOGY5MzdlMDViMmRkYmQ0MWI5OWY1YmRmYWQ0YTFmMmVkODUvYXBpL01lZGlhVHJhY2tTZXR0aW5ncy5qc29uXHJcbiAgICAgICAgLy9AdHMtaWdub3JlXHJcbiAgICAgICAgY29uc29sZS5pbmZvKFwiVHJhY2sgYXVkaW8gc2V0dGluZ3M6IENoIGNudDogXCIgKyBtdHJTdHMuY2hhbm5lbENvdW50ICsgXCIsIEFHQzogXCIgKyBtdHJTdHMuYXV0b0dhaW5Db250cm9sICsgXCIsIEVjaG8gY2FuY2VsbC46IFwiICsgbXRyU3RzLmVjaG9DYW5jZWxsYXRpb24pO1xyXG4gICAgICAgIGlmIChtdHJTdHMuYXV0b0dhaW5Db250cm9sKSB7XHJcbiAgICAgICAgICB0aGlzLmFnY1N0YXR1cyA9IG10clN0cy5hdXRvR2FpbkNvbnRyb2w7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBjb25zb2xlLmRlYnVnKFwiRWNobyBjYW5jZWxsYXRpb246IFwiK210clN0cy5lY2hvQ2FuY2VsbGF0aW9uKTtcclxuXHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGxldCB2VHJhY2tzID0gcy5nZXRWaWRlb1RyYWNrcygpO1xyXG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZUcmFja3MubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICBsZXQgdlRyYWNrID0gdlRyYWNrc1tpXTtcclxuICAgICAgICBjb25zb2xlLmluZm8oXCJUcmFjayB2aWRlbyBpbmZvOiBpZDogXCIgKyB2VHJhY2suaWQgKyBcIiBraW5kOiBcIiArIHZUcmFjay5raW5kICsgXCIgbGFiZWw6IFwiICsgdlRyYWNrLmxhYmVsKTtcclxuICAgICAgfVxyXG4gICAgICB0aGlzLm1lZGlhU3RyZWFtID0gdGhpcy5jb250ZXh0LmNyZWF0ZU1lZGlhU3RyZWFtU291cmNlKHMpO1xyXG4gICAgICAvLyBzdHJlYW0gY2hhbm5lbCBjb3VudCAoIGlzIGFsd2F5cyAyICEpXHJcbiAgICAgIGxldCBzdHJlYW1DaGFubmVsQ291bnQ6IG51bWJlciA9IHRoaXMubWVkaWFTdHJlYW0uY2hhbm5lbENvdW50O1xyXG4gICAgICBjb25zb2xlLmluZm8oXCJTdHJlYW0gY2hhbm5lbCBjb3VudDogXCIgKyBzdHJlYW1DaGFubmVsQ291bnQpO1xyXG4gICAgICAvLyBpcyBub3Qgc2V0ISFcclxuICAgICAgLy90aGlzLmN1cnJlbnRTYW1wbGVSYXRlID0gdGhpcy5tZWRpYVN0cmVhbS5zYW1wbGVSYXRlO1xyXG4gICAgICB0aGlzLmN1cnJlbnRTYW1wbGVSYXRlID0gdGhpcy5jb250ZXh0LnNhbXBsZVJhdGU7XHJcbiAgICAgIGNvbnNvbGUuaW5mbyhcIlNvdXJjZSBhdWRpbyBub2RlOiBjaGFubmVsczogXCIgKyBzdHJlYW1DaGFubmVsQ291bnQgKyBcIiBzYW1wbGVyYXRlOiBcIiArIHRoaXMuY3VycmVudFNhbXBsZVJhdGUpO1xyXG4gICAgICBpZiAodGhpcy5hdWRpb091dFN0cmVhbSkge1xyXG4gICAgICAgIHRoaXMuYXVkaW9PdXRTdHJlYW0uc2V0Rm9ybWF0KHRoaXMuY2hhbm5lbENvdW50LCB0aGlzLmN1cnJlbnRTYW1wbGVSYXRlKTtcclxuICAgICAgfVxyXG4gICAgICAvLyBXM0MgIC0+IG5ldyBuYW1lIGlzIGNyZWF0ZVNjcmlwdFByb2Nlc3NvclxyXG4gICAgICAvL1xyXG4gICAgICAvLyBBZ2FpbiBkZXByZWNhdGVkLCBidXQgQXVkaW9Xb3JrZXIgbm90IHlldCBpbXBsZW1lbnRlZCBpbiBzdGFibGUgcmVsZWFzZXMgKEp1bmUgMjAxNilcclxuICAgICAgLy8gQXVkaW9Xb3JrZXIgaXMgbm93IEF1ZGlvV29ya2xldFByb2Nlc3NvciAuLi4gKE1heSAyMDE3KVxyXG5cclxuICAgICAgLy8gVXBkYXRlIDEyLTIwMjA6XHJcbiAgICAgIC8vIFRoZSBTY3JpcHRQcm9jZXNzb3JOb2RlIEludGVyZmFjZSAtIERFUFJFQ0FURURcclxuXHJcbiAgICAgIC8vIFVwZGF0ZSAwNi0yMDIxXHJcbiAgICAgIC8vICBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgaXMgaGVyZSB0byBzdGF5LiBXZWIgQXVkaW8gQVBJIGhhcyBub3cgUmVjb21tZW5kYXRpb24gc3RhdHVzICFcclxuXHJcbiAgICAgIGlmICh0aGlzLmNvbnRleHQuYXVkaW9Xb3JrbGV0KSB7XHJcbiAgICAgICAgLy9jb25zdCB3b3JrbGV0RmlsZU5hbWUgPSAoJ2ZpbGUtbG9hZGVyIS4vaW50ZXJjZXB0b3Jfd29ya2xldC5qcycpO1xyXG4gICAgICAgIC8vY29uc3Qgd29ya2xldEZpbGVOYW1lID0gJ2h0dHA6Ly9sb2NhbGhvc3Q6NDIwMC9hc3NldHMvaW50ZXJjZXB0b3Jfd29ya2xldC5qcyc7XHJcbiAgICAgICAgLy9jb25zb2xlLmxvZyhhd3BTdHIpO1xyXG4gICAgICAgIGlmIChBdWRpb0NhcHR1cmUuY2FwdHVyZUludGVyY2VwdG9yTW9kdWxlUmVnaXN0ZXJlZCkge1xyXG4gICAgICAgICAgLy8gUmVxdWlyZWQgY2FwdHVyZSBpbnRlcmNlcHRvciBtb2R1bGUgYWxyZWFkeSByZWdpc3RlcmVkXHJcbiAgICAgICAgICB0aGlzLmFkZENhcHR1cmVJbnRlcmNlcHRvcigpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcblxyXG4gICAgICAgICAgLy8gUmVnaXN0ZXIgY2FwdHVyZSBpbnRlcmNlcHRvciBtb2R1bGVcclxuICAgICAgICAgIGxldCBhdWRpb1dvcmtsZXRNb2R1bGVCbG9iID0gbmV3IEJsb2IoW2F3cFN0cl0sIHt0eXBlOiAndGV4dC9qYXZhc2NyaXB0J30pO1xyXG5cclxuICAgICAgICAgIGxldCBhdWRpb1dvcmtsZXRNb2R1bGVCbG9iVXJsID0gd2luZG93LlVSTC5jcmVhdGVPYmplY3RVUkwoYXVkaW9Xb3JrbGV0TW9kdWxlQmxvYik7XHJcblxyXG4gICAgICAgICAgdGhpcy5jb250ZXh0LmF1ZGlvV29ya2xldC5hZGRNb2R1bGUoYXVkaW9Xb3JrbGV0TW9kdWxlQmxvYlVybCkudGhlbigoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICBBdWRpb0NhcHR1cmUuY2FwdHVyZUludGVyY2VwdG9yTW9kdWxlUmVnaXN0ZXJlZCA9IHRydWU7XHJcbiAgICAgICAgICAgICAgICB0aGlzLmFkZENhcHR1cmVJbnRlcmNlcHRvcigpO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICkuY2F0Y2goKGVycm9yOiBhbnkpID0+IHtcclxuICAgICAgICAgICAgY29uc29sZS5sb2coJ0NvdWxkIG5vdCBhZGQgbW9kdWxlICcgKyBlcnJvcik7XHJcbiAgICAgICAgICB9KTtcclxuICAgICAgICB9XHJcbiAgICAgIH0gZWxzZSBpZiAodGhpcy5jb250ZXh0LmNyZWF0ZVNjcmlwdFByb2Nlc3Nvcikge1xyXG4gICAgICAgIC8vIFRoZSBTY3JpcHRQcm9jZXNzb3JOb2RlIEludGVyZmFjZSAtIERFUFJFQ0FURUQgT25seSBhcyBmYWxsYmFja1xyXG4gICAgICAgIC8vIFRPRE8gc2hvdWxkIHdlIHVzZSBzdHJlYW1DaGFubmVsQ291bnQgb3IgY2hhbm5lbENvdW50IGhlcmUgP1xyXG4gICAgICAgIGxldCBzY3JpcHRQcm9jZXNzb3JOb2RlID0gdGhpcy5jb250ZXh0LmNyZWF0ZVNjcmlwdFByb2Nlc3NvcihBdWRpb0NhcHR1cmUuQlVGRkVSX1NJWkUsIHN0cmVhbUNoYW5uZWxDb3VudCwgc3RyZWFtQ2hhbm5lbENvdW50KTtcclxuICAgICAgICB0aGlzLmJ1ZmZlcmluZ05vZGUgPSBzY3JpcHRQcm9jZXNzb3JOb2RlO1xyXG4gICAgICAgIGxldCBjID0gMDtcclxuICAgICAgICBpZiAoc2NyaXB0UHJvY2Vzc29yTm9kZS5vbmF1ZGlvcHJvY2Vzcykge1xyXG4gICAgICAgICAgc2NyaXB0UHJvY2Vzc29yTm9kZS5vbmF1ZGlvcHJvY2VzcyA9IChlOiBBdWRpb1Byb2Nlc3NpbmdFdmVudCkgPT4ge1xyXG5cclxuICAgICAgICAgICAgaWYgKHRoaXMuY2FwdHVyaW5nKSB7XHJcbiAgICAgICAgICAgICAgbGV0IGluQnVmZmVyID0gZS5pbnB1dEJ1ZmZlcjtcclxuICAgICAgICAgICAgICAvLyBvbmx5IHByb2Nlc3MgcmVxdWVzdGVkIGNvdW50IG9mIGNoYW5uZWxzXHJcbiAgICAgICAgICAgICAgbGV0IGN1cnJlbnRCdWZmZXJzID0gbmV3IEFycmF5PEZsb2F0MzJBcnJheT4oY2hhbm5lbENvdW50KTtcclxuICAgICAgICAgICAgICBmb3IgKGxldCBjaDogbnVtYmVyID0gMDsgY2ggPCBjaGFubmVsQ291bnQ7IGNoKyspIHtcclxuICAgICAgICAgICAgICAgIGxldCBjaFNhbXBsZXMgPSBpbkJ1ZmZlci5nZXRDaGFubmVsRGF0YShjaCk7XHJcbiAgICAgICAgICAgICAgICBsZXQgY2hTYW1wbGVzQ29weSA9IGNoU2FtcGxlcy5zbGljZSgwKTtcclxuICAgICAgICAgICAgICAgIGN1cnJlbnRCdWZmZXJzW2NoXSA9IGNoU2FtcGxlc0NvcHkuc2xpY2UoMCk7XHJcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5kYXRhKSB7XHJcbiAgICAgICAgICAgICAgICAgIHRoaXMuZGF0YVtjaF0ucHVzaChjaFNhbXBsZXNDb3B5KTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIGlmIChERUJVR19UUkFDRV9MRVZFTCA+IDgpIHtcclxuICAgICAgICAgICAgICAgICAgY29uc29sZS5kZWJ1ZyhcIlByb2Nlc3MgXCIgKyBjaFNhbXBsZXNDb3B5Lmxlbmd0aCArIFwiIHNhbXBsZXMuXCIpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgdGhpcy5mcmFtZXNSZWNvcmRlZCArPSBjaFNhbXBsZXNDb3B5Lmxlbmd0aDtcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgYysrO1xyXG4gICAgICAgICAgICAgIGlmICh0aGlzLmF1ZGlvT3V0U3RyZWFtKSB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLmF1ZGlvT3V0U3RyZWFtLndyaXRlKGN1cnJlbnRCdWZmZXJzKTtcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH07XHJcbiAgICAgICAgICB0aGlzLl9vcGVuZWQgPSB0cnVlO1xyXG4gICAgICAgICAgaWYgKHRoaXMubGlzdGVuZXIpIHtcclxuICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5vcGVuZWQoKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgdGhpcy5saXN0ZW5lci5lcnJvcignQnJvd3NlciBkb2VzIG5vdCBzdXBwb3J0IGF1ZGlvIHByb2Nlc3NpbmcgKFNjcmlwdFByb2Nlc3Nvci5vbmF1ZGlvcHJvY2VzcyBtZXRob2Qgbm90IGZvdW5kKSEnKTtcclxuICAgICAgICB9XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdGhpcy5saXN0ZW5lci5lcnJvcignQnJvd3NlciBkb2VzIG5vdCBzdXBwb3J0IGF1ZGlvIHByb2Nlc3NpbmcgKG5laXRoZXIgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIG5vciBTY3JpcHRQcm9jZXNzb3IpIScpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICAgICAgfSwgKGUpID0+IHtcclxuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZSArIFwiIEVycm9yIG5hbWU6IFwiICtlLm5hbWUpO1xyXG4gICAgICAgICAgaWYgKHRoaXMubGlzdGVuZXIpIHtcclxuICAgICAgICAgICAgaWYoJ05vdEFsbG93ZWRFcnJvcicgPT09IGUubmFtZSl7XHJcbiAgICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5lcnJvcignTm90IGFsbG93ZWQgdG8gdXNlIHlvdXIgbWljcm9waG9uZS4nLCdQbGVhc2UgbWFrZSBzdXJlIHRoYXQgbWljcm9waG9uZSBhY2Nlc3MgaXMgYWxsb3dlZCBmb3IgdGhpcyB3ZWIgcGFnZSBhbmQgcmVsb2FkIHRoZSBwYWdlLicpO1xyXG4gICAgICAgICAgICB9ZWxzZSBpZignTm90UmVhZGFibGVFcnJvcicgPT09IGUubmFtZSl7XHJcbiAgICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5lcnJvcignQ291bGQgbm90IHJlYWQgZnJvbSB5b3VyIGF1ZGlvIGRldmljZS4nLCdQbGVhc2UgbWFrZSBzdXJlIHlvdXIgYXVkaW8gZGV2aWNlIGlzIHdvcmtpbmcuJyk7XHJcbiAgICAgICAgICAgIH1lbHNlIGlmKCdPdmVyY29uc3RyYWluZWRFcnJvcicgPT09IGUubmFtZSl7XHJcbiAgICAgICAgICAgICAgbGV0IGVNc2c9ZS5tc2c/ZS5tc2c6J092ZXJjb25zdHJhaW5lZCBtZWRpYSBkZXZpY2UgcmVxdWVzdCBlcnJvci4nO1xyXG4gICAgICAgICAgICAgIHRoaXMubGlzdGVuZXIuZXJyb3IoZU1zZyk7XHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5lcnJvcigpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgKVxyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBfc3RhcnQoKXtcclxuICAgIGlmKHRoaXMuY29udGV4dCkge1xyXG4gICAgICB0aGlzLmluaXREYXRhKCk7XHJcbiAgICAgIGlmICh0aGlzLmF1ZGlvT3V0U3RyZWFtKSB7XHJcbiAgICAgICAgdGhpcy5hdWRpb091dFN0cmVhbS5uZXh0U3RyZWFtKClcclxuICAgICAgfVxyXG4gICAgICB0aGlzLmNhcHR1cmluZyA9IHRydWU7XHJcbiAgICAgIGlmICh0aGlzLmJ1ZmZlcmluZ05vZGUpIHtcclxuICAgICAgICB0aGlzLm1lZGlhU3RyZWFtLmNvbm5lY3QodGhpcy5idWZmZXJpbmdOb2RlKTtcclxuICAgICAgICB0aGlzLmJ1ZmZlcmluZ05vZGUuY29ubmVjdCh0aGlzLmNvbnRleHQuZGVzdGluYXRpb24pO1xyXG4gICAgICB9XHJcbiAgICAgIGlmICh0aGlzLmxpc3RlbmVyKSB7XHJcbiAgICAgICAgdGhpcy5saXN0ZW5lci5zdGFydGVkKCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcblxyXG4gIHN0YXJ0KCkge1xyXG4gICAgaWYodGhpcy5jb250ZXh0KSB7XHJcbiAgICAgIGNvbnN0IGFTdCA9IHRoaXMuY29udGV4dC5zdGF0ZTtcclxuICAgICAgaWYgKGFTdCA9PT0gJ3J1bm5pbmcnKSB7XHJcbiAgICAgICAgdGhpcy5fc3RhcnQoKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBjb25zb2xlLmRlYnVnKFwiQ2FwdHVyZSBzdGFydDogYXVkaW8gY29udGV4dCBub3QgcnVubmluZywgc3RhdGU6IFwiICsgYVN0ICsgXCIsIHJlc3VtaW5nLi4uXCIpO1xyXG4gICAgICAgIHRoaXMuY29udGV4dC5yZXN1bWUoKS50aGVuKCgpID0+IHtcclxuICAgICAgICAgIGNvbnNvbGUuZGVidWcoXCJDYXB0dXJlIHN0YXJ0OiBhdWRpbyBjb250ZXh0IHJlc3VtZWQsIHN0YXJ0aW5nLi4uXCIpO1xyXG4gICAgICAgICAgdGhpcy5fc3RhcnQoKTtcclxuICAgICAgICB9KVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBzdG9wKCkge1xyXG5cclxuICAgIGlmICh0aGlzLmRpc2Nvbm5lY3RTdHJlYW1zICYmIHRoaXMuYnVmZmVyaW5nTm9kZSkge1xyXG4gICAgICB0aGlzLm1lZGlhU3RyZWFtLmRpc2Nvbm5lY3QodGhpcy5idWZmZXJpbmdOb2RlKTtcclxuICAgICAgaWYodGhpcy5jb250ZXh0KSB7XHJcbiAgICAgICAgdGhpcy5idWZmZXJpbmdOb2RlLmRpc2Nvbm5lY3QodGhpcy5jb250ZXh0LmRlc3RpbmF0aW9uKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgIGlmICh0aGlzLmF1ZGlvT3V0U3RyZWFtKSB7XHJcbiAgICAgICAgdGhpcy5hdWRpb091dFN0cmVhbS5mbHVzaCgpO1xyXG4gICAgICB9XHJcbiAgICB9Y2F0Y2goZXJyKXtcclxuICAgICAgY29uc29sZS5lcnJvcihcIkNvdWxkIG5vdCBmbHVzaCBjYXB0dXJlIHN0cmVhbS5cIik7XHJcbiAgICAgdGhyb3cgZXJyO1xyXG4gICAgfWZpbmFsbHkge1xyXG4gICAgICB0aGlzLmNhcHR1cmluZyA9IGZhbHNlO1xyXG4gICAgICBpZiAodGhpcy5pbmRkYkF1ZGlvQnVmZmVyICYmIHRoaXMucGVyc2lzdEVycm9yKSB7XHJcbiAgICAgICAgLy8gRGVsZXRlIGludmFsaWQgcGVyc2lzdGVudCBhdWRpbyBkYXRhIGFuZCBob3BlIHRoYXQgaXQgd2lsbCBiZSBjb21wbGV0ZWx5IHVwbG9hZGVkIHRvIHRoZSBzZXJ2ZXJcclxuICAgICAgICB0aGlzLmluZGRiQXVkaW9CdWZmZXIucmVsZWFzZUF1ZGlvRGF0YSgpO1xyXG4gICAgICAgIHRoaXMuaW5kZGJBdWRpb0J1ZmZlciA9IG51bGw7XHJcbiAgICAgIH1cclxuICAgICAgaWYgKHRoaXMubGlzdGVuZXIgJiYgKHRoaXMucGVyc2lzdEVycm9yIHx8IHRoaXMucGVyc2lzdGVkKSkge1xyXG4gICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIlN0b3BwZWQgYnkgc3RvcCgpIG1ldGhvZCBjYWxsXCIpO1xyXG4gICAgICAgIHRoaXMubGlzdGVuZXIuc3RvcHBlZCgpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuXHJcbiAgc3RvcmUoKXtcclxuICAgLy8gaWYodGhpcy5kYiAmJiB0aGlzLnJlY1VVSUQpe1xyXG4gICAvLyAgICBsZXQgdHI9IHRoaXMuZGIudHJhbnNhY3Rpb24oU3ByRGIuUkVDT1JESU5HX0ZJTEVfQ0hVTktTX09CSkVDVF9TVE9SRV9OQU1FLCAncmVhZHdyaXRlJyk7XHJcbiAgIC8vICAgIGxldCByZWNGaWxlT2JqU3RvcmUgPSB0ci5vYmplY3RTdG9yZShTcHJEYi5SRUNPUkRJTkdfRklMRV9DSFVOS1NfT0JKRUNUX1NUT1JFX05BTUUpO1xyXG4gICAvL1xyXG4gICAvLyAgICAgIHRyeSB7XHJcbiAgIC8vICAgICAgICBsZXQgY2gwRGF0YT10aGlzLmRhdGFbMF07XHJcbiAgIC8vICAgICAgICBsZXQgZGF0YUNoa0NudD1jaDBEYXRhLmxlbmd0aDtcclxuICAgLy8gICAgICAgICAgbGV0IHBvcyA9IDA7XHJcbiAgIC8vICAgICAgICAgIGZvciAobGV0IGNoQ2tJZHggPSAwOyBjaENrSWR4IDwgZGF0YUNoa0NudDsgY2hDa0lkeCsrKSB7XHJcbiAgIC8vICAgICAgICAgICAgbGV0IGJ1Zkxlbj0wO1xyXG4gICAvLyAgICAgICAgICAgIGZvciAobGV0IGNoID0gMDsgY2ggPCB0aGlzLmNoYW5uZWxDb3VudDsgY2grKykge1xyXG4gICAvLyAgICAgICAgICAgICAgbGV0IGNoQ2hrID0gdGhpcy5kYXRhW2NoXVtjaENrSWR4XTtcclxuICAgLy8gICAgICAgICAgICAgIGJ1ZkxlbiA9IGNoQ2hrLmxlbmd0aDtcclxuICAgLy8gICAgICAgICAgICAgIC8vbGV0IGNhY2hlSWQgPSB1dWlkICsgJ18nICsgY2ggKyAnXycgKyBjaENrSWR4O1xyXG4gICAvLyAgICAgICAgICAgICAgbGV0IGNoa0RiSWQgPSBbdGhpcy5yZWNVVUlELCB0aGlzLmluZERiQ2hrSWR4ICsgY2hDa0lkeCwgY2hdO1xyXG4gICAvLyAgICAgICAgICAgICAgbGV0IGNyID0gcmVjRmlsZU9ialN0b3JlLmFkZChjaENoaywgY2hrRGJJZCk7XHJcbiAgIC8vICAgICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJBZGRlZDogXCIrY2grXCIgXCIrKHRoaXMuaW5kRGJDaGtJZHgrY2hDa0lkeCkpO1xyXG4gICAvLyAgICAgICAgICAgICAgY3Iub25zdWNjZXNzPSgpPT57XHJcbiAgIC8vICAgICAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIlN0b3JlZCBhdWRpbyBkYXRhIHRvIGluZGV4ZWQgZGJcIik7XHJcbiAgIC8vICAgICAgICAgICAgICB9XHJcbiAgIC8vICAgICAgICAgICAgICBjci5vbmVycm9yPSgpPT57XHJcbiAgIC8vICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJFcnJvciBzdG9yaW5nIGF1ZGlvIGRhdGEgdG8gaW5kZXhlZCBkYlwiKTtcclxuICAgLy8gICAgICAgICAgICAgIH1cclxuICAgLy8gICAgICAgICAgICB9XHJcbiAgIC8vICAgICAgICAgICAgcG9zICs9IGJ1ZkxlbjtcclxuICAgLy8gICAgICAgIH1cclxuICAgLy8gICAgICAgIHRoaXMuaW5kRGJDaGtJZHgrPWRhdGFDaGtDbnQ7XHJcbiAgIC8vXHJcbiAgIC8vICAgICAgICB0ci5vbmVycm9yID0gKGVycikgPT4ge1xyXG4gICAvLyAgICAgICAgICBjb25zb2xlLmVycm9yKCdGYWlsZWQgdG8gY2FjaGUgYXVkaW8gZGF0YSB0byBpbmRleGVkIGRiOiAnICsgZXJyKVxyXG4gICAvLyAgICAgICAgfVxyXG4gICAvLyAgICAgICAgdHIub25jb21wbGV0ZSA9ICgpID0+IHtcclxuICAgLy8gICAgICAgICAgLy9jb25zb2xlLmRlYnVnKCdUcmFuc2ZlcnJlZCBjYXB0dXJlIGF1ZGlvIGRhdGEgdG8gaW5kZXhlZCBkYiwgZGVsZXRpbmcgb3JpZ2luYWwgZGF0YSBmcm9tIG1lbW9yeS4uLicpO1xyXG4gICAvL1xyXG4gICAvLyAgICAgICAgICAvLy8gQXVkaW8gZGF0YSBzYXZlZCB0byBpbmRleCBkYiBkZWxldGUgZnJvbSBpbiBtZW1vcnkgZGF0YSBhcnJheVxyXG4gICAvLyAgICAgICAgICBmb3IgKGxldCBjaCA9IDA7IGNoIDwgdGhpcy5jaGFubmVsQ291bnQ7IGNoKyspIHtcclxuICAgLy8gICAgICAgICAgIHRoaXMuZGF0YVtjaF0uc3BsaWNlKDApO1xyXG4gICAvLyAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIlNwbGljZWQvcmVtb3ZlZCBjaDogXCIrY2gpO1xyXG4gICAvLyAgICAgICAgICB9XHJcbiAgIC8vXHJcbiAgIC8vICAgICAgICAgIHRoaXMucGVyc2lzdGVkPXRydWU7XHJcbiAgIC8vICAgICAgICAgIGlmKHRoaXMubGlzdGVuZXIgJiYgIXRoaXMuY2FwdHVyaW5nKXtcclxuICAgLy8gICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJTdG9wcGVkIGJ5IGluZGV4ZWQgZGIgaG9va1wiKTtcclxuICAgLy8gICAgICAgICAgICB0aGlzLmxpc3RlbmVyLnN0b3BwZWQoKTtcclxuICAgLy8gICAgICAgICAgfVxyXG4gICAvLyAgICAgICAgfVxyXG4gICAvLyAgICAgICAgLy8gQ29tbWl0IGNodW5rc1xyXG4gICAvLyAgICAgICAgdGhpcy5wZXJzaXN0ZWQ9ZmFsc2U7XHJcbiAgIC8vICAgICAgICB0ci5jb21taXQoKTtcclxuICAgLy8gICAgICB9IGNhdGNoIChlcnIpIHtcclxuICAgLy8gICAgICAgIGNvbnNvbGUuZXJyb3IoJ1RyYW5zZmVyIGNhcHR1cmUgYXVkaW8gZGF0YSBlcnJvcjogJytlcnIpO1xyXG4gICAvLyAgICAgIH1cclxuICAgLy8gICAgfVxyXG5cclxuICAgIC8vIGlmKCF0aGlzLmluZGRiQXVkaW9CdWZmZXIgJiYgdGhpcy5fcGVyc2lzdGVudEF1ZGlvU3RvcmFnZVRhcmdldCAmJiB0aGlzLnJlY1VVSUQpIHtcclxuICAgIC8vICAgdGhpcy5pbmRkYkF1ZGlvQnVmZmVyID0gbmV3IEluZGV4ZWREYkF1ZGlvQnVmZmVyKHRoaXMuX3BlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQsIHRoaXMuY2hhbm5lbENvdW50LHRoaXMuY3VycmVudFNhbXBsZVJhdGUsQXVkaW9DYXB0dXJlLkJVRkZFUl9TSVpFLDAsdGhpcy5yZWNVVUlEKVxyXG4gICAgLy8gfVxyXG4gICAgaWYodGhpcy5pbmRkYkF1ZGlvQnVmZmVyICYmIHRoaXMuZGF0YSl7XHJcblxyXG4gICAgICAvLyBUcnkgdG8gYXBwZW5kIHRvXHJcbiAgICAgIHRoaXMuaW5kZGJBdWRpb0J1ZmZlci5hcHBlbmRSYXdBdWRpb0RhdGEodGhpcy5kYXRhKS5zdWJzY3JpYmUoe1xyXG4gICAgICAgIGNvbXBsZXRlOiAoKSA9PiB7XHJcbiAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoJ1RyYW5zZmVycmVkIGNhcHR1cmUgYXVkaW8gZGF0YSB0byBpbmRleGVkIGRiLCBkZWxldGluZyBvcmlnaW5hbCBkYXRhIGZyb20gbWVtb3J5Li4uJyk7XHJcblxyXG4gICAgICAgICAgLy8vIEF1ZGlvIGRhdGEgc2F2ZWQgdG8gaW5kZXggZGIgZGVsZXRlIGZyb20gaW4gbWVtb3J5IGRhdGEgYXJyYXlcclxuICAgICAgICAgIGlmKHRoaXMuZGF0YSkge1xyXG4gICAgICAgICAgICBmb3IgKGxldCBjaCA9IDA7IGNoIDwgdGhpcy5jaGFubmVsQ291bnQ7IGNoKyspIHtcclxuICAgICAgICAgICAgICB0aGlzLmRhdGFbY2hdLnNwbGljZSgwKTtcclxuICAgICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJTcGxpY2VkL3JlbW92ZWQgY2g6IFwiK2NoKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgdGhpcy5wZXJzaXN0ZWQgPSB0cnVlO1xyXG4gICAgICAgICAgaWYgKHRoaXMubGlzdGVuZXIgJiYgIXRoaXMuY2FwdHVyaW5nKSB7XHJcbiAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIlN0b3BwZWQgYnkgaW5kZXhlZCBkYiBob29rXCIpO1xyXG4gICAgICAgICAgICB0aGlzLmxpc3RlbmVyLnN0b3BwZWQoKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9LGVycm9yOihlcnIpPT57XHJcbiAgICAgICAgICAvLyBPbmx5IGxvZyB0aGUgZmlyc3QgZXJyb3JcclxuICAgICAgICAgIGlmKCF0aGlzLnBlcnNpc3RFcnJvcikge1xyXG4gICAgICAgICAgICB0aGlzLnBlcnNpc3RFcnJvciA9IGVycjtcclxuICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIkVycm9yIHBlcnNpc3RpbmcgYXVkaW8gZGF0YTogXCIgKyBlcnIpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcbiAgICAgIHRoaXMucGVyc2lzdGVkPWZhbHNlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcblxyXG4gIGNsb3NlKCkge1xyXG4gICAgaWYodGhpcy5tZWRpYVN0cmVhbSkge1xyXG4gICAgICB0aGlzLm1lZGlhU3RyZWFtLmRpc2Nvbm5lY3QoKTtcclxuICAgIH1cclxuICAgIGlmICh0aGlzLnN0cmVhbSkge1xyXG4gICAgICBjb25zdCBtdHMgPSB0aGlzLnN0cmVhbS5nZXRUcmFja3MoKTtcclxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBtdHMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICBtdHNbaV0uc3RvcCgpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICB0aGlzLl9vcGVuZWQ9ZmFsc2U7XHJcbiAgfVxyXG5cclxuICBhdWRpb0J1ZmZlcigpOiBBdWRpb0J1ZmZlciB8bnVsbHtcclxuICAgIGxldCBhYjogQXVkaW9CdWZmZXJ8bnVsbD1udWxsO1xyXG4gICAgaWYodGhpcy5jb250ZXh0ICYmIHRoaXMuZGF0YSkge1xyXG4gICAgICBsZXQgZnJhbWVMZW46IG51bWJlciA9IDA7XHJcblxyXG4gICAgICBsZXQgY2gwRGF0YSA9IHRoaXMuZGF0YVswXTtcclxuXHJcbiAgICAgIGZvciAobGV0IGNoMENoayBvZiBjaDBEYXRhKSB7XHJcbiAgICAgICAgZnJhbWVMZW4gKz0gY2gwQ2hrLmxlbmd0aDtcclxuICAgICAgfVxyXG5cclxuICAgICAgdHJ5IHtcclxuICAgICAgICBhYiA9IHRoaXMuY29udGV4dC5jcmVhdGVCdWZmZXIodGhpcy5jaGFubmVsQ291bnQsIGZyYW1lTGVuLCB0aGlzLmNvbnRleHQuc2FtcGxlUmF0ZSk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycikge1xyXG4gICAgICAgIGlmIChlcnIgaW5zdGFuY2VvZiBET01FeGNlcHRpb24pIHtcclxuICAgICAgICAgIGlmIChlcnIubmFtZSA9PT0gJ05vdFN1cHBvcnRlZEVycm9yJykge1xyXG4gICAgICAgICAgICBpZiAoZnJhbWVMZW4gPT0gMCkge1xyXG4gICAgICAgICAgICAgIC8vIEVtcHR5IGJ1ZmZlcnMgYXJlIG5vdCBzdXBwb3J0ZWQgYnkgQ2hyb21pdW1cclxuICAgICAgICAgICAgICAvLyBDcmVhdGUgZHVtbXkgYnVmZmVyIHdpdGggb25lIHNhbXBsZVxyXG4gICAgICAgICAgICAgIGFiID0gdGhpcy5jb250ZXh0LmNyZWF0ZUJ1ZmZlcih0aGlzLmNoYW5uZWxDb3VudCwgMSwgdGhpcy5jb250ZXh0LnNhbXBsZVJhdGUpO1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgIHRocm93IGVycjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgdGhyb3cgZXJyO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH0gZWxzZSBpZiAoZXJyIGluc3RhbmNlb2YgUmFuZ2VFcnJvcikge1xyXG4gICAgICAgICAgLy8gT3V0IG9mIG1lbW9yeVxyXG4gICAgICAgICAgLy8gVE9ETyBXaGF0IHRvIGRvID8/XHJcbiAgICAgICAgICB0aHJvdyBlcnI7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIHRocm93IGVycjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgZm9yIChsZXQgY2ggPSAwOyBjaCA8IHRoaXMuY2hhbm5lbENvdW50OyBjaCsrKSB7XHJcbiAgICAgICAgbGV0IGNoRCA9IGFiLmdldENoYW5uZWxEYXRhKGNoKTtcclxuICAgICAgICBsZXQgcG9zID0gMDtcclxuICAgICAgICBmb3IgKGxldCBjaENoayBvZiB0aGlzLmRhdGFbY2hdKSB7XHJcbiAgICAgICAgICBsZXQgYnVmTGVuID0gY2hDaGsubGVuZ3RoO1xyXG4gICAgICAgICAgY2hELnNldChjaENoaywgcG9zKTtcclxuICAgICAgICAgIHBvcyArPSBidWZMZW47XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICByZXR1cm4gYWI7XHJcbiAgfVxyXG5cclxuICBhdWRpb0J1ZmZlckFycmF5KCk6QXJyYXlBdWRpb0J1ZmZlcnxudWxse1xyXG4gICAgICBsZXQgYXJyQWI6QXJyYXlBdWRpb0J1ZmZlcnxudWxsPW51bGw7XHJcbiAgICAgIGlmKHRoaXMuZGF0YSkge1xyXG4gICAgICAgIGFyckFiPW5ldyBBcnJheUF1ZGlvQnVmZmVyKHRoaXMuY2hhbm5lbENvdW50LCB0aGlzLmN1cnJlbnRTYW1wbGVSYXRlLCB0aGlzLmRhdGEpO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBhcnJBYjtcclxuICB9XHJcblxyXG4gIGluZGRiQXVkaW9CdWZmZXJBcnJheSgpOkluZGV4ZWREYkF1ZGlvQnVmZmVyfG51bGx7XHJcbiAgICBpZih0aGlzLnBlcnNpc3RFcnJvcil7XHJcbiAgICAgIHJldHVybiBudWxsO1xyXG4gICAgfWVsc2Uge1xyXG4gICAgICByZXR1cm4gdGhpcy5pbmRkYkF1ZGlvQnVmZmVyO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcblxyXG59XHJcblxyXG4iXX0=