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.
- package/esm2022/lib/action/action.mjs +73 -0
- package/{esm2020 → esm2022}/lib/audio/array_audio_buffer.mjs +163 -163
- package/{esm2020 → esm2022}/lib/audio/array_audio_buffer_input_stream.mjs +85 -85
- package/{esm2020 → esm2022}/lib/audio/array_audio_buffer_random_access_stream.mjs +15 -15
- package/esm2022/lib/audio/audio_data_holder.mjs +264 -0
- package/{esm2020 → esm2022}/lib/audio/audio_display.mjs +93 -93
- package/{esm2020 → esm2022}/lib/audio/audio_player.mjs +213 -213
- package/esm2022/lib/audio/capture/capture.mjs +855 -0
- package/esm2022/lib/audio/context.mjs +79 -0
- package/{esm2020 → esm2022}/lib/audio/dsp/level_measure.mjs +515 -515
- package/{esm2020 → esm2022}/lib/audio/format.mjs +16 -16
- package/esm2022/lib/audio/impl/wavformat.mjs +6 -0
- package/{esm2020 → esm2022}/lib/audio/impl/wavreader.mjs +133 -133
- package/esm2022/lib/audio/impl/wavwriter.mjs +105 -0
- package/esm2022/lib/audio/inddb_audio_buffer.mjs +508 -0
- package/{esm2020 → esm2022}/lib/audio/io/stream.mjs +58 -58
- package/esm2022/lib/audio/net_audio_buffer.mjs +293 -0
- package/{esm2020 → esm2022}/lib/audio/persistor.mjs +80 -80
- package/{esm2020 → esm2022}/lib/audio/playback/array_audio_buffer_source_node.mjs +125 -125
- package/esm2022/lib/audio/playback/audio_source_node.mjs +18 -0
- package/esm2022/lib/audio/playback/audio_source_worklet_module_loader.mjs +167 -0
- package/{esm2020 → esm2022}/lib/audio/playback/inddb_audio_buffer_source_node.mjs +166 -166
- package/{esm2020 → esm2022}/lib/audio/playback/net_audio_buffer_source_node.mjs +217 -217
- package/esm2022/lib/audio/playback/player.mjs +402 -0
- package/esm2022/lib/audio/ui/audio_canvas_layer_comp.mjs +347 -0
- package/{esm2020 → esm2022}/lib/audio/ui/audio_display_control.mjs +65 -65
- package/{esm2020 → esm2022}/lib/audio/ui/audio_display_scroll_pane.mjs +139 -139
- package/{esm2020 → esm2022}/lib/audio/ui/audiosignal.mjs +524 -524
- package/{esm2020 → esm2022}/lib/audio/ui/common.mjs +18 -18
- package/esm2022/lib/audio/ui/container.mjs +414 -0
- package/{esm2020 → esm2022}/lib/audio/ui/livelevel.mjs +352 -352
- package/esm2022/lib/audio/ui/scroll_pane_horizontal.mjs +11 -0
- package/esm2022/lib/audio/ui/sonagram.mjs +900 -0
- package/esm2022/lib/db/inddb.mjs +120 -0
- package/esm2022/lib/dsp/utils.mjs +48 -0
- package/{esm2020 → esm2022}/lib/io/BinaryReader.mjs +84 -84
- package/esm2022/lib/io/BinaryWriter.mjs +74 -0
- package/{esm2020 → esm2022}/lib/io/stream.mjs +258 -258
- package/{esm2020 → esm2022}/lib/math/2d/geometry.mjs +27 -27
- package/esm2022/lib/math/complex.mjs +58 -0
- package/{esm2020 → esm2022}/lib/math/dft.mjs +195 -195
- package/{esm2020 → esm2022}/lib/media/utils.mjs +13 -13
- package/esm2022/lib/net/uploader.mjs +367 -0
- package/{esm2020 → esm2022}/lib/recorder_component.mjs +64 -64
- package/esm2022/lib/speechrecorder/project/project.mjs +49 -0
- package/esm2022/lib/speechrecorder/project/project.service.mjs +64 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/recording.mjs +123 -123
- package/esm2022/lib/speechrecorder/recordings/basic_recording.service.mjs +218 -0
- package/esm2022/lib/speechrecorder/recordings/recordings.service.mjs +1014 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/script/script.mjs +114 -114
- package/{esm2020 → esm2022}/lib/speechrecorder/script/script.service.mjs +47 -47
- package/esm2022/lib/speechrecorder/session/audiorecorder.mjs +1179 -0
- package/esm2022/lib/speechrecorder/session/basicrecorder.mjs +668 -0
- package/esm2022/lib/speechrecorder/session/controlpanel.mjs +416 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/session/item.mjs +29 -29
- package/{esm2020 → esm2022}/lib/speechrecorder/session/progress.mjs +69 -69
- package/{esm2020 → esm2022}/lib/speechrecorder/session/prompting.mjs +571 -571
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recorder_combi_pane.mjs +65 -65
- package/esm2022/lib/speechrecorder/session/recording_file_cache.mjs +195 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recording_list.mjs +103 -103
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file-meta.component.mjs +63 -63
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file-navi.component.mjs +51 -51
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file-u-i.component.mjs +104 -104
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file-view.component.mjs +381 -381
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file.mjs +67 -67
- package/esm2022/lib/speechrecorder/session/recordingfile/recordingfile-service.mjs +288 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/session/session.mjs +1 -1
- package/esm2022/lib/speechrecorder/session/session.service.mjs +69 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/session/session_finished_dialog.mjs +29 -29
- package/esm2022/lib/speechrecorder/session/sessionmanager.mjs +1386 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/session/warning_bar.mjs +28 -28
- package/{esm2020 → esm2022}/lib/speechrecorder/spruploader.mjs +22 -22
- package/{esm2020 → esm2022}/lib/speechrecorder/startstopsignal/startstopsignal.mjs +1 -1
- package/esm2022/lib/speechrecorder/startstopsignal/ui/simpletrafficlight.mjs +57 -0
- package/{esm2020 → esm2022}/lib/speechrecorderng.component.mjs +388 -388
- package/{esm2020 → esm2022}/lib/speechrecorderng.module.mjs +100 -100
- package/{esm2020 → esm2022}/lib/spr.config.mjs +26 -26
- package/{esm2020 → esm2022}/lib/spr.module.version.mjs +2 -2
- package/{esm2020 → esm2022}/lib/ui/canvas_layer_comp.mjs +38 -38
- package/{esm2020 → esm2022}/lib/ui/message_dialog.mjs +30 -30
- package/esm2022/lib/ui/recordingitem_display.mjs +253 -0
- package/{esm2020 → esm2022}/lib/ui/responsive_component.mjs +24 -24
- package/{esm2020 → esm2022}/lib/utils/scrollIntoViewToBottom.mjs +23 -23
- package/esm2022/lib/utils/ua-parser.mjs +190 -0
- package/esm2022/lib/utils/utils.mjs +105 -0
- package/esm2022/lib/utils/wake_lock.mjs +114 -0
- package/{esm2020 → esm2022}/lib/utils/wake_lock_media.mjs +1 -1
- package/{esm2020 → esm2022}/public-api.mjs +33 -33
- package/{esm2020 → esm2022}/speechrecorderng.mjs +4 -4
- package/{fesm2020 → fesm2022}/speechrecorderng.mjs +16518 -16454
- package/fesm2022/speechrecorderng.mjs.map +1 -0
- package/index.d.ts +5 -5
- package/lib/action/action.d.ts +27 -27
- package/lib/audio/array_audio_buffer.d.ts +31 -31
- package/lib/audio/array_audio_buffer_input_stream.d.ts +14 -14
- package/lib/audio/array_audio_buffer_random_access_stream.d.ts +9 -9
- package/lib/audio/audio_data_holder.d.ts +84 -84
- package/lib/audio/audio_display.d.ts +36 -36
- package/lib/audio/audio_player.d.ts +47 -47
- package/lib/audio/capture/capture.d.ts +67 -67
- package/lib/audio/context.d.ts +6 -6
- package/lib/audio/dsp/level_measure.d.ts +60 -60
- package/lib/audio/format.d.ts +11 -11
- package/lib/audio/impl/wavformat.d.ts +5 -5
- package/lib/audio/impl/wavreader.d.ts +16 -16
- package/lib/audio/impl/wavwriter.d.ts +13 -13
- package/lib/audio/inddb_audio_buffer.d.ts +68 -68
- package/lib/audio/io/stream.d.ts +28 -28
- package/lib/audio/net_audio_buffer.d.ts +57 -57
- package/lib/audio/persistor.d.ts +29 -29
- package/lib/audio/playback/array_audio_buffer_source_node.d.ts +18 -18
- package/lib/audio/playback/audio_source_node.d.ts +10 -10
- package/lib/audio/playback/audio_source_worklet_module_loader.d.ts +4 -4
- package/lib/audio/playback/inddb_audio_buffer_source_node.d.ts +21 -21
- package/lib/audio/playback/net_audio_buffer_source_node.d.ts +27 -27
- package/lib/audio/playback/player.d.ts +65 -65
- package/lib/audio/ui/audio_canvas_layer_comp.d.ts +68 -68
- package/lib/audio/ui/audio_display_control.d.ts +25 -25
- package/lib/audio/ui/audio_display_scroll_pane.d.ts +26 -26
- package/lib/audio/ui/audiosignal.d.ts +30 -30
- package/lib/audio/ui/common.d.ts +11 -11
- package/lib/audio/ui/container.d.ts +63 -63
- package/lib/audio/ui/livelevel.d.ts +60 -60
- package/lib/audio/ui/scroll_pane_horizontal.d.ts +5 -5
- package/lib/audio/ui/sonagram.d.ts +37 -37
- package/lib/db/inddb.d.ts +21 -21
- package/lib/dsp/utils.d.ts +19 -19
- package/lib/io/BinaryReader.d.ts +18 -18
- package/lib/io/BinaryWriter.d.ts +15 -15
- package/lib/io/stream.d.ts +59 -59
- package/lib/math/2d/geometry.d.ts +18 -18
- package/lib/math/complex.d.ts +17 -17
- package/lib/math/dft.d.ts +22 -22
- package/lib/media/utils.d.ts +3 -3
- package/lib/net/uploader.d.ts +81 -81
- package/lib/recorder_component.d.ts +16 -16
- package/lib/speechrecorder/project/project.d.ts +59 -59
- package/lib/speechrecorder/project/project.service.d.ts +21 -21
- package/lib/speechrecorder/recording.d.ts +51 -51
- package/lib/speechrecorder/recordings/basic_recording.service.d.ts +27 -27
- package/lib/speechrecorder/recordings/recordings.service.d.ts +48 -48
- package/lib/speechrecorder/script/script.d.ts +80 -79
- package/lib/speechrecorder/script/script.service.d.ts +16 -16
- package/lib/speechrecorder/session/audiorecorder.d.ts +122 -122
- package/lib/speechrecorder/session/basicrecorder.d.ts +139 -139
- package/lib/speechrecorder/session/controlpanel.d.ts +109 -107
- package/lib/speechrecorder/session/item.d.ts +12 -12
- package/lib/speechrecorder/session/progress.d.ts +17 -17
- package/lib/speechrecorder/session/prompting.d.ts +121 -121
- package/lib/speechrecorder/session/recorder_combi_pane.d.ts +32 -32
- package/lib/speechrecorder/session/recording_file_cache.d.ts +33 -33
- package/lib/speechrecorder/session/recording_list.d.ts +24 -24
- package/lib/speechrecorder/session/recordingfile/recording-file-meta.component.d.ts +15 -15
- package/lib/speechrecorder/session/recordingfile/recording-file-navi.component.d.ts +20 -20
- package/lib/speechrecorder/session/recordingfile/recording-file-u-i.component.d.ts +31 -31
- package/lib/speechrecorder/session/recordingfile/recording-file-view.component.d.ts +55 -55
- package/lib/speechrecorder/session/recordingfile/recording-file.d.ts +7 -7
- package/lib/speechrecorder/session/recordingfile/recordingfile-service.d.ts +24 -24
- package/lib/speechrecorder/session/session.d.ts +15 -15
- package/lib/speechrecorder/session/session.service.d.ts +20 -20
- package/lib/speechrecorder/session/session_finished_dialog.d.ts +10 -10
- package/lib/speechrecorder/session/sessionmanager.d.ts +129 -123
- package/lib/speechrecorder/session/warning_bar.d.ts +8 -8
- package/lib/speechrecorder/spruploader.d.ts +11 -11
- package/lib/speechrecorder/startstopsignal/startstopsignal.d.ts +10 -10
- package/lib/speechrecorder/startstopsignal/ui/simpletrafficlight.d.ts +11 -11
- package/lib/speechrecorderng.component.d.ts +61 -61
- package/lib/speechrecorderng.module.d.ts +56 -56
- package/lib/spr.config.d.ts +17 -17
- package/lib/spr.module.version.d.ts +1 -1
- package/lib/ui/canvas_layer_comp.d.ts +13 -13
- package/lib/ui/message_dialog.d.ts +10 -10
- package/lib/ui/recordingitem_display.d.ts +84 -84
- package/lib/ui/responsive_component.d.ts +9 -9
- package/lib/utils/scrollIntoViewToBottom.d.ts +9 -9
- package/lib/utils/ua-parser.d.ts +43 -43
- package/lib/utils/utils.d.ts +19 -19
- package/lib/utils/wake_lock.d.ts +23 -23
- package/lib/utils/wake_lock_media.d.ts +1 -1
- package/package.json +17 -23
- package/public-api.d.ts +33 -33
- package/esm2020/lib/action/action.mjs +0 -73
- package/esm2020/lib/audio/audio_data_holder.mjs +0 -264
- package/esm2020/lib/audio/capture/capture.mjs +0 -855
- package/esm2020/lib/audio/context.mjs +0 -79
- package/esm2020/lib/audio/impl/wavformat.mjs +0 -6
- package/esm2020/lib/audio/impl/wavwriter.mjs +0 -105
- package/esm2020/lib/audio/inddb_audio_buffer.mjs +0 -508
- package/esm2020/lib/audio/net_audio_buffer.mjs +0 -293
- package/esm2020/lib/audio/playback/audio_source_node.mjs +0 -18
- package/esm2020/lib/audio/playback/audio_source_worklet_module_loader.mjs +0 -167
- package/esm2020/lib/audio/playback/player.mjs +0 -402
- package/esm2020/lib/audio/ui/audio_canvas_layer_comp.mjs +0 -347
- package/esm2020/lib/audio/ui/container.mjs +0 -414
- package/esm2020/lib/audio/ui/scroll_pane_horizontal.mjs +0 -11
- package/esm2020/lib/audio/ui/sonagram.mjs +0 -900
- package/esm2020/lib/db/inddb.mjs +0 -120
- package/esm2020/lib/dsp/utils.mjs +0 -48
- package/esm2020/lib/io/BinaryWriter.mjs +0 -74
- package/esm2020/lib/math/complex.mjs +0 -58
- package/esm2020/lib/net/uploader.mjs +0 -367
- package/esm2020/lib/speechrecorder/project/project.mjs +0 -49
- package/esm2020/lib/speechrecorder/project/project.service.mjs +0 -64
- package/esm2020/lib/speechrecorder/recordings/basic_recording.service.mjs +0 -216
- package/esm2020/lib/speechrecorder/recordings/recordings.service.mjs +0 -1014
- package/esm2020/lib/speechrecorder/session/audiorecorder.mjs +0 -1179
- package/esm2020/lib/speechrecorder/session/basicrecorder.mjs +0 -666
- package/esm2020/lib/speechrecorder/session/controlpanel.mjs +0 -409
- package/esm2020/lib/speechrecorder/session/recording_file_cache.mjs +0 -195
- package/esm2020/lib/speechrecorder/session/recordingfile/recordingfile-service.mjs +0 -288
- package/esm2020/lib/speechrecorder/session/session.service.mjs +0 -69
- package/esm2020/lib/speechrecorder/session/sessionmanager.mjs +0 -1333
- package/esm2020/lib/speechrecorder/startstopsignal/ui/simpletrafficlight.mjs +0 -57
- package/esm2020/lib/ui/recordingitem_display.mjs +0 -253
- package/esm2020/lib/utils/ua-parser.mjs +0 -190
- package/esm2020/lib/utils/utils.mjs +0 -105
- package/esm2020/lib/utils/wake_lock.mjs +0 -114
- package/fesm2015/speechrecorderng.mjs +0 -17708
- package/fesm2015/speechrecorderng.mjs.map +0 -1
- 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FwdHVyZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3NwZWVjaHJlY29yZGVybmcvc3JjL2xpYi9hdWRpby9jYXB0dXJlL2NhcHR1cmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFDLE9BQU8sRUFBRSxRQUFRLEVBQUUsZ0JBQWdCLEVBQUMsTUFBTSx1QkFBdUIsQ0FBQztBQUUxRSxPQUFPLEVBQUMsZ0JBQWdCLEVBQXlCLFFBQVEsSUFBSSxXQUFXLEVBQUMsTUFBTSxzQ0FBc0MsQ0FBQztBQUN0SCxPQUFPLEVBQUMsZ0JBQWdCLEVBQUMsTUFBTSx1QkFBdUIsQ0FBQztBQUN2RCxPQUFPLEVBQUMsSUFBSSxFQUFDLE1BQU0sbUJBQW1CLENBQUM7QUFDdkMsT0FBTyxFQUFDLG9CQUFvQixFQUErQixNQUFNLHVCQUF1QixDQUFDO0FBQ3pGLE9BQU8sRUFBQyxvQkFBb0IsRUFBQyxNQUFNLFlBQVksQ0FBQztBQUdoRCxNQUFNLENBQUMsTUFBTSwwQ0FBMEMsR0FBQyxLQUFLLENBQUM7QUFFOUQsTUFBTSxpQkFBaUIsR0FBQyxDQUFDLENBQUM7QUFFMUIsZ0NBQWdDO0FBQ2hDLHdEQUF3RDtBQUN4RCxNQUFNLE1BQU0sR0FBQyx5RUFBeUU7SUFDbEYsSUFBSTtJQUNKLDJCQUEyQjtJQUMzQiw4QkFBOEI7SUFDOUIscUVBQXFFO0lBQ3JFLG9CQUFvQjtJQUNwQixvQkFBb0I7SUFDcEIseUJBQXlCO0lBQ3pCLHVCQUF1QjtJQUN2QixvQkFBb0I7SUFDcEIsSUFBSTtJQUNKLFNBQVM7SUFDVCxJQUFJO0lBQ0osYUFBYTtJQUNiLGlCQUFpQjtJQUNqQixrQkFBa0I7SUFDbEIsb0JBQW9CO0lBQ3BCLFFBQVE7SUFDUixJQUFJO0lBQ0oscUNBQXFDO0lBQ3JDLDRCQUE0QjtJQUM1Qix3QkFBd0I7SUFDeEIsNkJBQTZCO0lBQzdCLDBCQUEwQjtJQUMxQixvQ0FBb0M7SUFDcEMsMENBQTBDO0lBQzFDLG9DQUFvQztJQUNwQyx5Q0FBeUM7SUFDekMsMkNBQTJDO0lBQzNDLHVEQUF1RDtJQUN2RCxjQUFjO0lBQ2QsVUFBVTtJQUNWLGlFQUFpRTtJQUNqRSxtREFBbUQ7SUFDbkQsK0JBQStCO0lBQy9CLDJEQUEyRDtJQUMzRCw0RUFBNEU7SUFDNUUsb0NBQW9DO0lBQ3BDLHVDQUF1QztJQUN2QyxjQUFjO0lBQ2QsVUFBVTtJQUNWLCtEQUErRDtJQUMvRCxpREFBaUQ7SUFDakQsbUNBQW1DO0lBQ25DLDZDQUE2QztJQUM3Qyx3REFBd0Q7SUFDeEQseURBQXlEO0lBQ3pELGNBQWM7SUFDZCxvQ0FBb0M7SUFDcEMsMkJBQTJCO0lBQzNCLG1DQUFtQztJQUNuQyxvQ0FBb0M7SUFDcEMscUJBQXFCO0lBQ3JCLHlDQUF5QztJQUN6QyxnQ0FBZ0M7SUFDaEMsbUNBQW1DO0lBQ25DLFVBQVU7SUFDVixJQUFJO0lBQ0osMENBQTBDO0lBQzFDLHdEQUF3RDtJQUN4RCxnQ0FBZ0M7SUFDaEMsMkNBQTJDO0lBQzNDLGdEQUFnRDtJQUNoRCwrREFBK0Q7SUFDL0QsY0FBYztJQUNkLHNDQUFzQztJQUN0QyxnREFBZ0Q7SUFDaEQsVUFBVTtJQUNWLFFBQVE7SUFDUixxQkFBcUI7SUFDckIsT0FBTztJQUNQLEtBQUs7SUFDTCxJQUFJO0lBQ0osOEVBQThFLENBQUM7QUFpQm5GLE1BQU0sT0FBTyxZQUFZO0lBRXZCLElBQUkseUJBQXlCO1FBQzNCLE9BQU8sSUFBSSxDQUFDLDBCQUEwQixDQUFDO0lBQ3pDLENBQUM7SUFFRCxJQUFJLHlCQUF5QixDQUFDLEtBQWE7UUFDekMsSUFBSSxDQUFDLDBCQUEwQixHQUFHLEtBQUssQ0FBQztJQUMxQyxDQUFDO0lBQ0QsSUFBSSxPQUFPLENBQUMsS0FBa0I7UUFDNUIsSUFBSSxDQUFDLFFBQVEsR0FBRyxLQUFLLENBQUM7SUFDeEIsQ0FBQztJQUNELElBQUksT0FBTztRQUNULE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQztJQUN2QixDQUFDO0lBQ0QsSUFBSSxnQkFBZ0I7UUFDbEIsT0FBTyxJQUFJLENBQUMsaUJBQWlCLENBQUM7SUFDaEMsQ0FBQztJQUVELElBQUksZ0JBQWdCLENBQUMsS0FBdUI7UUFDMUMsSUFBSSxDQUFDLGlCQUFpQixHQUFHLEtBQUssQ0FBQztJQUNqQyxDQUFDO0lBQ0QsSUFBSSw0QkFBNEI7UUFDOUIsT0FBTyxJQUFJLENBQUMsNkJBQTZCLENBQUM7SUFDNUMsQ0FBQztJQUVELElBQUksNEJBQTRCLENBQUMsS0FBMEM7UUFDekUsSUFBSSxDQUFDLDZCQUE2QixHQUFHLEtBQUssQ0FBQztJQUM3QyxDQUFDO0lBR0QsSUFBSSxNQUFNO1FBQ1IsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDO0lBQ3RCLENBQUM7YUFFTSxnQkFBVyxHQUFXLElBQUksQUFBZixDQUFnQjthQUNWLDJDQUFzQyxHQUFRLE9BQU8sR0FBQyxDQUFDLEFBQWpCLENBQWtCLEdBQUMsNEJBQTRCO2FBRTlGLHVDQUFrQyxHQUFDLEtBQUssQUFBTixDQUFPO0lBMEJ4RCx5Q0FBeUM7SUFFekM7UUE3QlEsK0JBQTBCLEdBQVEsWUFBWSxDQUFDLHNDQUFzQyxDQUFDO1FBRTlGLFlBQU8sR0FBb0IsSUFBSSxDQUFDO1FBR3hCLGFBQVEsR0FBYSxJQUFJLENBQUM7UUFFbEMsY0FBUyxHQUFjLElBQUksQ0FBQztRQUM1QixrQkFBYSxHQUFpQixJQUFJLENBQUM7UUFFbkMsU0FBSSxHQUFrQyxJQUFJLENBQUM7UUFHM0MsbUJBQWMsR0FBdUMsSUFBSSxDQUFDO1FBQ2xELHNCQUFpQixHQUFHLElBQUksQ0FBQztRQUN6QixZQUFPLEdBQUMsS0FBSyxDQUFDO1FBQ2QsY0FBUyxHQUFHLEtBQUssQ0FBQztRQUUxQixtQkFBYyxHQUFTLENBQUMsQ0FBQztRQUVqQixzQkFBaUIsR0FBa0IsZ0JBQWdCLENBQUMsVUFBVSxDQUFDO1FBQy9ELGtDQUE2QixHQUFtQyxJQUFJLENBQUM7UUFFckUsY0FBUyxHQUFDLElBQUksQ0FBQztRQUNmLGlCQUFZLEdBQVksSUFBSSxDQUFDO1FBQzdCLHFCQUFnQixHQUEyQixJQUFJLENBQUM7UUFLdEQsSUFBSSxDQUFDLENBQUMsR0FBRyxTQUFTLENBQUM7SUFDckIsQ0FBQztJQUVPLGFBQWE7UUFDbkIsSUFBRyxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUM7WUFDZixJQUFJLENBQUMsT0FBTyxHQUFDLG9CQUFvQixDQUFDLG9CQUFvQixFQUFFLENBQUM7WUFDekQsSUFBRyxJQUFJLENBQUMsT0FBTyxFQUFFO2dCQUNmLElBQUksQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsYUFBYSxFQUFFLEdBQUcsRUFBRTtvQkFDaEQsSUFBSSxJQUFJLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxLQUFLLFNBQVMsRUFBRTt3QkFDcEQsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO3FCQUNkO2dCQUNILENBQUMsQ0FBQyxDQUFDO2FBQ0o7U0FDRjtRQUNELE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQztJQUN0QixDQUFDO0lBRU8sUUFBUTtRQUNkLElBQUcsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFO1lBQ2pCLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1NBQ2pDO1FBQ0QsSUFBSSxDQUFDLFlBQVksR0FBQyxJQUFJLENBQUM7UUFDdkIsc0ZBQXNGO1FBQ3RGLElBQUcsZ0JBQWdCLENBQUMsVUFBVSxLQUFLLElBQUksQ0FBQyxpQkFBaUIsSUFBSSxJQUFJLENBQUMsNkJBQTZCLElBQUksSUFBSSxDQUFDLFFBQVEsRUFBRTtZQUNoSCxtREFBbUQ7WUFDbkQsSUFBSSxDQUFDLGdCQUFnQixHQUFHLElBQUksb0JBQW9CLENBQUMsSUFBSSxDQUFDLDZCQUE2QixFQUFFLElBQUksQ0FBQyxZQUFZLEVBQUMsSUFBSSxDQUFDLGlCQUFpQixFQUFDLFlBQVksQ0FBQyxXQUFXLEVBQUMsQ0FBQyxFQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQTtTQUN4SztRQUNELElBQUcsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLFdBQVcsS0FBSyxJQUFJLENBQUMsaUJBQWlCLENBQUMsRUFBRTtZQUM3RCwrREFBK0Q7WUFDL0QsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLEtBQUssRUFBdUIsQ0FBQztZQUM3QyxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDLEVBQUUsRUFBRTtnQkFDMUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxLQUFLLEVBQWdCLENBQUMsQ0FBQzthQUMzQztTQUNGO1FBQ0QsSUFBSSxDQUFDLGNBQWMsR0FBRyxDQUFDLENBQUM7SUFDMUIsQ0FBQztJQUVELFdBQVc7UUFDVCxTQUFTLENBQUMsWUFBWSxDQUFDLGdCQUFnQixFQUFFLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBb0IsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ2pHLENBQUM7SUFFTyxZQUFZO1FBQ2xCLHFDQUFxQztRQUNyQyx3QkFBd0I7UUFDeEIsSUFBSSxjQUFjLEdBQTJCLEVBQUMsS0FBSyxFQUMvQyxFQUFDLGVBQWUsRUFBRSxLQUFLLEVBQUM7U0FDM0IsQ0FBQztRQUNGLE9BQU8sU0FBUyxDQUFDLFlBQVksQ0FBQyxZQUFZLENBQUMsY0FBYyxDQUFDLENBQUM7SUFFN0QsQ0FBQztJQUdPLG9CQUFvQixDQUFDLFdBQXVCO1FBQ2hELElBQUksR0FBRyxHQUFHLFdBQVcsQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUNsQyxLQUFLLElBQUksS0FBSyxHQUFHLENBQUMsRUFBRSxLQUFLLEdBQUcsR0FBRyxDQUFDLE1BQU0sRUFBRSxLQUFLLEVBQUUsRUFBRTtZQUMvQyxzREFBc0Q7WUFDdEQsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO1NBQ25CO0lBQ0wsQ0FBQztJQUVELFdBQVcsQ0FBQyxFQUFrRCxFQUFFLEtBQUssR0FBRyxJQUFJLEVBQUMsV0FBd0I7UUFFbkcsU0FBUyxDQUFDLFlBQVksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQW9CLEVBQUUsRUFBRTtZQUN0RSxJQUFJLGVBQWUsR0FBRyxLQUFLLENBQUM7WUFDNUIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUU7Z0JBQ2pDLElBQUksRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDZCxJQUFJLEVBQUUsQ0FBQyxLQUFLLEVBQUU7b0JBQ1osZUFBZSxHQUFHLElBQUksQ0FBQztpQkFDeEI7YUFDRjtZQUNELElBQUksQ0FBQyxlQUFlLEVBQUU7Z0JBQ3BCLHVEQUF1RDtnQkFDdkQsSUFBSSxLQUFLLEVBQUU7b0JBQ1AsT0FBTyxDQUFDLElBQUksQ0FBQyx3REFBd0QsQ0FBQyxDQUFBO29CQUV0RSxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBYyxFQUFFLEVBQUU7d0JBQzVDLDBCQUEwQjt3QkFFMUIsSUFBRyxDQUFDLEVBQUU7NEJBQ0osd0RBQXdEO3lCQUN6RDs2QkFBSTs0QkFDSCxrQ0FBa0M7eUJBQ25DO3dCQUNELG9CQUFvQjt3QkFDcEIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxFQUFFLEVBQUUsS0FBSyxFQUFDLENBQUMsQ0FBQyxDQUFDO29CQUNoQyxDQUFDLEVBQUMsTUFBTSxDQUFDLEVBQUU7d0JBQ1QsMENBQTBDO3dCQUMxQyxzQkFBc0I7d0JBQ3RCLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQztvQkFDWCxDQUFDLENBQUMsQ0FBQztpQkFDSjtxQkFBTTtvQkFDTCxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUM7aUJBQ1Y7YUFDRjtpQkFBTTtnQkFDTCxVQUFVO2dCQUNWLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQzthQUNQO1lBQ0QsSUFBRyxXQUFXLEVBQUM7Z0JBQ2IsSUFBSSxDQUFDLG9CQUFvQixDQUFDLFdBQVcsQ0FBQyxDQUFDO2FBQ3hDO1FBQ0gsQ0FBQyxFQUFDLENBQUMsTUFBTSxFQUFDLEVBQUU7WUFDVixVQUFVO1lBQ1YscURBQXFEO1lBQ3JELElBQUksS0FBSyxFQUFFO2dCQUNULHlFQUF5RTtnQkFDekUsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQWMsRUFBRSxFQUFFO29CQUMxQywwQkFBMEI7b0JBQzFCLGlDQUFpQztvQkFDakMsSUFBRyxDQUFDLEVBQUU7d0JBQ0osd0RBQXdEO3FCQUN6RDt5QkFBSTt3QkFDSCxrQ0FBa0M7cUJBQ25DO29CQUNELG9CQUFvQjtvQkFDcEIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxFQUFFLEVBQUUsS0FBSyxFQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNoQyxDQUFDLEVBQUUsTUFBTSxDQUFDLEVBQUU7b0JBQ1YsMENBQTBDO29CQUMxQyxzQkFBc0I7b0JBQ3RCLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDWCxDQUFDLENBQUMsQ0FBQzthQUNKO2lCQUFNO2dCQUNMLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQzthQUNWO1lBQ0QsSUFBRyxXQUFXLEVBQUM7Z0JBQ2IsSUFBSSxDQUFDLG9CQUFvQixDQUFDLFdBQVcsQ0FBQyxDQUFDO2FBQ3hDO1FBQ0gsQ0FBQyxDQUFDLENBQUM7SUFJTCxDQUFDO0lBR0QsWUFBWSxDQUFDLENBQW9CO1FBQy9CLGtDQUFrQztRQUNsQyxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRTtZQUNqQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDZCxPQUFPLENBQUMsR0FBRyxDQUFDLG9CQUFvQixHQUFHLEVBQUUsQ0FBQyxRQUFRLEdBQUcsWUFBWSxHQUFHLEVBQUUsQ0FBQyxPQUFPLEdBQUcsVUFBVSxHQUFHLEVBQUUsQ0FBQyxLQUFLLEdBQUcsU0FBUyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUMzSDtJQUNILENBQUM7SUFFRCxxQkFBcUI7UUFDbkIsSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFO1lBRWxCLE1BQU0sR0FBRyxHQUFHLElBQUksZ0JBQWdCLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxxQkFBcUIsQ0FBQyxDQUFDO1lBQ3RFLEdBQUcsQ0FBQyxnQkFBZ0IsR0FBRyxDQUFDLEVBQVMsRUFBRSxFQUFFO2dCQUNuQyxJQUFJLEdBQUcsR0FBRyxlQUFlLENBQUM7Z0JBQzFCLElBQUksRUFBRSxZQUFZLFVBQVUsRUFBRTtvQkFDNUIsR0FBRyxHQUFHLEVBQUUsQ0FBQyxPQUFPLENBQUM7aUJBQ2xCO2dCQUNELE9BQU8sQ0FBQyxLQUFLLENBQUMsK0JBQStCLEdBQUcsR0FBRyxDQUFDLENBQUM7Z0JBQ3JELElBQUksSUFBSSxDQUFDLFFBQVEsRUFBRTtvQkFDakIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7aUJBQzFCO1lBQ0gsQ0FBQyxDQUFBO1lBQ0QsSUFBSSxLQUFLLEdBQUcsR0FBRyxDQUFDLElBQUksQ0FBQztZQUNyQixJQUFJLEtBQUssRUFBRTtnQkFDVCxLQUFLLENBQUMsU0FBUyxHQUFHLENBQUMsRUFBZ0IsRUFBRSxFQUFFO29CQUNyQyxJQUFJLElBQUksQ0FBQyxTQUFTLEVBQUU7d0JBQ2xCLElBQUksRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUM7d0JBQ2pCLElBQUksR0FBRyxHQUFHLEVBQUUsQ0FBQyxHQUFHLENBQUM7d0JBQ2pCLElBQUksTUFBTSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDO3dCQUM1QixJQUFJLGlCQUFpQixHQUFHLENBQUMsRUFBRTs0QkFDekIsT0FBTyxDQUFDLEtBQUssQ0FBQyw4QkFBOEIsR0FBRyxHQUFHLEdBQUcsR0FBRyxHQUFHLEVBQUUsQ0FBQyxHQUFHLEdBQUcsYUFBYSxHQUFHLE1BQU0sQ0FBQyxDQUFDO3lCQUM3Rjt3QkFDRCxJQUFJLEtBQUssR0FBRyxJQUFJLEtBQUssQ0FBZSxHQUFHLENBQUMsQ0FBQzt3QkFDekMsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLGNBQWMsR0FBRyxHQUFHLENBQUM7d0JBQzFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQywyQkFBMkIsS0FBSyxJQUFJLENBQUMsZ0JBQWdCLElBQUksZ0JBQWdCLENBQUMsNEJBQTRCLEtBQUssSUFBSSxDQUFDLGdCQUFnQixDQUFDLElBQUksSUFBSSxDQUFDLElBQUksSUFBSSxPQUFPLEdBQUcsSUFBSSxDQUFDLDBCQUEwQixFQUFFOzRCQUNqTixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQzt5QkFDbEI7d0JBRUQsd0RBQXdEO3dCQUN4RCxLQUFLLElBQUksRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLEdBQUcsR0FBRyxFQUFFLEVBQUUsRUFBRSxFQUFFOzRCQUMvQixvRkFBb0Y7NEJBQ3BGLElBQUksRUFBRSxHQUFHLElBQUksQ0FBQyxZQUFZLEVBQUU7Z0NBQzFCLElBQUksRUFBRSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsRUFBRTtvQ0FDZixJQUFJLEVBQUUsR0FBRyxJQUFJLFlBQVksQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7b0NBQ3ZDLElBQUksSUFBSSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxFQUFFO3dDQUM5QixJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztxQ0FDeEI7b0NBQ0QsS0FBSyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEVBQUUsQ0FBQztvQ0FDZixxREFBcUQ7b0NBQ3JELElBQUksRUFBRSxJQUFJLENBQUMsRUFBRTt3Q0FDWCxJQUFJLENBQUMsY0FBYyxJQUFJLEVBQUUsQ0FBQyxNQUFNLENBQUM7cUNBQ2xDO2lDQUNGOzZCQUNGO3lCQUNGO3dCQUNELElBQUksSUFBSSxDQUFDLGNBQWMsRUFBRTs0QkFDdkIsSUFBSTtnQ0FDRixJQUFJLENBQUMsY0FBYyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztnQ0FDakMsd0JBQXdCO2dDQUN4QiwyQkFBMkI7Z0NBQzNCLDZCQUE2QjtnQ0FDN0IsSUFBSTs2QkFDTDs0QkFBQyxPQUFPLEdBQUcsRUFBRTtnQ0FDWixJQUFJLEdBQUcsWUFBWSxLQUFLLEVBQUU7b0NBQ3hCLElBQUksQ0FBQyxZQUFZLEdBQUcsR0FBRyxDQUFDO2lDQUN6QjtxQ0FBTTtvQ0FDTCxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksS0FBSyxDQUFDLG9DQUFvQyxDQUFDLENBQUM7aUNBQ3JFO2dDQUVELE9BQU8sQ0FBQyxLQUFLLENBQUMsaUJBQWlCLEdBQUcsR0FBRyxDQUFDLENBQUM7Z0NBQ3ZDLElBQUk7b0NBQ0YsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO2lDQUNiO2dDQUFDLE9BQU8sSUFBSSxFQUFFO29DQUNiLE9BQU8sQ0FBQyxLQUFLLENBQUMsZ0NBQWdDLEdBQUcsSUFBSSxDQUFDLENBQUM7aUNBQ3hEO3dDQUFTO29DQUNSLElBQUksSUFBSSxDQUFDLFFBQVEsRUFBRTt3Q0FDakIsSUFBSSxPQUFPLEdBQUcsRUFBRSxDQUFDO3dDQUNqQixJQUFJLEdBQUcsWUFBWSxZQUFZLEVBQUU7NENBQy9CLE9BQU8sR0FBRyxJQUFJLEdBQUcsR0FBRyxDQUFDLElBQUksR0FBRyxJQUFJLEdBQUcsR0FBRyxDQUFDLE9BQU8sQ0FBQzt5Q0FDaEQ7d0NBQ0QsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsc0NBQXNDLEdBQUcsT0FBTyxFQUFFLDZCQUE2QixDQUFDLENBQUM7cUNBQ3RHO3lDQUFNO3dDQUNMLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztxQ0FDZDtpQ0FDRjs2QkFDRjt5QkFDRjt3QkFDRCxJQUFJLGdCQUFnQixDQUFDLFVBQVUsS0FBSyxJQUFJLENBQUMsaUJBQWlCLElBQUksSUFBSSxDQUFDLDZCQUE2QixFQUFFOzRCQUNoRyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7eUJBQ2Q7cUJBQ0Y7Z0JBQ0gsQ0FBQyxDQUFDO2FBQ0g7WUFDRCw4REFBOEQ7WUFDOUQsZ0JBQWdCO1lBQ2hCLHFDQUFxQztZQUNyQyxrQ0FBa0M7WUFDbEMsc0VBQXNFO1lBRXRFLElBQUksQ0FBQyxhQUFhLEdBQUcsR0FBRyxDQUFDO1lBQ3pCLG9EQUFvRDtZQUNwRCxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQztZQUNwQixJQUFJLElBQUksQ0FBQyxRQUFRLEVBQUU7Z0JBQ2pCLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxFQUFFLENBQUM7YUFDeEI7U0FDRjtJQUNELENBQUM7SUFFRCxJQUFJLENBQUMsWUFBb0IsRUFBRSxXQUEwQyxFQUFDLHNCQUFtRSxFQUFDLHFCQUE4QjtRQUN0SyxnRUFBZ0U7UUFDaEUsSUFBSSxDQUFDLE9BQU8sR0FBQyxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDbEMsSUFBRyxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUM7WUFDZixNQUFNLElBQUksS0FBSyxDQUFDLDhCQUE4QixDQUFDLENBQUM7U0FDakQ7UUFDRCxJQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxLQUFHLFdBQVcsRUFBQztZQUNsQyxnREFBZ0Q7WUFDaEQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxJQUFJLENBQUMsR0FBRSxFQUFFO2dCQUM3Qiw4RUFBOEU7Z0JBQzlFLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUFFLFdBQVcsRUFBRSxzQkFBc0IsRUFBQyxxQkFBcUIsQ0FBQyxDQUFDO1lBQ3RGLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsRUFBQyxFQUFFO2dCQUNkLE9BQU8sQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDO2dCQUMzQixNQUFNLEdBQUcsQ0FBQztZQUNaLENBQUMsQ0FBQyxDQUFBO1NBQ0g7YUFBSyxJQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxLQUFHLFFBQVEsRUFBRTtZQUNwQyxNQUFNLEdBQUcsR0FBQyw4REFBOEQsQ0FBQztZQUN6RSxPQUFPLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ25CLE1BQU0sSUFBSSxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7U0FDeEI7YUFBSztZQUNKLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUFFLFdBQVcsRUFBRSxzQkFBc0IsRUFBQyxxQkFBcUIsQ0FBQyxDQUFDO1NBQ3JGO0lBRUgsQ0FBQztJQUVELEtBQUssQ0FBQyxZQUFvQixFQUFFLFdBQTBDLEVBQUMsc0JBQW1FLEVBQUMscUJBQThCO1FBQ3ZLLElBQUksQ0FBQyxZQUFZLEdBQUcsWUFBWSxDQUFDO1FBQ2pDLElBQUksQ0FBQyxjQUFjLEdBQUcsQ0FBQyxDQUFDO1FBRXhCLElBQUksQ0FBQyxPQUFPLEdBQUMsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBRWxDLElBQUcsQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFDO1lBQ2YsTUFBTSxJQUFJLEtBQUssQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO1NBQ2pEO1FBRUQsb0NBQW9DO1FBQ3BDLGNBQWM7UUFDZCxvQkFBb0I7UUFDcEIsbUJBQW1CO1FBRW5CLHNEQUFzRDtRQUN0RCx3SEFBd0g7UUFFeEgsK0RBQStEO1FBQy9ELDJEQUEyRDtRQUMzRCxxR0FBcUc7UUFFckcsd0RBQXdEO1FBQ3hELG9DQUFvQztRQUVwQyxJQUFJLEdBQTBCLENBQUM7UUFDL0IsT0FBTyxDQUFDLElBQUksQ0FBQyxjQUFjLEdBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBRy9DLElBQUksRUFBRSxHQUFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxDQUFDO1FBRXBDLCtCQUErQjtRQUMvQiw0Q0FBNEM7UUFDNUMsS0FBSztRQUVOLElBQUksTUFBTSxHQUE0QixJQUFJLENBQUM7UUFFNUMsSUFBSSxlQUFlLEdBQUMsS0FBSyxDQUFDO1FBQzFCLElBQUksc0JBQXNCLEdBQUMsS0FBSyxDQUFDO1FBQ2pDLElBQUcsc0JBQXNCLEVBQUM7WUFDeEIsS0FBSSxJQUFJLElBQUksSUFBSSxzQkFBc0IsRUFBQztnQkFDckMsSUFBRyxJQUFJLENBQUMsUUFBUSxLQUFHLFdBQVcsQ0FBQyxPQUFPLElBQUksRUFBRSxDQUFDLGdCQUFnQixLQUFHLFFBQVEsQ0FBQyxPQUFPLEVBQUM7b0JBQzdFLE1BQU0sR0FBQyxJQUFJLENBQUM7b0JBQ1osTUFBTTtpQkFDVDtnQkFDRCxJQUFHLElBQUksQ0FBQyxRQUFRLEtBQUcsV0FBVyxDQUFDLE9BQU8sSUFBSSxFQUFFLENBQUMsZ0JBQWdCLEtBQUcsUUFBUSxDQUFDLE9BQU8sRUFBQztvQkFDL0UsTUFBTSxHQUFDLElBQUksQ0FBQztvQkFDWixNQUFNO2lCQUNQO2FBQ0Y7WUFDRCxJQUFHLE1BQU0sRUFBQztnQkFDUixrQ0FBa0M7Z0JBQ2xDLGVBQWUsR0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDO2dCQUM3QixJQUFHLDBDQUEwQyxFQUFDO29CQUM1QyxzQkFBc0IsR0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDO2lCQUNyQztnQkFDRCw2QkFBNkI7Z0JBQzdCLElBQUksQ0FBQyxTQUFTLEdBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQzthQUM3QjtpQkFBSTtnQkFDSCxJQUFJLENBQUMsU0FBUyxHQUFDLEtBQUssQ0FBQzthQUN0QjtTQUNGO1FBRUQsVUFBVTtRQUNWLEdBQUcsR0FBRztZQUNKLEtBQUssRUFBRTtnQkFDTCxRQUFRLEVBQUUsV0FBVztnQkFDckIsZ0JBQWdCLEVBQUUsS0FBSztnQkFDdkIsWUFBWSxFQUFFLFlBQVk7Z0JBQzFCLGVBQWUsRUFBRSxlQUFlO2FBQ2pDO1lBQ0QsS0FBSyxFQUFFLEtBQUs7U0FDYixDQUFDO1FBRUYsSUFBSSxFQUFFLENBQUMsZUFBZSxLQUFHLE9BQU8sQ0FBQyxJQUFJLEVBQUU7WUFFckMsd0NBQXdDO1lBQ3hDLDhDQUE4QztZQUM5QyxPQUFPLENBQUMsSUFBSSxDQUFDLHFEQUFxRCxDQUFDLENBQUM7WUFDcEUsR0FBRyxHQUFHO2dCQUNKLEtBQUssRUFBRTtvQkFDTCxRQUFRLEVBQUUsV0FBVztvQkFDckIsZ0JBQWdCLEVBQUUsS0FBSztvQkFDdkIsWUFBWSxFQUFFLFlBQVk7b0JBQzFCLGVBQWUsRUFBRSxlQUFlO2lCQUNqQztnQkFDRCxLQUFLLEVBQUUsS0FBSzthQUNiLENBQUM7U0FDSDthQUFNLElBQUksRUFBRSxDQUFDLGVBQWUsS0FBRyxPQUFPLENBQUMsTUFBTSxFQUFFO1lBQzlDLG9GQUFvRjtZQUNwRixPQUFPLENBQUMsSUFBSSxDQUFDLG9EQUFvRCxDQUFDLENBQUM7WUFFbkUsMEJBQTBCO1lBQzFCLHNEQUFzRDtZQUd0RCw4QkFBOEI7WUFDOUIsR0FBRyxHQUFHO2dCQUNKLEtBQUssRUFBRTtvQkFDTCxRQUFRLEVBQUUsV0FBVztvQkFDckIsWUFBWSxFQUFFLFlBQVk7b0JBQzFCLGdCQUFnQixFQUFFLEVBQUMsS0FBSyxFQUFDLHNCQUFzQixFQUFDO29CQUNoRCxlQUFlLEVBQUUsRUFBQyxLQUFLLEVBQUMsZUFBZSxFQUFDO29CQUN4QyxVQUFVLEVBQUMsRUFBQyxHQUFHLEVBQUUsRUFBRSxFQUFDO2lCQUNyQjtnQkFDRCxLQUFLLEVBQUUsS0FBSzthQUNiLENBQUE7U0FFRjthQUFNLElBQUksRUFBRSxDQUFDLGVBQWUsS0FBRyxPQUFPLENBQUMsT0FBTyxFQUFFO1lBQy9DLE9BQU8sQ0FBQyxJQUFJLENBQUMsc0RBQXNELENBQUMsQ0FBQztZQUNyRSxVQUFVO1lBQ1YsR0FBRyxHQUFHO2dCQUNKLEtBQUssRUFBRTtvQkFDSCxRQUFRLEVBQUUsV0FBVztvQkFDckIsWUFBWSxFQUFFLFlBQVk7b0JBQzVCLGdCQUFnQixFQUFFLEtBQUs7b0JBQ3JCLGVBQWUsRUFBRSxlQUFlO29CQUNsQyxnQkFBZ0IsRUFBRSxLQUFLO2lCQUN4QjtnQkFDRCxLQUFLLEVBQUUsS0FBSzthQUNiLENBQUE7U0FFRjthQUFNLElBQUksRUFBRSxDQUFDLGVBQWUsS0FBRyxPQUFPLENBQUMsTUFBTSxFQUFFO1lBQzlDLE9BQU8sQ0FBQyxJQUFJLENBQUMscURBQXFELENBQUMsQ0FBQTtZQUNuRSw0RUFBNEU7WUFFNUUsSUFBSSxDQUFDLGlCQUFpQixHQUFHLElBQUksQ0FBQztZQUU5QixHQUFHLEdBQUc7Z0JBQ0osS0FBSyxFQUFFO29CQUNMLFFBQVEsRUFBRSxXQUFXO29CQUNyQixZQUFZLEVBQUUsWUFBWTtvQkFDMUIsZ0JBQWdCLEVBQUUscUJBQXFCLENBQUEsQ0FBQyxDQUFBLFNBQVMsQ0FBQSxDQUFDLENBQUEsS0FBSztpQkFDeEQ7Z0JBQ0QsS0FBSyxFQUFFLEtBQUs7YUFDYixDQUFBO1NBRUY7YUFBTTtZQUVMLDBEQUEwRDtTQUMzRDtRQUlELE9BQU8sQ0FBQyxLQUFLLENBQUMsc0JBQXNCLEdBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFBO1FBRXBELElBQUksR0FBRyxHQUFHLFNBQVMsQ0FBQyxZQUFZLENBQUMsWUFBWSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ25ELEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRTtZQUViLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRTtnQkFDbEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7Z0JBRWhCLElBQUksT0FBTyxHQUFHLENBQUMsQ0FBQyxjQUFjLEVBQUUsQ0FBQztnQkFFakMsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUU7b0JBQ3ZDLElBQUksTUFBTSxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQztvQkFFeEIsT0FBTyxDQUFDLElBQUksQ0FBQyx3QkFBd0IsR0FBRyxNQUFNLENBQUMsRUFBRSxHQUFHLFNBQVMsR0FBRyxNQUFNLENBQUMsSUFBSSxHQUFHLFlBQVksR0FBRyxNQUFNLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxDQUFDO29CQUNsSCxJQUFJLE1BQU0sR0FBRyxNQUFNLENBQUMsV0FBVyxFQUFFLENBQUM7b0JBRWxDLG1FQUFtRTtvQkFDbkUsdUhBQXVIO29CQUN2SCxZQUFZO29CQUNaLE9BQU8sQ0FBQyxJQUFJLENBQUMsZ0NBQWdDLEdBQUcsTUFBTSxDQUFDLFlBQVksR0FBRyxTQUFTLEdBQUcsTUFBTSxDQUFDLGVBQWUsR0FBRyxtQkFBbUIsR0FBRyxNQUFNLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztvQkFDMUosSUFBSSxNQUFNLENBQUMsZUFBZSxFQUFFO3dCQUMxQixJQUFJLENBQUMsU0FBUyxHQUFHLE1BQU0sQ0FBQyxlQUFlLENBQUM7cUJBQ3pDO29CQUVELE9BQU8sQ0FBQyxLQUFLLENBQUMscUJBQXFCLEdBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLENBQUM7aUJBRTlEO2dCQUVELElBQUksT0FBTyxHQUFHLENBQUMsQ0FBQyxjQUFjLEVBQUUsQ0FBQztnQkFDakMsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUU7b0JBQ3ZDLElBQUksTUFBTSxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQztvQkFDeEIsT0FBTyxDQUFDLElBQUksQ0FBQyx3QkFBd0IsR0FBRyxNQUFNLENBQUMsRUFBRSxHQUFHLFNBQVMsR0FBRyxNQUFNLENBQUMsSUFBSSxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7aUJBQzFHO2dCQUNELElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDM0Qsd0NBQXdDO2dCQUN4QyxJQUFJLGtCQUFrQixHQUFXLElBQUksQ0FBQyxXQUFXLENBQUMsWUFBWSxDQUFDO2dCQUMvRCxPQUFPLENBQUMsSUFBSSxDQUFDLHdCQUF3QixHQUFHLGtCQUFrQixDQUFDLENBQUM7Z0JBQzVELGVBQWU7Z0JBQ2YsdURBQXVEO2dCQUN2RCxJQUFJLENBQUMsaUJBQWlCLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUM7Z0JBQ2pELE9BQU8sQ0FBQyxJQUFJLENBQUMsK0JBQStCLEdBQUcsa0JBQWtCLEdBQUcsZUFBZSxHQUFHLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO2dCQUM5RyxJQUFJLElBQUksQ0FBQyxjQUFjLEVBQUU7b0JBQ3ZCLElBQUksQ0FBQyxjQUFjLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUM7aUJBQzFFO2dCQUNELDRDQUE0QztnQkFDNUMsRUFBRTtnQkFDRix1RkFBdUY7Z0JBQ3ZGLDBEQUEwRDtnQkFFMUQsa0JBQWtCO2dCQUNsQixpREFBaUQ7Z0JBRWpELGlCQUFpQjtnQkFDakIsd0ZBQXdGO2dCQUV4RixJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsWUFBWSxFQUFFO29CQUM3QixtRUFBbUU7b0JBQ25FLGdGQUFnRjtvQkFDaEYsc0JBQXNCO29CQUN0QixJQUFJLFlBQVksQ0FBQyxrQ0FBa0MsRUFBRTt3QkFDbkQseURBQXlEO3dCQUN6RCxJQUFJLENBQUMscUJBQXFCLEVBQUUsQ0FBQztxQkFDOUI7eUJBQU07d0JBRUwsc0NBQXNDO3dCQUN0QyxJQUFJLHNCQUFzQixHQUFHLElBQUksSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLEVBQUUsRUFBQyxJQUFJLEVBQUUsaUJBQWlCLEVBQUMsQ0FBQyxDQUFDO3dCQUUzRSxJQUFJLHlCQUF5QixHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsZUFBZSxDQUFDLHNCQUFzQixDQUFDLENBQUM7d0JBRW5GLElBQUksQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUFDLFNBQVMsQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUU7NEJBQ25FLFlBQVksQ0FBQyxrQ0FBa0MsR0FBRyxJQUFJLENBQUM7NEJBQ3ZELElBQUksQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO3dCQUMvQixDQUFDLENBQ0osQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFVLEVBQUUsRUFBRTs0QkFDckIsT0FBTyxDQUFDLEdBQUcsQ0FBQyx1QkFBdUIsR0FBRyxLQUFLLENBQUMsQ0FBQzt3QkFDL0MsQ0FBQyxDQUFDLENBQUM7cUJBQ0o7aUJBQ0Y7cUJBQU0sSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFO29CQUM3QyxrRUFBa0U7b0JBQ2xFLCtEQUErRDtvQkFDL0QsSUFBSSxtQkFBbUIsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLHFCQUFxQixDQUFDLFlBQVksQ0FBQyxXQUFXLEVBQUUsa0JBQWtCLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztvQkFDL0gsSUFBSSxDQUFDLGFBQWEsR0FBRyxtQkFBbUIsQ0FBQztvQkFDekMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO29CQUNWLElBQUksbUJBQW1CLENBQUMsY0FBYyxFQUFFO3dCQUN0QyxtQkFBbUIsQ0FBQyxjQUFjLEdBQUcsQ0FBQyxDQUF1QixFQUFFLEVBQUU7NEJBRS9ELElBQUksSUFBSSxDQUFDLFNBQVMsRUFBRTtnQ0FDbEIsSUFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDLFdBQVcsQ0FBQztnQ0FDN0IsMkNBQTJDO2dDQUMzQyxJQUFJLGNBQWMsR0FBRyxJQUFJLEtBQUssQ0FBZSxZQUFZLENBQUMsQ0FBQztnQ0FDM0QsS0FBSyxJQUFJLEVBQUUsR0FBVyxDQUFDLEVBQUUsRUFBRSxHQUFHLFlBQVksRUFBRSxFQUFFLEVBQUUsRUFBRTtvQ0FDaEQsSUFBSSxTQUFTLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyxFQUFFLENBQUMsQ0FBQztvQ0FDNUMsSUFBSSxhQUFhLEdBQUcsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztvQ0FDdkMsY0FBYyxDQUFDLEVBQUUsQ0FBQyxHQUFHLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7b0NBQzVDLElBQUksSUFBSSxDQUFDLElBQUksRUFBRTt3Q0FDYixJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsQ0FBQztxQ0FDbkM7b0NBQ0QsSUFBSSxpQkFBaUIsR0FBRyxDQUFDLEVBQUU7d0NBQ3pCLE9BQU8sQ0FBQyxLQUFLLENBQUMsVUFBVSxHQUFHLGFBQWEsQ0FBQyxNQUFNLEdBQUcsV0FBVyxDQUFDLENBQUM7cUNBQ2hFO29DQUNELElBQUksQ0FBQyxjQUFjLElBQUksYUFBYSxDQUFDLE1BQU0sQ0FBQztpQ0FDN0M7Z0NBQ0QsQ0FBQyxFQUFFLENBQUM7Z0NBQ0osSUFBSSxJQUFJLENBQUMsY0FBYyxFQUFFO29DQUN2QixJQUFJLENBQUMsY0FBYyxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsQ0FBQztpQ0FDM0M7NkJBQ0Y7d0JBQ0gsQ0FBQyxDQUFDO3dCQUNGLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDO3dCQUNwQixJQUFJLElBQUksQ0FBQyxRQUFRLEVBQUU7NEJBQ2pCLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxFQUFFLENBQUM7eUJBQ3hCO3FCQUNGO3lCQUFNO3dCQUNMLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLDhGQUE4RixDQUFDLENBQUM7cUJBQ3JIO2lCQUNGO3FCQUFNO29CQUNMLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLGdHQUFnRyxDQUFDLENBQUM7aUJBQ3ZIO2FBQ0Y7UUFDRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsRUFBRTtZQUNQLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLGVBQWUsR0FBRSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDM0MsSUFBSSxJQUFJLENBQUMsUUFBUSxFQUFFO2dCQUNqQixJQUFHLGlCQUFpQixLQUFLLENBQUMsQ0FBQyxJQUFJLEVBQUM7b0JBQzlCLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLHFDQUFxQyxFQUFDLDJGQUEyRixDQUFDLENBQUM7aUJBQ3hKO3FCQUFLLElBQUcsa0JBQWtCLEtBQUssQ0FBQyxDQUFDLElBQUksRUFBQztvQkFDckMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsd0NBQXdDLEVBQUMsZ0RBQWdELENBQUMsQ0FBQztpQkFDaEg7cUJBQUssSUFBRyxzQkFBc0IsS0FBSyxDQUFDLENBQUMsSUFBSSxFQUFDO29CQUN6QyxJQUFJLElBQUksR0FBQyxDQUFDLENBQUMsR0FBRyxDQUFBLENBQUMsQ0FBQSxDQUFDLENBQUMsR0FBRyxDQUFBLENBQUMsQ0FBQSw2Q0FBNkMsQ0FBQztvQkFDbkUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7aUJBQzNCO3FCQUFNO29CQUNMLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLENBQUM7aUJBQ3ZCO2FBQ0Y7UUFDSCxDQUFDLENBQ0osQ0FBQTtJQUNILENBQUM7SUFFTyxNQUFNO1FBQ1osSUFBRyxJQUFJLENBQUMsT0FBTyxFQUFFO1lBQ2YsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ2hCLElBQUksSUFBSSxDQUFDLGNBQWMsRUFBRTtnQkFDdkIsSUFBSSxDQUFDLGNBQWMsQ0FBQyxVQUFVLEVBQUUsQ0FBQTthQUNqQztZQUNELElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDO1lBQ3RCLElBQUksSUFBSSxDQUFDLGFBQWEsRUFBRTtnQkFDdEIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDO2dCQUM3QyxJQUFJLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxDQUFDO2FBQ3REO1lBQ0QsSUFBSSxJQUFJLENBQUMsUUFBUSxFQUFFO2dCQUNqQixJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sRUFBRSxDQUFDO2FBQ3pCO1NBQ0Y7SUFDSCxDQUFDO0lBRUQsS0FBSztRQUNILElBQUcsSUFBSSxDQUFDLE9BQU8sRUFBRTtZQUNmLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDO1lBQy9CLElBQUksR0FBRyxLQUFLLFNBQVMsRUFBRTtnQkFDckIsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO2FBQ2Y7aUJBQU07Z0JBQ0wsT0FBTyxDQUFDLEtBQUssQ0FBQyxtREFBbUQsR0FBRyxHQUFHLEdBQUcsZUFBZSxDQUFDLENBQUM7Z0JBQzNGLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRTtvQkFDOUIsT0FBTyxDQUFDLEtBQUssQ0FBQyxtREFBbUQsQ0FBQyxDQUFDO29CQUNuRSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7Z0JBQ2hCLENBQUMsQ0FBQyxDQUFBO2FBQ0g7U0FDRjtJQUNILENBQUM7SUFFRCxJQUFJO1FBRUYsSUFBSSxJQUFJLENBQUMsaUJBQWlCLElBQUksSUFBSSxDQUFDLGFBQWEsRUFBRTtZQUNoRCxJQUFJLENBQUMsV0FBVyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLENBQUM7WUFDaEQsSUFBRyxJQUFJLENBQUMsT0FBTyxFQUFFO2dCQUNmLElBQUksQ0FBQyxhQUFhLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLENBQUM7YUFDekQ7U0FDRjtRQUVELElBQUk7WUFDRixJQUFJLElBQUksQ0FBQyxjQUFjLEVBQUU7Z0JBQ3ZCLElBQUksQ0FBQyxjQUFjLENBQUMsS0FBSyxFQUFFLENBQUM7YUFDN0I7U0FDRjtRQUFBLE9BQU0sR0FBRyxFQUFDO1lBQ1QsT0FBTyxDQUFDLEtBQUssQ0FBQyxpQ0FBaUMsQ0FBQyxDQUFDO1lBQ2xELE1BQU0sR0FBRyxDQUFDO1NBQ1Y7Z0JBQVE7WUFDUCxJQUFJLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQztZQUN2QixJQUFJLElBQUksQ0FBQyxnQkFBZ0IsSUFBSSxJQUFJLENBQUMsWUFBWSxFQUFFO2dCQUM5QyxrR0FBa0c7Z0JBQ2xHLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO2dCQUN6QyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDO2FBQzlCO1lBQ0QsSUFBSSxJQUFJLENBQUMsUUFBUSxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVksSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUU7Z0JBQzFELGlEQUFpRDtnQkFDakQsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLEVBQUUsQ0FBQzthQUN6QjtTQUNGO0lBQ0gsQ0FBQztJQUdELEtBQUs7UUFDSiwrQkFBK0I7UUFDL0IsOEZBQThGO1FBQzlGLDBGQUEwRjtRQUMxRixFQUFFO1FBQ0YsYUFBYTtRQUNiLG1DQUFtQztRQUNuQyx3Q0FBd0M7UUFDeEMsd0JBQXdCO1FBQ3hCLG9FQUFvRTtRQUNwRSwyQkFBMkI7UUFDM0IsOERBQThEO1FBQzlELG1EQUFtRDtRQUNuRCxzQ0FBc0M7UUFDdEMsZ0VBQWdFO1FBQ2hFLDZFQUE2RTtRQUM3RSw2REFBNkQ7UUFDN0QsNkVBQTZFO1FBQzdFLGtDQUFrQztRQUNsQyxxRUFBcUU7UUFDckUsaUJBQWlCO1FBQ2pCLGdDQUFnQztRQUNoQywwRUFBMEU7UUFDMUUsaUJBQWlCO1FBQ2pCLGVBQWU7UUFDZiw0QkFBNEI7UUFDNUIsV0FBVztRQUNYLHVDQUF1QztRQUN2QyxFQUFFO1FBQ0YsaUNBQWlDO1FBQ2pDLDZFQUE2RTtRQUM3RSxXQUFXO1FBQ1gsaUNBQWlDO1FBQ2pDLG1IQUFtSDtRQUNuSCxFQUFFO1FBQ0YsNkVBQTZFO1FBQzdFLDREQUE0RDtRQUM1RCxxQ0FBcUM7UUFDckMseURBQXlEO1FBQ3pELGFBQWE7UUFDYixFQUFFO1FBQ0YsZ0NBQWdDO1FBQ2hDLGlEQUFpRDtRQUNqRCw0REFBNEQ7UUFDNUQsc0NBQXNDO1FBQ3RDLGFBQWE7UUFDYixXQUFXO1FBQ1gsMEJBQTBCO1FBQzFCLCtCQUErQjtRQUMvQixzQkFBc0I7UUFDdEIsdUJBQXVCO1FBQ3ZCLG1FQUFtRTtRQUNuRSxTQUFTO1FBQ1QsT0FBTztRQUVOLHFGQUFxRjtRQUNyRiwyS0FBMks7UUFDM0ssSUFBSTtRQUNKLElBQUcsSUFBSSxDQUFDLGdCQUFnQixJQUFJLElBQUksQ0FBQyxJQUFJLEVBQUM7WUFFcEMsbUJBQW1CO1lBQ25CLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsU0FBUyxDQUFDO2dCQUM1RCxRQUFRLEVBQUUsR0FBRyxFQUFFO29CQUNiLHVHQUF1RztvQkFFdkcsaUVBQWlFO29CQUNqRSxJQUFHLElBQUksQ0FBQyxJQUFJLEVBQUU7d0JBQ1osS0FBSyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLElBQUksQ0FBQyxZQUFZLEVBQUUsRUFBRSxFQUFFLEVBQUU7NEJBQzdDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDOzRCQUN4QiwyQ0FBMkM7eUJBQzVDO3FCQUNGO29CQUNELElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDO29CQUN0QixJQUFJLElBQUksQ0FBQyxRQUFRLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFO3dCQUNwQyw4Q0FBOEM7d0JBQzlDLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxFQUFFLENBQUM7cUJBQ3pCO2dCQUNILENBQUMsRUFBQyxLQUFLLEVBQUMsQ0FBQyxHQUFHLEVBQUMsRUFBRTtvQkFDYiwyQkFBMkI7b0JBQzNCLElBQUcsQ0FBQyxJQUFJLENBQUMsWUFBWSxFQUFFO3dCQUNyQixJQUFJLENBQUMsWUFBWSxHQUFHLEdBQUcsQ0FBQzt3QkFDeEIsT0FBTyxDQUFDLEtBQUssQ0FBQywrQkFBK0IsR0FBRyxHQUFHLENBQUMsQ0FBQztxQkFDdEQ7Z0JBQ0gsQ0FBQzthQUNGLENBQUMsQ0FBQztZQUNILElBQUksQ0FBQyxTQUFTLEdBQUMsS0FBSyxDQUFDO1NBQ3RCO0lBQ0gsQ0FBQztJQUdELEtBQUs7UUFDSCxJQUFHLElBQUksQ0FBQyxXQUFXLEVBQUU7WUFDbkIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxVQUFVLEVBQUUsQ0FBQztTQUMvQjtRQUNELElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRTtZQUNmLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDcEMsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUU7Z0JBQ25DLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQzthQUNmO1NBQ0Y7UUFDRCxJQUFJLENBQUMsT0FBTyxHQUFDLEtBQUssQ0FBQztJQUNyQixDQUFDO0lBRUQsV0FBVztRQUNULElBQUksRUFBRSxHQUFtQixJQUFJLENBQUM7UUFDOUIsSUFBRyxJQUFJLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxJQUFJLEVBQUU7WUFDNUIsSUFBSSxRQUFRLEdBQVcsQ0FBQyxDQUFDO1lBRXpCLElBQUksT0FBTyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFFM0IsS0FBSyxJQUFJLE1BQU0sSUFBSSxPQUFPLEVBQUU7Z0JBQzFCLFFBQVEsSUFBSSxNQUFNLENBQUMsTUFBTSxDQUFDO2FBQzNCO1lBRUQsSUFBSTtnQkFDRixFQUFFLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFlBQVksRUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQzthQUN0RjtZQUFDLE9BQU8sR0FBRyxFQUFFO2dCQUNaLElBQUksR0FBRyxZQUFZLFlBQVksRUFBRTtvQkFDL0IsSUFBSSxHQUFHLENBQUMsSUFBSSxLQUFLLG1CQUFtQixFQUFFO3dCQUNwQyxJQUFJLFFBQVEsSUFBSSxDQUFDLEVBQUU7NEJBQ2pCLDhDQUE4Qzs0QkFDOUMsc0NBQXNDOzRCQUN0QyxFQUFFLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQzt5QkFDL0U7NkJBQU07NEJBQ0wsTUFBTSxHQUFHLENBQUM7eUJBQ1g7cUJBQ0Y7eUJBQU07d0JBQ0wsTUFBTSxHQUFHLENBQUM7cUJBQ1g7aUJBQ0Y7cUJBQU0sSUFBSSxHQUFHLFlBQVksVUFBVSxFQUFFO29CQUNwQyxnQkFBZ0I7b0JBQ2hCLHFCQUFxQjtvQkFDckIsTUFBTSxHQUFHLENBQUM7aUJBQ1g7cUJBQU07b0JBQ0wsTUFBTSxHQUFHLENBQUM7aUJBQ1g7YUFDRjtZQUNELEtBQUssSUFBSSxFQUFFLEdBQUcsQ0FBQyxFQUFFLEVBQUUsR0FBRyxJQUFJLENBQUMsWUFBWSxFQUFFLEVBQUUsRUFBRSxFQUFFO2dCQUM3QyxJQUFJLEdBQUcsR0FBRyxFQUFFLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2dCQUNoQyxJQUFJLEdBQUcsR0FBRyxDQUFDLENBQUM7Z0JBQ1osS0FBSyxJQUFJLEtBQUssSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxFQUFFO29CQUMvQixJQUFJLE1BQU0sR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFDO29CQUMxQixHQUFHLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxHQUFHLENBQUMsQ0FBQztvQkFDcEIsR0FBRyxJQUFJLE1BQU0sQ0FBQztpQkFDZjthQUNGO1NBQ0Y7UUFDRCxPQUFPLEVBQUUsQ0FBQztJQUNaLENBQUM7SUFFRCxnQkFBZ0I7UUFDWixJQUFJLEtBQUssR0FBdUIsSUFBSSxDQUFDO1FBQ3JDLElBQUcsSUFBSSxDQUFDLElBQUksRUFBRTtZQUNaLEtBQUssR0FBQyxJQUFJLGdCQUFnQixDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsSUFBSSxDQUFDLGlCQUFpQixFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUNsRjtRQUNELE9BQU8sS0FBSyxDQUFDO0lBQ2pCLENBQUM7SUFFRCxxQkFBcUI7UUFDbkIsSUFBRyxJQUFJLENBQUMsWUFBWSxFQUFDO1lBQ25CLE9BQU8sSUFBSSxDQUFDO1NBQ2I7YUFBSztZQUNKLE9BQU8sSUFBSSxDQUFDLGdCQUFnQixDQUFDO1NBQzlCO0lBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7U2VxdWVuY2VBdWRpb0Zsb2F0MzJPdXRTdHJlYW19IGZyb20gXCIuLi9pby9zdHJlYW1cIjtcclxuaW1wb3J0IHtCcm93c2VyLCBQbGF0Zm9ybSwgVXNlckFnZW50QnVpbGRlcn0gZnJvbSBcIi4uLy4uL3V0aWxzL3VhLXBhcnNlclwiO1xyXG5cclxuaW1wb3J0IHtBdWRpb1N0b3JhZ2VUeXBlLCBBdXRvR2FpbkNvbnRyb2xDb25maWcsIFBsYXRmb3JtIGFzIENmZ1BsYXRmb3JtfSBmcm9tIFwiLi4vLi4vc3BlZWNocmVjb3JkZXIvcHJvamVjdC9wcm9qZWN0XCI7XHJcbmltcG9ydCB7QXJyYXlBdWRpb0J1ZmZlcn0gZnJvbSBcIi4uL2FycmF5X2F1ZGlvX2J1ZmZlclwiO1xyXG5pbXBvcnQge1VVSUR9IGZyb20gXCIuLi8uLi91dGlscy91dGlsc1wiO1xyXG5pbXBvcnQge0luZGV4ZWREYkF1ZGlvQnVmZmVyLCBQZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0fSBmcm9tIFwiLi4vaW5kZGJfYXVkaW9fYnVmZmVyXCI7XHJcbmltcG9ydCB7QXVkaW9Db250ZXh0UHJvdmlkZXJ9IGZyb20gXCIuLi9jb250ZXh0XCI7XHJcblxyXG5cclxuZXhwb3J0IGNvbnN0IENIUk9NRV9BQ1RJVkFURV9FQ0hPX0NBTkNFTExBVElPTl9XSVRIX0FHQz1mYWxzZTtcclxuXHJcbmNvbnN0IERFQlVHX1RSQUNFX0xFVkVMPTA7XHJcblxyXG4vLyBEaXJ0eSB3YXkgdG8gbG9hZCB0aGlzIG1vZHVsZVxyXG4vLyBDb3B5IGNvbnRlbnQgb2YgaW50ZXJjZXB0b3Jfd29ya2xldC5qcyB0byB0aGlzIHN0cmluZ1xyXG5jb25zdCBhd3BTdHI9XCJjbGFzcyBBdWRpb0NhcHR1cmVJbnRlcmNlcHRvclByb2Nlc3NvciBleHRlbmRzIEF1ZGlvV29ya2xldFByb2Nlc3NvcntcXG5cIiArXHJcbiAgICBcIlxcblwiICtcclxuICAgIFwiICAgIEJVRkZFUl9RVUFOVFVNUz02NDtcXG5cIiArXHJcbiAgICBcIiAgICBRVUFOVFVNX0ZSQU1FX0xFTj0xMjg7XFxuXCIgK1xyXG4gICAgXCIgICAgQlVGRkVSX0ZSQU1FX0xFTj10aGlzLlFVQU5UVU1fRlJBTUVfTEVOKnRoaXMuQlVGRkVSX1FVQU5UVU1TO1xcblwiICtcclxuICAgIFwiICAgIGJ1ZmZlcj1udWxsO1xcblwiICtcclxuICAgIFwiICAgIGJ1ZmZlclBvcz0wO1xcblwiICtcclxuICAgIFwiICAgIGJ1ZmZlclBvc0J5dGVzPTA7XFxuXCIgK1xyXG4gICAgXCIgICAgY29uc3RydWN0b3IoKSB7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgIHN1cGVyKCk7XFxuXCIgK1xyXG4gICAgXCJcXG5cIiArXHJcbiAgICBcIiAgICB9XFxuXCIgK1xyXG4gICAgXCJcXG5cIiArXHJcbiAgICBcIiBwcm9jZXNzKFxcblwiICtcclxuICAgIFwiICAgICAgaW5wdXRzLFxcblwiICtcclxuICAgIFwiICAgICAgb3V0cHV0cyxcXG5cIiArXHJcbiAgICBcIiAgICAgIHBhcmFtZXRlcnNcXG5cIiArXHJcbiAgICBcIiAgKXtcXG5cIiArXHJcbiAgICBcIlxcblwiICtcclxuICAgIFwiICAgICBsZXQgaW5wdXRzQ250PWlucHV0cy5sZW5ndGg7XFxuXCIgK1xyXG4gICAgXCIgICAgIGxldCBjaGFubmVsQ291bnQ9MDtcXG5cIiArXHJcbiAgICBcIiAgICAgbGV0IGlucHV0TGVuPTA7XFxuXCIgK1xyXG4gICAgXCIgICAgIGxldCBpbnB1dExlbkJ5dGVzPTA7XFxuXCIgK1xyXG4gICAgXCIgICAgIGlmKGlucHV0c0NudD4wKSB7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICBsZXQgaW5wdXQwID0gaW5wdXRzWzBdO1xcblwiICtcclxuICAgIFwiICAgICAgICAgY2hhbm5lbENvdW50ID0gaW5wdXQwLmxlbmd0aDtcXG5cIiArXHJcbiAgICBcIiAgICAgICAgIGlmIChjaGFubmVsQ291bnQgPiAwKSB7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICAgICAgbGV0IGlucHV0MGNoMD1pbnB1dDBbMF07XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICAgICAgaW5wdXRMZW49aW5wdXQwY2gwLmxlbmd0aDtcXG5cIiArXHJcbiAgICBcIiAgICAgICAgICAgICBpbnB1dExlbkJ5dGVzPWlucHV0MGNoMC5idWZmZXIubGVuZ3RoO1xcblwiICtcclxuICAgIFwiICAgICAgICAgfVxcblwiICtcclxuICAgIFwiICAgICB9XFxuXCIgK1xyXG4gICAgXCIgICAgIGlmICghdGhpcy5idWZmZXIgfHwgdGhpcy5idWZmZXIubGVuZ3RoIDwgY2hhbm5lbENvdW50KSB7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICB0aGlzLmJ1ZmZlciA9IG5ldyBBcnJheShjaGFubmVsQ291bnQpO1xcblwiICtcclxuICAgIFwiICAgICAgICAgdGhpcy5idWZmZXJQb3MgPSAwXFxuXCIgK1xyXG4gICAgXCIgICAgICAgICBmb3IgKGxldCBiY2ggPSAwOyBiY2ggPCBjaGFubmVsQ291bnQ7IGJjaCsrKSB7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICAgICAgdGhpcy5idWZmZXJbYmNoXSA9IG5ldyBGbG9hdDMyQXJyYXkodGhpcy5CVUZGRVJfRlJBTUVfTEVOKTtcXG5cIiArXHJcbiAgICBcIiAgICAgICAgICAgICB0aGlzLmJ1ZmZlclBvcyA9IDA7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICAgICAgdGhpcy5idWZmZXJQb3NCeXRlcz0wO1xcblwiICtcclxuICAgIFwiICAgICAgICAgfVxcblwiICtcclxuICAgIFwiICAgICB9XFxuXCIgK1xyXG4gICAgXCIgICAgIGxldCBidWZBdmFpbCA9IHRoaXMuQlVGRkVSX0ZSQU1FX0xFTiAtIHRoaXMuYnVmZmVyUG9zO1xcblwiICtcclxuICAgIFwiICAgICAvLyBjaGVjayBpZiBidWZmZXIgaGFzIHRvIGJlIHRyYW5zZmVycmVkXFxuXCIgK1xyXG4gICAgXCIgICAgIGlmIChpbnB1dExlbiA+IGJ1ZkF2YWlsKSB7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICBsZXQgYWRhPW5ldyBBcnJheShjaGFubmVsQ291bnQpO1xcblwiICtcclxuICAgIFwiICAgICAgICAgZm9yIChsZXQgY2ggPSAwOyBjaCA8IGNoYW5uZWxDb3VudDsgY2grKykge1xcblwiICtcclxuICAgIFwiICAgICAgICAgICAgIGFkYVtjaF09dGhpcy5idWZmZXJbY2hdLmJ1ZmZlci5zbGljZSgwKTtcXG5cIiArXHJcbiAgICBcIiAgICAgICAgIH1cXG5cIiArXHJcbiAgICBcIiAgICAgICAgIHRoaXMucG9ydC5wb3N0TWVzc2FnZSh7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICAgICAgZGF0YTogYWRhLFxcblwiICtcclxuICAgIFwiICAgICAgICAgICAgIGNoczogY2hhbm5lbENvdW50LFxcblwiICtcclxuICAgIFwiICAgICAgICAgICAgIGxlbjogdGhpcy5idWZmZXJQb3NcXG5cIiArXHJcbiAgICBcIiAgICAgICAgIH0sIGFkYSk7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICAvLyBidWZmZXIgdHJhbnNmZXJyZWQsIHJlc2V0XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICB0aGlzLmJ1ZmZlclBvcyA9IDA7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICB0aGlzLmJ1ZmZlclBvc0J5dGVzPTA7XFxuXCIgK1xyXG4gICAgXCIgICAgIH1cXG5cIiArXHJcbiAgICBcIlxcblwiICtcclxuICAgIFwiICAgICBmb3IobGV0IGlpPTA7aWk8aW5wdXRzQ250O2lpKyspIHtcXG5cIiArXHJcbiAgICBcIiAgICAgICAgIGZvciAobGV0IGNoID0gMDsgY2ggPCBjaGFubmVsQ291bnQ7IGNoKyspIHtcXG5cIiArXHJcbiAgICBcIiAgICAgICAgICAgICAvLyBNdXRlIG91dHB1dHNcXG5cIiArXHJcbiAgICBcIiAgICAgICAgICAgICAvL291dHB1dHNbaWldW2NoXS5maWxsKDApO1xcblwiICtcclxuICAgIFwiICAgICAgICAgICAgIGxldCBjaFNhbXBsZXMgPSBpbnB1dHNbaWldW2NoXTtcXG5cIiArXHJcbiAgICBcIiAgICAgICAgICAgICB0aGlzLmJ1ZmZlcltjaF0uc2V0KGNoU2FtcGxlcyx0aGlzLmJ1ZmZlclBvcyk7XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICB9XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICB0aGlzLmJ1ZmZlclBvcys9aW5wdXRMZW47XFxuXCIgK1xyXG4gICAgXCIgICAgICAgICB0aGlzLmJ1ZmZlclBvc0J5dGVzKz1pbnB1dExlbkJ5dGVzO1xcblwiICtcclxuICAgIFwiICAgICB9XFxuXCIgK1xyXG4gICAgXCIgICAgXFxuXCIgK1xyXG4gICAgXCIgICAgIHJldHVybiB0cnVlO1xcblwiICtcclxuICAgIFwiICB9XFxuXCIgK1xyXG4gICAgXCJ9XFxuXCIgK1xyXG4gICAgXCJcXG5cIiArXHJcbiAgICBcInJlZ2lzdGVyUHJvY2Vzc29yKCdjYXB0dXJlLWludGVyY2VwdG9yJyxBdWRpb0NhcHR1cmVJbnRlcmNlcHRvclByb2Nlc3Nvcik7XFxuXCI7XHJcblxyXG5cclxuXHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIEF1ZGlvQ2FwdHVyZUxpc3RlbmVyIHtcclxuICBvcGVuZWQoKTogdm9pZDtcclxuXHJcbiAgc3RhcnRlZCgpOiB2b2lkO1xyXG5cclxuICBzdG9wcGVkKCk6IHZvaWQ7XHJcblxyXG4gIGNsb3NlZCgpOiB2b2lkO1xyXG5cclxuICBlcnJvcihtc2c/OnN0cmluZyxhZHZpY2U/OnN0cmluZyk6IHZvaWQ7XHJcbn1cclxuXHJcbmV4cG9ydCBjbGFzcyBBdWRpb0NhcHR1cmUge1xyXG5cclxuICBnZXQgbWF4QXV0b05ldE1lbVN0b3JlU2FtcGxlcygpOiBudW1iZXIge1xyXG4gICAgcmV0dXJuIHRoaXMuX21heEF1dG9OZXRNZW1TdG9yZVNhbXBsZXM7XHJcbiAgfVxyXG5cclxuICBzZXQgbWF4QXV0b05ldE1lbVN0b3JlU2FtcGxlcyh2YWx1ZTogbnVtYmVyKSB7XHJcbiAgICB0aGlzLl9tYXhBdXRvTmV0TWVtU3RvcmVTYW1wbGVzID0gdmFsdWU7XHJcbiAgfVxyXG4gIHNldCByZWNVVUlEKHZhbHVlOiBzdHJpbmd8bnVsbCkge1xyXG4gICAgdGhpcy5fcmVjVVVJRCA9IHZhbHVlO1xyXG4gIH1cclxuICBnZXQgcmVjVVVJRCgpOiBzdHJpbmcgfG51bGx7XHJcbiAgICByZXR1cm4gdGhpcy5fcmVjVVVJRDtcclxuICB9XHJcbiAgZ2V0IGF1ZGlvU3RvcmFnZVR5cGUoKTogQXVkaW9TdG9yYWdlVHlwZSB7XHJcbiAgICByZXR1cm4gdGhpcy5fYXVkaW9TdG9yYWdlVHlwZTtcclxuICB9XHJcblxyXG4gIHNldCBhdWRpb1N0b3JhZ2VUeXBlKHZhbHVlOiBBdWRpb1N0b3JhZ2VUeXBlKSB7XHJcbiAgICB0aGlzLl9hdWRpb1N0b3JhZ2VUeXBlID0gdmFsdWU7XHJcbiAgfVxyXG4gIGdldCBwZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0KCk6IFBlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQgfCBudWxsIHtcclxuICAgIHJldHVybiB0aGlzLl9wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0O1xyXG4gIH1cclxuXHJcbiAgc2V0IHBlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQodmFsdWU6IFBlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQgfCBudWxsKSB7XHJcbiAgICB0aGlzLl9wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0ID0gdmFsdWU7XHJcbiAgfVxyXG5cclxuXHJcbiAgZ2V0IG9wZW5lZCgpOiBib29sZWFuIHtcclxuICAgIHJldHVybiB0aGlzLl9vcGVuZWQ7XHJcbiAgfVxyXG5cclxuICBzdGF0aWMgQlVGRkVSX1NJWkU6IG51bWJlciA9IDgxOTI7XHJcbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgREVGQVVMVF9NQVhfTkVUX0FVVE9fTUVNX1NUT1JFX1NBTVBMRVM6bnVtYmVyPTI4ODAwMDAqNTsgLy8gRGVmYXVsdCA1IG1pbnV0ZSBhdCA0OGtIelxyXG4gIHByaXZhdGUgX21heEF1dG9OZXRNZW1TdG9yZVNhbXBsZXM6bnVtYmVyPUF1ZGlvQ2FwdHVyZS5ERUZBVUxUX01BWF9ORVRfQVVUT19NRU1fU1RPUkVfU0FNUExFUztcclxuICBwcml2YXRlIHN0YXRpYyBjYXB0dXJlSW50ZXJjZXB0b3JNb2R1bGVSZWdpc3RlcmVkPWZhbHNlO1xyXG4gIGNvbnRleHQ6IEF1ZGlvQ29udGV4dHxudWxsPW51bGw7XHJcbiAgc3RyZWFtITogTWVkaWFTdHJlYW07XHJcbiAgY2hhbm5lbENvdW50ITogbnVtYmVyO1xyXG4gIHByaXZhdGUgX3JlY1VVSUQ6c3RyaW5nfG51bGw9bnVsbDtcclxuICBtZWRpYVN0cmVhbTogYW55O1xyXG4gIGFnY1N0YXR1czpib29sZWFufG51bGw9bnVsbDtcclxuICBidWZmZXJpbmdOb2RlOiBBdWRpb05vZGV8bnVsbD1udWxsO1xyXG4gIGxpc3RlbmVyITogQXVkaW9DYXB0dXJlTGlzdGVuZXI7XHJcbiAgZGF0YTogQXJyYXk8QXJyYXk8RmxvYXQzMkFycmF5Pj58bnVsbD1udWxsO1xyXG4gIGN1cnJlbnRTYW1wbGVSYXRlITogbnVtYmVyO1xyXG4gIG46IE5hdmlnYXRvcjtcclxuICBhdWRpb091dFN0cmVhbTogU2VxdWVuY2VBdWRpb0Zsb2F0MzJPdXRTdHJlYW0gfCBudWxsPW51bGw7XHJcbiAgcHJpdmF0ZSBkaXNjb25uZWN0U3RyZWFtcyA9IHRydWU7XHJcbiAgcHJpdmF0ZSBfb3BlbmVkPWZhbHNlO1xyXG4gIHByaXZhdGUgY2FwdHVyaW5nID0gZmFsc2U7XHJcblxyXG4gIGZyYW1lc1JlY29yZGVkOiBudW1iZXI9MDtcclxuXHJcbiAgcHJpdmF0ZSBfYXVkaW9TdG9yYWdlVHlwZTpBdWRpb1N0b3JhZ2VUeXBlPUF1ZGlvU3RvcmFnZVR5cGUuTUVNX0VOVElSRTtcclxuICBwcml2YXRlIF9wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0OlBlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXR8bnVsbD1udWxsO1xyXG5cclxuICBwcml2YXRlIHBlcnNpc3RlZD10cnVlO1xyXG4gIHByaXZhdGUgcGVyc2lzdEVycm9yOkVycm9yfG51bGw9bnVsbDtcclxuICBwcml2YXRlIGluZGRiQXVkaW9CdWZmZXI6SW5kZXhlZERiQXVkaW9CdWZmZXJ8bnVsbD1udWxsO1xyXG5cclxuICAvL3ByaXZhdGUgY29udGV4dDpBdWRpb0NvbnRleHR8bnVsbD1udWxsO1xyXG5cclxuICBjb25zdHJ1Y3RvcigpIHtcclxuICAgIHRoaXMubiA9IG5hdmlnYXRvcjtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgX2F1ZGlvQ29udGV4dCgpOkF1ZGlvQ29udGV4dHxudWxse1xyXG4gICAgaWYoIXRoaXMuY29udGV4dCl7XHJcbiAgICAgIHRoaXMuY29udGV4dD1BdWRpb0NvbnRleHRQcm92aWRlci5hdWRpb0NvbnRleHRJbnN0YW5jZSgpO1xyXG4gICAgICBpZih0aGlzLmNvbnRleHQpIHtcclxuICAgICAgICB0aGlzLmNvbnRleHQuYWRkRXZlbnRMaXN0ZW5lcignc3RhdGVjaGFuZ2UnLCAoKSA9PiB7XHJcbiAgICAgICAgICBpZiAodGhpcy5jb250ZXh0ICYmIHRoaXMuY29udGV4dC5zdGF0ZSAhPT0gJ3J1bm5pbmcnKSB7XHJcbiAgICAgICAgICAgIHRoaXMuY2xvc2UoKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgcmV0dXJuIHRoaXMuY29udGV4dDtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgaW5pdERhdGEoKSB7XHJcbiAgICBpZighdGhpcy5fcmVjVVVJRCkge1xyXG4gICAgICB0aGlzLl9yZWNVVUlEID0gVVVJRC5nZW5lcmF0ZSgpO1xyXG4gICAgfVxyXG4gICAgdGhpcy5wZXJzaXN0RXJyb3I9bnVsbDtcclxuICAgIC8vY29uc29sZS5kZWJ1ZyhcIkF1ZGlvIGNhcHR1cmUgaW5pdGlhbGl6ZSBzdG9yYWdlIGZvciB0eXBlOiBcIit0aGlzLl9hdWRpb1N0b3JhZ2VUeXBlKTtcclxuICAgIGlmKEF1ZGlvU3RvcmFnZVR5cGUuREJfQ0hVTktFRCA9PT0gdGhpcy5fYXVkaW9TdG9yYWdlVHlwZSAmJiB0aGlzLl9wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0ICYmIHRoaXMuX3JlY1VVSUQpIHtcclxuICAgICAgLy9jb25zb2xlLmRlYnVnKFwiQ3JlYXRlIGluZGV4ZWQgZGIgYXVkaW8gYnVmZmVyLlwiKTtcclxuICAgICAgdGhpcy5pbmRkYkF1ZGlvQnVmZmVyID0gbmV3IEluZGV4ZWREYkF1ZGlvQnVmZmVyKHRoaXMuX3BlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQsIHRoaXMuY2hhbm5lbENvdW50LHRoaXMuY3VycmVudFNhbXBsZVJhdGUsQXVkaW9DYXB0dXJlLkJVRkZFUl9TSVpFLDAsdGhpcy5fcmVjVVVJRClcclxuICAgIH1cclxuICAgIGlmKCEoQXVkaW9TdG9yYWdlVHlwZS5ORVRfQ0hVTktFRCA9PT0gdGhpcy5fYXVkaW9TdG9yYWdlVHlwZSkpIHtcclxuICAgICAgLy8gSW5pdGlhbGl6ZSBhdWRpbyBkYXRhIGFycmF5IGV4Y2VwdCBmb3IgbmV0IGF1ZGlvIGJ1ZmZlciBtb2RlXHJcbiAgICAgIHRoaXMuZGF0YSA9IG5ldyBBcnJheTxBcnJheTxGbG9hdDMyQXJyYXk+PigpO1xyXG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuY2hhbm5lbENvdW50OyBpKyspIHtcclxuICAgICAgICB0aGlzLmRhdGEucHVzaChuZXcgQXJyYXk8RmxvYXQzMkFycmF5PigpKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgdGhpcy5mcmFtZXNSZWNvcmRlZCA9IDA7XHJcbiAgfVxyXG5cclxuICBsaXN0RGV2aWNlcygpIHtcclxuICAgIG5hdmlnYXRvci5tZWRpYURldmljZXMuZW51bWVyYXRlRGV2aWNlcygpLnRoZW4oKGw6IE1lZGlhRGV2aWNlSW5mb1tdKSA9PiB0aGlzLnByaW50RGV2aWNlcyhsKSk7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIGR1bW15U2Vzc2lvbigpOlByb21pc2U8TWVkaWFTdHJlYW0+e1xyXG4gICAgLy8gd29ya2Fyb3VuZCB0byByZXF1ZXN0IHBlcm1pc3Npb25zOlxyXG4gICAgLy8gU3RhcnQgYSBkdW1teSBzZXNzaW9uXHJcbiAgICBsZXQgbWVkaWFTdHJDbnN0cnMgPSA8TWVkaWFTdHJlYW1Db25zdHJhaW50cz57YXVkaW86XHJcbiAgICAgICAge2VjaG9DYW5jZWxhdGlvbjogZmFsc2V9XHJcbiAgICB9O1xyXG4gICAgcmV0dXJuIG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhKG1lZGlhU3RyQ25zdHJzKTtcclxuXHJcbiAgfVxyXG5cclxuXHJcbiAgcHJpdmF0ZSBzdG9wQWxsU2Vzc2lvblRyYWNrcyhtZWRpYVN0cmVhbTpNZWRpYVN0cmVhbSl7XHJcbiAgICAgIGxldCBhdHMgPSBtZWRpYVN0cmVhbS5nZXRUcmFja3MoKTtcclxuICAgICAgZm9yIChsZXQgYXRJZHggPSAwOyBhdElkeCA8IGF0cy5sZW5ndGg7IGF0SWR4KyspIHtcclxuICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJTdG9wIGR1bW15IHNlc3Npb24gdHJhY2s6ICNcIiArIGF0SWR4KVxyXG4gICAgICAgIGF0c1thdElkeF0uc3RvcCgpO1xyXG4gICAgICB9XHJcbiAgfVxyXG5cclxuICBkZXZpY2VJbmZvcyhjYjogKGRldmljZUluZm9zOiBNZWRpYURldmljZUluZm9bXSB8IG51bGwpID0+IGFueSwgcmV0cnkgPSB0cnVlLGR1bW15U3RyZWFtPzpNZWRpYVN0cmVhbSkge1xyXG5cclxuICAgIG5hdmlnYXRvci5tZWRpYURldmljZXMuZW51bWVyYXRlRGV2aWNlcygpLnRoZW4oKGw6IE1lZGlhRGV2aWNlSW5mb1tdKSA9PiB7XHJcbiAgICAgIGxldCBsYWJlbHNBdmFpbGFibGUgPSBmYWxzZTtcclxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBsLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgbGV0IGRpID0gbFtpXTtcclxuICAgICAgICBpZiAoZGkubGFiZWwpIHtcclxuICAgICAgICAgIGxhYmVsc0F2YWlsYWJsZSA9IHRydWU7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICAgIGlmICghbGFiZWxzQXZhaWxhYmxlKSB7XHJcbiAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiTWVkaWEgZGV2aWNlIGVudW1lcmF0aW9uOiBObyBsYWJlbHMuXCIpXHJcbiAgICAgICAgaWYgKHJldHJ5KSB7XHJcbiAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcIlN0YXJ0aW5nIGR1bW15IHNlc3Npb24gdG8gcmVxdWVzdCBhdWRpbyBwZXJtaXNzaW9ucy4uLlwiKVxyXG5cclxuICAgICAgICAgICAgdGhpcy5kdW1teVNlc3Npb24oKS50aGVuKChzOiBNZWRpYVN0cmVhbSkgPT4ge1xyXG4gICAgICAgICAgICAvLyBhbmQgc3RvcCBpdCBpbW1lZGlhdGVseVxyXG5cclxuICAgICAgICAgICAgaWYocykge1xyXG4gICAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIkdvdCBkdW1teSBzZXNzaW9uIHN0cmVhbTogXCIgKyBzICsgXCIgLlwiKVxyXG4gICAgICAgICAgICB9ZWxzZXtcclxuICAgICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJObyBkdW1teSBzdHJlYW1cIilcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAvLyByZXRyeSAob25seSBvbmNlKVxyXG4gICAgICAgICAgICB0aGlzLmRldmljZUluZm9zKGNiLCBmYWxzZSxzKTtcclxuICAgICAgICAgIH0scmVhc29uID0+IHtcclxuICAgICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiRHVtbXkgc2Vzc2lvbiByZWplY3RlZC5cIilcclxuICAgICAgICAgICAgLy8gVE9ETyBlcnJvciBjYWxsYmFja1xyXG4gICAgICAgICAgICBjYihudWxsKTtcclxuICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICBjYihudWxsKTtcclxuICAgICAgICB9XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gc3VjY2Vzc1xyXG4gICAgICAgIGNiKGwpO1xyXG4gICAgICB9XHJcbiAgICAgIGlmKGR1bW15U3RyZWFtKXtcclxuICAgICAgICB0aGlzLnN0b3BBbGxTZXNzaW9uVHJhY2tzKGR1bW15U3RyZWFtKTtcclxuICAgICAgfVxyXG4gICAgfSwocmVhc29uKT0+IHtcclxuICAgICAgLy9yZWplY3RlZFxyXG4gICAgICAvL2NvbnNvbGUuZGVidWcoXCJNZWRpYSBkZXZpY2UgZW51bWVyYXRpb24gcmVqZWN0ZWQuXCIpXHJcbiAgICAgIGlmIChyZXRyeSkge1xyXG4gICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIlN0YXJ0aW5nIGR1bW15IHNlc3Npb24gdG8gcmVxdWVzdCBhdWRpbyBwZXJtaXNzaW9ucy4uLlwiKVxyXG4gICAgICAgIHRoaXMuZHVtbXlTZXNzaW9uKCkudGhlbigoczogTWVkaWFTdHJlYW0pID0+IHtcclxuICAgICAgICAgIC8vIGFuZCBzdG9wIGl0IGltbWVkaWF0ZWx5XHJcbiAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJEdW1teSBzZXNzaW9uLlwiKVxyXG4gICAgICAgICAgaWYocykge1xyXG4gICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJHb3QgZHVtbXkgc2Vzc2lvbiBzdHJlYW06IFwiICsgcyArIFwiIC5cIilcclxuICAgICAgICAgIH1lbHNle1xyXG4gICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJObyBkdW1teSBzdHJlYW1cIilcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIC8vIHJldHJ5IChvbmx5IG9uY2UpXHJcbiAgICAgICAgICB0aGlzLmRldmljZUluZm9zKGNiLCBmYWxzZSxzKTtcclxuICAgICAgICB9LCByZWFzb24gPT4ge1xyXG4gICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiRHVtbXkgc2Vzc2lvbiByZWplY3RlZC5cIilcclxuICAgICAgICAgIC8vIFRPRE8gZXJyb3IgY2FsbGJhY2tcclxuICAgICAgICAgIGNiKG51bGwpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIGNiKG51bGwpO1xyXG4gICAgICB9XHJcbiAgICAgIGlmKGR1bW15U3RyZWFtKXtcclxuICAgICAgICB0aGlzLnN0b3BBbGxTZXNzaW9uVHJhY2tzKGR1bW15U3RyZWFtKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG5cclxuXHJcbiAgfVxyXG5cclxuXHJcbiAgcHJpbnREZXZpY2VzKGw6IE1lZGlhRGV2aWNlSW5mb1tdKTogdm9pZCB7XHJcbiAgICAvL2xldCBzZWxEZXZpY2VJZCA9ICdfX19kdW1teV9fXyc7XHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGwubGVuZ3RoOyBpKyspIHtcclxuICAgICAgbGV0IGRpID0gbFtpXTtcclxuICAgICAgY29uc29sZS5sb2coXCJBdWRpbyBkZXZpY2U6IElkOiBcIiArIGRpLmRldmljZUlkICsgXCIgZ3JvdXBJZDogXCIgKyBkaS5ncm91cElkICsgXCIgbGFiZWw6IFwiICsgZGkubGFiZWwgKyBcIiBraW5kOiBcIiArIGRpLmtpbmQpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgYWRkQ2FwdHVyZUludGVyY2VwdG9yKCkge1xyXG4gICAgaWYgKHRoaXMuY29udGV4dCkge1xyXG5cclxuICAgIGNvbnN0IGF3biA9IG5ldyBBdWRpb1dvcmtsZXROb2RlKHRoaXMuY29udGV4dCwgJ2NhcHR1cmUtaW50ZXJjZXB0b3InKTtcclxuICAgIGF3bi5vbnByb2Nlc3NvcmVycm9yID0gKGV2OiBFdmVudCkgPT4ge1xyXG4gICAgICBsZXQgbXNnID0gJ1Vua253b24gZXJyb3InO1xyXG4gICAgICBpZiAoZXYgaW5zdGFuY2VvZiBFcnJvckV2ZW50KSB7XHJcbiAgICAgICAgbXNnID0gZXYubWVzc2FnZTtcclxuICAgICAgfVxyXG4gICAgICBjb25zb2xlLmVycm9yKFwiQ2FwdHVyZSBhdWRpbyB3b3JrbGV0IGVycm9yOiBcIiArIG1zZyk7XHJcbiAgICAgIGlmICh0aGlzLmxpc3RlbmVyKSB7XHJcbiAgICAgICAgdGhpcy5saXN0ZW5lci5lcnJvcihtc2cpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBsZXQgYXduUHQgPSBhd24ucG9ydDtcclxuICAgIGlmIChhd25QdCkge1xyXG4gICAgICBhd25QdC5vbm1lc3NhZ2UgPSAoZXY6IE1lc3NhZ2VFdmVudCkgPT4ge1xyXG4gICAgICAgIGlmICh0aGlzLmNhcHR1cmluZykge1xyXG4gICAgICAgICAgbGV0IGR0ID0gZXYuZGF0YTtcclxuICAgICAgICAgIGxldCBjaHMgPSBkdC5jaHM7XHJcbiAgICAgICAgICBsZXQgYWRhTGVuID0gZHQuZGF0YS5sZW5ndGg7XHJcbiAgICAgICAgICBpZiAoREVCVUdfVFJBQ0VfTEVWRUwgPiA4KSB7XHJcbiAgICAgICAgICAgIGNvbnNvbGUuZGVidWcoJ1JlY2VpdmVkIGRhdGEgZnJvbSB3b3JrbGV0OiAnICsgY2hzICsgJyAnICsgZHQubGVuICsgJyBEYXRhIGNoczogJyArIGFkYUxlbik7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICBsZXQgY2h1bmsgPSBuZXcgQXJyYXk8RmxvYXQzMkFycmF5PihjaHMpO1xyXG4gICAgICAgICAgY29uc3Qgc2FtcGxlcyA9IHRoaXMuZnJhbWVzUmVjb3JkZWQgKiBjaHM7XHJcbiAgICAgICAgICBpZiAoKEF1ZGlvU3RvcmFnZVR5cGUuTUVNX0VOVElSRV9BVVRPX05FVF9DSFVOS0VEID09PSB0aGlzLmF1ZGlvU3RvcmFnZVR5cGUgfHwgQXVkaW9TdG9yYWdlVHlwZS5NRU1fQ0hVTktFRF9BVVRPX05FVF9DSFVOS0VEID09PSB0aGlzLmF1ZGlvU3RvcmFnZVR5cGUpICYmIHRoaXMuZGF0YSAmJiBzYW1wbGVzID4gdGhpcy5fbWF4QXV0b05ldE1lbVN0b3JlU2FtcGxlcykge1xyXG4gICAgICAgICAgICB0aGlzLmRhdGEgPSBudWxsO1xyXG4gICAgICAgICAgfVxyXG5cclxuICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIkRhdGEgaW5pdGlhbGl6ZWQ6IFwiKyh0aGlzLmRhdGEhPW51bGwpKTtcclxuICAgICAgICAgIGZvciAobGV0IGNoID0gMDsgY2ggPCBjaHM7IGNoKyspIHtcclxuICAgICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiRGF0YSBjaCBpbml0aWFsaXplZDogXCIrKHRoaXMuZGF0YSAhPW51bGwgJiYgdGhpcy5kYXRhW2NoXSAhPW51bGwpKTtcclxuICAgICAgICAgICAgaWYgKGNoIDwgdGhpcy5jaGFubmVsQ291bnQpIHtcclxuICAgICAgICAgICAgICBpZiAoZHQuZGF0YVtjaF0pIHtcclxuICAgICAgICAgICAgICAgIGxldCBmYSA9IG5ldyBGbG9hdDMyQXJyYXkoZHQuZGF0YVtjaF0pO1xyXG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuZGF0YSAmJiB0aGlzLmRhdGFbY2hdKSB7XHJcbiAgICAgICAgICAgICAgICAgIHRoaXMuZGF0YVtjaF0ucHVzaChmYSk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBjaHVua1tjaF0gPSBmYTtcclxuICAgICAgICAgICAgICAgIC8vIFVzZSBzYW1wbGVzIG9mIGNoYW5uZWwgMCB0byBjb3VudCBmcmFtZXMgKHNhbXBsZXMpXHJcbiAgICAgICAgICAgICAgICBpZiAoY2ggPT0gMCkge1xyXG4gICAgICAgICAgICAgICAgICB0aGlzLmZyYW1lc1JlY29yZGVkICs9IGZhLmxlbmd0aDtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICAgIGlmICh0aGlzLmF1ZGlvT3V0U3RyZWFtKSB7XHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgdGhpcy5hdWRpb091dFN0cmVhbS53cml0ZShjaHVuayk7XHJcbiAgICAgICAgICAgICAgLy8gLy8gUmFuZG9tIHRlc3QgZXJyb3I6XHJcbiAgICAgICAgICAgICAgLy8gaWYoTWF0aC5yYW5kb20oKT4wLjk4KSB7XHJcbiAgICAgICAgICAgICAgLy8gICB0aHJvdyBuZXcgRXJyb3IoJ1Rlc3QnKTtcclxuICAgICAgICAgICAgICAvLyB9XHJcbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xyXG4gICAgICAgICAgICAgIGlmIChlcnIgaW5zdGFuY2VvZiBFcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5wZXJzaXN0RXJyb3IgPSBlcnI7XHJcbiAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgIHRoaXMucGVyc2lzdEVycm9yID0gbmV3IEVycm9yKCdFcnJvciBoYW5kbGluZyByZWNvcmRlZCBhdWRpbyBkYXRhJyk7XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiQ2FwdHVyZSBlcnJvcjogXCIgKyBlcnIpO1xyXG4gICAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLnN0b3AoKTtcclxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnIyKSB7XHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiQ2FwdHVyZSBuZXh0IGVycm9yIChpZ25vcmVkKTogXCIgKyBlcnIyKTtcclxuICAgICAgICAgICAgICB9IGZpbmFsbHkge1xyXG4gICAgICAgICAgICAgICAgaWYgKHRoaXMubGlzdGVuZXIpIHtcclxuICAgICAgICAgICAgICAgICAgbGV0IGVyckV4cGwgPSAnJztcclxuICAgICAgICAgICAgICAgICAgaWYgKGVyciBpbnN0YW5jZW9mIERPTUV4Y2VwdGlvbikge1xyXG4gICAgICAgICAgICAgICAgICAgIGVyckV4cGwgPSAnOiAnICsgZXJyLm5hbWUgKyAnOiAnICsgZXJyLm1lc3NhZ2U7XHJcbiAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5lcnJvcihcIkNvdWxkIG5vdCBoYW5kbGUgcmVjb3JkZWQgYXVkaW8gZGF0YVwiICsgZXJyRXhwbCwgXCJQbGVhc2UgdHJ5IHRvIHJlY29yZCBhZ2Fpbi5cIik7XHJcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICB0aGlzLmNsb3NlKCk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICBpZiAoQXVkaW9TdG9yYWdlVHlwZS5EQl9DSFVOS0VEID09PSB0aGlzLl9hdWRpb1N0b3JhZ2VUeXBlICYmIHRoaXMuX3BlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQpIHtcclxuICAgICAgICAgICAgdGhpcy5zdG9yZSgpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfTtcclxuICAgIH1cclxuICAgIC8vIFRyaWVkIHRvIGZpeCB0aGF0IFNhZmFyaSBkb2VzIG5vdCByZWNvcmQgdGhlIHNlY29uZCBjaGFubmVsXHJcbiAgICAvLyBEb2VzIG5vdCBoZWxwXHJcbiAgICAvL2F3bi5jaGFubmVsQ291bnQ9dGhpcy5jaGFubmVsQ291bnQ7XHJcbiAgICAvL2F3bi5jaGFubmVsQ291bnRNb2RlPSdleHBsaWNpdCc7XHJcbiAgICAvL2NvbnNvbGUuZGVidWcoJ0NoYW5uZWwgY291bnQgZXhwbGljaXRseSBzZXQgdG8gJyt0aGlzLmNoYW5uZWxDb3VudCk7XHJcblxyXG4gICAgdGhpcy5idWZmZXJpbmdOb2RlID0gYXduO1xyXG4gICAgLy90aGlzLmJ1ZmZlcmluZ05vZGUuY2hhbm5lbENvdW50PXRoaXMuY2hhbm5lbENvdW50O1xyXG4gICAgdGhpcy5fb3BlbmVkID0gdHJ1ZTtcclxuICAgIGlmICh0aGlzLmxpc3RlbmVyKSB7XHJcbiAgICAgIHRoaXMubGlzdGVuZXIub3BlbmVkKCk7XHJcbiAgICB9XHJcbiAgfVxyXG4gIH1cclxuXHJcbiAgb3BlbihjaGFubmVsQ291bnQ6IG51bWJlciwgc2VsRGV2aWNlSWQ/OiBDb25zdHJhaW5ET01TdHJpbmd8dW5kZWZpbmVkLGF1dG9HYWluQ29udHJvbENvbmZpZ3M/OkFycmF5PEF1dG9HYWluQ29udHJvbENvbmZpZz58bnVsbHx1bmRlZmluZWQsYWxsb3dFY2hvQ2FuY2VsbGF0aW9uPzpib29sZWFuKXtcclxuICAgIC8vY29uc29sZS5kZWJ1ZyhcIkNhcHR1cmUgb3BlbjogY3R4IHN0YXRlOiBcIit0aGlzLmNvbnRleHQuc3RhdGUpO1xyXG4gICAgdGhpcy5jb250ZXh0PXRoaXMuX2F1ZGlvQ29udGV4dCgpO1xyXG4gICAgaWYoIXRoaXMuY29udGV4dCl7XHJcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIkNvdWxkIG5vdCBnZXQgYXVkaW8gY29udGV4dCFcIik7XHJcbiAgICB9XHJcbiAgICBpZih0aGlzLmNvbnRleHQuc3RhdGU9PT0nc3VzcGVuZGVkJyl7XHJcbiAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIkNhcHR1cmUgb3BlbjogUmVzdW1lIGNvbnRleHRcIik7XHJcbiAgICAgIHRoaXMuY29udGV4dC5yZXN1bWUoKS50aGVuKCgpPT57XHJcbiAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiQ2FwdHVyZSBvcGVuIChjdHggcmVzdW1lZCk6IGN0eCBzdGF0ZTogXCIrdGhpcy5jb250ZXh0LnN0YXRlKTtcclxuICAgICAgICB0aGlzLl9vcGVuKGNoYW5uZWxDb3VudCwgc2VsRGV2aWNlSWQsIGF1dG9HYWluQ29udHJvbENvbmZpZ3MsYWxsb3dFY2hvQ2FuY2VsbGF0aW9uKTtcclxuICAgICAgfSkuY2F0Y2goKGVycik9PntcclxuICAgICAgICBjb25zb2xlLmVycm9yKGVyci5tZXNzYWdlKTtcclxuICAgICAgICB0aHJvdyBlcnI7XHJcbiAgICAgIH0pXHJcbiAgICB9ZWxzZSBpZih0aGlzLmNvbnRleHQuc3RhdGU9PT0nY2xvc2VkJykge1xyXG4gICAgICAgIGNvbnN0IG1zZz0nRXJyb3Igb24gc3RhcnQgY2FwdHVyZTogVGhlIGF1ZGlvIGNvbnRleHQgaXMgYWxyZWFkeSBjbG9zZWQuJztcclxuICAgICAgICBjb25zb2xlLmVycm9yKG1zZyk7XHJcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKG1zZyk7XHJcbiAgICB9ZWxzZSB7XHJcbiAgICAgIHRoaXMuX29wZW4oY2hhbm5lbENvdW50LCBzZWxEZXZpY2VJZCwgYXV0b0dhaW5Db250cm9sQ29uZmlncyxhbGxvd0VjaG9DYW5jZWxsYXRpb24pO1xyXG4gICAgfVxyXG5cclxuICB9XHJcblxyXG4gIF9vcGVuKGNoYW5uZWxDb3VudDogbnVtYmVyLCBzZWxEZXZpY2VJZD86IENvbnN0cmFpbkRPTVN0cmluZ3x1bmRlZmluZWQsYXV0b0dhaW5Db250cm9sQ29uZmlncz86QXJyYXk8QXV0b0dhaW5Db250cm9sQ29uZmlnPnxudWxsfHVuZGVmaW5lZCxhbGxvd0VjaG9DYW5jZWxsYXRpb24/OmJvb2xlYW4pIHtcclxuICAgIHRoaXMuY2hhbm5lbENvdW50ID0gY2hhbm5lbENvdW50O1xyXG4gICAgdGhpcy5mcmFtZXNSZWNvcmRlZCA9IDA7XHJcblxyXG4gICAgdGhpcy5jb250ZXh0PXRoaXMuX2F1ZGlvQ29udGV4dCgpO1xyXG5cclxuICAgIGlmKCF0aGlzLmNvbnRleHQpe1xyXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJDb3VsZCBub3QgZ2V0IGF1ZGlvIGNvbnRleHQhXCIpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vdmFyIG1zYyA9IG5ldyBBdWRpb1N0cmVhbUNvbnN0cigpO1xyXG4gICAgLy8gdmFyIG1zYz17fTtcclxuICAgIC8vbXNjLnZpZGVvID0gZmFsc2U7XHJcbiAgICAvL21zYy5hdWRpbyA9IHRydWU7XHJcblxyXG4gICAgLy8gQ2hyb21lIGFuZCBGaXJlZm94IHN0ZXJlbyBjaGFubmVscyBhcmUgaWRlbnRpY2FsICEhXHJcbiAgICAvLyBBbmQgZXZlbiB3b3JzZTogVGhlIGRhdGEgY29taW5nIGZyb20gdGhlIHNvdXJjZSBpcyBhbHJlYWR5IHByZXByb2Nlc3NlZCBvbiBGRiBhbmQgQ2hyb21lLiBJdCBjb250YWlucyBEU1AgYXJ0aWZhY3RzISFcclxuXHJcbiAgICAvLyBodHRwczovL2J1Z3MuY2hyb21pdW0ub3JnL3AvY2hyb21pdW0vaXNzdWVzL2RldGFpbD9pZD0zODc3MzdcclxuICAgIC8vIFRoZSB3b3JrYXJvdW5kIHRvIHNldCB0aGVzZSBjb25zdHJhaW50cyBkb2VzIF9OT1RfIGhlbHA6XHJcbiAgICAvL3ZhciBtc2M9e2F1ZGlvOiB7ZWNob0NhbmNlbGxhdGlvbjogZmFsc2UsY2hhbm5lbENvdW50OiAyLCBnb29nQXVkaW9NaXJyb3Jpbmc6IGZhbHNlfSx2aWRlbzogZmFsc2V9O1xyXG5cclxuICAgIC8vIFNhZmFyaSBhdCBsZWFzdCB2ZXJzaW9uIDExOiBTdXBwb3J0IGZvciBtZWRpYSBzdHJlYW1zXHJcbiAgICAvLyBUT0RPIHRlc3QgaWYgaW5wdXQgaXMgdW5wcm9jZXNzZWRcclxuXHJcbiAgICBsZXQgbXNjOk1lZGlhU3RyZWFtQ29uc3RyYWludHM7XHJcbiAgICBjb25zb2xlLmluZm8oJ1VzZXIgYWdlbnQ6ICcrbmF2aWdhdG9yLnVzZXJBZ2VudCk7XHJcblxyXG5cclxuICAgICAgbGV0IHVhPVVzZXJBZ2VudEJ1aWxkZXIudXNlckFnZW50KCk7XHJcblxyXG4gICAgICAvLyB1YS5jb21wb25lbnRzLmZvckVhY2goKGMpPT57XHJcbiAgICAgIC8vICAgY29uc29sZS5pbmZvKFwiVUFfQ29tcDogXCIrYy50b1N0cmluZygpKTtcclxuICAgICAgLy8gfSlcclxuXHJcbiAgICAgbGV0IGFnY0NmZzpBdXRvR2FpbkNvbnRyb2xDb25maWd8bnVsbD1udWxsO1xyXG5cclxuICAgIGxldCBhdXRvR2FpbkNvbnRyb2w9ZmFsc2U7XHJcbiAgICBsZXQgY2hyb21lRWNob0NhbmNlbGxhdGlvbj1mYWxzZTtcclxuICAgIGlmKGF1dG9HYWluQ29udHJvbENvbmZpZ3Mpe1xyXG4gICAgICBmb3IobGV0IGFnY2Mgb2YgYXV0b0dhaW5Db250cm9sQ29uZmlncyl7XHJcbiAgICAgICAgaWYoYWdjYy5wbGF0Zm9ybT09PUNmZ1BsYXRmb3JtLkFuZHJvaWQgJiYgdWEuZGV0ZWN0ZWRQbGF0Zm9ybT09PVBsYXRmb3JtLkFuZHJvaWQpe1xyXG4gICAgICAgICAgICBhZ2NDZmc9YWdjYztcclxuICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmKGFnY2MucGxhdGZvcm09PT1DZmdQbGF0Zm9ybS5XaW5kb3dzICYmIHVhLmRldGVjdGVkUGxhdGZvcm09PT1QbGF0Zm9ybS5XaW5kb3dzKXtcclxuICAgICAgICAgIGFnY0NmZz1hZ2NjO1xyXG4gICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICAgIGlmKGFnY0NmZyl7XHJcbiAgICAgICAgLy8gVE9ETyB1c2UgRVhBQ1QvSURFQUwgY29uc3RyYWludFxyXG4gICAgICAgIGF1dG9HYWluQ29udHJvbD1hZ2NDZmcudmFsdWU7XHJcbiAgICAgICAgaWYoQ0hST01FX0FDVElWQVRFX0VDSE9fQ0FOQ0VMTEFUSU9OX1dJVEhfQUdDKXtcclxuICAgICAgICAgIGNocm9tZUVjaG9DYW5jZWxsYXRpb249YWdjQ2ZnLnZhbHVlO1xyXG4gICAgICAgIH1cclxuICAgICAgICAvLyBUT0RPIHF1ZXJ5IHJlYWwgQUdDIHN0YXR1c1xyXG4gICAgICAgIHRoaXMuYWdjU3RhdHVzPWFnY0NmZy52YWx1ZTtcclxuICAgICAgfWVsc2V7XHJcbiAgICAgICAgdGhpcy5hZ2NTdGF0dXM9ZmFsc2U7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBkZWZhdWx0XHJcbiAgICBtc2MgPSB7XHJcbiAgICAgIGF1ZGlvOiB7XHJcbiAgICAgICAgZGV2aWNlSWQ6IHNlbERldmljZUlkLFxyXG4gICAgICAgIGVjaG9DYW5jZWxsYXRpb246IGZhbHNlLFxyXG4gICAgICAgIGNoYW5uZWxDb3VudDogY2hhbm5lbENvdW50LFxyXG4gICAgICAgIGF1dG9HYWluQ29udHJvbDogYXV0b0dhaW5Db250cm9sXHJcbiAgICAgIH0sXHJcbiAgICAgIHZpZGVvOiBmYWxzZVxyXG4gICAgfTtcclxuXHJcbiAgICBpZiAodWEuZGV0ZWN0ZWRCcm93c2VyPT09QnJvd3Nlci5FZGdlKSB7XHJcblxyXG4gICAgICAvLyBNaWNyb3NvZnQgRWRnZSBzZW5kcyB1bm1vZGlmaWVkIGF1ZGlvXHJcbiAgICAgIC8vIFRoZSBjb25zdHJhaW50IGNhbiBmb2xsb3cgdGhlIHNwZWNpZmljYXRpb25cclxuICAgICAgY29uc29sZS5pbmZvKFwiU2V0dGluZyBtZWRpYSB0cmFjayBjb25zdHJhaW50cyBmb3IgTWljcm9zb2Z0IEVkZ2UuXCIpO1xyXG4gICAgICBtc2MgPSB7XHJcbiAgICAgICAgYXVkaW86IHtcclxuICAgICAgICAgIGRldmljZUlkOiBzZWxEZXZpY2VJZCxcclxuICAgICAgICAgIGVjaG9DYW5jZWxsYXRpb246IGZhbHNlLFxyXG4gICAgICAgICAgY2hhbm5lbENvdW50OiBjaGFubmVsQ291bnQsXHJcbiAgICAgICAgICBhdXRvR2FpbkNvbnRyb2w6IGF1dG9HYWluQ29udHJvbFxyXG4gICAgICAgIH0sXHJcbiAgICAgICAgdmlkZW86IGZhbHNlXHJcbiAgICAgIH07XHJcbiAgICB9IGVsc2UgaWYgKHVhLmRldGVjdGVkQnJvd3Nlcj09PUJyb3dzZXIuQ2hyb21lKSB7XHJcbiAgICAgIC8vIEdvb2dsZSBDaHJvbWU6IHdlIG5lZWQgdG8gc3dpdGNoIG9mIGVhY2ggb2YgdGhlIHByZXByb2Nlc3NpbmcgdW5pdHMgaW5jbHVkaW5nIHRoZVxyXG4gICAgICBjb25zb2xlLmluZm8oXCJTZXR0aW5nIG1lZGlhIHRyYWNrIGNvbnN0cmFpbnRzIGZvciBHb29nbGUgQ2hyb21lLlwiKTtcclxuXHJcbiAgICAgIC8vIENocm9tZSA2MCAtPiA2MSBjaGFuZ2VkXHJcbiAgICAgIC8vIGl0IHdvcmtzIG5vdyB3aXRob3V0IG1hbmRhdG9yeS9vcHRpb25hbCBzdWItb2JqZWN0c1xyXG5cclxuXHJcbiAgICAgIC8vIFJlcXVpcmVzIGF0IGxlYXN0IENocm9tZSA2MVxyXG4gICAgICBtc2MgPSB7XHJcbiAgICAgICAgYXVkaW86IHtcclxuICAgICAgICAgIGRldmljZUlkOiBzZWxEZXZpY2VJZCxcclxuICAgICAgICAgIGNoYW5uZWxDb3VudDogY2hhbm5lbENvdW50LFxyXG4gICAgICAgICAgZWNob0NhbmNlbGxhdGlvbjoge2V4YWN0OmNocm9tZUVjaG9DYW5jZWxsYXRpb259LFxyXG4gICAgICAgICAgYXV0b0dhaW5Db250cm9sOiB7ZXhhY3Q6YXV0b0dhaW5Db250cm9sfSxcclxuICAgICAgICAgIHNhbXBsZVNpemU6e21pbjogMTZ9LFxyXG4gICAgICAgIH0sXHJcbiAgICAgICAgdmlkZW86IGZhbHNlLFxyXG4gICAgICB9XHJcblxyXG4gICAgfSBlbHNlIGlmICh1YS5kZXRlY3RlZEJyb3dzZXI9PT1Ccm93c2VyLkZpcmVmb3gpIHtcclxuICAgICAgY29uc29sZS5pbmZvKFwiU2V0dGluZyBtZWRpYSB0cmFjayBjb25zdHJhaW50cyBmb3IgTW96aWxsYSBGaXJlZm94LlwiKTtcclxuICAgICAgLy8gRmlyZWZveFxyXG4gICAgICBtc2MgPSB7XHJcbiAgICAgICAgYXVkaW86IHtcclxuICAgICAgICAgICAgZGV2aWNlSWQ6IHNlbERldmljZUlkLFxyXG4gICAgICAgICAgICBjaGFubmVsQ291bnQ6IGNoYW5uZWxDb3VudCxcclxuICAgICAgICAgIGVjaG9DYW5jZWxsYXRpb246IGZhbHNlLFxyXG4gICAgICAgICAgICBhdXRvR2FpbkNvbnRyb2w6IGF1dG9HYWluQ29udHJvbCxcclxuICAgICAgICAgIG5vaXNlU3VwcHJlc3Npb246IGZhbHNlXHJcbiAgICAgICAgfSxcclxuICAgICAgICB2aWRlbzogZmFsc2UsXHJcbiAgICAgIH1cclxuXHJcbiAgICB9IGVsc2UgaWYgKHVhLmRldGVjdGVkQnJvd3Nlcj09PUJyb3dzZXIuU2FmYXJpKSB7XHJcbiAgICAgIGNvbnNvbGUuaW5mbyhcIlNldHRpbmcgbWVkaWEgdHJhY2sgY29uc3RyYWludHMgZm9yIFNhZmFyaSBicm93c2VyLlwiKVxyXG4gICAgICAvL2NvbnNvbGUuaW5mbyhcIkFwcGx5IHdvcmthcm91bmQgZm9yIFNhZmFyaTogQXZvaWQgZGlzY29ubmVjdCBvZiBzdHJlYW1zLlwiKTtcclxuXHJcbiAgICAgIHRoaXMuZGlzY29ubmVjdFN0cmVhbXMgPSB0cnVlO1xyXG5cclxuICAgICAgbXNjID0ge1xyXG4gICAgICAgIGF1ZGlvOiB7XHJcbiAgICAgICAgICBkZXZpY2VJZDogc2VsRGV2aWNlSWQsXHJcbiAgICAgICAgICBjaGFubmVsQ291bnQ6IGNoYW5uZWxDb3VudCxcclxuICAgICAgICAgIGVjaG9DYW5jZWxsYXRpb246IGFsbG93RWNob0NhbmNlbGxhdGlvbj91bmRlZmluZWQ6ZmFsc2VcclxuICAgICAgICB9LFxyXG4gICAgICAgIHZpZGVvOiBmYWxzZSxcclxuICAgICAgfVxyXG5cclxuICAgIH0gZWxzZSB7XHJcblxyXG4gICAgICAvLyBUT0RPIGRlZmF1bHQgY29uc3RyYWludHMgb3IgZXJyb3IgQnJvd3NlciBub3Qgc3VwcG9ydGVkXHJcbiAgICB9XHJcblxyXG5cclxuXHJcbiAgICBjb25zb2xlLmRlYnVnKFwiQXVkaW8gY2FwdHVyZSwgQUdDOiBcIit0aGlzLmFnY1N0YXR1cylcclxuXHJcbiAgICBsZXQgdW1wID0gbmF2aWdhdG9yLm1lZGlhRGV2aWNlcy5nZXRVc2VyTWVkaWEobXNjKTtcclxuICAgIHVtcC50aGVuKChzKSA9PiB7XHJcblxyXG4gICAgICBpZiAodGhpcy5jb250ZXh0KSB7XHJcbiAgICAgIHRoaXMuc3RyZWFtID0gcztcclxuXHJcbiAgICAgIGxldCBhVHJhY2tzID0gcy5nZXRBdWRpb1RyYWNrcygpO1xyXG5cclxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhVHJhY2tzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgbGV0IGFUcmFjayA9IGFUcmFja3NbaV07XHJcblxyXG4gICAgICAgIGNvbnNvbGUuaW5mbyhcIlRyYWNrIGF1ZGlvIGluZm86IGlkOiBcIiArIGFUcmFjay5pZCArIFwiIGtpbmQ6IFwiICsgYVRyYWNrLmtpbmQgKyBcIiBsYWJlbDogXFxcIlwiICsgYVRyYWNrLmxhYmVsICsgXCJcXFwiXCIpO1xyXG4gICAgICAgIGxldCBtdHJTdHMgPSBhVHJhY2suZ2V0U2V0dGluZ3MoKTtcclxuXHJcbiAgICAgICAgLy8gVHlwZXNjcmlwdCBsaWIuZG9tLnRzIE1lZGlhVHJhY2tTZXR0aW5ncy5jaGFubmVsQ291bnQgaXMgbWlzc2luZ1xyXG4gICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9tZG4vYnJvd3Nlci1jb21wYXQtZGF0YS9ibG9iLzU0OTNkOGY5MzdlMDViMmRkYmQ0MWI5OWY1YmRmYWQ0YTFmMmVkODUvYXBpL01lZGlhVHJhY2tTZXR0aW5ncy5qc29uXHJcbiAgICAgICAgLy9AdHMtaWdub3JlXHJcbiAgICAgICAgY29uc29sZS5pbmZvKFwiVHJhY2sgYXVkaW8gc2V0dGluZ3M6IENoIGNudDogXCIgKyBtdHJTdHMuY2hhbm5lbENvdW50ICsgXCIsIEFHQzogXCIgKyBtdHJTdHMuYXV0b0dhaW5Db250cm9sICsgXCIsIEVjaG8gY2FuY2VsbC46IFwiICsgbXRyU3RzLmVjaG9DYW5jZWxsYXRpb24pO1xyXG4gICAgICAgIGlmIChtdHJTdHMuYXV0b0dhaW5Db250cm9sKSB7XHJcbiAgICAgICAgICB0aGlzLmFnY1N0YXR1cyA9IG10clN0cy5hdXRvR2FpbkNvbnRyb2w7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBjb25zb2xlLmRlYnVnKFwiRWNobyBjYW5jZWxsYXRpb246IFwiK210clN0cy5lY2hvQ2FuY2VsbGF0aW9uKTtcclxuXHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGxldCB2VHJhY2tzID0gcy5nZXRWaWRlb1RyYWNrcygpO1xyXG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZUcmFja3MubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICBsZXQgdlRyYWNrID0gdlRyYWNrc1tpXTtcclxuICAgICAgICBjb25zb2xlLmluZm8oXCJUcmFjayB2aWRlbyBpbmZvOiBpZDogXCIgKyB2VHJhY2suaWQgKyBcIiBraW5kOiBcIiArIHZUcmFjay5raW5kICsgXCIgbGFiZWw6IFwiICsgdlRyYWNrLmxhYmVsKTtcclxuICAgICAgfVxyXG4gICAgICB0aGlzLm1lZGlhU3RyZWFtID0gdGhpcy5jb250ZXh0LmNyZWF0ZU1lZGlhU3RyZWFtU291cmNlKHMpO1xyXG4gICAgICAvLyBzdHJlYW0gY2hhbm5lbCBjb3VudCAoIGlzIGFsd2F5cyAyICEpXHJcbiAgICAgIGxldCBzdHJlYW1DaGFubmVsQ291bnQ6IG51bWJlciA9IHRoaXMubWVkaWFTdHJlYW0uY2hhbm5lbENvdW50O1xyXG4gICAgICBjb25zb2xlLmluZm8oXCJTdHJlYW0gY2hhbm5lbCBjb3VudDogXCIgKyBzdHJlYW1DaGFubmVsQ291bnQpO1xyXG4gICAgICAvLyBpcyBub3Qgc2V0ISFcclxuICAgICAgLy90aGlzLmN1cnJlbnRTYW1wbGVSYXRlID0gdGhpcy5tZWRpYVN0cmVhbS5zYW1wbGVSYXRlO1xyXG4gICAgICB0aGlzLmN1cnJlbnRTYW1wbGVSYXRlID0gdGhpcy5jb250ZXh0LnNhbXBsZVJhdGU7XHJcbiAgICAgIGNvbnNvbGUuaW5mbyhcIlNvdXJjZSBhdWRpbyBub2RlOiBjaGFubmVsczogXCIgKyBzdHJlYW1DaGFubmVsQ291bnQgKyBcIiBzYW1wbGVyYXRlOiBcIiArIHRoaXMuY3VycmVudFNhbXBsZVJhdGUpO1xyXG4gICAgICBpZiAodGhpcy5hdWRpb091dFN0cmVhbSkge1xyXG4gICAgICAgIHRoaXMuYXVkaW9PdXRTdHJlYW0uc2V0Rm9ybWF0KHRoaXMuY2hhbm5lbENvdW50LCB0aGlzLmN1cnJlbnRTYW1wbGVSYXRlKTtcclxuICAgICAgfVxyXG4gICAgICAvLyBXM0MgIC0+IG5ldyBuYW1lIGlzIGNyZWF0ZVNjcmlwdFByb2Nlc3NvclxyXG4gICAgICAvL1xyXG4gICAgICAvLyBBZ2FpbiBkZXByZWNhdGVkLCBidXQgQXVkaW9Xb3JrZXIgbm90IHlldCBpbXBsZW1lbnRlZCBpbiBzdGFibGUgcmVsZWFzZXMgKEp1bmUgMjAxNilcclxuICAgICAgLy8gQXVkaW9Xb3JrZXIgaXMgbm93IEF1ZGlvV29ya2xldFByb2Nlc3NvciAuLi4gKE1heSAyMDE3KVxyXG5cclxuICAgICAgLy8gVXBkYXRlIDEyLTIwMjA6XHJcbiAgICAgIC8vIFRoZSBTY3JpcHRQcm9jZXNzb3JOb2RlIEludGVyZmFjZSAtIERFUFJFQ0FURURcclxuXHJcbiAgICAgIC8vIFVwZGF0ZSAwNi0yMDIxXHJcbiAgICAgIC8vICBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgaXMgaGVyZSB0byBzdGF5LiBXZWIgQXVkaW8gQVBJIGhhcyBub3cgUmVjb21tZW5kYXRpb24gc3RhdHVzICFcclxuXHJcbiAgICAgIGlmICh0aGlzLmNvbnRleHQuYXVkaW9Xb3JrbGV0KSB7XHJcbiAgICAgICAgLy9jb25zdCB3b3JrbGV0RmlsZU5hbWUgPSAoJ2ZpbGUtbG9hZGVyIS4vaW50ZXJjZXB0b3Jfd29ya2xldC5qcycpO1xyXG4gICAgICAgIC8vY29uc3Qgd29ya2xldEZpbGVOYW1lID0gJ2h0dHA6Ly9sb2NhbGhvc3Q6NDIwMC9hc3NldHMvaW50ZXJjZXB0b3Jfd29ya2xldC5qcyc7XHJcbiAgICAgICAgLy9jb25zb2xlLmxvZyhhd3BTdHIpO1xyXG4gICAgICAgIGlmIChBdWRpb0NhcHR1cmUuY2FwdHVyZUludGVyY2VwdG9yTW9kdWxlUmVnaXN0ZXJlZCkge1xyXG4gICAgICAgICAgLy8gUmVxdWlyZWQgY2FwdHVyZSBpbnRlcmNlcHRvciBtb2R1bGUgYWxyZWFkeSByZWdpc3RlcmVkXHJcbiAgICAgICAgICB0aGlzLmFkZENhcHR1cmVJbnRlcmNlcHRvcigpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcblxyXG4gICAgICAgICAgLy8gUmVnaXN0ZXIgY2FwdHVyZSBpbnRlcmNlcHRvciBtb2R1bGVcclxuICAgICAgICAgIGxldCBhdWRpb1dvcmtsZXRNb2R1bGVCbG9iID0gbmV3IEJsb2IoW2F3cFN0cl0sIHt0eXBlOiAndGV4dC9qYXZhc2NyaXB0J30pO1xyXG5cclxuICAgICAgICAgIGxldCBhdWRpb1dvcmtsZXRNb2R1bGVCbG9iVXJsID0gd2luZG93LlVSTC5jcmVhdGVPYmplY3RVUkwoYXVkaW9Xb3JrbGV0TW9kdWxlQmxvYik7XHJcblxyXG4gICAgICAgICAgdGhpcy5jb250ZXh0LmF1ZGlvV29ya2xldC5hZGRNb2R1bGUoYXVkaW9Xb3JrbGV0TW9kdWxlQmxvYlVybCkudGhlbigoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICBBdWRpb0NhcHR1cmUuY2FwdHVyZUludGVyY2VwdG9yTW9kdWxlUmVnaXN0ZXJlZCA9IHRydWU7XHJcbiAgICAgICAgICAgICAgICB0aGlzLmFkZENhcHR1cmVJbnRlcmNlcHRvcigpO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICkuY2F0Y2goKGVycm9yOiBhbnkpID0+IHtcclxuICAgICAgICAgICAgY29uc29sZS5sb2coJ0NvdWxkIG5vdCBhZGQgbW9kdWxlICcgKyBlcnJvcik7XHJcbiAgICAgICAgICB9KTtcclxuICAgICAgICB9XHJcbiAgICAgIH0gZWxzZSBpZiAodGhpcy5jb250ZXh0LmNyZWF0ZVNjcmlwdFByb2Nlc3Nvcikge1xyXG4gICAgICAgIC8vIFRoZSBTY3JpcHRQcm9jZXNzb3JOb2RlIEludGVyZmFjZSAtIERFUFJFQ0FURUQgT25seSBhcyBmYWxsYmFja1xyXG4gICAgICAgIC8vIFRPRE8gc2hvdWxkIHdlIHVzZSBzdHJlYW1DaGFubmVsQ291bnQgb3IgY2hhbm5lbENvdW50IGhlcmUgP1xyXG4gICAgICAgIGxldCBzY3JpcHRQcm9jZXNzb3JOb2RlID0gdGhpcy5jb250ZXh0LmNyZWF0ZVNjcmlwdFByb2Nlc3NvcihBdWRpb0NhcHR1cmUuQlVGRkVSX1NJWkUsIHN0cmVhbUNoYW5uZWxDb3VudCwgc3RyZWFtQ2hhbm5lbENvdW50KTtcclxuICAgICAgICB0aGlzLmJ1ZmZlcmluZ05vZGUgPSBzY3JpcHRQcm9jZXNzb3JOb2RlO1xyXG4gICAgICAgIGxldCBjID0gMDtcclxuICAgICAgICBpZiAoc2NyaXB0UHJvY2Vzc29yTm9kZS5vbmF1ZGlvcHJvY2Vzcykge1xyXG4gICAgICAgICAgc2NyaXB0UHJvY2Vzc29yTm9kZS5vbmF1ZGlvcHJvY2VzcyA9IChlOiBBdWRpb1Byb2Nlc3NpbmdFdmVudCkgPT4ge1xyXG5cclxuICAgICAgICAgICAgaWYgKHRoaXMuY2FwdHVyaW5nKSB7XHJcbiAgICAgICAgICAgICAgbGV0IGluQnVmZmVyID0gZS5pbnB1dEJ1ZmZlcjtcclxuICAgICAgICAgICAgICAvLyBvbmx5IHByb2Nlc3MgcmVxdWVzdGVkIGNvdW50IG9mIGNoYW5uZWxzXHJcbiAgICAgICAgICAgICAgbGV0IGN1cnJlbnRCdWZmZXJzID0gbmV3IEFycmF5PEZsb2F0MzJBcnJheT4oY2hhbm5lbENvdW50KTtcclxuICAgICAgICAgICAgICBmb3IgKGxldCBjaDogbnVtYmVyID0gMDsgY2ggPCBjaGFubmVsQ291bnQ7IGNoKyspIHtcclxuICAgICAgICAgICAgICAgIGxldCBjaFNhbXBsZXMgPSBpbkJ1ZmZlci5nZXRDaGFubmVsRGF0YShjaCk7XHJcbiAgICAgICAgICAgICAgICBsZXQgY2hTYW1wbGVzQ29weSA9IGNoU2FtcGxlcy5zbGljZSgwKTtcclxuICAgICAgICAgICAgICAgIGN1cnJlbnRCdWZmZXJzW2NoXSA9IGNoU2FtcGxlc0NvcHkuc2xpY2UoMCk7XHJcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5kYXRhKSB7XHJcbiAgICAgICAgICAgICAgICAgIHRoaXMuZGF0YVtjaF0ucHVzaChjaFNhbXBsZXNDb3B5KTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIGlmIChERUJVR19UUkFDRV9MRVZFTCA+IDgpIHtcclxuICAgICAgICAgICAgICAgICAgY29uc29sZS5kZWJ1ZyhcIlByb2Nlc3MgXCIgKyBjaFNhbXBsZXNDb3B5Lmxlbmd0aCArIFwiIHNhbXBsZXMuXCIpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgdGhpcy5mcmFtZXNSZWNvcmRlZCArPSBjaFNhbXBsZXNDb3B5Lmxlbmd0aDtcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgYysrO1xyXG4gICAgICAgICAgICAgIGlmICh0aGlzLmF1ZGlvT3V0U3RyZWFtKSB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLmF1ZGlvT3V0U3RyZWFtLndyaXRlKGN1cnJlbnRCdWZmZXJzKTtcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH07XHJcbiAgICAgICAgICB0aGlzLl9vcGVuZWQgPSB0cnVlO1xyXG4gICAgICAgICAgaWYgKHRoaXMubGlzdGVuZXIpIHtcclxuICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5vcGVuZWQoKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgdGhpcy5saXN0ZW5lci5lcnJvcignQnJvd3NlciBkb2VzIG5vdCBzdXBwb3J0IGF1ZGlvIHByb2Nlc3NpbmcgKFNjcmlwdFByb2Nlc3Nvci5vbmF1ZGlvcHJvY2VzcyBtZXRob2Qgbm90IGZvdW5kKSEnKTtcclxuICAgICAgICB9XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdGhpcy5saXN0ZW5lci5lcnJvcignQnJvd3NlciBkb2VzIG5vdCBzdXBwb3J0IGF1ZGlvIHByb2Nlc3NpbmcgKG5laXRoZXIgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIG5vciBTY3JpcHRQcm9jZXNzb3IpIScpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICAgICAgfSwgKGUpID0+IHtcclxuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZSArIFwiIEVycm9yIG5hbWU6IFwiICtlLm5hbWUpO1xyXG4gICAgICAgICAgaWYgKHRoaXMubGlzdGVuZXIpIHtcclxuICAgICAgICAgICAgaWYoJ05vdEFsbG93ZWRFcnJvcicgPT09IGUubmFtZSl7XHJcbiAgICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5lcnJvcignTm90IGFsbG93ZWQgdG8gdXNlIHlvdXIgbWljcm9waG9uZS4nLCdQbGVhc2UgbWFrZSBzdXJlIHRoYXQgbWljcm9waG9uZSBhY2Nlc3MgaXMgYWxsb3dlZCBmb3IgdGhpcyB3ZWIgcGFnZSBhbmQgcmVsb2FkIHRoZSBwYWdlLicpO1xyXG4gICAgICAgICAgICB9ZWxzZSBpZignTm90UmVhZGFibGVFcnJvcicgPT09IGUubmFtZSl7XHJcbiAgICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5lcnJvcignQ291bGQgbm90IHJlYWQgZnJvbSB5b3VyIGF1ZGlvIGRldmljZS4nLCdQbGVhc2UgbWFrZSBzdXJlIHlvdXIgYXVkaW8gZGV2aWNlIGlzIHdvcmtpbmcuJyk7XHJcbiAgICAgICAgICAgIH1lbHNlIGlmKCdPdmVyY29uc3RyYWluZWRFcnJvcicgPT09IGUubmFtZSl7XHJcbiAgICAgICAgICAgICAgbGV0IGVNc2c9ZS5tc2c/ZS5tc2c6J092ZXJjb25zdHJhaW5lZCBtZWRpYSBkZXZpY2UgcmVxdWVzdCBlcnJvci4nO1xyXG4gICAgICAgICAgICAgIHRoaXMubGlzdGVuZXIuZXJyb3IoZU1zZyk7XHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5lcnJvcigpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgKVxyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBfc3RhcnQoKXtcclxuICAgIGlmKHRoaXMuY29udGV4dCkge1xyXG4gICAgICB0aGlzLmluaXREYXRhKCk7XHJcbiAgICAgIGlmICh0aGlzLmF1ZGlvT3V0U3RyZWFtKSB7XHJcbiAgICAgICAgdGhpcy5hdWRpb091dFN0cmVhbS5uZXh0U3RyZWFtKClcclxuICAgICAgfVxyXG4gICAgICB0aGlzLmNhcHR1cmluZyA9IHRydWU7XHJcbiAgICAgIGlmICh0aGlzLmJ1ZmZlcmluZ05vZGUpIHtcclxuICAgICAgICB0aGlzLm1lZGlhU3RyZWFtLmNvbm5lY3QodGhpcy5idWZmZXJpbmdOb2RlKTtcclxuICAgICAgICB0aGlzLmJ1ZmZlcmluZ05vZGUuY29ubmVjdCh0aGlzLmNvbnRleHQuZGVzdGluYXRpb24pO1xyXG4gICAgICB9XHJcbiAgICAgIGlmICh0aGlzLmxpc3RlbmVyKSB7XHJcbiAgICAgICAgdGhpcy5saXN0ZW5lci5zdGFydGVkKCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcblxyXG4gIHN0YXJ0KCkge1xyXG4gICAgaWYodGhpcy5jb250ZXh0KSB7XHJcbiAgICAgIGNvbnN0IGFTdCA9IHRoaXMuY29udGV4dC5zdGF0ZTtcclxuICAgICAgaWYgKGFTdCA9PT0gJ3J1bm5pbmcnKSB7XHJcbiAgICAgICAgdGhpcy5fc3RhcnQoKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBjb25zb2xlLmRlYnVnKFwiQ2FwdHVyZSBzdGFydDogYXVkaW8gY29udGV4dCBub3QgcnVubmluZywgc3RhdGU6IFwiICsgYVN0ICsgXCIsIHJlc3VtaW5nLi4uXCIpO1xyXG4gICAgICAgIHRoaXMuY29udGV4dC5yZXN1bWUoKS50aGVuKCgpID0+IHtcclxuICAgICAgICAgIGNvbnNvbGUuZGVidWcoXCJDYXB0dXJlIHN0YXJ0OiBhdWRpbyBjb250ZXh0IHJlc3VtZWQsIHN0YXJ0aW5nLi4uXCIpO1xyXG4gICAgICAgICAgdGhpcy5fc3RhcnQoKTtcclxuICAgICAgICB9KVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBzdG9wKCkge1xyXG5cclxuICAgIGlmICh0aGlzLmRpc2Nvbm5lY3RTdHJlYW1zICYmIHRoaXMuYnVmZmVyaW5nTm9kZSkge1xyXG4gICAgICB0aGlzLm1lZGlhU3RyZWFtLmRpc2Nvbm5lY3QodGhpcy5idWZmZXJpbmdOb2RlKTtcclxuICAgICAgaWYodGhpcy5jb250ZXh0KSB7XHJcbiAgICAgICAgdGhpcy5idWZmZXJpbmdOb2RlLmRpc2Nvbm5lY3QodGhpcy5jb250ZXh0LmRlc3RpbmF0aW9uKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgIGlmICh0aGlzLmF1ZGlvT3V0U3RyZWFtKSB7XHJcbiAgICAgICAgdGhpcy5hdWRpb091dFN0cmVhbS5mbHVzaCgpO1xyXG4gICAgICB9XHJcbiAgICB9Y2F0Y2goZXJyKXtcclxuICAgICAgY29uc29sZS5lcnJvcihcIkNvdWxkIG5vdCBmbHVzaCBjYXB0dXJlIHN0cmVhbS5cIik7XHJcbiAgICAgdGhyb3cgZXJyO1xyXG4gICAgfWZpbmFsbHkge1xyXG4gICAgICB0aGlzLmNhcHR1cmluZyA9IGZhbHNlO1xyXG4gICAgICBpZiAodGhpcy5pbmRkYkF1ZGlvQnVmZmVyICYmIHRoaXMucGVyc2lzdEVycm9yKSB7XHJcbiAgICAgICAgLy8gRGVsZXRlIGludmFsaWQgcGVyc2lzdGVudCBhdWRpbyBkYXRhIGFuZCBob3BlIHRoYXQgaXQgd2lsbCBiZSBjb21wbGV0ZWx5IHVwbG9hZGVkIHRvIHRoZSBzZXJ2ZXJcclxuICAgICAgICB0aGlzLmluZGRiQXVkaW9CdWZmZXIucmVsZWFzZUF1ZGlvRGF0YSgpO1xyXG4gICAgICAgIHRoaXMuaW5kZGJBdWRpb0J1ZmZlciA9IG51bGw7XHJcbiAgICAgIH1cclxuICAgICAgaWYgKHRoaXMubGlzdGVuZXIgJiYgKHRoaXMucGVyc2lzdEVycm9yIHx8IHRoaXMucGVyc2lzdGVkKSkge1xyXG4gICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIlN0b3BwZWQgYnkgc3RvcCgpIG1ldGhvZCBjYWxsXCIpO1xyXG4gICAgICAgIHRoaXMubGlzdGVuZXIuc3RvcHBlZCgpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuXHJcbiAgc3RvcmUoKXtcclxuICAgLy8gaWYodGhpcy5kYiAmJiB0aGlzLnJlY1VVSUQpe1xyXG4gICAvLyAgICBsZXQgdHI9IHRoaXMuZGIudHJhbnNhY3Rpb24oU3ByRGIuUkVDT1JESU5HX0ZJTEVfQ0hVTktTX09CSkVDVF9TVE9SRV9OQU1FLCAncmVhZHdyaXRlJyk7XHJcbiAgIC8vICAgIGxldCByZWNGaWxlT2JqU3RvcmUgPSB0ci5vYmplY3RTdG9yZShTcHJEYi5SRUNPUkRJTkdfRklMRV9DSFVOS1NfT0JKRUNUX1NUT1JFX05BTUUpO1xyXG4gICAvL1xyXG4gICAvLyAgICAgIHRyeSB7XHJcbiAgIC8vICAgICAgICBsZXQgY2gwRGF0YT10aGlzLmRhdGFbMF07XHJcbiAgIC8vICAgICAgICBsZXQgZGF0YUNoa0NudD1jaDBEYXRhLmxlbmd0aDtcclxuICAgLy8gICAgICAgICAgbGV0IHBvcyA9IDA7XHJcbiAgIC8vICAgICAgICAgIGZvciAobGV0IGNoQ2tJZHggPSAwOyBjaENrSWR4IDwgZGF0YUNoa0NudDsgY2hDa0lkeCsrKSB7XHJcbiAgIC8vICAgICAgICAgICAgbGV0IGJ1Zkxlbj0wO1xyXG4gICAvLyAgICAgICAgICAgIGZvciAobGV0IGNoID0gMDsgY2ggPCB0aGlzLmNoYW5uZWxDb3VudDsgY2grKykge1xyXG4gICAvLyAgICAgICAgICAgICAgbGV0IGNoQ2hrID0gdGhpcy5kYXRhW2NoXVtjaENrSWR4XTtcclxuICAgLy8gICAgICAgICAgICAgIGJ1ZkxlbiA9IGNoQ2hrLmxlbmd0aDtcclxuICAgLy8gICAgICAgICAgICAgIC8vbGV0IGNhY2hlSWQgPSB1dWlkICsgJ18nICsgY2ggKyAnXycgKyBjaENrSWR4O1xyXG4gICAvLyAgICAgICAgICAgICAgbGV0IGNoa0RiSWQgPSBbdGhpcy5yZWNVVUlELCB0aGlzLmluZERiQ2hrSWR4ICsgY2hDa0lkeCwgY2hdO1xyXG4gICAvLyAgICAgICAgICAgICAgbGV0IGNyID0gcmVjRmlsZU9ialN0b3JlLmFkZChjaENoaywgY2hrRGJJZCk7XHJcbiAgIC8vICAgICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJBZGRlZDogXCIrY2grXCIgXCIrKHRoaXMuaW5kRGJDaGtJZHgrY2hDa0lkeCkpO1xyXG4gICAvLyAgICAgICAgICAgICAgY3Iub25zdWNjZXNzPSgpPT57XHJcbiAgIC8vICAgICAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIlN0b3JlZCBhdWRpbyBkYXRhIHRvIGluZGV4ZWQgZGJcIik7XHJcbiAgIC8vICAgICAgICAgICAgICB9XHJcbiAgIC8vICAgICAgICAgICAgICBjci5vbmVycm9yPSgpPT57XHJcbiAgIC8vICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJFcnJvciBzdG9yaW5nIGF1ZGlvIGRhdGEgdG8gaW5kZXhlZCBkYlwiKTtcclxuICAgLy8gICAgICAgICAgICAgIH1cclxuICAgLy8gICAgICAgICAgICB9XHJcbiAgIC8vICAgICAgICAgICAgcG9zICs9IGJ1ZkxlbjtcclxuICAgLy8gICAgICAgIH1cclxuICAgLy8gICAgICAgIHRoaXMuaW5kRGJDaGtJZHgrPWRhdGFDaGtDbnQ7XHJcbiAgIC8vXHJcbiAgIC8vICAgICAgICB0ci5vbmVycm9yID0gKGVycikgPT4ge1xyXG4gICAvLyAgICAgICAgICBjb25zb2xlLmVycm9yKCdGYWlsZWQgdG8gY2FjaGUgYXVkaW8gZGF0YSB0byBpbmRleGVkIGRiOiAnICsgZXJyKVxyXG4gICAvLyAgICAgICAgfVxyXG4gICAvLyAgICAgICAgdHIub25jb21wbGV0ZSA9ICgpID0+IHtcclxuICAgLy8gICAgICAgICAgLy9jb25zb2xlLmRlYnVnKCdUcmFuc2ZlcnJlZCBjYXB0dXJlIGF1ZGlvIGRhdGEgdG8gaW5kZXhlZCBkYiwgZGVsZXRpbmcgb3JpZ2luYWwgZGF0YSBmcm9tIG1lbW9yeS4uLicpO1xyXG4gICAvL1xyXG4gICAvLyAgICAgICAgICAvLy8gQXVkaW8gZGF0YSBzYXZlZCB0byBpbmRleCBkYiBkZWxldGUgZnJvbSBpbiBtZW1vcnkgZGF0YSBhcnJheVxyXG4gICAvLyAgICAgICAgICBmb3IgKGxldCBjaCA9IDA7IGNoIDwgdGhpcy5jaGFubmVsQ291bnQ7IGNoKyspIHtcclxuICAgLy8gICAgICAgICAgIHRoaXMuZGF0YVtjaF0uc3BsaWNlKDApO1xyXG4gICAvLyAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIlNwbGljZWQvcmVtb3ZlZCBjaDogXCIrY2gpO1xyXG4gICAvLyAgICAgICAgICB9XHJcbiAgIC8vXHJcbiAgIC8vICAgICAgICAgIHRoaXMucGVyc2lzdGVkPXRydWU7XHJcbiAgIC8vICAgICAgICAgIGlmKHRoaXMubGlzdGVuZXIgJiYgIXRoaXMuY2FwdHVyaW5nKXtcclxuICAgLy8gICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJTdG9wcGVkIGJ5IGluZGV4ZWQgZGIgaG9va1wiKTtcclxuICAgLy8gICAgICAgICAgICB0aGlzLmxpc3RlbmVyLnN0b3BwZWQoKTtcclxuICAgLy8gICAgICAgICAgfVxyXG4gICAvLyAgICAgICAgfVxyXG4gICAvLyAgICAgICAgLy8gQ29tbWl0IGNodW5rc1xyXG4gICAvLyAgICAgICAgdGhpcy5wZXJzaXN0ZWQ9ZmFsc2U7XHJcbiAgIC8vICAgICAgICB0ci5jb21taXQoKTtcclxuICAgLy8gICAgICB9IGNhdGNoIChlcnIpIHtcclxuICAgLy8gICAgICAgIGNvbnNvbGUuZXJyb3IoJ1RyYW5zZmVyIGNhcHR1cmUgYXVkaW8gZGF0YSBlcnJvcjogJytlcnIpO1xyXG4gICAvLyAgICAgIH1cclxuICAgLy8gICAgfVxyXG5cclxuICAgIC8vIGlmKCF0aGlzLmluZGRiQXVkaW9CdWZmZXIgJiYgdGhpcy5fcGVyc2lzdGVudEF1ZGlvU3RvcmFnZVRhcmdldCAmJiB0aGlzLnJlY1VVSUQpIHtcclxuICAgIC8vICAgdGhpcy5pbmRkYkF1ZGlvQnVmZmVyID0gbmV3IEluZGV4ZWREYkF1ZGlvQnVmZmVyKHRoaXMuX3BlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQsIHRoaXMuY2hhbm5lbENvdW50LHRoaXMuY3VycmVudFNhbXBsZVJhdGUsQXVkaW9DYXB0dXJlLkJVRkZFUl9TSVpFLDAsdGhpcy5yZWNVVUlEKVxyXG4gICAgLy8gfVxyXG4gICAgaWYodGhpcy5pbmRkYkF1ZGlvQnVmZmVyICYmIHRoaXMuZGF0YSl7XHJcblxyXG4gICAgICAvLyBUcnkgdG8gYXBwZW5kIHRvXHJcbiAgICAgIHRoaXMuaW5kZGJBdWRpb0J1ZmZlci5hcHBlbmRSYXdBdWRpb0RhdGEodGhpcy5kYXRhKS5zdWJzY3JpYmUoe1xyXG4gICAgICAgIGNvbXBsZXRlOiAoKSA9PiB7XHJcbiAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoJ1RyYW5zZmVycmVkIGNhcHR1cmUgYXVkaW8gZGF0YSB0byBpbmRleGVkIGRiLCBkZWxldGluZyBvcmlnaW5hbCBkYXRhIGZyb20gbWVtb3J5Li4uJyk7XHJcblxyXG4gICAgICAgICAgLy8vIEF1ZGlvIGRhdGEgc2F2ZWQgdG8gaW5kZXggZGIgZGVsZXRlIGZyb20gaW4gbWVtb3J5IGRhdGEgYXJyYXlcclxuICAgICAgICAgIGlmKHRoaXMuZGF0YSkge1xyXG4gICAgICAgICAgICBmb3IgKGxldCBjaCA9IDA7IGNoIDwgdGhpcy5jaGFubmVsQ291bnQ7IGNoKyspIHtcclxuICAgICAgICAgICAgICB0aGlzLmRhdGFbY2hdLnNwbGljZSgwKTtcclxuICAgICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJTcGxpY2VkL3JlbW92ZWQgY2g6IFwiK2NoKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgdGhpcy5wZXJzaXN0ZWQgPSB0cnVlO1xyXG4gICAgICAgICAgaWYgKHRoaXMubGlzdGVuZXIgJiYgIXRoaXMuY2FwdHVyaW5nKSB7XHJcbiAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIlN0b3BwZWQgYnkgaW5kZXhlZCBkYiBob29rXCIpO1xyXG4gICAgICAgICAgICB0aGlzLmxpc3RlbmVyLnN0b3BwZWQoKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9LGVycm9yOihlcnIpPT57XHJcbiAgICAgICAgICAvLyBPbmx5IGxvZyB0aGUgZmlyc3QgZXJyb3JcclxuICAgICAgICAgIGlmKCF0aGlzLnBlcnNpc3RFcnJvcikge1xyXG4gICAgICAgICAgICB0aGlzLnBlcnNpc3RFcnJvciA9IGVycjtcclxuICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIkVycm9yIHBlcnNpc3RpbmcgYXVkaW8gZGF0YTogXCIgKyBlcnIpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcbiAgICAgIHRoaXMucGVyc2lzdGVkPWZhbHNlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcblxyXG4gIGNsb3NlKCkge1xyXG4gICAgaWYodGhpcy5tZWRpYVN0cmVhbSkge1xyXG4gICAgICB0aGlzLm1lZGlhU3RyZWFtLmRpc2Nvbm5lY3QoKTtcclxuICAgIH1cclxuICAgIGlmICh0aGlzLnN0cmVhbSkge1xyXG4gICAgICBjb25zdCBtdHMgPSB0aGlzLnN0cmVhbS5nZXRUcmFja3MoKTtcclxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBtdHMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICBtdHNbaV0uc3RvcCgpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICB0aGlzLl9vcGVuZWQ9ZmFsc2U7XHJcbiAgfVxyXG5cclxuICBhdWRpb0J1ZmZlcigpOiBBdWRpb0J1ZmZlciB8bnVsbHtcclxuICAgIGxldCBhYjogQXVkaW9CdWZmZXJ8bnVsbD1udWxsO1xyXG4gICAgaWYodGhpcy5jb250ZXh0ICYmIHRoaXMuZGF0YSkge1xyXG4gICAgICBsZXQgZnJhbWVMZW46IG51bWJlciA9IDA7XHJcblxyXG4gICAgICBsZXQgY2gwRGF0YSA9IHRoaXMuZGF0YVswXTtcclxuXHJcbiAgICAgIGZvciAobGV0IGNoMENoayBvZiBjaDBEYXRhKSB7XHJcbiAgICAgICAgZnJhbWVMZW4gKz0gY2gwQ2hrLmxlbmd0aDtcclxuICAgICAgfVxyXG5cclxuICAgICAgdHJ5IHtcclxuICAgICAgICBhYiA9IHRoaXMuY29udGV4dC5jcmVhdGVCdWZmZXIodGhpcy5jaGFubmVsQ291bnQsIGZyYW1lTGVuLCB0aGlzLmNvbnRleHQuc2FtcGxlUmF0ZSk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycikge1xyXG4gICAgICAgIGlmIChlcnIgaW5zdGFuY2VvZiBET01FeGNlcHRpb24pIHtcclxuICAgICAgICAgIGlmIChlcnIubmFtZSA9PT0gJ05vdFN1cHBvcnRlZEVycm9yJykge1xyXG4gICAgICAgICAgICBpZiAoZnJhbWVMZW4gPT0gMCkge1xyXG4gICAgICAgICAgICAgIC8vIEVtcHR5IGJ1ZmZlcnMgYXJlIG5vdCBzdXBwb3J0ZWQgYnkgQ2hyb21pdW1cclxuICAgICAgICAgICAgICAvLyBDcmVhdGUgZHVtbXkgYnVmZmVyIHdpdGggb25lIHNhbXBsZVxyXG4gICAgICAgICAgICAgIGFiID0gdGhpcy5jb250ZXh0LmNyZWF0ZUJ1ZmZlcih0aGlzLmNoYW5uZWxDb3VudCwgMSwgdGhpcy5jb250ZXh0LnNhbXBsZVJhdGUpO1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgIHRocm93IGVycjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgdGhyb3cgZXJyO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH0gZWxzZSBpZiAoZXJyIGluc3RhbmNlb2YgUmFuZ2VFcnJvcikge1xyXG4gICAgICAgICAgLy8gT3V0IG9mIG1lbW9yeVxyXG4gICAgICAgICAgLy8gVE9ETyBXaGF0IHRvIGRvID8/XHJcbiAgICAgICAgICB0aHJvdyBlcnI7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIHRocm93IGVycjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgZm9yIChsZXQgY2ggPSAwOyBjaCA8IHRoaXMuY2hhbm5lbENvdW50OyBjaCsrKSB7XHJcbiAgICAgICAgbGV0IGNoRCA9IGFiLmdldENoYW5uZWxEYXRhKGNoKTtcclxuICAgICAgICBsZXQgcG9zID0gMDtcclxuICAgICAgICBmb3IgKGxldCBjaENoayBvZiB0aGlzLmRhdGFbY2hdKSB7XHJcbiAgICAgICAgICBsZXQgYnVmTGVuID0gY2hDaGsubGVuZ3RoO1xyXG4gICAgICAgICAgY2hELnNldChjaENoaywgcG9zKTtcclxuICAgICAgICAgIHBvcyArPSBidWZMZW47XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICByZXR1cm4gYWI7XHJcbiAgfVxyXG5cclxuICBhdWRpb0J1ZmZlckFycmF5KCk6QXJyYXlBdWRpb0J1ZmZlcnxudWxse1xyXG4gICAgICBsZXQgYXJyQWI6QXJyYXlBdWRpb0J1ZmZlcnxudWxsPW51bGw7XHJcbiAgICAgIGlmKHRoaXMuZGF0YSkge1xyXG4gICAgICAgIGFyckFiPW5ldyBBcnJheUF1ZGlvQnVmZmVyKHRoaXMuY2hhbm5lbENvdW50LCB0aGlzLmN1cnJlbnRTYW1wbGVSYXRlLCB0aGlzLmRhdGEpO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBhcnJBYjtcclxuICB9XHJcblxyXG4gIGluZGRiQXVkaW9CdWZmZXJBcnJheSgpOkluZGV4ZWREYkF1ZGlvQnVmZmVyfG51bGx7XHJcbiAgICBpZih0aGlzLnBlcnNpc3RFcnJvcil7XHJcbiAgICAgIHJldHVybiBudWxsO1xyXG4gICAgfWVsc2Uge1xyXG4gICAgICByZXR1cm4gdGhpcy5pbmRkYkF1ZGlvQnVmZmVyO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcblxyXG59XHJcblxyXG4iXX0=
|