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.
- package/esm2020/lib/action/action.mjs +2 -1
- package/esm2020/lib/audio/array_audio_buffer.mjs +65 -2
- package/esm2020/lib/audio/array_audio_buffer_input_stream.mjs +2 -2
- package/esm2020/lib/audio/array_audio_buffer_random_access_stream.mjs +16 -0
- package/esm2020/lib/audio/audio_data_holder.mjs +203 -48
- package/esm2020/lib/audio/audio_display.mjs +10 -34
- package/esm2020/lib/audio/audio_player.mjs +18 -45
- package/esm2020/lib/audio/capture/capture.mjs +293 -69
- package/esm2020/lib/audio/dsp/level_measure.mjs +211 -88
- package/esm2020/lib/audio/impl/wavformat.mjs +1 -1
- package/esm2020/lib/audio/impl/wavreader.mjs +134 -0
- package/esm2020/lib/audio/impl/wavwriter.mjs +10 -9
- package/esm2020/lib/audio/inddb_audio_buffer.mjs +508 -0
- package/esm2020/lib/audio/net_audio_buffer.mjs +297 -0
- package/esm2020/lib/audio/persistor.mjs +8 -2
- package/esm2020/lib/audio/playback/array_audio_buffer_source_node.mjs +15 -154
- package/esm2020/lib/audio/playback/audio_source_node.mjs +18 -0
- package/esm2020/lib/audio/playback/audio_source_worklet_module_loader.mjs +167 -0
- package/esm2020/lib/audio/playback/inddb_audio_buffer_source_node.mjs +167 -0
- package/esm2020/lib/audio/playback/net_audio_buffer_source_node.mjs +218 -0
- package/esm2020/lib/audio/playback/player.mjs +190 -171
- package/esm2020/lib/audio/ui/audio_canvas_layer_comp.mjs +35 -76
- package/esm2020/lib/audio/ui/audio_display_control.mjs +12 -24
- package/esm2020/lib/audio/ui/audio_display_scroll_pane.mjs +14 -45
- package/esm2020/lib/audio/ui/audiosignal.mjs +333 -267
- package/esm2020/lib/audio/ui/container.mjs +40 -57
- package/esm2020/lib/audio/ui/livelevel.mjs +53 -52
- package/esm2020/lib/audio/ui/scroll_pane_horizontal.mjs +5 -19
- package/esm2020/lib/audio/ui/sonagram.mjs +386 -339
- package/esm2020/lib/db/inddb.mjs +120 -0
- package/esm2020/lib/io/BinaryReader.mjs +85 -0
- package/esm2020/lib/io/stream.mjs +101 -1
- package/esm2020/lib/net/uploader.mjs +111 -5
- package/esm2020/lib/recorder_component.mjs +59 -1
- package/esm2020/lib/speechrecorder/project/project.mjs +10 -1
- package/esm2020/lib/speechrecorder/project/project.service.mjs +3 -3
- package/esm2020/lib/speechrecorder/recording.mjs +30 -6
- package/esm2020/lib/speechrecorder/recordings/basic_recording.service.mjs +213 -0
- package/esm2020/lib/speechrecorder/recordings/recordings.service.mjs +732 -65
- package/esm2020/lib/speechrecorder/script/script.service.mjs +3 -3
- package/esm2020/lib/speechrecorder/session/audiorecorder.mjs +358 -184
- package/esm2020/lib/speechrecorder/session/basicrecorder.mjs +181 -28
- package/esm2020/lib/speechrecorder/session/controlpanel.mjs +47 -129
- package/esm2020/lib/speechrecorder/session/item.mjs +13 -1
- package/esm2020/lib/speechrecorder/session/progress.mjs +26 -55
- package/esm2020/lib/speechrecorder/session/prompting.mjs +33 -176
- package/esm2020/lib/speechrecorder/session/recorder_combi_pane.mjs +6 -6
- package/esm2020/lib/speechrecorder/session/recording_file_cache.mjs +28 -15
- package/esm2020/lib/speechrecorder/session/recording_list.mjs +92 -55
- package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-meta.component.mjs +9 -13
- package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-navi.component.mjs +9 -18
- package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-u-i.component.mjs +10 -36
- package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-view.component.mjs +10 -37
- package/esm2020/lib/speechrecorder/session/recordingfile/recordingfile-service.mjs +80 -56
- package/esm2020/lib/speechrecorder/session/session.service.mjs +3 -3
- package/esm2020/lib/speechrecorder/session/session_finished_dialog.mjs +4 -4
- package/esm2020/lib/speechrecorder/session/sessionmanager.mjs +409 -165
- package/esm2020/lib/speechrecorder/session/warning_bar.mjs +6 -29
- package/esm2020/lib/speechrecorder/spruploader.mjs +3 -3
- package/esm2020/lib/speechrecorder/startstopsignal/ui/simpletrafficlight.mjs +6 -41
- package/esm2020/lib/speechrecorderng.component.mjs +42 -31
- package/esm2020/lib/speechrecorderng.module.mjs +5 -5
- package/esm2020/lib/spr.config.mjs +3 -3
- package/esm2020/lib/spr.module.version.mjs +2 -2
- package/esm2020/lib/ui/canvas_layer_comp.mjs +3 -3
- package/esm2020/lib/ui/message_dialog.mjs +7 -7
- package/esm2020/lib/ui/recordingitem_display.mjs +26 -59
- package/esm2020/lib/utils/scrollIntoViewToBottom.mjs +3 -3
- package/esm2020/lib/utils/ua-parser.mjs +28 -4
- package/esm2020/lib/utils/utils.mjs +29 -1
- package/fesm2015/speechrecorderng.mjs +12755 -9195
- package/fesm2015/speechrecorderng.mjs.map +1 -1
- package/fesm2020/speechrecorderng.mjs +12652 -9101
- package/fesm2020/speechrecorderng.mjs.map +1 -1
- package/{speechrecorderng.d.ts → index.d.ts} +0 -0
- package/lib/audio/array_audio_buffer.d.ts +17 -1
- package/lib/audio/array_audio_buffer_random_access_stream.d.ts +9 -0
- package/lib/audio/audio_data_holder.d.ts +69 -14
- package/lib/audio/audio_display.d.ts +1 -1
- package/lib/audio/audio_player.d.ts +3 -7
- package/lib/audio/capture/capture.d.ts +24 -4
- package/lib/audio/dsp/level_measure.d.ts +2 -23
- package/lib/audio/impl/wavformat.d.ts +3 -3
- package/lib/audio/impl/wavreader.d.ts +16 -0
- package/lib/audio/impl/wavwriter.d.ts +1 -4
- package/lib/audio/inddb_audio_buffer.d.ts +68 -0
- package/lib/audio/net_audio_buffer.d.ts +59 -0
- package/lib/audio/persistor.d.ts +3 -9
- package/lib/audio/playback/array_audio_buffer_source_node.d.ts +2 -8
- package/lib/audio/playback/audio_source_node.d.ts +10 -0
- package/lib/audio/playback/audio_source_worklet_module_loader.d.ts +4 -0
- package/lib/audio/playback/inddb_audio_buffer_source_node.d.ts +21 -0
- package/lib/audio/playback/net_audio_buffer_source_node.d.ts +27 -0
- package/lib/audio/playback/player.d.ts +19 -16
- package/lib/audio/ui/audio_canvas_layer_comp.d.ts +3 -3
- package/lib/audio/ui/audio_display_control.d.ts +3 -6
- package/lib/audio/ui/audio_display_scroll_pane.d.ts +1 -1
- package/lib/audio/ui/audiosignal.d.ts +4 -3
- package/lib/audio/ui/container.d.ts +1 -1
- package/lib/audio/ui/livelevel.d.ts +11 -4
- package/lib/audio/ui/scroll_pane_horizontal.d.ts +1 -1
- package/lib/audio/ui/sonagram.d.ts +4 -3
- package/lib/db/inddb.d.ts +21 -0
- package/lib/io/BinaryReader.d.ts +18 -0
- package/lib/io/stream.d.ts +18 -0
- package/lib/net/uploader.d.ts +20 -2
- package/lib/recorder_component.d.ts +5 -0
- package/lib/speechrecorder/project/project.d.ts +9 -0
- package/lib/speechrecorder/recording.d.ts +8 -2
- package/lib/speechrecorder/recordings/basic_recording.service.d.ts +27 -0
- package/lib/speechrecorder/recordings/recordings.service.d.ts +21 -6
- package/lib/speechrecorder/session/audiorecorder.d.ts +7 -18
- package/lib/speechrecorder/session/basicrecorder.d.ts +28 -4
- package/lib/speechrecorder/session/controlpanel.d.ts +7 -7
- package/lib/speechrecorder/session/item.d.ts +1 -0
- package/lib/speechrecorder/session/progress.d.ts +2 -1
- package/lib/speechrecorder/session/prompting.d.ts +5 -5
- package/lib/speechrecorder/session/recorder_combi_pane.d.ts +1 -1
- package/lib/speechrecorder/session/recording_file_cache.d.ts +7 -4
- package/lib/speechrecorder/session/recording_list.d.ts +2 -1
- package/lib/speechrecorder/session/recordingfile/recording-file-meta.component.d.ts +1 -1
- package/lib/speechrecorder/session/recordingfile/recording-file-navi.component.d.ts +1 -1
- package/lib/speechrecorder/session/recordingfile/recording-file-u-i.component.d.ts +1 -1
- package/lib/speechrecorder/session/recordingfile/recording-file-view.component.d.ts +1 -1
- package/lib/speechrecorder/session/recordingfile/recordingfile-service.d.ts +4 -5
- package/lib/speechrecorder/session/session_finished_dialog.d.ts +1 -1
- package/lib/speechrecorder/session/sessionmanager.d.ts +10 -10
- package/lib/speechrecorder/session/warning_bar.d.ts +1 -1
- package/lib/speechrecorder/startstopsignal/ui/simpletrafficlight.d.ts +1 -1
- package/lib/speechrecorderng.component.d.ts +2 -1
- package/lib/spr.module.version.d.ts +1 -1
- package/lib/ui/canvas_layer_comp.d.ts +1 -1
- package/lib/ui/message_dialog.d.ts +1 -1
- package/lib/ui/recordingitem_display.d.ts +3 -2
- package/lib/utils/scrollIntoViewToBottom.d.ts +1 -1
- package/lib/utils/ua-parser.d.ts +4 -1
- package/lib/utils/utils.d.ts +6 -0
- package/package.json +5 -4
- package/esm2020/lib/math/utils.mjs +0 -14
- package/esm2020/lib/utils/css_utils.mjs +0 -7
- package/lib/math/utils.d.ts +0 -3
- package/lib/utils/css_utils.d.ts +0 -3
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
import { Observable } from "rxjs";
|
|
2
|
+
import { UUID } from "../utils/utils";
|
|
3
|
+
import { BasicAudioSource } from "./audio_data_holder";
|
|
4
|
+
export class PersistentAudioStorageTarget {
|
|
5
|
+
constructor(_indexedDb, _storeName) {
|
|
6
|
+
this._indexedDb = _indexedDb;
|
|
7
|
+
this._storeName = _storeName;
|
|
8
|
+
}
|
|
9
|
+
get indexedDb() {
|
|
10
|
+
return this._indexedDb;
|
|
11
|
+
}
|
|
12
|
+
get storeName() {
|
|
13
|
+
return this._storeName;
|
|
14
|
+
}
|
|
15
|
+
transaction(mode) {
|
|
16
|
+
return this._indexedDb.transaction(this.storeName, mode);
|
|
17
|
+
}
|
|
18
|
+
objectStore(mode) {
|
|
19
|
+
let tr = this.transaction(mode);
|
|
20
|
+
return tr.objectStore(this._storeName);
|
|
21
|
+
}
|
|
22
|
+
deleteAll() {
|
|
23
|
+
return new Observable(subscriber => {
|
|
24
|
+
let os = this.objectStore('readwrite');
|
|
25
|
+
let clearReq = os.clear();
|
|
26
|
+
clearReq.onsuccess = () => {
|
|
27
|
+
console.debug("Cleared audio storage object store.");
|
|
28
|
+
os.transaction.commit();
|
|
29
|
+
};
|
|
30
|
+
clearReq.onerror = () => {
|
|
31
|
+
os.transaction.abort();
|
|
32
|
+
console.error("Could not clear object store: " + clearReq.error);
|
|
33
|
+
};
|
|
34
|
+
os.transaction.oncomplete = () => {
|
|
35
|
+
console.debug("Transaction complete: Clear audio storage object store.");
|
|
36
|
+
subscriber.complete();
|
|
37
|
+
};
|
|
38
|
+
os.transaction.onerror = (err) => {
|
|
39
|
+
subscriber.error(err);
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export class IndexedDbAudioBuffer extends BasicAudioSource {
|
|
45
|
+
constructor(_persistentAudioStorageTarget, _channelCount, _sampleRate, _chunkFrameLen, _frameLen, _uuid) {
|
|
46
|
+
super();
|
|
47
|
+
this._persistentAudioStorageTarget = _persistentAudioStorageTarget;
|
|
48
|
+
this._channelCount = _channelCount;
|
|
49
|
+
this._sampleRate = _sampleRate;
|
|
50
|
+
this._chunkFrameLen = _chunkFrameLen;
|
|
51
|
+
this._frameLen = _frameLen;
|
|
52
|
+
this._uuid = _uuid;
|
|
53
|
+
this._chunkCount = 0;
|
|
54
|
+
this.indDbChkIdx = 0;
|
|
55
|
+
this._sealed = false;
|
|
56
|
+
this._storeError = null;
|
|
57
|
+
this.ready();
|
|
58
|
+
}
|
|
59
|
+
get chunkFrameLen() {
|
|
60
|
+
return this._chunkFrameLen;
|
|
61
|
+
}
|
|
62
|
+
get persistentAudioStorageTarget() {
|
|
63
|
+
return this._persistentAudioStorageTarget;
|
|
64
|
+
}
|
|
65
|
+
get uuid() {
|
|
66
|
+
return this._uuid;
|
|
67
|
+
}
|
|
68
|
+
get channelCount() {
|
|
69
|
+
return this._channelCount;
|
|
70
|
+
}
|
|
71
|
+
sealed() {
|
|
72
|
+
return this._sealed;
|
|
73
|
+
}
|
|
74
|
+
seal() {
|
|
75
|
+
this._sealed = true;
|
|
76
|
+
}
|
|
77
|
+
deleteChunk(os, ci, cb, errCb) {
|
|
78
|
+
// Build bounds for channels
|
|
79
|
+
let lIdx = [this._uuid, ci, 0];
|
|
80
|
+
let hIdx = [this._uuid, ci, this._channelCount];
|
|
81
|
+
let chsKr = IDBKeyRange.bound(lIdx, hIdx);
|
|
82
|
+
let exRq = os.getAllKeys(chsKr);
|
|
83
|
+
exRq.onsuccess = () => {
|
|
84
|
+
let cc = exRq.result;
|
|
85
|
+
if (cc && cc.length > 0) {
|
|
86
|
+
let rq = os.delete(chsKr);
|
|
87
|
+
rq.onerror = (errEv) => {
|
|
88
|
+
errCb(new Error(errEv.toString()));
|
|
89
|
+
};
|
|
90
|
+
os.transaction.oncomplete = () => {
|
|
91
|
+
cb(cc);
|
|
92
|
+
};
|
|
93
|
+
os.transaction.commit();
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
exRq.onerror = (errEv) => {
|
|
97
|
+
errCb(new Error(errEv.toString()));
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
deleteAllBufs(os, ci, cbEnd, cbErr) {
|
|
101
|
+
//console.debug('IndexedDbAudioBuffer::fillBufs: framePos:'+framePos+', frameLen: '+frameLen+', filled: '+filled+', srcFramePos: '+srcFramePos+',ci: '+ci+', ccPos: '+ccPos);
|
|
102
|
+
this.deleteChunk(os, ci, (ccKeys) => {
|
|
103
|
+
if (ccKeys && ccKeys.length > 0) {
|
|
104
|
+
//console.debug('IndexedDbAudioBuffer::fillBufs frameLen: '+frameLen);
|
|
105
|
+
ci++;
|
|
106
|
+
this.deleteAllBufs(os, ci, cbEnd, cbErr);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
//console.debug('IndexedDbAudioBuffer::fillBufs (chunk not found) call: cbend '+filled);
|
|
110
|
+
cbEnd();
|
|
111
|
+
}
|
|
112
|
+
}, (err) => {
|
|
113
|
+
cbErr(err);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
get sampleRate() {
|
|
117
|
+
return this._sampleRate;
|
|
118
|
+
}
|
|
119
|
+
get frameLen() {
|
|
120
|
+
return this._frameLen;
|
|
121
|
+
}
|
|
122
|
+
get chunkCount() {
|
|
123
|
+
return this._chunkCount;
|
|
124
|
+
}
|
|
125
|
+
appendRawAudioData(data) {
|
|
126
|
+
return new Observable(subscriber => {
|
|
127
|
+
if (this._persistentAudioStorageTarget && this._uuid) {
|
|
128
|
+
let tr = this._persistentAudioStorageTarget.indexedDb.transaction(this._persistentAudioStorageTarget.storeName, 'readwrite');
|
|
129
|
+
let recFileObjStore = tr.objectStore(this._persistentAudioStorageTarget.storeName);
|
|
130
|
+
try {
|
|
131
|
+
let ch0Data = data[0];
|
|
132
|
+
let dataChkCnt = ch0Data.length;
|
|
133
|
+
let pos = 0;
|
|
134
|
+
for (let chCkIdx = 0; chCkIdx < dataChkCnt; chCkIdx++) {
|
|
135
|
+
let bufLen = 0;
|
|
136
|
+
for (let ch = 0; ch < this.channelCount; ch++) {
|
|
137
|
+
let chChk = data[ch][chCkIdx];
|
|
138
|
+
bufLen = chChk.length;
|
|
139
|
+
//let cacheId = uuid + '_' + ch + '_' + chCkIdx;
|
|
140
|
+
let chkDbId = [this._uuid, this.indDbChkIdx + chCkIdx, ch];
|
|
141
|
+
let cr = recFileObjStore.add(chChk, chkDbId);
|
|
142
|
+
//console.debug("Added: "+ch+" "+(this.indDbChkIdx+chCkIdx));
|
|
143
|
+
cr.onsuccess = () => {
|
|
144
|
+
//console.debug("Stored audio data to indexed db");
|
|
145
|
+
// if(chCkIdx>= dataChkCnt-1 && ch>=this.channelCount-1){
|
|
146
|
+
// tr.commit();
|
|
147
|
+
// }
|
|
148
|
+
};
|
|
149
|
+
cr.onerror = () => {
|
|
150
|
+
if (!this._storeError) {
|
|
151
|
+
// Only log the first error
|
|
152
|
+
console.error("Error storing audio data of " + chChk.byteLength + " bytes to indexed db: " + cr.error);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
pos += bufLen;
|
|
157
|
+
this._frameLen += bufLen;
|
|
158
|
+
}
|
|
159
|
+
this.indDbChkIdx += dataChkCnt;
|
|
160
|
+
tr.onerror = (ev) => {
|
|
161
|
+
if (!this._storeError) {
|
|
162
|
+
// Only log the first error
|
|
163
|
+
console.error("Failed to store audio data to indexed db, transaction error: " + tr.error);
|
|
164
|
+
this._storeError = tr.error;
|
|
165
|
+
}
|
|
166
|
+
subscriber.error(tr.error);
|
|
167
|
+
};
|
|
168
|
+
tr.onabort = (ev) => {
|
|
169
|
+
// If Chrome reaches quota it aborts the transaction
|
|
170
|
+
if (!this._storeError) {
|
|
171
|
+
// Only log the first error
|
|
172
|
+
console.error("Failed to store audio data to indexed db, transaction aborted: " + tr.error);
|
|
173
|
+
this._storeError = tr.error;
|
|
174
|
+
}
|
|
175
|
+
subscriber.error(tr.error);
|
|
176
|
+
};
|
|
177
|
+
tr.oncomplete = (ev) => {
|
|
178
|
+
subscriber.complete();
|
|
179
|
+
};
|
|
180
|
+
tr.commit();
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
subscriber.error(new Error('Transfer audio data error: ' + err));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
static fromChunkAudioBuffer(persistentAudioStorageTarget, audioBuffer) {
|
|
189
|
+
return new Observable(subscriber => {
|
|
190
|
+
let chs = audioBuffer.numberOfChannels;
|
|
191
|
+
let sr = audioBuffer.sampleRate;
|
|
192
|
+
let chkFrameLength = audioBuffer.length;
|
|
193
|
+
let iab = new IndexedDbAudioBuffer(persistentAudioStorageTarget, chs, sr, chkFrameLength, 0, UUID.generate());
|
|
194
|
+
iab.appendChunkAudioBuffer(audioBuffer).subscribe({
|
|
195
|
+
next: () => {
|
|
196
|
+
subscriber.next(iab);
|
|
197
|
+
},
|
|
198
|
+
complete: () => {
|
|
199
|
+
subscriber.complete();
|
|
200
|
+
},
|
|
201
|
+
error: (err) => {
|
|
202
|
+
subscriber.error(err);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
appendChunkAudioBuffer(audioBuffer) {
|
|
208
|
+
if (this._sealed) {
|
|
209
|
+
throw new Error('Cannot append audio buffer to already sealed audio buffer.');
|
|
210
|
+
}
|
|
211
|
+
let chs = audioBuffer.numberOfChannels;
|
|
212
|
+
if (this._channelCount !== chs) {
|
|
213
|
+
throw new Error('Cannot append audio buffer with ' + chs + ' channels to this array audio buffer with ' + this._channelCount + ' channels. Number of channels must match.');
|
|
214
|
+
}
|
|
215
|
+
let sr = audioBuffer.sampleRate;
|
|
216
|
+
if (sr !== this._sampleRate) {
|
|
217
|
+
throw new Error('Cannot append audio buffer with samplerate ' + sr + ' to this array audio buffer with samplerate ' + this._sampleRate + '. Samplerates must match.');
|
|
218
|
+
}
|
|
219
|
+
let abFl = audioBuffer.length;
|
|
220
|
+
if (abFl > this._chunkFrameLen) {
|
|
221
|
+
throw new Error('Cannot append audio buffer with frame length ' + abFl + ' to this array audio buffer with chunk frame length ' + this._chunkFrameLen + '. Chunk length must be equal or less if last chunk.');
|
|
222
|
+
}
|
|
223
|
+
return new Observable(subscriber => {
|
|
224
|
+
if (this._persistentAudioStorageTarget && this._uuid) {
|
|
225
|
+
let tr = this._persistentAudioStorageTarget.transaction('readwrite');
|
|
226
|
+
let recFileObjStore = tr.objectStore(this._persistentAudioStorageTarget.storeName);
|
|
227
|
+
try {
|
|
228
|
+
for (let ch = 0; ch < this.channelCount; ch++) {
|
|
229
|
+
let chChk = audioBuffer.getChannelData(ch);
|
|
230
|
+
let chkDbId = [this._uuid, this.indDbChkIdx++, ch];
|
|
231
|
+
try {
|
|
232
|
+
let cr = recFileObjStore.add(chChk, chkDbId);
|
|
233
|
+
cr.onsuccess = (ev) => { };
|
|
234
|
+
cr.onerror = (ev) => {
|
|
235
|
+
// iPad may throw QuotaExceededError here
|
|
236
|
+
// iPad asks for more storage and if denied, the error is thrown here
|
|
237
|
+
if (!this._storeError) {
|
|
238
|
+
// Only log the first error
|
|
239
|
+
console.error("Error storing audio data of " + chChk.byteLength + " bytes to indexed db: " + cr.error);
|
|
240
|
+
}
|
|
241
|
+
//subscriber.error(cr.error);
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
catch (err1) {
|
|
245
|
+
if (!this._storeError) {
|
|
246
|
+
console.error("Error adding audio data to indexed db store: " + err1);
|
|
247
|
+
this._storeError = (err1 instanceof Error) ? err1 : new Error(err1);
|
|
248
|
+
}
|
|
249
|
+
subscriber.error(err1);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
this._frameLen += abFl;
|
|
253
|
+
tr.onerror = (ev) => {
|
|
254
|
+
if (!this._storeError) {
|
|
255
|
+
// Only log the first error
|
|
256
|
+
console.error("Failed to store audio data to indexed db, transaction error: " + tr.error);
|
|
257
|
+
this._storeError = tr.error;
|
|
258
|
+
}
|
|
259
|
+
subscriber.error(tr.error);
|
|
260
|
+
};
|
|
261
|
+
tr.onabort = (ev) => {
|
|
262
|
+
// If Chrome reaches quota it aborts the transaction
|
|
263
|
+
if (!this._storeError) {
|
|
264
|
+
// Only log the first error
|
|
265
|
+
console.error("Failed to store audio data to indexed db, transaction aborted: " + tr.error);
|
|
266
|
+
this._storeError = tr.error;
|
|
267
|
+
}
|
|
268
|
+
subscriber.error(tr.error);
|
|
269
|
+
};
|
|
270
|
+
tr.oncomplete = (ev) => {
|
|
271
|
+
//console.debug("Transaction complete");
|
|
272
|
+
if (abFl < this._chunkFrameLen) {
|
|
273
|
+
// last chunk
|
|
274
|
+
this.seal();
|
|
275
|
+
}
|
|
276
|
+
subscriber.next();
|
|
277
|
+
subscriber.complete();
|
|
278
|
+
};
|
|
279
|
+
tr.commit();
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
console.error('Catched error: ' + err);
|
|
283
|
+
subscriber.error(new Error('Transfer audio data error: ' + err));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
releaseAudioData() {
|
|
289
|
+
return new Observable((subscriber) => {
|
|
290
|
+
let os = this._persistentAudioStorageTarget.objectStore('readwrite');
|
|
291
|
+
this.deleteAllBufs(os, 0, () => {
|
|
292
|
+
subscriber.next();
|
|
293
|
+
subscriber.complete();
|
|
294
|
+
}, (err) => {
|
|
295
|
+
subscriber.error(err);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
toString() {
|
|
300
|
+
return "Indexed db audio buffer. Channels: " + this.channelCount + ", sample rate: " + this.sampleRate + ", chunk frame length: " + this._chunkFrameLen + ", number of chunks: " + this.chunkCount + ", frame length: " + this.frameLen + ", sealed: " + this.sealed();
|
|
301
|
+
}
|
|
302
|
+
asyncAudioInputStream() {
|
|
303
|
+
return new IndexedDbAudioInputStream(this);
|
|
304
|
+
}
|
|
305
|
+
audioInputStream() {
|
|
306
|
+
// Synchronous stream not supported
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
get duration() {
|
|
310
|
+
return this._chunkFrameLen / this._sampleRate;
|
|
311
|
+
}
|
|
312
|
+
get numberOfChannels() {
|
|
313
|
+
return this._channelCount;
|
|
314
|
+
}
|
|
315
|
+
sampleCounts() {
|
|
316
|
+
return this._channelCount * this._frameLen;
|
|
317
|
+
}
|
|
318
|
+
randomAccessAudioStream() {
|
|
319
|
+
return new IndexedDbRandomAccessStream(this);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
export class IndexedDbRandomAccessStream {
|
|
323
|
+
constructor(_inddbAb) {
|
|
324
|
+
this._inddbAb = _inddbAb;
|
|
325
|
+
this._currCi = 0;
|
|
326
|
+
this._ccCache = null;
|
|
327
|
+
}
|
|
328
|
+
chunk(os, ci, cb, errCb) {
|
|
329
|
+
// Build bounds for channels
|
|
330
|
+
let lIdx = [this._inddbAb.uuid, ci, 0];
|
|
331
|
+
let hIdx = [this._inddbAb.uuid, ci, this._inddbAb.channelCount];
|
|
332
|
+
let chsKr = IDBKeyRange.bound(lIdx, hIdx);
|
|
333
|
+
let rq = os.getAll(chsKr);
|
|
334
|
+
rq.onsuccess = () => {
|
|
335
|
+
let cc = rq.result;
|
|
336
|
+
let cc0;
|
|
337
|
+
let ccChs = cc.length;
|
|
338
|
+
if (ccChs == 0) {
|
|
339
|
+
cb(null);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
if (ccChs !== this._inddbAb.channelCount) {
|
|
343
|
+
errCb(new Error('Number of channels of inddb data (' + ccChs + ') does not match number of channels (' + this._inddbAb.channelCount + ')'));
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
if (cc.length > 0) {
|
|
347
|
+
cc0 = cc[0];
|
|
348
|
+
}
|
|
349
|
+
let arrBuf = new Array();
|
|
350
|
+
let ccLen = cc0.length;
|
|
351
|
+
for (let ch = 0; ch < ccChs; ch++) {
|
|
352
|
+
let chArr = new Float32Array(ccLen);
|
|
353
|
+
for (let si = 0; si < ccLen; si++) {
|
|
354
|
+
chArr[si] = cc[ch][si];
|
|
355
|
+
}
|
|
356
|
+
arrBuf.push(chArr);
|
|
357
|
+
}
|
|
358
|
+
cb(arrBuf);
|
|
359
|
+
};
|
|
360
|
+
rq.onerror = (errEv) => {
|
|
361
|
+
errCb(new Error(errEv.toString()));
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
_fillBufs(ccBufs, trgState, srcState) {
|
|
365
|
+
let ccBufsChs = ccBufs.length;
|
|
366
|
+
if (ccBufsChs > 0) {
|
|
367
|
+
let ccBuf0 = ccBufs[0];
|
|
368
|
+
let ccBufsLen = ccBuf0.length;
|
|
369
|
+
//console.debug('IndexedDbAudioBuffer::fillBufs framePos: '+framePos+', srcFramePos: '+srcFramePos+', ccBufsLen: '+ccBufsLen);
|
|
370
|
+
if (trgState.framePos >= srcState.srcFramePos + ccBufsLen) {
|
|
371
|
+
// target frame position is ahead, seek to next source buffer
|
|
372
|
+
//console.debug('IndexedDbAudioBuffer::fillBufs seek to next inddb buffer');
|
|
373
|
+
this._ccCache = null;
|
|
374
|
+
srcState.ci++;
|
|
375
|
+
srcState.ccPos = 0;
|
|
376
|
+
srcState.srcFramePos += ccBufsLen;
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
// Assuming target frame pos is inside current source buffer
|
|
380
|
+
srcState.ccPos = trgState.framePos - srcState.srcFramePos;
|
|
381
|
+
let ccAvail = ccBufsLen - srcState.ccPos;
|
|
382
|
+
let toCopy = ccBufsLen;
|
|
383
|
+
if (toCopy > ccAvail) {
|
|
384
|
+
toCopy = ccAvail;
|
|
385
|
+
}
|
|
386
|
+
if (toCopy > trgState.frameLen) {
|
|
387
|
+
toCopy = trgState.frameLen;
|
|
388
|
+
}
|
|
389
|
+
for (let ch = 0; ch < ccBufsChs; ch++) {
|
|
390
|
+
for (let si = 0; si < toCopy; si++) {
|
|
391
|
+
trgState.trgBufs[ch][trgState.filled + si] = ccBufs[ch][srcState.ccPos + si];
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
trgState.filled += toCopy;
|
|
395
|
+
trgState.frameLen -= toCopy;
|
|
396
|
+
trgState.framePos += toCopy;
|
|
397
|
+
srcState.ccPos += toCopy;
|
|
398
|
+
if (srcState.ccPos >= ccBufsLen) {
|
|
399
|
+
// Invalidate cache
|
|
400
|
+
this._ccCache = null;
|
|
401
|
+
srcState.ci++;
|
|
402
|
+
this._currCi = srcState.ci;
|
|
403
|
+
srcState.ccPos = 0;
|
|
404
|
+
srcState.srcFramePos += ccBufsLen;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
fillBufs(os, trgState, srcState, cb, cbEnd, cbErr) {
|
|
410
|
+
//console.debug('IndexedDbAudioBuffer::fillBufs: framePos:'+trgState.framePos+', frameLen: '+trgState.frameLen+', filled: '+trgState.filled+', srcFramePos: '+srcState.srcFramePos+',ci: '+srcState.ci+', ccPos: '+srcState.ccPos);
|
|
411
|
+
if (this._ccCache) {
|
|
412
|
+
this._fillBufs(this._ccCache, trgState, srcState);
|
|
413
|
+
//console.debug('IndexedDbAudioBuffer::fillBufs frameLen: '+trgState.frameLen);
|
|
414
|
+
if (trgState.frameLen === 0) {
|
|
415
|
+
//console.debug('IndexedDbAudioBuffer::fillBufs (framelen==0) call: cbend '+trgState.filled);
|
|
416
|
+
cbEnd(trgState.filled);
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
this.fillBufs(os, trgState, srcState, cb, cbEnd, cbErr);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
this.chunk(os, srcState.ci, (ccBufs) => {
|
|
424
|
+
if (ccBufs) {
|
|
425
|
+
this._currCi = srcState.ci;
|
|
426
|
+
this._ccCache = ccBufs;
|
|
427
|
+
this._fillBufs(ccBufs, trgState, srcState);
|
|
428
|
+
//console.debug('IndexedDbAudioBuffer::fillBufs frameLen: '+trgState.frameLen);
|
|
429
|
+
if (trgState.frameLen === 0) {
|
|
430
|
+
//console.debug('IndexedDbAudioBuffer::fillBufs (framelen==0) call: cbend '+trgState.filled);
|
|
431
|
+
cbEnd(trgState.filled);
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
this.fillBufs(os, trgState, srcState, cb, cbEnd, cbErr);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
//console.debug('IndexedDbAudioBuffer::fillBufs (chunk not found) call: cbend '+trgState.filled);
|
|
439
|
+
cbEnd(trgState.filled);
|
|
440
|
+
}
|
|
441
|
+
}, (err) => {
|
|
442
|
+
cbErr(err);
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
framesObs(framePos, frameLen, bufs) {
|
|
447
|
+
return new Observable((subscriber) => {
|
|
448
|
+
let tr = this._inddbAb.persistentAudioStorageTarget.transaction();
|
|
449
|
+
let os = tr.objectStore(this._inddbAb.persistentAudioStorageTarget.storeName);
|
|
450
|
+
// Positioning
|
|
451
|
+
let newCi = Math.floor(framePos / this._inddbAb.chunkFrameLen);
|
|
452
|
+
if (newCi !== this._currCi) {
|
|
453
|
+
this._currCi = newCi;
|
|
454
|
+
this._ccCache = null;
|
|
455
|
+
}
|
|
456
|
+
let srcFramePos = newCi * this._inddbAb.chunkFrameLen;
|
|
457
|
+
let trgState = { framePos: framePos, frameLen: frameLen, trgBufs: bufs, filled: 0 };
|
|
458
|
+
let srcState = { srcFramePos: srcFramePos, ci: newCi, ccPos: 0, ccFilled: 0 };
|
|
459
|
+
this.fillBufs(os, trgState, srcState, (val) => { }, (filled) => {
|
|
460
|
+
subscriber.next(filled);
|
|
461
|
+
subscriber.complete();
|
|
462
|
+
}, err => {
|
|
463
|
+
subscriber.error(err);
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
close() {
|
|
468
|
+
this._currCi = 0;
|
|
469
|
+
this._ccCache = null;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
export class IndexedDbAudioInputStream {
|
|
473
|
+
constructor(inddbAb) {
|
|
474
|
+
this.inddbAb = inddbAb;
|
|
475
|
+
this.framePos = 0;
|
|
476
|
+
this.inddbAbStr = new IndexedDbRandomAccessStream(inddbAb);
|
|
477
|
+
}
|
|
478
|
+
close() {
|
|
479
|
+
this.inddbAbStr.close();
|
|
480
|
+
}
|
|
481
|
+
readObs(buffers) {
|
|
482
|
+
return new Observable(subscr => {
|
|
483
|
+
if (buffers && buffers.length > 0) {
|
|
484
|
+
let fl = buffers[0].length;
|
|
485
|
+
this.inddbAbStr.framesObs(this.framePos, fl, buffers).subscribe({
|
|
486
|
+
next: (read) => {
|
|
487
|
+
this.framePos += read;
|
|
488
|
+
subscr.next(read);
|
|
489
|
+
},
|
|
490
|
+
complete: () => {
|
|
491
|
+
subscr.complete();
|
|
492
|
+
},
|
|
493
|
+
error: (err) => {
|
|
494
|
+
subscr.error(err);
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
subscr.next(0);
|
|
500
|
+
subscr.complete();
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
skipFrames(n) {
|
|
505
|
+
this.framePos += n;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZGJfYXVkaW9fYnVmZmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvc3BlZWNocmVjb3JkZXJuZy9zcmMvbGliL2F1ZGlvL2luZGRiX2F1ZGlvX2J1ZmZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUMsVUFBVSxFQUFDLE1BQU0sTUFBTSxDQUFDO0FBRWhDLE9BQU8sRUFBQyxJQUFJLEVBQUMsTUFBTSxnQkFBZ0IsQ0FBQztBQUNwQyxPQUFPLEVBQWMsZ0JBQWdCLEVBQTBCLE1BQU0scUJBQXFCLENBQUM7QUFFM0YsTUFBTSxPQUFPLDRCQUE0QjtJQVF2QyxZQUFvQixVQUFzQixFQUFTLFVBQWlCO1FBQWhELGVBQVUsR0FBVixVQUFVLENBQVk7UUFBUyxlQUFVLEdBQVYsVUFBVSxDQUFPO0lBQ3BFLENBQUM7SUFSRCxJQUFJLFNBQVM7UUFDWCxPQUFPLElBQUksQ0FBQyxVQUFVLENBQUM7SUFDekIsQ0FBQztJQUVELElBQUksU0FBUztRQUNYLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQztJQUN6QixDQUFDO0lBSUQsV0FBVyxDQUFDLElBQXdCO1FBQ2xDLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUMzRCxDQUFDO0lBRUQsV0FBVyxDQUFDLElBQXdCO1FBQ2xDLElBQUksRUFBRSxHQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDOUIsT0FBTyxFQUFFLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBRUQsU0FBUztRQUNQLE9BQU8sSUFBSSxVQUFVLENBQU8sVUFBVSxDQUFDLEVBQUU7WUFDdkMsSUFBSSxFQUFFLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUN2QyxJQUFJLFFB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LE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUM7Z0JBQzNCLElBQUksQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsRUFBRSxFQUFFLE9BQU8sQ0FBQyxDQUFDLFNBQVMsQ0FBQztvQkFDOUQsSUFBSSxFQUFFLENBQUMsSUFBSSxFQUFFLEVBQUU7d0JBQ2IsSUFBSSxDQUFDLFFBQVEsSUFBSSxJQUFJLENBQUM7d0JBQ3RCLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7b0JBQ3BCLENBQUM7b0JBQ0QsUUFBUSxFQUFFLEdBQUcsRUFBRTt3QkFDYixNQUFNLENBQUMsUUFBUSxFQUFFLENBQUM7b0JBQ3BCLENBQUM7b0JBQ0QsS0FBSyxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUU7d0JBQ2IsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztvQkFDcEIsQ0FBQztpQkFDRixDQUFDLENBQUE7YUFDSDtpQkFBSTtnQkFDSCxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNmLE1BQU0sQ0FBQyxRQUFRLEVBQUUsQ0FBQzthQUNuQjtRQUNILENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELFVBQVUsQ0FBQyxDQUFTO1FBQ2xCLElBQUksQ0FBQyxRQUFRLElBQUUsQ0FBQyxDQUFDO0lBQ25CLENBQUM7Q0FFRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7T2JzZXJ2YWJsZX0gZnJvbSBcInJ4anNcIjtcclxuaW1wb3J0IHtBc3luY0Zsb2F0MzJBcnJheUlucHV0U3RyZWFtLCBGbG9hdDMyQXJyYXlJbnB1dFN0cmVhbX0gZnJvbSBcIi4uL2lvL3N0cmVhbVwiO1xyXG5pbXBvcnQge1VVSUR9IGZyb20gXCIuLi91dGlscy91dGlsc1wiO1xyXG5pbXBvcnQge0F1ZGlvU291cmNlLCBCYXNpY0F1ZGlvU291cmNlLCBSYW5kb21BY2Nlc3NBdWRpb1N0cmVhbX0gZnJvbSBcIi4vYXVkaW9fZGF0YV9ob2xkZXJcIjtcclxuXHJcbmV4cG9ydCBjbGFzcyBQZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0e1xyXG4gIGdldCBpbmRleGVkRGIoKTogSURCRGF0YWJhc2Uge1xyXG4gICAgcmV0dXJuIHRoaXMuX2luZGV4ZWREYjtcclxuICB9XHJcblxyXG4gIGdldCBzdG9yZU5hbWUoKTogc3RyaW5nIHtcclxuICAgIHJldHVybiB0aGlzLl9zdG9yZU5hbWU7XHJcbiAgfVxyXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgX2luZGV4ZWREYjpJREJEYXRhYmFzZSxwcml2YXRlIF9zdG9yZU5hbWU6c3RyaW5nKSB7XHJcbiAgfVxyXG5cclxuICB0cmFuc2FjdGlvbihtb2RlPzpJREJUcmFuc2FjdGlvbk1vZGUpOklEQlRyYW5zYWN0aW9ue1xyXG4gICAgcmV0dXJuIHRoaXMuX2luZGV4ZWREYi50cmFuc2FjdGlvbih0aGlzLnN0b3JlTmFtZSwgbW9kZSk7XHJcbiAgfVxyXG5cclxuICBvYmplY3RTdG9yZShtb2RlPzpJREJUcmFuc2FjdGlvbk1vZGUgKXtcclxuICAgIGxldCB0cj10aGlzLnRyYW5zYWN0aW9uKG1vZGUpO1xyXG4gICAgcmV0dXJuIHRyLm9iamVjdFN0b3JlKHRoaXMuX3N0b3JlTmFtZSk7XHJcbiAgfVxyXG5cclxuICBkZWxldGVBbGwoKTpPYnNlcnZhYmxlPHZvaWQ+e1xyXG4gICAgcmV0dXJuIG5ldyBPYnNlcnZhYmxlPHZvaWQ+KHN1YnNjcmliZXIgPT4ge1xyXG4gICAgICBsZXQgb3MgPSB0aGlzLm9iamVjdFN0b3JlKCdyZWFkd3JpdGUnKTtcclxuICAgICAgbGV0IGNsZWFyUmVxPW9zLmNsZWFyKCk7XHJcbiAgICAgIGNsZWFyUmVxLm9uc3VjY2Vzcz0oKT0+e1xyXG4gICAgICAgIGNvbnNvbGUuZGVidWcoXCJDbGVhcmVkIGF1ZGlvIHN0b3JhZ2Ugb2JqZWN0IHN0b3JlLlwiKTtcclxuICAgICAgICBvcy50cmFuc2FjdGlvbi5jb21taXQoKTtcclxuICAgICAgfVxyXG4gICAgICBjbGVhclJlcS5vbmVycm9yPSgpPT57XHJcbiAgICAgICAgb3MudHJhbnNhY3Rpb24uYWJvcnQoKTtcclxuICAgICAgICBjb25zb2xlLmVycm9yKFwiQ291bGQgbm90IGNsZWFyIG9iamVjdCBzdG9yZTogXCIrY2xlYXJSZXEuZXJyb3IpO1xyXG4gICAgICB9XHJcbiAgICAgIG9zLnRyYW5zYWN0aW9uLm9uY29tcGxldGUgPSAoKSA9PiB7XHJcbiAgICAgICAgY29uc29sZS5kZWJ1ZyhcIlRyYW5zYWN0aW9uIGNvbXBsZXRlOiBDbGVhciBhdWRpbyBzdG9yYWdlIG9iamVjdCBzdG9yZS5cIik7XHJcbiAgICAgICAgICBzdWJzY3JpYmVyLmNvbXBsZXRlKCk7XHJcbiAgICAgIH1cclxuICAgICAgb3MudHJhbnNhY3Rpb24ub25lcnJvciA9IChlcnIpID0+IHtcclxuICAgICAgICAgIHN1YnNjcmliZXIuZXJyb3IoZXJyKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIEluZGV4ZWREYkF1ZGlvQnVmZmVyIGV4dGVuZHMgQmFzaWNBdWRpb1NvdXJjZSBpbXBsZW1lbnRzIEF1ZGlvU291cmNle1xyXG4gIGdldCBjaHVua0ZyYW1lTGVuKCk6IG51bWJlciB7XHJcbiAgICByZXR1cm4gdGhpcy5fY2h1bmtGcmFtZUxlbjtcclxuICB9XHJcbiAgZ2V0IHBlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQoKTogUGVyc2lzdGVudEF1ZGlvU3RvcmFnZVRhcmdldCB7XHJcbiAgICByZXR1cm4gdGhpcy5fcGVyc2lzdGVudEF1ZGlvU3RvcmFnZVRhcmdldDtcclxuICB9XHJcbiAgZ2V0IHV1aWQoKTogc3RyaW5nIHtcclxuICAgIHJldHVybiB0aGlzLl91dWlkO1xyXG4gIH1cclxuXHJcblxyXG4gIHByaXZhdGUgX2NodW5rQ291bnQ9MDtcclxuICBwcml2YXRlIGluZERiQ2hrSWR4OiBudW1iZXI9MDtcclxuICBwcml2YXRlIF9zZWFsZWQ9ZmFsc2U7XHJcbiAgcHJpdmF0ZSBfc3RvcmVFcnJvcjpFcnJvcnxudWxsPW51bGw7XHJcblxyXG5cclxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIF9wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0OlBlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQscHJpdmF0ZSBfY2hhbm5lbENvdW50OiBudW1iZXIsIHByaXZhdGUgX3NhbXBsZVJhdGU6IG51bWJlcixwcml2YXRlIF9jaHVua0ZyYW1lTGVuOm51bWJlcixwcml2YXRlIF9mcmFtZUxlbjpudW1iZXIsIHByaXZhdGUgX3V1aWQ6c3RyaW5nKSB7XHJcbiAgICBzdXBlcigpO1xyXG4gICAgdGhpcy5yZWFkeSgpO1xyXG4gIH1cclxuXHJcbiAgZ2V0IGNoYW5uZWxDb3VudCgpOiBudW1iZXIge1xyXG4gICAgcmV0dXJuIHRoaXMuX2NoYW5uZWxDb3VudDtcclxuICB9XHJcblxyXG4gIHNlYWxlZCgpOiBib29sZWFuIHtcclxuICAgIHJldHVybiB0aGlzLl9zZWFsZWQ7XHJcbiAgfVxyXG5cclxuICBzZWFsKCk6dm9pZHtcclxuICAgIHRoaXMuX3NlYWxlZD10cnVlO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBkZWxldGVDaHVuayhvczpJREJPYmplY3RTdG9yZSxjaTpudW1iZXIsY2I6KGtleXM6SURCVmFsaWRLZXlbXSk9PnZvaWQsZXJyQ2I6KGVycjpFcnJvcik9PnZvaWQpe1xyXG5cclxuICAgIC8vIEJ1aWxkIGJvdW5kcyBmb3IgY2hhbm5lbHNcclxuICAgIGxldCBsSWR4PVt0aGlzLl91dWlkLGNpLDBdO1xyXG4gICAgbGV0IGhJZHg9W3RoaXMuX3V1aWQsY2ksdGhpcy5fY2hhbm5lbENvdW50XTtcclxuICAgIGxldCBjaHNLcj1JREJLZXlSYW5nZS5ib3VuZChsSWR4LGhJZHgpO1xyXG5cclxuICAgIGxldCBleFJxPW9zLmdldEFsbEtleXMoY2hzS3IpO1xyXG5cclxuICAgIGV4UnEub25zdWNjZXNzPSgpPT57XHJcbiAgICAgIGxldCBjYz1leFJxLnJlc3VsdDtcclxuICAgICAgaWYoY2MgJiYgY2MubGVuZ3RoPjApIHtcclxuICAgICAgICBsZXQgcnEgPSBvcy5kZWxldGUoY2hzS3IpO1xyXG4gICAgICAgIHJxLm9uZXJyb3I9KGVyckV2KT0+e1xyXG4gICAgICAgICAgZXJyQ2IobmV3IEVycm9yKGVyckV2LnRvU3RyaW5nKCkpKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgb3MudHJhbnNhY3Rpb24ub25jb21wbGV0ZT0oKT0+e1xyXG4gICAgICAgICAgY2IoY2MpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBvcy50cmFuc2FjdGlvbi5jb21taXQoKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgZXhScS5vbmVycm9yPShlcnJFdik9PntcclxuICAgICAgZXJyQ2IobmV3IEVycm9yKGVyckV2LnRvU3RyaW5nKCkpKTtcclxuICAgIH1cclxuXHJcbiAgfVxyXG5cclxuICBwcml2YXRlIGRlbGV0ZUFsbEJ1ZnMob3M6SURCT2JqZWN0U3RvcmUsY2k6bnVtYmVyLGNiRW5kOigpPT52b2lkLGNiRXJyOihlcnI6RXJyb3IpPT52b2lkKXtcclxuICAgIC8vY29uc29sZS5kZWJ1ZygnSW5kZXhlZERiQXVkaW9CdWZmZXI6OmZpbGxCdWZzOiBmcmFtZVBvczonK2ZyYW1lUG9zKycsIGZyYW1lTGVuOiAnK2ZyYW1lTGVuKycsIGZpbGxlZDogJytmaWxsZWQrJywgc3JjRnJhbWVQb3M6ICcrc3JjRnJhbWVQb3MrJyxjaTogJytjaSsnLCBjY1BvczogJytjY1Bvcyk7XHJcbiAgICB0aGlzLmRlbGV0ZUNodW5rKG9zLGNpLChjY0tleXMpPT57XHJcbiAgICAgIGlmKGNjS2V5cyAmJiBjY0tleXMubGVuZ3RoPjApe1xyXG4gICAgICAgIC8vY29uc29sZS5kZWJ1ZygnSW5kZXhlZERiQXVkaW9CdWZmZXI6OmZpbGxCdWZzIGZyYW1lTGVuOiAnK2ZyYW1lTGVuKTtcclxuICAgICAgICBjaSsrO1xyXG4gICAgICAgIHRoaXMuZGVsZXRlQWxsQnVmcyhvcyxjaSxjYkVuZCwgY2JFcnIpO1xyXG4gICAgICB9ZWxzZXtcclxuICAgICAgICAvL2NvbnNvbGUuZGVidWcoJ0luZGV4ZWREYkF1ZGlvQnVmZmVyOjpmaWxsQnVmcyAoY2h1bmsgbm90IGZvdW5kKSBjYWxsOiBjYmVuZCAnK2ZpbGxlZCk7XHJcbiAgICAgICAgY2JFbmQoKTtcclxuICAgICAgfVxyXG4gICAgfSwoZXJyKT0+e1xyXG4gICAgICBjYkVycihlcnIpO1xyXG4gICAgfSk7XHJcblxyXG4gIH1cclxuXHJcbiAgZ2V0IHNhbXBsZVJhdGUoKTogbnVtYmVyIHtcclxuICAgIHJldHVybiB0aGlzLl9zYW1wbGVSYXRlO1xyXG4gIH1cclxuXHJcbiAgZ2V0IGZyYW1lTGVuKCk6IG51bWJlciB7XHJcbiAgICByZXR1cm4gdGhpcy5fZnJhbWVMZW47XHJcbiAgfVxyXG5cclxuICBnZXQgY2h1bmtDb3VudCgpOiBudW1iZXIge1xyXG4gICAgcmV0dXJuIHRoaXMuX2NodW5rQ291bnQ7XHJcbiAgfVxyXG5cclxuICBhcHBlbmRSYXdBdWRpb0RhdGEoZGF0YTpBcnJheTxBcnJheTxGbG9hdDMyQXJyYXk+Pik6T2JzZXJ2YWJsZTx2b2lkPntcclxuXHJcbiAgcmV0dXJuIG5ldyBPYnNlcnZhYmxlPHZvaWQ+KHN1YnNjcmliZXIgPT4ge1xyXG5cclxuICAgIGlmICh0aGlzLl9wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0ICYmIHRoaXMuX3V1aWQpIHtcclxuICAgICAgbGV0IHRyID0gdGhpcy5fcGVyc2lzdGVudEF1ZGlvU3RvcmFnZVRhcmdldC5pbmRleGVkRGIudHJhbnNhY3Rpb24odGhpcy5fcGVyc2lzdGVudEF1ZGlvU3RvcmFnZVRhcmdldC5zdG9yZU5hbWUsICdyZWFkd3JpdGUnKTtcclxuICAgICAgbGV0IHJlY0ZpbGVPYmpTdG9yZSA9IHRyLm9iamVjdFN0b3JlKHRoaXMuX3BlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQuc3RvcmVOYW1lKTtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgbGV0IGNoMERhdGEgPSBkYXRhWzBdO1xyXG4gICAgICAgIGxldCBkYXRhQ2hrQ250ID0gY2gwRGF0YS5sZW5ndGg7XHJcbiAgICAgICAgbGV0IHBvcyA9IDA7XHJcbiAgICAgICAgZm9yIChsZXQgY2hDa0lkeCA9IDA7IGNoQ2tJZHggPCBkYXRhQ2hrQ250OyBjaENrSWR4KyspIHtcclxuICAgICAgICAgIGxldCBidWZMZW4gPSAwO1xyXG4gICAgICAgICAgZm9yIChsZXQgY2ggPSAwOyBjaCA8IHRoaXMuY2hhbm5lbENvdW50OyBjaCsrKSB7XHJcbiAgICAgICAgICAgIGxldCBjaENoayA9IGRhdGFbY2hdW2NoQ2tJZHhdO1xyXG4gICAgICAgICAgICBidWZMZW4gPSBjaENoay5sZW5ndGg7XHJcbiAgICAgICAgICAgIC8vbGV0IGNhY2hlSWQgPSB1dWlkICsgJ18nICsgY2ggKyAnXycgKyBjaENrSWR4O1xyXG4gICAgICAgICAgICBsZXQgY2hrRGJJZCA9IFt0aGlzLl91dWlkLCB0aGlzLmluZERiQ2hrSWR4ICsgY2hDa0lkeCwgY2hdO1xyXG4gICAgICAgICAgICBsZXQgY3IgPSByZWNGaWxlT2JqU3RvcmUuYWRkKGNoQ2hrLCBjaGtEYklkKTtcclxuICAgICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiQWRkZWQ6IFwiK2NoK1wiIFwiKyh0aGlzLmluZERiQ2hrSWR4K2NoQ2tJZHgpKTtcclxuICAgICAgICAgICAgY3Iub25zdWNjZXNzID0gKCkgPT4ge1xyXG4gICAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIlN0b3JlZCBhdWRpbyBkYXRhIHRvIGluZGV4ZWQgZGJcIik7XHJcbiAgICAgICAgICAgICAgLy8gaWYoY2hDa0lkeD49IGRhdGFDaGtDbnQtMSAmJiBjaD49dGhpcy5jaGFubmVsQ291bnQtMSl7XHJcbiAgICAgICAgICAgICAgLy8gICB0ci5jb21taXQoKTtcclxuICAgICAgICAgICAgICAvLyB9XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgY3Iub25lcnJvciA9ICgpID0+IHtcclxuICAgICAgICAgICAgICBpZighdGhpcy5fc3RvcmVFcnJvcikge1xyXG4gICAgICAgICAgICAgICAgLy8gT25seSBsb2cgdGhlIGZpcnN0IGVycm9yXHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiRXJyb3Igc3RvcmluZyBhdWRpbyBkYXRhIG9mIFwiICsgY2hDaGsuYnl0ZUxlbmd0aCArIFwiIGJ5dGVzIHRvIGluZGV4ZWQgZGI6IFwiICsgY3IuZXJyb3IpO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgcG9zICs9IGJ1ZkxlbjtcclxuICAgICAgICAgIHRoaXMuX2ZyYW1lTGVuKz1idWZMZW47XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRoaXMuaW5kRGJDaGtJZHggKz0gZGF0YUNoa0NudDtcclxuXHJcbiAgICAgICAgdHIub25lcnJvciA9IChldikgPT4ge1xyXG4gICAgICAgICAgaWYoIXRoaXMuX3N0b3JlRXJyb3IpIHtcclxuICAgICAgICAgICAgLy8gT25seSBsb2cgdGhlIGZpcnN0IGVycm9yXHJcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJGYWlsZWQgdG8gc3RvcmUgYXVkaW8gZGF0YSB0byBpbmRleGVkIGRiLCB0cmFuc2FjdGlvbiBlcnJvcjogXCIrdHIuZXJyb3IpO1xyXG4gICAgICAgICAgICB0aGlzLl9zdG9yZUVycm9yID0gdHIuZXJyb3I7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICBzdWJzY3JpYmVyLmVycm9yKHRyLmVycm9yKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgdHIub25hYm9ydD0oZXYpPT57XHJcbiAgICAgICAgICAvLyBJZiBDaHJvbWUgcmVhY2hlcyBxdW90YSBpdCBhYm9ydHMgdGhlIHRyYW5zYWN0aW9uXHJcbiAgICAgICAgICBpZighdGhpcy5fc3RvcmVFcnJvcikge1xyXG4gICAgICAgICAgICAvLyBPbmx5IGxvZyB0aGUgZmlyc3QgZXJyb3JcclxuICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIkZhaWxlZCB0byBzdG9yZSBhdWRpbyBkYXRhIHRvIGluZGV4ZWQgZGIsIHRyYW5zYWN0aW9uIGFib3J0ZWQ6IFwiK3RyLmVycm9yKTtcclxuICAgICAgICAgICAgdGhpcy5fc3RvcmVFcnJvciA9IHRyLmVycm9yO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgc3Vic2NyaWJlci5lcnJvcih0ci5lcnJvcik7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRyLm9uY29tcGxldGUgPSAoZXYpID0+IHtcclxuICAgICAgICAgIHN1YnNjcmliZXIuY29tcGxldGUoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgdHIuY29tbWl0KCk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycikge1xyXG4gICAgICAgIHN1YnNjcmliZXIuZXJyb3IobmV3IEVycm9yKCdUcmFuc2ZlciBhdWRpbyBkYXRhIGVycm9yOiAnICsgZXJyKSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9KTtcclxuICB9XHJcblxyXG4gIHN0YXRpYyBmcm9tQ2h1bmtBdWRpb0J1ZmZlcihwZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0OlBlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQgLGF1ZGlvQnVmZmVyOkF1ZGlvQnVmZmVyKTpPYnNlcnZhYmxlPEluZGV4ZWREYkF1ZGlvQnVmZmVyPntcclxuXHJcbiAgICByZXR1cm4gbmV3IE9ic2VydmFibGU8SW5kZXhlZERiQXVkaW9CdWZmZXI+KHN1YnNjcmliZXIgPT4ge1xyXG4gICAgICBsZXQgY2hzID0gYXVkaW9CdWZmZXIubnVtYmVyT2ZDaGFubmVscztcclxuICAgICAgbGV0IHNyID0gYXVkaW9CdWZmZXIuc2FtcGxlUmF0ZTtcclxuICAgICAgbGV0IGNoa0ZyYW1lTGVuZ3RoID0gYXVkaW9CdWZmZXIubGVuZ3RoO1xyXG5cclxuICAgICAgbGV0IGlhYiA9IG5ldyBJbmRleGVkRGJBdWRpb0J1ZmZlcihwZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0LCBjaHMsIHNyLCBjaGtGcmFtZUxlbmd0aCwgMCwgVVVJRC5nZW5lcmF0ZSgpKTtcclxuICAgICAgaWFiLmFwcGVuZENodW5rQXVkaW9CdWZmZXIoYXVkaW9CdWZmZXIpLnN1YnNjcmliZShcclxuICAgICAgICB7XHJcbiAgICAgICAgICBuZXh0OiAoKT0+e1xyXG4gICAgICAgICAgICBzdWJzY3JpYmVyLm5leHQoaWFiKTtcclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICBjb21wbGV0ZTogKCkgPT4ge1xyXG4gICAgICAgICAgICBzdWJzY3JpYmVyLmNvbXBsZXRlKCk7XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgZXJyb3I6IChlcnIpPT57XHJcbiAgICAgICAgICAgIHN1YnNjcmliZXIuZXJyb3IoZXJyKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIClcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgYXBwZW5kQ2h1bmtBdWRpb0J1ZmZlcihhdWRpb0J1ZmZlcjpBdWRpb0J1ZmZlcil7XHJcbiAgICBpZih0aGlzLl9zZWFsZWQpe1xyXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0Nhbm5vdCBhcHBlbmQgYXVkaW8gYnVmZmVyIHRvIGFscmVhZHkgc2VhbGVkIGF1ZGlvIGJ1ZmZlci4nKTtcclxuICAgIH1cclxuICAgIGxldCBjaHM9YXVkaW9CdWZmZXIubnVtYmVyT2ZDaGFubmVscztcclxuICAgIGlmKHRoaXMuX2NoYW5uZWxDb3VudCE9PSBjaHMpe1xyXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0Nhbm5vdCBhcHBlbmQgYXVkaW8gYnVmZmVyIHdpdGggJytjaHMrJyBjaGFubmVscyB0byB0aGlzIGFycmF5IGF1ZGlvIGJ1ZmZlciB3aXRoICcrdGhpcy5fY2hhbm5lbENvdW50KycgY2hhbm5lbHMuIE51bWJlciBvZiBjaGFubmVscyBtdXN0IG1hdGNoLicpO1xyXG4gICAgfVxyXG4gICAgbGV0IHNyPWF1ZGlvQnVmZmVyLnNhbXBsZVJhdGU7XHJcbiAgICBpZihzciE9PXRoaXMuX3NhbXBsZVJhdGUpe1xyXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0Nhbm5vdCBhcHBlbmQgYXVkaW8gYnVmZmVyIHdpdGggc2FtcGxlcmF0ZSAnK3NyKycgdG8gdGhpcyBhcnJheSBhdWRpbyBidWZmZXIgd2l0aCBzYW1wbGVyYXRlICcrdGhpcy5fc2FtcGxlUmF0ZSsnLiBTYW1wbGVyYXRlcyBtdXN0IG1hdGNoLicpO1xyXG4gICAgfVxyXG5cclxuICAgIGxldCBhYkZsPWF1ZGlvQnVmZmVyLmxlbmd0aDtcclxuICAgIGlmKGFiRmw+dGhpcy5fY2h1bmtGcmFtZUxlbil7XHJcbiAgICAgIHRocm93IG5ldyBFcnJvcignQ2Fubm90IGFwcGVuZCBhdWRpbyBidWZmZXIgd2l0aCBmcmFtZSBsZW5ndGggJythYkZsKycgdG8gdGhpcyBhcnJheSBhdWRpbyBidWZmZXIgd2l0aCBjaHVuayBmcmFtZSBsZW5ndGggJyt0aGlzLl9jaHVua0ZyYW1lTGVuKycuIENodW5rIGxlbmd0aCBtdXN0IGJlIGVxdWFsIG9yIGxlc3MgaWYgbGFzdCBjaHVuay4nKTtcclxuICAgIH1cclxuICAgIHJldHVybiBuZXcgT2JzZXJ2YWJsZTx2b2lkPihzdWJzY3JpYmVyID0+IHtcclxuICAgICAgaWYgKHRoaXMuX3BlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQgJiYgdGhpcy5fdXVpZCkge1xyXG4gICAgICAgIGxldCB0ciA9IHRoaXMuX3BlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQudHJhbnNhY3Rpb24oJ3JlYWR3cml0ZScpO1xyXG4gICAgICAgIGxldCByZWNGaWxlT2JqU3RvcmUgPSB0ci5vYmplY3RTdG9yZSh0aGlzLl9wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0LnN0b3JlTmFtZSk7XHJcbiAgICAgICAgdHJ5IHtcclxuXHJcbiAgICAgICAgICAgIGZvciAobGV0IGNoID0gMDsgY2ggPCB0aGlzLmNoYW5uZWxDb3VudDsgY2grKykge1xyXG4gICAgICAgICAgICAgIGxldCBjaENoayA9IGF1ZGlvQnVmZmVyLmdldENoYW5uZWxEYXRhKGNoKTtcclxuICAgICAgICAgICAgICBsZXQgY2hrRGJJZCA9IFt0aGlzLl91dWlkLCB0aGlzLmluZERiQ2hrSWR4KyssIGNoXTtcclxuICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgbGV0IGNyID0gcmVjRmlsZU9ialN0b3JlLmFkZChjaENoaywgY2hrRGJJZCk7XHJcbiAgICAgICAgICAgICAgICBjci5vbnN1Y2Nlc3MgPSAoZXYpID0+IHt9XHJcbiAgICAgICAgICAgICAgICBjci5vbmVycm9yID0gKGV2KSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIGlQYWQgbWF5IHRocm93IFF1b3RhRXhjZWVkZWRFcnJvciBoZXJlXHJcbiAgICAgICAgICAgICAgICAgIC8vIGlQYWQgYXNrcyBmb3IgbW9yZSBzdG9yYWdlIGFuZCBpZiBkZW5pZWQsIHRoZSBlcnJvciBpcyB0aHJvd24gaGVyZVxyXG4gICAgICAgICAgICAgICAgICBpZighdGhpcy5fc3RvcmVFcnJvcikge1xyXG4gICAgICAgICAgICAgICAgICAgIC8vIE9ubHkgbG9nIHRoZSBmaXJzdCBlcnJvclxyXG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJFcnJvciBzdG9yaW5nIGF1ZGlvIGRhdGEgb2YgXCIgKyBjaENoay5ieXRlTGVuZ3RoICsgXCIgYnl0ZXMgdG8gaW5kZXhlZCBkYjogXCIgKyBjci5lcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgLy9zdWJzY3JpYmVyLmVycm9yKGNyLmVycm9yKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICB9Y2F0Y2goZXJyMTphbnkpe1xyXG4gICAgICAgICAgICAgICAgaWYoIXRoaXMuX3N0b3JlRXJyb3IpIHtcclxuICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIkVycm9yIGFkZGluZyBhdWRpbyBkYXRhIHRvIGluZGV4ZWQgZGIgc3RvcmU6IFwiICsgZXJyMSk7XHJcbiAgICAgICAgICAgICAgICAgIHRoaXMuX3N0b3JlRXJyb3I9KGVycjEgaW5zdGFuY2VvZiBFcnJvcik/ZXJyMTpuZXcgRXJyb3IoZXJyMSk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBzdWJzY3JpYmVyLmVycm9yKGVycjEpO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB0aGlzLl9mcmFtZUxlbis9YWJGbDtcclxuXHJcbiAgICAgICAgICB0ci5vbmVycm9yID0gKGV2KSA9PiB7XHJcbiAgICAgICAgICAgIGlmKCF0aGlzLl9zdG9yZUVycm9yKSB7XHJcbiAgICAgICAgICAgICAgLy8gT25seSBsb2cgdGhlIGZpcnN0IGVycm9yXHJcbiAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIkZhaWxlZCB0byBzdG9yZSBhdWRpbyBkYXRhIHRvIGluZGV4ZWQgZGIsIHRyYW5zYWN0aW9uIGVycm9yOiBcIit0ci5lcnJvcik7XHJcbiAgICAgICAgICAgICAgdGhpcy5fc3RvcmVFcnJvciA9IHRyLmVycm9yO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIHN1YnNjcmliZXIuZXJyb3IodHIuZXJyb3IpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgdHIub25hYm9ydD0oZXYpPT57XHJcbiAgICAgICAgICAgIC8vIElmIENocm9tZSByZWFjaGVzIHF1b3RhIGl0IGFib3J0cyB0aGUgdHJhbnNhY3Rpb25cclxuICAgICAgICAgICAgaWYoIXRoaXMuX3N0b3JlRXJyb3IpIHtcclxuICAgICAgICAgICAgICAvLyBPbmx5IGxvZyB0aGUgZmlyc3QgZXJyb3JcclxuICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiRmFpbGVkIHRvIHN0b3JlIGF1ZGlvIGRhdGEgdG8gaW5kZXhlZCBkYiwgdHJhbnNhY3Rpb24gYWJvcnRlZDogXCIrdHIuZXJyb3IpO1xyXG4gICAgICAgICAgICAgIHRoaXMuX3N0b3JlRXJyb3IgPSB0ci5lcnJvcjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBzdWJzY3JpYmVyLmVycm9yKHRyLmVycm9yKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHRyLm9uY29tcGxldGUgPSAoZXYpID0+IHtcclxuICAgICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiVHJhbnNhY3Rpb24gY29tcGxldGVcIik7XHJcbiAgICAgICAgICAgIGlmKGFiRmw8dGhpcy5fY2h1bmtGcmFtZUxlbil7XHJcbiAgICAgICAgICAgICAgLy8gbGFzdCBjaHVua1xyXG4gICAgICAgICAgICAgIHRoaXMuc2VhbCgpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIHN1YnNjcmliZXIubmV4dCgpO1xyXG4gICAgICAgICAgICBzdWJzY3JpYmVyLmNvbXBsZXRlKCk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICB0ci5jb21taXQoKTtcclxuICAgICAgICB9IGNhdGNoIChlcnIpIHtcclxuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0NhdGNoZWQgZXJyb3I6ICcrZXJyKTtcclxuICAgICAgICAgIHN1YnNjcmliZXIuZXJyb3IobmV3IEVycm9yKCdUcmFuc2ZlciBhdWRpbyBkYXRhIGVycm9yOiAnICsgZXJyKSk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIHJlbGVhc2VBdWRpb0RhdGEoKTpPYnNlcnZhYmxlPHZvaWQ+e1xyXG4gICAgcmV0dXJuIG5ldyBPYnNlcnZhYmxlPHZvaWQ+KChzdWJzY3JpYmVyKT0+e1xyXG4gICAgICBsZXQgb3M9dGhpcy5fcGVyc2lzdGVudEF1ZGlvU3RvcmFnZVRhcmdldC5vYmplY3RTdG9yZSgncmVhZHdyaXRlJyk7XHJcbiAgICAgIHRoaXMuZGVsZXRlQWxsQnVmcyhvcywwLCgpPT57XHJcbiAgICAgICAgc3Vic2NyaWJlci5uZXh0KCk7XHJcbiAgICAgICAgc3Vic2NyaWJlci5jb21wbGV0ZSgpO1xyXG4gICAgICB9LChlcnIpPT57XHJcbiAgICAgICAgc3Vic2NyaWJlci5lcnJvcihlcnIpO1xyXG4gICAgICB9KVxyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICB0b1N0cmluZygpOnN0cmluZ3tcclxuICAgIHJldHVybiBcIkluZGV4ZWQgZGIgYXVkaW8gYnVmZmVyLiBDaGFubmVsczogXCIrdGhpcy5jaGFubmVsQ291bnQrXCIsIHNhbXBsZSByYXRlOiBcIit0aGlzLnNhbXBsZVJhdGUrXCIsIGNodW5rIGZyYW1lIGxlbmd0aDogXCIrdGhpcy5fY2h1bmtGcmFtZUxlbitcIiwgbnVtYmVyIG9mIGNodW5rczogXCIrdGhpcy5jaHVua0NvdW50K1wiLCBmcmFtZSBsZW5ndGg6IFwiK3RoaXMuZnJhbWVMZW4rXCIsIHNlYWxlZDogXCIrdGhpcy5zZWFsZWQoKTtcclxuICB9XHJcblxyXG4gIGFzeW5jQXVkaW9JbnB1dFN0cmVhbSgpOiBBc3luY0Zsb2F0MzJBcnJheUlucHV0U3RyZWFtIHwgbnVsbCB7XHJcbiAgICByZXR1cm4gbmV3IEluZGV4ZWREYkF1ZGlvSW5wdXRTdHJlYW0odGhpcyk7XHJcbiAgfVxyXG5cclxuICBhdWRpb0lucHV0U3RyZWFtKCk6IEZsb2F0MzJBcnJheUlucHV0U3RyZWFtIHwgbnVsbCB7XHJcbiAgICAvLyBTeW5jaHJvbm91cyBzdHJlYW0gbm90IHN1cHBvcnRlZFxyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG5cclxuICBnZXQgZHVyYXRpb24oKTogbnVtYmVyIHtcclxuICAgIHJldHVybiB0aGlzLl9jaHVua0ZyYW1lTGVuL3RoaXMuX3NhbXBsZVJhdGU7XHJcbiAgfVxyXG5cclxuICBnZXQgbnVtYmVyT2ZDaGFubmVscygpOiBudW1iZXIge1xyXG4gICAgcmV0dXJuIHRoaXMuX2NoYW5uZWxDb3VudDtcclxuICB9XHJcblxyXG4gIHNhbXBsZUNvdW50cygpOiBudW1iZXIge1xyXG4gICAgcmV0dXJuIHRoaXMuX2NoYW5uZWxDb3VudCp0aGlzLl9mcmFtZUxlbjtcclxuICB9XHJcblxyXG4gIHJhbmRvbUFjY2Vzc0F1ZGlvU3RyZWFtKCk6IFJhbmRvbUFjY2Vzc0F1ZGlvU3RyZWFtIHtcclxuICAgIHJldHVybiBuZXcgSW5kZXhlZERiUmFuZG9tQWNjZXNzU3RyZWFtKHRoaXMpO1xyXG4gIH1cclxuXHJcbn1cclxuXHJcblxyXG5leHBvcnQgY2xhc3MgSW5kZXhlZERiUmFuZG9tQWNjZXNzU3RyZWFtIGltcGxlbWVudHMgUmFuZG9tQWNjZXNzQXVkaW9TdHJlYW17XHJcblxyXG4gIHByaXZhdGUgX2N1cnJDaT0wO1xyXG4gIHByaXZhdGUgX2NjQ2FjaGU6RmxvYXQzMkFycmF5W118bnVsbD1udWxsO1xyXG5cclxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIF9pbmRkYkFiOkluZGV4ZWREYkF1ZGlvQnVmZmVyKSB7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIGNodW5rKG9zOklEQk9iamVjdFN0b3JlLGNpOm51bWJlcixjYjooYnVmczpBcnJheTxGbG9hdDMyQXJyYXk+fG51bGwpPT52b2lkLGVyckNiOihlcnI6RXJyb3IpPT52b2lkKXtcclxuXHJcbiAgICAvLyBCdWlsZCBib3VuZHMgZm9yIGNoYW5uZWxzXHJcbiAgICBsZXQgbElkeD1bdGhpcy5faW5kZGJBYi51dWlkLGNpLDBdO1xyXG4gICAgbGV0IGhJZHg9W3RoaXMuX2luZGRiQWIudXVpZCxjaSx0aGlzLl9pbmRkYkFiLmNoYW5uZWxDb3VudF07XHJcbiAgICBsZXQgY2hzS3I9SURCS2V5UmFuZ2UuYm91bmQobElkeCxoSWR4KTtcclxuXHJcbiAgICBsZXQgcnE9b3MuZ2V0QWxsKGNoc0tyKTtcclxuXHJcbiAgICBycS5vbnN1Y2Nlc3M9KCk9PntcclxuICAgICAgbGV0IGNjPXJxLnJlc3VsdDtcclxuICAgICAgbGV0IGNjMDtcclxuICAgICAgbGV0IGNjQ2hzPWNjLmxlbmd0aDtcclxuICAgICAgaWYoY2NDaHM9PTApe1xyXG4gICAgICAgIGNiKG51bGwpO1xyXG4gICAgICAgIHJldHVybjtcclxuICAgICAgfVxyXG4gICAgICBpZihjY0NocyE9PXRoaXMuX2luZGRiQWIuY2hhbm5lbENvdW50KXtcclxuICAgICAgICBlcnJDYihuZXcgRXJyb3IoJ051bWJlciBvZiBjaGFubmVscyBvZiBpbmRkYiBkYXRhICgnK2NjQ2hzKycpIGRvZXMgbm90IG1hdGNoIG51bWJlciBvZiBjaGFubmVscyAoJyt0aGlzLl9pbmRkYkFiLmNoYW5uZWxDb3VudCsnKScpKTtcclxuICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgfVxyXG4gICAgICBpZihjYy5sZW5ndGg+MCl7XHJcbiAgICAgICAgY2MwPWNjWzBdO1xyXG4gICAgICB9XHJcbiAgICAgIGxldCBhcnJCdWY9bmV3IEFycmF5PEZsb2F0MzJBcnJheT4oKTtcclxuICAgICAgbGV0IGNjTGVuPWNjMC5sZW5ndGg7XHJcbiAgICAgIGZvcihsZXQgY2g9MDtjaDxjY0NocztjaCsrKXtcclxuICAgICAgICBsZXQgY2hBcnI9bmV3IEZsb2F0MzJBcnJheShjY0xlbik7XHJcbiAgICAgICAgZm9yKGxldCBzaT0wO3NpPGNjTGVuO3NpKyspe1xyXG4gICAgICAgICAgY2hBcnJbc2ldPWNjW2NoXVtzaV07XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGFyckJ1Zi5wdXNoKGNoQXJyKTtcclxuICAgICAgfVxyXG4gICAgICBjYihhcnJCdWYpO1xyXG4gICAgfVxyXG4gICAgcnEub25lcnJvcj0oZXJyRXYpPT57XHJcbiAgICAgIGVyckNiKG5ldyBFcnJvcihlcnJFdi50b1N0cmluZygpKSk7XHJcbiAgICB9XHJcblxyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBfZmlsbEJ1ZnMoY2NCdWZzOkZsb2F0MzJBcnJheVtdLHRyZ1N0YXRlOntmcmFtZVBvczpudW1iZXIsZnJhbWVMZW46bnVtYmVyLHRyZ0J1ZnM6RmxvYXQzMkFycmF5W10sZmlsbGVkOm51bWJlcn0sc3JjU3RhdGU6e3NyY0ZyYW1lUG9zOm51bWJlcixjaTpudW1iZXIsY2NQb3M6bnVtYmVyfSl7XHJcbiAgICBsZXQgY2NCdWZzQ2hzPWNjQnVmcy5sZW5ndGg7XHJcbiAgICBpZihjY0J1ZnNDaHM+MCkge1xyXG4gICAgICBsZXQgY2NCdWYwID0gY2NCdWZzWzBdO1xyXG4gICAgICBsZXQgY2NCdWZzTGVuID0gY2NCdWYwLmxlbmd0aDtcclxuXHJcbiAgICAgIC8vY29uc29sZS5kZWJ1ZygnSW5kZXhlZERiQXVkaW9CdWZmZXI6OmZpbGxCdWZzIGZyYW1lUG9zOiAnK2ZyYW1lUG9zKycsIHNyY0ZyYW1lUG9zOiAnK3NyY0ZyYW1lUG9zKycsIGNjQnVmc0xlbjogJytjY0J1ZnNMZW4pO1xyXG4gICAgICBpZiAodHJnU3RhdGUuZnJhbWVQb3MgPj0gc3JjU3RhdGUuc3JjRnJhbWVQb3MgKyBjY0J1ZnNMZW4pIHtcclxuICAgICAgICAvLyB0YXJnZXQgZnJhbWUgcG9zaXRpb24gaXMgYWhlYWQsIHNlZWsgdG8gbmV4dCBzb3VyY2UgYnVmZmVyXHJcbiAgICAgICAgLy9jb25zb2xlLmRlYnVnKCdJbmRleGVkRGJBdWRpb0J1ZmZlcjo6ZmlsbEJ1ZnMgc2VlayB0byBuZXh0IGluZGRiIGJ1ZmZlcicpO1xyXG4gICAgICAgIHRoaXMuX2NjQ2FjaGU9bnVsbDtcclxuICAgICAgICBzcmNTdGF0ZS5jaSsrO1xyXG4gICAgICAgIHNyY1N0YXRlLmNjUG9zPTA7XHJcbiAgICAgICAgc3JjU3RhdGUuc3JjRnJhbWVQb3MrPWNjQnVmc0xlbjtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBBc3N1bWluZyB0YXJnZXQgZnJhbWUgcG9zIGlzIGluc2lkZSBjdXJyZW50IHNvdXJjZSBidWZmZXJcclxuICAgICAgICBzcmNTdGF0ZS5jY1Bvcz10cmdTdGF0ZS5mcmFtZVBvcy1zcmNTdGF0ZS5zcmNGcmFtZVBvcztcclxuICAgICAgICBsZXQgY2NBdmFpbCA9IGNjQnVmc0xlbiAtIHNyY1N0YXRlLmNjUG9zO1xyXG4gICAgICAgIGxldCB0b0NvcHkgPSBjY0J1ZnNMZW47XHJcblxyXG4gICAgICAgIGlmICh0b0NvcHkgPiBjY0F2YWlsKSB7XHJcbiAgICAgICAgICB0b0NvcHkgPSBjY0F2YWlsO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAodG9Db3B5ID4gdHJnU3RhdGUuZnJhbWVMZW4pIHtcclxuICAgICAgICAgIHRvQ29weSA9IHRyZ1N0YXRlLmZyYW1lTGVuO1xyXG4gICAgICAgIH1cclxuICAgICAgICBmb3IgKGxldCBjaCA9IDA7IGNoIDwgY2NCdWZzQ2hzOyBjaCsrKSB7XHJcbiAgICAgICAgICBmb3IgKGxldCBzaSA9IDA7IHNpIDwgdG9Db3B5OyBzaSsrKSB7XHJcbiAgICAgICAgICAgIHRyZ1N0YXRlLnRyZ0J1ZnNbY2hdW3RyZ1N0YXRlLmZpbGxlZCtzaV0gPSBjY0J1ZnNbY2hdW3NyY1N0YXRlLmNjUG9zK3NpXTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgICAgdHJnU3RhdGUuZmlsbGVkICs9IHRvQ29weTtcclxuICAgICAgICB0cmdTdGF0ZS5mcmFtZUxlbiAtPSB0b0NvcHk7XHJcbiAgICAgICAgdHJnU3RhdGUuZnJhbWVQb3MgKz0gdG9Db3B5O1xyXG4gICAgICAgIHNyY1N0YXRlLmNjUG9zICs9IHRvQ29weTtcclxuICAgICAgICBpZiAoc3JjU3RhdGUuY2NQb3MgPj0gY2NCdWZzTGVuKSB7XHJcbiAgICAgICAgICAvLyBJbnZhbGlkYXRlIGNhY2hlXHJcbiAgICAgICAgICB0aGlzLl9jY0NhY2hlPW51bGw7XHJcbiAgICAgICAgICBzcmNTdGF0ZS5jaSsrO1xyXG4gICAgICAgICAgdGhpcy5fY3VyckNpPXNyY1N0YXRlLmNpO1xyXG4gICAgICAgICAgc3JjU3RhdGUuY2NQb3MgPSAwO1xyXG4gICAgICAgICAgc3JjU3RhdGUuc3JjRnJhbWVQb3MrPWNjQnVmc0xlbjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgfVxyXG5cclxuICBwcml2YXRlIGZpbGxCdWZzKG9zOklEQk9iamVjdFN0b3JlLHRyZ1N0YXRlOntmcmFtZVBvczpudW1iZXIsZnJhbWVMZW46bnVtYmVyLHRyZ0J1ZnM6RmxvYXQzMkFycmF5W10sZmlsbGVkOm51bWJlcn0sc3JjU3RhdGU6e3NyY0ZyYW1lUG9zOm51bWJlcixjaTpudW1iZXIsY2NQb3M6bnVtYmVyLGNjRmlsbGVkOm51bWJlcn0sY2I6KGZpbGxlZDpudW1iZXIpPT52b2lkLGNiRW5kOihmaWxsZWQ6bnVtYmVyKT0+dm9pZCxjYkVycjooZXJyOkVycm9yKT0+dm9pZCl7XHJcbiAgICAvL2NvbnNvbGUuZGVidWcoJ0luZGV4ZWREYkF1ZGlvQnVmZmVyOjpmaWxsQnVmczogZnJhbWVQb3M6Jyt0cmdTdGF0ZS5mcmFtZVBvcysnLCBmcmFtZUxlbjogJyt0cmdTdGF0ZS5mcmFtZUxlbisnLCBmaWxsZWQ6ICcrdHJnU3RhdGUuZmlsbGVkKycsIHNyY0ZyYW1lUG9zOiAnK3NyY1N0YXRlLnNyY0ZyYW1lUG9zKycsY2k6ICcrc3JjU3RhdGUuY2krJywgY2NQb3M6ICcrc3JjU3RhdGUuY2NQb3MpO1xyXG4gICAgaWYodGhpcy5fY2NDYWNoZSl7XHJcbiAgICAgIHRoaXMuX2ZpbGxCdWZzKHRoaXMuX2NjQ2FjaGUsdHJnU3RhdGUsc3JjU3RhdGUpO1xyXG4gICAgICAvL2NvbnNvbGUuZGVidWcoJ0luZGV4ZWREYkF1ZGlvQnVmZmVyOjpmaWxsQnVmcyBmcmFtZUxlbjogJyt0cmdTdGF0ZS5mcmFtZUxlbik7XHJcbiAgICAgIGlmKHRyZ1N0YXRlLmZyYW1lTGVuPT09MCl7XHJcbiAgICAgICAgLy9jb25zb2xlLmRlYnVnKCdJbmRleGVkRGJBdWRpb0J1ZmZlcjo6ZmlsbEJ1ZnMgKGZyYW1lbGVuPT0wKSBjYWxsOiBjYmVuZCAnK3RyZ1N0YXRlLmZpbGxlZCk7XHJcbiAgICAgICAgY2JFbmQodHJnU3RhdGUuZmlsbGVkKTtcclxuICAgICAgfWVsc2Uge1xyXG4gICAgICAgIHRoaXMuZmlsbEJ1ZnMob3MsIHRyZ1N0YXRlLCBzcmNTdGF0ZSwgY2IsIGNiRW5kLCBjYkVycik7XHJcbiAgICAgIH1cclxuICAgIH1lbHNlIHtcclxuICAgICAgdGhpcy5jaHVuayhvcywgc3JjU3RhdGUuY2ksIChjY0J1ZnMpID0+IHtcclxuICAgICAgICBpZiAoY2NCdWZzKSB7XHJcbiAgICAgICAgICB0aGlzLl9jdXJyQ2kgPSBzcmNTdGF0ZS5jaTtcclxuICAgICAgICAgIHRoaXMuX2NjQ2FjaGUgPSBjY0J1ZnM7XHJcblxyXG4gICAgICAgICAgdGhpcy5fZmlsbEJ1ZnMoY2NCdWZzLCB0cmdTdGF0ZSwgc3JjU3RhdGUpO1xyXG4gICAgICAgICAgLy9jb25zb2xlLmRlYnVnKCdJbmRleGVkRGJBdWRpb0J1ZmZlcjo6ZmlsbEJ1ZnMgZnJhbWVMZW46ICcrdHJnU3RhdGUuZnJhbWVMZW4pO1xyXG4gICAgICAgICAgaWYgKHRyZ1N0YXRlLmZyYW1lTGVuID09PSAwKSB7XHJcbiAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZygnSW5kZXhlZERiQXVkaW9CdWZmZXI6OmZpbGxCdWZzIChmcmFtZWxlbj09MCkgY2FsbDogY2JlbmQgJyt0cmdTdGF0ZS5maWxsZWQpO1xyXG4gICAgICAgICAgICBjYkVuZCh0cmdTdGF0ZS5maWxsZWQpO1xyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgdGhpcy5maWxsQnVmcyhvcyx0cmdTdGF0ZSwgc3JjU3RhdGUsIGNiLCBjYkVuZCwgY2JFcnIpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoJ0luZGV4ZWREYkF1ZGlvQnVmZmVyOjpmaWxsQnVmcyAoY2h1bmsgbm90IGZvdW5kKSBjYWxsOiBjYmVuZCAnK3RyZ1N0YXRlLmZpbGxlZCk7XHJcbiAgICAgICAgICBjYkVuZCh0cmdTdGF0ZS5maWxsZWQpO1xyXG4gICAgICAgIH1cclxuICAgICAgfSwgKGVycikgPT4ge1xyXG4gICAgICAgIGNiRXJyKGVycik7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcblxyXG4gIGZyYW1lc09icyhmcmFtZVBvczpudW1iZXIsZnJhbWVMZW46bnVtYmVyLGJ1ZnM6RmxvYXQzMkFycmF5W10pOk9ic2VydmFibGU8bnVtYmVyPntcclxuICAgIHJldHVybiBuZXcgT2JzZXJ2YWJsZTxudW1iZXI+KChzdWJzY3JpYmVyKT0+e1xyXG4gICAgICBsZXQgdHI9dGhpcy5faW5kZGJBYi5wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0LnRyYW5zYWN0aW9uKCk7XHJcbiAgICAgIGxldCBvcz10ci5vYmplY3RTdG9yZSh0aGlzLl9pbmRkYkFiLnBlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQuc3RvcmVOYW1lKTtcclxuICAgICAgLy8gUG9zaXRpb25pbmdcclxuICAgICAgbGV0IG5ld0NpPU1hdGguZmxvb3IoZnJhbWVQb3MvdGhpcy5faW5kZGJBYi5jaHVua0ZyYW1lTGVuKTtcclxuICAgICAgaWYobmV3Q2khPT10aGlzLl9jdXJyQ2kpe1xyXG4gICAgICAgIHRoaXMuX2N1cnJDaT1uZXdDaTtcclxuICAgICAgICB0aGlzLl9jY0NhY2hlPW51bGw7XHJcbiAgICAgIH1cclxuICAgICAgbGV0IHNyY0ZyYW1lUG9zPW5ld0NpKnRoaXMuX2luZGRiQWIuY2h1bmtGcmFtZUxlbjtcclxuICAgICAgbGV0IHRyZ1N0YXRlPXtmcmFtZVBvczpmcmFtZVBvcyxmcmFtZUxlbjpmcmFtZUxlbix0cmdCdWZzOmJ1ZnMsZmlsbGVkOjB9O1xyXG4gICAgICBsZXQgc3JjU3RhdGU9e3NyY0ZyYW1lUG9zOnNyY0ZyYW1lUG9zLGNpOm5ld0NpLGNjUG9zOjAsY2NGaWxsZWQ6MH07XHJcbiAgICAgIHRoaXMuZmlsbEJ1ZnMob3MsdHJnU3RhdGUsc3JjU3RhdGUsKHZhbCk9Pnt9LChmaWxsZWQ6bnVtYmVyKT0+e1xyXG4gICAgICAgIHN1YnNjcmliZXIubmV4dChmaWxsZWQpO1xyXG4gICAgICAgIHN1YnNjcmliZXIuY29tcGxldGUoKTtcclxuICAgICAgfSxlcnIgPT4ge1xyXG4gICAgICAgIHN1YnNjcmliZXIuZXJyb3IoZXJyKTtcclxuICAgICAgfSlcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgY2xvc2UoKTogdm9pZCB7XHJcbiAgICB0aGlzLl9jdXJyQ2k9MDtcclxuICAgIHRoaXMuX2NjQ2FjaGU9bnVsbDtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBjbGFzcyBJbmRleGVkRGJBdWRpb0lucHV0U3RyZWFtIGltcGxlbWVudHMgQXN5bmNGbG9hdDMyQXJyYXlJbnB1dFN0cmVhbXtcclxuXHJcbiAgcHJpdmF0ZSBmcmFtZVBvczpudW1iZXI9MDtcclxuICBwcml2YXRlIGluZGRiQWJTdHI6SW5kZXhlZERiUmFuZG9tQWNjZXNzU3RyZWFtO1xyXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgaW5kZGJBYjpJbmRleGVkRGJBdWRpb0J1ZmZlcikge1xyXG4gICAgICB0aGlzLmluZGRiQWJTdHI9bmV3IEluZGV4ZWREYlJhbmRvbUFjY2Vzc1N0cmVhbShpbmRkYkFiKTtcclxuICB9XHJcblxyXG4gIGNsb3NlKCk6IHZvaWQge1xyXG4gICAgdGhpcy5pbmRkYkFiU3RyLmNsb3NlKCk7XHJcbiAgfVxyXG5cclxuICByZWFkT2JzKGJ1ZmZlcnM6IEFycmF5PEZsb2F0MzJBcnJheT4pOiBPYnNlcnZhYmxlPG51bWJlcj4ge1xyXG4gICAgcmV0dXJuIG5ldyBPYnNlcnZhYmxlPG51bWJlcj4oc3Vic2NyPT4ge1xyXG4gICAgICBpZiAoYnVmZmVycyAmJiBidWZmZXJzLmxlbmd0aCA+IDApIHtcclxuICAgICAgICBsZXQgZmwgPSBidWZmZXJzWzBdLmxlbmd0aDtcclxuICAgICAgICB0aGlzLmluZGRiQWJTdHIuZnJhbWVzT2JzKHRoaXMuZnJhbWVQb3MsIGZsLCBidWZmZXJzKS5zdWJzY3JpYmUoe1xyXG4gICAgICAgICAgbmV4dDogKHJlYWQpID0+IHtcclxuICAgICAgICAgICAgdGhpcy5mcmFtZVBvcyArPSByZWFkO1xyXG4gICAgICAgICAgICBzdWJzY3IubmV4dChyZWFkKTtcclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICBjb21wbGV0ZTogKCkgPT4ge1xyXG4gICAgICAgICAgICBzdWJzY3IuY29tcGxldGUoKTtcclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICBlcnJvcjogKGVycikgPT4ge1xyXG4gICAgICAgICAgICBzdWJzY3IuZXJyb3IoZXJyKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KVxyXG4gICAgICB9ZWxzZXtcclxuICAgICAgICBzdWJzY3IubmV4dCgwKTtcclxuICAgICAgICBzdWJzY3IuY29tcGxldGUoKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICBza2lwRnJhbWVzKG46IG51bWJlcik6dm9pZCB7XHJcbiAgICB0aGlzLmZyYW1lUG9zKz1uO1xyXG4gIH1cclxuXHJcbn1cclxuIl19
|