speechrecorderng 3.0.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/esm2020/lib/action/action.mjs +2 -1
  2. package/esm2020/lib/audio/array_audio_buffer.mjs +65 -2
  3. package/esm2020/lib/audio/array_audio_buffer_input_stream.mjs +2 -2
  4. package/esm2020/lib/audio/array_audio_buffer_random_access_stream.mjs +16 -0
  5. package/esm2020/lib/audio/audio_data_holder.mjs +203 -48
  6. package/esm2020/lib/audio/audio_display.mjs +10 -34
  7. package/esm2020/lib/audio/audio_player.mjs +18 -45
  8. package/esm2020/lib/audio/capture/capture.mjs +293 -69
  9. package/esm2020/lib/audio/dsp/level_measure.mjs +211 -88
  10. package/esm2020/lib/audio/impl/wavformat.mjs +1 -1
  11. package/esm2020/lib/audio/impl/wavreader.mjs +134 -0
  12. package/esm2020/lib/audio/impl/wavwriter.mjs +10 -9
  13. package/esm2020/lib/audio/inddb_audio_buffer.mjs +508 -0
  14. package/esm2020/lib/audio/net_audio_buffer.mjs +297 -0
  15. package/esm2020/lib/audio/persistor.mjs +8 -2
  16. package/esm2020/lib/audio/playback/array_audio_buffer_source_node.mjs +15 -154
  17. package/esm2020/lib/audio/playback/audio_source_node.mjs +18 -0
  18. package/esm2020/lib/audio/playback/audio_source_worklet_module_loader.mjs +167 -0
  19. package/esm2020/lib/audio/playback/inddb_audio_buffer_source_node.mjs +167 -0
  20. package/esm2020/lib/audio/playback/net_audio_buffer_source_node.mjs +218 -0
  21. package/esm2020/lib/audio/playback/player.mjs +190 -171
  22. package/esm2020/lib/audio/ui/audio_canvas_layer_comp.mjs +35 -76
  23. package/esm2020/lib/audio/ui/audio_display_control.mjs +12 -24
  24. package/esm2020/lib/audio/ui/audio_display_scroll_pane.mjs +14 -45
  25. package/esm2020/lib/audio/ui/audiosignal.mjs +333 -267
  26. package/esm2020/lib/audio/ui/container.mjs +40 -57
  27. package/esm2020/lib/audio/ui/livelevel.mjs +53 -52
  28. package/esm2020/lib/audio/ui/scroll_pane_horizontal.mjs +5 -19
  29. package/esm2020/lib/audio/ui/sonagram.mjs +386 -339
  30. package/esm2020/lib/db/inddb.mjs +120 -0
  31. package/esm2020/lib/io/BinaryReader.mjs +85 -0
  32. package/esm2020/lib/io/stream.mjs +101 -1
  33. package/esm2020/lib/net/uploader.mjs +111 -5
  34. package/esm2020/lib/recorder_component.mjs +59 -1
  35. package/esm2020/lib/speechrecorder/project/project.mjs +10 -1
  36. package/esm2020/lib/speechrecorder/project/project.service.mjs +3 -3
  37. package/esm2020/lib/speechrecorder/recording.mjs +30 -6
  38. package/esm2020/lib/speechrecorder/recordings/basic_recording.service.mjs +213 -0
  39. package/esm2020/lib/speechrecorder/recordings/recordings.service.mjs +732 -65
  40. package/esm2020/lib/speechrecorder/script/script.service.mjs +3 -3
  41. package/esm2020/lib/speechrecorder/session/audiorecorder.mjs +358 -184
  42. package/esm2020/lib/speechrecorder/session/basicrecorder.mjs +181 -28
  43. package/esm2020/lib/speechrecorder/session/controlpanel.mjs +47 -129
  44. package/esm2020/lib/speechrecorder/session/item.mjs +13 -1
  45. package/esm2020/lib/speechrecorder/session/progress.mjs +26 -55
  46. package/esm2020/lib/speechrecorder/session/prompting.mjs +33 -176
  47. package/esm2020/lib/speechrecorder/session/recorder_combi_pane.mjs +6 -6
  48. package/esm2020/lib/speechrecorder/session/recording_file_cache.mjs +28 -15
  49. package/esm2020/lib/speechrecorder/session/recording_list.mjs +92 -55
  50. package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-meta.component.mjs +9 -13
  51. package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-navi.component.mjs +9 -18
  52. package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-u-i.component.mjs +10 -36
  53. package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-view.component.mjs +10 -37
  54. package/esm2020/lib/speechrecorder/session/recordingfile/recordingfile-service.mjs +80 -56
  55. package/esm2020/lib/speechrecorder/session/session.service.mjs +3 -3
  56. package/esm2020/lib/speechrecorder/session/session_finished_dialog.mjs +4 -4
  57. package/esm2020/lib/speechrecorder/session/sessionmanager.mjs +409 -165
  58. package/esm2020/lib/speechrecorder/session/warning_bar.mjs +6 -29
  59. package/esm2020/lib/speechrecorder/spruploader.mjs +3 -3
  60. package/esm2020/lib/speechrecorder/startstopsignal/ui/simpletrafficlight.mjs +6 -41
  61. package/esm2020/lib/speechrecorderng.component.mjs +42 -31
  62. package/esm2020/lib/speechrecorderng.module.mjs +5 -5
  63. package/esm2020/lib/spr.config.mjs +3 -3
  64. package/esm2020/lib/spr.module.version.mjs +2 -2
  65. package/esm2020/lib/ui/canvas_layer_comp.mjs +3 -3
  66. package/esm2020/lib/ui/message_dialog.mjs +7 -7
  67. package/esm2020/lib/ui/recordingitem_display.mjs +26 -59
  68. package/esm2020/lib/utils/scrollIntoViewToBottom.mjs +3 -3
  69. package/esm2020/lib/utils/ua-parser.mjs +28 -4
  70. package/esm2020/lib/utils/utils.mjs +29 -1
  71. package/fesm2015/speechrecorderng.mjs +12755 -9195
  72. package/fesm2015/speechrecorderng.mjs.map +1 -1
  73. package/fesm2020/speechrecorderng.mjs +12652 -9101
  74. package/fesm2020/speechrecorderng.mjs.map +1 -1
  75. package/{speechrecorderng.d.ts → index.d.ts} +0 -0
  76. package/lib/audio/array_audio_buffer.d.ts +17 -1
  77. package/lib/audio/array_audio_buffer_random_access_stream.d.ts +9 -0
  78. package/lib/audio/audio_data_holder.d.ts +69 -14
  79. package/lib/audio/audio_display.d.ts +1 -1
  80. package/lib/audio/audio_player.d.ts +3 -7
  81. package/lib/audio/capture/capture.d.ts +24 -4
  82. package/lib/audio/dsp/level_measure.d.ts +2 -23
  83. package/lib/audio/impl/wavformat.d.ts +3 -3
  84. package/lib/audio/impl/wavreader.d.ts +16 -0
  85. package/lib/audio/impl/wavwriter.d.ts +1 -4
  86. package/lib/audio/inddb_audio_buffer.d.ts +68 -0
  87. package/lib/audio/net_audio_buffer.d.ts +59 -0
  88. package/lib/audio/persistor.d.ts +3 -9
  89. package/lib/audio/playback/array_audio_buffer_source_node.d.ts +2 -8
  90. package/lib/audio/playback/audio_source_node.d.ts +10 -0
  91. package/lib/audio/playback/audio_source_worklet_module_loader.d.ts +4 -0
  92. package/lib/audio/playback/inddb_audio_buffer_source_node.d.ts +21 -0
  93. package/lib/audio/playback/net_audio_buffer_source_node.d.ts +27 -0
  94. package/lib/audio/playback/player.d.ts +19 -16
  95. package/lib/audio/ui/audio_canvas_layer_comp.d.ts +3 -3
  96. package/lib/audio/ui/audio_display_control.d.ts +3 -6
  97. package/lib/audio/ui/audio_display_scroll_pane.d.ts +1 -1
  98. package/lib/audio/ui/audiosignal.d.ts +4 -3
  99. package/lib/audio/ui/container.d.ts +1 -1
  100. package/lib/audio/ui/livelevel.d.ts +11 -4
  101. package/lib/audio/ui/scroll_pane_horizontal.d.ts +1 -1
  102. package/lib/audio/ui/sonagram.d.ts +4 -3
  103. package/lib/db/inddb.d.ts +21 -0
  104. package/lib/io/BinaryReader.d.ts +18 -0
  105. package/lib/io/stream.d.ts +18 -0
  106. package/lib/net/uploader.d.ts +20 -2
  107. package/lib/recorder_component.d.ts +5 -0
  108. package/lib/speechrecorder/project/project.d.ts +9 -0
  109. package/lib/speechrecorder/recording.d.ts +8 -2
  110. package/lib/speechrecorder/recordings/basic_recording.service.d.ts +27 -0
  111. package/lib/speechrecorder/recordings/recordings.service.d.ts +21 -6
  112. package/lib/speechrecorder/session/audiorecorder.d.ts +7 -18
  113. package/lib/speechrecorder/session/basicrecorder.d.ts +28 -4
  114. package/lib/speechrecorder/session/controlpanel.d.ts +7 -7
  115. package/lib/speechrecorder/session/item.d.ts +1 -0
  116. package/lib/speechrecorder/session/progress.d.ts +2 -1
  117. package/lib/speechrecorder/session/prompting.d.ts +5 -5
  118. package/lib/speechrecorder/session/recorder_combi_pane.d.ts +1 -1
  119. package/lib/speechrecorder/session/recording_file_cache.d.ts +7 -4
  120. package/lib/speechrecorder/session/recording_list.d.ts +2 -1
  121. package/lib/speechrecorder/session/recordingfile/recording-file-meta.component.d.ts +1 -1
  122. package/lib/speechrecorder/session/recordingfile/recording-file-navi.component.d.ts +1 -1
  123. package/lib/speechrecorder/session/recordingfile/recording-file-u-i.component.d.ts +1 -1
  124. package/lib/speechrecorder/session/recordingfile/recording-file-view.component.d.ts +1 -1
  125. package/lib/speechrecorder/session/recordingfile/recordingfile-service.d.ts +4 -5
  126. package/lib/speechrecorder/session/session_finished_dialog.d.ts +1 -1
  127. package/lib/speechrecorder/session/sessionmanager.d.ts +10 -10
  128. package/lib/speechrecorder/session/warning_bar.d.ts +1 -1
  129. package/lib/speechrecorder/startstopsignal/ui/simpletrafficlight.d.ts +1 -1
  130. package/lib/speechrecorderng.component.d.ts +2 -1
  131. package/lib/spr.module.version.d.ts +1 -1
  132. package/lib/ui/canvas_layer_comp.d.ts +1 -1
  133. package/lib/ui/message_dialog.d.ts +1 -1
  134. package/lib/ui/recordingitem_display.d.ts +3 -2
  135. package/lib/utils/scrollIntoViewToBottom.d.ts +1 -1
  136. package/lib/utils/ua-parser.d.ts +4 -1
  137. package/lib/utils/utils.d.ts +6 -0
  138. package/package.json +5 -4
  139. package/esm2020/lib/math/utils.mjs +0 -14
  140. package/esm2020/lib/utils/css_utils.mjs +0 -7
  141. package/lib/math/utils.d.ts +0 -3
  142. package/lib/utils/css_utils.d.ts +0 -3
