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,1179 @@
|
|
|
1
|
+
import { AudioCapture } from '../../audio/capture/capture';
|
|
2
|
+
import { AudioPlayer, EventType } from '../../audio/playback/player';
|
|
3
|
+
import { WavWriter } from '../../audio/impl/wavwriter';
|
|
4
|
+
import { RecordingFile, RecordingFileUtils } from '../recording';
|
|
5
|
+
import { Component, HostListener, Inject, Input, ViewChild } from "@angular/core";
|
|
6
|
+
import { SessionService } from "./session.service";
|
|
7
|
+
import { SPEECHRECORDER_CONFIG } from "../../spr.config";
|
|
8
|
+
import { AudioStorageType, ProjectUtil } from "../project/project";
|
|
9
|
+
import { MessageDialog } from "../../ui/message_dialog";
|
|
10
|
+
import { RecordingService } from "../recordings/recordings.service";
|
|
11
|
+
import { AudioClip } from "../../audio/persistor";
|
|
12
|
+
import { Upload, UploaderStatus, UploadHolder } from "../../net/uploader";
|
|
13
|
+
import { LevelBar, State as LiveLevelState } from "../../audio/ui/livelevel";
|
|
14
|
+
import { RecorderCombiPane } from "./recorder_combi_pane";
|
|
15
|
+
import { BasicRecorder, MAX_RECORDING_TIME_MS, RECFILE_API_CTX } from "./basicrecorder";
|
|
16
|
+
import { RecorderComponent } from "../../recorder_component";
|
|
17
|
+
import { AudioBufferSource, AudioDataHolder } from "../../audio/audio_data_holder";
|
|
18
|
+
import { NetAudioBuffer } from "../../audio/net_audio_buffer";
|
|
19
|
+
import * as i0 from "@angular/core";
|
|
20
|
+
import * as i1 from "@angular/cdk/layout";
|
|
21
|
+
import * as i2 from "@angular/material/dialog";
|
|
22
|
+
import * as i3 from "./session.service";
|
|
23
|
+
import * as i4 from "../recordings/recordings.service";
|
|
24
|
+
import * as i5 from "../spruploader";
|
|
25
|
+
import * as i6 from "@angular/common";
|
|
26
|
+
import * as i7 from "@angular/material/icon";
|
|
27
|
+
import * as i8 from "@angular/material/button";
|
|
28
|
+
import * as i9 from "../../audio/ui/livelevel";
|
|
29
|
+
import * as i10 from "./controlpanel";
|
|
30
|
+
import * as i11 from "../../ui/recordingitem_display";
|
|
31
|
+
import * as i12 from "./warning_bar";
|
|
32
|
+
import * as i13 from "./recorder_combi_pane";
|
|
33
|
+
import * as i14 from "../../spr.config";
|
|
34
|
+
import * as i15 from "@angular/router";
|
|
35
|
+
import * as i16 from "../project/project.service";
|
|
36
|
+
export class AudioRecorder extends BasicRecorder {
|
|
37
|
+
constructor(bpo, changeDetectorRef, renderer, dialog, sessionService, recFileService, uploader, config) {
|
|
38
|
+
super(bpo, changeDetectorRef, dialog, sessionService, uploader, config);
|
|
39
|
+
this.bpo = bpo;
|
|
40
|
+
this.renderer = renderer;
|
|
41
|
+
this.recFileService = recFileService;
|
|
42
|
+
this.uploader = uploader;
|
|
43
|
+
this._project = null;
|
|
44
|
+
this.projectName = null;
|
|
45
|
+
this.enableUploadRecordings = true;
|
|
46
|
+
this.enableDownloadRecordings = false;
|
|
47
|
+
this.status = 0 /* Status.BLOCKED */;
|
|
48
|
+
this.dataSaved = true;
|
|
49
|
+
this._displayRecFile = null;
|
|
50
|
+
this.displayRecFileVersion = 0;
|
|
51
|
+
//super(injector);
|
|
52
|
+
this.status = 1 /* Status.IDLE */;
|
|
53
|
+
this.audio = document.getElementById('audio');
|
|
54
|
+
if (this.config && this.config.enableUploadRecordings != null) {
|
|
55
|
+
this.enableUploadRecordings = this.config.enableUploadRecordings;
|
|
56
|
+
}
|
|
57
|
+
if (this.config && this.config.enableDownloadRecordings != null) {
|
|
58
|
+
this.enableDownloadRecordings = this.config.enableDownloadRecordings;
|
|
59
|
+
}
|
|
60
|
+
//this.init();
|
|
61
|
+
}
|
|
62
|
+
ngAfterViewInit() {
|
|
63
|
+
this.streamLevelMeasure.levelListener = this.liveLevelDisplay;
|
|
64
|
+
this.streamLevelMeasure.peakLevelListener = (peakLvlInDb) => {
|
|
65
|
+
this.peakLevelInDb = peakLvlInDb;
|
|
66
|
+
this.changeDetectorRef.detectChanges();
|
|
67
|
+
};
|
|
68
|
+
//let wakeLockSupp=('wakeLock' in navigator);
|
|
69
|
+
//alert('Wake lock API supported: '+wakeLockSupp);
|
|
70
|
+
}
|
|
71
|
+
ready() {
|
|
72
|
+
return this.dataSaved && !this.isActive();
|
|
73
|
+
}
|
|
74
|
+
ngOnDestroy() {
|
|
75
|
+
this.disableWakeLockCond();
|
|
76
|
+
this.destroyed = true;
|
|
77
|
+
// TODO stop capture /playback
|
|
78
|
+
}
|
|
79
|
+
ngOnInit() {
|
|
80
|
+
this.transportActions.startAction.disabled = true;
|
|
81
|
+
this.transportActions.stopAction.disabled = true;
|
|
82
|
+
this.transportActions.nextAction.disabled = true;
|
|
83
|
+
this.transportActions.pauseAction.disabled = true;
|
|
84
|
+
this.playStartAction.disabled = true;
|
|
85
|
+
this.ac = new AudioCapture();
|
|
86
|
+
if (this.ac) {
|
|
87
|
+
this.transportActions.startAction.onAction = () => this.startItem();
|
|
88
|
+
this.ac.listener = this;
|
|
89
|
+
this.configureStreamCaptureStream();
|
|
90
|
+
// Don't list the devices here. If we do not have audio permissions we only get anonymized devices without labels.
|
|
91
|
+
//this.ac.listDevices();
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
this.transportActions.startAction.disabled = true;
|
|
95
|
+
let errMsg = 'Browser does not support Media/Audio API!';
|
|
96
|
+
this.statusMsg = 'ERROR: ' + errMsg;
|
|
97
|
+
this.statusAlertType = 'error';
|
|
98
|
+
this.dialog.open(MessageDialog, {
|
|
99
|
+
data: {
|
|
100
|
+
type: 'error',
|
|
101
|
+
title: 'Error',
|
|
102
|
+
msg: errMsg,
|
|
103
|
+
advice: 'Please use a supported browser.',
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
this.transportActions.stopAction.onAction = () => this.stopItem();
|
|
109
|
+
this.transportActions.nextAction.onAction = () => this.stopItem();
|
|
110
|
+
//this.transportActions.pauseAction.onAction = () => this.pauseItem();
|
|
111
|
+
this.playStartAction.onAction = () => this.controlAudioPlayer?.start();
|
|
112
|
+
this.uploader.listener = (ue) => {
|
|
113
|
+
this.uploadUpdate(ue);
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
onKeyPress(ke) {
|
|
117
|
+
if (ke.key == ' ') {
|
|
118
|
+
//this.transportActions.startAction.perform();
|
|
119
|
+
//this.transportActions.nextAction.perform();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
onKeyDown(ke) {
|
|
123
|
+
if (ke.key == ' ') {
|
|
124
|
+
if (!this.transportActions.startAction.disabled) {
|
|
125
|
+
this.transportActions.startAction.perform();
|
|
126
|
+
}
|
|
127
|
+
else if (!this.transportActions.stopAction.disabled) {
|
|
128
|
+
this.transportActions.stopAction.perform();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (ke.key == 'p') {
|
|
132
|
+
this.transportActions.pauseAction.perform();
|
|
133
|
+
}
|
|
134
|
+
if (ke.key == 'Escape') {
|
|
135
|
+
if (!this.audioSignalCollapsed) {
|
|
136
|
+
this.audioSignalCollapsed = true;
|
|
137
|
+
}
|
|
138
|
+
this.transportActions.stopAction.perform();
|
|
139
|
+
this.transportActions.pauseAction.perform();
|
|
140
|
+
}
|
|
141
|
+
if (ke.key == 'MediaPlayPause') {
|
|
142
|
+
this.playStartAction.perform();
|
|
143
|
+
}
|
|
144
|
+
if (ke.key === 'ArrowRight') {
|
|
145
|
+
this.transportActions.fwdAction.perform();
|
|
146
|
+
}
|
|
147
|
+
if (ke.key === 'ArrowLeft') {
|
|
148
|
+
this.transportActions.bwdAction.perform();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
isTestSession() {
|
|
152
|
+
return ((this._session != null) && (this._session.type === 'TEST' || this._session.type === 'TEST_DEF_A' || this._session.type === 'SINUS_TEST'));
|
|
153
|
+
}
|
|
154
|
+
isDefaultAudioTestSession() {
|
|
155
|
+
return ((this._session != null) && (this._session.type === 'TEST_DEF_A'));
|
|
156
|
+
}
|
|
157
|
+
isDefaultAudioTestSessionOverwriteingProjectRequirements() {
|
|
158
|
+
return ((this._session != null) && (this._session.type === 'TEST_DEF_A') && (this.audioDevices != null) && this.audioDevices.length > 0);
|
|
159
|
+
}
|
|
160
|
+
fetchRecordings(sess) {
|
|
161
|
+
this.statusAlertType = 'info';
|
|
162
|
+
this.statusMsg = 'Fetching infos of recordings...';
|
|
163
|
+
this.statusWaiting = true;
|
|
164
|
+
//let prNm:string|null=null;
|
|
165
|
+
if (this.project) {
|
|
166
|
+
let rfsObs = this.recFileService.recordingFileList(this.project.name, sess.sessionId);
|
|
167
|
+
rfsObs.subscribe({ next: (rfs) => {
|
|
168
|
+
this.statusAlertType = 'info';
|
|
169
|
+
this.statusMsg = 'Received infos of recordings.';
|
|
170
|
+
this.statusWaiting = false;
|
|
171
|
+
if (rfs) {
|
|
172
|
+
if (rfs instanceof Array) {
|
|
173
|
+
rfs.forEach((rf) => {
|
|
174
|
+
// the list comes from the server, asssuem all recording files as server persisted
|
|
175
|
+
rf.serverPersisted = true;
|
|
176
|
+
if (rf.startedDate) {
|
|
177
|
+
rf._startedAsDateObj = new Date(rf.startedDate);
|
|
178
|
+
}
|
|
179
|
+
if (rf.date) {
|
|
180
|
+
rf._dateAsDateObj = new Date(rf.date);
|
|
181
|
+
}
|
|
182
|
+
this.recorderCombiPane.addRecFile(rf);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
console.error('Expected type array for list of already recorded files ');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
//console.debug("Recording file list: " + rfs);
|
|
191
|
+
}
|
|
192
|
+
}, error: (err) => {
|
|
193
|
+
// Failed fetching existing, but we start the session anyway
|
|
194
|
+
this.start();
|
|
195
|
+
}, complete: () => {
|
|
196
|
+
// Normal start
|
|
197
|
+
this.start();
|
|
198
|
+
} });
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
// No project def -> error
|
|
202
|
+
this.statusAlertType = 'error';
|
|
203
|
+
this.statusMsg = 'No project definiton.';
|
|
204
|
+
this.statusWaiting = false;
|
|
205
|
+
console.error(this.statusMsg);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
set project(project) {
|
|
209
|
+
this._project = project;
|
|
210
|
+
let chCnt = ProjectUtil.DEFAULT_AUDIO_CHANNEL_COUNT;
|
|
211
|
+
if (project) {
|
|
212
|
+
console.info("Project name: " + project.name);
|
|
213
|
+
if (project.recordingDeviceWakeLock === true) {
|
|
214
|
+
this.wakeLock = true;
|
|
215
|
+
}
|
|
216
|
+
this.audioDevices = project.audioDevices;
|
|
217
|
+
chCnt = ProjectUtil.audioChannelCount(project);
|
|
218
|
+
console.info("Project requested recording channel count: " + chCnt);
|
|
219
|
+
this.autoGainControlConfigs = project.autoGainControlConfigs;
|
|
220
|
+
if (project.allowEchoCancellation !== undefined) {
|
|
221
|
+
this.allowEchoCancellation = project.allowEchoCancellation;
|
|
222
|
+
}
|
|
223
|
+
if (project.chunkedRecording === true) {
|
|
224
|
+
this.uploadChunkSizeSeconds = BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
this.uploadChunkSizeSeconds = null;
|
|
228
|
+
}
|
|
229
|
+
if (project.clientAudioStorageType) {
|
|
230
|
+
this.clientAudioStorageType = project.clientAudioStorageType;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
console.error("Empty project configuration!");
|
|
235
|
+
}
|
|
236
|
+
this.channelCount = chCnt;
|
|
237
|
+
}
|
|
238
|
+
get project() {
|
|
239
|
+
return this._project;
|
|
240
|
+
}
|
|
241
|
+
selectRecordingFile(rf) {
|
|
242
|
+
this.liveLevelDisplayState = LiveLevelState.READY;
|
|
243
|
+
this.keepLiveLevel = false;
|
|
244
|
+
this.displayRecFile = rf;
|
|
245
|
+
}
|
|
246
|
+
uploadUpdate(ue) {
|
|
247
|
+
let upStatus = ue.status;
|
|
248
|
+
this.dataSaved = (UploaderStatus.DONE === upStatus);
|
|
249
|
+
let percentUpl = ue.percentDone();
|
|
250
|
+
if (UploaderStatus.ERR === upStatus) {
|
|
251
|
+
this.uploadStatus = 'warn';
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
if (percentUpl < 50) {
|
|
255
|
+
this.uploadStatus = 'accent';
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
this.uploadStatus = 'success';
|
|
259
|
+
}
|
|
260
|
+
this.uploadProgress = percentUpl;
|
|
261
|
+
}
|
|
262
|
+
this.changeDetectorRef.detectChanges();
|
|
263
|
+
}
|
|
264
|
+
set controlAudioPlayer(controlAudioPlayer) {
|
|
265
|
+
this._controlAudioPlayer = controlAudioPlayer;
|
|
266
|
+
if (this._controlAudioPlayer) {
|
|
267
|
+
this._controlAudioPlayer.listener = this;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
get controlAudioPlayer() {
|
|
271
|
+
return this._controlAudioPlayer;
|
|
272
|
+
}
|
|
273
|
+
update(e) {
|
|
274
|
+
if (e.type == EventType.STARTED) {
|
|
275
|
+
this.playStartAction.disabled = true;
|
|
276
|
+
this.updateTimerId = window.setInterval(() => {
|
|
277
|
+
//this.audioSignal.playFramePosition = this.ap.playPositionFrames;
|
|
278
|
+
}, 50);
|
|
279
|
+
}
|
|
280
|
+
else if (e.type == EventType.STOPPED || e.type == EventType.ENDED) {
|
|
281
|
+
window.clearInterval(this.updateTimerId);
|
|
282
|
+
//console.debug("Enable play start action (by player events stopped or ended): "+(!(this.displayRecFile)));
|
|
283
|
+
this.playStartAction.disabled = (!(this.displayRecFile));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
startDisabled() {
|
|
287
|
+
return !this.transportActions || this.readonly || this.transportActions.startAction.disabled;
|
|
288
|
+
}
|
|
289
|
+
stopDisabled() {
|
|
290
|
+
return !this.transportActions || this.transportActions.stopAction.disabled;
|
|
291
|
+
}
|
|
292
|
+
startStopNextName() {
|
|
293
|
+
if (!this.startDisabled()) {
|
|
294
|
+
this.startStopNextButtonName = "Start";
|
|
295
|
+
}
|
|
296
|
+
else if (!this.stopDisabled()) {
|
|
297
|
+
this.startStopNextButtonName = "Stop";
|
|
298
|
+
}
|
|
299
|
+
return this.startStopNextButtonName;
|
|
300
|
+
}
|
|
301
|
+
startStopNextIconName() {
|
|
302
|
+
if (!this.startDisabled()) {
|
|
303
|
+
this.startStopNextButtonIconName = "fiber_manual_record";
|
|
304
|
+
}
|
|
305
|
+
else if (!this.stopDisabled()) {
|
|
306
|
+
this.startStopNextButtonIconName = "stop";
|
|
307
|
+
}
|
|
308
|
+
return this.startStopNextButtonIconName;
|
|
309
|
+
}
|
|
310
|
+
startStopNextIconColor() {
|
|
311
|
+
if (!this.startDisabled()) {
|
|
312
|
+
return "red";
|
|
313
|
+
}
|
|
314
|
+
else if (!this.stopDisabled()) {
|
|
315
|
+
return "yellow";
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
return "grey";
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
startStopPerform() {
|
|
322
|
+
if (!this.startDisabled()) {
|
|
323
|
+
this.transportActions.startAction.perform();
|
|
324
|
+
}
|
|
325
|
+
else if (!this.stopDisabled()) {
|
|
326
|
+
this.transportActions.stopAction.perform();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
startItem() {
|
|
330
|
+
this.status = 2 /* Status.STARTING */;
|
|
331
|
+
super.startItem();
|
|
332
|
+
if (this.readonly) {
|
|
333
|
+
this.status = 1 /* Status.IDLE */;
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
this.transportActions.fwdAction.disabled = true;
|
|
337
|
+
this.transportActions.fwdNextAction.disabled = true;
|
|
338
|
+
this.transportActions.bwdAction.disabled = true;
|
|
339
|
+
this.displayRecFile = null;
|
|
340
|
+
this.displayRecFileVersion = 0;
|
|
341
|
+
this.displayAudioClip = null;
|
|
342
|
+
this.liveLevelDisplay.reset(true);
|
|
343
|
+
this.liveLevelDisplayState = LiveLevelState.READY;
|
|
344
|
+
this.showRecording();
|
|
345
|
+
this.startCapture();
|
|
346
|
+
}
|
|
347
|
+
downloadRecording() {
|
|
348
|
+
if (this.displayRecFile) {
|
|
349
|
+
let ab = this.displayRecFile.audioDataHolder;
|
|
350
|
+
let ww = new WavWriter();
|
|
351
|
+
let as = ab?.audioSource;
|
|
352
|
+
if (as instanceof AudioBufferSource) {
|
|
353
|
+
ww.writeAsync(as.audioBuffer, (wavFile) => {
|
|
354
|
+
let blob = new Blob([wavFile], { type: 'audio/wav' });
|
|
355
|
+
let rfUrl = URL.createObjectURL(blob);
|
|
356
|
+
let dataDnlLnk = this.renderer.createElement('a');
|
|
357
|
+
//dataDnlLnk.name = 'Recording';
|
|
358
|
+
dataDnlLnk.href = rfUrl;
|
|
359
|
+
this.renderer.appendChild(document.body, dataDnlLnk);
|
|
360
|
+
// download property not yet in TS def
|
|
361
|
+
if (this.displayRecFile) {
|
|
362
|
+
let fn = this.displayRecFile.filenameString();
|
|
363
|
+
fn += '_' + this.displayRecFileVersion;
|
|
364
|
+
fn += '.wav';
|
|
365
|
+
dataDnlLnk.download = fn;
|
|
366
|
+
dataDnlLnk.click();
|
|
367
|
+
}
|
|
368
|
+
this.renderer.removeChild(document.body, dataDnlLnk);
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
set displayRecFile(displayRecFile) {
|
|
374
|
+
//this.audioLoaded=false;
|
|
375
|
+
this._displayRecFile = displayRecFile;
|
|
376
|
+
if (this._displayRecFile) {
|
|
377
|
+
let adh = this._displayRecFile.audioDataHolder;
|
|
378
|
+
if (adh) {
|
|
379
|
+
this.displayAudioClip = new AudioClip(adh);
|
|
380
|
+
//this.audioLoaded=true;
|
|
381
|
+
//console.debug(" set recording file: display audio clip set");
|
|
382
|
+
if (this._controlAudioPlayer) {
|
|
383
|
+
this._controlAudioPlayer.audioClip = this.displayAudioClip;
|
|
384
|
+
}
|
|
385
|
+
this.showRecording();
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
// clear for now ...
|
|
389
|
+
this.displayAudioClip = null;
|
|
390
|
+
//console.debug("set recording file: display audio clip null");
|
|
391
|
+
if (this._controlAudioPlayer) {
|
|
392
|
+
this._controlAudioPlayer.audioClip = null;
|
|
393
|
+
}
|
|
394
|
+
if (this._controlAudioPlayer && this._session) {
|
|
395
|
+
//... and try to fetch from server
|
|
396
|
+
this.liveLevelDisplayState = LiveLevelState.LOADING;
|
|
397
|
+
const rf = this._displayRecFile;
|
|
398
|
+
let audioDownloadType = this._clientAudioStorageType;
|
|
399
|
+
if (AudioStorageType.MEM_ENTIRE_AUTO_NET_CHUNKED === this._clientAudioStorageType || AudioStorageType.MEM_CHUNKED_AUTO_NET_CHUNKED === this._clientAudioStorageType) {
|
|
400
|
+
// Default is network mode
|
|
401
|
+
audioDownloadType = AudioStorageType.NET_CHUNKED;
|
|
402
|
+
if (rf.channels && rf.frames) {
|
|
403
|
+
const samples = rf.channels * rf.frames;
|
|
404
|
+
if (samples <= this._maxAutoNetMemStoreSamples) {
|
|
405
|
+
// But if audio file is small, load in continuous resp. chunked mode
|
|
406
|
+
if (AudioStorageType.MEM_ENTIRE_AUTO_NET_CHUNKED === this._clientAudioStorageType) {
|
|
407
|
+
audioDownloadType = AudioStorageType.MEM_ENTIRE;
|
|
408
|
+
}
|
|
409
|
+
else if (AudioStorageType.MEM_CHUNKED_AUTO_NET_CHUNKED === this._clientAudioStorageType) {
|
|
410
|
+
audioDownloadType = AudioStorageType.MEM_CHUNKED;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (AudioStorageType.DB_CHUNKED === this._clientAudioStorageType) {
|
|
416
|
+
// Fetch chunked indexed db audio buffer
|
|
417
|
+
let nextIab = null;
|
|
418
|
+
if (!this._persistentAudioStorageTarget) {
|
|
419
|
+
throw Error('Error: Persistent storage target not set.');
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
//console.debug("Fetch audio and store to indexed db...");
|
|
423
|
+
this.audioFetchSubscription = this.recFileService.fetchRecordingFileIndDbAudioBuffer(this._persistentAudioStorageTarget, this._session.project, rf).subscribe({
|
|
424
|
+
next: (iab) => {
|
|
425
|
+
//console.debug("Sessionmanager: Received inddb audio buffer: "+iab);
|
|
426
|
+
nextIab = iab;
|
|
427
|
+
},
|
|
428
|
+
complete: () => {
|
|
429
|
+
this.liveLevelDisplayState = LiveLevelState.READY;
|
|
430
|
+
let fabDh = null;
|
|
431
|
+
if (nextIab) {
|
|
432
|
+
if (rf) {
|
|
433
|
+
fabDh = new AudioDataHolder(nextIab);
|
|
434
|
+
//this.audioLoaded=true;
|
|
435
|
+
this.recorderCombiPane.setRecFileAudioData(rf, fabDh);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
// Should actually be handled by the error resolver
|
|
440
|
+
this.statusMsg = 'Recording file could not be loaded.';
|
|
441
|
+
this.statusAlertType = 'error';
|
|
442
|
+
}
|
|
443
|
+
if (fabDh) {
|
|
444
|
+
// this.displayAudioClip could have been changed meanwhile, but the recorder unsubcribes before changing the item. Therefore, there should be no risk to set to wrong item
|
|
445
|
+
this.displayAudioClip = new AudioClip(fabDh);
|
|
446
|
+
}
|
|
447
|
+
if (this._controlAudioPlayer) {
|
|
448
|
+
this._controlAudioPlayer.audioClip = this.displayAudioClip;
|
|
449
|
+
}
|
|
450
|
+
this.showRecording();
|
|
451
|
+
},
|
|
452
|
+
error: err => {
|
|
453
|
+
console.error("Could not load recording file from server: " + err);
|
|
454
|
+
this.liveLevelDisplayState = LiveLevelState.READY;
|
|
455
|
+
this.statusMsg = 'Recording file could not be loaded: ' + err;
|
|
456
|
+
this.statusAlertType = 'error';
|
|
457
|
+
this.changeDetectorRef.detectChanges();
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
else if (AudioStorageType.NET_CHUNKED === audioDownloadType) {
|
|
463
|
+
// Fetch chunked audio buffer from network
|
|
464
|
+
let nextNetAb = null;
|
|
465
|
+
//console.debug("Fetch chunked audio from network");
|
|
466
|
+
this.audioFetchSubscription = this.recFileService.fetchRecordingFileNetAudioBuffer(this._session.project, rf).subscribe({
|
|
467
|
+
next: (netAb) => {
|
|
468
|
+
//console.debug("Sessionmanager: Received net audio buffer: "+netAb);
|
|
469
|
+
nextNetAb = netAb;
|
|
470
|
+
},
|
|
471
|
+
complete: () => {
|
|
472
|
+
this.liveLevelDisplayState = LiveLevelState.READY;
|
|
473
|
+
let fabDh = null;
|
|
474
|
+
if (nextNetAb) {
|
|
475
|
+
if (rf) {
|
|
476
|
+
fabDh = new AudioDataHolder(nextNetAb);
|
|
477
|
+
this.recorderCombiPane.setRecFileAudioData(rf, fabDh);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
// Should actually be handled by the error resolver
|
|
482
|
+
this.statusMsg = 'Recording file could not be loaded.';
|
|
483
|
+
this.statusAlertType = 'error';
|
|
484
|
+
}
|
|
485
|
+
if (fabDh) {
|
|
486
|
+
// this.displayAudioClip could have been changed meanwhile, but the recorder unsubcribes before changing the item. Therefore, there should be no risk to set to wrong item
|
|
487
|
+
//console.debug("set displayRecFile(): fetch net ab complete, set displayAudioClip.")
|
|
488
|
+
this.displayAudioClip = new AudioClip(fabDh);
|
|
489
|
+
// fabDh.onReady=()=>{
|
|
490
|
+
// this.audioLoaded=true;
|
|
491
|
+
// }
|
|
492
|
+
}
|
|
493
|
+
if (this._controlAudioPlayer) {
|
|
494
|
+
this._controlAudioPlayer.audioClip = this.displayAudioClip;
|
|
495
|
+
}
|
|
496
|
+
this.showRecording();
|
|
497
|
+
},
|
|
498
|
+
error: err => {
|
|
499
|
+
console.error("Could not load recording file from server: " + err);
|
|
500
|
+
this.liveLevelDisplayState = LiveLevelState.READY;
|
|
501
|
+
this.statusMsg = 'Recording file could not be loaded: ' + err;
|
|
502
|
+
this.statusAlertType = 'error';
|
|
503
|
+
this.changeDetectorRef.detectChanges();
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
else if (AudioStorageType.MEM_CHUNKED === audioDownloadType) {
|
|
508
|
+
// Fetch chunked array audio buffer
|
|
509
|
+
let nextAab = null;
|
|
510
|
+
//console.debug("Fetch audio and store to (chunked) array buffer...");
|
|
511
|
+
this.audioFetchSubscription = this.recFileService.fetchRecordingFileArrayAudioBuffer(this._session.project, rf).subscribe({
|
|
512
|
+
next: (aab) => {
|
|
513
|
+
nextAab = aab;
|
|
514
|
+
},
|
|
515
|
+
complete: () => {
|
|
516
|
+
this.liveLevelDisplayState = LiveLevelState.READY;
|
|
517
|
+
let fabDh = null;
|
|
518
|
+
if (nextAab) {
|
|
519
|
+
if (rf) {
|
|
520
|
+
fabDh = new AudioDataHolder(nextAab);
|
|
521
|
+
this.recorderCombiPane.setRecFileAudioData(rf, fabDh);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
else {
|
|
525
|
+
// Should actually be handled by the error resolver
|
|
526
|
+
this.statusMsg = 'Recording file could not be loaded.';
|
|
527
|
+
this.statusAlertType = 'error';
|
|
528
|
+
}
|
|
529
|
+
if (fabDh) {
|
|
530
|
+
// this.displayAudioClip could have been changed meanwhile, but the recorder unsubcribes before changing the item. Therefore, there should be no risk to set to wrong item
|
|
531
|
+
this.displayAudioClip = new AudioClip(fabDh);
|
|
532
|
+
//this.audioLoaded=true;
|
|
533
|
+
}
|
|
534
|
+
if (this._controlAudioPlayer) {
|
|
535
|
+
this._controlAudioPlayer.audioClip = this.displayAudioClip;
|
|
536
|
+
}
|
|
537
|
+
this.showRecording();
|
|
538
|
+
},
|
|
539
|
+
error: err => {
|
|
540
|
+
console.error("Could not load recording file from server: " + err);
|
|
541
|
+
this.liveLevelDisplayState = LiveLevelState.READY;
|
|
542
|
+
this.statusMsg = 'Recording file could not be loaded: ' + err;
|
|
543
|
+
this.statusAlertType = 'error';
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
this.audioFetchSubscription = this.recFileService.fetchRecordingFileAudioBuffer(this._session.project, rf).subscribe({
|
|
549
|
+
next: ab => {
|
|
550
|
+
this.liveLevelDisplayState = LiveLevelState.READY;
|
|
551
|
+
let fabDh = null;
|
|
552
|
+
if (ab) {
|
|
553
|
+
let abSrc = new AudioBufferSource(ab);
|
|
554
|
+
if (rf) {
|
|
555
|
+
fabDh = new AudioDataHolder(abSrc);
|
|
556
|
+
this.recorderCombiPane.setRecFileAudioData(rf, fabDh);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
console.error('Recording file could not be loaded.');
|
|
561
|
+
this.statusMsg = 'Recording file could not be loaded.';
|
|
562
|
+
this.statusAlertType = 'error';
|
|
563
|
+
}
|
|
564
|
+
if (fabDh) {
|
|
565
|
+
// this.displayAudioClip could have been changed meanwhile, but the recorder unsubcribes before changing the item. Therefore, there should be no risk to set to wrong item
|
|
566
|
+
this.displayAudioClip = new AudioClip(fabDh);
|
|
567
|
+
//this.audioLoaded=true;
|
|
568
|
+
//console.debug("set recording file: display audio clip from fetched audio buffer");
|
|
569
|
+
}
|
|
570
|
+
if (this._controlAudioPlayer) {
|
|
571
|
+
this._controlAudioPlayer.audioClip = this.displayAudioClip;
|
|
572
|
+
}
|
|
573
|
+
this.showRecording();
|
|
574
|
+
}, error: err => {
|
|
575
|
+
console.error("Could not load recording file from server: " + err);
|
|
576
|
+
this.liveLevelDisplayState = LiveLevelState.READY;
|
|
577
|
+
this.statusMsg = 'Recording file could not be loaded: ' + err;
|
|
578
|
+
this.statusAlertType = 'error';
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
this.statusMsg = 'Recording file could not be decoded. Audio context unavailable.';
|
|
585
|
+
this.statusAlertType = 'error';
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
//console.debug("recording file null");
|
|
591
|
+
this.displayAudioClip = null;
|
|
592
|
+
if (this._controlAudioPlayer) {
|
|
593
|
+
this._controlAudioPlayer.audioClip = null;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
this.showRecording();
|
|
597
|
+
}
|
|
598
|
+
get displayRecFile() {
|
|
599
|
+
return this._displayRecFile;
|
|
600
|
+
}
|
|
601
|
+
updateStartActionDisableState() {
|
|
602
|
+
this.transportActions.startAction.disabled = !(this.ac);
|
|
603
|
+
}
|
|
604
|
+
start() {
|
|
605
|
+
super.start();
|
|
606
|
+
this.recorderCombiPane.selectTop();
|
|
607
|
+
this.enableNavigation();
|
|
608
|
+
this.updateStartActionDisableState();
|
|
609
|
+
}
|
|
610
|
+
isRecording() {
|
|
611
|
+
return (this.status === 3 /* Status.RECORDING */ || this.status === 4 /* Status.STOPPING_STOP */);
|
|
612
|
+
}
|
|
613
|
+
isActive() {
|
|
614
|
+
return (!(this.status === 0 /* Status.BLOCKED */ || this.status === 1 /* Status.IDLE */ || this.status === 5 /* Status.ERROR */) || this.processingRecording || this.sessionService.uploadCount > 0);
|
|
615
|
+
}
|
|
616
|
+
updateWakeLock(dataSaved = this.dataSaved) {
|
|
617
|
+
//console.debug("Update wake lock: dataSaved: "+dataSaved+", not active: "+! this.isActive())
|
|
618
|
+
if (dataSaved && !this.isActive()) {
|
|
619
|
+
this.disableWakeLockCond();
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
updateNavigationActions() {
|
|
623
|
+
this.transportActions.fwdAction.disabled = this.navigationDisabled;
|
|
624
|
+
this.transportActions.bwdAction.disabled = this.navigationDisabled;
|
|
625
|
+
}
|
|
626
|
+
enableStartUserGesture() {
|
|
627
|
+
super.enableStartUserGesture();
|
|
628
|
+
this.transportActions.startAction.disabled = !(this.ac);
|
|
629
|
+
}
|
|
630
|
+
enableNavigation() {
|
|
631
|
+
this.navigationDisabled = false;
|
|
632
|
+
this.updateNavigationActions();
|
|
633
|
+
}
|
|
634
|
+
started() {
|
|
635
|
+
this.status = 3 /* Status.RECORDING */;
|
|
636
|
+
super.started();
|
|
637
|
+
this.statusAlertType = 'info';
|
|
638
|
+
this.statusMsg = 'Recording...';
|
|
639
|
+
let sessId = 0;
|
|
640
|
+
if (this._session) {
|
|
641
|
+
sessId = this._session.sessionId;
|
|
642
|
+
}
|
|
643
|
+
if (this.rfUuid) {
|
|
644
|
+
let rf = new RecordingFile(this.rfUuid, sessId, null);
|
|
645
|
+
rf._startedAsDateObj = this.startedDate;
|
|
646
|
+
if (rf._startedAsDateObj) {
|
|
647
|
+
rf.startedDate = rf._startedAsDateObj.toString();
|
|
648
|
+
}
|
|
649
|
+
this._recordingFile = rf;
|
|
650
|
+
}
|
|
651
|
+
let maxRecordingTimeMs = MAX_RECORDING_TIME_MS;
|
|
652
|
+
this.maxRecTimerId = window.setTimeout(() => {
|
|
653
|
+
this.stopRecordingMaxRec();
|
|
654
|
+
}, maxRecordingTimeMs);
|
|
655
|
+
this.maxRecTimerRunning = true;
|
|
656
|
+
this.transportActions.stopAction.disabled = false;
|
|
657
|
+
}
|
|
658
|
+
stopItem() {
|
|
659
|
+
this.transportActions.stopAction.disabled = true;
|
|
660
|
+
this.transportActions.nextAction.disabled = true;
|
|
661
|
+
this.status = 4 /* Status.STOPPING_STOP */;
|
|
662
|
+
this.stopRecording();
|
|
663
|
+
}
|
|
664
|
+
stopRecording() {
|
|
665
|
+
if (this.maxRecTimerRunning && this.maxRecTimerId != null) {
|
|
666
|
+
window.clearTimeout(this.maxRecTimerId);
|
|
667
|
+
this.maxRecTimerRunning = false;
|
|
668
|
+
}
|
|
669
|
+
if (this.ac) {
|
|
670
|
+
this.ac.stop();
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
stopRecordingMaxRec() {
|
|
674
|
+
this.maxRecTimerRunning = false;
|
|
675
|
+
this.status = 4 /* Status.STOPPING_STOP */;
|
|
676
|
+
if (this.ac) {
|
|
677
|
+
this.ac.stop();
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
stopped() {
|
|
681
|
+
this.updateStartActionDisableState();
|
|
682
|
+
this.transportActions.stopAction.disabled = true;
|
|
683
|
+
this.transportActions.nextAction.disabled = true;
|
|
684
|
+
this.transportActions.pauseAction.disabled = true;
|
|
685
|
+
this.statusAlertType = 'info';
|
|
686
|
+
this.statusMsg = 'Recorded.';
|
|
687
|
+
let ab = null;
|
|
688
|
+
if (this.ac) {
|
|
689
|
+
let adh = null;
|
|
690
|
+
let as = null;
|
|
691
|
+
if (this.ac) {
|
|
692
|
+
if (AudioStorageType.NET_CHUNKED === this.ac.audioStorageType) {
|
|
693
|
+
this.playStartAction.disabled = true;
|
|
694
|
+
//this.audioLoaded=false;
|
|
695
|
+
this.keepLiveLevel = true;
|
|
696
|
+
let rUUID = null;
|
|
697
|
+
let burl = null;
|
|
698
|
+
if (this._session) {
|
|
699
|
+
if (this._recordingFile) {
|
|
700
|
+
let rf = this._recordingFile;
|
|
701
|
+
rf.frames = this.ac.framesRecorded;
|
|
702
|
+
rUUID = rf.uuid;
|
|
703
|
+
//console.debug("stopped(): Set frames: "+rf.frames+" on rfId: "+this.displayRecFile?.recordingFileId);
|
|
704
|
+
burl = this.recFileService.audioFileUrl(this._session?.project, rf);
|
|
705
|
+
}
|
|
706
|
+
else if (this.session?.project) {
|
|
707
|
+
if (this.ac.recUUID) {
|
|
708
|
+
rUUID = this.ac.recUUID;
|
|
709
|
+
burl = this.recFileService.audioFileUrlByUUID(this.session.project, this.session.sessionId, rUUID);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
console.error("Could not create net audio buffer.");
|
|
714
|
+
}
|
|
715
|
+
if (burl) {
|
|
716
|
+
const sr = this.ac.currentSampleRate;
|
|
717
|
+
const chFl = sr * RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
718
|
+
//console.debug("stopped(): rfID: "+this._recordingFile?.recordingFileId+", net ab url: " + burl+", frames: "+this.ac.framesRecorded+", sample rate: "+sr);
|
|
719
|
+
let netAs = new NetAudioBuffer(this.recFileService, burl, this.ac.channelCount, sr, chFl, this.ac.framesRecorded, rUUID, chFl);
|
|
720
|
+
as = netAs;
|
|
721
|
+
if (this.uploadSet) {
|
|
722
|
+
this.uploadSet.onDone = (uploadSet) => {
|
|
723
|
+
//console.debug("upload set on done: Call ready provider.ready");
|
|
724
|
+
netAs.ready();
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
else if (AudioStorageType.MEM_ENTIRE_AUTO_NET_CHUNKED === this.ac.audioStorageType || AudioStorageType.MEM_CHUNKED_AUTO_NET_CHUNKED === this.ac.audioStorageType) {
|
|
731
|
+
if (AudioStorageType.MEM_ENTIRE_AUTO_NET_CHUNKED === this.ac.audioStorageType) {
|
|
732
|
+
const acAb = this.ac.audioBuffer();
|
|
733
|
+
if (acAb) {
|
|
734
|
+
as = new AudioBufferSource(acAb);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
if (AudioStorageType.MEM_CHUNKED_AUTO_NET_CHUNKED === this.ac.audioStorageType) {
|
|
738
|
+
as = this.ac.audioBufferArray();
|
|
739
|
+
}
|
|
740
|
+
if (!as) {
|
|
741
|
+
this.playStartAction.disabled = true;
|
|
742
|
+
this.keepLiveLevel = true;
|
|
743
|
+
let rUUID = null;
|
|
744
|
+
let burl = null;
|
|
745
|
+
if (this._session) {
|
|
746
|
+
if (this._recordingFile) {
|
|
747
|
+
let rf = this._recordingFile;
|
|
748
|
+
rf.frames = this.ac.framesRecorded;
|
|
749
|
+
rUUID = rf.uuid;
|
|
750
|
+
//console.debug("stopped(): Set frames: "+rf.frames+" on rfId: "+this.displayRecFile?.recordingFileId);
|
|
751
|
+
burl = this.recFileService.audioFileUrl(this._session?.project, rf);
|
|
752
|
+
}
|
|
753
|
+
else if (this.session?.project) {
|
|
754
|
+
if (this.ac.recUUID) {
|
|
755
|
+
rUUID = this.ac.recUUID;
|
|
756
|
+
burl = this.recFileService.audioFileUrlByUUID(this.session.project, this.session.sessionId, rUUID);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
else {
|
|
760
|
+
console.error("Could not create net audio buffer.");
|
|
761
|
+
}
|
|
762
|
+
if (burl) {
|
|
763
|
+
const sr = this.ac.currentSampleRate;
|
|
764
|
+
const chFl = sr * RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
765
|
+
//console.debug("stopped(): rfID: "+this._recordingFile?.recordingFileId+", net ab url: " + burl+", frames: "+this.ac.framesRecorded+", sample rate: "+sr);
|
|
766
|
+
let netAs = new NetAudioBuffer(this.recFileService, burl, this.ac.channelCount, sr, chFl, this.ac.framesRecorded, rUUID, chFl);
|
|
767
|
+
as = netAs;
|
|
768
|
+
if (this.uploadSet) {
|
|
769
|
+
this.uploadSet.onDone = (uploadSet) => {
|
|
770
|
+
//console.debug("upload set on done: Call ready provider.ready");
|
|
771
|
+
netAs.ready();
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
else if (AudioStorageType.DB_CHUNKED === this.ac.audioStorageType) {
|
|
779
|
+
as = this.ac.inddbAudioBufferArray();
|
|
780
|
+
}
|
|
781
|
+
else if (AudioStorageType.MEM_CHUNKED === this.ac.audioStorageType) {
|
|
782
|
+
as = this.ac.audioBufferArray();
|
|
783
|
+
}
|
|
784
|
+
else {
|
|
785
|
+
ab = this.ac.audioBuffer();
|
|
786
|
+
if (ab) {
|
|
787
|
+
as = new AudioBufferSource(ab);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
if (as) {
|
|
791
|
+
adh = new AudioDataHolder(as);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
let sessId = 0;
|
|
795
|
+
if (this._session) {
|
|
796
|
+
sessId = this._session.sessionId;
|
|
797
|
+
}
|
|
798
|
+
if (this._recordingFile) {
|
|
799
|
+
//this._recordingFile.samplerate=this.ac.currentSampleRate;
|
|
800
|
+
// Use an own reference since the writing of the wave file is asynchronous and this._recordingFile might already contain the next recording
|
|
801
|
+
let rf = this._recordingFile;
|
|
802
|
+
RecordingFileUtils.setAudioData(rf, adh);
|
|
803
|
+
this.recorderCombiPane.addRecFile(rf);
|
|
804
|
+
// Upload if upload enabled and not in chunked upload mode
|
|
805
|
+
if (this.enableUploadRecordings && this._uploadChunkSizeSeconds === null && AudioStorageType.MEM_ENTIRE === this._clientAudioStorageType && rf != null && ab != null) {
|
|
806
|
+
let apiEndPoint = '';
|
|
807
|
+
if (this.config && this.config.apiEndPoint) {
|
|
808
|
+
apiEndPoint = this.config.apiEndPoint;
|
|
809
|
+
}
|
|
810
|
+
if (apiEndPoint !== '') {
|
|
811
|
+
apiEndPoint = apiEndPoint + '/';
|
|
812
|
+
}
|
|
813
|
+
let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
|
|
814
|
+
let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.uuid;
|
|
815
|
+
// convert asynchronously to 16-bit integer PCM
|
|
816
|
+
// TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
|
|
817
|
+
// TODO duplicate conversion for manual download
|
|
818
|
+
this.processingRecording = true;
|
|
819
|
+
let ww = new WavWriter();
|
|
820
|
+
ww.writeAsync(ab, (wavFile) => {
|
|
821
|
+
this.postRecordingMultipart(wavFile, recUrl, rf);
|
|
822
|
+
this.processingRecording = false;
|
|
823
|
+
this.updateWakeLock();
|
|
824
|
+
this.changeDetectorRef.detectChanges();
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
this.displayRecFile = this._recordingFile;
|
|
830
|
+
this.status = 1 /* Status.IDLE */;
|
|
831
|
+
this.navigationDisabled = false;
|
|
832
|
+
this.updateNavigationActions();
|
|
833
|
+
this.updateWakeLock();
|
|
834
|
+
this.changeDetectorRef.detectChanges();
|
|
835
|
+
}
|
|
836
|
+
error(msg = 'An unknown error occured during recording.', advice = 'Please retry.') {
|
|
837
|
+
this.status = 5 /* Status.ERROR */;
|
|
838
|
+
super.error(msg, advice);
|
|
839
|
+
this.updateNavigationActions();
|
|
840
|
+
this.updateStartActionDisableState();
|
|
841
|
+
}
|
|
842
|
+
postRecordingMultipart(wavFile, recUrl, rf) {
|
|
843
|
+
let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
|
|
844
|
+
let fd = new FormData();
|
|
845
|
+
if (rf.uuid) {
|
|
846
|
+
fd.set('uuid', rf.uuid);
|
|
847
|
+
}
|
|
848
|
+
if (rf.session !== null) {
|
|
849
|
+
fd.set('sessionId', rf.session.toString());
|
|
850
|
+
}
|
|
851
|
+
if (rf._startedAsDateObj) {
|
|
852
|
+
fd.set('startedDate', rf._startedAsDateObj.toJSON());
|
|
853
|
+
}
|
|
854
|
+
fd.set('audio', wavBlob);
|
|
855
|
+
let ul = new Upload(fd, recUrl, rf);
|
|
856
|
+
this.uploader.queueUpload(ul);
|
|
857
|
+
}
|
|
858
|
+
postChunkAudioBuffer(audioBuffer, chunkIdx) {
|
|
859
|
+
this.processingRecording = true;
|
|
860
|
+
let ww = new WavWriter();
|
|
861
|
+
let sessionsUrl = this.sessionsBaseUrl();
|
|
862
|
+
let recUrl = sessionsUrl + '/' + this.session?.sessionId + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/' + chunkIdx;
|
|
863
|
+
let rf = this._recordingFile;
|
|
864
|
+
// The upload holder is required to add the upload now to the upload set. The real upload is created async in postrecording and the upload set is already complete at that time.
|
|
865
|
+
let ulh = new UploadHolder();
|
|
866
|
+
if (this.uploadSet) {
|
|
867
|
+
this.uploadSet.add(ulh);
|
|
868
|
+
}
|
|
869
|
+
ww.writeAsync(audioBuffer, (wavFile) => {
|
|
870
|
+
this.postRecording(wavFile, recUrl, rf, ulh);
|
|
871
|
+
this.processingRecording = false;
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
stop() {
|
|
875
|
+
if (this.ac) {
|
|
876
|
+
this.ac.close();
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
updateControlPlaybackPosition() {
|
|
880
|
+
if (this._controlAudioPlayer) {
|
|
881
|
+
const ppFrames = this._controlAudioPlayer.playPositionFrames;
|
|
882
|
+
if (ppFrames !== null) {
|
|
883
|
+
this.recorderCombiPane.audioDisplay.playFramePosition = ppFrames;
|
|
884
|
+
this.liveLevelDisplay.playFramePosition = ppFrames;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
audioPlayerUpdate(e) {
|
|
889
|
+
if (EventType.READY === e.type) {
|
|
890
|
+
}
|
|
891
|
+
else if (EventType.STARTED === e.type) {
|
|
892
|
+
//this.status = 'Playback...';
|
|
893
|
+
this.updateTimerId = window.setInterval(() => this.updateControlPlaybackPosition(), 50);
|
|
894
|
+
}
|
|
895
|
+
else if (EventType.ENDED === e.type) {
|
|
896
|
+
//.status='Ready.';
|
|
897
|
+
window.clearInterval(this.updateTimerId);
|
|
898
|
+
}
|
|
899
|
+
if (!this.destroyed) {
|
|
900
|
+
this.changeDetectorRef.detectChanges();
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: AudioRecorder, deps: [{ token: i1.BreakpointObserver }, { token: i0.ChangeDetectorRef }, { token: i0.Renderer2 }, { token: i2.MatDialog }, { token: i3.SessionService }, { token: i4.RecordingService }, { token: i5.SpeechRecorderUploader }, { token: SPEECHRECORDER_CONFIG }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
904
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.0.6", type: AudioRecorder, selector: "app-audiorecorder", inputs: { projectName: "projectName", dataSaved: "dataSaved" }, host: { listeners: { "window:keypress": "onKeyPress($event)", "window:keydown": "onKeyDown($event)" } }, providers: [SessionService], viewQueries: [{ propertyName: "recorderCombiPane", first: true, predicate: RecorderCombiPane, descendants: true, static: true }, { propertyName: "liveLevelDisplay", first: true, predicate: LevelBar, descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: `
|
|
905
|
+
<app-warningbar [show]="isTestSession()" warningText="Test recording only!"></app-warningbar>
|
|
906
|
+
<app-warningbar [show]="isDefaultAudioTestSession()"
|
|
907
|
+
warningText="This test uses default audio device! Regular sessions may require a particular audio device (microphone)!"></app-warningbar>
|
|
908
|
+
<app-recordercombipane (selectedRecordingFileChanged)="selectRecordingFile($event)"
|
|
909
|
+
[audioSignalCollapsed]="audioSignalCollapsed"
|
|
910
|
+
[selectedRecordingFile]="displayRecFile"
|
|
911
|
+
[selectDisabled]="isActive()"
|
|
912
|
+
[displayAudioClip]="displayAudioClip"
|
|
913
|
+
[playStartAction]="controlAudioPlayer?.startAction"
|
|
914
|
+
[playStopAction]="controlAudioPlayer?.stopAction"
|
|
915
|
+
[playSelectionAction]="controlAudioPlayer?.startSelectionAction"
|
|
916
|
+
[autoPlayOnSelectToggleAction]="controlAudioPlayer?.autoPlayOnSelectToggleAction"
|
|
917
|
+
></app-recordercombipane>
|
|
918
|
+
|
|
919
|
+
<div [class]="{audioStatusDisplay:!screenXs,audioStatusDisplayXs:screenXs}">
|
|
920
|
+
<audio-levelbar style="flex:1 0 1%" [streamingMode]="isRecording() || keepLiveLevel" [state]="liveLevelDisplayState"
|
|
921
|
+
[displayLevelInfos]="displayAudioClip?.levelInfos"></audio-levelbar>
|
|
922
|
+
<div style="display:flex;flex-direction: row">
|
|
923
|
+
<spr-recordingitemcontrols style="flex:10 0 1px"
|
|
924
|
+
[disableAudioDetails]="disableAudioDetails"
|
|
925
|
+
[audioLoaded]="audioLoaded"
|
|
926
|
+
[playStartAction]="controlAudioPlayer?.startAction"
|
|
927
|
+
[playStopAction]="controlAudioPlayer?.stopAction"
|
|
928
|
+
[peakDbLvl]="peakLevelInDb"
|
|
929
|
+
[agc]="this.ac?.agcStatus"
|
|
930
|
+
(onShowRecordingDetails)="audioSignalCollapsed=!audioSignalCollapsed">
|
|
931
|
+
</spr-recordingitemcontrols>
|
|
932
|
+
|
|
933
|
+
<app-uploadstatus *ngIf="screenXs && enableUploadRecordings" class="ricontrols dark" style="flex:0 0 0"
|
|
934
|
+
[value]="uploadProgress"
|
|
935
|
+
[status]="uploadStatus" [awaitNewUpload]="processingRecording"></app-uploadstatus>
|
|
936
|
+
<app-wakelockindicator *ngIf="screenXs" class="ricontrols dark" style="flex:0 0 0" [screenLocked]="screenLocked"></app-wakelockindicator>
|
|
937
|
+
<app-readystateindicator *ngIf="screenXs" class="ricontrols dark" style="flex:0 0 0"
|
|
938
|
+
[ready]="dataSaved && !isActive()"></app-readystateindicator>
|
|
939
|
+
</div>
|
|
940
|
+
</div>
|
|
941
|
+
<div #controlpanel class="controlpanel">
|
|
942
|
+
<app-sprstatusdisplay *ngIf="!screenXs" style="flex:0 1 30%;" [statusMsg]="statusMsg" [statusAlertType]="statusAlertType"
|
|
943
|
+
[statusWaiting]="statusWaiting"
|
|
944
|
+
class="hidden-xs"></app-sprstatusdisplay>
|
|
945
|
+
<div [class.startstop]="!screenXs" [class.startstopscreenxs]="screenXs">
|
|
946
|
+
<div style="align-content: center">
|
|
947
|
+
<button (click)="startStopPerform()" [disabled]="startDisabled() && stopDisabled()" mat-raised-button class="bigbutton">
|
|
948
|
+
<mat-icon class="bigbuttonicon" [style.color]="startStopNextIconColor()">{{startStopNextIconName()}}</mat-icon>
|
|
949
|
+
<span class="bigbuttontext">{{startStopNextName()}}</span>
|
|
950
|
+
</button>
|
|
951
|
+
</div>
|
|
952
|
+
</div>
|
|
953
|
+
<div style="flex:0 1 30%;display:flex;justify-items: flex-end;justify-content:flex-end" >
|
|
954
|
+
<app-uploadstatus *ngIf="!screenXs && enableUploadRecordings" class="ricontrols"
|
|
955
|
+
[value]="uploadProgress"
|
|
956
|
+
[status]="uploadStatus" [awaitNewUpload]="processingRecording"></app-uploadstatus>
|
|
957
|
+
<app-wakelockindicator *ngIf="!screenXs" class="ricontrols" [screenLocked]="screenLocked"></app-wakelockindicator>
|
|
958
|
+
<app-readystateindicator *ngIf="!screenXs" class="ricontrols"
|
|
959
|
+
[ready]="dataSaved && !isActive()"></app-readystateindicator>
|
|
960
|
+
</div>
|
|
961
|
+
</div>
|
|
962
|
+
`, isInline: true, styles: [":host{flex:2;background:lightgrey;display:flex;flex-direction:column;margin:0;padding:0;height:100%;min-height:0px;overflow:hidden}\n", ".ricontrols{padding:4px;box-sizing:border-box;height:100%}\n", ".dark{background:darkgray}\n", ".controlpanel{display:flex;flex-direction:row;align-content:center;align-items:center;margin:0;padding:20px;min-height:min-content}\n", ".startstop{width:100%;flex:1 0 30%;align-items:center;text-align:center;align-content:center}\n", ".startstopscreenxs{width:100%;flex:1 0 100%;align-items:center;text-align:center;align-content:center}\n", ".bigbutton{vertical-align:middle;overflow:hidden;text-overflow:clip;white-space:nowrap;letter-spacing:normal;min-width:70px;min-height:50px;border-radius:20px}\n", ".bigbuttonicon{min-width:50px;min-height:50px;font-size:50px}\n", ".bigbuttontext{font-weight:bolder;font-size:14px;vertical-align:middle}\n", ".audioStatusDisplay{display:flex;flex-direction:row;height:100px;min-height:100px}\n", ".audioStatusDisplayXs{display:flex;flex-direction:column;height:125px;min-height:125px}\n"], dependencies: [{ kind: "directive", type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i8.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i9.LevelBar, selector: "audio-levelbar", inputs: ["streamingMode", "displayLevelInfos", "state"] }, { kind: "component", type: i10.StatusDisplay, selector: "app-sprstatusdisplay", inputs: ["statusAlertType", "statusMsg", "statusWaiting"] }, { kind: "component", type: i11.RecordingItemControls, selector: "spr-recordingitemcontrols", inputs: ["audioSignalCollapsed", "enableDownload", "peakDbLvl", "agc", "disableAudioDetails", "audioLoaded", "playStartAction", "playStopAction", "displayLevelInfos"], outputs: ["onShowRecordingDetails", "onDownloadRecording"] }, { kind: "component", type: i10.UploadStatus, selector: "app-uploadstatus", inputs: ["value", "awaitNewUpload", "status"] }, { kind: "component", type: i10.WakeLockIndicator, selector: "app-wakelockindicator", inputs: ["screenLocked"] }, { kind: "component", type: i10.ReadyStateIndicator, selector: "app-readystateindicator", inputs: ["ready"] }, { kind: "component", type: i12.WarningBar, selector: "app-warningbar", inputs: ["warningText", "show"] }, { kind: "component", type: i13.RecorderCombiPane, selector: "app-recordercombipane", inputs: ["selectDisabled", "selectedRecordingFile", "audioSignalCollapsed", "displayAudioClip", "playStartAction", "playSelectionAction", "autoPlayOnSelectToggleAction", "playStopAction"], outputs: ["selectedRecordingFileChanged"] }] }); }
|
|
963
|
+
}
|
|
964
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: AudioRecorder, decorators: [{
|
|
965
|
+
type: Component,
|
|
966
|
+
args: [{ selector: 'app-audiorecorder', providers: [SessionService], template: `
|
|
967
|
+
<app-warningbar [show]="isTestSession()" warningText="Test recording only!"></app-warningbar>
|
|
968
|
+
<app-warningbar [show]="isDefaultAudioTestSession()"
|
|
969
|
+
warningText="This test uses default audio device! Regular sessions may require a particular audio device (microphone)!"></app-warningbar>
|
|
970
|
+
<app-recordercombipane (selectedRecordingFileChanged)="selectRecordingFile($event)"
|
|
971
|
+
[audioSignalCollapsed]="audioSignalCollapsed"
|
|
972
|
+
[selectedRecordingFile]="displayRecFile"
|
|
973
|
+
[selectDisabled]="isActive()"
|
|
974
|
+
[displayAudioClip]="displayAudioClip"
|
|
975
|
+
[playStartAction]="controlAudioPlayer?.startAction"
|
|
976
|
+
[playStopAction]="controlAudioPlayer?.stopAction"
|
|
977
|
+
[playSelectionAction]="controlAudioPlayer?.startSelectionAction"
|
|
978
|
+
[autoPlayOnSelectToggleAction]="controlAudioPlayer?.autoPlayOnSelectToggleAction"
|
|
979
|
+
></app-recordercombipane>
|
|
980
|
+
|
|
981
|
+
<div [class]="{audioStatusDisplay:!screenXs,audioStatusDisplayXs:screenXs}">
|
|
982
|
+
<audio-levelbar style="flex:1 0 1%" [streamingMode]="isRecording() || keepLiveLevel" [state]="liveLevelDisplayState"
|
|
983
|
+
[displayLevelInfos]="displayAudioClip?.levelInfos"></audio-levelbar>
|
|
984
|
+
<div style="display:flex;flex-direction: row">
|
|
985
|
+
<spr-recordingitemcontrols style="flex:10 0 1px"
|
|
986
|
+
[disableAudioDetails]="disableAudioDetails"
|
|
987
|
+
[audioLoaded]="audioLoaded"
|
|
988
|
+
[playStartAction]="controlAudioPlayer?.startAction"
|
|
989
|
+
[playStopAction]="controlAudioPlayer?.stopAction"
|
|
990
|
+
[peakDbLvl]="peakLevelInDb"
|
|
991
|
+
[agc]="this.ac?.agcStatus"
|
|
992
|
+
(onShowRecordingDetails)="audioSignalCollapsed=!audioSignalCollapsed">
|
|
993
|
+
</spr-recordingitemcontrols>
|
|
994
|
+
|
|
995
|
+
<app-uploadstatus *ngIf="screenXs && enableUploadRecordings" class="ricontrols dark" style="flex:0 0 0"
|
|
996
|
+
[value]="uploadProgress"
|
|
997
|
+
[status]="uploadStatus" [awaitNewUpload]="processingRecording"></app-uploadstatus>
|
|
998
|
+
<app-wakelockindicator *ngIf="screenXs" class="ricontrols dark" style="flex:0 0 0" [screenLocked]="screenLocked"></app-wakelockindicator>
|
|
999
|
+
<app-readystateindicator *ngIf="screenXs" class="ricontrols dark" style="flex:0 0 0"
|
|
1000
|
+
[ready]="dataSaved && !isActive()"></app-readystateindicator>
|
|
1001
|
+
</div>
|
|
1002
|
+
</div>
|
|
1003
|
+
<div #controlpanel class="controlpanel">
|
|
1004
|
+
<app-sprstatusdisplay *ngIf="!screenXs" style="flex:0 1 30%;" [statusMsg]="statusMsg" [statusAlertType]="statusAlertType"
|
|
1005
|
+
[statusWaiting]="statusWaiting"
|
|
1006
|
+
class="hidden-xs"></app-sprstatusdisplay>
|
|
1007
|
+
<div [class.startstop]="!screenXs" [class.startstopscreenxs]="screenXs">
|
|
1008
|
+
<div style="align-content: center">
|
|
1009
|
+
<button (click)="startStopPerform()" [disabled]="startDisabled() && stopDisabled()" mat-raised-button class="bigbutton">
|
|
1010
|
+
<mat-icon class="bigbuttonicon" [style.color]="startStopNextIconColor()">{{startStopNextIconName()}}</mat-icon>
|
|
1011
|
+
<span class="bigbuttontext">{{startStopNextName()}}</span>
|
|
1012
|
+
</button>
|
|
1013
|
+
</div>
|
|
1014
|
+
</div>
|
|
1015
|
+
<div style="flex:0 1 30%;display:flex;justify-items: flex-end;justify-content:flex-end" >
|
|
1016
|
+
<app-uploadstatus *ngIf="!screenXs && enableUploadRecordings" class="ricontrols"
|
|
1017
|
+
[value]="uploadProgress"
|
|
1018
|
+
[status]="uploadStatus" [awaitNewUpload]="processingRecording"></app-uploadstatus>
|
|
1019
|
+
<app-wakelockindicator *ngIf="!screenXs" class="ricontrols" [screenLocked]="screenLocked"></app-wakelockindicator>
|
|
1020
|
+
<app-readystateindicator *ngIf="!screenXs" class="ricontrols"
|
|
1021
|
+
[ready]="dataSaved && !isActive()"></app-readystateindicator>
|
|
1022
|
+
</div>
|
|
1023
|
+
</div>
|
|
1024
|
+
`, styles: [":host{flex:2;background:lightgrey;display:flex;flex-direction:column;margin:0;padding:0;height:100%;min-height:0px;overflow:hidden}\n", ".ricontrols{padding:4px;box-sizing:border-box;height:100%}\n", ".dark{background:darkgray}\n", ".controlpanel{display:flex;flex-direction:row;align-content:center;align-items:center;margin:0;padding:20px;min-height:min-content}\n", ".startstop{width:100%;flex:1 0 30%;align-items:center;text-align:center;align-content:center}\n", ".startstopscreenxs{width:100%;flex:1 0 100%;align-items:center;text-align:center;align-content:center}\n", ".bigbutton{vertical-align:middle;overflow:hidden;text-overflow:clip;white-space:nowrap;letter-spacing:normal;min-width:70px;min-height:50px;border-radius:20px}\n", ".bigbuttonicon{min-width:50px;min-height:50px;font-size:50px}\n", ".bigbuttontext{font-weight:bolder;font-size:14px;vertical-align:middle}\n", ".audioStatusDisplay{display:flex;flex-direction:row;height:100px;min-height:100px}\n", ".audioStatusDisplayXs{display:flex;flex-direction:column;height:125px;min-height:125px}\n"] }]
|
|
1025
|
+
}], ctorParameters: () => [{ type: i1.BreakpointObserver }, { type: i0.ChangeDetectorRef }, { type: i0.Renderer2 }, { type: i2.MatDialog }, { type: i3.SessionService }, { type: i4.RecordingService }, { type: i5.SpeechRecorderUploader }, { type: i14.SpeechRecorderConfig, decorators: [{
|
|
1026
|
+
type: Inject,
|
|
1027
|
+
args: [SPEECHRECORDER_CONFIG]
|
|
1028
|
+
}] }], propDecorators: { projectName: [{
|
|
1029
|
+
type: Input
|
|
1030
|
+
}], recorderCombiPane: [{
|
|
1031
|
+
type: ViewChild,
|
|
1032
|
+
args: [RecorderCombiPane, { static: true }]
|
|
1033
|
+
}], liveLevelDisplay: [{
|
|
1034
|
+
type: ViewChild,
|
|
1035
|
+
args: [LevelBar, { static: true }]
|
|
1036
|
+
}], dataSaved: [{
|
|
1037
|
+
type: Input
|
|
1038
|
+
}], onKeyPress: [{
|
|
1039
|
+
type: HostListener,
|
|
1040
|
+
args: ['window:keypress', ['$event']]
|
|
1041
|
+
}], onKeyDown: [{
|
|
1042
|
+
type: HostListener,
|
|
1043
|
+
args: ['window:keydown', ['$event']]
|
|
1044
|
+
}] } });
|
|
1045
|
+
export class AudioRecorderComponent extends RecorderComponent {
|
|
1046
|
+
constructor(route, router, changeDetectorRef, sessionService, projectService, uploader) {
|
|
1047
|
+
super(uploader);
|
|
1048
|
+
this.route = route;
|
|
1049
|
+
this.router = router;
|
|
1050
|
+
this.changeDetectorRef = changeDetectorRef;
|
|
1051
|
+
this.sessionService = sessionService;
|
|
1052
|
+
this.projectService = projectService;
|
|
1053
|
+
this.uploader = uploader;
|
|
1054
|
+
}
|
|
1055
|
+
ngOnInit() {
|
|
1056
|
+
this.controlAudioPlayer = new AudioPlayer(this.ar);
|
|
1057
|
+
this.ar.controlAudioPlayer = this.controlAudioPlayer;
|
|
1058
|
+
//TODO Duplicate code in SpeechRecorderComponent
|
|
1059
|
+
window.addEventListener('beforeunload', (e) => {
|
|
1060
|
+
console.debug("Before page unload event");
|
|
1061
|
+
if (this.ready()) {
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
else {
|
|
1065
|
+
// all this attempts to customize the message do not work anymore (for security reasons)!!
|
|
1066
|
+
const message = "Please do not leave the page, until all recordings are uploaded!";
|
|
1067
|
+
alert(message);
|
|
1068
|
+
e = e || window.event;
|
|
1069
|
+
if (e) {
|
|
1070
|
+
e.returnValue = message;
|
|
1071
|
+
e.cancelBubble = true;
|
|
1072
|
+
if (e.stopPropagation) {
|
|
1073
|
+
e.stopPropagation();
|
|
1074
|
+
}
|
|
1075
|
+
if (e.preventDefault) {
|
|
1076
|
+
e.preventDefault();
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
return message;
|
|
1080
|
+
}
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
ngAfterViewInit() {
|
|
1084
|
+
// TODO call prepare !!
|
|
1085
|
+
this.uploader.listener = (ue) => {
|
|
1086
|
+
this.uploadUpdate(ue);
|
|
1087
|
+
};
|
|
1088
|
+
this.route.queryParams.subscribe((params) => {
|
|
1089
|
+
if (params['sessionId']) {
|
|
1090
|
+
this.fetchSession(params['sessionId']);
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1093
|
+
this.route.params.subscribe((params) => {
|
|
1094
|
+
let routeParamsId = params['id'];
|
|
1095
|
+
if (routeParamsId) {
|
|
1096
|
+
this.fetchSession(routeParamsId);
|
|
1097
|
+
}
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
ngOnDestroy() {
|
|
1101
|
+
//super.ngOnDestroy();
|
|
1102
|
+
}
|
|
1103
|
+
get screenLocked() {
|
|
1104
|
+
return this.ar.screenLocked;
|
|
1105
|
+
}
|
|
1106
|
+
fetchSession(sessionId) {
|
|
1107
|
+
let sessObs = this.sessionService.sessionObserver(sessionId);
|
|
1108
|
+
if (sessObs) {
|
|
1109
|
+
sessObs.subscribe({
|
|
1110
|
+
next: (sess) => {
|
|
1111
|
+
this.ar.statusAlertType = 'info';
|
|
1112
|
+
this.ar.statusMsg = 'Received session info.';
|
|
1113
|
+
this.ar.statusWaiting = false;
|
|
1114
|
+
this.session = sess;
|
|
1115
|
+
this.ar.session = sess;
|
|
1116
|
+
if (sess.project) {
|
|
1117
|
+
//console.debug("Session associated project: "+sess.project)
|
|
1118
|
+
this.projectService.projectObservable(sess.project).subscribe({
|
|
1119
|
+
next: (project) => {
|
|
1120
|
+
this.ar.project = project;
|
|
1121
|
+
this.ar.fetchRecordings(sess);
|
|
1122
|
+
}, error: (reason) => {
|
|
1123
|
+
this.ar.statusMsg = reason;
|
|
1124
|
+
this.ar.statusAlertType = 'error';
|
|
1125
|
+
this.ar.statusWaiting = false;
|
|
1126
|
+
console.error("Error fetching project config: " + reason);
|
|
1127
|
+
}
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
else {
|
|
1131
|
+
console.info("Session has no associated project. Using default configuration.");
|
|
1132
|
+
}
|
|
1133
|
+
},
|
|
1134
|
+
error: (reason) => {
|
|
1135
|
+
this.ar.statusMsg = reason;
|
|
1136
|
+
this.ar.statusAlertType = 'error';
|
|
1137
|
+
this.ar.statusWaiting = false;
|
|
1138
|
+
console.error("Error fetching session " + reason);
|
|
1139
|
+
}
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
uploadUpdate(ue) {
|
|
1144
|
+
let upStatus = ue.status;
|
|
1145
|
+
this.dataSaved = (UploaderStatus.DONE === upStatus);
|
|
1146
|
+
let percentUpl = ue.percentDone();
|
|
1147
|
+
if (UploaderStatus.ERR === upStatus) {
|
|
1148
|
+
this.ar.uploadStatus = 'warn';
|
|
1149
|
+
}
|
|
1150
|
+
else {
|
|
1151
|
+
if (percentUpl < 50) {
|
|
1152
|
+
this.ar.uploadStatus = 'accent';
|
|
1153
|
+
}
|
|
1154
|
+
else {
|
|
1155
|
+
this.ar.uploadStatus = 'success';
|
|
1156
|
+
}
|
|
1157
|
+
this.ar.uploadProgress = percentUpl;
|
|
1158
|
+
}
|
|
1159
|
+
this.ar.updateWakeLock(this.dataSaved);
|
|
1160
|
+
this.changeDetectorRef.detectChanges();
|
|
1161
|
+
}
|
|
1162
|
+
ready() {
|
|
1163
|
+
return this.dataSaved && !this.ar.isActive();
|
|
1164
|
+
}
|
|
1165
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: AudioRecorderComponent, deps: [{ token: i15.ActivatedRoute }, { token: i15.Router }, { token: i0.ChangeDetectorRef }, { token: i3.SessionService }, { token: i16.ProjectService }, { token: i5.SpeechRecorderUploader }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1166
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.0.6", type: AudioRecorderComponent, selector: "app-audiorecorder-comp", providers: [SessionService], viewQueries: [{ propertyName: "ar", first: true, predicate: AudioRecorder, descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: `
|
|
1167
|
+
<app-audiorecorder [projectName]="_project?.name" [dataSaved]="dataSaved"></app-audiorecorder>
|
|
1168
|
+
`, isInline: true, styles: [":host{flex:2;display:flex;height:100%;flex-direction:column;min-height:0}\n"], dependencies: [{ kind: "component", type: AudioRecorder, selector: "app-audiorecorder", inputs: ["projectName", "dataSaved"] }] }); }
|
|
1169
|
+
}
|
|
1170
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: AudioRecorderComponent, decorators: [{
|
|
1171
|
+
type: Component,
|
|
1172
|
+
args: [{ selector: 'app-audiorecorder-comp', providers: [SessionService], template: `
|
|
1173
|
+
<app-audiorecorder [projectName]="_project?.name" [dataSaved]="dataSaved"></app-audiorecorder>
|
|
1174
|
+
`, styles: [":host{flex:2;display:flex;height:100%;flex-direction:column;min-height:0}\n"] }]
|
|
1175
|
+
}], ctorParameters: () => [{ type: i15.ActivatedRoute }, { type: i15.Router }, { type: i0.ChangeDetectorRef }, { type: i3.SessionService }, { type: i16.ProjectService }, { type: i5.SpeechRecorderUploader }], propDecorators: { ar: [{
|
|
1176
|
+
type: ViewChild,
|
|
1177
|
+
args: [AudioRecorder, { static: true }]
|
|
1178
|
+
}] } });
|
|
1179
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXVkaW9yZWNvcmRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3NwZWVjaHJlY29yZGVybmcvc3JjL2xpYi9zcGVlY2hyZWNvcmRlci9zZXNzaW9uL2F1ZGlvcmVjb3JkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFDLFlBQVksRUFBdUIsTUFBTSw2QkFBNkIsQ0FBQztBQUMvRSxPQUFPLEVBQUMsV0FBVyxFQUFvQixTQUFTLEVBQUMsTUFBTSw2QkFBNkIsQ0FBQTtBQUNwRixPQUFPLEVBQUMsU0FBUyxFQUFDLE1BQU0sNEJBQTRCLENBQUE7QUFDcEQsT0FBTyxFQUFDLGFBQWEsRUFBRSxrQkFBa0IsRUFBQyxNQUFNLGNBQWMsQ0FBQTtBQUM5RCxPQUFPLEVBR0wsU0FBUyxFQUNULFlBQVksRUFDWixNQUFNLEVBQ04sS0FBSyxFQUlMLFNBQVMsRUFDVixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUMsY0FBYyxFQUFDLE1BQU0sbUJBQW1CLENBQUM7QUFHakQsT0FBTyxFQUFDLHFCQUFxQixFQUF1QixNQUFNLGtCQUFrQixDQUFDO0FBRTdFLE9BQU8sRUFBQyxnQkFBZ0IsRUFBVyxXQUFXLEVBQUMsTUFBTSxvQkFBb0IsQ0FBQztBQUMxRSxPQUFPLEVBQUMsYUFBYSxFQUFDLE1BQU0seUJBQXlCLENBQUM7QUFDdEQsT0FBTyxFQUFDLGdCQUFnQixFQUFDLE1BQU0sa0NBQWtDLENBQUM7QUFHbEUsT0FBTyxFQUFDLFNBQVMsRUFBQyxNQUFNLHVCQUF1QixDQUFDO0FBRWhELE9BQU8sRUFBQyxNQUFNLEVBQUUsY0FBYyxFQUE2QixZQUFZLEVBQUMsTUFBTSxvQkFBb0IsQ0FBQztBQUduRyxPQUFPLEVBQUMsUUFBUSxFQUFFLEtBQUssSUFBSSxjQUFjLEVBQUMsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRSxPQUFPLEVBQUMsaUJBQWlCLEVBQUMsTUFBTSx1QkFBdUIs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LFNBQVM7K0JBQ0Usd0JBQXdCLGFBQ3ZCLENBQUMsY0FBYyxDQUFDLFlBQ2pCOztHQUVUOzBPQXFCMkMsRUFBRTtzQkFBN0MsU0FBUzt1QkFBQyxhQUFhLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtBdWRpb0NhcHR1cmUsIEF1ZGlvQ2FwdHVyZUxpc3RlbmVyfSBmcm9tICcuLi8uLi9hdWRpby9jYXB0dXJlL2NhcHR1cmUnO1xyXG5pbXBvcnQge0F1ZGlvUGxheWVyLCBBdWRpb1BsYXllckV2ZW50LCBFdmVudFR5cGV9IGZyb20gJy4uLy4uL2F1ZGlvL3BsYXliYWNrL3BsYXllcidcclxuaW1wb3J0IHtXYXZXcml0ZXJ9IGZyb20gJy4uLy4uL2F1ZGlvL2ltcGwvd2F2d3JpdGVyJ1xyXG5pbXBvcnQge1JlY29yZGluZ0ZpbGUsIFJlY29yZGluZ0ZpbGVVdGlsc30gZnJvbSAnLi4vcmVjb3JkaW5nJ1xyXG5pbXBvcnQge1xyXG4gIEFmdGVyVmlld0luaXQsXHJcbiAgQ2hhbmdlRGV0ZWN0b3JSZWYsXHJcbiAgQ29tcG9uZW50LFxyXG4gIEhvc3RMaXN0ZW5lcixcclxuICBJbmplY3QsXHJcbiAgSW5wdXQsXHJcbiAgT25EZXN0cm95LFxyXG4gIE9uSW5pdCxcclxuICBSZW5kZXJlcjIsXHJcbiAgVmlld0NoaWxkXHJcbn0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcclxuaW1wb3J0IHtTZXNzaW9uU2VydmljZX0gZnJvbSBcIi4vc2Vzc2lvbi5zZXJ2aWNlXCI7XHJcbmltcG9ydCB7TWF0RGlhbG9nfSBmcm9tIFwiQGFuZ3VsYXIvbWF0ZXJpYWwvZGlhbG9nXCI7XHJcbmltcG9ydCB7U3BlZWNoUmVjb3JkZXJVcGxvYWRlcn0gZnJvbSBcIi4uL3NwcnVwbG9hZGVyXCI7XHJcbmltcG9ydCB7U1BFRUNIUkVDT1JERVJfQ09ORklHLCBTcGVlY2hSZWNvcmRlckNvbmZpZ30gZnJvbSBcIi4uLy4uL3Nwci5jb25maWdcIjtcclxuaW1wb3J0IHtTZXNzaW9ufSBmcm9tIFwiLi9zZXNzaW9uXCI7XHJcbmltcG9ydCB7QXVkaW9TdG9yYWdlVHlwZSwgUHJvamVjdCwgUHJvamVjdFV0aWx9IGZyb20gXCIuLi9wcm9qZWN0L3Byb2plY3RcIjtcclxuaW1wb3J0IHtNZXNzYWdlRGlhbG9nfSBmcm9tIFwiLi4vLi4vdWkvbWVzc2FnZV9kaWFsb2dcIjtcclxuaW1wb3J0IHtSZWNvcmRpbmdTZXJ2aWNlfSBmcm9tIFwiLi4vcmVjb3JkaW5ncy9yZWNvcmRpbmdzLnNlcnZpY2VcIjtcclxuXHJcbmltcG9ydCB7QXVkaW9Db250ZXh0UHJvdmlkZXJ9IGZyb20gXCIuLi8uLi9hdWRpby9jb250ZXh0XCI7XHJcbmltcG9ydCB7QXVkaW9DbGlwfSBmcm9tIFwiLi4vLi4vYXVkaW8vcGVyc2lzdG9yXCI7XHJcblxyXG5pbXBvcnQge1VwbG9hZCwgVXBsb2FkZXJTdGF0dXMsIFVwbG9hZGVyU3RhdHVzQ2hhbmdlRXZlbnQsIFVwbG9hZEhvbGRlcn0gZnJvbSBcIi4uLy4uL25ldC91cGxvYWRlclwiO1xyXG5pbXBvcnQge0FjdGl2YXRlZFJvdXRlLCBQYXJhbXMsIFJvdXRlcn0gZnJvbSBcIkBhbmd1bGFyL3JvdXRlclwiO1xyXG5pbXBvcnQge1Byb2plY3RTZXJ2aWNlfSBmcm9tIFwiLi4vcHJvamVjdC9wcm9qZWN0LnNlcnZpY2VcIjtcclxuaW1wb3J0IHtMZXZlbEJhciwgU3RhdGUgYXMgTGl2ZUxldmVsU3RhdGV9IGZyb20gXCIuLi8uLi9hdWRpby91aS9saXZlbGV2ZWxcIjtcclxuaW1wb3J0IHtSZWNvcmRlckNvbWJpUGFuZX0gZnJvbSBcIi4vcmVjb3JkZXJfY29tYmlfcGFuZVwiO1xyXG5pbXBvcnQge0Jhc2ljUmVjb3JkZXIsIENodW5rQXVkaW9CdWZmZXJSZWNlaXZlciwgTUFYX1JFQ09SRElOR19USU1FX01TLCBSRUNGSUxFX0FQSV9DVFh9IGZyb20gXCIuL2Jhc2ljcmVjb3JkZXJcIjtcclxuaW1wb3J0IHtSZWFkeVN0YXRlUHJvdmlkZXIsIFJlY29yZGVyQ29tcG9uZW50fSBmcm9tIFwiLi4vLi4vcmVjb3JkZXJfY29tcG9uZW50XCI7XHJcbmltcG9ydCB7TW9kZX0gZnJvbSBcIi4uLy4uL3NwZWVjaHJlY29yZGVybmcuY29tcG9uZW50XCI7XHJcbmltcG9ydCB7QXVkaW9CdWZmZXJTb3VyY2UsIEF1ZGlvRGF0YUhvbGRlciwgQXVkaW9Tb3VyY2V9IGZyb20gXCIuLi8uLi9hdWRpby9hdWRpb19kYXRhX2hvbGRlclwiO1xyXG5pbXBvcnQge0FycmF5QXVkaW9CdWZmZXJ9IGZyb20gXCIuLi8uLi9hdWRpby9hcnJheV9hdWRpb19idWZmZXJcIjtcclxuaW1wb3J0IHtOZXRBdWRpb0J1ZmZlcn0gZnJvbSBcIi4uLy4uL2F1ZGlvL25ldF9hdWRpb19idWZmZXJcIjtcclxuaW1wb3J0IHtJbmRleGVkRGJBdWRpb0J1ZmZlcn0gZnJvbSBcIi4uLy4uL2F1ZGlvL2luZGRiX2F1ZGlvX2J1ZmZlclwiO1xyXG5pbXBvcnQge0JyZWFrcG9pbnRPYnNlcnZlcn0gZnJvbSBcIkBhbmd1bGFyL2Nkay9sYXlvdXRcIjtcclxuXHJcbmV4cG9ydCBjb25zdCBlbnVtIFN0YXR1cyB7XHJcbiAgQkxPQ0tFRCwgSURMRSxTVEFSVElORywgUkVDT1JESU5HLCAgU1RPUFBJTkdfU1RPUCwgRVJST1JcclxufVxyXG5cclxuXHJcbkBDb21wb25lbnQoe1xyXG4gIHNlbGVjdG9yOiAnYXBwLWF1ZGlvcmVjb3JkZXInLFxyXG4gIHByb3ZpZGVyczogW1Nlc3Npb25TZXJ2aWNlXSxcclxuICB0ZW1wbGF0ZTogYFxyXG4gICAgPGFwcC13YXJuaW5nYmFyIFtzaG93XT1cImlzVGVzdFNlc3Npb24oKVwiIHdhcm5pbmdUZXh0PVwiVGVzdCByZWNvcmRpbmcgb25seSFcIj48L2FwcC13YXJuaW5nYmFyPlxyXG4gICAgPGFwcC13YXJuaW5nYmFyIFtzaG93XT1cImlzRGVmYXVsdEF1ZGlvVGVzdFNlc3Npb24oKVwiXHJcbiAgICAgICAgICAgICAgICAgICAgd2FybmluZ1RleHQ9XCJUaGlzIHRlc3QgdXNlcyBkZWZhdWx0IGF1ZGlvIGRldmljZSEgUmVndWxhciBzZXNzaW9ucyBtYXkgcmVxdWlyZSBhIHBhcnRpY3VsYXIgYXVkaW8gZGV2aWNlIChtaWNyb3Bob25lKSFcIj48L2FwcC13YXJuaW5nYmFyPlxyXG4gICAgPGFwcC1yZWNvcmRlcmNvbWJpcGFuZSAoc2VsZWN0ZWRSZWNvcmRpbmdGaWxlQ2hhbmdlZCk9XCJzZWxlY3RSZWNvcmRpbmdGaWxlKCRldmVudClcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICBbYXVkaW9TaWduYWxDb2xsYXBzZWRdPVwiYXVkaW9TaWduYWxDb2xsYXBzZWRcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICBbc2VsZWN0ZWRSZWNvcmRpbmdGaWxlXT1cImRpc3BsYXlSZWNGaWxlXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgW3NlbGVjdERpc2FibGVkXT1cImlzQWN0aXZlKClcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICBbZGlzcGxheUF1ZGlvQ2xpcF09XCJkaXNwbGF5QXVkaW9DbGlwXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgW3BsYXlTdGFydEFjdGlvbl09XCJjb250cm9sQXVkaW9QbGF5ZXI/LnN0YXJ0QWN0aW9uXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgW3BsYXlTdG9wQWN0aW9uXT1cImNvbnRyb2xBdWRpb1BsYXllcj8uc3RvcEFjdGlvblwiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgIFtwbGF5U2VsZWN0aW9uQWN0aW9uXT1cImNvbnRyb2xBdWRpb1BsYXllcj8uc3RhcnRTZWxlY3Rpb25BY3Rpb25cIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICBbYXV0b1BsYXlPblNlbGVjdFRvZ2dsZUFjdGlvbl09XCJjb250cm9sQXVkaW9QbGF5ZXI/LmF1dG9QbGF5T25TZWxlY3RUb2dnbGVBY3Rpb25cIlxyXG4gICAgPjwvYXBwLXJlY29yZGVyY29tYmlwYW5lPlxyXG5cclxuICAgIDxkaXYgW2NsYXNzXT1cInthdWRpb1N0YXR1c0Rpc3BsYXk6IXNjcmVlblhzLGF1ZGlvU3RhdHVzRGlzcGxheVhzOnNjcmVlblhzfVwiPlxyXG4gICAgICA8YXVkaW8tbGV2ZWxiYXIgc3R5bGU9XCJmbGV4OjEgMCAxJVwiIFtzdHJlYW1pbmdNb2RlXT1cImlzUmVjb3JkaW5nKCkgfHwga2VlcExpdmVMZXZlbFwiIFtzdGF0ZV09XCJsaXZlTGV2ZWxEaXNwbGF5U3RhdGVcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgW2Rpc3BsYXlMZXZlbEluZm9zXT1cImRpc3BsYXlBdWRpb0NsaXA/LmxldmVsSW5mb3NcIj48L2F1ZGlvLWxldmVsYmFyPlxyXG4gICAgICA8ZGl2IHN0eWxlPVwiZGlzcGxheTpmbGV4O2ZsZXgtZGlyZWN0aW9uOiByb3dcIj5cclxuICAgICAgICA8c3ByLXJlY29yZGluZ2l0ZW1jb250cm9scyBzdHlsZT1cImZsZXg6MTAgMCAxcHhcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFtkaXNhYmxlQXVkaW9EZXRhaWxzXT1cImRpc2FibGVBdWRpb0RldGFpbHNcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFthdWRpb0xvYWRlZF09XCJhdWRpb0xvYWRlZFwiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW3BsYXlTdGFydEFjdGlvbl09XCJjb250cm9sQXVkaW9QbGF5ZXI/LnN0YXJ0QWN0aW9uXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbcGxheVN0b3BBY3Rpb25dPVwiY29udHJvbEF1ZGlvUGxheWVyPy5zdG9wQWN0aW9uXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbcGVha0RiTHZsXT1cInBlYWtMZXZlbEluRGJcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFthZ2NdPVwidGhpcy5hYz8uYWdjU3RhdHVzXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAob25TaG93UmVjb3JkaW5nRGV0YWlscyk9XCJhdWRpb1NpZ25hbENvbGxhcHNlZD0hYXVkaW9TaWduYWxDb2xsYXBzZWRcIj5cclxuICAgICAgICA8L3Nwci1yZWNvcmRpbmdpdGVtY29udHJvbHM+XHJcblxyXG4gICAgICAgIDxhcHAtdXBsb2Fkc3RhdHVzICpuZ0lmPVwic2NyZWVuWHMgJiYgZW5hYmxlVXBsb2FkUmVjb3JkaW5nc1wiIGNsYXNzPVwicmljb250cm9scyBkYXJrXCIgc3R5bGU9XCJmbGV4OjAgMCAwXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICBbdmFsdWVdPVwidXBsb2FkUHJvZ3Jlc3NcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgIFtzdGF0dXNdPVwidXBsb2FkU3RhdHVzXCIgW2F3YWl0TmV3VXBsb2FkXT1cInByb2Nlc3NpbmdSZWNvcmRpbmdcIj48L2FwcC11cGxvYWRzdGF0dXM+XHJcbiAgICAgICAgPGFwcC13YWtlbG9ja2luZGljYXRvciAqbmdJZj1cInNjcmVlblhzXCIgY2xhc3M9XCJyaWNvbnRyb2xzIGRhcmtcIiBzdHlsZT1cImZsZXg6MCAwIDBcIiBbc2NyZWVuTG9ja2VkXT1cInNjcmVlbkxvY2tlZFwiPjwvYXBwLXdha2Vsb2NraW5kaWNhdG9yPlxyXG4gICAgICAgIDxhcHAtcmVhZHlzdGF0ZWluZGljYXRvciAqbmdJZj1cInNjcmVlblhzXCIgY2xhc3M9XCJyaWNvbnRyb2xzIGRhcmtcIiBzdHlsZT1cImZsZXg6MCAwIDBcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbcmVhZHldPVwiZGF0YVNhdmVkICYmICFpc0FjdGl2ZSgpXCI+PC9hcHAtcmVhZHlzdGF0ZWluZGljYXRvcj5cclxuICAgICAgPC9kaXY+XHJcbiAgICA8L2Rpdj5cclxuICAgIDxkaXYgI2NvbnRyb2xwYW5lbCBjbGFzcz1cImNvbnRyb2xwYW5lbFwiPlxyXG4gICAgICA8YXBwLXNwcnN0YXR1c2Rpc3BsYXkgKm5nSWY9XCIhc2NyZWVuWHNcIiBzdHlsZT1cImZsZXg6MCAxIDMwJTtcIiBbc3RhdHVzTXNnXT1cInN0YXR1c01zZ1wiIFtzdGF0dXNBbGVydFR5cGVdPVwic3RhdHVzQWxlcnRUeXBlXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFtzdGF0dXNXYWl0aW5nXT1cInN0YXR1c1dhaXRpbmdcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJoaWRkZW4teHNcIj48L2FwcC1zcHJzdGF0dXNkaXNwbGF5PlxyXG4gICAgICA8ZGl2IFtjbGFzcy5zdGFydHN0b3BdPVwiIXNjcmVlblhzXCIgW2NsYXNzLnN0YXJ0c3RvcHNjcmVlbnhzXT1cInNjcmVlblhzXCI+XHJcbiAgICAgICAgPGRpdiBzdHlsZT1cImFsaWduLWNvbnRlbnQ6IGNlbnRlclwiPlxyXG4gICAgICAgICAgPGJ1dHRvbiAoY2xpY2spPVwic3RhcnRTdG9wUGVyZm9ybSgpXCIgW2Rpc2FibGVkXT1cInN0YXJ0RGlzYWJsZWQoKSAmJiBzdG9wRGlzYWJsZWQoKVwiIG1hdC1yYWlzZWQtYnV0dG9uIGNsYXNzPVwiYmlnYnV0dG9uXCI+XHJcbiAgICAgICAgICAgIDxtYXQtaWNvbiBjbGFzcz1cImJpZ2J1dHRvbmljb25cIiBbc3R5bGUuY29sb3JdPVwic3RhcnRTdG9wTmV4dEljb25Db2xvcigpXCI+e3tzdGFydFN0b3BOZXh0SWNvbk5hbWUoKX19PC9tYXQtaWNvbj5cclxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJiaWdidXR0b250ZXh0XCI+e3tzdGFydFN0b3BOZXh0TmFtZSgpfX08L3NwYW4+XHJcbiAgICAgICAgICA8L2J1dHRvbj5cclxuICAgICAgICA8L2Rpdj5cclxuICAgICAgPC9kaXY+XHJcbiAgICAgIDxkaXYgc3R5bGU9XCJmbGV4OjAgMSAzMCU7ZGlzcGxheTpmbGV4O2p1c3RpZnktaXRlbXM6IGZsZXgtZW5kO2p1c3RpZnktY29udGVudDpmbGV4LWVuZFwiID5cclxuICAgICAgICA8YXBwLXVwbG9hZHN0YXR1cyAqbmdJZj1cIiFzY3JlZW5YcyAmJiBlbmFibGVVcGxvYWRSZWNvcmRpbmdzXCIgY2xhc3M9XCJyaWNvbnRyb2xzXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICBbdmFsdWVdPVwidXBsb2FkUHJvZ3Jlc3NcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgIFtzdGF0dXNdPVwidXBsb2FkU3RhdHVzXCIgW2F3YWl0TmV3VXBsb2FkXT1cInByb2Nlc3NpbmdSZWNvcmRpbmdcIj48L2FwcC11cGxvYWRzdGF0dXM+XHJcbiAgICAgICAgPGFwcC13YWtlbG9ja2luZGljYXRvciAgKm5nSWY9XCIhc2NyZWVuWHNcIiBjbGFzcz1cInJpY29udHJvbHNcIiBbc2NyZWVuTG9ja2VkXT1cInNjcmVlbkxvY2tlZFwiPjwvYXBwLXdha2Vsb2NraW5kaWNhdG9yPlxyXG4gICAgICAgIDxhcHAtcmVhZHlzdGF0ZWluZGljYXRvciAqbmdJZj1cIiFzY3JlZW5Yc1wiIGNsYXNzPVwicmljb250cm9sc1wiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFtyZWFkeV09XCJkYXRhU2F2ZWQgJiYgIWlzQWN0aXZlKClcIj48L2FwcC1yZWFkeXN0YXRlaW5kaWNhdG9yPlxyXG4gICAgICA8L2Rpdj5cclxuICAgIDwvZGl2PlxyXG4gIGAsXHJcbiAgc3R5bGVzOiBbYDpob3N0IHtcclxuICAgIGZsZXg6IDI7XHJcbiAgICBiYWNrZ3JvdW5kOiBsaWdodGdyZXk7XHJcbiAgICBkaXNwbGF5OiBmbGV4OyAvKiBWZXJ0aWNhbCBmbGV4IGNvbnRhaW5lcjogQm90dG9tIHRyYW5zcG9ydCBwYW5lbCwgYWJvdmUgcHJvbXB0aW5nIHBhbmVsICovXHJcbiAgICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xyXG4gICAgbWFyZ2luOiAwO1xyXG4gICAgcGFkZGluZzogMDtcclxuICAgIGhlaWdodDogMTAwJTtcclxuICAgIG1pbi1oZWlnaHQ6IDBweDtcclxuICAgICAgLyogUHJldmVudHMgaG9yaXpvbnRhbCBzY3JvbGwgYmFyIG9uIHN3aXBlIHJpZ2h0ICovXHJcbiAgICAgIG92ZXJmbG93OiBoaWRkZW47XHJcbiAgfWAsYC5yaWNvbnRyb2xzIHtcclxuICAgICAgICBwYWRkaW5nOiA0cHg7XHJcbiAgICAgICAgYm94LXNpemluZzogYm9yZGVyLWJveDtcclxuICAgICAgICBoZWlnaHQ6IDEwMCU7XHJcbiAgICB9YCxgLmRhcmsge1xyXG4gICAgYmFja2dyb3VuZDogZGFya2dyYXk7XHJcbiAgfWAsYC5jb250cm9scGFuZWwge1xyXG4gICAgZGlzcGxheTpmbGV4O1xyXG4gICAgZmxleC1kaXJlY3Rpb246IHJvdztcclxuICAgIGFsaWduLWNvbnRlbnQ6IGNlbnRlcjtcclxuICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XHJcbiAgICBtYXJnaW46IDA7XHJcbiAgICBwYWRkaW5nOiAyMHB4O1xyXG4gICAgbWluLWhlaWdodDogbWluLWNvbnRlbnQ7IC8qIGltcG9ydGFudCAqL1xyXG4gIH1gLGAuc3RhcnRzdG9wIHtcclxuICAgIHdpZHRoOiAxMDAlO1xyXG4gICAgZmxleDoxIDAgMzAlO1xyXG4gICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcclxuICAgIHRleHQtYWxpZ246IGNlbnRlcjtcclxuICAgIGFsaWduLWNvbnRlbnQ6IGNlbnRlcjtcclxuICB9YCxgLnN0YXJ0c3RvcHNjcmVlbnhzIHtcclxuICAgIHdpZHRoOiAxMDAlO1xyXG4gICAgZmxleDoxIDAgMTAwJTtcclxuICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XHJcbiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7XHJcbiAgICBhbGlnbi1jb250ZW50OiBjZW50ZXI7XHJcbiAgfWAsYC5iaWdidXR0b24ge1xyXG4gICAgdmVydGljYWwtYWxpZ246IG1pZGRsZTtcclxuICAgIG92ZXJmbG93OiBoaWRkZW47XHJcbiAgICB0ZXh0LW92ZXJmbG93OiBjbGlwO1xyXG4gICAgd2hpdGUtc3BhY2U6IG5vd3JhcDtcclxuICAgIGxldHRlci1zcGFjaW5nOiBub3JtYWw7XHJcbiAgICBtaW4td2lkdGg6IDcwcHg7XHJcbiAgICBtaW4taGVpZ2h0OiA1MHB4O1xyXG4gICAgYm9yZGVyLXJhZGl1czogMjBweDtcclxuICB9YCxgLmJpZ2J1dHRvbmljb24ge1xyXG4gICAgbWluLXdpZHRoOiA1MHB4O1xyXG4gICAgbWluLWhlaWdodDogNTBweDtcclxuICAgIGZvbnQtc2l6ZTogNTBweDtcclxuICB9YCxgLmJpZ2J1dHRvbnRleHQge1xyXG4gICAgICBmb250LXdlaWdodDogYm9sZGVyO1xyXG4gICAgICBmb250LXNpemU6IDE0cHg7XHJcbiAgICAgIHZlcnRpY2FsLWFsaWduOiBtaWRkbGU7XHJcbiAgfVxyXG4gIGAsYC5hdWRpb1N0YXR1c0Rpc3BsYXl7XHJcbiAgICBkaXNwbGF5OmZsZXg7XHJcbiAgICBmbGV4LWRpcmVjdGlvbjogcm93O1xyXG4gICAgaGVpZ2h0OjEwMHB4O1xyXG4gICAgbWluLWhlaWdodDogMTAwcHg7XHJcbiAgfWAsYC5hdWRpb1N0YXR1c0Rpc3BsYXlYc3tcclxuICAgIGRpc3BsYXk6ZmxleDtcclxuICAgIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XHJcbiAgICBoZWlnaHQ6MTI1cHg7XHJcbiAgICBtaW4taGVpZ2h0OiAxMjVweDtcclxuICB9YFxyXG4gICBdXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBBdWRpb1JlY29yZGVyIGV4dGVuZHMgQmFzaWNSZWNvcmRlciBpbXBsZW1lbnRzIE9uSW5pdCxBZnRlclZpZXdJbml0LE9uRGVzdHJveSwgQXVkaW9DYXB0dXJlTGlzdGVuZXIsUmVhZHlTdGF0ZVByb3ZpZGVyLENodW5rQXVkaW9CdWZmZXJSZWNlaXZlciB7XHJcblxyXG4gIF9wcm9qZWN0OlByb2plY3R8dW5kZWZpbmVkfCBudWxsPW51bGw7XHJcbiAgQElucHV0KCkgcHJvamVjdE5hbWU6c3RyaW5nfHVuZGVmaW5lZHxudWxsPW51bGw7XHJcbiAgZW5hYmxlVXBsb2FkUmVjb3JkaW5nczogYm9vbGVhbiA9IHRydWU7XHJcbiAgZW5hYmxlRG93bmxvYWRSZWNvcmRpbmdzOiBib29sZWFuID0gZmFsc2U7XHJcbiAgc3RhdHVzOiBTdGF0dXMgPSBTdGF0dXMuQkxPQ0tFRDtcclxuXHJcbiAgQFZpZXdDaGlsZChSZWNvcmRlckNvbWJpUGFuZSwgeyBzdGF0aWM6IHRydWUgfSkgcmVjb3JkZXJDb21iaVBhbmUhOiBSZWNvcmRlckNvbWJpUGFuZTtcclxuICBAVmlld0NoaWxkKExldmVsQmFyLCB7IHN0YXRpYzogdHJ1ZSB9KSBsaXZlTGV2ZWxEaXNwbGF5ITogTGV2ZWxCYXI7XHJcblxyXG4gIEBJbnB1dCgpIGRhdGFTYXZlZD10cnVlXHJcblxyXG5cclxuXHJcblxyXG4gIHN0YXJ0U3RvcE5leHRCdXR0b25OYW1lITpzdHJpbmc7XHJcbiAgc3RhcnRTdG9wTmV4dEJ1dHRvbkljb25OYW1lITpzdHJpbmc7XHJcblxyXG4gIGF1ZGlvOiBhbnk7XHJcblxyXG4gIHByaXZhdGUgX2Rpc3BsYXlSZWNGaWxlOiBSZWNvcmRpbmdGaWxlIHwgbnVsbD1udWxsO1xyXG4gIHByaXZhdGUgZGlzcGxheVJlY0ZpbGVWZXJzaW9uOiBudW1iZXI9MDtcclxuXHJcblxyXG4gIGNvbnN0cnVjdG9yKHByb3RlY3RlZCBicG86QnJlYWtwb2ludE9ic2VydmVyLGNoYW5nZURldGVjdG9yUmVmOiBDaGFuZ2VEZXRlY3RvclJlZixcclxuICAgICAgICAgICAgICBwcml2YXRlIHJlbmRlcmVyOiBSZW5kZXJlcjIsXHJcbiAgICAgICAgICAgICAgZGlhbG9nOiBNYXREaWFsb2csXHJcbiAgICAgICAgICAgICAgc2Vzc2lvblNlcnZpY2U6U2Vzc2lvblNlcnZpY2UsXHJcbiAgICAgICAgICAgICAgcHJpdmF0ZSByZWNGaWxlU2VydmljZTpSZWNvcmRpbmdTZXJ2aWNlLFxyXG4gICAgICAgICAgICAgIHByb3RlY3RlZCB1cGxvYWRlcjogU3BlZWNoUmVjb3JkZXJVcGxvYWRlcixcclxuICAgICAgICAgICAgICBASW5qZWN0KFNQRUVDSFJFQ09SREVSX0NPTkZJRykgY29uZmlnPzogU3BlZWNoUmVjb3JkZXJDb25maWcpIHtcclxuICAgIHN1cGVyKGJwbyxjaGFuZ2VEZXRlY3RvclJlZixkaWFsb2csc2Vzc2lvblNlcnZpY2UsdXBsb2FkZXIsY29uZmlnKTtcclxuXHJcbiAgICAvL3N1cGVyKGluamVjdG9yKTtcclxuICAgIHRoaXMuc3RhdHVzID0gU3RhdHVzLklETEU7XHJcblxyXG4gICAgdGhpcy5hdWRpbyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdhdWRpbycpO1xyXG5cclxuICAgIGlmICh0aGlzLmNvbmZpZyAmJiB0aGlzLmNvbmZpZy5lbmFibGVVcGxvYWRSZWNvcmRpbmdzICE9IG51bGwpIHtcclxuICAgICAgdGhpcy5lbmFibGVVcGxvYWRSZWNvcmRpbmdzID0gdGhpcy5jb25maWcuZW5hYmxlVXBsb2FkUmVjb3JkaW5ncztcclxuICAgIH1cclxuICAgIGlmICh0aGlzLmNvbmZpZyAmJiB0aGlzLmNvbmZpZy5lbmFibGVEb3dubG9hZFJlY29yZGluZ3MgIT0gbnVsbCkge1xyXG4gICAgICB0aGlzLmVuYWJsZURvd25sb2FkUmVjb3JkaW5ncyA9IHRoaXMuY29uZmlnLmVuYWJsZURvd25sb2FkUmVjb3JkaW5ncztcclxuICAgIH1cclxuICAgIC8vdGhpcy5pbml0KCk7XHJcbiAgfVxyXG5cclxuXHJcbiAgbmdBZnRlclZpZXdJbml0KCkge1xyXG5cclxuICAgIHRoaXMuc3RyZWFtTGV2ZWxNZWFzdXJlLmxldmVsTGlzdGVuZXIgPSB0aGlzLmxpdmVMZXZlbERpc3BsYXk7XHJcbiAgICB0aGlzLnN0cmVhbUxldmVsTWVhc3VyZS5wZWFrTGV2ZWxMaXN0ZW5lcj0ocGVha0x2bEluRGIpPT57XHJcbiAgICAgIHRoaXMucGVha0xldmVsSW5EYj1wZWFrTHZsSW5EYjtcclxuICAgICAgdGhpcy5jaGFuZ2VEZXRlY3RvclJlZi5kZXRlY3RDaGFuZ2VzKCk7XHJcbiAgICB9XHJcbiAgICAvL2xldCB3YWtlTG9ja1N1cHA9KCd3YWtlTG9jaycgaW4gbmF2aWdhdG9yKTtcclxuICAgIC8vYWxlcnQoJ1dha2UgbG9jayBBUEkgc3VwcG9ydGVkOiAnK3dha2VMb2NrU3VwcCk7XHJcbiAgfVxyXG5cclxuICByZWFkeSgpOmJvb2xlYW57XHJcbiAgICByZXR1cm4gdGhpcy5kYXRhU2F2ZWQgJiYgIXRoaXMuaXNBY3RpdmUoKVxyXG4gIH1cclxuICAgIG5nT25EZXN0cm95KCkge1xyXG4gICAgICB0aGlzLmRpc2FibGVXYWtlTG9ja0NvbmQoKTtcclxuICAgICAgIHRoaXMuZGVzdHJveWVkPXRydWU7XHJcbiAgICAgICAvLyBUT0RPIHN0b3AgY2FwdHVyZSAvcGxheWJhY2tcclxuICAgIH1cclxuXHJcbiAgbmdPbkluaXQoKSB7XHJcbiAgICB0aGlzLnRyYW5zcG9ydEFjdGlvbnMuc3RhcnRBY3Rpb24uZGlzYWJsZWQgPSB0cnVlO1xyXG4gICAgdGhpcy50cmFuc3BvcnRBY3Rpb25zLnN0b3BBY3Rpb24uZGlzYWJsZWQgPSB0cnVlO1xyXG4gICAgdGhpcy50cmFuc3BvcnRBY3Rpb25zLm5leHRBY3Rpb24uZGlzYWJsZWQgPSB0cnVlO1xyXG4gICAgdGhpcy50cmFuc3BvcnRBY3Rpb25zLnBhdXNlQWN0aW9uLmRpc2FibGVkID0gdHJ1ZTtcclxuICAgIHRoaXMucGxheVN0YXJ0QWN0aW9uLmRpc2FibGVkID0gdHJ1ZTtcclxuXHJcbiAgICAgIHRoaXMuYWMgPSBuZXcgQXVkaW9DYXB0dXJlKCk7XHJcbiAgICAgIGlmICh0aGlzLmFjKSB7XHJcbiAgICAgICAgdGhpcy50cmFuc3BvcnRBY3Rpb25zLnN0YXJ0QWN0aW9uLm9uQWN0aW9uID0gKCkgPT4gdGhpcy5zdGFydEl0ZW0oKTtcclxuICAgICAgICB0aGlzLmFjLmxpc3RlbmVyID0gdGhpcztcclxuICAgICAgICB0aGlzLmNvbmZpZ3VyZVN0cmVhbUNhcHR1cmVTdHJlYW0oKTtcclxuXHJcbiAgICAgICAgLy8gRG9uJ3QgbGlzdCB0aGUgZGV2aWNlcyBoZXJlLiBJZiB3ZSBkbyBub3QgaGF2ZSBhdWRpbyBwZXJtaXNzaW9ucyB3ZSBvbmx5IGdldCBhbm9ueW1pemVkIGRldmljZXMgd2l0aG91dCBsYWJlbHMuXHJcbiAgICAgICAgLy90aGlzLmFjLmxpc3REZXZpY2VzKCk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdGhpcy50cmFuc3BvcnRBY3Rpb25zLnN0YXJ0QWN0aW9uLmRpc2FibGVkID0gdHJ1ZTtcclxuICAgICAgICBsZXQgZXJyTXNnID0gJ0Jyb3dzZXIgZG9lcyBub3Qgc3VwcG9ydCBNZWRpYS9BdWRpbyBBUEkhJztcclxuICAgICAgICB0aGlzLnN0YXR1c01zZyA9ICdFUlJPUjogJyArIGVyck1zZztcclxuICAgICAgICB0aGlzLnN0YXR1c0FsZXJ0VHlwZSA9ICdlcnJvcic7XHJcbiAgICAgICAgdGhpcy5kaWFsb2cub3BlbihNZXNzYWdlRGlhbG9nLCB7XHJcbiAgICAgICAgICBkYXRhOiB7XHJcbiAgICAgICAgICAgIHR5cGU6ICdlcnJvcicsXHJcbiAgICAgICAgICAgIHRpdGxlOiAnRXJyb3InLFxyXG4gICAgICAgICAgICBtc2c6IGVyck1zZyxcclxuICAgICAgICAgICAgYWR2aWNlOiAnUGxlYXNlIHVzZSBhIHN1cHBvcnRlZCBicm93c2VyLicsXHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSk7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgICB9XHJcbiAgICAgIHRoaXMudHJhbnNwb3J0QWN0aW9ucy5zdG9wQWN0aW9uLm9uQWN0aW9uID0gKCkgPT4gdGhpcy5zdG9wSXRlbSgpO1xyXG4gICAgICB0aGlzLnRyYW5zcG9ydEFjdGlvbnMubmV4dEFjdGlvbi5vbkFjdGlvbiA9ICgpID0+IHRoaXMuc3RvcEl0ZW0oKTtcclxuICAgICAgLy90aGlzLnRyYW5zcG9ydEFjdGlvbnMucGF1c2VBY3Rpb24ub25BY3Rpb24gPSAoKSA9PiB0aGlzLnBhdXNlSXRlbSgpO1xyXG5cclxuICAgICAgdGhpcy5wbGF5U3RhcnRBY3Rpb24ub25BY3Rpb24gPSAoKSA9PiB0aGlzLmNvbnRyb2xBdWRpb1BsYXllcj8uc3RhcnQoKTtcclxuXHJcbiAgICB0aGlzLnVwbG9hZGVyLmxpc3RlbmVyID0gKHVlKSA9PiB7XHJcbiAgICAgIHRoaXMudXBsb2FkVXBkYXRlKHVlKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIEBIb3N0TGlzdGVuZXIoJ3dpbmRvdzprZXlwcmVzcycsIFsnJGV2ZW50J10pXHJcbiAgb25LZXlQcmVzcyhrZTogS2V5Ym9hcmRFdmVudCkge1xyXG4gICAgaWYgKGtlLmtleSA9PSAnICcpIHtcclxuICAgICAgLy90aGlzLnRyYW5zcG9ydEFjdGlvbnMuc3RhcnRBY3Rpb24ucGVyZm9ybSgpO1xyXG4gICAgICAvL3RoaXMudHJhbnNwb3J0QWN0aW9ucy5uZXh0QWN0aW9uLnBlcmZvcm0oKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIEBIb3N0TGlzdGVuZXIoJ3dpbmRvdzprZXlkb3duJywgWyckZXZlbnQnXSlcclxuICBvbktleURvd24oa2U6IEtleWJvYXJkRXZlbnQpIHtcclxuICAgIGlmIChrZS5rZXkgPT0gJyAnKSB7XHJcbiAgICAgIGlmKCF0aGlzLnRyYW5zcG9ydEFjdGlvbnMuc3RhcnRBY3Rpb24uZGlzYWJsZWQpe1xyXG4gICAgICAgIHRoaXMudHJhbnNwb3J0QWN0aW9ucy5zdGFydEFjdGlvbi5wZXJmb3JtKCk7XHJcbiAgICAgIH1lbHNlIGlmKCF0aGlzLnRyYW5zcG9ydEFjdGlvbnMuc3RvcEFjdGlvbi5kaXNhYmxlZCkge1xyXG4gICAgICAgIHRoaXMudHJhbnNwb3J0QWN0aW9ucy5zdG9wQWN0aW9uLnBlcmZvcm0oKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgaWYgKGtlLmtleSA9PSAncCcpIHtcclxuICAgICAgdGhpcy50cmFuc3BvcnRBY3Rpb25zLnBhdXNlQWN0aW9uLnBlcmZvcm0oKTtcclxuICAgIH1cclxuICAgIGlmIChrZS5rZXkgPT0gJ0VzY2FwZScpIHtcclxuICAgICAgaWYgKCF0aGlzLmF1ZGlvU2lnbmFsQ29sbGFwc2VkKSB7XHJcbiAgICAgICAgdGhpcy5hdWRpb1NpZ25hbENvbGxhcHNlZCA9IHRydWU7XHJcbiAgICAgIH1cclxuICAgICAgdGhpcy50cmFuc3BvcnRBY3Rpb25zLnN0b3BBY3Rpb24ucGVyZm9ybSgpO1xyXG4gICAgICB0aGlzLnRyYW5zcG9ydEFjdGlvbnMucGF1c2VBY3Rpb24ucGVyZm9ybSgpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChrZS5rZXkgPT0gJ01lZGlhUGxheVBhdXNlJykge1xyXG4gICAgICB0aGlzLnBsYXlTdGFydEFjdGlvbi5wZXJmb3JtKCk7XHJcbiAgICB9XHJcbiAgICBpZiAoa2Uua2V5ID09PSAnQXJyb3dSaWdodCcpIHtcclxuICAgICAgdGhpcy50cmFuc3BvcnRBY3Rpb25zLmZ3ZEFjdGlvbi5wZXJmb3JtKCk7XHJcbiAgICB9XHJcbiAgICBpZiAoa2Uua2V5ID09PSAnQXJyb3dMZWZ0Jykge1xyXG4gICAgICB0aGlzLnRyYW5zcG9ydEFjdGlvbnMuYndkQWN0aW9uLnBlcmZvcm0oKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIGlzVGVzdFNlc3Npb24oKTpib29sZWFuIHtcclxuICAgIHJldHVybiAoKHRoaXMuX3Nlc3Npb24hPW51bGwpICYmICh0aGlzLl9zZXNzaW9uLnR5cGUgPT09ICdURVNUJyB8fCB0aGlzLl9zZXNzaW9uLnR5cGU9PT0nVEVTVF9ERUZfQScgfHwgdGhpcy5fc2Vzc2lvbi50eXBlID09PSAnU0lOVVNfVEVTVCcpKTtcclxuICB9XHJcblxyXG4gIGlzRGVmYXVsdEF1ZGlvVGVzdFNlc3Npb24oKTpib29sZWFuIHtcclxuICAgIHJldHVybiAoKHRoaXMuX3Nlc3Npb24hPW51bGwpICYmICh0aGlzLl9zZXNzaW9uLnR5cGU9PT0nVEVTVF9ERUZfQScpKTtcclxuICB9XHJcblxyXG4gIGlzRGVmYXVsdEF1ZGlvVGVzdFNlc3Npb25PdmVyd3JpdGVpbmdQcm9qZWN0UmVxdWlyZW1lbnRzKCk6Ym9vbGVhbiB7XHJcbiAgICByZXR1cm4gKCh0aGlzLl9zZXNzaW9uIT1udWxsKSAmJiAodGhpcy5fc2Vzc2lvbi50eXBlPT09J1RFU1RfREVGX0EnKSAmJiAodGhpcy5hdWRpb0RldmljZXMhPW51bGwpICYmIHRoaXMuYXVkaW9EZXZpY2VzLmxlbmd0aD4wKVxyXG4gIH1cclxuXHJcblxyXG5cclxuICBmZXRjaFJlY29yZGluZ3Moc2VzczpTZXNzaW9uKXtcclxuICAgIHRoaXMuc3RhdHVzQWxlcnRUeXBlPSdpbmZvJztcclxuICAgIHRoaXMuc3RhdHVzTXNnID0gJ0ZldGNoaW5nIGluZm9zIG9mIHJlY29yZGluZ3MuLi4nO1xyXG4gICAgdGhpcy5zdGF0dXNXYWl0aW5nPXRydWU7XHJcbiAgICAvL2xldCBwck5tOnN0cmluZ3xudWxsPW51bGw7XHJcbiAgICBpZih0aGlzLnByb2plY3QpIHtcclxuICAgICAgbGV0IHJmc09icyA9IHRoaXMucmVjRmlsZVNlcnZpY2UucmVjb3JkaW5nRmlsZUxpc3QodGhpcy5wcm9qZWN0Lm5hbWUsIHNlc3Muc2Vzc2lvbklkKTtcclxuICAgICAgcmZzT2JzLnN1YnNjcmliZSh7bmV4dDoocmZzOiBBcnJheTxSZWNvcmRpbmdGaWxlPikgPT4ge1xyXG4gICAgICAgIHRoaXMuc3RhdHVzQWxlcnRUeXBlID0gJ2luZm8nO1xyXG4gICAgICAgIHRoaXMuc3RhdHVzTXNnID0gJ1JlY2VpdmVkIGluZm9zIG9mIHJlY29yZGluZ3MuJztcclxuICAgICAgICB0aGlzLnN0YXR1c1dhaXRpbmcgPSBmYWxzZTtcclxuICAgICAgICBpZiAocmZzKSB7XHJcbiAgICAgICAgICBpZiAocmZzIGluc3RhbmNlb2YgQXJyYXkpIHtcclxuICAgICAgICAgICAgcmZzLmZvckVhY2goKHJmKSA9PiB7XHJcbiAgICAgICAgICAgICAgLy8gdGhlIGxpc3QgY29tZXMgZnJvbSB0aGUgc2VydmVyLCBhc3NzdWVtIGFsbCByZWNvcmRpbmcgZmlsZXMgYXMgc2VydmVyIHBlcnNpc3RlZFxyXG4gICAgICAgICAgICAgIHJmLnNlcnZlclBlcnNpc3RlZD10cnVlO1xyXG4gICAgICAgICAgICAgIGlmKHJmLnN0YXJ0ZWREYXRlKXtcclxuICAgICAgICAgICAgICAgIHJmLl9zdGFydGVkQXNEYXRlT2JqPW5ldyBEYXRlKHJmLnN0YXJ0ZWREYXRlKTtcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgaWYocmYuZGF0ZSl7XHJcbiAgICAgICAgICAgICAgICByZi5fZGF0ZUFzRGF0ZU9iaj1uZXcgRGF0ZShyZi5kYXRlKTtcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgdGhpcy5yZWNvcmRlckNvbWJpUGFuZS5hZGRSZWNGaWxlKHJmKTtcclxuICAgICAgICAgICAgfSlcclxuICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0V4cGVjdGVkIHR5cGUgYXJyYXkgZm9yIGxpc3Qgb2YgYWxyZWFkeSByZWNvcmRlZCBmaWxlcyAnKVxyXG4gICAgICAgICAgfVxyXG5cclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiUmVjb3JkaW5nIGZpbGUgbGlzdDogXCIgKyByZnMpO1xyXG4gICAgICAgIH1cclxuICAgICAgfSwgZXJyb3I6KGVycikgPT4ge1xyXG4gICAgICAgIC8vIEZhaWxlZCBmZXRjaGluZyBleGlzdGluZywgYnV0IHdlIHN0YXJ0IHRoZSBzZXNzaW9uIGFueXdheVxyXG4gICAgICAgIHRoaXMuc3RhcnQoKVxyXG4gICAgICB9LCBjb21wbGV0ZTooKSA9PiB7XHJcbiAgICAgICAgLy8gTm9ybWFsIHN0YXJ0XHJcbiAgICAgICAgdGhpcy5zdGFydCgpXHJcbiAgICAgIH1cclxuICAgIH0pXHJcbiAgICB9ZWxzZXtcclxuICAgICAgLy8gTm8gcHJvamVjdCBkZWYgLT4gZXJyb3JcclxuICAgICAgdGhpcy5zdGF0dXNBbGVydFR5cGUgPSAnZXJyb3InO1xyXG4gICAgICB0aGlzLnN0YXR1c01zZyA9ICdObyBwcm9qZWN0IGRlZmluaXRvbi4nO1xyXG4gICAgICB0aGlzLnN0YXR1c1dhaXRpbmcgPSBmYWxzZTtcclxuICAgICAgY29uc29sZS5lcnJvcih0aGlzLnN0YXR1c01zZyk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuXHJcbiAgc2V0IHByb2plY3QocHJvamVjdDogUHJvamVjdHx1bmRlZmluZWR8bnVsbCkge1xyXG4gICAgdGhpcy5fcHJvamVjdCA9IHByb2plY3Q7XHJcbiAgICBsZXQgY2hDbnQgPSBQcm9qZWN0VXRpbC5ERUZBVUxUX0FVRElPX0NIQU5ORUxfQ09VTlQ7XHJcblxyXG4gICAgaWYgKHByb2plY3QpIHtcclxuICAgICAgY29uc29sZS5pbmZvKFwiUHJvamVjdCBuYW1lOiBcIiArIHByb2plY3QubmFtZSlcclxuICAgICAgaWYgKHByb2plY3QucmVjb3JkaW5nRGV2aWNlV2FrZUxvY2sgPT09IHRydWUpIHtcclxuICAgICAgICB0aGlzLndha2VMb2NrID0gdHJ1ZTtcclxuICAgICAgfVxyXG4gICAgICB0aGlzLmF1ZGlvRGV2aWNlcyA9IHByb2plY3QuYXVkaW9EZXZpY2VzO1xyXG4gICAgICBjaENudCA9IFByb2plY3RVdGlsLmF1ZGlvQ2hhbm5lbENvdW50KHByb2plY3QpO1xyXG4gICAgICBjb25zb2xlLmluZm8oXCJQcm9qZWN0IHJlcXVlc3RlZCByZWNvcmRpbmcgY2hhbm5lbCBjb3VudDogXCIgKyBjaENudCk7XHJcbiAgICAgIHRoaXMuYXV0b0dhaW5Db250cm9sQ29uZmlncyA9IHByb2plY3QuYXV0b0dhaW5Db250cm9sQ29uZmlncztcclxuICAgICAgaWYocHJvamVjdC5hbGxvd0VjaG9DYW5jZWxsYXRpb24hPT11bmRlZmluZWQpIHtcclxuICAgICAgICB0aGlzLmFsbG93RWNob0NhbmNlbGxhdGlvbiA9IHByb2plY3QuYWxsb3dFY2hvQ2FuY2VsbGF0aW9uO1xyXG4gICAgICB9XHJcbiAgICAgIGlmIChwcm9qZWN0LmNodW5rZWRSZWNvcmRpbmcgPT09IHRydWUpIHtcclxuICAgICAgICB0aGlzLnVwbG9hZENodW5rU2l6ZVNlY29uZHMgPSBCYXNpY1JlY29yZGVyLkRFRkFVTFRfQ0hVTktfU0laRV9TRUNPTkRTO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRoaXMudXBsb2FkQ2h1bmtTaXplU2Vjb25kcyA9IG51bGw7XHJcbiAgICAgIH1cclxuICAgICAgaWYgKHByb2plY3QuY2xpZW50QXVkaW9TdG9yYWdlVHlwZSkge1xyXG4gICAgICAgIHRoaXMuY2xpZW50QXVkaW9TdG9yYWdlVHlwZSA9IHByb2plY3QuY2xpZW50QXVkaW9TdG9yYWdlVHlwZTtcclxuICAgICAgfVxyXG4gICAgfSBlbHNlIHtcclxuICAgICAgY29uc29sZS5lcnJvcihcIkVtcHR5IHByb2plY3QgY29uZmlndXJhdGlvbiFcIilcclxuICAgIH1cclxuICAgIHRoaXMuY2hhbm5lbENvdW50ID0gY2hDbnQ7XHJcbiAgfVxyXG5cclxuXHJcbiAgZ2V0IHByb2plY3QoKTpQcm9qZWN0fHVuZGVmaW5lZHxudWxse1xyXG4gICAgcmV0dXJuIHRoaXMuX3Byb2plY3Q7XHJcbiAgfVxyXG5cclxuICBzZWxlY3RSZWNvcmRpbmdGaWxlKHJmOlJlY29yZGluZ0ZpbGUpe1xyXG4gICAgdGhpcy5saXZlTGV2ZWxEaXNwbGF5U3RhdGU9TGl2ZUxldmVsU3RhdGUuUkVBRFk7XHJcbiAgICB0aGlzLmtlZXBMaXZlTGV2ZWw9ZmFsc2U7XHJcbiAgICB0aGlzLmRpc3BsYXlSZWNGaWxlPXJmO1xyXG4gIH1cclxuXHJcbiAgdXBsb2FkVXBkYXRlKHVlOiBVcGxvYWRlclN0YXR1c0NoYW5nZUV2ZW50KSB7XHJcbiAgICBsZXQgdXBTdGF0dXMgPSB1ZS5zdGF0dXM7XHJcbiAgICB0aGlzLmRhdGFTYXZlZCA9IChVcGxvYWRlclN0YXR1cy5ET05FID09PSB1cFN0YXR1cyk7XHJcbiAgICBsZXQgcGVyY2VudFVwbCA9IHVlLnBlcmNlbnREb25lKCk7XHJcbiAgICBpZiAoVXBsb2FkZXJTdGF0dXMuRVJSID09PSB1cFN0YXR1cykge1xyXG4gICAgICB0aGlzLnVwbG9hZFN0YXR1cyA9ICd3YXJuJ1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgaWYgKHBlcmNlbnRVcGwgPCA1MCkge1xyXG4gICAgICAgIHRoaXMudXBsb2FkU3RhdHVzID0gJ2FjY2VudCdcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0aGlzLnVwbG9hZFN0YXR1cyA9ICdzdWNjZXNzJ1xyXG4gICAgICB9XHJcbiAgICAgIHRoaXMudXBsb2FkUHJvZ3Jlc3MgPSBwZXJjZW50VXBsO1xyXG4gICAgfVxyXG5cclxuICAgIHRoaXMuY2hhbmdlRGV0ZWN0b3JSZWYuZGV0ZWN0Q2hhbmdlcygpXHJcbiAgfVxyXG5cclxuICBzZXQgY29udHJvbEF1ZGlvUGxheWVyKGNvbnRyb2xBdWRpb1BsYXllcjogQXVkaW9QbGF5ZXJ8bnVsbCkge1xyXG4gICAgdGhpcy5fY29udHJvbEF1ZGlvUGxheWVyPWNvbnRyb2xBdWRpb1BsYXllcjtcclxuICAgIGlmICh0aGlzLl9jb250cm9sQXVkaW9QbGF5ZXIpIHtcclxuICAgICAgdGhpcy5fY29udHJvbEF1ZGlvUGxheWVyLmxpc3RlbmVyID0gdGhpcztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIGdldCBjb250cm9sQXVkaW9QbGF5ZXIoKTogQXVkaW9QbGF5ZXJ8bnVsbCB7XHJcbiAgICByZXR1cm4gdGhpcy5fY29udHJvbEF1ZGlvUGxheWVyO1xyXG4gIH1cclxuXHJcbiAgdXBkYXRlKGU6IEF1ZGlvUGxheWVyRXZlbnQpIHtcclxuICAgIGlmIChlLnR5cGUgPT0gRXZlbnRUeXBlLlNUQVJURUQpIHtcclxuICAgICAgdGhpcy5wbGF5U3RhcnRBY3Rpb24uZGlzYWJsZWQgPSB0cnVlO1xyXG4gICAgICB0aGlzLnVwZGF0ZVRpbWVySWQgPSB3aW5kb3cuc2V0SW50ZXJ2YWwoKCkgPT4ge1xyXG4gICAgICAgIC8vdGhpcy5hdWRpb1NpZ25hbC5wbGF5RnJhbWVQb3NpdGlvbiA9IHRoaXMuYXAucGxheVBvc2l0aW9uRnJhbWVzO1xyXG4gICAgICB9LCA1MCk7XHJcbiAgICB9IGVsc2UgaWYgKGUudHlwZSA9PSBFdmVudFR5cGUuU1RPUFBFRCB8fCBlLnR5cGUgPT0gRXZlbnRUeXBlLkVOREVEKSB7XHJcbiAgICAgIHdpbmRvdy5jbGVhckludGVydmFsKHRoaXMudXBkYXRlVGltZXJJZCk7XHJcbiAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIkVuYWJsZSBwbGF5IHN0YXJ0IGFjdGlvbiAoYnkgcGxheWVyIGV2ZW50cyBzdG9wcGVkIG9yIGVuZGVkKTogXCIrKCEodGhpcy5kaXNwbGF5UmVjRmlsZSkpKTtcclxuICAgICAgdGhpcy5wbGF5U3RhcnRBY3Rpb24uZGlzYWJsZWQgPSAoISh0aGlzLmRpc3BsYXlSZWNGaWxlKSk7XHJcblxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgc3RhcnREaXNhYmxlZCgpIHtcclxuICAgIHJldHVybiAhdGhpcy50cmFuc3BvcnRBY3Rpb25zIHx8IHRoaXMucmVhZG9ubHkgfHwgdGhpcy50cmFuc3BvcnRBY3Rpb25zLnN0YXJ0QWN0aW9uLmRpc2FibGVkXHJcbiAgfVxyXG5cclxuICBzdG9wRGlzYWJsZWQoKSB7XHJcbiAgICByZXR1cm4gIXRoaXMudHJhbnNwb3J0QWN0aW9ucyB8fCB0aGlzLnRyYW5zcG9ydEFjdGlvbnMuc3RvcEFjdGlvbi5kaXNhYmxlZFxyXG4gIH1cclxuXHJcbiAgc3RhcnRTdG9wTmV4dE5hbWUoKTpzdHJpbmd7XHJcbiAgICBpZighdGhpcy5zdGFydERpc2FibGVkKCkpe1xyXG4gICAgICB0aGlzLnN0YXJ0U3RvcE5leHRCdXR0b25OYW1lPVwiU3RhcnRcIlxyXG4gICAgfWVsc2UgaWYoIXRoaXMuc3RvcERpc2FibGVkKCkpIHtcclxuICAgICAgdGhpcy5zdGFydFN0b3BOZXh0QnV0dG9uTmFtZSA9IFwiU3RvcFwiXHJcbiAgICB9XHJcbiAgICByZXR1cm4gdGhpcy5zdGFydFN0b3BOZXh0QnV0dG9uTmFtZTtcclxuICB9XHJcbiAgc3RhcnRTdG9wTmV4dEljb25OYW1lKCk6c3RyaW5ne1xyXG4gICAgaWYoIXRoaXMuc3RhcnREaXNhYmxlZCgpKXtcclxuICAgICAgdGhpcy5zdGFydFN0b3BOZXh0QnV0dG9uSWNvbk5hbWU9XCJmaWJlcl9tYW51YWxfcmVjb3JkXCJcclxuICAgIH1lbHNlIGlmKCF0aGlzLnN0b3BEaXNhYmxlZCgpKXtcclxuICAgICAgdGhpcy5zdGFydFN0b3BOZXh0QnV0dG9uSWNvbk5hbWU9XCJzdG9wXCJcclxuICAgIH1cclxuICAgIHJldHVybiB0aGlzLnN0YXJ0U3RvcE5leHRCdXR0b25JY29uTmFtZVxyXG4gIH1cclxuICBzdGFydFN0b3BOZXh0SWNvbkNvbG9yKCk6c3RyaW5ne1xyXG4gICAgaWYoIXRoaXMuc3RhcnREaXNhYmxlZCgpKXtcclxuICAgICAgcmV0dXJuIFwicmVkXCJcclxuICAgIH1lbHNlIGlmKCF0aGlzLnN0b3BEaXNhYmxlZCgpKXtcclxuICAgICAgcmV0dXJuIFwieWVsbG93XCJcclxuICAgIH1lbHNle1xyXG4gICAgICByZXR1cm4gXCJncmV5XCI7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBzdGFydFN0b3BQZXJmb3JtKCl7XHJcbiAgICBpZighdGhpcy5zdGFydERpc2FibGVkKCkpe1xyXG4gICAgICB0aGlzLnRyYW5zcG9ydEFjdGlvbnMuc3RhcnRBY3Rpb24ucGVyZm9ybSgpO1xyXG4gICAgfWVsc2UgaWYoIXRoaXMuc3RvcERpc2FibGVkKCkpe1xyXG4gICAgICB0aGlzLnRyYW5zcG9ydEFjdGlvbnMuc3RvcEFjdGlvbi5wZXJmb3JtKCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBzdGFydEl0ZW0oKSB7XHJcbiAgICB0aGlzLnN0YXR1cz1TdGF0dXMuU1RBUlRJTkc7XHJcbiAgICBzdXBlci5zdGFydEl0ZW0oKTtcclxuICAgIGlmICh0aGlzLnJlYWRvbmx5KSB7XHJcbiAgICAgIHRoaXMuc3RhdHVzPVN0YXR1cy5JRExFO1xyXG4gICAgICByZXR1cm5cclxuICAgIH1cclxuICAgIHRoaXMudHJhbnNwb3J0QWN0aW9ucy5md2RBY3Rpb24uZGlzYWJsZWQgPSB0cnVlXHJcbiAgICB0aGlzLnRyYW5zcG9ydEFjdGlvbnMuZndkTmV4dEFjdGlvbi5kaXNhYmxlZCA9IHRydWVcclxuICAgIHRoaXMudHJhbnNwb3J0QWN0aW9ucy5id2RBY3Rpb24uZGlzYWJsZWQgPSB0cnVlXHJcbiAgICB0aGlzLmRpc3BsYXlSZWNGaWxlID0gbnVsbDtcclxuICAgIHRoaXMuZGlzcGxheVJlY0ZpbGVWZXJzaW9uID0gMDtcclxuICAgIHRoaXMuZGlzcGxheUF1ZGlvQ2xpcCA9IG51bGw7XHJcbiAgICB0aGlzLmxpdmVMZXZlbERpc3BsYXkucmVzZXQodHJ1ZSk7XHJcbiAgICB0aGlzLmxpdmVMZXZlbERpc3BsYXlTdGF0ZT1MaXZlTGV2ZWxTdGF0ZS5SRUFEWTtcclxuICAgIHRoaXMuc2hvd1JlY29yZGluZygpO1xyXG5cclxuICAgIHRoaXMuc3RhcnRDYXB0dXJlKCk7XHJcbiAgfVxyXG5cclxuXHJcbiAgZG93bmxvYWRSZWNvcmRpbmcoKSB7XHJcbiAgICBpZiAodGhpcy5kaXNwbGF5UmVjRmlsZSkge1xyXG4gICAgICBsZXQgYWI6IEF1ZGlvRGF0YUhvbGRlciB8IG51bGwgPSB0aGlzLmRpc3BsYXlSZWNGaWxlLmF1ZGlvRGF0YUhvbGRlcjtcclxuICAgICAgbGV0IHd3ID0gbmV3IFdhdldyaXRlcigpO1xyXG4gICAgICBsZXQgYXM9YWI/LmF1ZGlvU291cmNlO1xyXG4gICAgICBpZihhcyBpbnN0YW5jZW9mIEF1ZGlvQnVmZmVyU291cmNlKSB7XHJcbiAgICAgICAgICB3dy53cml0ZUFzeW5jKGFzLmF1ZGlvQnVmZmVyLCAod2F2RmlsZSkgPT4ge1xyXG4gICAgICAgICAgICBsZXQgYmxvYiA9IG5ldyBCbG9iKFt3YXZGaWxlXSwge3R5cGU6ICdhdWRpby93YXYnfSk7XHJcbiAgICAgICAgICAgIGxldCByZlVybCA9IFVSTC5jcmVhdGVPYmplY3RVUkwoYmxvYik7XHJcblxyXG4gICAgICAgICAgICBsZXQgZGF0YURubExuayA9IHRoaXMucmVuZGVyZXIuY3JlYXRlRWxlbWVudCgnYScpO1xyXG4gICAgICAgICAgICAvL2RhdGFEbmxMbmsubmFtZSA9ICdSZWNvcmRpbmcnO1xyXG4gICAgICAgICAgICBkYXRhRG5sTG5rLmhyZWYgPSByZlVybDtcclxuXHJcbiAgICAgICAgICAgIHRoaXMucmVuZGVyZXIuYXBwZW5kQ2hpbGQoZG9jdW1lbnQuYm9keSwgZGF0YURubExuayk7XHJcblxyXG4gICAgICAgICAgICAvLyBkb3dubG9hZCBwcm9wZXJ0eSBub3QgeWV0IGluIFRTIGRlZlxyXG4gICAgICAgICAgICBpZiAodGhpcy5kaXNwbGF5UmVjRmlsZSkge1xyXG4gICAgICAgICAgICAgIGxldCBmbiA9IHRoaXMuZGlzcGxheVJlY0ZpbGUuZmlsZW5hbWVTdHJpbmcoKTtcclxuICAgICAgICAgICAgICBmbiArPSAnXycgKyB0aGlzLmRpc3BsYXlSZWNGaWxlVmVyc2lvbjtcclxuICAgICAgICAgICAgICBmbiArPSAnLndhdic7XHJcbiAgICAgICAgICAgICAgZGF0YURubExuay5kb3dubG9hZCA9IGZuO1xyXG4gICAgICAgICAgICAgIGRhdGFEbmxMbmsuY2xpY2soKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB0aGlzLnJlbmRlcmVyLnJlbW92ZUNoaWxkKGRvY3VtZW50LmJvZHksIGRhdGFEbmxMbmspO1xyXG4gICAgICAgICAgfSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcblxyXG4gIHNldCBkaXNwbGF5UmVjRmlsZShkaXNwbGF5UmVjRmlsZTogUmVjb3JkaW5nRmlsZSB8IG51bGwpIHtcclxuICAgIC8vdGhpcy5hdWRpb0xvYWRlZD1mYWxzZTtcclxuICAgIHRoaXMuX2Rpc3BsYXlSZWNGaWxlID0gZGlzcGxheVJlY0ZpbGU7XHJcbiAgICBpZiAodGhpcy5fZGlzcGxheVJlY0ZpbGUpIHtcclxuICAgICAgbGV0IGFkaDogQXVkaW9EYXRhSG9sZGVyfCBudWxsID0gdGhpcy5fZGlzcGxheVJlY0ZpbGUuYXVkaW9EYXRhSG9sZGVyO1xyXG4gICAgICBpZihhZGgpIHtcclxuICAgICAgICB0aGlzLmRpc3BsYXlBdWRpb0NsaXAgPSBuZXcgQXVkaW9DbGlwKGFkaCk7XHJcbiAgICAgICAgLy90aGlzLmF1ZGlvTG9hZGVkPXRydWU7XHJcbiAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiIHNldCByZWNvcmRpbmcgZmlsZTogZGlzcGxheSBhdWRpbyBjbGlwIHNldFwiKTtcclxuICAgICAgICBpZih0aGlzLl9jb250cm9sQXVkaW9QbGF5ZXIpIHtcclxuICAgICAgICAgIHRoaXMuX2NvbnRyb2xBdWRpb1BsYXllci5hdWRpb0NsaXAgPSB0aGlzLmRpc3BsYXlBdWRpb0NsaXA7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRoaXMuc2hvd1JlY29yZGluZygpO1xyXG4gICAgICB9ZWxzZSB7XHJcbiAgICAgICAgLy8gY2xlYXIgZm9yIG5vdyAuLi5cclxuICAgICAgICB0aGlzLmRpc3BsYXlBdWRpb0NsaXAgPSBudWxsO1xyXG4gICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcInNldCByZWNvcmRpbmcgZmlsZTogZGlzcGxheSBhdWRpbyBjbGlwIG51bGxcIik7XHJcbiAgICAgICAgaWYodGhpcy5fY29udHJvbEF1ZGlvUGxheWVyKSB7XHJcbiAgICAgICAgICB0aGlzLl9jb250cm9sQXVkaW9QbGF5ZXIuYXVkaW9DbGlwID0gbnVsbDtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmICh0aGlzLl9jb250cm9sQXVkaW9QbGF5ZXIgJiYgdGhpcy5fc2Vzc2lvbikge1xyXG4gICAgICAgICAgLy8uLi4gYW5kIHRyeSB0byBmZXRjaCBmcm9tIHNlcnZlclxyXG4gICAgICAgICAgdGhpcy5saXZlTGV2ZWxEaXNwbGF5U3RhdGU9TGl2ZUxldmVsU3RhdGUuTE9BRElORztcclxuICAgICAgICAgIGNvbnN0IHJmPXRoaXMuX2Rpc3BsYXlSZWNGaWxlO1xyXG5cclxuICAgICAgICAgIGxldCBhdWRpb0Rvd25sb2FkVHlwZT10aGlzLl9jbGllbnRBdWRpb1N0b3JhZ2VUeXBlO1xyXG4gICAgICAgICAgaWYoQXVkaW9TdG9yYWdlVHlwZS5NRU1fRU5USVJFX0FVVE9fTkVUX0NIVU5LRUQ9PT10aGlzLl9jbGllbnRBdWRpb1N0b3JhZ2VUeXBlIHx8IEF1ZGlvU3RvcmFnZVR5cGUuTUVNX0NIVU5LRURfQVVUT19ORVRfQ0hVTktFRD09PXRoaXMuX2NsaWVudEF1ZGlvU3RvcmFnZVR5cGUpIHtcclxuICAgICAgICAgICAgLy8gRGVmYXVsdCBpcyBuZXR3b3JrIG1vZGVcclxuICAgICAgICAgICAgYXVkaW9Eb3dubG9hZFR5cGU9QXVkaW9TdG9yYWdlVHlwZS5ORVRfQ0hVTktFRDtcclxuICAgICAgICAgICAgaWYgKHJmLmNoYW5uZWxzICYmIHJmLmZyYW1lcykge1xyXG4gICAgICAgICAgICAgIGNvbnN0IHNhbXBsZXMgPSByZi5jaGFubmVscyAqIHJmLmZyYW1lcztcclxuICAgICAgICAgICAgICBpZiAoc2FtcGxlcyA8PSB0aGlzLl9tYXhBdXRvTmV0TWVtU3RvcmVTYW1wbGVzKSB7XHJcbiAgICAgICAgICAgICAgICAvLyBCdXQgaWYgYXVkaW8gZmlsZSBpcyBzbWFsbCwgbG9hZCBpbiBjb250aW51b3VzIHJlc3AuIGNodW5rZWQgbW9kZVxyXG4gICAgICAgICAgICAgICAgaWYoQXVkaW9TdG9yYWdlVHlwZS5NRU1fRU5USVJFX0FVVE9fTkVUX0NIVU5LRUQ9PT10aGlzLl9jbGllbnRBdWRpb1N0b3JhZ2VUeXBlKXtcclxuICAgICAgICAgICAgICAgICAgYXVkaW9Eb3dubG9hZFR5cGU9QXVkaW9TdG9yYWdlVHlwZS5NRU1fRU5USVJFO1xyXG4gICAgICAgICAgICAgICAgfWVsc2UgaWYoQXVkaW9TdG9yYWdlVHlwZS5NRU1fQ0hVTktFRF9BVVRPX05FVF9DSFVOS0VEPT09dGhpcy5fY2xpZW50QXVkaW9TdG9yYWdlVHlwZSkge1xyXG4gICAgICAgICAgICAgICAgICBhdWRpb0Rvd25sb2FkVHlwZSA9IEF1ZGlvU3RvcmFnZVR5cGUuTUVNX0NIVU5LRUQ7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgaWYoQXVkaW9TdG9yYWdlVHlwZS5EQl9DSFVOS0VEPT09dGhpcy5fY2xpZW50QXVkaW9TdG9yYWdlVHlwZSl7XHJcbiAgICAgICAgICAgIC8vIEZldGNoIGNodW5rZWQgaW5kZXhlZCBkYiBhdWRpbyBidWZmZXJcclxuICAgICAgICAgICAgbGV0IG5leHRJYWI6IEluZGV4ZWREYkF1ZGlvQnVmZmVyIHwgbnVsbCA9IG51bGw7XHJcbiAgICAgICAgICAgIGlmKCF0aGlzLl9wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0KXtcclxuICAgICAgICAgICAgICB0aHJvdyBFcnJvcignRXJyb3I6IFBlcnNpc3RlbnQgc3RvcmFnZSB0YXJnZXQgbm90IHNldC4nKTtcclxuICAgICAgICAgICAgfWVsc2Uge1xyXG4gICAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIkZldGNoIGF1ZGlvIGFuZCBzdG9yZSB0byBpbmRleGVkIGRiLi4uXCIpO1xyXG4gICAgICAgICAgICAgIHRoaXMuYXVkaW9GZXRjaFN1YnNjcmlwdGlvbiA9IHRoaXMucmVjRmlsZVNlcnZpY2UuZmV0Y2hSZWNvcmRpbmdGaWxlSW5kRGJBdWRpb0J1ZmZlcih0aGlzLl9wZXJzaXN0ZW50QXVkaW9TdG9yYWdlVGFyZ2V0LCB0aGlzLl9zZXNzaW9uLnByb2plY3QsIHJmKS5zdWJzY3JpYmUoe1xyXG4gICAgICAgICAgICAgICAgbmV4dDogKGlhYikgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJTZXNzaW9ubWFuYWdlcjogUmVjZWl2ZWQgaW5kZGIgYXVkaW8gYnVmZmVyOiBcIitpYWIpO1xyXG4gICAgICAgICAgICAgICAgICBuZXh0SWFiID0gaWFiO1xyXG4gICAgICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgICAgIGNvbXBsZXRlOiAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgIHRoaXMubGl2ZUxldmVsRGlzcGxheVN0YXRlID0gTGl2ZUxldmVsU3RhdGUuUkVBRFk7XHJcbiAgICAgICAgICAgICAgICAgIGxldCBmYWJEaCA9IG51bGw7XHJcbiAgICAgICAgICAgICAgICAgIGlmIChuZXh0SWFiKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHJmICkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgZmFiRGggPSBuZXcgQXVkaW9EYXRhSG9sZGVyKG5leHRJYWIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgLy90aGlzLmF1ZGlvTG9hZGVkPXRydWU7XHJcbiAgICAgICAgICAgICAgICAgICAgICB0aGlzLnJlY29yZGVyQ29tYmlQYW5lLnNldFJlY0ZpbGVBdWRpb0RhdGEocmYsIGZhYkRoKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAgLy8gU2hvdWxkIGFjdHVhbGx5IGJlIGhhbmRsZWQgYnkgdGhlIGVycm9yIHJlc29sdmVyXHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zdGF0dXNNc2cgPSAnUmVjb3JkaW5nIGZpbGUgY291bGQgbm90IGJlIGxvYWRlZC4nXHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zdGF0dXNBbGVydFR5cGUgPSAnZXJyb3InXHJcbiAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgaWYgKGZhYkRoKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgLy8gdGhpcy5kaXNwbGF5QXVkaW9DbGlwIGNvdWxkIGhhdmUgYmVlbiBjaGFuZ2VkIG1lYW53aGlsZSwgYnV0IHRoZSByZWNvcmRlciB1bnN1YmNyaWJlcyBiZWZvcmUgY2hhbmdpbmcgdGhlIGl0ZW0uIFRoZXJlZm9yZSwgdGhlcmUgc2hvdWxkIGJlIG5vIHJpc2sgdG8gc2V0IHRvIHdyb25nIGl0ZW1cclxuICAgICAgICAgICAgICAgICAgICB0aGlzLmRpc3BsYXlBdWRpb0NsaXAgPSBuZXcgQXVkaW9DbGlwKGZhYkRoKTtcclxuICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICBpZih0aGlzLl9jb250cm9sQXVkaW9QbGF5ZXIpIHtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9jb250cm9sQXVkaW9QbGF5ZXIuYXVkaW9DbGlwID0gdGhpcy5kaXNwbGF5QXVkaW9DbGlwXHJcbiAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgdGhpcy5zaG93UmVjb3JkaW5nKCk7XHJcbiAgICAgICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAgICAgZXJyb3I6IGVyciA9PiB7XHJcbiAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJDb3VsZCBub3QgbG9hZCByZWNvcmRpbmcgZmlsZSBmcm9tIHNlcnZlcjogXCIgKyBlcnIpO1xyXG4gICAgICAgICAgICAgICAgICB0aGlzLmxpdmVMZXZlbERpc3BsYXlTdGF0ZSA9IExpdmVMZXZlbFN0YXRlLlJFQURZO1xyXG4gICAgICAgICAgICAgICAgICB0aGlzLnN0YXR1c01zZyA9ICdSZWNvcmRpbmcgZmlsZSBjb3VsZCBub3QgYmUgbG9hZGVkOiAnICsgZXJyO1xyXG4gICAgICAgICAgICAgICAgICB0aGlzLnN0YXR1c0FsZXJ0VHlwZSA9ICdlcnJvcic7XHJcbiAgICAgICAgICAgICAgICAgIHRoaXMuY2hhbmdlRGV0ZWN0b3JSZWYuZGV0ZWN0Q2hhbmdlcygpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9ZWxzZSBpZihBdWRpb1N0b3JhZ2VUeXBlLk5FVF9DSFVOS0VEPT09YXVkaW9Eb3dubG9hZFR5cGUpe1xyXG4gICAgICAgICAgICAvLyBGZXRjaCBjaHVua2VkIGF1ZGlvIGJ1ZmZlciBmcm9tIG5ldHdvcmtcclxuICAgICAgICAgICAgbGV0IG5leHROZXRBYjogTmV0QXVkaW9CdWZmZXIgfCBudWxsID0gbnVsbDtcclxuXHJcbiAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcIkZldGNoIGNodW5rZWQgYXVkaW8gZnJvbSBuZXR3b3JrXCIpO1xyXG4gICAgICAgICAgICB0aGlzLmF1ZGlvRmV0Y2hTdWJzY3JpcHRpb24gPSB0aGlzLnJlY0ZpbGVTZXJ2aWNlLmZldGNoUmVjb3JkaW5nRmlsZU5ldEF1ZGlvQnVmZmVyKCB0aGlzLl9zZXNzaW9uLnByb2plY3QsIHJmKS5zdWJzY3JpYmUoe1xyXG4gICAgICAgICAgICAgIG5leHQ6IChuZXRBYikgPT4ge1xyXG4gICAgICAgICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiU2Vzc2lvbm1hbmFnZXI6IFJlY2VpdmVkIG5ldCBhdWRpbyBidWZmZXI6IFwiK25ldEFiKTtcclxuICAgICAgICAgICAgICAgIG5leHROZXRBYiA9IG5ldEFiO1xyXG4gICAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgICAgY29tcGxldGU6ICgpID0+IHtcclxuICAgICAgICAgICAgICAgIHRoaXMubGl2ZUxldmVsRGlzcGxheVN0YXRlID0gTGl2ZUxldmVsU3RhdGUuUkVBRFk7XHJcbiAgICAgICAgICAgICAgICBsZXQgZmFiRGggPSBudWxsO1xyXG4gICAgICAgICAgICAgICAgaWYgKG5leHROZXRBYikge1xyXG4gICAgICAgICAgICAgICAgICBpZiAocmYpIHtcclxuICAgICAgICAgICAgICAgICAgICBmYWJEaCA9IG5ldyBBdWRpb0RhdGFIb2xkZXIobmV4dE5ldEFiKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5yZWNvcmRlckNvbWJpUGFuZS5zZXRSZWNGaWxlQXVkaW9EYXRhKHJmLCBmYWJEaCk7XHJcbiAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIFNob3VsZCBhY3R1YWxseSBiZSBoYW5kbGVkIGJ5IHRoZSBlcnJvciByZXNvbHZlclxyXG4gICAgICAgICAgICAgICAgICB0aGlzLnN0YXR1c01zZyA9ICdSZWNvcmRpbmcgZmlsZSBjb3VsZCBub3QgYmUgbG9hZGVkLidcclxuICAgICAgICAgICAgICAgICAgdGhpcy5zdGF0dXNBbGVydFR5cGUgPSAnZXJyb3InXHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBpZiAoZmFiRGgpIHtcclxuICAgICAgICAgICAgICAgICAgLy8gdGhpcy5kaXNwbGF5QXVkaW9DbGlwIGNvdWxkIGhhdmUgYmVlbiBjaGFuZ2VkIG1lYW53aGlsZSwgYnV0IHRoZSByZWNvcmRlciB1bnN1YmNyaWJlcyBiZWZvcmUgY2hhbmdpbmcgdGhlIGl0ZW0uIFRoZXJlZm9yZSwgdGhlcmUgc2hvdWxkIGJlIG5vIHJpc2sgdG8gc2V0IHRvIHdyb25nIGl0ZW1cclxuICAgICAgICAgICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwic2V0IGRpc3BsYXlSZWNGaWxlKCk6IGZldGNoIG5ldCBhYiBjb21wbGV0ZSwgc2V0IGRpc3BsYXlBdWRpb0NsaXAuXCIpXHJcbiAgICAgICAgICAgICAgICAgIHRoaXMuZGlzcGxheUF1ZGlvQ2xpcCA9IG5ldyBBdWRpb0NsaXAoZmFiRGgpO1xyXG4gICAgICAgICAgICAgICAgICAvLyBmYWJEaC5vblJlYWR5PSgpPT57XHJcbiAgICAgICAgICAgICAgICAgIC8vICAgdGhpcy5hdWRpb0xvYWRlZD10cnVlO1xyXG4gICAgICAgICAgICAgICAgICAvLyB9XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBpZih0aGlzLl9jb250cm9sQXVkaW9QbGF5ZXIpIHtcclxuICAgICAgICAgICAgICAgICAgdGhpcy5fY29udHJvbEF1ZGlvUGxheWVyLmF1ZGlvQ2xpcCA9IHRoaXMuZGlzcGxheUF1ZGlvQ2xpcDtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIHRoaXMuc2hvd1JlY29yZGluZygpO1xyXG4gICAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgICAgZXJyb3I6IGVyciA9PiB7XHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiQ291bGQgbm90IGxvYWQgcmVjb3JkaW5nIGZpbGUgZnJvbSBzZXJ2ZXI6IFwiICsgZXJyKTtcclxuICAgICAgICAgICAgICAgIHRoaXMubGl2ZUxldmVsRGlzcGxheVN0YXRlID0gTGl2ZUxldmVsU3RhdGUuUkVBRFk7XHJcbiAgICAgICAgICAgICAgICB0aGlzLnN0YXR1c01zZyA9ICdSZWNvcmRpbmcgZmlsZSBjb3VsZCBub3QgYmUgbG9hZGVkOiAnICsgZXJyO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5zdGF0dXNBbGVydFR5cGUgPSAnZXJyb3InO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5jaGFuZ2VEZXRlY3RvclJlZi5kZXRlY3RDaGFuZ2VzKCk7XHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgICB9ZWxzZSBpZihBdWRpb1N0b3JhZ2VUeXBlLk1FTV9DSFVOS0VEPT09YXVkaW9Eb3dubG9hZFR5cGUpe1xyXG4gICAgICAgICAgICAvLyBGZXRjaCBjaHVua2VkIGFycmF5IGF1ZGlvIGJ1ZmZlclxyXG4gICAgICAgICAgICBsZXQgbmV4dEFhYjogQXJyYXlBdWRpb0J1ZmZlciB8IG51bGwgPSBudWxsO1xyXG4gICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJGZXRjaCBhdWRpbyBhbmQgc3RvcmUgdG8gKGNodW5rZWQpIGFycmF5IGJ1ZmZlci4uLlwiKTtcclxuICAgICAgICAgICAgdGhpcy5hdWRpb0ZldGNoU3Vic2NyaXB0aW9uID0gdGhpcy5yZWNGaWxlU2VydmljZS5mZXRjaFJlY29yZGluZ0ZpbGVBcnJheUF1ZGlvQnVmZmVyKCB0aGlzLl9zZXNzaW9uLnByb2plY3QsIHJmKS5zdWJzY3JpYmUoe1xyXG4gICAgICAgICAgICAgIG5leHQ6IChhYWIpID0+IHtcclxuICAgICAgICAgICAgICAgIG5leHRBYWIgPSBhYWI7XHJcbiAgICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgICBjb21wbGV0ZTogKCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5saXZlTGV2ZWxEaXNwbGF5U3RhdGUgPSBMaXZlTGV2ZWxTdGF0ZS5SRUFEWTtcclxuICAgICAgICAgICAgICAgIGxldCBmYWJEaCA9IG51bGw7XHJcbiAgICAgICAgICAgICAgICBpZiAobmV4dEFhYikge1xyXG4gICAgICAgICAgICAgICAgICBpZiAocmYgKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgZmFiRGggPSBuZXcgQXVkaW9EYXRhSG9sZGVyKG5leHRBYWIpO1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMucmVjb3JkZXJDb21iaVBhbmUuc2V0UmVjRmlsZUF1ZGlvRGF0YShyZiwgZmFiRGgpO1xyXG4gICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAvLyBTaG91bGQgYWN0dWFsbHkgYmUgaGFuZGxlZCBieSB0aGUgZXJyb3IgcmVzb2x2ZXJcclxuICAgICAgICAgICAgICAgICAgdGhpcy5zdGF0dXNNc2cgPSAnUmVjb3JkaW5nIGZpbGUgY291bGQgbm90IGJlIGxvYWRlZC4nXHJcbiAgICAgICAgICAgICAgICAgIHRoaXMuc3RhdHVzQWxlcnRUeXBlID0gJ2Vycm9yJ1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgaWYgKGZhYkRoKSB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIHRoaXMuZGlzcGxheUF1ZGlvQ2xpcCBjb3VsZCBoYXZlIGJlZW4gY2hhbmdlZCBtZWFud2hpbGUsIGJ1dCB0aGUgcmVjb3JkZXIgdW5zdWJjcmliZXMgYmVmb3JlIGNoYW5naW5nIHRoZSBpdGVtLiBUaGVyZWZvcmUsIHRoZXJlIHNob3VsZCBiZSBubyByaXNrIHRvIHNldCB0byB3cm9uZyBpdGVtXHJcbiAgICAgICAgICAgICAgICAgIHRoaXMuZGlzcGxheUF1ZGlvQ2xpcCA9IG5ldyBBdWRpb0NsaXAoZmFiRGgpO1xyXG4gICAgICAgICAgICAgICAgICAvL3RoaXMuYXVkaW9Mb2FkZWQ9dHJ1ZTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIGlmKHRoaXMuX2NvbnRyb2xBdWRpb1BsYXllcikge1xyXG4gICAgICAgICAgICAgICAgICB0aGlzLl9jb250cm9sQXVkaW9QbGF5ZXIuYXVkaW9DbGlwID0gdGhpcy5kaXNwbGF5QXVkaW9DbGlwO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgdGhpcy5zaG93UmVjb3JkaW5nKCk7XHJcbiAgICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgICBlcnJvcjogZXJyID0+IHtcclxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJDb3VsZCBub3QgbG9hZCByZWNvcmRpbmcgZmlsZSBmcm9tIHNlcnZlcjogXCIgKyBlcnIpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5saXZlTGV2ZWxEaXNwbGF5U3RhdGUgPSBMaXZlTGV2ZWxTdGF0ZS5SRUFEWTtcclxuICAgICAgICAgICAgICAgIHRoaXMuc3RhdHVzTXNnID0gJ1JlY29yZGluZyBmaWxlIGNvdWxkIG5vdCBiZSBsb2FkZWQ6ICcgKyBlcnI7XHJcbiAgICAgICAgICAgICAgICB0aGlzLnN0YXR1c0FsZXJ0VHlwZSA9ICdlcnJvcic7XHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICB0aGlzLmF1ZGlvRmV0Y2hTdWJzY3JpcHRpb24gPSB0aGlzLnJlY0ZpbGVTZXJ2aWNlLmZldGNoUmVjb3JkaW5nRmlsZUF1ZGlvQnVmZmVyKHRoaXMuX3Nlc3Npb24ucHJvamVjdCwgcmYpLnN1YnNjcmliZSh7XHJcbiAgICAgICAgICAgICAgbmV4dDogYWIgPT4ge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5saXZlTGV2ZWxEaXNwbGF5U3RhdGUgPSBMaXZlTGV2ZWxTdGF0ZS5SRUFEWTtcclxuICAgICAgICAgICAgICAgIGxldCBmYWJEaCA9IG51bGw7XHJcbiAgICAgICAgICAgICAgICBpZiAoYWIpIHtcclxuICAgICAgICAgICAgICAgICAgbGV0IGFiU3JjPW5ldyBBdWRpb0J1ZmZlclNvdXJjZShhYik7XHJcbiAgICAgICAgICAgICAgICAgIGlmIChyZikge1xyXG4gICAgICAgICAgICAgICAgICAgIGZhYkRoID0gbmV3IEF1ZGlvRGF0YUhvbGRlcihhYlNyYyk7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5yZWNvcmRlckNvbWJpUGFuZS5zZXRSZWNGaWxlQXVkaW9EYXRhKHJmLCBmYWJEaCk7XHJcbiAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ1JlY29yZGluZyBmaWxlIGNvdWxkIG5vdCBiZSBsb2FkZWQuJyk7XHJcbiAgICAgICAgICAgICAgICAgIHRoaXMuc3RhdHVzTXNnID0gJ1JlY29yZGluZyBmaWxlIGNvdWxkIG5vdCBiZSBsb2FkZWQuJztcclxuICAgICAgICAgICAgICAgICAgdGhpcy5zdGF0dXNBbGVydFR5cGUgPSAnZXJyb3InO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgaWYgKGZhYkRoKSB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIHRoaXMuZGlzcGxheUF1ZGlvQ2xpcCBjb3VsZCBoYXZlIGJlZW4gY2hhbmdlZCBtZWFud2hpbGUsIGJ1dCB0aGUgcmVjb3JkZXIgdW5zdWJjcmliZXMgYmVmb3JlIGNoYW5naW5nIHRoZSBpdGVtLiBUaGVyZWZvcmUsIHRoZXJlIHNob3VsZCBiZSBubyByaXNrIHRvIHNldCB0byB3cm9uZyBpdGVtXHJcbiAgICAgICAgICAgICAgICAgIHRoaXMuZGlzcGxheUF1ZGlvQ2xpcCA9IG5ldyBBdWRpb0NsaXAoZmFiRGgpO1xyXG4gICAgICAgICAgICAgICAgICAvL3RoaXMuYXVkaW9Mb2FkZWQ9dHJ1ZTtcclxuICAgICAgICAgICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwic2V0IHJlY29yZGluZyBmaWxlOiBkaXNwbGF5IGF1ZGlvIGNsaXAgZnJvbSBmZXRjaGVkIGF1ZGlvIGJ1ZmZlclwiKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIGlmKHRoaXMuX2NvbnRyb2xBdWRpb1BsYXllcikge1xyXG4gICAgICAgICAgICAgICAgICB0aGlzLl9jb250cm9sQXVkaW9QbGF5ZXIuYXVkaW9DbGlwID0gdGhpcy5kaXNwbGF5QXVkaW9DbGlwO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgdGhpcy5zaG93UmVjb3JkaW5nKCk7XHJcbiAgICAgICAgICAgICAgfSwgZXJyb3I6IGVyciA9PiB7XHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiQ291bGQgbm90IGxvYWQgcmVjb3JkaW5nIGZpbGUgZnJvbSBzZXJ2ZXI6IFwiICsgZXJyKTtcclxuICAgICAgICAgICAgICAgIHRoaXMubGl2ZUxldmVsRGlzcGxheVN0YXRlID0gTGl2ZUxldmVsU3RhdGUuUkVBRFk7XHJcbiAgICAgICAgICAgICAgICB0aGlzLnN0YXR1c01zZyA9ICdSZWNvcmRpbmcgZmlsZSBjb3VsZCBub3QgYmUgbG9hZGVkOiAnICsgZXJyO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5zdGF0dXNBbGVydFR5cGUgPSAnZXJyb3InO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfSlcclxuICAgICAgICAgIH1cclxuICAgICAgICB9ZWxzZXtcclxuICAgICAgICAgIHRoaXMuc3RhdHVzTXNnID0gJ1JlY29yZGluZyBmaWxlIGNvdWxkIG5vdCBiZSBkZWNvZGVkLiBBdWRpbyBjb250ZXh0IHVuYXZhaWxhYmxlLic7XHJcbiAgICAgICAgICB0aGlzLnN0YXR1c0FsZXJ0VHlwZSA9ICdlcnJvcic7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcblxyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy9jb25zb2xlLmRlYnVnKFwicmVjb3JkaW5nIGZpbGUgbnVsbFwiKTtcclxuICAgICAgdGhpcy5kaXNwbGF5QXVkaW9DbGlwID0gbnVsbDtcclxuICAgICAgaWYodGhpcy5fY29udHJvbEF1ZGlvUGxheWVyKSB7XHJcbiAgICAgICAgdGhpcy5fY29udHJvbEF1ZGlvUGxheWVyLmF1ZGlvQ2xpcCA9IG51bGw7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIHRoaXMuc2hvd1JlY29yZGluZygpO1xyXG4gIH1cclxuXHJcbiAgZ2V0IGRpc3BsYXlSZWNGaWxlKCk6IFJlY29yZGluZ0ZpbGUgfCBudWxsIHtcclxuICAgIHJldHVybiB0aGlzLl9kaXNwbGF5UmVjRmlsZTtcclxuICB9XHJcblxyXG4gIHVwZGF0ZVN0YXJ0QWN0aW9uRGlzYWJsZVN0YXRlKCl7XHJcbiAgICB0aGlzLnRyYW5zcG9ydEFjdGlvbnMuc3RhcnRBY3Rpb24uZGlzYWJsZWQ9ISh0aGlzLmFjKTtcclxuICB9XHJcblxyXG4gIHN0YXJ0KCkge1xyXG4gICAgc3VwZXIuc3RhcnQoKTtcclxuICAgIHRoaXMucmVjb3JkZXJDb21iaVBhbmUuc2VsZWN0VG9wKCk7XHJcbiAgICB0aGlzLmVuYWJsZU5hdmlnYXRpb24oKTtcclxuICAgIHRoaXMudXBkYXRlU3RhcnRBY3Rpb25EaXNhYmxlU3RhdGUoKTtcclxuXHJcbiAgfVxyXG5cclxuICBpc1JlY29yZGluZygpOiBib29sZWFuIHtcclxuICAgIHJldHVybiAodGhpcy5zdGF0dXMgPT09IFN0YXR1cy5SRUNPUkRJTkcgfHwgdGhpcy5zdGF0dXM9PT1TdGF0dXMuU1RPUFBJTkdfU1RPUCk7XHJcbiAgfVxyXG5cclxuICBpc0FjdGl2ZSgpOiBib29sZWFue1xyXG4gICAgcmV0dXJuICghKHRoaXMuc3RhdHVzID09PSBTdGF0dXMuQkxPQ0tFRCB8fCB0aGlzLnN0YXR1cz09PSBTdGF0dXMuSURMRSB8fCB0aGlzLnN0YXR1cz09PVN0YXR1cy5FUlJPUikgfHwgdGhpcy5wcm9jZXNzaW5nUmVjb3JkaW5nIHx8IHRoaXMuc2Vzc2lvblNlcnZpY2UudXBsb2FkQ291bnQ+MClcclxuICB9XHJcblxyXG5cclxuICB1cGRhdGVXYWtlTG9jayhkYXRhU2F2ZWQ6Ym9vbGVhbj10aGlzLmRhdGFTYXZlZCl7XHJcbiAgICAvL2NvbnNvbGUuZGVidWcoXCJVcGRhdGUgd2FrZSBsb2NrOiBkYXRhU2F2ZWQ6IFwiK2RhdGFTYXZlZCtcIiwgbm90IGFjdGl2ZTogXCIrISB0aGlzLmlzQWN0aXZlKCkpXHJcbiAgICBpZihkYXRhU2F2ZWQgJiYgISB0aGlzLmlzQWN0aXZlKCkpe1xyXG4gICAgICB0aGlzLmRpc2FibGVXYWtlTG9ja0NvbmQoKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHByaXZhdGUgdXBkYXRlTmF2aWdhdGlvbkFjdGlvbnMoKXtcclxuXHJcbiAgICB0aGlzLnRyYW5zcG9ydEFjdGlvbnMuZndkQWN0aW9uLmRpc2FibGVkID0gdGhpcy5uYXZpZ2F0aW9uRGlzYWJsZWQ7XHJcbiAgICB0aGlzLnRyYW5zcG9ydEFjdGlvbnMuYndkQWN0aW9uLmRpc2FibGVkID0gdGhpcy5uYXZpZ2F0aW9uRGlzYWJsZWQ7XHJcbiAgfVxyXG5cclxuICBlbmFibGVTdGFydFVzZXJHZXN0dXJlKCkge1xyXG4gICAgc3VwZXIuZW5hYmxlU3RhcnRVc2VyR2VzdHVyZSgpO1xyXG4gICAgdGhpcy50cmFuc3BvcnRBY3Rpb25zLnN0YXJ0QWN0aW9uLmRpc2FibGVkPSEodGhpcy5hYyk7XHJcbiAgfVxyXG5cclxuICBlbmFibGVOYXZpZ2F0aW9uKCl7XHJcbiAgICB0aGlzLm5hdmlnYXRpb25EaXNhYmxlZD1mYWxzZTtcclxuICAgIHRoaXMudXBkYXRlTmF2aWdhdGlvbkFjdGlvbnMoKTtcclxuICB9XHJcblxyXG4gIHN0YXJ0ZWQoKSB7XHJcbiAgICB0aGlzLnN0YXR1cyA9IFN0YXR1cy5SRUNPUkRJTkc7XHJcbiAgICBzdXBlci5zdGFydGVkKCk7XHJcbiAgICB0aGlzLnN0YXR1c0FsZXJ0VHlwZSA9ICdpbmZvJztcclxuICAgIHRoaXMuc3RhdHVzTXNnID0gJ1JlY29yZGluZy4uLic7XHJcblxyXG4gICAgbGV0IHNlc3NJZDogc3RyaW5nIHwgbnVtYmVyID0gMDtcclxuICAgIGlmKHRoaXMuX3Nlc3Npb24pe1xyXG4gICAgICBzZXNzSWQ9dGhpcy5fc2Vzc2lvbi5zZXNzaW9uSWQ7XHJcbiAgICB9XHJcblxyXG4gICAgaWYodGhpcy5yZlV1aWQpIHtcclxuICAgICAgbGV0IHJmID0gbmV3IFJlY29yZGluZ0ZpbGUodGhpcy5yZlV1aWQsIHNlc3NJZCwgbnVsbCk7XHJcbiAgICAgIHJmLl9zdGFydGVkQXNEYXRlT2JqID0gdGhpcy5zdGFydGVkRGF0ZTtcclxuICAgICAgaWYgKHJmLl9zdGFydGVkQXNEYXRlT2JqKSB7XHJcbiAgICAgICAgcmYuc3RhcnRlZERhdGUgPSByZi5fc3RhcnRlZEFzRGF0ZU9iai50b1N0cmluZygpO1xyXG4gICAgICB9XHJcbiAgICAgIHRoaXMuX3JlY29yZGluZ0ZpbGUgPSByZjtcclxuICAgIH1cclxuICAgIGxldCBtYXhSZWNvcmRpbmdUaW1lTXMgPSBNQVhfUkVDT1JESU5HX1RJTUVfTVM7XHJcbiAgICB0aGlzLm1heFJlY1RpbWVySWQgPSB3aW5kb3cuc2V0VGltZW91dCgoKSA9PiB7XHJcbiAgICAgIHRoaXMuc3RvcFJlY29yZGluZ01heFJlYygpXHJcbiAgICB9LCBtYXhSZWNvcmRpbmdUaW1lTXMpO1xyXG4gICAgdGhpcy5tYXhSZWNUaW1lclJ1bm5pbmcgPSB0cnVlO1xyXG4gICAgdGhpcy50cmFuc3BvcnRBY3Rpb25zLnN0b3BBY3Rpb24uZGlzYWJsZWQgPSBmYWxzZTtcclxuICB9XHJcblxyXG4gIHN0b3BJdGVtKCkge1xyXG5cclxuICAgIHRoaXMudHJhbnNwb3J0QWN0aW9ucy5zdG9wQWN0aW9uLmRpc2FibGVkID0gdHJ1ZTtcclxuICAgIHRoaXMudHJhbnNwb3J0QWN0aW9ucy5uZXh0QWN0aW9uLmRpc2FibGVkID0gdHJ1ZTtcclxuICAgIHRoaXMuc3RhdHVzID0gU3RhdHVzLlNUT1BQSU5HX1NUT1A7XHJcbiAgICB0aGlzLnN0b3BSZWNvcmRpbmcoKTtcclxuXHJcbiAgfVxyXG5cclxuICBzdG9wUmVjb3JkaW5nKCkge1xyXG4gICAgaWYgKHRoaXMubWF4UmVjVGltZXJSdW5uaW5nICYmIHRoaXMubWF4UmVjVGltZXJJZCE9bnVsbCkge1xyXG4gICAgICB3aW5kb3cuY2xlYXJUaW1lb3V0KHRoaXMubWF4UmVjVGltZXJJZCk7XHJcbiAgICAgIHRoaXMubWF4UmVjVGltZXJSdW5uaW5nID0gZmFsc2U7XHJcbiAgICB9XHJcbiAgICBpZih0aGlzLmFjKSB7XHJcbiAgICAgIHRoaXMuYWMuc3RvcCgpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgc3RvcFJlY29yZGluZ01heFJlYygpe1xyXG5cclxuICAgIHRoaXMubWF4UmVjVGltZXJSdW5uaW5nID0gZmFsc2U7XHJcbiAgICB0aGlzLnN0YXR1cyA9IFN0YXR1cy5TVE9QUElOR19TVE9QO1xyXG4gICAgaWYodGhpcy5hYykge1xyXG4gICAgICB0aGlzLmFjLnN0b3AoKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHN0b3BwZWQoKSB7XHJcbiAgICB0aGlzLnVwZGF0ZVN0YXJ0QWN0aW9uRGlzYWJsZVN0YXRlKClcclxuICAgIHRoaXMudHJhbnNwb3J0QWN0aW9ucy5zdG9wQWN0aW9uLmRpc2FibGVkID0gdHJ1ZTtcclxuICAgIHRoaXMudHJhbnNwb3J0QWN0aW9ucy5uZXh0QWN0aW9uLmRpc2FibGVkID0gdHJ1ZTtcclxuICAgIHRoaXMudHJhbnNwb3J0QWN0aW9ucy5wYXVzZUFjdGlvbi5kaXNhYmxlZCA9IHRydWU7XHJcbiAgICB0aGlzLnN0YXR1c0FsZXJ0VHlwZSA9ICdpbmZvJztcclxuICAgIHRoaXMuc3RhdHVzTXNnID0gJ1JlY29yZGVkLic7XHJcblxyXG4gICAgbGV0IGFiOkF1ZGlvQnVmZmVyfG51bGw9bnVsbDtcclxuXHJcbiAgICBpZih0aGlzLmFjKSB7XHJcbiAgICAgIGxldCBhZGg6QXVkaW9EYXRhSG9sZGVyfG51bGw9bnVsbDtcclxuICAgICAgbGV0IGFzOkF1ZGlvU291cmNlfG51bGw9bnVsbDtcclxuICAgICAgaWYodGhpcy5hYykge1xyXG4gICAgICAgIGlmIChBdWRpb1N0b3JhZ2VUeXBlLk5FVF9DSFVOS0VEID09PSB0aGlzLmFjLmF1ZGlvU3RvcmFnZVR5cGUpIHtcclxuICAgICAgICAgIHRoaXMucGxheVN0YXJ0QWN0aW9uLmRpc2FibGVkID0gdHJ1ZTtcclxuICAgICAgICAgIC8vdGhpcy5hdWRpb0xvYWRlZD1mYWxzZTtcclxuICAgICAgICAgIHRoaXMua2VlcExpdmVMZXZlbD10cnVlO1xyXG4gICAgICAgICAgbGV0IHJVVUlEOnN0cmluZ3xudWxsPW51bGw7XHJcbiAgICAgICAgICAgIGxldCBidXJsOnN0cmluZ3xudWxsPW51bGw7XHJcbiAgICAgICAgICAgIGlmKHRoaXMuX3Nlc3Npb24pIHtcclxuICAgICAgICAgICAgICBpZiAodGhpcy5fcmVjb3JkaW5nRmlsZSkge1xyXG5cclxuICAgICAgICAgICAgICAgIGxldCByZiA9IHRoaXMuX3JlY29yZGluZ0ZpbGU7XHJcbiAgICAgICAgICAgICAgICByZi5mcmFtZXM9dGhpcy5hYy5mcmFtZXNSZWNvcmRlZDtcclxuICAgICAgICAgICAgICAgIHJVVUlEPXJmLnV1aWQ7XHJcbiAgICAgICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJzdG9wcGVkKCk6IFNldCBmcmFtZXM6IFwiK3JmLmZyYW1lcytcIiBvbiByZklkOiBcIit0aGlzLmRpc3BsYXlSZWNGaWxlPy5yZWNvcmRpbmdGaWxlSWQpO1xyXG4gICAgICAgICAgICAgICAgYnVybCA9IHRoaXMucmVjRmlsZVNlcnZpY2UuYXVkaW9GaWxlVXJsKHRoaXMuX3Nlc3Npb24/LnByb2plY3QsIHJmKTtcclxuICAgICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMuc2Vzc2lvbj8ucHJvamVjdCkge1xyXG4gICAgICAgICAgICAgICAgaWYodGhpcy5hYy5yZWNVVUlEKSB7XHJcbiAgICAgICAgICAgICAgICAgIHJVVUlEPXRoaXMuYWMucmVjVVVJRDtcclxuICAgICAgICAgICAgICAgICAgYnVybCA9IHRoaXMucmVjRmlsZVNlcnZpY2UuYXVkaW9GaWxlVXJsQnlVVUlEKHRoaXMuc2Vzc2lvbi5wcm9qZWN0LCB0aGlzLnNlc3Npb24uc2Vzc2lvbklkLCByVVVJRCk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgfWVsc2V7XHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiQ291bGQgbm90IGNyZWF0ZSBuZXQgYXVkaW8gYnVmZmVyLlwiKTtcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgaWYgKGJ1cmwpIHtcclxuICAgICAgICAgICAgICAgIGNvbnN0IHNyID0gdGhpcy5hYy5jdXJyZW50U2FtcGxlUmF0ZTtcclxuICAgICAgICAgICAgICAgIGNvbnN0IGNoRmw9c3IqUmVjb3JkaW5nU2VydmljZS5ERUZBVUxUX0NIVU5LRURfRE9XTkxPQURfU0VDT05EUztcclxuICAgICAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcInN0b3BwZWQoKTogcmZJRDogXCIrdGhpcy5fcmVjb3JkaW5nRmlsZT8ucmVjb3JkaW5nRmlsZUlkK1wiLCBuZXQgYWIgdXJsOiBcIiArIGJ1cmwrXCIsIGZyYW1lczogXCIrdGhpcy5hYy5mcmFtZXNSZWNvcmRlZCtcIiwgc2FtcGxlIHJhdGU6IFwiK3NyKTtcclxuICAgICAgICAgICAgICAgIGxldCBuZXRBcyA9IG5ldyBOZXRBdWRpb0J1ZmZlcih0aGlzLnJlY0ZpbGVTZXJ2aWNlLCBidXJsLCB0aGlzLmFjLmNoYW5uZWxDb3VudCwgc3IsIGNoRmwsIHRoaXMuYWMuZnJhbWVzUmVjb3JkZWQsIHJVVUlELCBjaEZsKTtcclxuICAgICAgICAgICAgICAgIGFzPW5ldEFzO1xyXG4gICAgICAgICAgICAgICAgaWYodGhpcy51cGxvYWRTZXQpe1xyXG4gICAgICAgICAgICAgICAgICB0aGlzLnVwbG9hZFNldC5vbkRvbmU9KHVwbG9hZFNldCk9PntcclxuICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUuZGVidWcoXCJ1cGxvYWQgc2V0IG9uIGRvbmU6IENhbGwgcmVhZHkgcHJvdmlkZXIucmVhZHlcIik7XHJcbiAgICAgICAgICAgICAgICAgICAgbmV0QXMucmVhZHkoKTtcclxuICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgfWVsc2UgaWYgKEF1ZGlvU3RvcmFnZVR5cGUuTUVNX0VOVElSRV9BVVRPX05FVF9DSFVOS0VEID09PSB0aGlzLmFjLmF1ZGlvU3RvcmFnZVR5cGUgfHwgQXVkaW9TdG9yYWdlVHlwZS5NRU1fQ0hVTktFRF9BVVRPX05FVF9DSFVOS0VEID09PSB0aGlzLmFjLmF1ZGlvU3RvcmFnZVR5cGUpIHtcclxuICAgICAgICAgIGlmIChBdWRpb1N0b3JhZ2VUeXBlLk1FTV9FTlRJUkVfQVVUT19ORVRfQ0hVTktFRCA9PT0gdGhpcy5hYy5hdWRpb1N0b3JhZ2VUeXBlKXtcclxuICAgICAgICAgICAgY29uc3QgYWNBYj10aGlzLmFjLmF1ZGlvQnVmZmVyKCk7XHJcbiAgICAgICAgICAgIGlmKGFjQWIpIHtcclxuICAgICAgICAgICAgICBhcyA9IG5ldyBBdWRpb0J1ZmZlclNvdXJjZShhY0FiKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgaWYoQXVkaW9TdG9yYWdlVHlwZS5NRU1fQ0hVTktFRF9BVVRPX05FVF9DSFVOS0VEID09PSB0aGlzLmFjLmF1ZGlvU3RvcmFnZVR5cGUpe1xyXG4gICAgICAgICAgICBhcyA9IHRoaXMuYWMuYXVkaW9CdWZmZXJBcnJheSgpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgaWYoIWFzKXtcclxuICAgICAgICAgICAgdGhpcy5wbGF5U3RhcnRBY3Rpb24uZGlzYWJsZWQgPSB0cnVlO1xyXG4gICAgICAgICAgICB0aGlzLmtlZXBMaXZlTGV2ZWw9dHJ1ZTtcclxuICAgICAgICAgICAgbGV0IHJVVUlEOnN0cmluZ3xudWxsPW51bGw7XHJcbiAgICAgICAgICAgIGxldCBidXJsOnN0cmluZ3xudWxsPW51bGw7XHJcbiAgICAgICAgICAgIGlmKHRoaXMuX3Nlc3Npb24pIHtcclxuICAgICAgICAgICAgICBpZiAodGhpcy5fcmVjb3JkaW5nRmlsZSkge1xyXG4gICAgICAgICAgICAgICAgbGV0IHJmID0gdGhpcy5fcmVjb3JkaW5nRmlsZTtcclxuICAgICAgICAgICAgICAgIHJmLmZyYW1lcz10aGlzLmFjLmZyYW1lc1JlY29yZGVkO1xyXG4gICAgICAgICAgICAgICAgclVVSUQ9cmYudXVpZDtcclxuICAgICAgICAgICAgICAgIC8vY29uc29sZS5kZWJ1ZyhcInN0b3BwZWQoKTogU2V0IGZyYW1lczogXCIrcmYuZnJhbWVzK1wiIG9uIHJmSWQ6IFwiK3RoaXMuZGlzcGxheVJlY0ZpbGU/LnJlY29yZGluZ0ZpbGVJZCk7XHJcbiAgICAgICAgICAgICAgICBidXJsID0gdGhpcy5yZWNGaWxlU2VydmljZS5hdWRpb0ZpbGVVcmwodGhpcy5fc2Vzc2lvbj8ucHJvamVjdCwgcmYpO1xyXG4gICAgICAgICAgICAgIH0gZWxzZSBpZiAodGhpcy5zZXNzaW9uPy5wcm9qZWN0KSB7XHJcbiAgICAgICAgICAgICAgICBpZih0aGlzLmFjLnJlY1VVSUQpIHtcclxuICAgICAgICAgICAgICAgICAgclVVSUQ9dGhpcy5hYy5yZWNVVUlEO1xyXG4gICAgICAgICAgICAgICAgICBidXJsID0gdGhpcy5yZWNGaWxlU2VydmljZS5hdWRpb0ZpbGVVcmxCeVVVSUQodGhpcy5zZXNzaW9uLnByb2plY3QsIHRoaXMuc2Vzc2lvbi5zZXNzaW9uSWQsIHJVVUlEKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICB9ZWxzZXtcclxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJDb3VsZCBub3QgY3JlYXRlIG5ldCBhdWRpbyBidWZmZXIuXCIpO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICBpZiAoYnVybCkge1xyXG4gICAgICAgICAgICAgICAgY29uc3Qgc3IgPSB0aGlzLmFjLmN1cnJlbnRTYW1wbGVSYXRlO1xyXG4gICAgICAgICAgICAgICAgY29uc3QgY2hGbD1zcipSZWNvcmRpbmdTZXJ2aWNlLkRFRkFVTFRfQ0hVTktFRF9ET1dOTE9BRF9TRUNPTkRTO1xyXG4gICAgICAgICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwic3RvcHBlZCgpOiByZklEOiBcIit0aGlzLl9yZWNvcmRpbmdGaWxlPy5yZWNvcmRpbmdGaWxlSWQrXCIsIG5ldCBhYiB1cmw6IFwiICsgYnVybCtcIiwgZnJhbWVzOiBcIit0aGlzLmFjLmZyYW1lc1JlY29yZGVkK1wiLCBzYW1wbGUgcmF0ZTogXCIrc3IpO1xyXG4gICAgICAgICAgICAgICAgbGV0IG5ldEFzID0gbmV3IE5ldEF1ZGlvQnVmZmVyKHRoaXMucmVjRmlsZVNlcnZpY2UsIGJ1cmwsIHRoaXMuYWMuY2hhbm5lbENvdW50LCBzciwgY2hGbCwgdGhpcy5hYy5mcmFtZXNSZWNvcmRlZCwgclVVSUQsIGNoRmwpO1xyXG4gICAgICAgICAgICAgICAgYXMgPSBuZXRBcztcclxuICAgICAgICAgICAgICAgIGlmICh0aGlzLnVwbG9hZFNldCkge1xyXG4gICAgICAgICAgICAgICAgICB0aGlzLnVwbG9hZFNldC5vbkRvbmUgPSAodXBsb2FkU2V0KSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwidXBsb2FkIHNldCBvbiBkb25lOiBDYWxsIHJlYWR5IHByb3ZpZGVyLnJlYWR5XCIpO1xyXG4gICAgICAgICAgICAgICAgICAgIG5ldEFzLnJlYWR5KCk7XHJcbiAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICB9IGVsc2UgaWYgKEF1ZGlvU3RvcmFnZVR5cGUuREJfQ0hVTktFRCA9PT0gdGhpcy5hYy5hdWRpb1N0b3JhZ2VUeXBlKSB7XHJcbiAgICAgICAgICBhcyA9IHRoaXMuYWMuaW5kZGJBdWRpb0J1ZmZlckFycmF5KCk7XHJcbiAgICAgICAgfSBlbHNlIGlmIChBdWRpb1N0b3JhZ2VUeXBlLk1FTV9DSFVOS0VEID09PSB0aGlzLmFjLmF1ZGlvU3RvcmFnZVR5cGUpIHtcclxuICAgICAgICAgIGFzID0gdGhpcy5hYy5hdWRpb0J1ZmZlckFycmF5KCk7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIGFiID0gdGhpcy5hYy5hdWRpb0J1ZmZlcigpO1xyXG4gICAgICAgICAgaWYoYWIpIHtcclxuICAgICAgICAgICAgYXMgPSBuZXcgQXVkaW9CdWZmZXJTb3VyY2UoYWIpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAoYXMpIHtcclxuICAgICAgICAgIGFkaCA9IG5ldyBBdWRpb0RhdGFIb2xkZXIoYXMpO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG5cclxuICAgICAgbGV0IHNlc3NJZDogc3RyaW5nIHwgbnVtYmVyID0gMDtcclxuICAgICAgaWYodGhpcy5fc2Vzc2lvbil7XHJcbiAgICAgICAgc2Vzc0lkPXRoaXMuX3Nlc3Npb24uc2Vzc2lvbklkO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpZih0aGlzLl9yZWNvcmRpbmdGaWxlKSB7XHJcbiAgICAgICAgLy90aGlzLl9yZWNvcmRpbmdGaWxlLnNhbXBsZXJhdGU9dGhpcy5hYy5jdXJyZW50U2FtcGxlUmF0ZTtcclxuICAgICAgICAvLyBVc2UgYW4gb3duIHJlZmVyZW5jZSBzaW5jZSB0aGUgd3JpdGluZyBvZiB0aGUgd2F2ZSBmaWxlIGlzIGFzeW5jaHJvbm91cyBhbmQgdGhpcy5fcmVjb3JkaW5nRmlsZSBtaWdodCBhbHJlYWR5IGNvbnRhaW4gdGhlIG5leHQgcmVjb3JkaW5nXHJcbiAgICAgICAgbGV0IHJmID0gdGhpcy5fcmVjb3JkaW5nRmlsZTtcclxuICAgICAgICBSZWNvcmRpbmdGaWxlVXRpbHMuc2V0QXVkaW9EYXRhKHJmLGFkaCk7XHJcbiAgICAgICAgdGhpcy5yZWNvcmRlckNvbWJpUGFuZS5hZGRSZWNGaWxlKHJmKTtcclxuXHJcbiAgICAgICAgLy8gVXBsb2FkIGlmIHVwbG9hZCBlbmFibGVkIGFuZCBub3QgaW4gY2h1bmtlZCB1cGxvYWQgbW9kZVxyXG4gICAgICAgIGlmICh0aGlzLmVuYWJsZVVwbG9hZFJlY29yZGluZ3MgJiYgdGhpcy5fdXBsb2FkQ2h1bmtTaXplU2Vjb25kcz09PW51bGwgJiYgQXVkaW9TdG9yYWdlVHlwZS5NRU1fRU5USVJFPT09dGhpcy5fY2xpZW50QXVkaW9TdG9yYWdlVHlwZSAmJiByZiAhPSBudWxsICYmIGFiICE9IG51bGwpIHtcclxuICAgICAgICAgIGxldCBhcGlFbmRQb2ludCA9ICcnO1xyXG5cclxuICAgICAgICAgIGlmICh0aGlzLmNvbmZpZyAmJiB0aGlzLmNvbmZpZy5hcGlFbmRQb2ludCkge1xyXG4gICAgICAgICAgICBhcGlFbmRQb2ludCA9IHRoaXMuY29uZmlnLmFwaUVuZFBvaW50O1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgaWYgKGFwaUVuZFBvaW50ICE9PSAnJykge1xyXG4gICAgICAgICAgICBhcGlFbmRQb2ludCA9IGFwaUVuZFBvaW50ICsgJy8nXHJcbiAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgbGV0IHNlc3Npb25zVXJsID0gYXBpRW5kUG9pbnQgKyBTZXNzaW9uU2VydmljZS5TRVNTSU9OX0FQSV9DVFg7XHJcbiAgICAgICAgICBsZXQgcmVjVXJsOiBzdHJpbmcgPSBzZXNzaW9uc1VybCArICcvJyArIHJmLnNlc3Npb24gKyAnLycgKyBSRUNGSUxFX0FQSV9DVFggKyAnLycgKyByZi51dWlkO1xyXG5cclxuICAgICAgICAgIC8vIGNvbnZlcnQgYXN5bmNocm9ub3VzbHkgdG8gMTYtYml0IGludGVnZXIgUENNXHJcbiAgICAgICAgICAvLyBUT0RPIGNvdWxkIHdlIGF2b2lkIGNvbnZlcnNpb24gdG8gc2F2ZSBDUFUgcmVzb3VyY2VzIGFuZCB0cmFuc2ZlciBmbG9hdCBQQ00gZGlyZWN0bHk/XHJcbiAgICAgICAgICAvLyBUT0RPIGR1cGxpY2F0ZSBjb252ZXJzaW9uIGZvciBtYW51YWwgZG93bmxvYWRcclxuXHJcbiAgICAgICAgICB0aGlzLnByb2Nlc3NpbmdSZWNvcmRpbmcgPSB0cnVlXHJcbiAgICAgICAgICBsZXQgd3cgPSBuZXcgV2F2V3JpdGVyKCk7XHJcbiAgICAgICAgICB3dy53cml0ZUFzeW5jKGFiLCAod2F2RmlsZSkgPT4ge1xyXG4gICAgICAgICAgICB0aGlzLnBvc3RSZWNvcmRpbmdNdWx0aXBhcnQod2F2RmlsZSxyZWNVcmwscmYpO1xyXG4gICAgICAgICAgICB0aGlzLnByb2Nlc3NpbmdSZWNvcmRpbmcgPSBmYWxzZTtcclxuICAgICAgICAgICAgdGhpcy51cGRhdGVXYWtlTG9jaygpO1xyXG4gICAgICAgICAgICB0aGlzLmNoYW5nZURldGVjdG9yUmVmLmRldGVjdENoYW5nZXMoKTtcclxuICAgICAgICAgIH0pO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgdGhpcy5kaXNwbGF5UmVjRmlsZSA9IHRoaXMuX3JlY29yZGluZ0ZpbGU7XHJcbiAgICB0aGlzLnN0YXR1cyA9IFN0YXR1cy5JRExFO1xyXG4gICAgdGhpcy5uYXZpZ2F0aW9uRGlzYWJsZWQgPSBmYWxzZTtcclxuICAgIHRoaXMudXBkYXRlTmF2aWdhdGlvbkFjdGlvbnMoKTtcclxuICAgIHRoaXMudXBkYXRlV2FrZUxvY2soKTtcclxuICAgIHRoaXMuY2hhbmdlRGV0ZWN0b3JSZWYuZGV0ZWN0Q2hhbmdlcygpO1xyXG4gIH1cclxuXHJcbiAgZXJyb3IobXNnPSdBbiB1bmtub3duIGVycm9yIG9jY3VyZWQgZHVyaW5nIHJlY29yZGluZy4nLGFkdmljZTpzdHJpbmc9J1BsZWFzZSByZXRyeS4nKSB7XHJcbiAgICB0aGlzLnN0YXR1cz1TdGF0dXMuRVJST1I7XHJcbiAgICBzdXBlci5lcnJvcihtc2csYWR2aWNlKTtcclxuICAgIHRoaXMudXBkYXRlTmF2aWdhdGlvbkFjdGlvbnMoKTtcclxuICAgIHRoaXMudXBkYXRlU3RhcnRBY3Rpb25EaXNhYmxlU3RhdGUoKTtcclxuICB9XHJcblxyXG4gIHBvc3RSZWNvcmRpbmdNdWx0aXBhcnQod2F2RmlsZTogVWludDhBcnJheSxyZWNVcmw6IHN0cmluZyxyZjpSZWNvcmRpbmdGaWxlKSB7XHJcbiAgICBsZXQgd2F2QmxvYiA9IG5ldyBCbG9iKFt3YXZGaWxlXSwge3R5cGU6ICdhdWRpby93YXYnfSk7XHJcblxyXG4gICAgbGV0IGZkPW5ldyBGb3JtRGF0YSgpO1xyXG4gICAgaWYocmYudXVpZCkge1xyXG4gICAgICBmZC5zZXQoJ3V1aWQnLCByZi51dWlkKTtcclxuICAgIH1cclxuICAgIGlmKHJmLnNlc3Npb24hPT1udWxsKSB7XHJcbiAgICAgIGZkLnNldCgnc2Vzc2lvbklkJywgcmYuc2Vzc2lvbi50b1N0cmluZygpKTtcclxuICAgIH1cclxuICAgIGlmKHJmLl9zdGFydGVkQXNEYXRlT2JqKXtcclxuICAgICAgZmQuc2V0KCdzdGFydGVkRGF0ZScscmYuX3N0YXJ0ZWRBc0RhdGVPYmoudG9KU09OKCkpO1xyXG4gICAgfVxyXG4gICAgZmQuc2V0KCdhdWRpbycsd2F2QmxvYik7XHJcbiAgICBsZXQgdWwgPSBuZXcgVXBsb2FkKGZkLCByZWNVcmwscmYpO1xyXG4gICAgdGhpcy51cGxvYWRlci5xdWV1ZVVwbG9hZCh1bCk7XHJcbiAgfVxyXG5cclxuICBwb3N0Q2h1bmtBdWRpb0J1ZmZlcihhdWRpb0J1ZmZlcjogQXVkaW9CdWZmZXIsIGNodW5rSWR4OiBudW1iZXIpOiB2b2lkIHtcclxuICAgIHRoaXMucHJvY2Vzc2luZ1JlY29yZGluZyA9IHRydWU7XHJcbiAgICBsZXQgd3cgPSBuZXcgV2F2V3JpdGVyKCk7XHJcbiAgICBsZXQgc2Vzc2lvbnNVcmwgPSB0aGlzLnNlc3Npb25zQmFzZVVybCgpO1xyXG4gICAgbGV0IHJlY1VybDogc3RyaW5nID0gc2Vzc2lvbnNVcmwgKyAnLycgKyB0aGlzLnNlc3Npb24/LnNlc3Npb25JZCArICcvJyArIFJFQ0ZJTEVfQVBJX0NUWCArICcvJyArIHRoaXMucmZVdWlkKycvJytjaHVua0lkeDtcclxuICAgIGxldCByZj10aGlzLl9yZWNvcmRpbmdGaWxlO1xyXG5cclxuICAgIC8vIFRoZSB1cGxvYWQgaG9sZGVyIGlzIHJlcXVpcmVkIHRvIGFkZCB0aGUgdXBsb2FkIG5vdyB0byB0aGUgdXBsb2FkIHNldC4gVGhlIHJlYWwgdXBsb2FkIGlzIGNyZWF0ZWQgYXN5bmMgaW4gcG9zdHJlY29yZGluZyBhbmQgdGhlIHVwbG9hZCBzZXQgaXMgYWxyZWFkeSBjb21wbGV0ZSBhdCB0aGF0IHRpbWUuXHJcbiAgICBsZXQgdWxoPW5ldyBVcGxvYWRIb2xkZXIoKTtcclxuICAgIGlmKHRoaXMudXBsb2FkU2V0KXtcclxuICAgICAgdGhpcy51cGxvYWRTZXQuYWRkKHVsaCk7XHJcbiAgICB9XHJcbiAgICB3dy53cml0ZUFzeW5jKGF1ZGlvQnVmZmVyLCAod2F2RmlsZSkgPT4ge1xyXG4gICAgICB0aGlzLnBvc3RSZWNvcmRpbmcod2F2RmlsZSwgcmVjVXJsLHJmLHVsaCk7XHJcbiAgICAgIHRoaXMucHJvY2Vzc2luZ1JlY29yZGluZyA9IGZhbHNlO1xyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICBzdG9wKCkge1xyXG4gICAgaWYodGhpcy5hYykge1xyXG4gICAgICB0aGlzLmFjLmNsb3NlKCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIHVwZGF0ZUNvbnRyb2xQbGF5YmFja1Bvc2l0aW9uKCkge1xyXG4gICAgaWYodGhpcy5fY29udHJvbEF1ZGlvUGxheWVyKXtcclxuICAgICAgY29uc3QgcHBGcmFtZXM9dGhpcy5fY29udHJvbEF1ZGlvUGxheWVyLnBsYXlQb3NpdGlvbkZyYW1lcztcclxuICAgICAgaWYgKHBwRnJhbWVzIT09bnVsbCkge1xyXG4gICAgICAgIHRoaXMucmVjb3JkZXJDb21iaVBhbmUuYXVkaW9EaXNwbGF5LnBsYXlGcmFtZVBvc2l0aW9uID0gcHBGcmFtZXM7XHJcbiAgICAgICAgdGhpcy5saXZlTGV2ZWxEaXNwbGF5LnBsYXlGcmFtZVBvc2l0aW9uID0gcHBGcmFtZXM7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcblxyXG4gIGF1ZGlvUGxheWVyVXBkYXRlKGU6IEF1ZGlvUGxheWVyRXZlbnQpIHtcclxuICAgIGlmIChFdmVudFR5cGUuUkVBRFkgPT09IGUudHlwZSkge1xyXG5cclxuICAgIH0gZWxzZSBpZiAoRXZlbnRUeXBlLlNUQVJURUQgPT09IGUudHlwZSkge1xyXG4gICAgICAvL3RoaXMuc3RhdHVzID0gJ1BsYXliYWNrLi4uJztcclxuICAgICAgdGhpcy51cGRhdGVUaW1lcklkID0gd2luZG93LnNldEludGVydmFsKCgpID0+IHRoaXMudXBkYXRlQ29udHJvbFBsYXliYWNrUG9zaXRpb24oKSwgNTApO1xyXG5cclxuICAgIH0gZWxzZSBpZiAoRXZlbnRUeXBlLkVOREVEID09PSBlLnR5cGUpIHtcclxuICAgICAgLy8uc3RhdHVzPSdSZWFkeS4nO1xyXG4gICAgICB3aW5kb3cuY2xlYXJJbnRlcnZhbCh0aGlzLnVwZGF0ZVRpbWVySWQpO1xyXG5cclxuICAgIH1cclxuICAgIGlmKCF0aGlzLmRlc3Ryb3llZCkge1xyXG4gICAgICAgIHRoaXMuY2hhbmdlRGV0ZWN0b3JSZWYuZGV0ZWN0Q2hhbmdlcygpO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgc2VsZWN0b3I6ICdhcHAtYXVkaW9yZWNvcmRlci1jb21wJyxcclxuICBwcm92aWRlcnM6IFtTZXNzaW9uU2VydmljZV0sXHJcbiAgdGVtcGxhdGU6IGBcclxuICAgIDxhcHAtYXVkaW9yZWNvcmRlciBbcHJvamVjdE5hbWVdPVwiX3Byb2plY3Q/Lm5hbWVcIiBbZGF0YVNhdmVkXT1cImRhdGFTYXZlZFwiPjwvYXBwLWF1ZGlvcmVjb3JkZXI+XHJcbiAgYCxcclxuICBzdHlsZXM6IFtgOmhvc3R7XHJcbiAgICBmbGV4OiAyO1xyXG4gICAgZGlzcGxheTogZmxleDtcclxuICAgICAgaGVpZ2h0OiAxMDAlO1xyXG4gICAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcclxuICAgIG1pbi1oZWlnaHQ6MDtcclxuXHJcbiAgfWBdXHJcblxyXG59KVxyXG5leHBvcnQgY2xhc3MgQXVkaW9SZWNvcmRlckNvbXBvbmVudCBleHRlbmRzIFJlY29yZGVyQ29tcG9uZW50ICBpbXBsZW1lbnRzIE9uSW5pdCxPbkRlc3Ryb3ksQWZ0ZXJWaWV3SW5pdCxSZWFkeVN0YXRlUHJvdmlkZXIge1xyXG5cclxuICBtb2RlITpNb2RlO1xyXG4gIGNvbnRyb2xBdWRpb1BsYXllciE6QXVkaW9QbGF5ZXI7XHJcbiAgYXVkaW86YW55O1xyXG5cclxuICBfcHJvamVjdDpQcm9qZWN0fHVuZGVmaW5lZDtcclxuICBzZXNzaW9uSWQhOiBzdHJpbmc7XHJcbiAgc2Vzc2lvbiE6U2Vzc2lvbjtcclxuXHJcbiAgQFZpZXdDaGlsZChBdWRpb1JlY29yZGVyLCB7IHN0YXRpYzogdHJ1ZSB9KSBhciE6QXVkaW9SZWNvcmRlcjtcclxuXHJcbiAgY29uc3RydWN0b3IocHJpdmF0ZSByb3V0ZTogQWN0aXZhdGVkUm91dGUsXHJcbiAgICAgICAgICAgICAgcHJpdmF0ZSByb3V0ZXI6IFJvdXRlcixcclxuICAgICAgICAgICAgICBwcml2YXRlIGNoYW5nZURldGVjdG9yUmVmOiBDaGFuZ2VEZXRlY3RvclJlZixcclxuICAgICAgICAgICAgICBwcml2YXRlIHNlc3Npb25TZXJ2aWNlOlNlc3Npb25TZXJ2aWNlLFxyXG4gICAgICAgICAgICAgIHByaXZhdGUgcHJvamVjdFNlcnZpY2U6UHJvamVjdFNlcnZpY2UsXHJcbiAgICAgICAgICAgICAgcHJvdGVjdGVkIHVwbG9hZGVyOlNwZWVjaFJlY29yZGVyVXBsb2FkZXJcclxuICApIHtcclxuICAgIHN1cGVyKHVwbG9hZGVyKTtcclxuICB9XHJcblxyXG4gIG5nT25Jbml0KCkge1xyXG5cclxuICAgIHRoaXMuY29udHJvbEF1ZGlvUGxheWVyID0gbmV3IEF1ZGlvUGxheWVyKHRoaXMuYXIpO1xyXG5cclxuICAgIHRoaXMuYXIuY29udHJvbEF1ZGlvUGxheWVyPXRoaXMuY29udHJvbEF1ZGlvUGxheWVyO1xyXG5cclxuICAgIC8vVE9ETyBEdXBsaWNhdGUgY29kZSBpbiBTcGVlY2hSZWNvcmRlckNvbXBvbmVudFxyXG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ2JlZm9yZXVubG9hZCcsIChlKSA9PiB7XHJcbiAgICAgIGNvbnNvbGUuZGVidWcoXCJCZWZvcmUgcGFnZSB1bmxvYWQgZXZlbnRcIik7XHJcblxyXG4gICAgICBpZiAodGhpcy5yZWFkeSgpKSB7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIGFsbCB0aGlzIGF0dGVtcHRzIHRvIGN1c3RvbWl6ZSB0aGUgbWVzc2FnZSBkbyBub3Qgd29yayBhbnltb3JlIChmb3Igc2VjdXJpdHkgcmVhc29ucykhIVxyXG4gICAgICAgIGNvbnN0IG1lc3NhZ2UgPSBcIlBsZWFzZSBkbyBub3QgbGVhdmUgdGhlIHBhZ2UsIHVudGlsIGFsbCByZWNvcmRpbmdzIGFyZSB1cGxvYWRlZCFcIjtcclxuICAgICAgICBhbGVydChtZXNzYWdlKTtcclxuICAgICAgICBlID0gZSB8fCB3aW5kb3cuZXZlbnQ7XHJcblxyXG4gICAgICAgIGlmIChlKSB7XHJcbiAgICAgICAgICBlLnJldHVyblZhbHVlID0gbWVzc2FnZTtcclxuICAgICAgICAgIGUuY2FuY2VsQnViYmxlID0gdHJ1ZTtcclxuICAgICAgICAgIGlmIChlLnN0b3BQcm9wYWdhdGlvbikge1xyXG4gICAgICAgICAgICBlLnN0b3BQcm9wYWdhdGlvbigpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgaWYgKGUucHJldmVudERlZmF1bHQpIHtcclxuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuIG1lc3NhZ2U7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgbmdBZnRlclZpZXdJbml0KCkge1xyXG5cclxuICAgIC8vIFRPRE8gY2FsbCBwcmVwYXJlICEhXHJcblxyXG4gICAgdGhpcy51cGxvYWRlci5saXN0ZW5lciA9ICh1ZSkgPT4ge1xyXG4gICAgICB0aGlzLnVwbG9hZFVwZGF0ZSh1ZSk7XHJcbiAgICB9XHJcbiAgICB0aGlzLnJvdXRlLnF1ZXJ5UGFyYW1zLnN1YnNjcmliZSgocGFyYW1zOiBQYXJhbXMpID0+IHtcclxuICAgICAgaWYgKHBhcmFtc1snc2Vzc2lvbklkJ10pIHtcclxuICAgICAgICB0aGlzLmZldGNoU2Vzc2lvbihwYXJhbXNbJ3Nlc3Npb25JZCddKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgICB0aGlzLnJvdXRlLnBhcmFtcy5zdWJzY3JpYmUoKHBhcmFtczogUGFyYW1zKSA9PiB7XHJcbiAgICAgIGxldCByb3V0ZVBhcmFtc0lkID0gcGFyYW1zWydpZCddO1xyXG4gICAgICBpZiAocm91dGVQYXJhbXNJZCkge1xyXG4gICAgICAgIHRoaXMuZmV0Y2hTZXNzaW9uKHJvdXRlUGFyYW1zSWQpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIG5nT25EZXN0cm95KCkge1xyXG4gICAgLy9zdXBlci5uZ09uRGVzdHJveSgpO1xyXG4gIH1cclxuXHJcbiAgZ2V0IHNjcmVlbkxvY2tlZCgpOmJvb2xlYW57XHJcbiAgICByZXR1cm4gIHRoaXMuYXIuc2NyZWVuTG9ja2VkO1xyXG4gIH1cclxuXHJcbiAgZmV0Y2hTZXNzaW9uKHNlc3Npb25JZDpzdHJpbmcpe1xyXG5cclxuICAgIGxldCBzZXNzT2JzPSB0aGlzLnNlc3Npb25TZXJ2aWNlLnNlc3Npb25PYnNlcnZlcihzZXNzaW9uSWQpO1xyXG5cclxuICAgIGlmKHNlc3NPYnMpIHtcclxuICAgICAgc2Vzc09icy5zdWJzY3JpYmUoe1xyXG4gICAgICAgIG5leHQ6KHNlc3MpID0+IHtcclxuICAgICAgICAgIHRoaXMuYXIuc3RhdHVzQWxlcnRUeXBlPSdpbmZvJztcclxuICAgICAgICAgIHRoaXMuYXIuc3RhdHVzTXNnID0gJ1JlY2VpdmVkIHNlc3Npb24gaW5mby4nO1xyXG4gICAgICAgICAgdGhpcy5hci5zdGF0dXNXYWl0aW5nPWZhbHNlO1xyXG4gICAgICAgICAgdGhpcy5zZXNzaW9uPXNlc3M7XHJcbiAgICAgICAgICB0aGlzLmFyLnNlc3Npb249c2VzcztcclxuICAgICAgICAgIGlmIChzZXNzLnByb2plY3QpIHtcclxuICAgICAgICAgICAgLy9jb25zb2xlLmRlYnVnKFwiU2Vzc2lvbiBhc3NvY2lhdGVkIHByb2plY3Q6IFwiK3Nlc3MucHJvamVjdClcclxuICAgICAgICAgICAgdGhpcy5wcm9qZWN0U2VydmljZS5wcm9qZWN0T2JzZXJ2YWJsZShzZXNzLnByb2plY3QpLnN1YnNjcmliZSh7XHJcbiAgICAgICAgICAgICAgbmV4dDoocHJvamVjdCk9PntcclxuICAgICAgICAgICAgICB0aGlzLmFyLnByb2plY3Q9cHJvamVjdDtcclxuICAgICAgICAgICAgICB0aGlzLmFyLmZldGNoUmVjb3JkaW5ncyhzZXNzKTtcclxuICAgICAgICAgICAgfSxlcnJvcjoocmVhc29uKSA9PntcclxuICAgICAgICAgICAgICB0aGlzLmFyLnN0YXR1c01zZz1yZWFzb247XHJcbiAgICAgICAgICAgICAgdGhpcy5hci5zdGF0dXNBbGVydFR5cGU9J2Vycm9yJztcclxuICAgICAgICAgICAgICB0aGlzLmFyLnN0YXR1c1dhaXRpbmc9ZmFsc2U7XHJcbiAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIkVycm9yIGZldGNoaW5nIHByb2plY3QgY29uZmlnOiBcIityZWFzb24pXHJcbiAgICAgICAgICAgIH19KTtcclxuXHJcbiAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICBjb25zb2xlLmluZm8oXCJTZXNzaW9uIGhhcyBubyBhc3NvY2lhdGVkIHByb2plY3QuIFVzaW5nIGRlZmF1bHQgY29uZmlndXJhdGlvbi5cIilcclxuICAgICAgICAgIH1cclxuICAgICAgICB9LFxyXG4gICAgICAgIGVycm9yOihyZWFzb24pID0+IHtcclxuICAgICAgICAgIHRoaXMuYXIuc3RhdHVzTXNnID0gcmVhc29uO1xyXG4gICAgICAgICAgdGhpcy5hci5zdGF0dXNBbGVydFR5cGUgPSAnZXJyb3InO1xyXG4gICAgICAgICAgdGhpcy5hci5zdGF0dXNXYWl0aW5nPWZhbHNlO1xyXG4gICAgICAgICAgY29uc29sZS5lcnJvcihcIkVycm9yIGZldGNoaW5nIHNlc3Npb24gXCIgKyByZWFzb24pXHJcbiAgICAgICAgfX0pO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgdXBsb2FkVXBkYXRlKHVlOiBVcGxvYWRlclN0YXR1c0NoYW5nZUV2ZW50KSB7XHJcbiAgICBsZXQgdXBTdGF0dXMgPSB1ZS5zdGF0dXM7XHJcbiAgICB0aGlzLmRhdGFTYXZlZCA9IChVcGxvYWRlclN0YXR1cy5ET05FID09PSB1cFN0YXR1cyk7XHJcbiAgICBsZXQgcGVyY2VudFVwbCA9IHVlLnBlcmNlbnREb25lKCk7XHJcbiAgICBpZiAoVXBsb2FkZXJTdGF0dXMuRVJSID09PSB1cFN0YXR1cykge1xyXG4gICAgICB0aGlzLmFyLnVwbG9hZFN0YXR1cyA9ICd3YXJuJ1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgaWYgKHBlcmNlbnRVcGwgPCA1MCkge1xyXG4gICAgICAgIHRoaXMuYXIudXBsb2FkU3RhdHVzID0gJ2FjY2VudCdcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0aGlzLmFyLnVwbG9hZFN0YXR1cyA9ICdzdWNjZXNzJ1xyXG4gICAgICB9XHJcbiAgICAgIHRoaXMuYXIudXBsb2FkUHJvZ3Jlc3MgPSBwZXJjZW50VXBsO1xyXG4gICAgfVxyXG4gICAgdGhpcy5hci51cGRhdGVXYWtlTG9jayh0aGlzLmRhdGFTYXZlZCk7XHJcbiAgICB0aGlzLmNoYW5nZURldGVjdG9yUmVmLmRldGVjdENoYW5nZXMoKVxyXG4gIH1cclxuXHJcblxyXG4gIHJlYWR5KCk6Ym9vbGVhbntcclxuICAgIHJldHVybiB0aGlzLmRhdGFTYXZlZCAmJiAhdGhpcy5hci5pc0FjdGl2ZSgpXHJcbiAgfVxyXG5cclxufVxyXG4iXX0=
|