biometry-angular-components
Version:
Angular UI component library for capturing biometric data
278 lines • 55.9 kB
JavaScript
import { Component, EventEmitter, Input, Output, ChangeDetectionStrategy, ViewChild } from '@angular/core';
import { generateRandomDigits, numbersToPhrase } from '../../utils/numbers';
import { CommonModule } from '@angular/common';
import * as i0 from "@angular/core";
import * as i1 from "../../services/permissions.service";
import * as i2 from "../../services/recorder.service";
import * as i3 from "@angular/common";
const RECORD_SECONDS = 10;
const PREP_SECONDS = 3;
export class VoiceRecorderComponent {
perms;
recorder;
ngZone;
cdr;
rectWidth = 360;
rectHeight;
noShadow = false;
className;
style;
capture = new EventEmitter();
audioRef;
permission = false;
isLoading = true;
stream;
state = 'preparation';
prepTimer = PREP_SECONDS;
recordTimer = RECORD_SECONDS;
// digits & phrase
digits = generateRandomDigits(10);
maskedDigits = Array(10).fill('*');
// visualization
audioLevel = 0;
waveformData = [];
// blobs/urls
audioBlob = null;
audioUrl = null;
isConfirming = false;
// timers / raf
countdownTimeout;
recordInterval;
rafId;
// audio context nodes
audioCtx;
analyser;
sourceNode;
constructor(perms, recorder, ngZone, cdr) {
this.perms = perms;
this.recorder = recorder;
this.ngZone = ngZone;
this.cdr = cdr;
}
async ngOnInit() {
try {
const status = await (navigator.permissions?.query?.({ name: 'microphone' }) ?? Promise.reject());
this.ngZone.run(() => {
this.permission = status.state === 'granted';
this.isLoading = false;
this.cdr.detectChanges();
});
if (this.permission)
await this.requestPermissions();
status.onchange = async () => {
this.ngZone.run(async () => {
this.permission = status.state === 'granted';
if (this.permission && !this.stream)
await this.requestPermissions();
this.cdr.detectChanges();
});
};
}
catch {
this.ngZone.run(() => {
this.permission = false;
this.isLoading = false;
this.cdr.detectChanges();
});
}
}
ngOnDestroy() {
this.clearAllTimers();
this.teardownVisualizer();
this.perms.stopStream(this.stream);
if (this.audioUrl)
URL.revokeObjectURL(this.audioUrl);
}
async requestPermissions() {
this.ngZone.run(() => {
this.isLoading = true;
this.cdr.detectChanges();
});
const { stream, granted } = await this.perms.requestMicrophone();
this.ngZone.run(() => {
this.permission = !!granted;
this.isLoading = false;
if (granted && stream)
this.stream = stream;
this.cdr.detectChanges();
});
}
startRecording() {
this.digits = generateRandomDigits(10);
this.recordTimer = RECORD_SECONDS;
this.prepTimer = PREP_SECONDS;
this.state = 'countdown';
this.tickCountdown();
}
tickCountdown() {
if (this.prepTimer > 0) {
this.countdownTimeout = setTimeout(() => {
this.ngZone.run(() => {
this.prepTimer -= 1;
this.cdr.detectChanges();
});
this.tickCountdown();
}, 1000);
return;
}
this.ngZone.run(() => {
this.state = 'recording';
this.prepTimer = PREP_SECONDS;
this.cdr.detectChanges();
});
this.beginRecording();
}
async beginRecording() {
if (!this.stream) {
const { stream, granted } = await this.perms.requestMicrophone();
if (!granted || !stream)
return;
this.stream = stream;
}
this.recorder.start(this.stream, 'audio/webm');
this.setupVisualizer();
this.recordInterval = setInterval(async () => {
this.ngZone.run(async () => {
this.recordTimer -= 1;
this.cdr.detectChanges();
if (this.recordTimer <= 0) {
clearInterval(this.recordInterval);
await this.finishRecording();
}
});
}, 1000);
}
async finishRecording() {
this.state = 'processing';
const blob = await this.recorder.stop();
// stop devices visualizer
this.teardownVisualizer();
this.perms.stopStream(this.stream);
this.stream = undefined;
// produce url and move to confirm
const finalBlob = blob && blob.size > 0 ? blob : new Blob(['dummy audio data'], { type: 'audio/webm' });
this.audioBlob = finalBlob;
this.audioUrl = URL.createObjectURL(finalBlob);
this.state = 'result';
this.isConfirming = true;
}
async handleCancel() {
this.clearAllTimers();
try {
await this.recorder.stop();
}
catch { }
this.teardownVisualizer();
this.perms.stopStream(this.stream);
this.stream = undefined;
// reset UI
this.state = 'preparation';
this.isConfirming = false;
if (this.audioUrl)
URL.revokeObjectURL(this.audioUrl);
this.audioUrl = null;
this.audioBlob = null;
this.audioLevel = 0;
this.waveformData = [];
this.requestPermissions();
}
handleConfirm() {
if (!this.audioBlob)
return;
const file = new File([this.audioBlob], 'voice-recording.webm', { type: this.audioBlob.type });
this.capture.emit({ file, phrase: numbersToPhrase(this.digits) });
if (this.audioUrl)
URL.revokeObjectURL(this.audioUrl);
this.audioUrl = null;
this.audioBlob = null;
this.isConfirming = false;
this.state = 'preparation';
}
handleDecline() {
if (this.audioUrl)
URL.revokeObjectURL(this.audioUrl);
this.audioUrl = null;
this.audioBlob = null;
this.isConfirming = false;
this.state = 'preparation';
this.requestPermissions();
}
// ----- Visualization -----
setupVisualizer() {
if (!this.stream)
return;
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
this.analyser = this.audioCtx.createAnalyser();
this.sourceNode = this.audioCtx.createMediaStreamSource(this.stream);
this.analyser.fftSize = 256;
this.analyser.smoothingTimeConstant = 0.8;
this.sourceNode.connect(this.analyser);
const data = new Uint8Array(this.analyser.frequencyBinCount);
const loop = () => {
if (!this.analyser || this.state !== 'recording')
return;
this.analyser.getByteFrequencyData(data);
const avg = data.reduce((s, v) => s + v, 0) / data.length;
this.ngZone.run(() => {
this.audioLevel = avg / 255;
this.waveformData = Array.from(data).slice(0, 64);
this.cdr.detectChanges();
});
this.rafId = requestAnimationFrame(loop);
};
loop();
}
teardownVisualizer() {
if (this.rafId)
cancelAnimationFrame(this.rafId);
this.rafId = undefined;
try {
this.sourceNode?.disconnect();
}
catch { }
try {
this.analyser?.disconnect();
}
catch { }
try {
this.audioCtx?.close();
}
catch { }
this.sourceNode = undefined;
this.analyser = undefined;
this.audioCtx = undefined;
}
clearAllTimers() {
if (this.countdownTimeout)
clearTimeout(this.countdownTimeout);
this.countdownTimeout = undefined;
if (this.recordInterval)
clearInterval(this.recordInterval);
this.recordInterval = undefined;
}
getVolumeHeight(v) {
return Math.max(2, (v / 255) * 100);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: VoiceRecorderComponent, deps: [{ token: i1.PermissionsService }, { token: i2.RecorderService }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: VoiceRecorderComponent, isStandalone: true, selector: "bio-voice-recorder", inputs: { rectWidth: "rectWidth", rectHeight: "rectHeight", noShadow: "noShadow", className: "className", style: "style" }, outputs: { capture: "capture" }, viewQueries: [{ propertyName: "audioRef", first: true, predicate: ["audioRef"], descendants: true }], ngImport: i0, template: "<div class=\"vr-wrapper\" [class.no-shadow]=\"noShadow\" [style.width.px]=\"rectWidth\" [style.height.px]=\"rectHeight || null\"\n [ngStyle]=\"style\">\n <!-- LOADING -->\n <div *ngIf=\"isLoading\" class=\"vr-center\">\n <div class=\"vr-spinner\"></div>\n <h3>Initializing Microphone...</h3>\n <p>Please wait while we set up your microphone.</p>\n </div>\n\n <!-- PERMISSION -->\n <div *ngIf=\"!isLoading && !permission\" class=\"vr-center\">\n <h3>Microphone Permission Required</h3>\n <p>Please allow microphone access to use this component.</p>\n <button type=\"button\" class=\"vr-btn\" (click)=\"requestPermissions()\">Grant Permissions</button>\n </div>\n\n <!-- MAIN FLOW -->\n <ng-container *ngIf=\"!isLoading && permission\">\n <!-- Not in result/processing state -->\n <ng-container *ngIf=\"state !== 'result' && state !== 'processing' && !isConfirming\">\n <div class=\"vr-audio-area\">\n <div class=\"vr-audio-dim\"></div>\n\n <div class=\"vr-overlay\">\n <!-- PREPARATION -->\n <div *ngIf=\"state === 'preparation'\" class=\"vr-center-col\">\n <div class=\"vr-guidance\">Click Start to begin voice recording</div>\n </div>\n\n <!-- COUNTDOWN -->\n <div *ngIf=\"state === 'countdown'\" class=\"vr-center-col\">\n <div class=\"vr-guidance\">Get ready to speak clearly</div>\n <div class=\"vr-prep-timer\">{{ prepTimer }}</div>\n </div>\n\n <!-- RECORDING -->\n <div *ngIf=\"state === 'recording'\" class=\"vr-center-col\">\n <div class=\"vr-waveform\">\n <div class=\"vr-bars\">\n <div *ngFor=\"let v of waveformData; let i = index\" class=\"vr-bar\"\n [style.height.%]=\"this.getVolumeHeight(v)\" [class.active]=\"state === 'recording'\">\n </div>\n </div>\n </div>\n <div class=\"vr-timer\">{{ recordTimer }}</div>\n <div class=\"vr-guidance\">Speak clearly into your microphone</div>\n </div>\n </div>\n </div>\n\n <!-- DIGIT ROW -->\n <div class=\"vr-number-row\">\n <div class=\"vr-number-box\" *ngFor=\"let n of (state === 'recording' ? digits : maskedDigits)\">\n {{ n }}\n </div>\n </div>\n\n <!-- ACTIONS -->\n <div class=\"vr-actions\">\n <button *ngIf=\"state === 'preparation'\" class=\"vr-btn\" type=\"button\" (click)=\"startRecording()\">Start\n recording</button>\n <button *ngIf=\"state === 'recording'\" class=\"vr-btn danger\" type=\"button\"\n (click)=\"handleCancel()\">Cancel</button>\n </div>\n </ng-container>\n\n <!-- PROCESSING -->\n <ng-container *ngIf=\"state === 'processing'\">\n <div class=\"vr-preview\">\n <div class=\"vr-center-col\" style=\"color:#fff;\">\n <div class=\"vr-spinner light\"></div>\n <div class=\"vr-processing-title\">Processing audio...</div>\n <div class=\"vr-processing-sub\">Please wait while we prepare your recording</div>\n </div>\n </div>\n </ng-container>\n\n <!-- RESULT + CONFIRM -->\n <ng-container *ngIf=\"state === 'result' && isConfirming\">\n <div class=\"vr-result\">\n <audio #audioRef *ngIf=\"audioUrl\" [src]=\"audioUrl\" controls\n style=\"width:100%;max-width:320px;margin-bottom:16px;\"></audio>\n <div class=\"vr-confirm-text\">Do you want to use this recording?</div>\n <div class=\"vr-confirm-btns\">\n <button type=\"button\" class=\"vr-icon-btn\" aria-label=\"Retake\" (click)=\"handleDecline()\">\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 36 36\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"18\" cy=\"18\" r=\"18\" fill=\"#fff\" fill-opacity=\"0.15\" />\n <path d=\"M12 12L24 24\" stroke=\"#d32f2f\" stroke-width=\"3\" stroke-linecap=\"round\" />\n <path d=\"M24 12L12 24\" stroke=\"#d32f2f\" stroke-width=\"3\" stroke-linecap=\"round\" />\n </svg>\n </button>\n <button type=\"button\" class=\"vr-icon-btn\" aria-label=\"Confirm\" (click)=\"handleConfirm()\">\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 36 36\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"18\" cy=\"18\" r=\"18\" fill=\"#fff\" fill-opacity=\"0.15\" />\n <path d=\"M10 19L16 25L26 13\" stroke=\"#388e3c\" stroke-width=\"3\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n </div>\n </ng-container>\n </ng-container>\n</div>", styles: [".vr-wrapper{background:#fff;border-radius:10px;box-shadow:0 4px 24px #0000001f;padding:20px;display:flex;flex-direction:column;align-items:stretch;justify-content:center;margin:0 auto}.vr-wrapper.no-shadow{box-shadow:none}.vr-center{text-align:center;padding:20px}.vr-center-col{display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;padding:20px}.vr-spinner{width:48px;height:48px;border:4px solid rgba(0,0,0,.1);border-top:4px solid #1976d2;border-radius:50%;animation:vr-spin 1s linear infinite;margin:0 auto 16px}.vr-spinner.light{border:4px solid rgba(255,255,255,.3);border-top-color:#fff}@keyframes vr-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.vr-btn{background:#1976d2;color:#fff;border:none;padding:12px 32px;font-size:16px;border-radius:8px;cursor:pointer;box-shadow:0 2px 8px #0000001a;transition:background .2s ease}.vr-btn:hover{background:#125aa2}.vr-btn.danger{background:#d32f2f}.vr-btn.danger:hover{background:#a52121}.vr-audio-area{position:relative;width:100%;height:200px;margin-bottom:20px;display:flex;align-items:center;justify-content:center;background:#111;border-radius:8px;overflow:hidden}.vr-audio-dim{position:absolute;inset:0;background:#0006;z-index:1;border-radius:8px}.vr-overlay{position:relative;z-index:2;width:100%}.vr-waveform{width:100%;height:120px;display:flex;align-items:center;justify-content:center}.vr-bars{display:flex;align-items:flex-end;height:120px;gap:2px}.vr-bar{width:4px;background:#666;margin:0 1px;border-radius:2px;transition:height .1s ease;opacity:.6}.vr-bar.active{background:#4caf50;opacity:1}.vr-guidance{color:#fff;font-size:14px;font-weight:400;margin-top:12px;letter-spacing:.2px;text-shadow:0 1px 4px rgba(0,0,0,.25);-webkit-user-select:none;user-select:none;text-align:center}.vr-prep-timer{color:#fff;font-size:24px;font-weight:700;margin-top:8px;text-shadow:0 1px 4px rgba(0,0,0,.25)}.vr-timer{color:#fff;font-size:16px;font-weight:600;margin-top:8px;text-shadow:0 1px 4px rgba(0,0,0,.25);text-align:center}.vr-number-row{display:flex;justify-content:center;gap:6px;margin:16px 0 24px}.vr-number-box{min-width:24px;min-height:32px;background:#f5f5f5;border:2px solid #333;border-radius:4px;font-size:16px;font-weight:600;color:#333;display:flex;align-items:center;justify-content:center;box-shadow:0 1px 4px #00000014;transition:background .3s}.vr-actions{display:flex;justify-content:center;gap:12px}.vr-preview{width:100%;height:400px;background:#111;color:#fff;border-radius:8px;display:flex;align-items:center;justify-content:center}.vr-processing-title{font-size:18px;font-weight:500;margin-top:12px}.vr-processing-sub{font-size:14px;opacity:.85}.vr-result{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:10px;width:100%}.vr-confirm-text{text-align:center;margin-top:16px;color:#111;font-size:17px;font-weight:500;text-shadow:0 1px 4px rgba(255,255,255,.12)}.vr-confirm-btns{display:flex;justify-content:center;gap:32px;margin-top:12px;width:100%}.vr-icon-btn{background:none;border:none;cursor:pointer;padding:8px;border-radius:50%;transition:background .2s}.vr-icon-btn:hover{background:#0000000f}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: VoiceRecorderComponent, decorators: [{
type: Component,
args: [{ selector: 'bio-voice-recorder', imports: [CommonModule], standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"vr-wrapper\" [class.no-shadow]=\"noShadow\" [style.width.px]=\"rectWidth\" [style.height.px]=\"rectHeight || null\"\n [ngStyle]=\"style\">\n <!-- LOADING -->\n <div *ngIf=\"isLoading\" class=\"vr-center\">\n <div class=\"vr-spinner\"></div>\n <h3>Initializing Microphone...</h3>\n <p>Please wait while we set up your microphone.</p>\n </div>\n\n <!-- PERMISSION -->\n <div *ngIf=\"!isLoading && !permission\" class=\"vr-center\">\n <h3>Microphone Permission Required</h3>\n <p>Please allow microphone access to use this component.</p>\n <button type=\"button\" class=\"vr-btn\" (click)=\"requestPermissions()\">Grant Permissions</button>\n </div>\n\n <!-- MAIN FLOW -->\n <ng-container *ngIf=\"!isLoading && permission\">\n <!-- Not in result/processing state -->\n <ng-container *ngIf=\"state !== 'result' && state !== 'processing' && !isConfirming\">\n <div class=\"vr-audio-area\">\n <div class=\"vr-audio-dim\"></div>\n\n <div class=\"vr-overlay\">\n <!-- PREPARATION -->\n <div *ngIf=\"state === 'preparation'\" class=\"vr-center-col\">\n <div class=\"vr-guidance\">Click Start to begin voice recording</div>\n </div>\n\n <!-- COUNTDOWN -->\n <div *ngIf=\"state === 'countdown'\" class=\"vr-center-col\">\n <div class=\"vr-guidance\">Get ready to speak clearly</div>\n <div class=\"vr-prep-timer\">{{ prepTimer }}</div>\n </div>\n\n <!-- RECORDING -->\n <div *ngIf=\"state === 'recording'\" class=\"vr-center-col\">\n <div class=\"vr-waveform\">\n <div class=\"vr-bars\">\n <div *ngFor=\"let v of waveformData; let i = index\" class=\"vr-bar\"\n [style.height.%]=\"this.getVolumeHeight(v)\" [class.active]=\"state === 'recording'\">\n </div>\n </div>\n </div>\n <div class=\"vr-timer\">{{ recordTimer }}</div>\n <div class=\"vr-guidance\">Speak clearly into your microphone</div>\n </div>\n </div>\n </div>\n\n <!-- DIGIT ROW -->\n <div class=\"vr-number-row\">\n <div class=\"vr-number-box\" *ngFor=\"let n of (state === 'recording' ? digits : maskedDigits)\">\n {{ n }}\n </div>\n </div>\n\n <!-- ACTIONS -->\n <div class=\"vr-actions\">\n <button *ngIf=\"state === 'preparation'\" class=\"vr-btn\" type=\"button\" (click)=\"startRecording()\">Start\n recording</button>\n <button *ngIf=\"state === 'recording'\" class=\"vr-btn danger\" type=\"button\"\n (click)=\"handleCancel()\">Cancel</button>\n </div>\n </ng-container>\n\n <!-- PROCESSING -->\n <ng-container *ngIf=\"state === 'processing'\">\n <div class=\"vr-preview\">\n <div class=\"vr-center-col\" style=\"color:#fff;\">\n <div class=\"vr-spinner light\"></div>\n <div class=\"vr-processing-title\">Processing audio...</div>\n <div class=\"vr-processing-sub\">Please wait while we prepare your recording</div>\n </div>\n </div>\n </ng-container>\n\n <!-- RESULT + CONFIRM -->\n <ng-container *ngIf=\"state === 'result' && isConfirming\">\n <div class=\"vr-result\">\n <audio #audioRef *ngIf=\"audioUrl\" [src]=\"audioUrl\" controls\n style=\"width:100%;max-width:320px;margin-bottom:16px;\"></audio>\n <div class=\"vr-confirm-text\">Do you want to use this recording?</div>\n <div class=\"vr-confirm-btns\">\n <button type=\"button\" class=\"vr-icon-btn\" aria-label=\"Retake\" (click)=\"handleDecline()\">\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 36 36\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"18\" cy=\"18\" r=\"18\" fill=\"#fff\" fill-opacity=\"0.15\" />\n <path d=\"M12 12L24 24\" stroke=\"#d32f2f\" stroke-width=\"3\" stroke-linecap=\"round\" />\n <path d=\"M24 12L12 24\" stroke=\"#d32f2f\" stroke-width=\"3\" stroke-linecap=\"round\" />\n </svg>\n </button>\n <button type=\"button\" class=\"vr-icon-btn\" aria-label=\"Confirm\" (click)=\"handleConfirm()\">\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 36 36\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"18\" cy=\"18\" r=\"18\" fill=\"#fff\" fill-opacity=\"0.15\" />\n <path d=\"M10 19L16 25L26 13\" stroke=\"#388e3c\" stroke-width=\"3\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </button>\n </div>\n </div>\n </ng-container>\n </ng-container>\n</div>", styles: [".vr-wrapper{background:#fff;border-radius:10px;box-shadow:0 4px 24px #0000001f;padding:20px;display:flex;flex-direction:column;align-items:stretch;justify-content:center;margin:0 auto}.vr-wrapper.no-shadow{box-shadow:none}.vr-center{text-align:center;padding:20px}.vr-center-col{display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;padding:20px}.vr-spinner{width:48px;height:48px;border:4px solid rgba(0,0,0,.1);border-top:4px solid #1976d2;border-radius:50%;animation:vr-spin 1s linear infinite;margin:0 auto 16px}.vr-spinner.light{border:4px solid rgba(255,255,255,.3);border-top-color:#fff}@keyframes vr-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.vr-btn{background:#1976d2;color:#fff;border:none;padding:12px 32px;font-size:16px;border-radius:8px;cursor:pointer;box-shadow:0 2px 8px #0000001a;transition:background .2s ease}.vr-btn:hover{background:#125aa2}.vr-btn.danger{background:#d32f2f}.vr-btn.danger:hover{background:#a52121}.vr-audio-area{position:relative;width:100%;height:200px;margin-bottom:20px;display:flex;align-items:center;justify-content:center;background:#111;border-radius:8px;overflow:hidden}.vr-audio-dim{position:absolute;inset:0;background:#0006;z-index:1;border-radius:8px}.vr-overlay{position:relative;z-index:2;width:100%}.vr-waveform{width:100%;height:120px;display:flex;align-items:center;justify-content:center}.vr-bars{display:flex;align-items:flex-end;height:120px;gap:2px}.vr-bar{width:4px;background:#666;margin:0 1px;border-radius:2px;transition:height .1s ease;opacity:.6}.vr-bar.active{background:#4caf50;opacity:1}.vr-guidance{color:#fff;font-size:14px;font-weight:400;margin-top:12px;letter-spacing:.2px;text-shadow:0 1px 4px rgba(0,0,0,.25);-webkit-user-select:none;user-select:none;text-align:center}.vr-prep-timer{color:#fff;font-size:24px;font-weight:700;margin-top:8px;text-shadow:0 1px 4px rgba(0,0,0,.25)}.vr-timer{color:#fff;font-size:16px;font-weight:600;margin-top:8px;text-shadow:0 1px 4px rgba(0,0,0,.25);text-align:center}.vr-number-row{display:flex;justify-content:center;gap:6px;margin:16px 0 24px}.vr-number-box{min-width:24px;min-height:32px;background:#f5f5f5;border:2px solid #333;border-radius:4px;font-size:16px;font-weight:600;color:#333;display:flex;align-items:center;justify-content:center;box-shadow:0 1px 4px #00000014;transition:background .3s}.vr-actions{display:flex;justify-content:center;gap:12px}.vr-preview{width:100%;height:400px;background:#111;color:#fff;border-radius:8px;display:flex;align-items:center;justify-content:center}.vr-processing-title{font-size:18px;font-weight:500;margin-top:12px}.vr-processing-sub{font-size:14px;opacity:.85}.vr-result{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:10px;width:100%}.vr-confirm-text{text-align:center;margin-top:16px;color:#111;font-size:17px;font-weight:500;text-shadow:0 1px 4px rgba(255,255,255,.12)}.vr-confirm-btns{display:flex;justify-content:center;gap:32px;margin-top:12px;width:100%}.vr-icon-btn{background:none;border:none;cursor:pointer;padding:8px;border-radius:50%;transition:background .2s}.vr-icon-btn:hover{background:#0000000f}\n"] }]
}], ctorParameters: () => [{ type: i1.PermissionsService }, { type: i2.RecorderService }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }], propDecorators: { rectWidth: [{
type: Input
}], rectHeight: [{
type: Input
}], noShadow: [{
type: Input
}], className: [{
type: Input
}], style: [{
type: Input
}], capture: [{
type: Output
}], audioRef: [{
type: ViewChild,
args: ['audioRef', { static: false }]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidm9pY2UtcmVjb3JkZXIuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9jb21wb25lbnRzL3ZvaWNlLXJlY29yZGVyL3ZvaWNlLXJlY29yZGVyLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy92b2ljZS1yZWNvcmRlci92b2ljZS1yZWNvcmRlci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsU0FBUyxFQUNULFlBQVksRUFDWixLQUFLLEVBQ0wsTUFBTSxFQUdOLHVCQUF1QixFQUV2QixTQUFTLEVBR1YsTUFBTSxlQUFlLENBQUM7QUFHdkIsT0FBTyxFQUFFLG9CQUFvQixFQUFFLGVBQWUsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQzVFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQzs7Ozs7QUFJL0MsTUFBTSxjQUFjLEdBQUcsRUFBRSxDQUFDO0FBQzFCLE1BQU0sWUFBWSxHQUFHLENBQUMsQ0FBQztBQVV2QixNQUFNLE9BQU8sc0JBQXNCO0lBMkN2QjtJQUNBO0lBQ0E7SUFDQTtJQTdDRCxTQUFTLEdBQUcsR0FBRyxDQUFDO0lBQ2hCLFVBQVUsQ0FBVTtJQUNwQixRQUFRLEdBQUcsS0FBSyxDQUFDO0lBQ2pCLFNBQVMsQ0FBVTtJQUNuQixLQUFLLENBQXdCO0lBRTVCLE9BQU8sR0FBRyxJQUFJLFlBQVksRUFBa0MsQ0FBQztJQUU3QixRQUFRLENBQWdDO0lBRWxGLFVBQVUsR0FBRyxLQUFLLENBQUM7SUFDbkIsU0FBUyxHQUFHLElBQUksQ0FBQztJQUNqQixNQUFNLENBQWU7SUFFckIsS0FBSyxHQUFZLGFBQWEsQ0FBQztJQUMvQixTQUFTLEdBQUcsWUFBWSxDQUFDO0lBQ3pCLFdBQVcsR0FBRyxjQUFjLENBQUM7SUFFN0Isa0JBQWtCO0lBQ2xCLE1BQU0sR0FBYSxvQkFBb0Is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RSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBDb21wb25lbnQsXG4gIEV2ZW50RW1pdHRlcixcbiAgSW5wdXQsXG4gIE91dHB1dCxcbiAgT25Jbml0LFxuICBPbkRlc3Ryb3ksXG4gIENoYW5nZURldGVjdGlvblN0cmF0ZWd5LFxuICBFbGVtZW50UmVmLFxuICBWaWV3Q2hpbGQsXG4gIE5nWm9uZSxcbiAgQ2hhbmdlRGV0ZWN0b3JSZWZcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBQZXJtaXNzaW9uc1NlcnZpY2UgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9wZXJtaXNzaW9ucy5zZXJ2aWNlJztcbmltcG9ydCB7IFJlY29yZGVyU2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL3JlY29yZGVyLnNlcnZpY2UnO1xuaW1wb3J0IHsgZ2VuZXJhdGVSYW5kb21EaWdpdHMsIG51bWJlcnNUb1BocmFzZSB9IGZyb20gJy4uLy4uL3V0aWxzL251bWJlcnMnO1xuaW1wb3J0IHsgQ29tbW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcblxudHlwZSBWUlN0YXRlID0gJ3ByZXBhcmF0aW9uJyB8ICdjb3VudGRvd24nIHwgJ3JlY29yZGluZycgfCAncHJvY2Vzc2luZycgfCAncmVzdWx0JztcblxuY29uc3QgUkVDT1JEX1NFQ09ORFMgPSAxMDtcbmNvbnN0IFBSRVBfU0VDT05EUyA9IDM7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2Jpby12b2ljZS1yZWNvcmRlcicsXG4gIGltcG9ydHM6IFtDb21tb25Nb2R1bGVdLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICB0ZW1wbGF0ZVVybDogJy4vdm9pY2UtcmVjb3JkZXIuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi92b2ljZS1yZWNvcmRlci5jb21wb25lbnQuc2NzcyddLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbn0pXG5leHBvcnQgY2xhc3MgVm9pY2VSZWNvcmRlckNvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25EZXN0cm95IHtcbiAgQElucHV0KCkgcmVjdFdpZHRoID0gMzYwO1xuICBASW5wdXQoKSByZWN0SGVpZ2h0PzogbnVtYmVyO1xuICBASW5wdXQoKSBub1NoYWRvdyA9IGZhbHNlO1xuICBASW5wdXQoKSBjbGFzc05hbWU/OiBzdHJpbmc7XG4gIEBJbnB1dCgpIHN0eWxlPzogeyBbazogc3RyaW5nXTogYW55IH07XG5cbiAgQE91dHB1dCgpIGNhcHR1cmUgPSBuZXcgRXZlbnRFbWl0dGVyPHsgZmlsZTogRmlsZTsgcGhyYXNlOiBzdHJpbmcgfT4oKTtcblxuICBAVmlld0NoaWxkKCdhdWRpb1JlZicsIHsgc3RhdGljOiBmYWxzZSB9KSBhdWRpb1JlZj86IEVsZW1lbnRSZWY8SFRNTEF1ZGlvRWxlbWVudD47XG5cbiAgcGVybWlzc2lvbiA9IGZhbHNlO1xuICBpc0xvYWRpbmcgPSB0cnVlO1xuICBzdHJlYW0/OiBNZWRpYVN0cmVhbTtcblxuICBzdGF0ZTogVlJTdGF0ZSA9ICdwcmVwYXJhdGlvbic7XG4gIHByZXBUaW1lciA9IFBSRVBfU0VDT05EUztcbiAgcmVjb3JkVGltZXIgPSBSRUNPUkRfU0VDT05EUztcblxuICAvLyBkaWdpdHMgJiBwaHJhc2VcbiAgZGlnaXRzOiBudW1iZXJbXSA9IGdlbmVyYXRlUmFuZG9tRGlnaXRzKDEwKTtcbiAgbWFza2VkRGlnaXRzID0gQXJyYXkoMTApLmZpbGwoJyonKTtcblxuICAvLyB2aXN1YWxpemF0aW9uXG4gIGF1ZGlvTGV2ZWwgPSAwO1xuICB3YXZlZm9ybURhdGE6IG51bWJlcltdID0gW107XG5cbiAgLy8gYmxvYnMvdXJsc1xuICBwcml2YXRlIGF1ZGlvQmxvYjogQmxvYiB8IG51bGwgPSBudWxsO1xuICBhdWRpb1VybDogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG4gIGlzQ29uZmlybWluZyA9IGZhbHNlO1xuXG4gIC8vIHRpbWVycyAvIHJhZlxuICBwcml2YXRlIGNvdW50ZG93blRpbWVvdXQ/OiBhbnk7XG4gIHByaXZhdGUgcmVjb3JkSW50ZXJ2YWw/OiBhbnk7XG4gIHByaXZhdGUgcmFmSWQ/OiBudW1iZXI7XG5cbiAgLy8gYXVkaW8gY29udGV4dCBub2Rlc1xuICBwcml2YXRlIGF1ZGlvQ3R4PzogQXVkaW9Db250ZXh0O1xuICBwcml2YXRlIGFuYWx5c2VyPzogQW5hbHlzZXJOb2RlO1xuICBwcml2YXRlIHNvdXJjZU5vZGU/OiBNZWRpYVN0cmVhbUF1ZGlvU291cmNlTm9kZTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHBlcm1zOiBQZXJtaXNzaW9uc1NlcnZpY2UsXG4gICAgcHJpdmF0ZSByZWNvcmRlcjogUmVjb3JkZXJTZXJ2aWNlLFxuICAgIHByaXZhdGUgbmdab25lOiBOZ1pvbmUsXG4gICAgcHJpdmF0ZSBjZHI6IENoYW5nZURldGVjdG9yUmVmXG4gICkgeyB9XG5cbiAgYXN5bmMgbmdPbkluaXQoKSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHN0YXR1cyA9IGF3YWl0IChuYXZpZ2F0b3IucGVybWlzc2lvbnM/LnF1ZXJ5Py4oeyBuYW1lOiAnbWljcm9waG9uZScgYXMgUGVybWlzc2lvbk5hbWUgfSkgPz8gUHJvbWlzZS5yZWplY3QoKSk7XG4gICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgICB0aGlzLnBlcm1pc3Npb24gPSBzdGF0dXMuc3RhdGUgPT09ICdncmFudGVkJztcbiAgICAgICAgdGhpcy5pc0xvYWRpbmcgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5jZHIuZGV0ZWN0Q2hhbmdlcygpO1xuICAgICAgfSk7XG5cbiAgICAgIGlmICh0aGlzLnBlcm1pc3Npb24pIGF3YWl0IHRoaXMucmVxdWVzdFBlcm1pc3Npb25zKCk7XG5cbiAgICAgIHN0YXR1cy5vbmNoYW5nZSA9IGFzeW5jICgpID0+IHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKGFzeW5jICgpID0+IHtcbiAgICAgICAgICB0aGlzLnBlcm1pc3Npb24gPSBzdGF0dXMuc3RhdGUgPT09ICdncmFudGVkJztcbiAgICAgICAgICBpZiAodGhpcy5wZXJtaXNzaW9uICYmICF0aGlzLnN0cmVhbSkgYXdhaXQgdGhpcy5yZXF1ZXN0UGVybWlzc2lvbnMoKTtcbiAgICAgICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICAgIH0pO1xuICAgICAgfTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB7XG4gICAgICAgIHRoaXMucGVybWlzc2lvbiA9IGZhbHNlO1xuICAgICAgICB0aGlzLmlzTG9hZGluZyA9IGZhbHNlO1xuICAgICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICBuZ09uRGVzdHJveSgpIHtcbiAgICB0aGlzLmNsZWFyQWxsVGltZXJzKCk7XG4gICAgdGhpcy50ZWFyZG93blZpc3VhbGl6ZXIoKTtcbiAgICB0aGlzLnBlcm1zLnN0b3BTdHJlYW0odGhpcy5zdHJlYW0pO1xuICAgIGlmICh0aGlzLmF1ZGlvVXJsKSBVUkwucmV2b2tlT2JqZWN0VVJMKHRoaXMuYXVkaW9VcmwpO1xuICB9XG5cbiAgYXN5bmMgcmVxdWVzdFBlcm1pc3Npb25zKCkge1xuICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB7XG4gICAgICB0aGlzLmlzTG9hZGluZyA9IHRydWU7XG4gICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgfSk7XG5cbiAgICBjb25zdCB7IHN0cmVhbSwgZ3JhbnRlZCB9ID0gYXdhaXQgdGhpcy5wZXJtcy5yZXF1ZXN0TWljcm9waG9uZSgpO1xuXG4gICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHtcbiAgICAgIHRoaXMucGVybWlzc2lvbiA9ICEhZ3JhbnRlZDtcbiAgICAgIHRoaXMuaXNMb2FkaW5nID0gZmFsc2U7XG4gICAgICBpZiAoZ3JhbnRlZCAmJiBzdHJlYW0pIHRoaXMuc3RyZWFtID0gc3RyZWFtO1xuICAgICAgdGhpcy5jZHIuZGV0ZWN0Q2hhbmdlcygpO1xuICAgIH0pO1xuICB9XG5cbiAgc3RhcnRSZWNvcmRpbmcoKSB7XG4gICAgdGhpcy5kaWdpdHMgPSBnZW5lcmF0ZVJhbmRvbURpZ2l0cygxMCk7XG4gICAgdGhpcy5yZWNvcmRUaW1lciA9IFJFQ09SRF9TRUNPTkRTO1xuICAgIHRoaXMucHJlcFRpbWVyID0gUFJFUF9TRUNPTkRTO1xuICAgIHRoaXMuc3RhdGUgPSAnY291bnRkb3duJztcblxuICAgIHRoaXMudGlja0NvdW50ZG93bigpO1xuICB9XG5cbiAgcHJpdmF0ZSB0aWNrQ291bnRkb3duKCkge1xuICAgIGlmICh0aGlzLnByZXBUaW1lciA+IDApIHtcbiAgICAgIHRoaXMuY291bnRkb3duVGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgICAgIHRoaXMucHJlcFRpbWVyIC09IDE7XG4gICAgICAgICAgdGhpcy5jZHIuZGV0ZWN0Q2hhbmdlcygpO1xuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy50aWNrQ291bnRkb3duKCk7XG4gICAgICB9LCAxMDAwKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgdGhpcy5zdGF0ZSA9ICdyZWNvcmRpbmcnO1xuICAgICAgdGhpcy5wcmVwVGltZXIgPSBQUkVQX1NFQ09ORFM7XG4gICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgfSk7XG4gICAgdGhpcy5iZWdpblJlY29yZGluZygpO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBiZWdpblJlY29yZGluZygpIHtcbiAgICBpZiAoIXRoaXMuc3RyZWFtKSB7XG4gICAgICBjb25zdCB7IHN0cmVhbSwgZ3JhbnRlZCB9ID0gYXdhaXQgdGhpcy5wZXJtcy5yZXF1ZXN0TWljcm9waG9uZSgpO1xuICAgICAgaWYgKCFncmFudGVkIHx8ICFzdHJlYW0pIHJldHVybjtcbiAgICAgIHRoaXMuc3RyZWFtID0gc3RyZWFtO1xuICAgIH1cblxuICAgIHRoaXMucmVjb3JkZXIuc3RhcnQodGhpcy5zdHJlYW0sICdhdWRpby93ZWJtJyk7XG4gICAgdGhpcy5zZXR1cFZpc3VhbGl6ZXIoKTtcblxuICAgIHRoaXMucmVjb3JkSW50ZXJ2YWwgPSBzZXRJbnRlcnZhbChhc3luYyAoKSA9PiB7XG4gICAgICB0aGlzLm5nWm9uZS5ydW4oYXN5bmMgKCkgPT4ge1xuICAgICAgICB0aGlzLnJlY29yZFRpbWVyIC09IDE7XG4gICAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICAgICAgaWYgKHRoaXMucmVjb3JkVGltZXIgPD0gMCkge1xuICAgICAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy5yZWNvcmRJbnRlcnZhbCk7XG4gICAgICAgICAgYXdhaXQgdGhpcy5maW5pc2hSZWNvcmRpbmcoKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSwgMTAwMCk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGZpbmlzaFJlY29yZGluZygpIHtcbiAgICB0aGlzLnN0YXRlID0gJ3Byb2Nlc3NpbmcnO1xuXG4gICAgY29uc3QgYmxvYiA9IGF3YWl0IHRoaXMucmVjb3JkZXIuc3RvcCgpO1xuXG4gICAgLy8gc3RvcCBkZXZpY2VzIHZpc3VhbGl6ZXJcbiAgICB0aGlzLnRlYXJkb3duVmlzdWFsaXplcigpO1xuICAgIHRoaXMucGVybXMuc3RvcFN0cmVhbSh0aGlzLnN0cmVhbSk7XG4gICAgdGhpcy5zdHJlYW0gPSB1bmRlZmluZWQ7XG5cbiAgICAvLyBwcm9kdWNlIHVybCBhbmQgbW92ZSB0byBjb25maXJtXG4gICAgY29uc3QgZmluYWxCbG9iID0gYmxvYiAmJiBibG9iLnNpemUgPiAwID8gYmxvYiA6IG5ldyBCbG9iKFsnZHVtbXkgYXVkaW8gZGF0YSddLCB7IHR5cGU6ICdhdWRpby93ZWJtJyB9KTtcbiAgICB0aGlzLmF1ZGlvQmxvYiA9IGZpbmFsQmxvYjtcbiAgICB0aGlzLmF1ZGlvVXJsID0gVVJMLmNyZWF0ZU9iamVjdFVSTChmaW5hbEJsb2IpO1xuICAgIHRoaXMuc3RhdGUgPSAncmVzdWx0JztcbiAgICB0aGlzLmlzQ29uZmlybWluZyA9IHRydWU7XG4gIH1cblxuICBhc3luYyBoYW5kbGVDYW5jZWwoKSB7XG4gICAgdGhpcy5jbGVhckFsbFRpbWVycygpO1xuXG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHRoaXMucmVjb3JkZXIuc3RvcCgpO1xuICAgIH0gY2F0Y2ggeyB9XG5cbiAgICB0aGlzLnRlYXJkb3duVmlzdWFsaXplcigpO1xuICAgIHRoaXMucGVybXMuc3RvcFN0cmVhbSh0aGlzLnN0cmVhbSk7XG4gICAgdGhpcy5zdHJlYW0gPSB1bmRlZmluZWQ7XG5cbiAgICAvLyByZXNldCBVSVxuICAgIHRoaXMuc3RhdGUgPSAncHJlcGFyYXRpb24nO1xuICAgIHRoaXMuaXNDb25maXJtaW5nID0gZmFsc2U7XG4gICAgaWYgKHRoaXMuYXVkaW9VcmwpIFVSTC5yZXZva2VPYmplY3RVUkwodGhpcy5hdWRpb1VybCk7XG4gICAgdGhpcy5hdWRpb1VybCA9IG51bGw7XG4gICAgdGhpcy5hdWRpb0Jsb2IgPSBudWxsO1xuICAgIHRoaXMuYXVkaW9MZXZlbCA9IDA7XG4gICAgdGhpcy53YXZlZm9ybURhdGEgPSBbXTtcblxuICAgIHRoaXMucmVxdWVzdFBlcm1pc3Npb25zKCk7XG4gIH1cblxuICBoYW5kbGVDb25maXJtKCkge1xuICAgIGlmICghdGhpcy5hdWRpb0Jsb2IpIHJldHVybjtcbiAgICBjb25zdCBmaWxlID0gbmV3IEZpbGUoW3RoaXMuYXVkaW9CbG9iXSwgJ3ZvaWNlLXJlY29yZGluZy53ZWJtJywgeyB0eXBlOiB0aGlzLmF1ZGlvQmxvYi50eXBlIH0pO1xuICAgIHRoaXMuY2FwdHVyZS5lbWl0KHsgZmlsZSwgcGhyYXNlOiBudW1iZXJzVG9QaHJhc2UodGhpcy5kaWdpdHMpIH0pO1xuXG4gICAgaWYgKHRoaXMuYXVkaW9VcmwpIFVSTC5yZXZva2VPYmplY3RVUkwodGhpcy5hdWRpb1VybCk7XG4gICAgdGhpcy5hdWRpb1VybCA9IG51bGw7XG4gICAgdGhpcy5hdWRpb0Jsb2IgPSBudWxsO1xuICAgIHRoaXMuaXNDb25maXJtaW5nID0gZmFsc2U7XG4gICAgdGhpcy5zdGF0ZSA9ICdwcmVwYXJhdGlvbic7XG4gIH1cblxuICBoYW5kbGVEZWNsaW5lKCkge1xuICAgIGlmICh0aGlzLmF1ZGlvVXJsKSBVUkwucmV2b2tlT2JqZWN0VVJMKHRoaXMuYXVkaW9VcmwpO1xuICAgIHRoaXMuYXVkaW9VcmwgPSBudWxsO1xuICAgIHRoaXMuYXVkaW9CbG9iID0gbnVsbDtcbiAgICB0aGlzLmlzQ29uZmlybWluZyA9IGZhbHNlO1xuICAgIHRoaXMuc3RhdGUgPSAncHJlcGFyYXRpb24nO1xuICAgIHRoaXMucmVxdWVzdFBlcm1pc3Npb25zKCk7XG4gIH1cblxuICAvLyAtLS0tLSBWaXN1YWxpemF0aW9uIC0tLS0tXG4gIHByaXZhdGUgc2V0dXBWaXN1YWxpemVyKCkge1xuICAgIGlmICghdGhpcy5zdHJlYW0pIHJldHVybjtcbiAgICB0aGlzLmF1ZGlvQ3R4ID0gbmV3ICh3aW5kb3cuQXVkaW9Db250ZXh0IHx8ICh3aW5kb3cgYXMgYW55KS53ZWJraXRBdWRpb0NvbnRleHQpKCk7XG4gICAgdGhpcy5hbmFseXNlciA9IHRoaXMuYXVkaW9DdHguY3JlYXRlQW5hbHlzZXIoKTtcbiAgICB0aGlzLnNvdXJjZU5vZGUgPSB0aGlzLmF1ZGlvQ3R4LmNyZWF0ZU1lZGlhU3RyZWFtU291cmNlKHRoaXMuc3RyZWFtKTtcbiAgICB0aGlzLmFuYWx5c2VyLmZmdFNpemUgPSAyNTY7XG4gICAgdGhpcy5hbmFseXNlci5zbW9vdGhpbmdUaW1lQ29uc3RhbnQgPSAwLjg7XG4gICAgdGhpcy5zb3VyY2VOb2RlLmNvbm5lY3QodGhpcy5hbmFseXNlcik7XG5cbiAgICBjb25zdCBkYXRhID0gbmV3IFVpbnQ4QXJyYXkodGhpcy5hbmFseXNlci5mcmVxdWVuY3lCaW5Db3VudCk7XG4gICAgY29uc3QgbG9vcCA9ICgpID0+IHtcbiAgICAgIGlmICghdGhpcy5hbmFseXNlciB8fCB0aGlzLnN0YXRlICE9PSAncmVjb3JkaW5nJykgcmV0dXJuO1xuICAgICAgdGhpcy5hbmFseXNlci5nZXRCeXRlRnJlcXVlbmN5RGF0YShkYXRhKTtcbiAgICAgIGNvbnN0IGF2ZyA9IGRhdGEucmVkdWNlKChzLCB2KSA9PiBzICsgdiwgMCkgLyBkYXRhLmxlbmd0aDtcblxuICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHtcbiAgICAgICAgdGhpcy5hdWRpb0xldmVsID0gYXZnIC8gMjU1O1xuICAgICAgICB0aGlzLndhdmVmb3JtRGF0YSA9IEFycmF5LmZyb20oZGF0YSkuc2xpY2UoMCwgNjQpO1xuICAgICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICB9KTtcblxuICAgICAgdGhpcy5yYWZJZCA9IHJlcXVlc3RBbmltYXRpb25GcmFtZShsb29wKTtcbiAgICB9O1xuICAgIGxvb3AoKTtcbiAgfVxuXG4gIHByaXZhdGUgdGVhcmRvd25WaXN1YWxpemVyKCkge1xuICAgIGlmICh0aGlzLnJhZklkKSBjYW5jZWxBbmltYXRpb25GcmFtZSh0aGlzLnJhZklkKTtcbiAgICB0aGlzLnJhZklkID0gdW5kZWZpbmVkO1xuXG4gICAgdHJ5IHsgdGhpcy5zb3VyY2VOb2RlPy5kaXNjb25uZWN0KCk7IH0gY2F0Y2ggeyB9XG4gICAgdHJ5IHsgdGhpcy5hbmFseXNlcj8uZGlzY29ubmVjdCgpOyB9IGNhdGNoIHsgfVxuICAgIHRyeSB7IHRoaXMuYXVkaW9DdHg/LmNsb3NlKCk7IH0gY2F0Y2ggeyB9XG5cbiAgICB0aGlzLnNvdXJjZU5vZGUgPSB1bmRlZmluZWQ7XG4gICAgdGhpcy5hbmFseXNlciA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLmF1ZGlvQ3R4ID0gdW5kZWZpbmVkO1xuICB9XG5cbiAgcHJpdmF0ZSBjbGVhckFsbFRpbWVycygpIHtcbiAgICBpZiAodGhpcy5jb3VudGRvd25UaW1lb3V0KSBjbGVhclRpbWVvdXQodGhpcy5jb3VudGRvd25UaW1lb3V0KTtcbiAgICB0aGlzLmNvdW50ZG93blRpbWVvdXQgPSB1bmRlZmluZWQ7XG4gICAgaWYgKHRoaXMucmVjb3JkSW50ZXJ2YWwpIGNsZWFySW50ZXJ2YWwodGhpcy5yZWNvcmRJbnRlcnZhbCk7XG4gICAgdGhpcy5yZWNvcmRJbnRlcnZhbCA9IHVuZGVmaW5lZDtcbiAgfVxuXG4gIGdldFZvbHVtZUhlaWdodCh2OiBudW1iZXIpOiBudW1iZXIge1xuICAgIHJldHVybiBNYXRoLm1heCgyLCAodiAvIDI1NSkgKiAxMDApO1xuICB9XG59XG4iLCI8ZGl2IGNsYXNzPVwidnItd3JhcHBlclwiIFtjbGFzcy5uby1zaGFkb3ddPVwibm9TaGFkb3dcIiBbc3R5bGUud2lkdGgucHhdPVwicmVjdFdpZHRoXCIgW3N0eWxlLmhlaWdodC5weF09XCJyZWN0SGVpZ2h0IHx8IG51bGxcIlxuICBbbmdTdHlsZV09XCJzdHlsZVwiPlxuICA8IS0tIExPQURJTkcgLS0+XG4gIDxkaXYgKm5nSWY9XCJpc0xvYWRpbmdcIiBjbGFzcz1cInZyLWNlbnRlclwiPlxuICAgIDxkaXYgY2xhc3M9XCJ2ci1zcGlubmVyXCI+PC9kaXY+XG4gICAgPGgzPkluaXRpYWxpemluZyBNaWNyb3Bob25lLi4uPC9oMz5cbiAgICA8cD5QbGVhc2Ugd2FpdCB3aGlsZSB3ZSBzZXQgdXAg