UNPKG

ngx-surveys

Version:
416 lines 63.6 kB
import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
import { Observable } from 'rxjs';
import { SurveyErrorStateMatcher } from '../index';
import { FormItem, SurveyFile } from '../form-item';
import * as _ from 'lodash';
//import { map } from 'rxjs/operators';
import * as WaveSurfer from 'wavesurfer.js';
import RecordRTC, { StereoAudioRecorder } from 'recordrtc';
import * as i0 from "@angular/core";
import * as i1 from "../../ngx-survey.service";
import * as i2 from "@angular/common";
import * as i3 from "@angular/material/button";
import * as i4 from "@angular/material/form-field";
import * as i5 from "@angular/material/icon";
import * as i6 from "@angular/material/progress-bar";
import * as i7 from "../../pipes/duration.pipe";
export class FormItemVoice extends FormItem {
}
export class FormItemVoiceComponent {
    constructor(service, zone) {
        this.service = service;
        this.zone = zone;
        this.editable = true;
        this.changes = new EventEmitter();
        this.matcher = new SurveyErrorStateMatcher();
        this.allowDelete = true;
        this.uploadedFilesNumber = 0;
        this.uploadFilesNumber = 0;
        this.timeLimit = 120;
        this.isEdge = navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator['msSaveOrOpenBlob'] || !!navigator['msSaveBlob']);
        this.isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
        this.controlIcon = 'record-circle';
    }
    ngOnInit() {
        if (this.item.value) {
            this.file = _.extend(new SurveyFile(), this.item.value);
            if (this.file?.url) {
                this.hasRecord = true;
            }
        }
        this.matcher.item = this.item;
        this.service;
        this.zone;
        Observable;
    }
    ngAfterViewInit() {
        // set the initial state of the video
        let audio = this.audio.nativeElement;
        audio.muted = false;
        audio.controls = true;
        audio.autoplay = false;
        if (this.file?.url) {
            this.hasRecord = true;
            this.wavesurfer = WaveSurfer.create({
                container: this.waveformElement.nativeElement,
                height: 190,
                waveColor: '#d4d9dd',
                progressColor: '#555',
                normalize: true,
                barHeight: 8,
                cursorWidth: 1,
                cursorColor: '#d9d9d9'
            });
            this.wavesurfer.load(this.file.url);
            this.wavesurfer.on('ready', () => {
                console.log(this.wavesurfer);
                this.zone.run(() => {
                    if (this.file) {
                        this.file.duration = this.wavesurfer.getDuration();
                        this.duration = this.file.duration * 1000;
                        this.wavesurferPlay = false;
                    }
                });
            });
            this.wavesurfer.on('play', () => {
                this.zone.run(() => {
                    this.wavesurferPlay = true;
                });
            });
            this.wavesurfer.on('pause', () => {
                this.zone.run(() => {
                    this.wavesurferPlay = false;
                });
            });
        }
    }
    ngOnChanges() {
        this.matcher.item = this.item;
    }
    checkRequired(placeholder) {
        if (placeholder === 'Required') {
            return true;
        }
    }
    onValueChanges(item) {
        this.changes.emit(item);
    }
    clear() {
        this.file = undefined;
        this.fileObject = undefined;
    }
    /*
        uploadFiles(files: SurveyFile[]) {
            console.log(files);
            if(!files.length){
                return;
            }
            files.forEach(file=>{
                file.uploading=true;
                file.progressSubject = new Observable(observer => {
                    file.progressObserver = observer
                });
                file.progressSubject.subscribe(
                  data => {
                    this.zone.run(()=>{
                        file.progressValue=data;
                        //console.log(file);
                        if (file.url){
                            this.item.value=this.files.filter(f=>f.url).map(f=>{
                                const obj=new SurveyFile;
                                obj.url=f.url;
                                obj.name=f.name;
                                obj.mime=f.mime;
                                return obj;
                            });
                            console.log(this.item);
                            this.changes.emit(this.item);
                        }
                    })
                  }
                );
            })
            this.service.onFilesSelected.next(files);
            this.files=_.union(this.files, files);
    
            this.isFilesUploading = true;
    
            this.uploadFilesNumber=files.length;
            this.uploadedFilesNumber=1;
        }
        */
    onCloseClick() {
        //this.dialogRef.close();
    }
    startRecording() {
        let mediaConstraints = {
            audio: this.isEdge ? true : {
                echoCancellation: false
            }
        };
        this.blobSize = 0;
        //window['AudioContext'] = window['AudioContext'] || window['webkitAudioContext'];
        navigator.mediaDevices
            .getUserMedia(mediaConstraints)
            .then(this.successCallback.bind(this), this.errorCallback.bind(this));
    }
    errorCallback(stream) {
        //console.log(stream);
    }
    successCallback(stream) {
        const microphone = stream;
        if (this.isSafari) {
            this.audio.muted = true;
            this.audio.srcObject = microphone;
        }
        //console.log(stream);
        //let audio: HTMLAudioElement = this.audio.nativeElement;
        let options = {
            mimeType: 'audio/webm',
            numberOfAudioChannels: this.isEdge ? 1 : 2,
            checkForInactiveTracks: true,
            bufferSize: 16384,
            elementClass: 'multi-streams-mixer'
        };
        this.stream = stream;
        if (this.isSafari || this.isEdge) {
            options['recorderType'] = StereoAudioRecorder;
        }
        if (navigator.platform && navigator.platform.toString().toLowerCase().indexOf('win') === -1) {
            options['sampleRate'] = 48000; // or 44100 or remove this line for default
        }
        if (this.isSafari) {
            options['sampleRate'] = 44100;
            options['bufferSize'] = 4096;
            options['numberOfAudioChannels'] = 2;
            options['mimeType'] = 'audio/wav';
        }
        if (this.recordRTC) {
            this.recordRTC.destroy();
            this.recordRTC = null;
        }
        this.recordRTC = new RecordRTC(stream, options);
        this.recordRTC.startRecording();
        this.audio.srcObject = stream;
        //audio.src = this.recordRTC.toURL();
        this.duration = 0;
        this.durationInterval = setInterval(() => {
            //console.log(this.recordRTC.getState(), this.recordRTC);
            if (this.recordRTC && this.recordRTC.getState() === 'recording') {
                this.duration++;
                //this.cd.detectChanges();
            }
            else {
                //clearInterval(durationInterval);
            }
        }, 1000);
        //console.log(this.recordRTC);
        this.toggleControls();
        this.recordingInprogress = true;
    }
    toggleControls() {
        let audio = this.audio.nativeElement;
        audio.muted = !audio.muted;
        audio.controls = !audio.controls;
        audio.autoplay = !audio.autoplay;
    }
    stopRecording() {
        let recordRTC = this.recordRTC;
        recordRTC.stopRecording(this.processAudio.bind(this));
        let stream = this.stream;
        stream.getTracks().forEach(track => track.stop());
        this.recordingInprogress = false;
        if (this.durationInterval) {
            clearInterval(this.durationInterval);
        }
    }
    processAudio(audioVideoWebMURL) {
        let audio = this.audio.nativeElement;
        let recordRTC = this.recordRTC;
        let fileReader = new FileReader();
        fileReader;
        audio.src = audioVideoWebMURL;
        this.toggleControls();
        this.recordedBlob = recordRTC.getBlob();
        if (!this.recordedBlob) {
            this.hasRecord = false;
            return;
        }
        let ext = 'mp3';
        if (this.recordedBlob.type) {
            ext = this.recordedBlob.type.split('/')[1];
            ext = ext.split(';')[0];
        }
        this.recordedBlob.name = 'audio.' + ext;
        if (!this.file) {
            this.file = new SurveyFile();
            this.file.name = 'audio.' + ext;
        }
        //this.file.workflow='audio';
        this.file.mime = this.recordedBlob.type;
        fileReader.onload = (event) => {
            if (this.file && event?.target) {
                this.file.src = event.target['result'];
                this.uploadFile(this.file);
            }
        };
        fileReader.readAsArrayBuffer(this.recordedBlob);
        this.file.size = this.recordedBlob.size;
        this.zone.run(() => {
            this.hasRecord = true;
        });
        //console.log(this.recordedBlob);
        recordRTC.getDataURL(dataURL => {
            //console.log(audioVideoWebMURL, dataURL, audio.src);
            this.wavesurfer = WaveSurfer.create({
                container: this.waveformElement.nativeElement,
                height: 190,
                waveColor: '#d4d9dd',
                progressColor: '#555',
                normalize: true,
                barHeight: 8,
                cursorWidth: 1,
                cursorColor: '#d9d9d9'
            });
            this.wavesurfer.loadBlob(this.recordedBlob);
            this.wavesurfer.on('ready', () => {
                //console.log(this.wavesurfer.getDuration());
                if (this.file) {
                    this.file.duration = this.wavesurfer.getDuration();
                    this.duration = this.file.duration * 1000;
                    this.wavesurferPlay = false;
                }
            });
            this.wavesurfer.on('play', () => {
                this.zone.run(() => {
                    this.wavesurferPlay = true;
                });
            });
            this.wavesurfer.on('pause', () => {
                this.zone.run(() => {
                    this.wavesurferPlay = false;
                });
            });
        });
        return this.file;
    }
    uploadFile(file) {
        file.uploading = true;
        file.progressSubject = new Observable(observer => {
            file.progressObserver = observer;
        });
        this.item.busy = true;
        file.progressSubject.subscribe(data => {
            this.zone.run(() => {
                file.progressValue = data;
                //console.log(file);
                if (file.url) {
                    const obj = new SurveyFile;
                    obj.url = file.url;
                    obj.name = file.name;
                    obj.mime = file.mime;
                    this.item.value = obj;
                    console.log(this.item);
                    this.item.busy = false;
                    this.changes.emit(this.item);
                }
            });
        }, err => {
            this.item.busy = false;
        });
        this.service.onFilesSelected.next([file]);
        this.file = file;
    }
    play() {
        if (this.wavesurfer) {
            this.wavesurfer.play();
        }
    }
    pause() {
        if (this.wavesurfer) {
            this.wavesurfer.pause();
        }
    }
    togglePlay() {
        if (this.wavesurfer) {
            if (this.wavesurferPlay) {
                this.pause();
                this.controlIcon = 'play-circle-thin';
            }
            else {
                this.play();
                this.controlIcon = 'pause-circle-thin';
            }
        }
    }
    toggleRecording() {
        //console.log('CLICKED');
        if (this.recordingInprogress) {
            this.stopRecording();
            this.controlIcon = 'play-circle-thin';
        }
        else {
            this.startRecording();
            this.controlIcon = 'record-round';
        }
    }
    clearBlob() {
        this.file = undefined;
        this.recordedBlob = null;
        this.hasRecord = false;
        if (this.wavesurfer) {
            this.wavesurfer.destroy();
        }
        if (this.recordRTC) {
            //this.recordRTC.destroy()
        }
        this.controlIcon = 'record-circle';
        this.item.value = undefined;
        this.changes.emit(this.item);
    }
    download() {
        this.recordRTC.save('audio.webm');
    }
    onSave() {
        //this.stopRecording();
    }
    ngOnDestroy() {
        if (this.wavesurfer) {
            this.wavesurfer.destroy();
        }
        if (this.recordRTC && this.recordRTC.state === 'recording') {
            this.recordRTC.stopRecording();
            this.recordRTC.clearRecordedData();
            //this.stopRecording();
        }
        if (this.recordRTC) {
            this.recordRTC.destroy();
        }
        if (this.stream) {
            this.stream.getTracks().forEach(track => track.stop());
        }
        if (this.durationInterval) {
            this.duration = 0;
            clearInterval(this.durationInterval);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: FormItemVoiceComponent, deps: [{ token: i1.NgxSurveyService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.12", type: FormItemVoiceComponent, selector: "app-form-item-voice", inputs: { item: "item", editable: "editable" }, outputs: { changes: "changes" }, viewQueries: [{ propertyName: "audio", first: true, predicate: ["audio"], descendants: true, static: true }, { propertyName: "waveformElement", first: true, predicate: ["waveform"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"form-group\">\n    <div class=\"col-xs-12 voice-recorder\">\n        <div *ngIf=\"item.label\" class=\"voice-recorder-title label\" [ngClass]=\"{'has-error':item.errors && item.errors.length}\">{{item.label}} <span *ngIf=\"item.required\" class=\"required-asterix\">*</span></div>\n        <div class=\"audio-recorder\">\n            <audio #audio class=\"audio\" style=\"display:none;\" [src]=\"file?.url\"></audio>\n            <div class=\"main-screen\">\n                <div #waveform class=\"waveform\" [ngClass]=\"{hidden:recordingInprogress || !hasRecord}\"></div>\n                <ng-container *ngIf=\"!recordingInprogress && hasRecord; else startScreen\">\n                    <button mat-button class=\"main-screen-button\" (click)=\"togglePlay()\" type=\"button\">\n                        <mat-icon class=\"main-screen-icon-big\">{{this.wavesurferPlay ? 'pause_circle_outline' : 'play_circle_outline'}}</mat-icon>\n                    </button>\n\n                    <a mat-button class=\"main-screen-button-aside download-btn\" *ngIf=\"item.value && item.value.url\" [href]=\"item.value.url\" target=\"_blank\" title=\"Dowload Record\">\n                        <mat-icon class=\"main-screen-icon-small\">download</mat-icon>\n                    </a>\n                    <button mat-button class=\"main-screen-button-aside delete-btn\" title=\"Delete Record\" (click)=\"clearBlob()\" *ngIf=\"editable\" type=\"button\">\n                        <mat-icon class=\"main-screen-icon-small\">cancel</mat-icon>\n                    </button>\n                    <mat-progress-bar mode=\"determinate\"  *ngIf=\"file && file.uploading\" [value]=\"file.progressValue\"></mat-progress-bar>\n                </ng-container>\n\n            </div>\n\n            <ng-template #startScreen>\n                <div class=\"start-screen\">\n                    <button mat-button class=\"start-screen-button\" (click)=\"toggleRecording()\" [disabled]=\"!editable\" type=\"button\">\n                        <mat-icon class=\"start-screen-icon\" >{{recordingInprogress ? 'mic':'mic_none'}}</mat-icon>\n                        <div *ngIf=\"!recordingInprogress && !hasRecord\" class=\"start-screen-text\">Tap to record audio</div>\n                        <div *ngIf=\"recordingInprogress\" class=\"start-screen-text\">\n                            {{duration | duration:'seconds'}}\n                            / {{timeLimit - duration | duration:'seconds'}}\n                        </div>\n                    </button>\n                </div>\n            </ng-template>\n        </div>\n        <mat-error *ngIf=\"item.errors && item.errors.length\">\n            <div *ngFor=\"let error of item.errors\">{{error.message}}</div>\n        </mat-error>\n        <mat-hint align=\"start\" *ngIf=\"!(item.errors && item.errors.length)\"><strong>{{item.hint}}</strong> </mat-hint>\n    </div>\n</div>\n", styles: [".mat-mdc-form-field{width:100%}.audio-recorder{height:192px;background-color:#f9f9f9;border:1px solid #efefef;border-radius:4px}.start-screen{display:flex;align-items:center;justify-content:center;height:100%;position:absolute;top:0;width:100%}.start-screen-button{height:100%;width:100%}.start-screen-icon{font-size:6rem;color:#9b0000;width:96px;height:96px}.start-screen-text{color:#919191;margin-top:15px}.main-screen{position:relative;height:100%}.main-screen-button{position:absolute;top:50%;left:50%;transform:translate3d(-50%,-50%,0);padding:0;z-index:3;border-radius:50%}.main-screen-button-aside{position:absolute;bottom:15px;right:15px;min-width:48px;padding:0;border-radius:50%;z-index:3}.main-screen-button-aside.delete-btn .mat-icon{color:#c72939}.main-screen-button-aside.download-btn{left:15px;right:auto}.main-screen-button-aside.download-btn .mat-icon{color:#3f51b5}.main-screen-icon-big{font-size:6rem;color:#10027b;width:96px;height:96px}.main-screen-icon-small{font-size:3rem;color:#b1b1b1;width:48px;height:48px}#waveform{height:100%!important;background-color:#f9f9f9;display:block}#waveform.hidden{display:block}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.MatAnchor, selector: "a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatButton, selector: "    button[mat-button], button[mat-raised-button], button[mat-flat-button],    button[mat-stroked-button]  ", exportAs: ["matButton"] }, { kind: "directive", type: i4.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i6.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "pipe", type: i7.DurationPipe, name: "duration" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: FormItemVoiceComponent, decorators: [{
            type: Component,
            args: [{ selector: 'app-form-item-voice', template: "<div class=\"form-group\">\n    <div class=\"col-xs-12 voice-recorder\">\n        <div *ngIf=\"item.label\" class=\"voice-recorder-title label\" [ngClass]=\"{'has-error':item.errors && item.errors.length}\">{{item.label}} <span *ngIf=\"item.required\" class=\"required-asterix\">*</span></div>\n        <div class=\"audio-recorder\">\n            <audio #audio class=\"audio\" style=\"display:none;\" [src]=\"file?.url\"></audio>\n            <div class=\"main-screen\">\n                <div #waveform class=\"waveform\" [ngClass]=\"{hidden:recordingInprogress || !hasRecord}\"></div>\n                <ng-container *ngIf=\"!recordingInprogress && hasRecord; else startScreen\">\n                    <button mat-button class=\"main-screen-button\" (click)=\"togglePlay()\" type=\"button\">\n                        <mat-icon class=\"main-screen-icon-big\">{{this.wavesurferPlay ? 'pause_circle_outline' : 'play_circle_outline'}}</mat-icon>\n                    </button>\n\n                    <a mat-button class=\"main-screen-button-aside download-btn\" *ngIf=\"item.value && item.value.url\" [href]=\"item.value.url\" target=\"_blank\" title=\"Dowload Record\">\n                        <mat-icon class=\"main-screen-icon-small\">download</mat-icon>\n                    </a>\n                    <button mat-button class=\"main-screen-button-aside delete-btn\" title=\"Delete Record\" (click)=\"clearBlob()\" *ngIf=\"editable\" type=\"button\">\n                        <mat-icon class=\"main-screen-icon-small\">cancel</mat-icon>\n                    </button>\n                    <mat-progress-bar mode=\"determinate\"  *ngIf=\"file && file.uploading\" [value]=\"file.progressValue\"></mat-progress-bar>\n                </ng-container>\n\n            </div>\n\n            <ng-template #startScreen>\n                <div class=\"start-screen\">\n                    <button mat-button class=\"start-screen-button\" (click)=\"toggleRecording()\" [disabled]=\"!editable\" type=\"button\">\n                        <mat-icon class=\"start-screen-icon\" >{{recordingInprogress ? 'mic':'mic_none'}}</mat-icon>\n                        <div *ngIf=\"!recordingInprogress && !hasRecord\" class=\"start-screen-text\">Tap to record audio</div>\n                        <div *ngIf=\"recordingInprogress\" class=\"start-screen-text\">\n                            {{duration | duration:'seconds'}}\n                            / {{timeLimit - duration | duration:'seconds'}}\n                        </div>\n                    </button>\n                </div>\n            </ng-template>\n        </div>\n        <mat-error *ngIf=\"item.errors && item.errors.length\">\n            <div *ngFor=\"let error of item.errors\">{{error.message}}</div>\n        </mat-error>\n        <mat-hint align=\"start\" *ngIf=\"!(item.errors && item.errors.length)\"><strong>{{item.hint}}</strong> </mat-hint>\n    </div>\n</div>\n", styles: [".mat-mdc-form-field{width:100%}.audio-recorder{height:192px;background-color:#f9f9f9;border:1px solid #efefef;border-radius:4px}.start-screen{display:flex;align-items:center;justify-content:center;height:100%;position:absolute;top:0;width:100%}.start-screen-button{height:100%;width:100%}.start-screen-icon{font-size:6rem;color:#9b0000;width:96px;height:96px}.start-screen-text{color:#919191;margin-top:15px}.main-screen{position:relative;height:100%}.main-screen-button{position:absolute;top:50%;left:50%;transform:translate3d(-50%,-50%,0);padding:0;z-index:3;border-radius:50%}.main-screen-button-aside{position:absolute;bottom:15px;right:15px;min-width:48px;padding:0;border-radius:50%;z-index:3}.main-screen-button-aside.delete-btn .mat-icon{color:#c72939}.main-screen-button-aside.download-btn{left:15px;right:auto}.main-screen-button-aside.download-btn .mat-icon{color:#3f51b5}.main-screen-icon-big{font-size:6rem;color:#10027b;width:96px;height:96px}.main-screen-icon-small{font-size:3rem;color:#b1b1b1;width:48px;height:48px}#waveform{height:100%!important;background-color:#f9f9f9;display:block}#waveform.hidden{display:block}\n"] }]
        }], ctorParameters: () => [{ type: i1.NgxSurveyService }, { type: i0.NgZone }], propDecorators: { audio: [{
                type: ViewChild,
                args: ['audio', { static: true }]
            }], waveformElement: [{
                type: ViewChild,
                args: ['waveform', { static: true }]
            }], item: [{
                type: Input
            }], editable: [{
                type: Input
            }], changes: [{
                type: Output
            }] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9ybS1pdGVtLXZvaWNlLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL25neC1zdXJ2ZXlzL3NyYy9saWIvZm9ybS1pdGVtL2Zvcm0taXRlbS12b2ljZS9mb3JtLWl0ZW0tdm9pY2UuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LXN1cnZleXMvc3JjL2xpYi9mb3JtLWl0ZW0vZm9ybS1pdGVtLXZvaWNlL2Zvcm0taXRlbS12b2ljZS5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFVLEtBQUssRUFBRSxNQUFNLEVBQUUsWUFBWSxFQUF5QixTQUFTLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDakgsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUNsQyxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDbkQsT0FBTyxFQUFFLFFBQVEsRUFBa0IsVUFBVSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ3BFLE9BQU8sS0FBSyxDQUFDLE1BQU0sUUFBUSxDQUFDO0FBRTVCLHVDQUF1QztBQUN2QyxPQUFPLEtBQUssVUFBVSxNQUFNLGVBQWUsQ0FBQztBQUM1QyxPQUFPLFNBQVMsRUFBRSxFQUFFLG1CQUFtQixFQUFFLE1BQU0sV0FBVyxDQUFDOzs7Ozs7Ozs7QUFFM0QsTUFBTSxPQUFPLGFBQWMsU0FBUSxRQUFRO0NBSTFDO0FBT0QsTUFBTSxPQUFPLHNCQUFzQjtJQW1DL0IsWUFDWSxPQUF5QixFQUN6QixJQUFZO1FBRFosWUFBTyxHQUFQLE9BQU8sQ0FBa0I7UUFDekIsU0FBSSxHQUFKLElBQUksQ0FBUTtRQS9CZixhQUFRLEdBQVUsSUFBSSxDQUFDO1FBQ3RCLFlBQU8sR0FBRyxJQUFJLFlBQVksRUFBaUIsQ0FBQztRQUN0RCxZQUFPLEdBQUcsSUFBSSx1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LE1BQU0sRUFBRSxJQUFJLEVBQUU7Z0JBQ0ssZUFBZTtzQkFBdkQsU0FBUzt1QkFBQyxVQUFVLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFO2dCQUU5QixJQUFJO3NCQUFaLEtBQUs7Z0JBQ0csUUFBUTtzQkFBaEIsS0FBSztnQkFDSSxPQUFPO3NCQUFoQixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBPbkluaXQsIElucHV0LCBPdXRwdXQsIEV2ZW50RW1pdHRlciwgTmdab25lLCBBZnRlclZpZXdJbml0LCBWaWV3Q2hpbGQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IE9ic2VydmFibGUgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IFN1cnZleUVycm9yU3RhdGVNYXRjaGVyIH0gZnJvbSAnLi4vaW5kZXgnO1xuaW1wb3J0IHsgRm9ybUl0ZW0sIEZvcm1JdGVtV2lkZ2V0LCBTdXJ2ZXlGaWxlIH0gZnJvbSAnLi4vZm9ybS1pdGVtJztcbmltcG9ydCAqIGFzIF8gZnJvbSAnbG9kYXNoJztcbmltcG9ydCB7IE5neFN1cnZleVNlcnZpY2UgfSBmcm9tICcuLi8uLi9uZ3gtc3VydmV5LnNlcnZpY2UnO1xuLy9pbXBvcnQgeyBtYXAgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgKiBhcyBXYXZlU3VyZmVyIGZyb20gJ3dhdmVzdXJmZXIuanMnO1xuaW1wb3J0IFJlY29yZFJUQywgeyBTdGVyZW9BdWRpb1JlY29yZGVyIH0gZnJvbSAncmVjb3JkcnRjJztcblxuZXhwb3J0IGNsYXNzIEZvcm1JdGVtVm9pY2UgZXh0ZW5kcyBGb3JtSXRlbSB7XG4gICAgdmFsdWU/OiBTdXJ2ZXlGaWxlO1xuICAgIGFyZWFMYWJlbDogc3RyaW5nO1xuICAgIGJ1dHRvbkxhYmVsOiBzdHJpbmc7XG59XG5cbkBDb21wb25lbnQoe1xuICAgIHNlbGVjdG9yOiAnYXBwLWZvcm0taXRlbS12b2ljZScsXG4gICAgdGVtcGxhdGVVcmw6ICcuL2Zvcm0taXRlbS12b2ljZS5jb21wb25lbnQuaHRtbCcsXG4gICAgc3R5bGVVcmxzOiBbJy4vZm9ybS1pdGVtLXZvaWNlLmNvbXBvbmVudC5zY3NzJ11cbiAgfSlcbmV4cG9ydCBjbGFzcyBGb3JtSXRlbVZvaWNlQ29tcG9uZW50IGltcGxlbWVudHMgRm9ybUl0ZW1XaWRnZXQsIE9uSW5pdCwgQWZ0ZXJWaWV3SW5pdCB7XG5cbiAgICBAVmlld0NoaWxkKCdhdWRpbycsIHsgc3RhdGljOiB0cnVlIH0pIGF1ZGlvOiBhbnk7XG4gICAgQFZpZXdDaGlsZCgnd2F2ZWZvcm0nLCB7IHN0YXRpYzogdHJ1ZSB9KSB3YXZlZm9ybUVsZW1lbnQ6IGFueTtcblxuICAgIEBJbnB1dCgpIGl0ZW06IEZvcm1JdGVtVm9pY2U7XG4gICAgQElucHV0KCkgZWRpdGFibGU6IGJvb2xlYW49dHJ1ZTtcbiAgICBAT3V0cHV0KCkgY2hhbmdlcyA9IG5ldyBFdmVudEVtaXR0ZXI8Rm9ybUl0ZW1Wb2ljZT4oKTtcbiAgICBtYXRjaGVyID0gbmV3IFN1cnZleUVycm9yU3RhdGVNYXRjaGVyKCk7XG5cbiAgICBwdWJsaWMgZmlsZU9iamVjdDogYW55O1xuXG4gICAgcHVibGljIGZpbGU6IHVuZGVmaW5lZCB8IFN1cnZleUZpbGU7XG5cbiAgICBwdWJsaWMgaXNGaWxlc1VwbG9hZGluZzogYm9vbGVhbjtcbiAgICBwdWJsaWMgYWxsb3dEZWxldGU6IGJvb2xlYW49dHJ1ZTtcbiAgICBwdWJsaWMgdXBsb2FkZWRGaWxlc051bWJlcjogbnVtYmVyPTA7XG4gICAgcHVibGljIHVwbG9hZEZpbGVzTnVtYmVyOiBudW1iZXI9MDtcblxuICAgIHB1YmxpYyBzdHJlYW06YW55O1xuICAgIHB1YmxpYyByZWNvcmRSVEM6YW55O1xuICAgIHB1YmxpYyByZWNvcmRlZEJsb2I6IGFueTtcbiAgICBwdWJsaWMgcmVjb3JkaW5nSW5wcm9ncmVzczogYm9vbGVhbjtcbiAgICBwdWJsaWMgYmxvYlNpemU6bnVtYmVyO1xuICAgIHB1YmxpYyBoYXNSZWNvcmQ6Ym9vbGVhbjtcbiAgICBwdWJsaWMgZHVyYXRpb246bnVtYmVyO1xuICAgIHB1YmxpYyB0aW1lTGltaXQ6bnVtYmVyPTEyMDtcbiAgICBwdWJsaWMgd2F2ZXN1cmZlcjphbnk7XG4gICAgcHVibGljIHdhdmVzdXJmZXJQbGF5OmJvb2xlYW47XG4gICAgcHVibGljIGlzRWRnZTogYm9vbGVhbiA9IG5hdmlnYXRvci51c2VyQWdlbnQuaW5kZXhPZignRWRnZScpICE9PSAtMSAmJiAoISFuYXZpZ2F0b3JbJ21zU2F2ZU9yT3BlbkJsb2InXSB8fCAhIW5hdmlnYXRvclsnbXNTYXZlQmxvYiddKTtcbiAgICBwdWJsaWMgaXNTYWZhcmk6IGJvb2xlYW4gPSAvXigoPyFjaHJvbWV8YW5kcm9pZCkuKSpzYWZhcmkvaS50ZXN0KG5hdmlnYXRvci51c2VyQWdlbnQpO1xuICAgIHB1YmxpYyBkdXJhdGlvbkludGVydmFsO1xuICAgIHB1YmxpYyBjb250cm9sSWNvbjpzdHJpbmcgPSAncmVjb3JkLWNpcmNsZSc7XG5cblxuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBwcml2YXRlIHNlcnZpY2U6IE5neFN1cnZleVNlcnZpY2UsXG4gICAgICAgIHByaXZhdGUgem9uZTogTmdab25lLFxuICAgICkgeyB9XG5cblxuICAgIG5nT25Jbml0KCkge1xuICAgICAgICBpZiAodGhpcy5pdGVtLnZhbHVlKXtcbiAgICAgICAgICAgIHRoaXMuZmlsZT1fLmV4dGVuZChuZXcgU3VydmV5RmlsZSgpLCB0aGlzLml0ZW0udmFsdWUpO1xuICAgICAgICAgICAgaWYgKHRoaXMuZmlsZT8udXJsKXtcbiAgICAgICAgICAgICAgICB0aGlzLmhhc1JlY29yZD10cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHRoaXMubWF0Y2hlci5pdGVtPXRoaXMuaXRlbTtcbiAgICAgICAgdGhpcy5zZXJ2aWNlO1xuICAgICAgICB0aGlzLnpvbmU7XG4gICAgICAgIE9ic2VydmFibGU7XG4gICAgfVxuXG4gICAgbmdBZnRlclZpZXdJbml0KCkge1xuICAgICAgICAvLyBzZXQgdGhlIGluaXRpYWwgc3RhdGUgb2YgdGhlIHZpZGVvXG4gICAgICAgIGxldCBhdWRpbzpIVE1MQXVkaW9FbGVtZW50ID0gdGhpcy5hdWRpby5uYXRpdmVFbGVtZW50O1xuICAgICAgICBhdWRpby5tdXRlZCA9IGZhbHNlO1xuICAgICAgICBhdWRpby5jb250cm9scyA9IHRydWU7XG4gICAgICAgIGF1ZGlvLmF1dG9wbGF5ID0gZmFsc2U7XG5cbiAgICAgICAgaWYgKHRoaXMuZmlsZT8udXJsKXtcbiAgICAgICAgICAgIHRoaXMuaGFzUmVjb3JkPXRydWU7XG4gICAgICAgICAgICB0aGlzLndhdmVzdXJmZXIgPSBXYXZlU3VyZmVyLmNyZWF0ZSh7XG4gICAgICAgICAgICAgICAgY29udGFpbmVyOiB0aGlzLndhdmVmb3JtRWxlbWVudC5uYXRpdmVFbGVtZW50LFxuICAgICAgICAgICAgICAgIGhlaWdodDogMTkwLFxuICAgICAgICAgICAgICAgIHdhdmVDb2xvcjogJyNkNGQ5ZGQnLFxuICAgICAgICAgICAgICAgIHByb2dyZXNzQ29sb3I6ICcjNTU1JyxcbiAgICAgICAgICAgICAgICBub3JtYWxpemU6IHRydWUsXG4gICAgICAgICAgICAgICAgYmFySGVpZ2h0OiA4LFxuICAgICAgICAgICAgICAgIGN1cnNvcldpZHRoOiAxLFxuICAgICAgICAgICAgICAgIGN1cnNvckNvbG9yOiAnI2Q5ZDlkOSdcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgdGhpcy53YXZlc3VyZmVyLmxvYWQodGhpcy5maWxlLnVybCk7XG5cbiAgICAgICAgICAgIHRoaXMud2F2ZXN1cmZlci5vbigncmVhZHknLCAoKT0+e1xuICAgICAgICAgICAgY29uc29sZS5sb2codGhpcy53YXZlc3VyZmVyKTtcbiAgICAgICAgICAgICAgICB0aGlzLnpvbmUucnVuKCgpPT57XG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmZpbGUpe1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5maWxlLmR1cmF0aW9uPXRoaXMud2F2ZXN1cmZlci5nZXREdXJhdGlvbigpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5kdXJhdGlvbj10aGlzLmZpbGUuZHVyYXRpb24qMTAwMDtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMud2F2ZXN1cmZlclBsYXk9ZmFsc2U7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KVxuXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHRoaXMud2F2ZXN1cmZlci5vbigncGxheScsICgpPT57XG4gICAgICAgICAgICAgICAgdGhpcy56b25lLnJ1bigoKT0+e1xuICAgICAgICAgICAgICAgICAgICB0aGlzLndhdmVzdXJmZXJQbGF5PXRydWU7XG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgdGhpcy53YXZlc3VyZmVyLm9uKCdwYXVzZScsICgpPT57XG4gICAgICAgICAgICAgICAgdGhpcy56b25lLnJ1bigoKT0+e1xuICAgICAgICAgICAgICAgICAgICB0aGlzLndhdmVzdXJmZXJQbGF5PWZhbHNlO1xuICAgICAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgbmdPbkNoYW5nZXMoKSB7XG4gICAgICAgIHRoaXMubWF0Y2hlci5pdGVtPXRoaXMuaXRlbTtcbiAgICB9XG5cbiAgICBjaGVja1JlcXVpcmVkKHBsYWNlaG9sZGVyKSB7XG4gICAgICAgIGlmIChwbGFjZWhvbGRlciA9PT0gJ1JlcXVpcmVkJykge1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBvblZhbHVlQ2hhbmdlcyhpdGVtKSB7XG4gICAgICAgIHRoaXMuY2hhbmdlcy5lbWl0KGl0ZW0pO1xuICAgIH1cblxuICAgIGNsZWFyKCl7XG4gICAgICAgIHRoaXMuZmlsZT11bmRlZmluZWQ7XG4gICAgICAgIHRoaXMuZmlsZU9iamVjdD11bmRlZmluZWQ7XG4gICAgfVxuLypcbiAgICB1cGxvYWRGaWxlcyhmaWxlczogU3VydmV5RmlsZVtdKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGZpbGVzKTtcbiAgICAgICAgaWYoIWZpbGVzLmxlbmd0aCl7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgZmlsZXMuZm9yRWFjaChmaWxlPT57XG4gICAgICAgICAgICBmaWxlLnVwbG9hZGluZz10cnVlO1xuICAgICAgICAgICAgZmlsZS5wcm9ncmVzc1N1YmplY3QgPSBuZXcgT2JzZXJ2YWJsZShvYnNlcnZlciA9PiB7XG4gICAgICAgICAgICAgICAgZmlsZS5wcm9ncmVzc09ic2VydmVyID0gb2JzZXJ2ZXJcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgZmlsZS5wcm9ncmVzc1N1YmplY3Quc3Vic2NyaWJlKFxuICAgICAgICAgICAgICBkYXRhID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLnpvbmUucnVuKCgpPT57XG4gICAgICAgICAgICAgICAgICAgIGZpbGUucHJvZ3Jlc3NWYWx1ZT1kYXRhO1xuICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUubG9nKGZpbGUpO1xuICAgICAgICAgICAgICAgICAgICBpZiAoZmlsZS51cmwpe1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5pdGVtLnZhbHVlPXRoaXMuZmlsZXMuZmlsdGVyKGY9PmYudXJsKS5tYXAoZj0+e1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IG9iaj1uZXcgU3VydmV5RmlsZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvYmoudXJsPWYudXJsO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9iai5uYW1lPWYubmFtZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvYmoubWltZT1mLm1pbWU7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG9iajtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2codGhpcy5pdGVtKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY2hhbmdlcy5lbWl0KHRoaXMuaXRlbSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgICAgICB9KVxuICAgICAgICB0aGlzLnNlcnZpY2Uub25GaWxlc1NlbGVjdGVkLm5leHQoZmlsZXMpO1xuICAgICAgICB0aGlzLmZpbGVzPV8udW5pb24odGhpcy5maWxlcywgZmlsZXMpO1xuXG4gICAgICAgIHRoaXMuaXNGaWxlc1VwbG9hZGluZyA9IHRydWU7XG5cbiAgICAgICAgdGhpcy51cGxvYWRGaWxlc051bWJlcj1maWxlcy5sZW5ndGg7XG4gICAgICAgIHRoaXMudXBsb2FkZWRGaWxlc051bWJlcj0xO1xuICAgIH1cbiAgICAqL1xuXG4gICAgb25DbG9zZUNsaWNrKCk6IHZvaWQge1xuICAgICAgICAvL3RoaXMuZGlhbG9nUmVmLmNsb3NlKCk7XG4gICAgfVxuXG4gICAgc3RhcnRSZWNvcmRpbmcoKSB7XG4gICAgICAgIGxldCBtZWRpYUNvbnN0cmFpbnRzID0ge1xuICAgICAgICAgICAgYXVkaW86IHRoaXMuaXNFZGdlID8gdHJ1ZSA6IHtcbiAgICAgICAgICAgICAgICBlY2hvQ2FuY2VsbGF0aW9uOiBmYWxzZVxuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgICB0aGlzLmJsb2JTaXplPTA7XG4gICAgICAgIC8vd2luZG93WydBdWRpb0NvbnRleHQnXSA9IHdpbmRvd1snQXVkaW9Db250ZXh0J10gfHwgd2luZG93Wyd3ZWJraXRBdWRpb0NvbnRleHQnXTtcbiAgICAgICAgbmF2aWdhdG9yLm1lZGlhRGV2aWNlc1xuICAgICAgICAgIC5nZXRVc2VyTWVkaWEobWVkaWFDb25zdHJhaW50cylcbiAgICAgICAgICAudGhlbih0aGlzLnN1Y2Nlc3NDYWxsYmFjay5iaW5kKHRoaXMpLCB0aGlzLmVycm9yQ2FsbGJhY2suYmluZCh0aGlzKSk7XG4gICAgfVxuXG4gICAgZXJyb3JDYWxsYmFjayhzdHJlYW06IE1lZGlhU3RyZWFtKSB7XG4gICAgICAgIC8vY29uc29sZS5sb2coc3RyZWFtKTtcbiAgICB9XG5cbiAgICBzdWNjZXNzQ2FsbGJhY2soc3RyZWFtOiBNZWRpYVN0cmVhbSkge1xuICAgICAgICBjb25zdCBtaWNyb3Bob25lPXN0cmVhbTtcblxuICAgICAgICBpZih0aGlzLmlzU2FmYXJpKSB7XG4gICAgICAgICAgICB0aGlzLmF1ZGlvLm11dGVkID0gdHJ1ZTtcbiAgICAgICAgICAgIHRoaXMuYXVkaW8uc3JjT2JqZWN0ID0gbWljcm9waG9uZTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vY29uc29sZS5sb2coc3RyZWFtKTtcbiAgICAgICAgLy9sZXQgYXVkaW86IEhUTUxBdWRpb0VsZW1lbnQgPSB0aGlzLmF1ZGlvLm5hdGl2ZUVsZW1lbnQ7XG4gICAgICAgIGxldCBvcHRpb25zID0ge1xuICAgICAgICAgICAgbWltZVR5cGU6ICdhdWRpby93ZWJtJyxcbiAgICAgICAgICAgIG51bWJlck9mQXVkaW9DaGFubmVsczogdGhpcy5pc0VkZ2UgPyAxIDogMixcbiAgICAgICAgICAgIGNoZWNrRm9ySW5hY3RpdmVUcmFja3M6IHRydWUsXG4gICAgICAgICAgICBidWZmZXJTaXplOiAxNjM4NCxcbiAgICAgICAgICAgIGVsZW1lbnRDbGFzczogJ211bHRpLXN0cmVhbXMtbWl4ZXInXG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuc3RyZWFtID0gc3RyZWFtO1xuXG4gICAgICAgIGlmKHRoaXMuaXNTYWZhcmkgfHwgdGhpcy5pc0VkZ2UpIHtcbiAgICAgICAgICAgIG9wdGlvbnNbJ3JlY29yZGVyVHlwZSddID0gU3RlcmVvQXVkaW9SZWNvcmRlcjtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmKG5hdmlnYXRvci5wbGF0Zm9ybSAmJiBuYXZpZ2F0b3IucGxhdGZvcm0udG9TdHJpbmcoKS50b0xvd2VyQ2FzZSgpLmluZGV4T2YoJ3dpbicpID09PSAtMSkge1xuICAgICAgICAgICAgb3B0aW9uc1snc2FtcGxlUmF0ZSddID0gNDgwMDA7IC8vIG9yIDQ0MTAwIG9yIHJlbW92ZSB0aGlzIGxpbmUgZm9yIGRlZmF1bHRcbiAgICAgICAgfVxuICAgICAgICBpZih0aGlzLmlzU2FmYXJpKSB7XG4gICAgICAgICAgICBvcHRpb25zWydzYW1wbGVSYXRlJ10gPSA0NDEwMDtcbiAgICAgICAgICAgIG9wdGlvbnNbJ2J1ZmZlclNpemUnXSA9IDQwOTY7XG4gICAgICAgICAgICBvcHRpb25zWydudW1iZXJPZkF1ZGlvQ2hhbm5lbHMnXSA9IDI7XG4gICAgICAgICAgICBvcHRpb25zWydtaW1lVHlwZSddPSdhdWRpby93YXYnO1xuICAgICAgICB9XG4gICAgICAgIGlmKHRoaXMucmVjb3JkUlRDKSB7XG4gICAgICAgICAgICB0aGlzLnJlY29yZFJUQy5kZXN0cm95KCk7XG4gICAgICAgICAgICB0aGlzLnJlY29yZFJUQyA9IG51bGw7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnJlY29yZFJUQyA9IG5ldyBSZWNvcmRSVEMoc3RyZWFtLCBvcHRpb25zKTtcbiAgICAgICAgdGhpcy5yZWNvcmRSVEMuc3RhcnRSZWNvcmRpbmcoKTtcbiAgICAgICAgdGhpcy5hdWRpby5zcmNPYmplY3QgPSBzdHJlYW07XG4gICAgICAgIC8vYXVkaW8uc3JjID0gdGhpcy5yZWNvcmRSVEMudG9VUkwoKTtcbiAgICAgICAgdGhpcy5kdXJhdGlvbj0wO1xuICAgICAgICB0aGlzLmR1cmF0aW9uSW50ZXJ2YWw9c2V0SW50ZXJ2YWwoKCk9PntcbiAgICAgICAgICAgIC8vY29uc29sZS5sb2codGhpcy5yZWNvcmRSVEMuZ2V0U3RhdGUoKSwgdGhpcy5yZWNvcmRSVEMpO1xuICAgICAgICAgICAgaWYgKHRoaXMucmVjb3JkUlRDICYmIHRoaXMucmVjb3JkUlRDLmdldFN0YXRlKCk9PT0ncmVjb3JkaW5nJyl7XG4gICAgICAgICAgICAgICAgdGhpcy5kdXJhdGlvbisrO1xuICAgICAgICAgICAgICAgIC8vdGhpcy5jZC5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAvL2NsZWFySW50ZXJ2YWwoZHVyYXRpb25JbnRlcnZhbCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sIDEwMDApO1xuICAgICAgICAvL2NvbnNvbGUubG9nKHRoaXMucmVjb3JkUlRDKTtcblxuICAgICAgICB0aGlzLnRvZ2dsZUNvbnRyb2xzKCk7XG4gICAgICAgIHRoaXMucmVjb3JkaW5nSW5wcm9ncmVzcz10cnVlO1xuICAgIH1cblxuICAgIHRvZ2dsZUNvbnRyb2xzKCkge1xuICAgICAgICBsZXQgYXVkaW86IEhUTUxBdWRpb0VsZW1lbnQgPSB0aGlzLmF1ZGlvLm5hdGl2ZUVsZW1lbnQ7XG4gICAgICAgIGF1ZGlvLm11dGVkID0gIWF1ZGlvLm11dGVkO1xuICAgICAgICBhdWRpby5jb250cm9scyA9ICFhdWRpby5jb250cm9scztcbiAgICAgICAgYXVkaW8uYXV0b3BsYXkgPSAhYXVkaW8uYXV0b3BsYXk7XG4gICAgfVxuXG4gICAgc3RvcFJlY29yZGluZygpIHtcbiAgICAgICAgbGV0IHJlY29yZFJUQyA9IHRoaXMucmVjb3JkUlRDO1xuICAgICAgICByZWNvcmRSVEMuc3RvcFJlY29yZGluZyh0aGlzLnByb2Nlc3NBdWRpby5iaW5kKHRoaXMpKTtcbiAgICAgICAgbGV0IHN0cmVhbSA9IHRoaXMuc3RyZWFtO1xuICAgICAgICBzdHJlYW0uZ2V0VHJhY2tzKCkuZm9yRWFjaCh0cmFjayA9PiB0cmFjay5zdG9wKCkpO1xuICAgICAgICB0aGlzLnJlY29yZGluZ0lucHJvZ3Jlc3M9ZmFsc2U7XG4gICAgICAgIGlmICh0aGlzLmR1cmF0aW9uSW50ZXJ2YWwpe1xuICAgICAgICAgICAgY2xlYXJJbnRlcnZhbCh0aGlzLmR1cmF0aW9uSW50ZXJ2YWwpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJvY2Vzc0F1ZGlvKGF1ZGlvVmlkZW9XZWJNVVJMKSB7XG4gICAgICAgIGxldCBhdWRpbzogSFRNTEF1ZGlvRWxlbWVudCA9IHRoaXMuYXVkaW8ubmF0aXZlRWxlbWVudDtcbiAgICAgICAgbGV0IHJlY29yZFJUQyA9IHRoaXMucmVjb3JkUlRDO1xuICAgICAgICBsZXQgZmlsZVJlYWRlciA9IG5ldyBGaWxlUmVhZGVyKCk7XG4gICAgICAgIGZpbGVSZWFkZXI7XG5cbiAgICAgICAgYXVkaW8uc3JjID0gYXVkaW9WaWRlb1dlYk1VUkw7XG4gICAgICAgIHRoaXMudG9nZ2xlQ29udHJvbHMoKTtcbiAgICAgICAgdGhpcy5yZWNvcmRlZEJsb2IgPSByZWNvcmRSVEMuZ2V0QmxvYigpO1xuICAgICAgICBpZiAoIXRoaXMucmVjb3JkZWRCbG9iKXtcbiAgICAgICAgICAgIHRoaXMuaGFzUmVjb3JkPWZhbHNlO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGxldCBleHQ9J21wMyc7XG4gICAgICAgIGlmICh0aGlzLnJlY29yZGVkQmxvYi50eXBlKXtcbiAgICAgICAgICAgIGV4dD10aGlzLnJlY29yZGVkQmxvYi50eXBlLnNwbGl0KCcvJylbMV07XG4gICAgICAgICAgICBleHQ9ZXh0LnNwbGl0KCc7JylbMF07XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5yZWNvcmRlZEJsb2IubmFtZT0nYXVkaW8uJytleHQ7XG5cbiAgICAgICAgaWYgKCF0aGlzLmZpbGUpe1xuICAgICAgICAgICAgdGhpcy5maWxlPW5ldyBTdXJ2ZXlGaWxlKCk7XG4gICAgICAgICAgICB0aGlzLmZpbGUubmFtZT0nYXVkaW8uJytleHQ7XG4gICAgICAgIH1cbiAgICAgICAgLy90aGlzLmZpbGUud29ya2Zsb3c9J2F1ZGlvJztcbiAgICAgICAgdGhpcy5maWxlLm1pbWU9dGhpcy5yZWNvcmRlZEJsb2IudHlwZTtcbiAgICAgICAgZmlsZVJlYWRlci5vbmxvYWQgPSAoZXZlbnQpID0+IHtcbiAgICAgICAgICAgIGlmICh0aGlzLmZpbGUgJiYgZXZlbnQ/LnRhcmdldCl7XG4gICAgICAgICAgICAgICAgdGhpcy5maWxlLnNyYz1ldmVudC50YXJnZXRbJ3Jlc3VsdCddO1xuICAgICAgICAgICAgICAgIHRoaXMudXBsb2FkRmlsZSh0aGlzLmZpbGUpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgICBmaWxlUmVhZGVyLnJlYWRBc0FycmF5QnVmZmVyKHRoaXMucmVjb3JkZWRCbG9iKTtcbiAgICAgICAgdGhpcy5maWxlLnNpemU9dGhpcy5yZWNvcmRlZEJsb2Iuc2l6ZTtcbiAgICAgICAgdGhpcy56b25lLnJ1bigoKT0+e1xuICAgICAgICAgICAgdGhpcy5oYXNSZWNvcmQ9dHJ1ZTtcbiAgICAgICAgfSlcbiAgICAgICAgLy9jb25zb2xlLmxvZyh0aGlzLnJlY29yZGVkQmxvYik7XG4gICAgICAgIHJlY29yZFJUQy5nZXREYXRhVVJMKGRhdGFVUkw9PntcbiAgICAgICAgICAgIC8vY29uc29sZS5sb2coYXVkaW9WaWRlb1dlYk1VUkwsIGRhdGFVUkwsIGF1ZGlvLnNyYyk7XG4gICAgICAgICAgICB0aGlzLndhdmVzdXJmZXIgPSBXYXZlU3VyZmVyLmNyZWF0ZSh7XG4gICAgICAgICAgICAgICAgICBjb250YWluZXI6IHRoaXMud2F2ZWZvcm1FbGVtZW50Lm5hdGl2ZUVsZW1lbnQsXG4gICAgICAgICAgICAgICAgICBoZWlnaHQ6IDE5MCxcbiAgICAgICAgICAgICAgICAgIHdhdmVDb2xvcjogJyNkNGQ5ZGQnLFxuICAgICAgICAgICAgICAgICAgcHJvZ3Jlc3NDb2xvcjogJyM1NTUnLFxuICAgICAgICAgICAgICAgICAgbm9ybWFsaXplOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgYmFySGVpZ2h0OiA4LFxuICAgICAgICAgICAgICAgICAgY3Vyc29yV2lkdGg6IDEsXG4gICAgICAgICAgICAgICAgICBjdXJzb3JDb2xvcjogJyNkOWQ5ZDknXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHRoaXMud2F2ZXN1cmZlci5sb2FkQmxvYih0aGlzLnJlY29yZGVkQmxvYik7XG5cbiAgICAgICAgICAgIHRoaXMud2F2ZXN1cmZlci5vbigncmVhZHknLCAoKT0+e1xuICAgICAgICAgICAgICAgIC8vY29uc29sZS5sb2codGhpcy53YXZlc3VyZmVyLmdldER1cmF0aW9uKCkpO1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLmZpbGUpe1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmZpbGUuZHVyYXRpb249dGhpcy53YXZlc3VyZmVyLmdldER1cmF0aW9uKCk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZHVyYXRpb249dGhpcy5maWxlLmR1cmF0aW9uKjEwMDA7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMud2F2ZXN1cmZlclBsYXk9ZmFsc2U7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB0aGlzLndhdmVzdXJmZXIub24oJ3BsYXknLCAoKT0+e1xuICAgICAgICAgICAgICAgIHRoaXMuem9uZS5ydW4oKCk9PntcbiAgICAgICAgICAgICAgICAgICAgdGhpcy53YXZlc3VyZmVyUGxheT10cnVlO1xuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHRoaXMud2F2ZXN1cmZlci5vbigncGF1c2UnLCAoKT0+e1xuICAgICAgICAgICAgICAgIHRoaXMuem9uZS5ydW4oKCk9PntcbiAgICAgICAgICAgICAgICAgICAgdGhpcy53YXZlc3VyZmVyUGxheT1mYWxzZTtcbiAgICAgICAgICAgICAgICB9KVxuXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgcmV0dXJuIHRoaXMuZmlsZTtcbiAgICB9XG5cbiAgICB1cGxvYWRGaWxlKGZpbGU6U3VydmV5RmlsZSl7XG4gICAgICAgIGZpbGUudXBsb2FkaW5nPXRydWU7XG4gICAgICAgIGZpbGUucHJvZ3Jlc3NTdWJqZWN0ID0gbmV3IE9ic2VydmFibGUob2JzZXJ2ZXIgPT4ge1xuICAgICAgICAgICAgZmlsZS5wcm9ncmVzc09ic2VydmVyID0gb2JzZXJ2ZXJcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuaXRlbS5idXN5PXRydWU7XG4gICAgICAgIGZpbGUucHJvZ3Jlc3NTdWJqZWN0LnN1YnNjcmliZShcbiAgICAgICAgICAgIGRhdGEgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuem9uZS5ydW4oKCk9PntcbiAgICAgICAgICAgICAgICAgICAgZmlsZS5wcm9ncmVzc1ZhbHVlPWRhdGE7XG4gICAgICAgICAgICAgICAgICAgIC8vY29uc29sZS5sb2coZmlsZSk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChmaWxlLnVybCl7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBvYmo9bmV3IFN1cnZleUZpbGU7XG4gICAgICAgICAgICAgICAgICAgICAgICBvYmoudXJsPWZpbGUudXJsO1xuICAgICAgICAgICAgICAgICAgICAgICAgb2JqLm5hbWU9ZmlsZS5uYW1lO1xuICAgICAgICAgICAgICAgICAgICAgICAgb2JqLm1pbWU9ZmlsZS5taW1lO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5pdGVtLnZhbHVlPW9iajtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKHRoaXMuaXRlbSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLml0ZW0uYnVzeT1mYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY2hhbmdlcy5lbWl0KHRoaXMuaXRlbSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGVycj0+e1xuICAgICAgICAgICAgICAgIHRoaXMuaXRlbS5idXN5PWZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgICAgICB0aGlzLnNlcnZpY2Uub25GaWxlc1NlbGVjdGVkLm5leHQoW2ZpbGVdKTtcbiAgICAgICAgdGhpcy5maWxlPWZpbGU7XG4gICAgfVxuXG4gICAgcGxheSgpe1xuICAgICAgICBpZiAodGhpcy53YXZlc3VyZmVyKXtcbiAgICAgICAgICAgIHRoaXMud2F2ZXN1cmZlci5wbGF5KCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcGF1c2UoKXtcbiAgICAgICAgaWYgKHRoaXMud2F2ZXN1cmZlcil7XG4gICAgICAgICAgICB0aGlzLndhdmVzdXJmZXIucGF1c2UoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHRvZ2dsZVBsYXkoKXtcbiAgICAgICAgaWYgKHRoaXMud2F2ZXN1cmZlcil7XG4gICAgICAgICAgICBpZiAodGhpcy53YXZlc3VyZmVyUGxheSkge1xuICAgICAgICAgICAgICAgIHRoaXMucGF1c2UoKTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbnRyb2xJY29uID0gJ3BsYXktY2lyY2xlLXRoaW4nO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLnBsYXkoKTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbnRyb2xJY29uID0gJ3BhdXNlLWNpcmNsZS10aGluJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHRvZ2dsZVJlY29yZGluZygpe1xuICAgICAgICAvL2NvbnNvbGUubG9nKCdDTElDS0VEJyk7XG4gICAgICAgIGlmICh0aGlzLnJlY29yZGluZ0lucHJvZ3Jlc3Mpe1xuICAgICAgICAgICAgdGhpcy5zdG9wUmVjb3JkaW5nKCk7XG4gICAgICAgICAgICB0aGlzLmNvbnRyb2xJY29uID0gJ3BsYXktY2lyY2xlLXRoaW4nO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5zdGFydFJlY29yZGluZygpO1xuICAgICAgICAgICAgdGhpcy5jb250cm9sSWNvbiA9ICdyZWNvcmQtcm91bmQnO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgY2xlYXJCbG9iKCl7XG4gICAgICAgIHRoaXMuZmlsZT11bmRlZmluZWQ7XG4gICAgICAgIHRoaXMucmVjb3JkZWRCbG9iPW51bGw7XG4gICAgICAgIHRoaXMuaGFzUmVjb3JkPWZhbHNlO1xuICAgICAgICBpZiAodGhpcy53YXZlc3VyZmVyKXtcbiAgICAgICAgICAgIHRoaXMud2F2ZXN1cmZlci5kZXN0cm95KClcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5yZWNvcmRSVEMpe1xuICAgICAgICAgICAgLy90aGlzLnJlY29yZFJUQy5kZXN0cm95KClcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmNvbnRyb2xJY29uID0gJ3JlY29yZC1jaXJjbGUnO1xuICAgICAgICB0aGlzLml0ZW0udmFsdWU9dW5kZWZpbmVkO1xuICAgICAgICB0aGlzLmNoYW5nZXMuZW1pdCh0aGlzLml0ZW0pO1xuICAgIH1cblxuICAgIGRvd25sb2FkKCkge1xuICAgICAgICB0aGlzLnJlY29yZFJUQy5zYXZlKCdhdWRpby53ZWJtJyk7XG4gICAgfVxuXG4gICAgb25TYXZlKCl7XG4gICAgICAgIC8vdGhpcy5zdG9wUmVjb3JkaW5nKCk7XG4gICAgfVxuXG4gICAgbmdPbkRlc3Ryb3koKSB7XG4gICAgICAgIGlmICh0aGlzLndhdmVzdXJmZXIpe1xuICAgICAgICAgICAgdGhpcy53YXZlc3VyZmVyLmRlc3Ryb3koKVxuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLnJlY29yZFJUQyAmJiB0aGlzLnJlY29yZFJUQy5zdGF0ZT09PSdyZWNvcmRpbmcnKXtcbiAgICAgICAgICAgIHRoaXMucmVjb3JkUlRDLnN0b3BSZWNvcmRpbmcoKTtcbiAgICAgICAgICAgIHRoaXMucmVjb3JkUlRDLmNsZWFyUmVjb3JkZWREYXRhKCk7XG4gICAgICAgICAgICAvL3RoaXMuc3RvcFJlY29yZGluZygpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLnJlY29yZFJUQyl7XG4gICAgICAgICAgICB0aGlzLnJlY29yZFJUQy5kZXN0cm95KClcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5zdHJlYW0pe1xuICAgICAgICAgICAgdGhpcy5zdHJlYW0uZ2V0VHJhY2tzKCkuZm9yRWFjaCh0cmFjayA9PiB0cmFjay5zdG9wKCkpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLmR1cmF0aW9uSW50ZXJ2YWwpe1xuICAgICAgICAgICAgdGhpcy5kdXJhdGlvbj0wO1xuICAgICAgICAgICAgY2xlYXJJbnRlcnZhbCh0aGlzLmR1cmF0aW9uSW50ZXJ2YWwpO1xuICAgICAgICB9XG4gICAgfVxufVxuIiwiPGRpdiBjbGFzcz1cImZvcm0tZ3JvdXBcIj5cbiAgICA8ZGl2IGNsYXNzPVwiY29sLXhzLTEyIHZvaWNlLXJlY29yZGVyXCI+XG4gICAgICAgIDxkaXYgKm5nSWY9XCJpdGVtLmxhYmVsXCIgY2xhc3M9XCJ2b2ljZS1yZWNvcmRlci10aXRsZSBsYWJlbFwiIFtuZ0NsYXNzXT1cInsnaGFzLWVycm9yJzppdGVtLmVycm9ycyAmJiBpdGVtLmVycm9ycy5sZW5ndGh9XCI+e3tpdGVtLmxhYmVsfX0gPHNwYW4gKm5nSWY9XCJpdGVtLnJlcXVpcmVkXCIgY2xhc3M9XCJyZXF1aXJlZC1hc3Rlcml4XCI+Kjwvc3Bhbj48L2Rpdj5cbiAgICAgICAgPGRpdiBjbGFzcz1cImF1ZGlvLXJlY29yZGVyXCI+XG4gICAgICAgICAgICA8YXVkaW8gI2F1ZGlvIGNsYXNzPVwiYXVkaW9cIiBzdHlsZT1cImRpc3BsYXk6bm9uZTtcIiBbc3JjXT1cImZpbGU/LnVybFwiPjwvYXVkaW8+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwibWFpbi1zY3JlZW5cIj5cbiAgICAgICAgICAgICAgICA8ZGl2ICN3YXZlZm9ybSBjbGFzcz1cIndhdmVmb3JtXCIgW25nQ2xhc3NdPVwie2hpZGRlbjpyZWNvcmRpbmdJbnByb2dyZXNzIHx8ICFoYXNSZWNvcmR9XCI+PC9kaXY+XG4gICAgICAgICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIiFyZWNvcmRpbmdJbnByb2dyZXNzICYmIGhhc1JlY29yZDsgZWxzZSBzdGFydFNjcmVlblwiPlxuICAgICAgICAgICAgICAgICAgICA8YnV0dG9uIG1hdC1idXR0b24gY2xhc3M9XCJtYWluLXNjcmVlbi1idXR0b25cIiAoY2xpY2spPVwidG9nZ2xlUGxheSgpXCIgdHlwZT1cImJ1dHRvblwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgPG1hdC1pY29uIGNsYXNzPVwibWFpbi1zY3JlZW4taWNvbi1iaWdcIj57e3RoaXMud2F2ZXN1cmZlclBsYXkgPyAncGF1c2VfY2lyY2xlX291dGxpbmUnIDogJ3BsYXlfY2lyY2xlX291dGxpbmUnfX08L21hdC1pY29uPlxuICAgICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cblxuICAgICAgICAgICAgICAgICAgICA8YSBtYXQtYnV0dG9uIGNsYXNzPVwibWFpbi1zY3JlZW4tYnV0dG9uLWFzaWRlIGRvd25sb2FkLWJ0blwiICpuZ0lmPVwiaXRlbS52YWx1ZSAmJiBpdGVtLnZhbHVlLnVybFwiIFtocmVmXT1cIml0ZW0udmFsdWUudXJsXCIgdGFyZ2V0PVwiX2JsYW5rXCIgdGl0bGU9XCJEb3dsb2FkIFJlY29yZFwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgPG1hdC1pY29uIGNsYXNzPVwibWFpbi1zY3JlZW4taWNvbi1zbWFsbFwiPmRvd25sb2FkPC9tYXQtaWNvbj5cbiAgICAgICAgICAgICAgICAgICAgPC9hPlxuICAgICAgICAgICAgICAgICAgICA8YnV0dG9uIG1hdC1idXR0b24gY2xhc3M9XCJtYWluLXNjcmVlbi1idXR0b24tYXNpZGUgZGVsZXRlLWJ0blwiIHRpdGxlPVwiRGVsZXRlIFJlY29yZFwiIChjbGljayk9XCJjbGVhckJsb2IoKVwiICpuZ0lmPVwiZWRpdGFibGVcIiB0eXBlPVwiYnV0dG9uXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICA8bWF0LWljb24gY2xhc3M9XCJtYWluLXNjcmVlbi1pY29uLXNtYWxsXCI+Y2FuY2VsPC9tYXQtaWNvbj5cbiAgICAgICAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICAgICAgICAgIDxtYXQtcHJvZ3Jlc3MtYmFyIG1vZGU9XCJkZXRlcm1pbmF0ZVwiICAqbmdJZj1cImZpbGUgJiYgZmlsZS51cGxvYWRpbmdcIiBbdmFsdWVdPVwiZmlsZS5wcm9ncmVzc1ZhbHVlXCI+PC9tYXQtcHJvZ3Jlc3MtYmFyPlxuICAgICAgICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgICAgICAgICA8L2Rpdj5cblxuICAgICAgICAgICAgPG5nLXRlbXBsYXRlICNzdGFydFNjcmVlbj5cbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwic3RhcnQtc2NyZWVuXCI+XG4gICAgICAgICAgICAgICAgICAgIDxidXR0b24gbWF0LWJ1dHRvbiBjbGFzcz1cInN0YXJ0LXNjcmVlbi1idXR0b25cIiAoY2xpY2spPVwidG9nZ2xlUmVjb3JkaW5nKClcIiBbZGlzYWJsZWRdPVwiIWVkaXRhYmxlXCIgdHlwZT1cImJ1dHRvblwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgPG1hdC1pY29uIGNsYXNzPVwic3RhcnQtc2NyZWVuLWljb25cIiA+e3tyZWNvcmRpbmdJbnByb2dyZXNzID8gJ21pYyc6J21pY19ub25lJ319PC9tYXQtaWNvbj5cbiAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgKm5nSWY9XCIhcmVjb3JkaW5nSW5wcm9ncmVzcyAmJiAhaGFzUmVjb3JkXCIgY2xhc3M9XCJzdGFydC1zY3JlZW4tdGV4dFwiPlRhcCB0byByZWNvcmQgYXVkaW88L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgKm5nSWY9XCJyZWNvcmRpbmdJbnByb2dyZXNzXCIgY2xhc3M9XCJzdGFydC1zY3JlZW4tdGV4dFwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHt7ZHVyYXRpb24gfCBkdXJhdGlvbjonc2Vjb25kcyd9fVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8ge3t0aW1lTGltaXQgLSBkdXJhdGlvbiB8IGR1cmF0aW9uOidzZWNvbmRzJ319XG4gICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgICA8L2Rpdj5cbiAgICAgICAgPG1hdC1lcnJvciAqbmdJZj1cIml0ZW0uZXJyb3JzICYmIGl0ZW0uZXJyb3JzLmxlbmd0aFwiPlxuICAgICAgICAgICAgPGRpdiAqbmdGb3I9XCJsZXQgZXJyb3Igb2YgaXRlbS5lcnJvcnNcIj57e2Vycm9yLm1lc3NhZ2V9fTwvZGl2PlxuICAgICAgICA8L21hdC1lcnJvcj5cbiAgICAgICAgPG1hdC1oaW50IGFsaWduPVwic3RhcnRcIiAqbmdJZj1cIiEoaXRlbS5lcnJvcnMgJiYgaXRlbS5lcnJvcnMubGVuZ3RoKVwiPjxzdHJvbmc+e3tpdGVtLmhpbnR9fTwvc3Ryb25nPiA8L21hdC1oaW50PlxuICAgIDwvZGl2PlxuPC9kaXY+XG4iXX0=