dozee-ecg-chart
Version:
A library for drawing ECG charts with real-time data
217 lines • 28.1 kB
JavaScript
import { Component, Input } from '@angular/core';
import { Chart, CategoryScale, LinearScale, LineController, LineElement, PointElement, Title, Tooltip, Legend, } from 'chart.js';
import * as i0 from "@angular/core";
export class DozeeEcgChartComponent {
renderer;
ngZone;
accessToken;
userId;
stage;
strokeColor = '#00ff00';
backgroundColor = '#000000';
eventSource = null;
buffer = [];
chart;
frequency = 256; // Hz
duration = 8; // seconds
maxPoints = this.frequency * this.duration; // 2048 points
ecgData = Array(this.maxPoints).fill(null);
bufferLimit = 64 * 64;
isPageActive = true;
intervalId;
inactivityTimeout; // Timer to track inactivity
INACTIVE_DELAY = 30000;
RETRY_DELAY = 6000;
constructor(renderer, ngZone) {
this.renderer = renderer;
this.ngZone = ngZone;
}
ngOnInit() {
// Register necessary chart components
Chart.register(CategoryScale, LinearScale, LineController, LineElement, PointElement, Title, Tooltip, Legend);
const ctx = document.getElementById('ecgChart');
ctx.style.backgroundColor = this.backgroundColor;
this.chart = new Chart(ctx, {
type: 'line',
data: {
labels: Array(this.maxPoints).fill(0.0), // Empty labels as X axis is fixed
datasets: [
{
label: 'ECG Data',
data: this.ecgData,
borderColor: this.strokeColor,
borderWidth: 1,
fill: false,
pointRadius: 0, // Hide points for smooth line
},
],
},
options: {
animation: false,
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
type: 'linear',
min: 0,
max: this.maxPoints, // Set fixed X-axis range (8 seconds)
display: false, // Hide the x-axis
grid: { display: false },
},
y: {
max: 4000, // Adjust based on ECG value range
min: 0,
type: 'linear',
beginAtZero: true,
grid: { display: false },
display: false,
},
},
plugins: {
legend: {
display: false,
},
},
elements: {
line: {
tension: 0.4,
},
},
},
});
// Initialize SSE connection
this.initializeSse();
this.renderer.listen('document', 'visibilitychange', () => {
if (document.hidden) {
console.log('Page became inactive, starting 30s timeout...');
this.startInactivityTimer();
}
else {
console.log('Page is active again, canceling inactivity timeout');
this.cancelInactivityTimer();
this.startInterval();
}
});
// Start interval initially
this.startInterval();
}
startInterval() {
if (!this.intervalId) {
// Update chart data at regular intervals
this.intervalId = setInterval(() => {
if (this.buffer.length > this.bufferLimit && this.currentIndex === 0) {
if (this.stage === 'sit') {
console.warn(`Buffer overflow: Skipping ${this.buffer.length - 64} old entries`);
}
this.buffer.splice(0, this.buffer.length - 64);
}
if (this.buffer.length > 0) {
const entry = this.buffer.shift();
for (const e of entry) {
this.addData(e);
}
}
}, 16); // Update frequency (256 Hz)
}
}
startInactivityTimer() {
this.inactivityTimeout = setTimeout(() => {
console.log('Page has been inactive for 30 seconds, stopping updates.');
this.isPageActive = false;
this.clearInterval();
}, this.INACTIVE_DELAY);
}
cancelInactivityTimer() {
if (this.inactivityTimeout) {
clearTimeout(this.inactivityTimeout);
this.inactivityTimeout = null;
}
this.isPageActive = true;
}
clearInterval() {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
}
}
currentIndex = 0;
addData(e) {
if (this.chart.data.labels) {
this.ecgData[this.currentIndex] = e.Value;
this.chart.data.labels[this.currentIndex] = this.currentIndex;
for (let i = this.currentIndex + 1; i < Math.min(this.currentIndex + 64, this.maxPoints); i++) {
this.ecgData[i] = null;
}
// Advance the index, and wrap around if it exceeds maxPoints
this.currentIndex = (this.currentIndex + 1) % this.maxPoints;
if (this.stage === 'sit') {
console.log('Current index:', this.currentIndex);
}
// Update the chart data
this.chart.data.datasets[0].data = this.ecgData;
this.chart.update();
}
}
initializeSse() {
const sseUrl = `https://ecgsse${this.stage ? `-${this.stage}` : ''}.dozee.cloud/sse/ecgstream?userId=${this.userId}&accessToken=${this.accessToken}&ngsw-bypass=true`;
this.ngZone.runOutsideAngular(() => {
this.eventSource = new EventSource(sseUrl);
this.eventSource.onmessage = (e) => {
const data = JSON.parse(e.data);
if (data.Key === 'SIGNAL' && this.isPageActive) {
this.ngZone.run(() => {
// Angular will detect this change and update bindings
let entry = [];
data.Values.forEach((s, i) => {
entry.push({
Timestamp: data.Timestamp + i * (1000 / this.frequency), // Adjust timestamp for frequency
Value: s,
});
if (entry.length === 4) {
this.buffer.push(entry);
if (this.stage === 'sit') {
console.log('Buffer length:', this.buffer.length);
}
entry = [];
}
});
});
}
};
this.eventSource.onerror = (error) => {
console.error('SSE error, will retry in 6s', error);
this.closeConnection();
// schedule a reconnect
setTimeout(() => {
this.initializeSse();
}, this.RETRY_DELAY);
};
});
}
ngOnDestroy() {
this.closeConnection();
}
closeConnection() {
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DozeeEcgChartComponent, deps: [{ token: i0.Renderer2 }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: DozeeEcgChartComponent, selector: "app-ecg-chart", inputs: { accessToken: "accessToken", userId: "userId", stage: "stage", strokeColor: "strokeColor", backgroundColor: "backgroundColor" }, ngImport: i0, template: "<div class=\"chart-container\">\n <canvas id=\"ecgChart\"></canvas>\n</div>\n", styles: [".chart-container{width:100%;height:100%;position:relative}canvas{width:100%!important;height:100%!important}\n"] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DozeeEcgChartComponent, decorators: [{
type: Component,
args: [{ selector: 'app-ecg-chart', template: "<div class=\"chart-container\">\n <canvas id=\"ecgChart\"></canvas>\n</div>\n", styles: [".chart-container{width:100%;height:100%;position:relative}canvas{width:100%!important;height:100%!important}\n"] }]
}], ctorParameters: () => [{ type: i0.Renderer2 }, { type: i0.NgZone }], propDecorators: { accessToken: [{
type: Input
}], userId: [{
type: Input
}], stage: [{
type: Input
}], strokeColor: [{
type: Input
}], backgroundColor: [{
type: Input
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZG96ZWUtZWNnLWNoYXJ0LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL2RvemVlLWVjZy1jaGFydC9zcmMvbGliL2RvemVlLWVjZy1jaGFydC5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9wcm9qZWN0cy9kb3plZS1lY2ctY2hhcnQvc3JjL2xpYi9kb3plZS1lY2ctY2hhcnQuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBVSxLQUFLLEVBQXFCLE1BQU0sZUFBZSxDQUFDO0FBQzVFLE9BQU8sRUFDTCxLQUFLLEVBQ0wsYUFBYSxFQUNiLFdBQVcsRUFDWCxjQUFjLEVBQ2QsV0FBVyxFQUNYLFlBQVksRUFDWixLQUFLLEVBQ0wsT0FBTyxFQUNQLE1BQU0sR0FDUCxNQUFNLFVBQVUsQ0FBQzs7QUFrQmxCLE1BQU0sT0FBTyxzQkFBc0I7SUFxQmI7SUFBNkI7SUFwQnhDLFdBQVcsQ0FBVTtJQUNyQixNQUFNLENBQVU7SUFDaEIsS0FBSyxDQUFVO0lBQ2YsV0FBVyxHQUFXLFNBQVMsQ0FBQztJQUNoQyxlQUFlLEdBQVcsU0FBUyxDQUFDO0lBRXJDLFdBQVcsR0FBdUIsSUFBSSxDQUFDO0lBQ3ZDLE1BQU0sR0FBVyxFQUFFLENBQUM7SUFDcEIsS0FBSyxDQUFTO0lBQ2QsU0FBUyxHQUFHLEdBQUcsQ0FBQyxDQUFDLEtBQUs7SUFDdEIsUUFBUSxHQUFHLENBQUMsQ0FBQyxDQUFDLFVBQVU7SUFDeEIsU0FBUyxHQUFHLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLGNBQWM7SUFDMUQsT0FBTyxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzNDLFdBQVcsR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFDO0lBQ3RCLFlBQVksR0FBRyxJ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LENBQUMsQ0FBQztZQUN2QixDQUFDLENBQUM7UUFDSixDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxXQUFXO1FBQ1QsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO0lBQ3pCLENBQUM7SUFFTyxlQUFlO1FBQ3JCLElBQUksSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQ3JCLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDekIsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUM7UUFDMUIsQ0FBQztJQUNILENBQUM7dUdBbk9VLHNCQUFzQjsyRkFBdEIsc0JBQXNCLCtMQzdCbkMsZ0ZBR0E7OzJGRDBCYSxzQkFBc0I7a0JBTGxDLFNBQVM7K0JBQ0UsZUFBZTttR0FLaEIsV0FBVztzQkFBbkIsS0FBSztnQkFDRyxNQUFNO3NCQUFkLEtBQUs7Z0JBQ0csS0FBSztzQkFBYixLQUFLO2dCQUNHLFdBQVc7c0JBQW5CLEtBQUs7Z0JBQ0csZUFBZTtzQkFBdkIsS0FBSyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgT25Jbml0LCBJbnB1dCwgUmVuZGVyZXIyLCBOZ1pvbmUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7XG4gIENoYXJ0LFxuICBDYXRlZ29yeVNjYWxlLFxuICBMaW5lYXJTY2FsZSxcbiAgTGluZUNvbnRyb2xsZXIsXG4gIExpbmVFbGVtZW50LFxuICBQb2ludEVsZW1lbnQsXG4gIFRpdGxlLFxuICBUb29sdGlwLFxuICBMZWdlbmQsXG59IGZyb20gJ2NoYXJ0LmpzJztcblxuaW50ZXJmYWNlIEVjZ1NpZ25hbERhdGEge1xuICBLZXk6IHN0cmluZztcbiAgVGltZXN0YW1wOiBudW1iZXI7XG4gIFZhbHVlczogbnVtYmVyW107XG59XG5cbmludGVyZmFjZSBYeSB7XG4gIFRpbWVzdGFtcDogbnVtYmVyO1xuICBWYWx1ZTogbnVtYmVyO1xufVxuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdhcHAtZWNnLWNoYXJ0JyxcbiAgdGVtcGxhdGVVcmw6ICcuL2RvemVlLWVjZy1jaGFydC5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL2RvemVlLWVjZy1jaGFydC5jb21wb25lbnQuY3NzJ10sXG59KVxuZXhwb3J0IGNsYXNzIERvemVlRWNnQ2hhcnRDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQge1xuICBASW5wdXQoKSBhY2Nlc3NUb2tlbiE6IHN0cmluZztcbiAgQElucHV0KCkgdXNlcklkITogc3RyaW5nO1xuICBASW5wdXQoKSBzdGFnZSE6IHN0cmluZztcbiAgQElucHV0KCkgc3Ryb2tlQ29sb3I6IHN0cmluZyA9ICcjMDBmZjAwJztcbiAgQElucHV0KCkgYmFja2dyb3VuZENvbG9yOiBzdHJpbmcgPSAnIzAwMDAwMCc7XG5cbiAgcHJpdmF0ZSBldmVudFNvdXJjZTogRXZlbnRTb3VyY2UgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBidWZmZXI6IFh5W11bXSA9IFtdO1xuICBwcml2YXRlIGNoYXJ0ITogQ2hhcnQ7XG4gIHByaXZhdGUgZnJlcXVlbmN5ID0gMjU2OyAvLyBIelxuICBwcml2YXRlIGR1cmF0aW9uID0gODsgLy8gc2Vjb25kc1xuICBwcml2YXRlIG1heFBvaW50cyA9IHRoaXMuZnJlcXVlbmN5ICogdGhpcy5kdXJhdGlvbjsgLy8gMjA0OCBwb2ludHNcbiAgcHJpdmF0ZSBlY2dEYXRhID0gQXJyYXkodGhpcy5tYXhQb2ludHMpLmZpbGwobnVsbCk7XG4gIHByaXZhdGUgYnVmZmVyTGltaXQgPSA2NCAqIDY0O1xuICBwcml2YXRlIGlzUGFnZUFjdGl2ZSA9IHRydWU7XG4gIHByaXZhdGUgaW50ZXJ2YWxJZDogYW55O1xuICBwcml2YXRlIGluYWN0aXZpdHlUaW1lb3V0OiBhbnk7IC8vIFRpbWVyIHRvIHRyYWNrIGluYWN0aXZpdHlcbiAgcHJpdmF0ZSByZWFkb25seSBJTkFDVElWRV9ERUxBWSA9IDMwMDAwO1xuICBwcml2YXRlIHJlYWRvbmx5IFJFVFJZX0RFTEFZID0gNjAwMDtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlbmRlcmVyOiBSZW5kZXJlcjIsIHByaXZhdGUgbmdab25lOiBOZ1pvbmUpIHt9XG5cbiAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgLy8gUmVnaXN0ZXIgbmVjZXNzYXJ5IGNoYXJ0IGNvbXBvbmVudHNcbiAgICBDaGFydC5yZWdpc3RlcihcbiAgICAgIENhdGVnb3J5U2NhbGUsXG4gICAgICBMaW5lYXJTY2FsZSxcbiAgICAgIExpbmVDb250cm9sbGVyLFxuICAgICAgTGluZUVsZW1lbnQsXG4gICAgICBQb2ludEVsZW1lbnQsXG4gICAgICBUaXRsZSxcbiAgICAgIFRvb2x0aXAsXG4gICAgICBMZWdlbmRcbiAgICApO1xuXG4gICAgY29uc3QgY3R4ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2VjZ0NoYXJ0JykgYXMgSFRNTENhbnZhc0VsZW1lbnQ7XG4gICAgY3R4LnN0eWxlLmJhY2tncm91bmRDb2xvciA9IHRoaXMuYmFja2dyb3VuZENvbG9yO1xuXG4gICAgdGhpcy5jaGFydCA9IG5ldyBDaGFydChjdHgsIHtcbiAgICAgIHR5cGU6ICdsaW5lJyxcbiAgICAgIGRhdGE6IHtcbiAgICAgICAgbGFiZWxzOiBBcnJheSh0aGlzLm1heFBvaW50cykuZmlsbCgwLjApLCAvLyBFbXB0eSBsYWJlbHMgYXMgWCBheGlzIGlzIGZpeGVkXG4gICAgICAgIGRhdGFzZXRzOiBbXG4gICAgICAgICAge1xuICAgICAgICAgICAgbGFiZWw6ICdFQ0cgRGF0YScsXG4gICAgICAgICAgICBkYXRhOiB0aGlzLmVjZ0RhdGEsXG4gICAgICAgICAgICBib3JkZXJDb2xvcjogdGhpcy5zdHJva2VDb2xvcixcbiAgICAgICAgICAgIGJvcmRlcldpZHRoOiAxLFxuICAgICAgICAgICAgZmlsbDogZmFsc2UsXG4gICAgICAgICAgICBwb2ludFJhZGl1czogMCwgLy8gSGlkZSBwb2ludHMgZm9yIHNtb290aCBsaW5lXG4gICAgICAgICAgfSxcbiAgICAgICAgXSxcbiAgICAgIH0sXG4gICAgICBvcHRpb25zOiB7XG4gICAgICAgIGFuaW1hdGlvbjogZmFsc2UsXG4gICAgICAgIHJlc3BvbnNpdmU6IHRydWUsXG4gICAgICAgIG1haW50YWluQXNwZWN0UmF0aW86IGZhbHNlLFxuICAgICAgICBzY2FsZXM6IHtcbiAgICAgICAgICB4OiB7XG4gICAgICAgICAgICB0eXBlOiAnbGluZWFyJyxcbiAgICAgICAgICAgIG1pbjogMCxcbiAgICAgICAgICAgIG1heDogdGhpcy5tYXhQb2ludHMsIC8vIFNldCBmaXhlZCBYLWF4aXMgcmFuZ2UgKDggc2Vjb25kcylcbiAgICAgICAgICAgIGRpc3BsYXk6IGZhbHNlLCAvLyBIaWRlIHRoZSB4LWF4aXNcbiAgICAgICAgICAgIGdyaWQ6IHsgZGlzcGxheTogZmFsc2UgfSxcbiAgICAgICAgICB9LFxuICAgICAgICAgIHk6IHtcbiAgICAgICAgICAgIG1heDogNDAwMCwgLy8gQWRqdXN0IGJhc2VkIG9uIEVDRyB2YWx1ZSByYW5nZVxuICAgICAgICAgICAgbWluOiAwLFxuICAgICAgICAgICAgdHlwZTogJ2xpbmVhcicsXG4gICAgICAgICAgICBiZWdpbkF0WmVybzogdHJ1ZSxcbiAgICAgICAgICAgIGdyaWQ6IHsgZGlzcGxheTogZmFsc2UgfSxcbiAgICAgICAgICAgIGRpc3BsYXk6IGZhbHNlLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0sXG4gICAgICAgIHBsdWdpbnM6IHtcbiAgICAgICAgICBsZWdlbmQ6IHtcbiAgICAgICAgICAgIGRpc3BsYXk6IGZhbHNlLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0sXG4gICAgICAgIGVsZW1lbnRzOiB7XG4gICAgICAgICAgbGluZToge1xuICAgICAgICAgICAgdGVuc2lvbjogMC40LFxuICAgICAgICAgIH0sXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgLy8gSW5pdGlhbGl6ZSBTU0UgY29ubmVjdGlvblxuICAgIHRoaXMuaW5pdGlhbGl6ZVNzZSgpO1xuXG4gICAgdGhpcy5yZW5kZXJlci5saXN0ZW4oJ2RvY3VtZW50JywgJ3Zpc2liaWxpdHljaGFuZ2UnLCAoKSA9PiB7XG4gICAgICBpZiAoZG9jdW1lbnQuaGlkZGVuKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCdQYWdlIGJlY2FtZSBpbmFjdGl2ZSwgc3RhcnRpbmcgMzBzIHRpbWVvdXQuLi4nKTtcbiAgICAgICAgdGhpcy5zdGFydEluYWN0aXZpdHlUaW1lcigpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc29sZS5sb2coJ1BhZ2UgaXMgYWN0aXZlIGFnYWluLCBjYW5jZWxpbmcgaW5hY3Rpdml0eSB0aW1lb3V0Jyk7XG4gICAgICAgIHRoaXMuY2FuY2VsSW5hY3Rpdml0eVRpbWVyKCk7XG4gICAgICAgIHRoaXMuc3RhcnRJbnRlcnZhbCgpO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgLy8gU3RhcnQgaW50ZXJ2YWwgaW5pdGlhbGx5XG4gICAgdGhpcy5zdGFydEludGVydmFsKCk7XG4gIH1cblxuICBwcml2YXRlIHN0YXJ0SW50ZXJ2YWwoKSB7XG4gICAgaWYgKCF0aGlzLmludGVydmFsSWQpIHtcbiAgICAgIC8vIFVwZGF0ZSBjaGFydCBkYXRhIGF0IHJlZ3VsYXIgaW50ZXJ2YWxzXG4gICAgICB0aGlzLmludGVydmFsSWQgPSBzZXRJbnRlcnZhbCgoKSA9PiB7XG5cbiAgICAgICAgaWYgKHRoaXMuYnVmZmVyLmxlbmd0aCA+IHRoaXMuYnVmZmVyTGltaXQgJiYgdGhpcy5jdXJyZW50SW5kZXggPT09IDApIHtcbiAgICAgICAgICBpZiAodGhpcy5zdGFnZSA9PT0gJ3NpdCcpIHtcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihgQnVmZmVyIG92ZXJmbG93OiBTa2lwcGluZyAke3RoaXMuYnVmZmVyLmxlbmd0aCAtIDY0fSBvbGQgZW50cmllc2ApO1xuICAgICAgICAgIH1cbiAgICAgICAgICB0aGlzLmJ1ZmZlci5zcGxpY2UoMCwgdGhpcy5idWZmZXIubGVuZ3RoIC0gNjQpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMuYnVmZmVyLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBjb25zdCBlbnRyeSA9IHRoaXMuYnVmZmVyLnNoaWZ0KCkgYXMgWHlbXTtcbiAgICAgICAgICBmb3IgKGNvbnN0IGUgb2YgZW50cnkpIHtcbiAgICAgICAgICAgIHRoaXMuYWRkRGF0YShlKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0sIDE2KTsgLy8gVXBkYXRlIGZyZXF1ZW5jeSAoMjU2IEh6KVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgc3RhcnRJbmFjdGl2aXR5VGltZXIoKSB7XG4gICAgdGhpcy5pbmFjdGl2aXR5VGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgY29uc29sZS5sb2coJ1BhZ2UgaGFzIGJlZW4gaW5hY3RpdmUgZm9yIDMwIHNlY29uZHMsIHN0b3BwaW5nIHVwZGF0ZXMuJyk7XG4gICAgICB0aGlzLmlzUGFnZUFjdGl2ZSA9IGZhbHNlO1xuICAgICAgdGhpcy5jbGVhckludGVydmFsKCk7XG4gICAgfSwgdGhpcy5JTkFDVElWRV9ERUxBWSk7XG4gIH1cblxuICBwcml2YXRlIGNhbmNlbEluYWN0aXZpdHlUaW1lcigpIHtcbiAgICBpZiAodGhpcy5pbmFjdGl2aXR5VGltZW91dCkge1xuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMuaW5hY3Rpdml0eVRpbWVvdXQpO1xuICAgICAgdGhpcy5pbmFjdGl2aXR5VGltZW91dCA9IG51bGw7XG4gICAgfVxuICAgIHRoaXMuaXNQYWdlQWN0aXZlID0gdHJ1ZTtcbiAgfVxuXG4gIHByaXZhdGUgY2xlYXJJbnRlcnZhbCgpIHtcbiAgICBpZiAodGhpcy5pbnRlcnZhbElkKSB7XG4gICAgICBjbGVhckludGVydmFsKHRoaXMuaW50ZXJ2YWxJZCk7XG4gICAgICB0aGlzLmludGVydmFsSWQgPSBudWxsO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgY3VycmVudEluZGV4ID0gMDtcbiAgYWRkRGF0YShlOiBYeSkge1xuICAgIGlmICh0aGlzLmNoYXJ0LmRhdGEubGFiZWxzKSB7XG4gICAgICB0aGlzLmVjZ0RhdGFbdGhpcy5jdXJyZW50SW5kZXhdID0gZS5WYWx1ZTtcbiAgICAgIHRoaXMuY2hhcnQuZGF0YS5sYWJlbHNbdGhpcy5jdXJyZW50SW5kZXhdID0gdGhpcy5jdXJyZW50SW5kZXg7XG5cbiAgICAgIGZvciAoXG4gICAgICAgIGxldCBpID0gdGhpcy5jdXJyZW50SW5kZXggKyAxO1xuICAgICAgICBpIDwgTWF0aC5taW4odGhpcy5jdXJyZW50SW5kZXggKyA2NCwgdGhpcy5tYXhQb2ludHMpO1xuICAgICAgICBpKytcbiAgICAgICkge1xuICAgICAgICB0aGlzLmVjZ0RhdGFbaV0gPSBudWxsO1xuICAgICAgfVxuXG4gICAgICAvLyBBZHZhbmNlIHRoZSBpbmRleCwgYW5kIHdyYXAgYXJvdW5kIGlmIGl0IGV4Y2VlZHMgbWF4UG9pbnRzXG4gICAgICB0aGlzLmN1cnJlbnRJbmRleCA9ICh0aGlzLmN1cnJlbnRJbmRleCArIDEpICUgdGhpcy5tYXhQb2ludHM7XG4gICAgICBpZiAodGhpcy5zdGFnZSA9PT0gJ3NpdCcpIHtcbiAgICAgIGNvbnNvbGUubG9nKCdDdXJyZW50IGluZGV4OicsIHRoaXMuY3VycmVudEluZGV4KTtcbiAgICAgIH1cblxuICAgICAgLy8gVXBkYXRlIHRoZSBjaGFydCBkYXRhXG4gICAgICB0aGlzLmNoYXJ0LmRhdGEuZGF0YXNldHNbMF0uZGF0YSA9IHRoaXMuZWNnRGF0YTtcbiAgICAgIHRoaXMuY2hhcnQudXBkYXRlKCk7XG4gICAgfVxuICB9XG5cbiAgaW5pdGlhbGl6ZVNzZSgpOiB2b2lkIHtcbiAgICBjb25zdCBzc2VVcmwgPSBgaHR0cHM6Ly9lY2dzc2Uke3RoaXMuc3RhZ2UgPyBgLSR7dGhpcy5zdGFnZX1gOiAnJ30uZG96ZWUuY2xvdWQvc3NlL2VjZ3N0cmVhbT91c2VySWQ9JHt0aGlzLnVzZXJJZH0mYWNjZXNzVG9rZW49JHt0aGlzLmFjY2Vzc1Rva2VufSZuZ3N3LWJ5cGFzcz10cnVlYDtcblxuICAgIHRoaXMubmdab25lLnJ1bk91dHNpZGVBbmd1bGFyKCgpID0+IHtcbiAgICAgIHRoaXMuZXZlbnRTb3VyY2UgPSBuZXcgRXZlbnRTb3VyY2Uoc3NlVXJsKTtcbiAgICAgIHRoaXMuZXZlbnRTb3VyY2Uub25tZXNzYWdlID0gKGU6IE1lc3NhZ2VFdmVudCkgPT4ge1xuICAgICAgICBjb25zdCBkYXRhID0gSlNPTi5wYXJzZShlLmRhdGEpIGFzIEVjZ1NpZ25hbERhdGE7XG4gICAgICAgIGlmIChkYXRhLktleSA9PT0gJ1NJR05BTCcgJiYgdGhpcy5pc1BhZ2VBY3RpdmUpIHtcbiAgICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgICAgICAgLy8gQW5ndWxhciB3aWxsIGRldGVjdCB0aGlzIGNoYW5nZSBhbmQgdXBkYXRlIGJpbmRpbmdzXG4gICAgICAgICAgICBsZXQgZW50cnk6IFh5W10gPSBbXTtcbiAgICAgICAgICAgIGRhdGEuVmFsdWVzLmZvckVhY2goKHM6IG51bWJlciwgaTogbnVtYmVyKSA9PiB7XG4gICAgICAgICAgICAgIGVudHJ5LnB1c2goe1xuICAgICAgICAgICAgICAgIFRpbWVzdGFtcDogZGF0YS5UaW1lc3RhbXAgKyBpICogKDEwMDAgLyB0aGlzLmZyZXF1ZW5jeSksIC8vIEFkanVzdCB0aW1lc3RhbXAgZm9yIGZyZXF1ZW5jeVxuICAgICAgICAgICAgICAgIFZhbHVlOiBzLFxuICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICBpZiAoZW50cnkubGVuZ3RoID09PSA0KSB7XG4gICAgICAgICAgICAgICAgdGhpcy5idWZmZXIucHVzaChlbnRyeSk7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuc3RhZ2UgPT09ICdzaXQnKSB7XG4gICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnQnVmZmVyIGxlbmd0aDonLCB0aGlzLmJ1ZmZlci5sZW5ndGgpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbnRyeSA9IFtdO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfTtcblxuICAgICAgdGhpcy5ldmVudFNvdXJjZS5vbmVycm9yID0gKGVycm9yKSA9PiB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ1NTRSBlcnJvciwgd2lsbCByZXRyeSBpbiA2cycsIGVycm9yKTtcbiAgICAgICAgdGhpcy5jbG9zZUNvbm5lY3Rpb24oKTtcblxuICAgICAgICAvLyBzY2hlZHVsZSBhIHJlY29ubmVjdFxuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICB0aGlzLmluaXRpYWxpemVTc2UoKTtcbiAgICAgICAgfSwgdGhpcy5SRVRSWV9ERUxBWSk7XG4gICAgICB9O1xuICAgIH0pO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgdGhpcy5jbG9zZUNvbm5lY3Rpb24oKTtcbiAgfVxuXG4gIHByaXZhdGUgY2xvc2VDb25uZWN0aW9uKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmV2ZW50U291cmNlKSB7XG4gICAgICB0aGlzLmV2ZW50U291cmNlLmNsb3NlKCk7XG4gICAgICB0aGlzLmV2ZW50U291cmNlID0gbnVsbDtcbiAgICB9XG4gIH1cbn1cbiIsIjxkaXYgY2xhc3M9XCJjaGFydC1jb250YWluZXJcIj5cbiAgPGNhbnZhcyBpZD1cImVjZ0NoYXJ0XCI+PC9jYW52YXM+XG48L2Rpdj5cbiJdfQ==