@@ -0,0 +1,213 @@
1
+ import { __decorate, __param } from "tslib";
2
+ import { HttpHeaders } from "@angular/common/http";
3
+ import { Inject } from "@angular/core";
4
+ import { ApiType, SPEECHRECORDER_CONFIG } from "../../spr.config";
5
+ import { Observable } from "rxjs";
6
+ import { NetAudioBuffer } from "../../audio/net_audio_buffer";
7
+ import { UUID } from "../../utils/utils";
8
+ import { WavReader } from "../../audio/impl/wavreader";
9
+ export class ChunkDownload {
10
+ constructor(_orgPCMAudioFormat, _orgFrameLength, _decodedAudioBuffer) {
11
+ this._orgPCMAudioFormat = _orgPCMAudioFormat;
12
+ this._orgFrameLength = _orgFrameLength;
13
+ this._decodedAudioBuffer = _decodedAudioBuffer;
14
+ }
15
+ get orgPCMAudioFormat() {
16
+ return this._orgPCMAudioFormat;
17
+ }
18
+ get orgFrameLength() {
19
+ return this._orgFrameLength;
20
+ }
21
+ get decodedAudioBuffer() {
22
+ return this._decodedAudioBuffer;
23
+ }
24
+ }
25
+ let BasicRecordingService = class BasicRecordingService {
26
+ constructor(http, config) {
27
+ this.http = http;
28
+ this.config = config;
29
+ this._maxAutoNetMemStoreSamples = BasicRecordingService.DEFAULT_MAX_NET_AUTO_MEM_STORE_SAMPLES;
30
+ this.withCredentials = false;
31
+ this.apiEndPoint = '';
32
+ if (config && config.apiEndPoint) {
33
+ this.apiEndPoint = config.apiEndPoint;
34
+ }
35
+ if (this.apiEndPoint !== '') {
36
+ this.apiEndPoint = this.apiEndPoint + '/';
37
+ }
38
+ if (config != null && config.withCredentials != null) {
39
+ this.withCredentials = config.withCredentials;
40
+ }
41
+ }
42
+ audioRequestByURL(audioBaseUrl, audioURLSearchParams) {
43
+ let audioUrl = audioBaseUrl;
44
+ if (this.config && this.config.apiType === ApiType.FILES) {
45
+ // for development and demo
46
+ audioUrl = audioUrl + '.wav';
47
+ // append UUID to make request URL unique to avoid localhost server caching
48
+ audioURLSearchParams.set('requestUUID', UUID.generate());
49
+ //audioUrl = audioUrl + '.wav?requestUUID=' + UUID.generate();
50
+ }
51
+ audioUrl = audioUrl + '?' + audioURLSearchParams.toString();
52
+ let headers = new HttpHeaders();
53
+ headers = headers.set('Accept', 'audio/wav');
54
+ return this.http.get(audioUrl, {
55
+ headers: headers,
56
+ observe: 'response',
57
+ responseType: 'arraybuffer',
58
+ withCredentials: this.withCredentials
59
+ });
60
+ }
61
+ chunkAudioRequest(aCtx, baseAudioUrl, startFrame = 0, frameLength) {
62
+ let ausps = new URLSearchParams();
63
+ ausps.set('startFrame', startFrame.toString());
64
+ ausps.set('frameLength', frameLength.toString());
65
+ if (this.config && this.config.apiType === ApiType.FILES) {
66
+ // for development and demo
67
+ // append UUID to make request URL unique to avoid localhost server caching
68
+ //audioUrl = audioUrl + '.wav?requestUUID=' + UUID.generate();
69
+ ausps.set('requestUUID', UUID.generate());
70
+ }
71
+ let obs = new Observable(observer => {
72
+ this.audioRequestByURL(baseAudioUrl, ausps).subscribe(resp => {
73
+ // Do not use Promise version, which does not work with Safari 13 (13.0.5)
74
+ if (resp.body) {
75
+ //console.debug("chunkAudioRequest: observer.closed: "+observer.closed);
76
+ //console.debug("Audio file bytes: "+resp.body.byteLength);
77
+ // Check original audio format
78
+ let wr = new WavReader(resp.body);
79
+ const pcmFmt = wr.readFormat();
80
+ const orgFl = wr.frameLength();
81
+ // if(pcmFmt){
82
+ // console.debug("Original WAVE format of download chunk: "+pcmFmt);
83
+ // }else{
84
+ // console.error("Could not read WAVE format of original download chunk!");
85
+ // }
86
+ // if(orgFl){
87
+ // console.debug("Original frame length of download chunk: "+orgFl);
88
+ // }else{
89
+ // console.error("Could not read WAVE format of original download chunk!");
90
+ // }
91
+ if (pcmFmt && orgFl) {
92
+ aCtx.decodeAudioData(resp.body, ab => {
93
+ //console.debug("Decoded audio chunk frames: "+ab.length);
94
+ let chDl = new ChunkDownload(pcmFmt, orgFl, ab);
95
+ observer.next(chDl);
96
+ observer.complete();
97
+ }, error => {
98
+ //if(error instanceof HttpErrorResponse) {
99
+ // if (error.status == 404) {
100
+ // // Interpret not as an error, the file ist not recorded yet
101
+ // observer.next(null);
102
+ // observer.complete()
103
+ // } else {
104
+ // // all other states are errors
105
+ console.error("Recordings service chunkAudioRequest error decoding audio data: " + error.name + ": " + error.message);
106
+ observer.error(error);
107
+ // }
108
+ // }
109
+ });
110
+ }
111
+ else {
112
+ const errMsg = 'Could not parse audio header for format and/or frame length of download.';
113
+ console.error(errMsg);
114
+ observer.error(errMsg);
115
+ }
116
+ }
117
+ else {
118
+ const errMsg = 'Fetching audio file: response has no body';
119
+ console.error(errMsg);
120
+ observer.error(errMsg);
121
+ }
122
+ }, (error) => {
123
+ // all other states are errors
124
+ //const errMsg='Fetching audio file HTTP error: '+error;
125
+ //console.error(errMsg);
126
+ observer.error(error);
127
+ //observer.complete();
128
+ });
129
+ });
130
+ return obs;
131
+ }
132
+ chunkAudioRequestToNetAudioBuffer(aCtx, baseAudioUrl, startFrame = 0, orgSampleRate, seconds, frames) {
133
+ //let audioUrl=baseAudioUrl+'?startFrame='+startFrame+'&frameLength='+frameLength;
134
+ //let audioUrl=new URL(baseAudioUrl);
135
+ // if(orgSampleRate!=null && frameLength%orgSampleRate>0){
136
+ // const errMsg='frameLength must be equal or multiple of original samplerate.';
137
+ // console.error(errMsg+' ('+frameLength+'%'+orgSampleRate+'=='+(frameLength%orgSampleRate)+')');
138
+ // throw Error(errMsg)
139
+ // }
140
+ let frameLength = orgSampleRate * Math.round(seconds);
141
+ let ausps = new URLSearchParams();
142
+ ausps.set('startFrame', startFrame.toString());
143
+ ausps.set('frameLength', frameLength.toString());
144
+ if (this.config && this.config.apiType === ApiType.FILES) {
145
+ // for development and demo
146
+ // append UUID to make request URL unique to avoid localhost server caching
147
+ //audioUrl = audioUrl + '.wav?requestUUID=' + UUID.generate();
148
+ ausps.set('requestUUID', UUID.generate());
149
+ }
150
+ let obs = new Observable(subscriber => {
151
+ this.audioRequestByURL(baseAudioUrl, ausps).subscribe({ next: (resp) => {
152
+ // Do not use Promise version, which does not work with Safari 13 (13.0.5)
153
+ if (resp.body) {
154
+ //console.debug("chunkAudioRequestTonetAb: subscriber.closed: "+subscriber.closed);
155
+ //console.debug("chunkAudioRequestTonetAb: Audio file bytes: "+resp.body.byteLength);
156
+ aCtx.decodeAudioData(resp.body, ab => {
157
+ //console.debug("chunkAudioRequestTonetAb: Decoded audio chunk frames for netAb: "+ab.length);
158
+ //console.debug("chunkAudioRequestTonetAb: Create netAb ab from chunk ab...");
159
+ if (frames === null) {
160
+ subscriber.error(new Error('Could not get frame length from recording file object'));
161
+ }
162
+ else {
163
+ let fl = frames;
164
+ if (ab.sampleRate !== orgSampleRate) {
165
+ if (orgSampleRate && frames) {
166
+ fl = Math.round(ab.sampleRate * frames / orgSampleRate);
167
+ //console.debug("Platform sr: "+ab.sampleRate+", file sr: "+orgSampleRate+", decoded/org frame length: "+fl+"/"+frames+", ab.length: "+ab.length);
168
+ }
169
+ }
170
+ let nab = NetAudioBuffer.fromChunkAudioBuffer(aCtx, this, baseAudioUrl, ab, fl, frameLength);
171
+ //let rp=new ReadyProvider();
172
+ //nab.readyProvider=rp;
173
+ //rp.ready();
174
+ nab.ready();
175
+ if (nab.frameLen < frameLength) {
176
+ //console.debug("chunkAudioRequestTonetAb: Built netAb ab from chunk ab: First chunk shorter tha frameLength ("+netAbAudioBuffer.frameLen+"<"+frameLength+"), assuming end of data, sealing netAb ab.");
177
+ nab.seal();
178
+ }
179
+ subscriber.next(nab);
180
+ subscriber.complete();
181
+ }
182
+ }, error => {
183
+ console.error('chunkAudioRequestToNetAb: error: ' + error);
184
+ //if(error instanceof HttpErrorResponse) {
185
+ subscriber.error(error);
186
+ //}
187
+ });
188
+ }
189
+ else {
190
+ console.error('chunkAudioRequestToNetAb: Fetching audio file: response has no body');
191
+ subscriber.error('chunkAudioRequestToNetAb: Fetching audio file: response has no body');
192
+ }
193
+ },
194
+ error: (error) => {
195
+ console.error('chunkAudioRequestToNetAb: error: ' + error);
196
+ subscriber.error(error);
197
+ //subscriber.complete();
198
+ }
199
+ });
200
+ });
201
+ return obs;
202
+ }
203
+ };
204
+ // iPad 9th generation, iOS 15.7.1, sometimes:
205
+ // Failed to load resource: WebKit hat einen internen Fehler festgestellt
206
+ // Firefox on Windows crashes
207
+ BasicRecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS = 10;
208
+ BasicRecordingService.DEFAULT_MAX_NET_AUTO_MEM_STORE_SAMPLES = 2880000 * 5; // Default 5 minutes one channel at 48kHz
209
+ BasicRecordingService = __decorate([
210
+ __param(1, Inject(SPEECHRECORDER_CONFIG))
211
+ ], BasicRecordingService);
212
+ export { BasicRecordingService };
213
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFzaWNfcmVjb3JkaW5nLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9zcGVlY2hyZWNvcmRlcm5nL3NyYy9saWIvc3BlZWNocmVjb3JkZXIvcmVjb3JkaW5ncy9iYXNpY19yZWNvcmRpbmcuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxFQUFhLFdBQVcsRUFBZSxNQUFNLHNCQUFzQixDQUFDO0FBQzNFLE9BQU8sRUFBQyxNQUFNLEVBQUMsTUFBTSxlQUFlLENBQUM7QUFDckMsT0FBTyxFQUFDLE9BQU8sRUFBRSxxQkFBcUIsRUFBdUIsTUFBTSxrQkFBa0IsQ0FBQztBQUN0RixPQUFPLEVBQUMsVUFBVSxFQUFDLE1BQU0sTUFBTSxDQUFDO0FBQ2hDLE9BQU8sRUFBQyxjQUFjLEVBQUMsTUFBTSw4QkFBOEIsQ0FBQztBQUM1RCxPQUFPLEVBQUMsSUFBSSxFQUFDLE1BQU0sbUJBQW1CLENBQUM7QUFDdkMsT0FBTyxFQUFDLFNBQVMsRUFBQyxNQUFNLDRCQUE0QixDQUFDO0FBR3JELE1BQU0sT0FBTyxhQUFhO0lBWXhCLFlBQW9CLGtCQUFpQyxFQUFTLGVBQXNCLEVBQVMsbUJBQStCO1FBQXhHLHVCQUFrQixHQUFsQixrQkFBa0IsQ0FBZTtRQUFTLG9CQUFlLEdBQWYsZUFBZSxDQUFPO1FBQVMsd0JBQW1CLEdBQW5CLG1CQUFtQixDQUFZO0lBQUUsQ0FBQztJQVgvSCxJQUFJLGlCQUFpQjtRQUNuQixPQUFPLElBQUksQ0FBQyxrQkFBa0IsQ0FBQztJQUNqQyxDQUFDO0lBRUQsSUFBSSxjQUFjO1FBQ2hCLE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBQztJQUM5QixDQUFDO0lBRUQsSUFBSSxrQkFBa0I7UUFDcEIsT0FBTyxJQUFJLENBQUMsbUJBQW1CLENBQUM7SUFDbEMsQ0FBQztDQUVGO0FBRUQsSUFBYSxxQkFBcUIsR0FBbEMsTUFBYSxxQkFBcUI7SUFT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dXJjZXNDb250ZW50IjpbImltcG9ydCB7SHR0cENsaWVudCwgSHR0cEhlYWRlcnMsIEh0dHBSZXNwb25zZX0gZnJvbSBcIkBhbmd1bGFyL2NvbW1vbi9odHRwXCI7XHJcbmltcG9ydCB7SW5qZWN0fSBmcm9tIFwiQGFuZ3VsYXIvY29yZVwiO1xyXG5pbXBvcnQge0FwaVR5cGUsIFNQRUVDSFJFQ09SREVSX0NPTkZJRywgU3BlZWNoUmVjb3JkZXJDb25maWd9IGZyb20gXCIuLi8uLi9zcHIuY29uZmlnXCI7XHJcbmltcG9ydCB7T2JzZXJ2YWJsZX0gZnJvbSBcInJ4anNcIjtcclxuaW1wb3J0IHtOZXRBdWRpb0J1ZmZlcn0gZnJvbSBcIi4uLy4uL2F1ZGlvL25ldF9hdWRpb19idWZmZXJcIjtcclxuaW1wb3J0IHtVVUlEfSBmcm9tIFwiLi4vLi4vdXRpbHMvdXRpbHNcIjtcclxuaW1wb3J0IHtXYXZSZWFkZXJ9IGZyb20gXCIuLi8uLi9hdWRpby9pbXBsL3dhdnJlYWRlclwiO1xyXG5pbXBvcnQge1BDTUF1ZGlvRm9ybWF0fSBmcm9tIFwiLi4vLi4vYXVkaW8vZm9ybWF0XCI7XHJcblxyXG5leHBvcnQgY2xhc3MgQ2h1bmtEb3dubG9hZHtcclxuICBnZXQgb3JnUENNQXVkaW9Gb3JtYXQoKTogUENNQXVkaW9Gb3JtYXQge1xyXG4gICAgcmV0dXJuIHRoaXMuX29yZ1BDTUF1ZGlvRm9ybWF0O1xyXG4gIH1cclxuXHJcbiAgZ2V0IG9yZ0ZyYW1lTGVuZ3RoKCk6IG51bWJlciB7XHJcbiAgICByZXR1cm4gdGhpcy5fb3JnRnJhbWVMZW5ndGg7XHJcbiAgfVxyXG5cclxuICBnZXQgZGVjb2RlZEF1ZGlvQnVmZmVyKCk6IEF1ZGlvQnVmZmVyIHtcclxuICAgIHJldHVybiB0aGlzLl9kZWNvZGVkQXVkaW9CdWZmZXI7XHJcbiAgfVxyXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgX29yZ1BDTUF1ZGlvRm9ybWF0OlBDTUF1ZGlvRm9ybWF0LHByaXZhdGUgX29yZ0ZyYW1lTGVuZ3RoOm51bWJlcixwcml2YXRlIF9kZWNvZGVkQXVkaW9CdWZmZXI6QXVkaW9CdWZmZXIpe31cclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIEJhc2ljUmVjb3JkaW5nU2VydmljZXtcclxuICAvLyBpUGFkIDl0aCBnZW5lcmF0aW9uLCBpT1MgMTUuNy4xLCBzb21ldGltZXM6XHJcbiAgLy8gRmFpbGVkIHRvIGxvYWQgcmVzb3VyY2U6IFdlYktpdCBoYXQgZWluZW4gaW50ZXJuZW4gRmVobGVyIGZlc3RnZXN0ZWxsdFxyXG4gIC8vIEZpcmVmb3ggb24gV2luZG93cyBjcmFzaGVzXHJcbiAgcHVibGljIHN0YXRpYyByZWFkb25seSBERUZBVUxUX0NIVU5LRURfRE9XTkxPQURfU0VDT05EUzpudW1iZXI9MTA7XHJcbiAgcHVibGljIHN0YXRpYyByZWFkb25seSBERUZBVUxUX01BWF9ORVRfQVVUT19NRU1fU1RPUkVfU0FNUExFUzpudW1iZXI9Mjg4MDAwMCo1OyAvLyBEZWZhdWx0IDUgbWludXRlcyBvbmUgY2hhbm5lbCBhdCA0OGtIelxyXG4gIHByb3RlY3RlZCBfbWF4QXV0b05ldE1lbVN0b3JlU2FtcGxlczpudW1iZXI9QmFzaWNSZWNvcmRpbmdTZXJ2aWNlLkRFRkFVTFRfTUFYX05FVF9BVVRPX01FTV9TVE9SRV9TQU1QTEVTO1xyXG4gICAgcHJvdGVjdGVkIGFwaUVuZFBvaW50OiBzdHJpbmc7XHJcbiAgICBwcm90ZWN0ZWQgd2l0aENyZWRlbnRpYWxzOiBib29sZWFuID0gZmFsc2U7XHJcbiAgICBjb25zdHJ1Y3Rvcihwcm90ZWN0ZWQgaHR0cDogSHR0cENsaWVudCwgQEluamVjdChTUEVFQ0hSRUNPUkRFUl9DT05GSUcpIHByb3RlY3RlZCBjb25maWc/OiBTcGVlY2hSZWNvcmRlckNvbmZpZykge1xyXG4gICAgICAgIHRoaXMuYXBpRW5kUG9pbnQgPSAnJ1xyXG5cclxuICAgICAgICBpZiAoY29uZmlnICYmIGNvbmZpZy5hcGlFbmRQb2ludCkge1xyXG4gICAgICAgICAgICB0aGlzLmFwaUVuZFBvaW50ID0gY29uZmlnLmFwaUVuZFBvaW50O1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAodGhpcy5hcGlFbmRQb2ludCAhPT0gJycpIHtcclxuICAgICAgICAgICAgdGhpcy5hcGlFbmRQb2ludCA9IHRoaXMuYXBpRW5kUG9pbnQgKyAnLydcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKGNvbmZpZyAhPSBudWxsICYmIGNvbmZpZy53aXRoQ3JlZGVudGlhbHMgIT0gbnVsbCkge1xyXG4gICAgICAgICAgICB0aGlzLndpdGhDcmVkZW50aWFscyA9IGNvbmZpZy53aXRoQ3JlZGVudGlhbHM7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHByb3RlY3RlZCBhdWRpb1JlcXVlc3RCeVVSTChhdWRpb0Jhc2VVcmw6c3RyaW5nLGF1ZGlvVVJMU2VhcmNoUGFyYW1zOlVSTFNlYXJjaFBhcmFtcyk6IE9ic2VydmFibGU8SHR0cFJlc3BvbnNlPEFycmF5QnVmZmVyPj4ge1xyXG4gICAgICAgIGxldCBhdWRpb1VybD1hdWRpb0Jhc2VVcmw7XHJcbiAgICAgICAgaWYgKHRoaXMuY29uZmlnICYmIHRoaXMuY29uZmlnLmFwaVR5cGUgPT09IEFwaVR5cGUuRklMRVMpIHtcclxuICAgICAgICAgICAgLy8gZm9yIGRldmVsb3BtZW50IGFuZCBkZW1vXHJcbiAgICAgICAgICAgIGF1ZGlvVXJsPWF1ZGlvVXJsKycud2F2JztcclxuICAgICAgICAgICAgLy8gYXBwZW5kIFVVSUQgdG8gbWFrZSByZXF1ZXN0IFVSTCB1bmlxdWUgdG8gYXZvaWQgbG9jYWxob3N0IHNlcnZlciBjYWNoaW5nXHJcbiAgICAgICAgICAgIGF1ZGlvVVJMU2VhcmNoUGFyYW1zLnNldCgncmVxdWVzdFVVSUQnLFVVSUQuZ2VuZXJhdGUoKSk7XHJcbiAgICAgICAgICAgIC8vYXVkaW9VcmwgPSBhdWRpb1VybCArICcud2F2P3JlcXVlc3RVVUlEPScgKyBVVUlELmdlbmVyYXRlKCk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBhdWRpb1VybD1hdWRpb1VybCsnPycrYXVkaW9VUkxTZWFyY2hQYXJhbXMudG9TdHJpbmcoKTtcclxuXHJcbiAgICAgICAgbGV0IGhlYWRlcnMgPSBuZXcgSHR0cEhlYWRlcnMoKTtcclxuICAgICAgICBoZWFkZXJzID0gaGVhZGVycy5zZXQoJ0FjY2VwdCcsICdhdWRpby93YXYnKTtcclxuICAgICAgICByZXR1cm4gdGhpcy5odHRwLmdldChhdWRpb1VybCwge1xyXG4gICAgICAgICAgICBoZWFkZXJzOiBoZWFkZXJzLFxyXG4gICAgICAgICAgICBvYnNlcnZlOiAncmVzcG9uc2UnLFxyXG4gICAgICAgICAgICByZXNwb25zZVR5cGU6ICdhcnJheWJ1ZmZlcicsXHJcbiAgICAgICAgICAgIHdpdGhDcmVkZW50aWFsczogdGhpcy53aXRoQ3JlZGVudGlhbHNcclxuICAgICAgICB9KTtcclxuXHJcbiAgICB9XHJcblxyXG4gICAgcHVibGljIGNodW5rQXVkaW9SZXF1ZXN0KGFDdHg6QXVkaW9Db250ZXh0LGJhc2VBdWRpb1VybDpzdHJpbmcsc3RhcnRGcmFtZTpudW1iZXI9MCxmcmFtZUxlbmd0aDpudW1iZXIpOiBPYnNlcnZhYmxlPENodW5rRG93bmxvYWR8bnVsbD4ge1xyXG5cclxuICAgICAgICBsZXQgYXVzcHM9bmV3IFVSTFNlYXJjaFBhcmFtcygpO1xyXG4gICAgICAgIGF1c3BzLnNldCgnc3RhcnRGcmFtZScsc3RhcnRGcmFtZS50b1N0cmluZygpKTtcclxuICAgICAgICBhdXNwcy5zZXQoJ2ZyYW1lTGVuZ3RoJyxmcmFtZUxlbmd0aC50b1N0cmluZygpKTtcclxuICAgICAgICBpZiAodGhpcy5jb25maWcgJiYgdGhpcy5jb25maWcuYXBpVHlwZSA9PT0gQXBpVHlwZS5GSUxFUykge1xyXG4gICAgICAgICAgICAvLyBmb3IgZGV2ZWxvcG1lbnQgYW5kIGRlbW9cclxuICAgICAgICAgICAgLy8gYXBwZW5kIFVVSUQgdG8gbWFrZSByZXF1ZXN0IFVSTCB1bmlxdWUgdG8gYXZvaWQgbG9jYWxob3N0IHNlcnZlciBjYWNoaW5nXHJcbiAgICAgICAgICAgIC8vYXVkaW9VcmwgPSBhdWRpb1VybCArICcud2F2P3JlcXVlc3RVVUlEPScgKyBVVUlELmdlbmVyYXRlKCk7XHJcbiAgICAgICAgICAgIGF1c3BzLnNldCgncmVxdWVzdFVVSUQnLFVVSUQuZ2VuZXJhdGUoKSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBsZXQgb2JzPW5ldyBPYnNlcnZhYmxlPENodW5rRG93bmxvYWR8bnVsbD4ob2JzZXJ2ZXI9PiB7XHJcbiAgICAgICAgICAgIHRoaXMuYXVkaW9SZXF1ZXN0QnlVUkwoYmFzZUF1ZGlvVXJsLGF1c3BzKS5zdWJzY3JpYmUocmVzcCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgLy8gRG8gbm90IHVzZSBQcm9taXNlIHZlcnNpb24sIHdoaWNoIGRvZXMgbm90IHdvcmsgd2l0aCBTYWZhcmkgMTMgKDEzLjAuNSlcclxuICAgICAgICAgICAgICAgICAgICBpZiAocmVzcC5ib2R5KSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcImNodW5rQXVkaW9SZXF1ZXN0OiBvYnNlcnZlci5jbG9zZWQ6IFwiK29ic2VydmVyLmNsb3NlZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIkF1ZGlvIGZpbGUgYnl0ZXM6IFwiK3Jlc3AuYm9keS5ieXRlTGVuZ3RoKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIENoZWNrIG9yaWdpbmFsIGF1ZGlvIGZvcm1hdFxyXG4gICAgICAgICAgICAgICAgICAgICAgICBsZXQgd3I9bmV3IFdhdlJlYWRlcihyZXNwLmJvZHkpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBwY21GbXQ9d3IucmVhZEZvcm1hdCgpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBvcmdGbD13ci5mcmFtZUxlbmd0aCgpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBpZihwY21GbXQpe1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyAgIGNvbnNvbGUuZGVidWcoXCJPcmlnaW5hbCBXQVZFIGZvcm1hdCBvZiBkb3dubG9hZCBjaHVuazogXCIrcGNtRm10KTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gfWVsc2V7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vICAgY29uc29sZS5lcnJvcihcIkNvdWxkIG5vdCByZWFkIFdBVkUgZm9ybWF0IG9mIG9yaWdpbmFsIGRvd25sb2FkIGNodW5rIVwiKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBpZihvcmdGbCl7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vICAgY29uc29sZS5kZWJ1ZyhcIk9yaWdpbmFsIGZyYW1lIGxlbmd0aCBvZiBkb3dubG9hZCBjaHVuazogXCIrb3JnRmwpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyB9ZWxzZXtcclxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gICBjb25zb2xlLmVycm9yKFwiQ291bGQgbm90IHJlYWQgV0FWRSBmb3JtYXQgb2Ygb3JpZ2luYWwgZG93bmxvYWQgY2h1bmshXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICBpZihwY21GbXQgJiYgb3JnRmwpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFDdHguZGVjb2RlQXVkaW9EYXRhKHJlc3AuYm9keSwgYWIgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJEZWNvZGVkIGF1ZGlvIGNodW5rIGZyYW1lczogXCIrYWIubGVuZ3RoKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV0IGNoRGwgPSBuZXcgQ2h1bmtEb3dubG9hZChwY21GbXQsIG9yZ0ZsLCBhYik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ic2VydmVyLm5leHQoY2hEbCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ic2VydmVyLmNvbXBsZXRlKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwgZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL2lmKGVycm9yIGluc3RhbmNlb2YgSHR0cEVycm9yUmVzcG9uc2UpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gaWYgKGVycm9yLnN0YXR1cyA9PSA0MDQpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gICAvLyBJbnRlcnByZXQgbm90IGFzIGFuIGVycm9yLCB0aGUgZmlsZSBpc3Qgbm90IHJlY29yZGVkIHlldFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyAgIG9ic2VydmVyLm5leHQobnVsbCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vICAgb2JzZXJ2ZXIuY29tcGxldGUoKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyAgIC8vIGFsbCBvdGhlciBzdGF0ZXMgYXJlIGVycm9yc1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiUmVjb3JkaW5ncyBzZXJ2aWNlIGNodW5rQXVkaW9SZXF1ZXN0IGVycm9yIGRlY29kaW5nIGF1ZGlvIGRhdGE6IFwiK2Vycm9yLm5hbWUrXCI6IFwiK2Vycm9yLm1lc3NhZ2UpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvYnNlcnZlci5lcnJvcihlcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIH1cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB9ZWxzZXtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGVyck1zZz0nQ291bGQgbm90IHBhcnNlIGF1ZGlvIGhlYWRlciBmb3IgZm9ybWF0IGFuZC9vciBmcmFtZSBsZW5ndGggb2YgZG93bmxvYWQuJztcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZXJyTXNnKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ic2VydmVyLmVycm9yKGVyck1zZyk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBlcnJNc2c9J0ZldGNoaW5nIGF1ZGlvIGZpbGU6IHJlc3BvbnNlIGhhcyBubyBib2R5JztcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihlcnJNc2cpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBvYnNlcnZlci5lcnJvcihlcnJNc2cpO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgICAgICAoZXJyb3IpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAvLyBhbGwgb3RoZXIgc3RhdGVzIGFyZSBlcnJvcnNcclxuICAgICAgICAgICAgICAgICAgICAvL2NvbnN0IGVyck1zZz0nRmV0Y2hpbmcgYXVkaW8gZmlsZSBIVFRQIGVycm9yOiAnK2Vycm9yO1xyXG4gICAgICAgICAgICAgICAgICAgIC8vY29uc29sZS5lcnJvcihlcnJNc2cpO1xyXG4gICAgICAgICAgICAgICAgICAgIG9ic2VydmVyLmVycm9yKGVycm9yKTtcclxuICAgICAgICAgICAgICAgICAgICAvL29ic2VydmVyLmNvbXBsZXRlKCk7XHJcblxyXG4gICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgICAgcmV0dXJuIG9icztcclxuICAgIH1cclxuXHJcblxyXG4gICAgcHJvdGVjdGVkIGNodW5rQXVkaW9SZXF1ZXN0VG9OZXRBdWRpb0J1ZmZlcihhQ3R4OiBBdWRpb0NvbnRleHQsIGJhc2VBdWRpb1VybDogc3RyaW5nLCBzdGFydEZyYW1lOiBudW1iZXIgPSAwLCBvcmdTYW1wbGVSYXRlOiBudW1iZXIsIHNlY29uZHM6bnVtYmVyLGZyYW1lczogbnVtYmVyIHwgbnVsbCk6IE9ic2VydmFibGU8TmV0QXVkaW9CdWZmZXIgfCBudWxsPiB7XHJcbiAgICAgICAgLy9sZXQgYXVkaW9Vcmw9YmFzZUF1ZGlvVXJsKyc/c3RhcnRGcmFtZT0nK3N0YXJ0RnJhbWUrJyZmcmFtZUxlbmd0aD0nK2ZyYW1lTGVuZ3RoO1xyXG4gICAgICAgIC8vbGV0IGF1ZGlvVXJsPW5ldyBVUkwoYmFzZUF1ZGlvVXJsKTtcclxuICAgICAgICAvLyBpZihvcmdTYW1wbGVSYXRlIT1udWxsICYmIGZyYW1lTGVuZ3RoJW9yZ1NhbXBsZVJhdGU+MCl7XHJcbiAgICAgICAgLy8gICBjb25zdCBlcnJNc2c9J2ZyYW1lTGVuZ3RoIG11c3QgYmUgZXF1YWwgb3IgbXVsdGlwbGUgb2Ygb3JpZ2luYWwgc2FtcGxlcmF0ZS4nO1xyXG4gICAgICAgIC8vICAgY29uc29sZS5lcnJvcihlcnJNc2crJyAoJytmcmFtZUxlbmd0aCsnJScrb3JnU2FtcGxlUmF0ZSsnPT0nKyhmcmFtZUxlbmd0aCVvcmdTYW1wbGVSYXRlKSsnKScpO1xyXG4gICAgICAgIC8vICAgdGhyb3cgRXJyb3IoZXJyTXNnKVxyXG4gICAgICAgIC8vIH1cclxuICAgICAgICBsZXQgZnJhbWVMZW5ndGg6bnVtYmVyPW9yZ1NhbXBsZVJhdGUqTWF0aC5yb3VuZChzZWNvbmRzKTtcclxuICAgICAgICBsZXQgYXVzcHM9bmV3IFVSTFNlYXJjaFBhcmFtcygpO1xyXG4gICAgICAgIGF1c3BzLnNldCgnc3RhcnRGcmFtZScsc3RhcnRGcmFtZS50b1N0cmluZygpKTtcclxuICAgICAgICBhdXNwcy5zZXQoJ2ZyYW1lTGVuZ3RoJyxmcmFtZUxlbmd0aC50b1N0cmluZygpKTtcclxuICAgICAgICBpZiAodGhpcy5jb25maWcgJiYgdGhpcy5jb25maWcuYXBpVHlwZSA9PT0gQXBpVHlwZS5GSUxFUykge1xyXG4gICAgICAgICAgICAvLyBmb3IgZGV2ZWxvcG1lbnQgYW5kIGRlbW9cclxuICAgICAgICAgICAgLy8gYXBwZW5kIFVVSUQgdG8gbWFrZSByZXF1ZXN0IFVSTCB1bmlxdWUgdG8gYXZvaWQgbG9jYWxob3N0IHNlcnZlciBjYWNoaW5nXHJcbiAgICAgICAgICAgIC8vYXVkaW9VcmwgPSBhdWRpb1VybCArICcud2F2P3JlcXVlc3RVVUlEPScgKyBVVUlELmdlbmVyYXRlKCk7XHJcbiAgICAgICAgICAgIGF1c3BzLnNldCgncmVxdWVzdFVVSUQnLFVVSUQuZ2VuZXJhdGUoKSk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGxldCBvYnM9bmV3IE9ic2VydmFibGU8TmV0QXVkaW9CdWZmZXJ8bnVsbD4oc3Vic2NyaWJlcj0+IHtcclxuICAgICAgICAgICAgdGhpcy5hdWRpb1JlcXVlc3RCeVVSTChiYXNlQXVkaW9VcmwsYXVzcHMpLnN1YnNjcmliZSh7bmV4dDoocmVzcCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIC8vIERvIG5vdCB1c2UgUHJvbWlzZSB2ZXJzaW9uLCB3aGljaCBkb2VzIG5vdCB3b3JrIHdpdGggU2FmYXJpIDEzICgxMy4wLjUpXHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHJlc3AuYm9keSkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJjaHVua0F1ZGlvUmVxdWVzdFRvbmV0QWI6IHN1YnNjcmliZXIuY2xvc2VkOiBcIitzdWJzY3JpYmVyLmNsb3NlZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcImNodW5rQXVkaW9SZXF1ZXN0VG9uZXRBYjogQXVkaW8gZmlsZSBieXRlczogXCIrcmVzcC5ib2R5LmJ5dGVMZW5ndGgpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBhQ3R4LmRlY29kZUF1ZGlvRGF0YShyZXNwLmJvZHksIGFiID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJjaHVua0F1ZGlvUmVxdWVzdFRvbmV0QWI6IERlY29kZWQgYXVkaW8gY2h1bmsgZnJhbWVzIGZvciBuZXRBYjogXCIrYWIubGVuZ3RoKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJjaHVua0F1ZGlvUmVxdWVzdFRvbmV0QWI6IENyZWF0ZSBuZXRBYiBhYiBmcm9tIGNodW5rIGFiLi4uXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmKGZyYW1lcz09PW51bGwpe1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzY3JpYmVyLmVycm9yKG5ldyBFcnJvcignQ291bGQgbm90IGdldCBmcmFtZSBsZW5ndGggZnJvbSByZWNvcmRpbmcgZmlsZSBvYmplY3QnKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfWVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXQgZmwgPSBmcmFtZXM7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChhYi5zYW1wbGVSYXRlICE9PSBvcmdTYW1wbGVSYXRlKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAob3JnU2FtcGxlUmF0ZSAmJiBmcmFtZXMpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmbCA9IE1hdGgucm91bmQoYWIuc2FtcGxlUmF0ZSAqIGZyYW1lcyAvIG9yZ1NhbXBsZVJhdGUpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIlBsYXRmb3JtIHNyOiBcIithYi5zYW1wbGVSYXRlK1wiLCBmaWxlIHNyOiBcIitvcmdTYW1wbGVSYXRlK1wiLCBkZWNvZGVkL29yZyBmcmFtZSBsZW5ndGg6IFwiK2ZsK1wiL1wiK2ZyYW1lcytcIiwgYWIubGVuZ3RoOiBcIithYi5sZW5ndGgpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXQgbmFiID0gTmV0QXVkaW9CdWZmZXIuZnJvbUNodW5rQXVkaW9CdWZmZXIoYUN0eCwgdGhpcyxiYXNlQXVkaW9VcmwsIGFiLCBmbCxmcmFtZUxlbmd0aCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vbGV0IHJwPW5ldyBSZWFkeVByb3ZpZGVyKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vbmFiLnJlYWR5UHJvdmlkZXI9cnA7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vcnAucmVhZHkoKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFiLnJlYWR5KCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChuYWIuZnJhbWVMZW4gPCBmcmFtZUxlbmd0aCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiY2h1bmtBdWRpb1JlcXVlc3RUb25ldEFiOiBCdWlsdCBuZXRBYiBhYiBmcm9tIGNodW5rIGFiOiBGaXJzdCBjaHVuayBzaG9ydGVyIHRoYSBmcmFtZUxlbmd0aCAoXCIrbmV0QWJBdWRpb0J1ZmZlci5mcmFtZUxlbitcIjxcIitmcmFtZUxlbmd0aCtcIiksIGFzc3VtaW5nIGVuZCBvZiBkYXRhLCBzZWFsaW5nIG5ldEFiIGFiLlwiKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hYi5zZWFsKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2NyaWJlci5uZXh0KG5hYik7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzY3JpYmVyLmNvbXBsZXRlKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLCBlcnJvciA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignY2h1bmtBdWRpb1JlcXVlc3RUb05ldEFiOiBlcnJvcjogJytlcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9pZihlcnJvciBpbnN0YW5jZW9mIEh0dHBFcnJvclJlc3BvbnNlKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2NyaWJlci5lcnJvcihlcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy99XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KVxyXG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ2NodW5rQXVkaW9SZXF1ZXN0VG9OZXRBYjogRmV0Y2hpbmcgYXVkaW8gZmlsZTogcmVzcG9uc2UgaGFzIG5vIGJvZHknKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2NyaWJlci5lcnJvcignY2h1bmtBdWRpb1JlcXVlc3RUb05ldEFiOiBGZXRjaGluZyBhdWRpbyBmaWxlOiByZXNwb25zZSBoYXMgbm8gYm9keScpO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgICAgICBlcnJvcjooZXJyb3IpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdjaHVua0F1ZGlvUmVxdWVzdFRvTmV0QWI6IGVycm9yOiAnK2Vycm9yKTtcclxuICAgICAgICAgICAgICAgICAgICBzdWJzY3JpYmVyLmVycm9yKGVycm9yKTtcclxuICAgICAgICAgICAgICAgICAgICAvL3N1YnNjcmliZXIuY29tcGxldGUoKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgICAgcmV0dXJuIG9icztcclxuICAgIH1cclxuXHJcbn1cclxuIl19