speechrecorderng 3.6.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/lib/action/action.mjs +73 -0
- package/{esm2020 → esm2022}/lib/audio/array_audio_buffer.mjs +163 -163
- package/{esm2020 → esm2022}/lib/audio/array_audio_buffer_input_stream.mjs +85 -85
- package/{esm2020 → esm2022}/lib/audio/array_audio_buffer_random_access_stream.mjs +15 -15
- package/esm2022/lib/audio/audio_data_holder.mjs +264 -0
- package/{esm2020 → esm2022}/lib/audio/audio_display.mjs +93 -93
- package/{esm2020 → esm2022}/lib/audio/audio_player.mjs +213 -213
- package/esm2022/lib/audio/capture/capture.mjs +855 -0
- package/esm2022/lib/audio/context.mjs +79 -0
- package/{esm2020 → esm2022}/lib/audio/dsp/level_measure.mjs +515 -515
- package/{esm2020 → esm2022}/lib/audio/format.mjs +16 -16
- package/esm2022/lib/audio/impl/wavformat.mjs +6 -0
- package/{esm2020 → esm2022}/lib/audio/impl/wavreader.mjs +133 -133
- package/esm2022/lib/audio/impl/wavwriter.mjs +105 -0
- package/esm2022/lib/audio/inddb_audio_buffer.mjs +508 -0
- package/{esm2020 → esm2022}/lib/audio/io/stream.mjs +58 -58
- package/esm2022/lib/audio/net_audio_buffer.mjs +293 -0
- package/{esm2020 → esm2022}/lib/audio/persistor.mjs +80 -80
- package/{esm2020 → esm2022}/lib/audio/playback/array_audio_buffer_source_node.mjs +125 -125
- package/esm2022/lib/audio/playback/audio_source_node.mjs +18 -0
- package/esm2022/lib/audio/playback/audio_source_worklet_module_loader.mjs +167 -0
- package/{esm2020 → esm2022}/lib/audio/playback/inddb_audio_buffer_source_node.mjs +166 -166
- package/{esm2020 → esm2022}/lib/audio/playback/net_audio_buffer_source_node.mjs +217 -217
- package/esm2022/lib/audio/playback/player.mjs +402 -0
- package/esm2022/lib/audio/ui/audio_canvas_layer_comp.mjs +347 -0
- package/{esm2020 → esm2022}/lib/audio/ui/audio_display_control.mjs +65 -65
- package/{esm2020 → esm2022}/lib/audio/ui/audio_display_scroll_pane.mjs +139 -139
- package/{esm2020 → esm2022}/lib/audio/ui/audiosignal.mjs +524 -524
- package/{esm2020 → esm2022}/lib/audio/ui/common.mjs +18 -18
- package/esm2022/lib/audio/ui/container.mjs +414 -0
- package/{esm2020 → esm2022}/lib/audio/ui/livelevel.mjs +352 -352
- package/esm2022/lib/audio/ui/scroll_pane_horizontal.mjs +11 -0
- package/esm2022/lib/audio/ui/sonagram.mjs +900 -0
- package/esm2022/lib/db/inddb.mjs +120 -0
- package/esm2022/lib/dsp/utils.mjs +48 -0
- package/{esm2020 → esm2022}/lib/io/BinaryReader.mjs +84 -84
- package/esm2022/lib/io/BinaryWriter.mjs +74 -0
- package/{esm2020 → esm2022}/lib/io/stream.mjs +258 -258
- package/{esm2020 → esm2022}/lib/math/2d/geometry.mjs +27 -27
- package/esm2022/lib/math/complex.mjs +58 -0
- package/{esm2020 → esm2022}/lib/math/dft.mjs +195 -195
- package/{esm2020 → esm2022}/lib/media/utils.mjs +13 -13
- package/esm2022/lib/net/uploader.mjs +367 -0
- package/{esm2020 → esm2022}/lib/recorder_component.mjs +64 -64
- package/esm2022/lib/speechrecorder/project/project.mjs +49 -0
- package/esm2022/lib/speechrecorder/project/project.service.mjs +64 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/recording.mjs +123 -123
- package/esm2022/lib/speechrecorder/recordings/basic_recording.service.mjs +218 -0
- package/esm2022/lib/speechrecorder/recordings/recordings.service.mjs +1014 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/script/script.mjs +114 -114
- package/{esm2020 → esm2022}/lib/speechrecorder/script/script.service.mjs +47 -47
- package/esm2022/lib/speechrecorder/session/audiorecorder.mjs +1179 -0
- package/esm2022/lib/speechrecorder/session/basicrecorder.mjs +668 -0
- package/esm2022/lib/speechrecorder/session/controlpanel.mjs +416 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/session/item.mjs +29 -29
- package/{esm2020 → esm2022}/lib/speechrecorder/session/progress.mjs +69 -69
- package/{esm2020 → esm2022}/lib/speechrecorder/session/prompting.mjs +571 -571
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recorder_combi_pane.mjs +65 -65
- package/esm2022/lib/speechrecorder/session/recording_file_cache.mjs +195 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recording_list.mjs +103 -103
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file-meta.component.mjs +63 -63
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file-navi.component.mjs +51 -51
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file-u-i.component.mjs +104 -104
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file-view.component.mjs +381 -381
- package/{esm2020 → esm2022}/lib/speechrecorder/session/recordingfile/recording-file.mjs +67 -67
- package/esm2022/lib/speechrecorder/session/recordingfile/recordingfile-service.mjs +288 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/session/session.mjs +1 -1
- package/esm2022/lib/speechrecorder/session/session.service.mjs +69 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/session/session_finished_dialog.mjs +29 -29
- package/esm2022/lib/speechrecorder/session/sessionmanager.mjs +1386 -0
- package/{esm2020 → esm2022}/lib/speechrecorder/session/warning_bar.mjs +28 -28
- package/{esm2020 → esm2022}/lib/speechrecorder/spruploader.mjs +22 -22
- package/{esm2020 → esm2022}/lib/speechrecorder/startstopsignal/startstopsignal.mjs +1 -1
- package/esm2022/lib/speechrecorder/startstopsignal/ui/simpletrafficlight.mjs +57 -0
- package/{esm2020 → esm2022}/lib/speechrecorderng.component.mjs +388 -388
- package/{esm2020 → esm2022}/lib/speechrecorderng.module.mjs +100 -100
- package/{esm2020 → esm2022}/lib/spr.config.mjs +26 -26
- package/{esm2020 → esm2022}/lib/spr.module.version.mjs +2 -2
- package/{esm2020 → esm2022}/lib/ui/canvas_layer_comp.mjs +38 -38
- package/{esm2020 → esm2022}/lib/ui/message_dialog.mjs +30 -30
- package/esm2022/lib/ui/recordingitem_display.mjs +253 -0
- package/{esm2020 → esm2022}/lib/ui/responsive_component.mjs +24 -24
- package/{esm2020 → esm2022}/lib/utils/scrollIntoViewToBottom.mjs +23 -23
- package/esm2022/lib/utils/ua-parser.mjs +190 -0
- package/esm2022/lib/utils/utils.mjs +105 -0
- package/esm2022/lib/utils/wake_lock.mjs +114 -0
- package/{esm2020 → esm2022}/lib/utils/wake_lock_media.mjs +1 -1
- package/{esm2020 → esm2022}/public-api.mjs +33 -33
- package/{esm2020 → esm2022}/speechrecorderng.mjs +4 -4
- package/{fesm2020 → fesm2022}/speechrecorderng.mjs +16518 -16454
- package/fesm2022/speechrecorderng.mjs.map +1 -0
- package/index.d.ts +5 -5
- package/lib/action/action.d.ts +27 -27
- package/lib/audio/array_audio_buffer.d.ts +31 -31
- package/lib/audio/array_audio_buffer_input_stream.d.ts +14 -14
- package/lib/audio/array_audio_buffer_random_access_stream.d.ts +9 -9
- package/lib/audio/audio_data_holder.d.ts +84 -84
- package/lib/audio/audio_display.d.ts +36 -36
- package/lib/audio/audio_player.d.ts +47 -47
- package/lib/audio/capture/capture.d.ts +67 -67
- package/lib/audio/context.d.ts +6 -6
- package/lib/audio/dsp/level_measure.d.ts +60 -60
- package/lib/audio/format.d.ts +11 -11
- package/lib/audio/impl/wavformat.d.ts +5 -5
- package/lib/audio/impl/wavreader.d.ts +16 -16
- package/lib/audio/impl/wavwriter.d.ts +13 -13
- package/lib/audio/inddb_audio_buffer.d.ts +68 -68
- package/lib/audio/io/stream.d.ts +28 -28
- package/lib/audio/net_audio_buffer.d.ts +57 -57
- package/lib/audio/persistor.d.ts +29 -29
- package/lib/audio/playback/array_audio_buffer_source_node.d.ts +18 -18
- package/lib/audio/playback/audio_source_node.d.ts +10 -10
- package/lib/audio/playback/audio_source_worklet_module_loader.d.ts +4 -4
- package/lib/audio/playback/inddb_audio_buffer_source_node.d.ts +21 -21
- package/lib/audio/playback/net_audio_buffer_source_node.d.ts +27 -27
- package/lib/audio/playback/player.d.ts +65 -65
- package/lib/audio/ui/audio_canvas_layer_comp.d.ts +68 -68
- package/lib/audio/ui/audio_display_control.d.ts +25 -25
- package/lib/audio/ui/audio_display_scroll_pane.d.ts +26 -26
- package/lib/audio/ui/audiosignal.d.ts +30 -30
- package/lib/audio/ui/common.d.ts +11 -11
- package/lib/audio/ui/container.d.ts +63 -63
- package/lib/audio/ui/livelevel.d.ts +60 -60
- package/lib/audio/ui/scroll_pane_horizontal.d.ts +5 -5
- package/lib/audio/ui/sonagram.d.ts +37 -37
- package/lib/db/inddb.d.ts +21 -21
- package/lib/dsp/utils.d.ts +19 -19
- package/lib/io/BinaryReader.d.ts +18 -18
- package/lib/io/BinaryWriter.d.ts +15 -15
- package/lib/io/stream.d.ts +59 -59
- package/lib/math/2d/geometry.d.ts +18 -18
- package/lib/math/complex.d.ts +17 -17
- package/lib/math/dft.d.ts +22 -22
- package/lib/media/utils.d.ts +3 -3
- package/lib/net/uploader.d.ts +81 -81
- package/lib/recorder_component.d.ts +16 -16
- package/lib/speechrecorder/project/project.d.ts +59 -59
- package/lib/speechrecorder/project/project.service.d.ts +21 -21
- package/lib/speechrecorder/recording.d.ts +51 -51
- package/lib/speechrecorder/recordings/basic_recording.service.d.ts +27 -27
- package/lib/speechrecorder/recordings/recordings.service.d.ts +48 -48
- package/lib/speechrecorder/script/script.d.ts +80 -79
- package/lib/speechrecorder/script/script.service.d.ts +16 -16
- package/lib/speechrecorder/session/audiorecorder.d.ts +122 -122
- package/lib/speechrecorder/session/basicrecorder.d.ts +139 -139
- package/lib/speechrecorder/session/controlpanel.d.ts +109 -107
- package/lib/speechrecorder/session/item.d.ts +12 -12
- package/lib/speechrecorder/session/progress.d.ts +17 -17
- package/lib/speechrecorder/session/prompting.d.ts +121 -121
- package/lib/speechrecorder/session/recorder_combi_pane.d.ts +32 -32
- package/lib/speechrecorder/session/recording_file_cache.d.ts +33 -33
- package/lib/speechrecorder/session/recording_list.d.ts +24 -24
- package/lib/speechrecorder/session/recordingfile/recording-file-meta.component.d.ts +15 -15
- package/lib/speechrecorder/session/recordingfile/recording-file-navi.component.d.ts +20 -20
- package/lib/speechrecorder/session/recordingfile/recording-file-u-i.component.d.ts +31 -31
- package/lib/speechrecorder/session/recordingfile/recording-file-view.component.d.ts +55 -55
- package/lib/speechrecorder/session/recordingfile/recording-file.d.ts +7 -7
- package/lib/speechrecorder/session/recordingfile/recordingfile-service.d.ts +24 -24
- package/lib/speechrecorder/session/session.d.ts +15 -15
- package/lib/speechrecorder/session/session.service.d.ts +20 -20
- package/lib/speechrecorder/session/session_finished_dialog.d.ts +10 -10
- package/lib/speechrecorder/session/sessionmanager.d.ts +129 -123
- package/lib/speechrecorder/session/warning_bar.d.ts +8 -8
- package/lib/speechrecorder/spruploader.d.ts +11 -11
- package/lib/speechrecorder/startstopsignal/startstopsignal.d.ts +10 -10
- package/lib/speechrecorder/startstopsignal/ui/simpletrafficlight.d.ts +11 -11
- package/lib/speechrecorderng.component.d.ts +61 -61
- package/lib/speechrecorderng.module.d.ts +56 -56
- package/lib/spr.config.d.ts +17 -17
- package/lib/spr.module.version.d.ts +1 -1
- package/lib/ui/canvas_layer_comp.d.ts +13 -13
- package/lib/ui/message_dialog.d.ts +10 -10
- package/lib/ui/recordingitem_display.d.ts +84 -84
- package/lib/ui/responsive_component.d.ts +9 -9
- package/lib/utils/scrollIntoViewToBottom.d.ts +9 -9
- package/lib/utils/ua-parser.d.ts +43 -43
- package/lib/utils/utils.d.ts +19 -19
- package/lib/utils/wake_lock.d.ts +23 -23
- package/lib/utils/wake_lock_media.d.ts +1 -1
- package/package.json +17 -23
- package/public-api.d.ts +33 -33
- package/esm2020/lib/action/action.mjs +0 -73
- package/esm2020/lib/audio/audio_data_holder.mjs +0 -264
- package/esm2020/lib/audio/capture/capture.mjs +0 -855
- package/esm2020/lib/audio/context.mjs +0 -79
- package/esm2020/lib/audio/impl/wavformat.mjs +0 -6
- package/esm2020/lib/audio/impl/wavwriter.mjs +0 -105
- package/esm2020/lib/audio/inddb_audio_buffer.mjs +0 -508
- package/esm2020/lib/audio/net_audio_buffer.mjs +0 -293
- package/esm2020/lib/audio/playback/audio_source_node.mjs +0 -18
- package/esm2020/lib/audio/playback/audio_source_worklet_module_loader.mjs +0 -167
- package/esm2020/lib/audio/playback/player.mjs +0 -402
- package/esm2020/lib/audio/ui/audio_canvas_layer_comp.mjs +0 -347
- package/esm2020/lib/audio/ui/container.mjs +0 -414
- package/esm2020/lib/audio/ui/scroll_pane_horizontal.mjs +0 -11
- package/esm2020/lib/audio/ui/sonagram.mjs +0 -900
- package/esm2020/lib/db/inddb.mjs +0 -120
- package/esm2020/lib/dsp/utils.mjs +0 -48
- package/esm2020/lib/io/BinaryWriter.mjs +0 -74
- package/esm2020/lib/math/complex.mjs +0 -58
- package/esm2020/lib/net/uploader.mjs +0 -367
- package/esm2020/lib/speechrecorder/project/project.mjs +0 -49
- package/esm2020/lib/speechrecorder/project/project.service.mjs +0 -64
- package/esm2020/lib/speechrecorder/recordings/basic_recording.service.mjs +0 -216
- package/esm2020/lib/speechrecorder/recordings/recordings.service.mjs +0 -1014
- package/esm2020/lib/speechrecorder/session/audiorecorder.mjs +0 -1179
- package/esm2020/lib/speechrecorder/session/basicrecorder.mjs +0 -666
- package/esm2020/lib/speechrecorder/session/controlpanel.mjs +0 -409
- package/esm2020/lib/speechrecorder/session/recording_file_cache.mjs +0 -195
- package/esm2020/lib/speechrecorder/session/recordingfile/recordingfile-service.mjs +0 -288
- package/esm2020/lib/speechrecorder/session/session.service.mjs +0 -69
- package/esm2020/lib/speechrecorder/session/sessionmanager.mjs +0 -1333
- package/esm2020/lib/speechrecorder/startstopsignal/ui/simpletrafficlight.mjs +0 -57
- package/esm2020/lib/ui/recordingitem_display.mjs +0 -253
- package/esm2020/lib/utils/ua-parser.mjs +0 -190
- package/esm2020/lib/utils/utils.mjs +0 -105
- package/esm2020/lib/utils/wake_lock.mjs +0 -114
- package/fesm2015/speechrecorderng.mjs +0 -17708
- package/fesm2015/speechrecorderng.mjs.map +0 -1
- package/fesm2020/speechrecorderng.mjs.map +0 -1
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
import { Observable } from "rxjs";
|
|
2
|
+
import { UUID } from "../utils/utils";
|
|
3
|
+
import { BasicAudioSource } from "./audio_data_holder";
|
|
4
|
+
export class PersistentAudioStorageTarget {
|
|
5
|
+
get indexedDb() {
|
|
6
|
+
return this._indexedDb;
|
|
7
|
+
}
|
|
8
|
+
get storeName() {
|
|
9
|
+
return this._storeName;
|
|
10
|
+
}
|
|
11
|
+
constructor(_indexedDb, _storeName) {
|
|
12
|
+
this._indexedDb = _indexedDb;
|
|
13
|
+
this._storeName = _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
|
+
get chunkFrameLen() {
|
|
46
|
+
return this._chunkFrameLen;
|
|
47
|
+
}
|
|
48
|
+
get persistentAudioStorageTarget() {
|
|
49
|
+
return this._persistentAudioStorageTarget;
|
|
50
|
+
}
|
|
51
|
+
get uuid() {
|
|
52
|
+
return this._uuid;
|
|
53
|
+
}
|
|
54
|
+
constructor(_persistentAudioStorageTarget, _channelCount, _sampleRate, _chunkFrameLen, _frameLen, _uuid) {
|
|
55
|
+
super();
|
|
56
|
+
this._persistentAudioStorageTarget = _persistentAudioStorageTarget;
|
|
57
|
+
this._channelCount = _channelCount;
|
|
58
|
+
this._sampleRate = _sampleRate;
|
|
59
|
+
this._chunkFrameLen = _chunkFrameLen;
|
|
60
|
+
this._frameLen = _frameLen;
|
|
61
|
+
this._uuid = _uuid;
|
|
62
|
+
this._chunkCount = 0;
|
|
63
|
+
this.indDbChkIdx = 0;
|
|
64
|
+
this._sealed = false;
|
|
65
|
+
this._storeError = null;
|
|
66
|
+
this.ready();
|
|
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZGJfYXVkaW9fYnVmZmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvc3BlZWNocmVjb3JkZXJuZy9zcmMvbGliL2F1ZGlvL2luZGRiX2F1ZGlvX2J1ZmZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUMsVUFBVSxFQUFDLE1BQU0sTUFBTSxDQUFDO0FBRWhDLE9BQU8sRUFBQyxJQUFJLEVBQUMsTUFBTSxnQkFBZ0IsQ0FBQztBQUNwQyxPQUFPLEVBQWMsZ0JBQWdCLEVBQTBCLE1BQU0scUJBQXFCLENBQUM7QUFFM0YsTUFBTSxPQUFPLDRCQUE0QjtJQUN2QyxJQUFJLFNBQVM7UUFDWCxPQUFPLElBQUksQ0FBQyxVQUFVLENBQUM7SUFDekIsQ0FBQztJQUVELElBQUksU0FBUztRQUNYLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQztJQUN6QixDQUFDO0lBQ0QsWUFBb0IsVUFBc0IsRUFBUyxVQUFpQjtRQUFoRCxlQUFVLEdBQVYsVUFBVSxDQUFZO1FBQVMsZUFBVSxHQUFWLFVBQVUsQ0FBTztJQUNwRSxDQUFDO0lBRUQsV0FBVyxDQUFDLElBQXdCO1FBQ2xDLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUMzRCxDQUFDO0lBRUQsV0FBVyxDQUFDLElBQXdCO1FBQ2xDLElBQUksRUFBRSxHQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDOUIsT0FBTyxFQUFFLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBRUQsU0FBUztRQUNQLE9BQU8sSUFBSSxVQUFVLENBQU8sVUFBVSxDQUFDLEVBQUU7WUFDdkMsSUFBSSxFQUFFLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUN2QyxJQUFJLFFB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QUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDO2dCQUMzQixJQUFJLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLEVBQUUsRUFBRSxPQUFPLENBQUMsQ0FBQyxTQUFTLENBQUM7b0JBQzlELElBQUksRUFBRSxDQUFDLElBQUksRUFBRSxFQUFFO3dCQUNiLElBQUksQ0FBQyxRQUFRLElBQUksSUFBSSxDQUFDO3dCQUN0QixNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO29CQUNwQixDQUFDO29CQUNELFFBQVEsRUFBRSxHQUFHLEVBQUU7d0JBQ2IsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFDO29CQUNwQixDQUFDO29CQUNELEtBQUssRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFO3dCQUNiLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7b0JBQ3BCLENBQUM7aUJBQ0YsQ0FBQyxDQUFBO2FBQ0g7aUJBQUk7Z0JBQ0gsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDZixNQUFNLENBQUMsUUFBUSxFQUFFLENBQUM7YUFDbkI7UUFDSCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxVQUFVLENBQUMsQ0FBUztRQUNsQixJQUFJLENBQUMsUUFBUSxJQUFFLENBQUMsQ0FBQztJQUNuQixDQUFDO0NBRUYiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge09ic2VydmFibGV9IGZyb20gXCJyeGpzXCI7XHJcbmltcG9ydCB7QXN5bmNGbG9hdDMyQXJyYXlJbnB1dFN0cmVhbSwgRmxvYXQzMkFycmF5SW5wdXRTdHJlYW19IGZyb20gXCIuLi9pby9zdHJlYW1cIjtcclxuaW1wb3J0IHtVVUlEfSBmcm9tIFwiLi4vdXRpbHMvdXRpbHNcIjtcclxuaW1wb3J0IHtBdWRpb1NvdXJjZSwgQmFzaWNBdWRpb1NvdXJjZSwgUmFuZG9tQWNjZXNzQXVkaW9TdHJlYW19IGZyb20gXCIuL2F1ZGlvX2RhdGFfaG9sZGVyXCI7XHJcblxyXG5leHBvcnQgY2xhc3MgUGVyc2lzdGVudEF1ZGlvU3RvcmFnZVRhcmdldHtcclxuICBnZXQgaW5kZXhlZERiKCk6IElEQkRhdGFiYXNlIHtcclxuICAgIHJldHVybiB0aGlzLl9pbmRleGVkRGI7XHJcbiAgfVxyXG5cclxuICBnZXQgc3RvcmVOYW1lKCk6IHN0cmluZyB7XHJcbiAgICByZXR1cm4gdGhpcy5fc3RvcmVOYW1lO1xyXG4gIH1cclxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIF9pbmRleGVkRGI6SURCRGF0YWJhc2UscHJpdmF0ZSBfc3RvcmVOYW1lOnN0cmluZykge1xyXG4gIH1cclxuXHJcbiAgdHJhbnNhY3Rpb24obW9kZT86SURCVHJhbnNhY3Rpb25Nb2RlKTpJREJUcmFuc2FjdGlvbntcclxuICAgIHJldHVybiB0aGlzLl9pbmRleGVkRGIudHJhbnNhY3Rpb24odGhpcy5zdG9yZU5hbWUsIG1vZGUpO1xyXG4gIH1cclxuXHJcbiAgb2JqZWN0U3RvcmUobW9kZT86SURCVHJhbnNhY3Rpb25Nb2RlICl7XHJcbiAgICBsZXQgdHI9dGhpcy50cmFuc2FjdGlvbihtb2RlKTtcclxuICAgIHJldHVybiB0ci5vYmplY3RTdG9yZSh0aGlzLl9zdG9yZU5hbWUpO1xyXG4gIH1cclxuXHJcbiAgZGVsZXRlQWxsKCk6T2JzZXJ2YWJsZTx2b2lkPntcclxuICAgIHJldHVybiBuZXcgT2JzZXJ2YWJsZTx2b2lkPihzdWJzY3JpYmVyID0+IHtcclxuICAgICAgbGV0IG9zID0gdGhpcy5vYmplY3RTdG9yZSgncmVhZHdyaXRlJyk7XHJcbiAgICAgIGxldCBjbGVhclJlcT1vcy5jbGVhcigpO1xyXG4gICAgICBjbGVhclJlcS5vbnN1Y2Nlc3M9KCk9PntcclxuICAgICAgICBjb25zb2xlLmRlYnVnKFwiQ2xlYXJlZCBhdWRpbyBzdG9yYWdlIG9iamVjdCBzdG9yZS5cIik7XHJcbiAgICAgICAgb3MudHJhbnNhY3Rpb24uY29tbWl0KCk7XHJcbiAgICAgIH1cclxuICAgICAgY2xlYXJSZXEub25lcnJvcj0oKT0+e1xyXG4gICAgICAgIG9zLnRyYW5zYWN0aW9uLmFib3J0KCk7XHJcbiAgICAgICAgY29uc29sZS5lcnJvcihcIkNvdWxkIG5vdCBjbGVhciBvYmplY3Qgc3RvcmU6IFwiK2NsZWFyUmVxLmVycm9yKTtcclxuICAgICAgfVxyXG4gICAgICBvcy50cmFuc2FjdGlvbi5vbmNvbXBsZXRlID0gKCkgPT4ge1xyXG4gICAgICAgIGNvbnNvbGUuZGVidWcoXCJUcmFuc2FjdGlvbiBjb21wbGV0ZTogQ2xlYXIgYXVkaW8gc3RvcmFnZSBvYmplY3Qgc3RvcmUuXCIpO1xyXG4gICAgICAgICAgc3Vic2NyaWJlci5jb21wbGV0ZSgpO1xyXG4gICAgICB9XHJcbiAgICAgIG9zLnRyYW5zYWN0aW9uLm9uZXJyb3IgPSAoZXJyKSA9PiB7XHJcbiAgICAgICAgICBzdWJzY3JpYmVyLmVycm9yKGVycik7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbn1cclxuXHJcbmV4cG9ydCBjbGFzcyBJbmRleGVkRGJBdWRpb0J1ZmZlciBleHRlbmRzIEJhc2ljQXVkaW9Tb3VyY2UgaW1wbGVtZW50cyBBdWRpb1NvdXJjZXtcclxuICBnZXQgY2h1bmtGcmFtZUxlbigpOiBudW1iZXIge1xyXG4gICAgcmV0dXJuIHRoaXMuX2NodW5rRnJhbWVMZW47XHJcbiAgfVxyXG4gIGdldCBwZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0KCk6IFBlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQge1xyXG4gICAgcmV0dXJuIHRoaXMuX3BlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQ7XHJcbiAgfVxyXG4gIGdldCB1dWlkKCk6IHN0cmluZyB7XHJcbiAgICByZXR1cm4gdGhpcy5fdXVpZDtcclxuICB9XHJcblxyXG5cclxuICBwcml2YXRlIF9jaHVua0NvdW50PTA7XHJcbiAgcHJpdmF0ZSBpbmREYkNoa0lkeDogbnVtYmVyPTA7XHJcbiAgcHJpdmF0ZSBfc2VhbGVkPWZhbHNlO1xyXG4gIHByaXZhdGUgX3N0b3JlRXJyb3I6RXJyb3J8bnVsbD1udWxsO1xyXG5cclxuXHJcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBfcGVyc2lzdGVudEF1ZGlvU3RvcmFnZVRhcmdldDpQZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0LHByaXZhdGUgX2NoYW5uZWxDb3VudDogbnVtYmVyLCBwcml2YXRlIF9zYW1wbGVSYXRlOiBudW1iZXIscHJpdmF0ZSBfY2h1bmtGcmFtZUxlbjpudW1iZXIscHJpdmF0ZSBfZnJhbWVMZW46bnVtYmVyLCBwcml2YXRlIF91dWlkOnN0cmluZykge1xyXG4gICAgc3VwZXIoKTtcclxuICAgIHRoaXMucmVhZHkoKTtcclxuICB9XHJcblxyXG4gIGdldCBjaGFubmVsQ291bnQoKTogbnVtYmVyIHtcclxuICAgIHJldHVybiB0aGlzLl9jaGFubmVsQ291bnQ7XHJcbiAgfVxyXG5cclxuICBzZWFsZWQoKTogYm9vbGVhbiB7XHJcbiAgICByZXR1cm4gdGhpcy5fc2VhbGVkO1xyXG4gIH1cclxuXHJcbiAgc2VhbCgpOnZvaWR7XHJcbiAgICB0aGlzLl9zZWFsZWQ9dHJ1ZTtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgZGVsZXRlQ2h1bmsob3M6SURCT2JqZWN0U3RvcmUsY2k6bnVtYmVyLGNiOihrZXlzOklEQlZhbGlkS2V5W10pPT52b2lkLGVyckNiOihlcnI6RXJyb3IpPT52b2lkKXtcclxuXHJcbiAgICAvLyBCdWlsZCBib3VuZHMgZm9yIGNoYW5uZWxzXHJcbiAgICBsZXQgbElkeD1bdGhpcy5fdXVpZCxjaSwwXTtcclxuICAgIGxldCBoSWR4PVt0aGlzLl91dWlkLGNpLHRoaXMuX2NoYW5uZWxDb3VudF07XHJcbiAgICBsZXQgY2hzS3I9SURCS2V5UmFuZ2UuYm91bmQobElkeCxoSWR4KTtcclxuXHJcbiAgICBsZXQgZXhScT1vcy5nZXRBbGxLZXlzKGNoc0tyKTtcclxuXHJcbiAgICBleFJxLm9uc3VjY2Vzcz0oKT0+e1xyXG4gICAgICBsZXQgY2M9ZXhScS5yZXN1bHQ7XHJcbiAgICAgIGlmKGNjICYmIGNjLmxlbmd0aD4wKSB7XHJcbiAgICAgICAgbGV0IHJxID0gb3MuZGVsZXRlKGNoc0tyKTtcclxuICAgICAgICBycS5vbmVycm9yPShlcnJFdik9PntcclxuICAgICAgICAgIGVyckNiKG5ldyBFcnJvcihlcnJFdi50b1N0cmluZygpKSk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIG9zLnRyYW5zYWN0aW9uLm9uY29tcGxldGU9KCk9PntcclxuICAgICAgICAgIGNiKGNjKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgb3MudHJhbnNhY3Rpb24uY29tbWl0KCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIGV4UnEub25lcnJvcj0oZXJyRXYpPT57XHJcbiAgICAgIGVyckNiKG5ldyBFcnJvcihlcnJFdi50b1N0cmluZygpKSk7XHJcbiAgICB9XHJcblxyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBkZWxldGVBbGxCdWZzKG9zOklEQk9iamVjdFN0b3JlLGNpOm51bWJlcixjYkVuZDooKT0+dm9pZCxjYkVycjooZXJyOkVycm9yKT0+dm9pZCl7XHJcbiAgICAvL2NvbnNvbGUuZGVidWcoJ0luZGV4ZWREYkF1ZGlvQnVmZmVyOjpmaWxsQnVmczogZnJhbWVQb3M6JytmcmFtZVBvcysnLCBmcmFtZUxlbjogJytmcmFtZUxlbisnLCBmaWxsZWQ6ICcrZmlsbGVkKycsIHNyY0ZyYW1lUG9zOiAnK3NyY0ZyYW1lUG9zKycsY2k6ICcrY2krJywgY2NQb3M6ICcrY2NQb3MpO1xyXG4gICAgdGhpcy5kZWxldGVDaHVuayhvcyxjaSwoY2NLZXlzKT0+e1xyXG4gICAgICBpZihjY0tleXMgJiYgY2NLZXlzLmxlbmd0aD4wKXtcclxuICAgICAgICAvL2NvbnNvbGUuZGVidWcoJ0luZGV4ZWREYkF1ZGlvQnVmZmVyOjpmaWxsQnVmcyBmcmFtZUxlbjogJytmcmFtZUxlbik7XHJcbiAgICAgICAgY2krKztcclxuICAgICAgICB0aGlzLmRlbGV0ZUFsbEJ1ZnMob3MsY2ksY2JFbmQsIGNiRXJyKTtcclxuICAgICAgfWVsc2V7XHJcbiAgICAgICAgLy9jb25zb2xlLmRlYnVnKCdJbmRleGVkRGJBdWRpb0J1ZmZlcjo6ZmlsbEJ1ZnMgKGNodW5rIG5vdCBmb3VuZCkgY2FsbDogY2JlbmQgJytmaWxsZWQpO1xyXG4gICAgICAgIGNiRW5kKCk7XHJcbiAgICAgIH1cclxuICAgIH0sKGVycik9PntcclxuICAgICAgY2JFcnIoZXJyKTtcclxuICAgIH0pO1xyXG5cclxuICB9XHJcblxyXG4gIGdldCBzYW1wbGVSYXRlKCk6IG51bWJlciB7XHJcbiAgICByZXR1cm4gdGhpcy5fc2FtcGxlUmF0ZTtcclxuICB9XHJcblxyXG4gIGdldCBmcmFtZUxlbigpOiBudW1iZXIge1xyXG4gICAgcmV0dXJuIHRoaXMuX2ZyYW1lTGVuO1xyXG4gIH1cclxuXHJcbiAgZ2V0IGNodW5rQ291bnQoKTogbnVtYmVyIHtcclxuICAgIHJldHVybiB0aGlzLl9jaHVua0NvdW50O1xyXG4gIH1cclxuXHJcbiAgYXBwZW5kUmF3QXVkaW9EYXRhKGRhdGE6QXJyYXk8QXJyYXk8RmxvYXQzMkFycmF5Pj4pOk9ic2VydmFibGU8dm9pZD57XHJcblxyXG4gIHJldHVybiBuZXcgT2JzZXJ2YWJsZTx2b2lkPihzdWJzY3JpYmVyID0+IHtcclxuXHJcbiAgICBpZiAodGhpcy5fcGVyc2lzdGVudEF1ZGlvU3RvcmFnZVRhcmdldCAmJiB0aGlzLl91dWlkKSB7XHJcbiAgICAgIGxldCB0ciA9IHRoaXMuX3BlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQuaW5kZXhlZERiLnRyYW5zYWN0aW9uKHRoaXMuX3BlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQuc3RvcmVOYW1lLCAncmVhZHdyaXRlJyk7XHJcbiAgICAgIGxldCByZWNGaWxlT2JqU3RvcmUgPSB0ci5vYmplY3RTdG9yZSh0aGlzLl9wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0LnN0b3JlTmFtZSk7XHJcblxyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGxldCBjaDBEYXRhID0gZGF0YVswXTtcclxuICAgICAgICBsZXQgZGF0YUNoa0NudCA9IGNoMERhdGEubGVuZ3RoO1xyXG4gICAgICAgIGxldCBwb3MgPSAwO1xyXG4gICAgICAgIGZvciAobGV0IGNoQ2tJZHggPSAwOyBjaENrSWR4IDwgZGF0YUNoa0NudDsgY2hDa0lkeCsrKSB7XHJcbiAgICAgICAgICBsZXQgYnVmTGVuID0gMDtcclxuICAgICAgICAgIGZvciAobGV0IGNoID0gMDsgY2ggPCB0aGlzLmNoYW5uZWxDb3VudDsgY2grKykge1xyXG4gICAgICAgICAgICBsZXQgY2hDaGsgPSBkYXRhW2NoXVtjaENrSWR4XTtcclxuICAgICAgICAgICAgYnVmTGVuID0gY2hDaGsubGVuZ3RoO1xyXG4gICAgICAgICAgICAvL2xldCBjYWNoZUlkID0gdXVpZCArICdfJyArIGNoICsgJ18nICsgY2hDa0lkeDtcclxuICAgICAgICAgICAgbGV0IGNoa0RiSWQgPSBbdGhpcy5fdXVpZCwgdGhpcy5pbmREYkNoa0lkeCArIGNoQ2tJZHgsIGNoXTtcclxuICAgICAgICAgICAgbGV0IGNyID0gcmVjRmlsZU9ialN0b3JlLmFkZChjaENoaywgY2hrRGJJZCk7XHJcbiAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIkFkZGVkOiBcIitjaCtcIiBcIisodGhpcy5pbmREYkNoa0lkeCtjaENrSWR4KSk7XHJcbiAgICAgICAgICAgIGNyLm9uc3VjY2VzcyA9ICgpID0+IHtcclxuICAgICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJTdG9yZWQgYXVkaW8gZGF0YSB0byBpbmRleGVkIGRiXCIpO1xyXG4gICAgICAgICAgICAgIC8vIGlmKGNoQ2tJZHg+PSBkYXRhQ2hrQ250LTEgJiYgY2g+PXRoaXMuY2hhbm5lbENvdW50LTEpe1xyXG4gICAgICAgICAgICAgIC8vICAgdHIuY29tbWl0KCk7XHJcbiAgICAgICAgICAgICAgLy8gfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGNyLm9uZXJyb3IgPSAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgaWYoIXRoaXMuX3N0b3JlRXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIC8vIE9ubHkgbG9nIHRoZSBmaXJzdCBlcnJvclxyXG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIkVycm9yIHN0b3JpbmcgYXVkaW8gZGF0YSBvZiBcIiArIGNoQ2hrLmJ5dGVMZW5ndGggKyBcIiBieXRlcyB0byBpbmRleGVkIGRiOiBcIiArIGNyLmVycm9yKTtcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHBvcyArPSBidWZMZW47XHJcbiAgICAgICAgICB0aGlzLl9mcmFtZUxlbis9YnVmTGVuO1xyXG4gICAgICAgIH1cclxuICAgICAgICB0aGlzLmluZERiQ2hrSWR4ICs9IGRhdGFDaGtDbnQ7XHJcblxyXG4gICAgICAgIHRyLm9uZXJyb3IgPSAoZXYpID0+IHtcclxuICAgICAgICAgIGlmKCF0aGlzLl9zdG9yZUVycm9yKSB7XHJcbiAgICAgICAgICAgIC8vIE9ubHkgbG9nIHRoZSBmaXJzdCBlcnJvclxyXG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiRmFpbGVkIHRvIHN0b3JlIGF1ZGlvIGRhdGEgdG8gaW5kZXhlZCBkYiwgdHJhbnNhY3Rpb24gZXJyb3I6IFwiK3RyLmVycm9yKTtcclxuICAgICAgICAgICAgdGhpcy5fc3RvcmVFcnJvciA9IHRyLmVycm9yO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgc3Vic2NyaWJlci5lcnJvcih0ci5lcnJvcik7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRyLm9uYWJvcnQ9KGV2KT0+e1xyXG4gICAgICAgICAgLy8gSWYgQ2hyb21lIHJlYWNoZXMgcXVvdGEgaXQgYWJvcnRzIHRoZSB0cmFuc2FjdGlvblxyXG4gICAgICAgICAgaWYoIXRoaXMuX3N0b3JlRXJyb3IpIHtcclxuICAgICAgICAgICAgLy8gT25seSBsb2cgdGhlIGZpcnN0IGVycm9yXHJcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJGYWlsZWQgdG8gc3RvcmUgYXVkaW8gZGF0YSB0byBpbmRleGVkIGRiLCB0cmFuc2FjdGlvbiBhYm9ydGVkOiBcIit0ci5lcnJvcik7XHJcbiAgICAgICAgICAgIHRoaXMuX3N0b3JlRXJyb3IgPSB0ci5lcnJvcjtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHN1YnNjcmliZXIuZXJyb3IodHIuZXJyb3IpO1xyXG4gICAgICAgIH1cclxuICAgICAgICB0ci5vbmNvbXBsZXRlID0gKGV2KSA9PiB7XHJcbiAgICAgICAgICBzdWJzY3JpYmVyLmNvbXBsZXRlKCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRyLmNvbW1pdCgpO1xyXG4gICAgICB9IGNhdGNoIChlcnIpIHtcclxuICAgICAgICBzdWJzY3JpYmVyLmVycm9yKG5ldyBFcnJvcignVHJhbnNmZXIgYXVkaW8gZGF0YSBlcnJvcjogJyArIGVycikpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcbiAgfVxyXG5cclxuICBzdGF0aWMgZnJvbUNodW5rQXVkaW9CdWZmZXIocGVyc2lzdGVudEF1ZGlvU3RvcmFnZVRhcmdldDpQZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0ICxhdWRpb0J1ZmZlcjpBdWRpb0J1ZmZlcik6T2JzZXJ2YWJsZTxJbmRleGVkRGJBdWRpb0J1ZmZlcj57XHJcblxyXG4gICAgcmV0dXJuIG5ldyBPYnNlcnZhYmxlPEluZGV4ZWREYkF1ZGlvQnVmZmVyPihzdWJzY3JpYmVyID0+IHtcclxuICAgICAgbGV0IGNocyA9IGF1ZGlvQnVmZmVyLm51bWJlck9mQ2hhbm5lbHM7XHJcbiAgICAgIGxldCBzciA9IGF1ZGlvQnVmZmVyLnNhbXBsZVJhdGU7XHJcbiAgICAgIGxldCBjaGtGcmFtZUxlbmd0aCA9IGF1ZGlvQnVmZmVyLmxlbmd0aDtcclxuXHJcbiAgICAgIGxldCBpYWIgPSBuZXcgSW5kZXhlZERiQXVkaW9CdWZmZXIocGVyc2lzdGVudEF1ZGlvU3RvcmFnZVRhcmdldCwgY2hzLCBzciwgY2hrRnJhbWVMZW5ndGgsIDAsIFVVSUQuZ2VuZXJhdGUoKSk7XHJcbiAgICAgIGlhYi5hcHBlbmRDaHVua0F1ZGlvQnVmZmVyKGF1ZGlvQnVmZmVyKS5zdWJzY3JpYmUoXHJcbiAgICAgICAge1xyXG4gICAgICAgICAgbmV4dDogKCk9PntcclxuICAgICAgICAgICAgc3Vic2NyaWJlci5uZXh0KGlhYik7XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgY29tcGxldGU6ICgpID0+IHtcclxuICAgICAgICAgICAgc3Vic2NyaWJlci5jb21wbGV0ZSgpO1xyXG4gICAgICAgICAgfSxcclxuICAgICAgICAgIGVycm9yOiAoZXJyKT0+e1xyXG4gICAgICAgICAgICBzdWJzY3JpYmVyLmVycm9yKGVycik7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICApXHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIGFwcGVuZENodW5rQXVkaW9CdWZmZXIoYXVkaW9CdWZmZXI6QXVkaW9CdWZmZXIpe1xyXG4gICAgaWYodGhpcy5fc2VhbGVkKXtcclxuICAgICAgdGhyb3cgbmV3IEVycm9yKCdDYW5ub3QgYXBwZW5kIGF1ZGlvIGJ1ZmZlciB0byBhbHJlYWR5IHNlYWxlZCBhdWRpbyBidWZmZXIuJyk7XHJcbiAgICB9XHJcbiAgICBsZXQgY2hzPWF1ZGlvQnVmZmVyLm51bWJlck9mQ2hhbm5lbHM7XHJcbiAgICBpZih0aGlzLl9jaGFubmVsQ291bnQhPT0gY2hzKXtcclxuICAgICAgdGhyb3cgbmV3IEVycm9yKCdDYW5ub3QgYXBwZW5kIGF1ZGlvIGJ1ZmZlciB3aXRoICcrY2hzKycgY2hhbm5lbHMgdG8gdGhpcyBhcnJheSBhdWRpbyBidWZmZXIgd2l0aCAnK3RoaXMuX2NoYW5uZWxDb3VudCsnIGNoYW5uZWxzLiBOdW1iZXIgb2YgY2hhbm5lbHMgbXVzdCBtYXRjaC4nKTtcclxuICAgIH1cclxuICAgIGxldCBzcj1hdWRpb0J1ZmZlci5zYW1wbGVSYXRlO1xyXG4gICAgaWYoc3IhPT10aGlzLl9zYW1wbGVSYXRlKXtcclxuICAgICAgdGhyb3cgbmV3IEVycm9yKCdDYW5ub3QgYXBwZW5kIGF1ZGlvIGJ1ZmZlciB3aXRoIHNhbXBsZXJhdGUgJytzcisnIHRvIHRoaXMgYXJyYXkgYXVkaW8gYnVmZmVyIHdpdGggc2FtcGxlcmF0ZSAnK3RoaXMuX3NhbXBsZVJhdGUrJy4gU2FtcGxlcmF0ZXMgbXVzdCBtYXRjaC4nKTtcclxuICAgIH1cclxuXHJcbiAgICBsZXQgYWJGbD1hdWRpb0J1ZmZlci5sZW5ndGg7XHJcbiAgICBpZihhYkZsPnRoaXMuX2NodW5rRnJhbWVMZW4pe1xyXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0Nhbm5vdCBhcHBlbmQgYXVkaW8gYnVmZmVyIHdpdGggZnJhbWUgbGVuZ3RoICcrYWJGbCsnIHRvIHRoaXMgYXJyYXkgYXVkaW8gYnVmZmVyIHdpdGggY2h1bmsgZnJhbWUgbGVuZ3RoICcrdGhpcy5fY2h1bmtGcmFtZUxlbisnLiBDaHVuayBsZW5ndGggbXVzdCBiZSBlcXVhbCBvciBsZXNzIGlmIGxhc3QgY2h1bmsuJyk7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gbmV3IE9ic2VydmFibGU8dm9pZD4oc3Vic2NyaWJlciA9PiB7XHJcbiAgICAgIGlmICh0aGlzLl9wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0ICYmIHRoaXMuX3V1aWQpIHtcclxuICAgICAgICBsZXQgdHIgPSB0aGlzLl9wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0LnRyYW5zYWN0aW9uKCdyZWFkd3JpdGUnKTtcclxuICAgICAgICBsZXQgcmVjRmlsZU9ialN0b3JlID0gdHIub2JqZWN0U3RvcmUodGhpcy5fcGVyc2lzdGVudEF1ZGlvU3RvcmFnZVRhcmdldC5zdG9yZU5hbWUpO1xyXG4gICAgICAgIHRyeSB7XHJcblxyXG4gICAgICAgICAgICBmb3IgKGxldCBjaCA9IDA7IGNoIDwgdGhpcy5jaGFubmVsQ291bnQ7IGNoKyspIHtcclxuICAgICAgICAgICAgICBsZXQgY2hDaGsgPSBhdWRpb0J1ZmZlci5nZXRDaGFubmVsRGF0YShjaCk7XHJcbiAgICAgICAgICAgICAgbGV0IGNoa0RiSWQgPSBbdGhpcy5fdXVpZCwgdGhpcy5pbmREYkNoa0lkeCsrLCBjaF07XHJcbiAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGxldCBjciA9IHJlY0ZpbGVPYmpTdG9yZS5hZGQoY2hDaGssIGNoa0RiSWQpO1xyXG4gICAgICAgICAgICAgICAgY3Iub25zdWNjZXNzID0gKGV2KSA9PiB7fVxyXG4gICAgICAgICAgICAgICAgY3Iub25lcnJvciA9IChldikgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAvLyBpUGFkIG1heSB0aHJvdyBRdW90YUV4Y2VlZGVkRXJyb3IgaGVyZVxyXG4gICAgICAgICAgICAgICAgICAvLyBpUGFkIGFza3MgZm9yIG1vcmUgc3RvcmFnZSBhbmQgaWYgZGVuaWVkLCB0aGUgZXJyb3IgaXMgdGhyb3duIGhlcmVcclxuICAgICAgICAgICAgICAgICAgaWYoIXRoaXMuX3N0b3JlRXJyb3IpIHtcclxuICAgICAgICAgICAgICAgICAgICAvLyBPbmx5IGxvZyB0aGUgZmlyc3QgZXJyb3JcclxuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiRXJyb3Igc3RvcmluZyBhdWRpbyBkYXRhIG9mIFwiICsgY2hDaGsuYnl0ZUxlbmd0aCArIFwiIGJ5dGVzIHRvIGluZGV4ZWQgZGI6IFwiICsgY3IuZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgIC8vc3Vic2NyaWJlci5lcnJvcihjci5lcnJvcik7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgfWNhdGNoKGVycjE6YW55KXtcclxuICAgICAgICAgICAgICAgIGlmKCF0aGlzLl9zdG9yZUVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJFcnJvciBhZGRpbmcgYXVkaW8gZGF0YSB0byBpbmRleGVkIGRiIHN0b3JlOiBcIiArIGVycjEpO1xyXG4gICAgICAgICAgICAgICAgICB0aGlzLl9zdG9yZUVycm9yPShlcnIxIGluc3RhbmNlb2YgRXJyb3IpP2VycjE6bmV3IEVycm9yKGVycjEpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgc3Vic2NyaWJlci5lcnJvcihlcnIxKTtcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgdGhpcy5fZnJhbWVMZW4rPWFiRmw7XHJcblxyXG4gICAgICAgICAgdHIub25lcnJvciA9IChldikgPT4ge1xyXG4gICAgICAgICAgICBpZighdGhpcy5fc3RvcmVFcnJvcikge1xyXG4gICAgICAgICAgICAgIC8vIE9ubHkgbG9nIHRoZSBmaXJzdCBlcnJvclxyXG4gICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJGYWlsZWQgdG8gc3RvcmUgYXVkaW8gZGF0YSB0byBpbmRleGVkIGRiLCB0cmFuc2FjdGlvbiBlcnJvcjogXCIrdHIuZXJyb3IpO1xyXG4gICAgICAgICAgICAgIHRoaXMuX3N0b3JlRXJyb3IgPSB0ci5lcnJvcjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBzdWJzY3JpYmVyLmVycm9yKHRyLmVycm9yKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHRyLm9uYWJvcnQ9KGV2KT0+e1xyXG4gICAgICAgICAgICAvLyBJZiBDaHJvbWUgcmVhY2hlcyBxdW90YSBpdCBhYm9ydHMgdGhlIHRyYW5zYWN0aW9uXHJcbiAgICAgICAgICAgIGlmKCF0aGlzLl9zdG9yZUVycm9yKSB7XHJcbiAgICAgICAgICAgICAgLy8gT25seSBsb2cgdGhlIGZpcnN0IGVycm9yXHJcbiAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIkZhaWxlZCB0byBzdG9yZSBhdWRpbyBkYXRhIHRvIGluZGV4ZWQgZGIsIHRyYW5zYWN0aW9uIGFib3J0ZWQ6IFwiK3RyLmVycm9yKTtcclxuICAgICAgICAgICAgICB0aGlzLl9zdG9yZUVycm9yID0gdHIuZXJyb3I7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgc3Vic2NyaWJlci5lcnJvcih0ci5lcnJvcik7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICB0ci5vbmNvbXBsZXRlID0gKGV2KSA9PiB7XHJcbiAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIlRyYW5zYWN0aW9uIGNvbXBsZXRlXCIpO1xyXG4gICAgICAgICAgICBpZihhYkZsPHRoaXMuX2NodW5rRnJhbWVMZW4pe1xyXG4gICAgICAgICAgICAgIC8vIGxhc3QgY2h1bmtcclxuICAgICAgICAgICAgICB0aGlzLnNlYWwoKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBzdWJzY3JpYmVyLm5leHQoKTtcclxuICAgICAgICAgICAgc3Vic2NyaWJlci5jb21wbGV0ZSgpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgdHIuY29tbWl0KCk7XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyKSB7XHJcbiAgICAgICAgICBjb25zb2xlLmVycm9yKCdDYXRjaGVkIGVycm9yOiAnK2Vycik7XHJcbiAgICAgICAgICBzdWJzY3JpYmVyLmVycm9yKG5ldyBFcnJvcignVHJhbnNmZXIgYXVkaW8gZGF0YSBlcnJvcjogJyArIGVycikpO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICByZWxlYXNlQXVkaW9EYXRhKCk6T2JzZXJ2YWJsZTx2b2lkPntcclxuICAgIHJldHVybiBuZXcgT2JzZXJ2YWJsZTx2b2lkPigoc3Vic2NyaWJlcik9PntcclxuICAgICAgbGV0IG9zPXRoaXMuX3BlcnNpc3RlbnRBdWRpb1N0b3JhZ2VUYXJnZXQub2JqZWN0U3RvcmUoJ3JlYWR3cml0ZScpO1xyXG4gICAgICB0aGlzLmRlbGV0ZUFsbEJ1ZnMob3MsMCwoKT0+e1xyXG4gICAgICAgIHN1YnNjcmliZXIubmV4dCgpO1xyXG4gICAgICAgIHN1YnNjcmliZXIuY29tcGxldGUoKTtcclxuICAgICAgfSwoZXJyKT0+e1xyXG4gICAgICAgIHN1YnNjcmliZXIuZXJyb3IoZXJyKTtcclxuICAgICAgfSlcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgdG9TdHJpbmcoKTpzdHJpbmd7XHJcbiAgICByZXR1cm4gXCJJbmRleGVkIGRiIGF1ZGlvIGJ1ZmZlci4gQ2hhbm5lbHM6IFwiK3RoaXMuY2hhbm5lbENvdW50K1wiLCBzYW1wbGUgcmF0ZTogXCIrdGhpcy5zYW1wbGVSYXRlK1wiLCBjaHVuayBmcmFtZSBsZW5ndGg6IFwiK3RoaXMuX2NodW5rRnJhbWVMZW4rXCIsIG51bWJlciBvZiBjaHVua3M6IFwiK3RoaXMuY2h1bmtDb3VudCtcIiwgZnJhbWUgbGVuZ3RoOiBcIit0aGlzLmZyYW1lTGVuK1wiLCBzZWFsZWQ6IFwiK3RoaXMuc2VhbGVkKCk7XHJcbiAgfVxyXG5cclxuICBhc3luY0F1ZGlvSW5wdXRTdHJlYW0oKTogQXN5bmNGbG9hdDMyQXJyYXlJbnB1dFN0cmVhbSB8IG51bGwge1xyXG4gICAgcmV0dXJuIG5ldyBJbmRleGVkRGJBdWRpb0lucHV0U3RyZWFtKHRoaXMpO1xyXG4gIH1cclxuXHJcbiAgYXVkaW9JbnB1dFN0cmVhbSgpOiBGbG9hdDMyQXJyYXlJbnB1dFN0cmVhbSB8IG51bGwge1xyXG4gICAgLy8gU3luY2hyb25vdXMgc3RyZWFtIG5vdCBzdXBwb3J0ZWRcclxuICAgIHJldHVybiBudWxsO1xyXG4gIH1cclxuXHJcbiAgZ2V0IGR1cmF0aW9uKCk6IG51bWJlciB7XHJcbiAgICByZXR1cm4gdGhpcy5fY2h1bmtGcmFtZUxlbi90aGlzLl9zYW1wbGVSYXRlO1xyXG4gIH1cclxuXHJcbiAgZ2V0IG51bWJlck9mQ2hhbm5lbHMoKTogbnVtYmVyIHtcclxuICAgIHJldHVybiB0aGlzLl9jaGFubmVsQ291bnQ7XHJcbiAgfVxyXG5cclxuICBzYW1wbGVDb3VudHMoKTogbnVtYmVyIHtcclxuICAgIHJldHVybiB0aGlzLl9jaGFubmVsQ291bnQqdGhpcy5fZnJhbWVMZW47XHJcbiAgfVxyXG5cclxuICByYW5kb21BY2Nlc3NBdWRpb1N0cmVhbSgpOiBSYW5kb21BY2Nlc3NBdWRpb1N0cmVhbSB7XHJcbiAgICByZXR1cm4gbmV3IEluZGV4ZWREYlJhbmRvbUFjY2Vzc1N0cmVhbSh0aGlzKTtcclxuICB9XHJcblxyXG59XHJcblxyXG5cclxuZXhwb3J0IGNsYXNzIEluZGV4ZWREYlJhbmRvbUFjY2Vzc1N0cmVhbSBpbXBsZW1lbnRzIFJhbmRvbUFjY2Vzc0F1ZGlvU3RyZWFte1xyXG5cclxuICBwcml2YXRlIF9jdXJyQ2k9MDtcclxuICBwcml2YXRlIF9jY0NhY2hlOkZsb2F0MzJBcnJheVtdfG51bGw9bnVsbDtcclxuXHJcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBfaW5kZGJBYjpJbmRleGVkRGJBdWRpb0J1ZmZlcikge1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBjaHVuayhvczpJREJPYmplY3RTdG9yZSxjaTpudW1iZXIsY2I6KGJ1ZnM6QXJyYXk8RmxvYXQzMkFycmF5PnxudWxsKT0+dm9pZCxlcnJDYjooZXJyOkVycm9yKT0+dm9pZCl7XHJcblxyXG4gICAgLy8gQnVpbGQgYm91bmRzIGZvciBjaGFubmVsc1xyXG4gICAgbGV0IGxJZHg9W3RoaXMuX2luZGRiQWIudXVpZCxjaSwwXTtcclxuICAgIGxldCBoSWR4PVt0aGlzLl9pbmRkYkFiLnV1aWQsY2ksdGhpcy5faW5kZGJBYi5jaGFubmVsQ291bnRdO1xyXG4gICAgbGV0IGNoc0tyPUlEQktleVJhbmdlLmJvdW5kKGxJZHgsaElkeCk7XHJcblxyXG4gICAgbGV0IHJxPW9zLmdldEFsbChjaHNLcik7XHJcblxyXG4gICAgcnEub25zdWNjZXNzPSgpPT57XHJcbiAgICAgIGxldCBjYz1ycS5yZXN1bHQ7XHJcbiAgICAgIGxldCBjYzA7XHJcbiAgICAgIGxldCBjY0Nocz1jYy5sZW5ndGg7XHJcbiAgICAgIGlmKGNjQ2hzPT0wKXtcclxuICAgICAgICBjYihudWxsKTtcclxuICAgICAgICByZXR1cm47XHJcbiAgICAgIH1cclxuICAgICAgaWYoY2NDaHMhPT10aGlzLl9pbmRkYkFiLmNoYW5uZWxDb3VudCl7XHJcbiAgICAgICAgZXJyQ2IobmV3IEVycm9yKCdOdW1iZXIgb2YgY2hhbm5lbHMgb2YgaW5kZGIgZGF0YSAoJytjY0NocysnKSBkb2VzIG5vdCBtYXRjaCBudW1iZXIgb2YgY2hhbm5lbHMgKCcrdGhpcy5faW5kZGJBYi5jaGFubmVsQ291bnQrJyknKSk7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICAgICAgIH1cclxuICAgICAgaWYoY2MubGVuZ3RoPjApe1xyXG4gICAgICAgIGNjMD1jY1swXTtcclxuICAgICAgfVxyXG4gICAgICBsZXQgYXJyQnVmPW5ldyBBcnJheTxGbG9hdDMyQXJyYXk+KCk7XHJcbiAgICAgIGxldCBjY0xlbj1jYzAubGVuZ3RoO1xyXG4gICAgICBmb3IobGV0IGNoPTA7Y2g8Y2NDaHM7Y2grKyl7XHJcbiAgICAgICAgbGV0IGNoQXJyPW5ldyBGbG9hdDMyQXJyYXkoY2NMZW4pO1xyXG4gICAgICAgIGZvcihsZXQgc2k9MDtzaTxjY0xlbjtzaSsrKXtcclxuICAgICAgICAgIGNoQXJyW3NpXT1jY1tjaF1bc2ldO1xyXG4gICAgICAgIH1cclxuICAgICAgICBhcnJCdWYucHVzaChjaEFycik7XHJcbiAgICAgIH1cclxuICAgICAgY2IoYXJyQnVmKTtcclxuICAgIH1cclxuICAgIHJxLm9uZXJyb3I9KGVyckV2KT0+e1xyXG4gICAgICBlcnJDYihuZXcgRXJyb3IoZXJyRXYudG9TdHJpbmcoKSkpO1xyXG4gICAgfVxyXG5cclxuICB9XHJcblxyXG4gIHByaXZhdGUgX2ZpbGxCdWZzKGNjQnVmczpGbG9hdDMyQXJyYXlbXSx0cmdTdGF0ZTp7ZnJhbWVQb3M6bnVtYmVyLGZyYW1lTGVuOm51bWJlcix0cmdCdWZzOkZsb2F0MzJBcnJheVtdLGZpbGxlZDpudW1iZXJ9LHNyY1N0YXRlOntzcmNGcmFtZVBvczpudW1iZXIsY2k6bnVtYmVyLGNjUG9zOm51bWJlcn0pe1xyXG4gICAgbGV0IGNjQnVmc0Nocz1jY0J1ZnMubGVuZ3RoO1xyXG4gICAgaWYoY2NCdWZzQ2hzPjApIHtcclxuICAgICAgbGV0IGNjQnVmMCA9IGNjQnVmc1swXTtcclxuICAgICAgbGV0IGNjQnVmc0xlbiA9IGNjQnVmMC5sZW5ndGg7XHJcblxyXG4gICAgICAvL2NvbnNvbGUuZGVidWcoJ0luZGV4ZWREYkF1ZGlvQnVmZmVyOjpmaWxsQnVmcyBmcmFtZVBvczogJytmcmFtZVBvcysnLCBzcmNGcmFtZVBvczogJytzcmNGcmFtZVBvcysnLCBjY0J1ZnNMZW46ICcrY2NCdWZzTGVuKTtcclxuICAgICAgaWYgKHRyZ1N0YXRlLmZyYW1lUG9zID49IHNyY1N0YXRlLnNyY0ZyYW1lUG9zICsgY2NCdWZzTGVuKSB7XHJcbiAgICAgICAgLy8gdGFyZ2V0IGZyYW1lIHBvc2l0aW9uIGlzIGFoZWFkLCBzZWVrIHRvIG5leHQgc291cmNlIGJ1ZmZlclxyXG4gICAgICAgIC8vY29uc29sZS5kZWJ1ZygnSW5kZXhlZERiQXVkaW9CdWZmZXI6OmZpbGxCdWZzIHNlZWsgdG8gbmV4dCBpbmRkYiBidWZmZXInKTtcclxuICAgICAgICB0aGlzLl9jY0NhY2hlPW51bGw7XHJcbiAgICAgICAgc3JjU3RhdGUuY2krKztcclxuICAgICAgICBzcmNTdGF0ZS5jY1Bvcz0wO1xyXG4gICAgICAgIHNyY1N0YXRlLnNyY0ZyYW1lUG9zKz1jY0J1ZnNMZW47XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gQXNzdW1pbmcgdGFyZ2V0IGZyYW1lIHBvcyBpcyBpbnNpZGUgY3VycmVudCBzb3VyY2UgYnVmZmVyXHJcbiAgICAgICAgc3JjU3RhdGUuY2NQb3M9dHJnU3RhdGUuZnJhbWVQb3Mtc3JjU3RhdGUuc3JjRnJhbWVQb3M7XHJcbiAgICAgICAgbGV0IGNjQXZhaWwgPSBjY0J1ZnNMZW4gLSBzcmNTdGF0ZS5jY1BvcztcclxuICAgICAgICBsZXQgdG9Db3B5ID0gY2NCdWZzTGVuO1xyXG5cclxuICAgICAgICBpZiAodG9Db3B5ID4gY2NBdmFpbCkge1xyXG4gICAgICAgICAgdG9Db3B5ID0gY2NBdmFpbDtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKHRvQ29weSA+IHRyZ1N0YXRlLmZyYW1lTGVuKSB7XHJcbiAgICAgICAgICB0b0NvcHkgPSB0cmdTdGF0ZS5mcmFtZUxlbjtcclxuICAgICAgICB9XHJcbiAgICAgICAgZm9yIChsZXQgY2ggPSAwOyBjaCA8IGNjQnVmc0NoczsgY2grKykge1xyXG4gICAgICAgICAgZm9yIChsZXQgc2kgPSAwOyBzaSA8IHRvQ29weTsgc2krKykge1xyXG4gICAgICAgICAgICB0cmdTdGF0ZS50cmdCdWZzW2NoXVt0cmdTdGF0ZS5maWxsZWQrc2ldID0gY2NCdWZzW2NoXVtzcmNTdGF0ZS5jY1BvcytzaV07XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRyZ1N0YXRlLmZpbGxlZCArPSB0b0NvcHk7XHJcbiAgICAgICAgdHJnU3RhdGUuZnJhbWVMZW4gLT0gdG9Db3B5O1xyXG4gICAgICAgIHRyZ1N0YXRlLmZyYW1lUG9zICs9IHRvQ29weTtcclxuICAgICAgICBzcmNTdGF0ZS5jY1BvcyArPSB0b0NvcHk7XHJcbiAgICAgICAgaWYgKHNyY1N0YXRlLmNjUG9zID49IGNjQnVmc0xlbikge1xyXG4gICAgICAgICAgLy8gSW52YWxpZGF0ZSBjYWNoZVxyXG4gICAgICAgICAgdGhpcy5fY2NDYWNoZT1udWxsO1xyXG4gICAgICAgICAgc3JjU3RhdGUuY2krKztcclxuICAgICAgICAgIHRoaXMuX2N1cnJDaT1zcmNTdGF0ZS5jaTtcclxuICAgICAgICAgIHNyY1N0YXRlLmNjUG9zID0gMDtcclxuICAgICAgICAgIHNyY1N0YXRlLnNyY0ZyYW1lUG9zKz1jY0J1ZnNMZW47XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBmaWxsQnVmcyhvczpJREJPYmplY3RTdG9yZSx0cmdTdGF0ZTp7ZnJhbWVQb3M6bnVtYmVyLGZyYW1lTGVuOm51bWJlcix0cmdCdWZzOkZsb2F0MzJBcnJheVtdLGZpbGxlZDpudW1iZXJ9LHNyY1N0YXRlOntzcmNGcmFtZVBvczpudW1iZXIsY2k6bnVtYmVyLGNjUG9zOm51bWJlcixjY0ZpbGxlZDpudW1iZXJ9LGNiOihmaWxsZWQ6bnVtYmVyKT0+dm9pZCxjYkVuZDooZmlsbGVkOm51bWJlcik9PnZvaWQsY2JFcnI6KGVycjpFcnJvcik9PnZvaWQpe1xyXG4gICAgLy9jb25zb2xlLmRlYnVnKCdJbmRleGVkRGJBdWRpb0J1ZmZlcjo6ZmlsbEJ1ZnM6IGZyYW1lUG9zOicrdHJnU3RhdGUuZnJhbWVQb3MrJywgZnJhbWVMZW46ICcrdHJnU3RhdGUuZnJhbWVMZW4rJywgZmlsbGVkOiAnK3RyZ1N0YXRlLmZpbGxlZCsnLCBzcmNGcmFtZVBvczogJytzcmNTdGF0ZS5zcmNGcmFtZVBvcysnLGNpOiAnK3NyY1N0YXRlLmNpKycsIGNjUG9zOiAnK3NyY1N0YXRlLmNjUG9zKTtcclxuICAgIGlmKHRoaXMuX2NjQ2FjaGUpe1xyXG4gICAgICB0aGlzLl9maWxsQnVmcyh0aGlzLl9jY0NhY2hlLHRyZ1N0YXRlLHNyY1N0YXRlKTtcclxuICAgICAgLy9jb25zb2xlLmRlYnVnKCdJbmRleGVkRGJBdWRpb0J1ZmZlcjo6ZmlsbEJ1ZnMgZnJhbWVMZW46ICcrdHJnU3RhdGUuZnJhbWVMZW4pO1xyXG4gICAgICBpZih0cmdTdGF0ZS5mcmFtZUxlbj09PTApe1xyXG4gICAgICAgIC8vY29uc29sZS5kZWJ1ZygnSW5kZXhlZERiQXVkaW9CdWZmZXI6OmZpbGxCdWZzIChmcmFtZWxlbj09MCkgY2FsbDogY2JlbmQgJyt0cmdTdGF0ZS5maWxsZWQpO1xyXG4gICAgICAgIGNiRW5kKHRyZ1N0YXRlLmZpbGxlZCk7XHJcbiAgICAgIH1lbHNlIHtcclxuICAgICAgICB0aGlzLmZpbGxCdWZzKG9zLCB0cmdTdGF0ZSwgc3JjU3RhdGUsIGNiLCBjYkVuZCwgY2JFcnIpO1xyXG4gICAgICB9XHJcbiAgICB9ZWxzZSB7XHJcbiAgICAgIHRoaXMuY2h1bmsob3MsIHNyY1N0YXRlLmNpLCAoY2NCdWZzKSA9PiB7XHJcbiAgICAgICAgaWYgKGNjQnVmcykge1xyXG4gICAgICAgICAgdGhpcy5fY3VyckNpID0gc3JjU3RhdGUuY2k7XHJcbiAgICAgICAgICB0aGlzLl9jY0NhY2hlID0gY2NCdWZzO1xyXG5cclxuICAgICAgICAgIHRoaXMuX2ZpbGxCdWZzKGNjQnVmcywgdHJnU3RhdGUsIHNyY1N0YXRlKTtcclxuICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZygnSW5kZXhlZERiQXVkaW9CdWZmZXI6OmZpbGxCdWZzIGZyYW1lTGVuOiAnK3RyZ1N0YXRlLmZyYW1lTGVuKTtcclxuICAgICAgICAgIGlmICh0cmdTdGF0ZS5mcmFtZUxlbiA9PT0gMCkge1xyXG4gICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoJ0luZGV4ZWREYkF1ZGlvQnVmZmVyOjpmaWxsQnVmcyAoZnJhbWVsZW49PTApIGNhbGw6IGNiZW5kICcrdHJnU3RhdGUuZmlsbGVkKTtcclxuICAgICAgICAgICAgY2JFbmQodHJnU3RhdGUuZmlsbGVkKTtcclxuICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIHRoaXMuZmlsbEJ1ZnMob3MsdHJnU3RhdGUsIHNyY1N0YXRlLCBjYiwgY2JFbmQsIGNiRXJyKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgLy9jb25zb2xlLmRlYnVnKCdJbmRleGVkRGJBdWRpb0J1ZmZlcjo6ZmlsbEJ1ZnMgKGNodW5rIG5vdCBmb3VuZCkgY2FsbDogY2JlbmQgJyt0cmdTdGF0ZS5maWxsZWQpO1xyXG4gICAgICAgICAgY2JFbmQodHJnU3RhdGUuZmlsbGVkKTtcclxuICAgICAgICB9XHJcbiAgICAgIH0sIChlcnIpID0+IHtcclxuICAgICAgICBjYkVycihlcnIpO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuICB9XHJcblxyXG5cclxuICBmcmFtZXNPYnMoZnJhbWVQb3M6bnVtYmVyLGZyYW1lTGVuOm51bWJlcixidWZzOkZsb2F0MzJBcnJheVtdKTpPYnNlcnZhYmxlPG51bWJlcj57XHJcbiAgICByZXR1cm4gbmV3IE9ic2VydmFibGU8bnVtYmVyPigoc3Vic2NyaWJlcik9PntcclxuICAgICAgbGV0IHRyPXRoaXMuX2luZGRiQWIucGVyc2lzdGVudEF1ZGlvU3RvcmFnZVRhcmdldC50cmFuc2FjdGlvbigpO1xyXG4gICAgICBsZXQgb3M9dHIub2JqZWN0U3RvcmUodGhpcy5faW5kZGJBYi5wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0LnN0b3JlTmFtZSk7XHJcbiAgICAgIC8vIFBvc2l0aW9uaW5nXHJcbiAgICAgIGxldCBuZXdDaT1NYXRoLmZsb29yKGZyYW1lUG9zL3RoaXMuX2luZGRiQWIuY2h1bmtGcmFtZUxlbik7XHJcbiAgICAgIGlmKG5ld0NpIT09dGhpcy5fY3VyckNpKXtcclxuICAgICAgICB0aGlzLl9jdXJyQ2k9bmV3Q2k7XHJcbiAgICAgICAgdGhpcy5fY2NDYWNoZT1udWxsO1xyXG4gICAgICB9XHJcbiAgICAgIGxldCBzcmNGcmFtZVBvcz1uZXdDaSp0aGlzLl9pbmRkYkFiLmNodW5rRnJhbWVMZW47XHJcbiAgICAgIGxldCB0cmdTdGF0ZT17ZnJhbWVQb3M6ZnJhbWVQb3MsZnJhbWVMZW46ZnJhbWVMZW4sdHJnQnVmczpidWZzLGZpbGxlZDowfTtcclxuICAgICAgbGV0IHNyY1N0YXRlPXtzcmNGcmFtZVBvczpzcmNGcmFtZVBvcyxjaTpuZXdDaSxjY1BvczowLGNjRmlsbGVkOjB9O1xyXG4gICAgICB0aGlzLmZpbGxCdWZzKG9zLHRyZ1N0YXRlLHNyY1N0YXRlLCh2YWwpPT57fSwoZmlsbGVkOm51bWJlcik9PntcclxuICAgICAgICBzdWJzY3JpYmVyLm5leHQoZmlsbGVkKTtcclxuICAgICAgICBzdWJzY3JpYmVyLmNvbXBsZXRlKCk7XHJcbiAgICAgIH0sZXJyID0+IHtcclxuICAgICAgICBzdWJzY3JpYmVyLmVycm9yKGVycik7XHJcbiAgICAgIH0pXHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIGNsb3NlKCk6IHZvaWQge1xyXG4gICAgdGhpcy5fY3VyckNpPTA7XHJcbiAgICB0aGlzLl9jY0NhY2hlPW51bGw7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgY2xhc3MgSW5kZXhlZERiQXVkaW9JbnB1dFN0cmVhbSBpbXBsZW1lbnRzIEFzeW5jRmxvYXQzMkFycmF5SW5wdXRTdHJlYW17XHJcblxyXG4gIHByaXZhdGUgZnJhbWVQb3M6bnVtYmVyPTA7XHJcbiAgcHJpdmF0ZSBpbmRkYkFiU3RyOkluZGV4ZWREYlJhbmRvbUFjY2Vzc1N0cmVhbTtcclxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGluZGRiQWI6SW5kZXhlZERiQXVkaW9CdWZmZXIpIHtcclxuICAgICAgdGhpcy5pbmRkYkFiU3RyPW5ldyBJbmRleGVkRGJSYW5kb21BY2Nlc3NTdHJlYW0oaW5kZGJBYik7XHJcbiAgfVxyXG5cclxuICBjbG9zZSgpOiB2b2lkIHtcclxuICAgIHRoaXMuaW5kZGJBYlN0ci5jbG9zZSgpO1xyXG4gIH1cclxuXHJcbiAgcmVhZE9icyhidWZmZXJzOiBBcnJheTxGbG9hdDMyQXJyYXk+KTogT2JzZXJ2YWJsZTxudW1iZXI+IHtcclxuICAgIHJldHVybiBuZXcgT2JzZXJ2YWJsZTxudW1iZXI+KHN1YnNjcj0+IHtcclxuICAgICAgaWYgKGJ1ZmZlcnMgJiYgYnVmZmVycy5sZW5ndGggPiAwKSB7XHJcbiAgICAgICAgbGV0IGZsID0gYnVmZmVyc1swXS5sZW5ndGg7XHJcbiAgICAgICAgdGhpcy5pbmRkYkFiU3RyLmZyYW1lc09icyh0aGlzLmZyYW1lUG9zLCBmbCwgYnVmZmVycykuc3Vic2NyaWJlKHtcclxuICAgICAgICAgIG5leHQ6IChyZWFkKSA9PiB7XHJcbiAgICAgICAgICAgIHRoaXMuZnJhbWVQb3MgKz0gcmVhZDtcclxuICAgICAgICAgICAgc3Vic2NyLm5leHQocmVhZCk7XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgY29tcGxldGU6ICgpID0+IHtcclxuICAgICAgICAgICAgc3Vic2NyLmNvbXBsZXRlKCk7XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgZXJyb3I6IChlcnIpID0+IHtcclxuICAgICAgICAgICAgc3Vic2NyLmVycm9yKGVycik7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSlcclxuICAgICAgfWVsc2V7XHJcbiAgICAgICAgc3Vic2NyLm5leHQoMCk7XHJcbiAgICAgICAgc3Vic2NyLmNvbXBsZXRlKCk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgc2tpcEZyYW1lcyhuOiBudW1iZXIpOnZvaWQge1xyXG4gICAgdGhpcy5mcmFtZVBvcys9bjtcclxuICB9XHJcblxyXG59XHJcbiJdfQ==
|