@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
123 lines • 19.2 kB
JavaScript
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { BehaviorSubject, fromEvent, of, Subject, timer } from 'rxjs';
import { debounceTime, filter, first, mapTo, switchMap, takeUntil, tap } from 'rxjs/operators';
import * as i0 from "@angular/core";
import * as i1 from "@angular/common";
import * as i2 from "@ngx-translate/core";
/**
* Component to count down specific time interval, then emit when countdown ends.
* Next countdown is started right after previous one.
*/
export class CountdownIntervalComponent {
constructor() {
/**
* Emits when countdown ends.
*/
this.countdownEnded = new EventEmitter();
this.runSpinner$ = new BehaviorSubject(false);
this.destroy$ = new Subject();
this.MINIMUM_INTERVAL = 5_000;
this.ONE_SECOND = 1000; // One second in milliseconds
this.start$ = new Subject();
}
/**
* Time in milliseconds to count down from.
*/
set countdownInterval(value) {
this._countdownInterval = Math.max(value, this.MINIMUM_INTERVAL);
}
get countdownInterval() {
return this._countdownInterval || this.MINIMUM_INTERVAL;
}
ngOnInit() {
this.secondsUntilRefresh$ = new BehaviorSubject(`${Math.floor(this.countdownInterval / 1000)}`);
this.start$
.pipe(debounceTime(100), takeUntil(this.destroy$))
.subscribe(() => this.initializeCountdownInterval());
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
start() {
this.countdownSubscription?.unsubscribe();
this.start$.next(null);
}
stop(stopAtZero = false) {
this.countdownSubscription?.unsubscribe();
this.runSpinner$.next(false);
if (stopAtZero) {
this.secondsUntilRefresh$.next('0');
}
else {
this.secondsUntilRefresh$.next(`${Math.floor(this.countdownInterval / 1000)}`);
}
}
reset() {
this.stop();
this.start();
}
/**
* Initializes the countdown timer.
* Kicks off the timer logic and activates any associated UI elements.
*/
initializeCountdownInterval() {
const timerStart$ = new Subject();
const documentHiddenEvent$ = this.createDocumentHiddenEventStream();
const interval$ = this.createIntervalStream(timerStart$, documentHiddenEvent$);
this.subscribeToInterval(interval$);
timerStart$.next();
this.runSpinner$.next(true);
}
/**
* Creates an Observable stream that emits events when the document's visibility changes.
* @returns An Observable emitting `null` whenever the visibility of the document changes.
*/
createDocumentHiddenEventStream() {
return fromEvent(document, 'visibilitychange').pipe(mapTo(null), takeUntil(this.destroy$));
}
/**
* Creates the main interval Observable for the countdown timer.
* @param timerStart$ - A Subject to signal the start of the timer.
* @param documentHiddenEvent$ - An Observable for document visibility changes.
* @returns An Observable that orchestrates the timer logic.
*/
createIntervalStream(timerStart$, documentHiddenEvent$) {
return timerStart$.pipe(switchMap(() => this.createTimer()), switchMap(() => (document.hidden ? documentHiddenEvent$ : of(null))), takeUntil(this.destroy$));
}
/**
* Creates a timer Observable that emits every second.
* @returns An Observable that emits a number every second, starting from 0.
*/
createTimer() {
return timer(0, this.ONE_SECOND).pipe(tap(value => this.updateRemainingTime(value)), filter(value => value >= this.countdownInterval / this.ONE_SECOND), first());
}
/**
* Updates the remaining time in the countdown.
* @param value - The current timer value in seconds.
*/
updateRemainingTime(value) {
const remainingSeconds = Math.floor(this.countdownInterval / this.ONE_SECOND - value);
this.secondsUntilRefresh$.next(`${remainingSeconds}`);
}
/**
* Subscribes to the interval Observable and sets up the event handlers for the countdown.
* @param interval$ - The countdown interval Observable to subscribe to.
*/
subscribeToInterval(interval$) {
this.countdownSubscription = interval$.pipe(takeUntil(this.destroy$)).subscribe(() => {
this.countdownEnded.emit();
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CountdownIntervalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: CountdownIntervalComponent, selector: "c8y-countdown-interval", inputs: { countdownInterval: "countdownInterval" }, outputs: { countdownEnded: "countdownEnded" }, ngImport: i0, template: "<div\n class=\"time-elapsed\"\n data-cy=\"c8y-countdown-interval--countdown\"\n [style.--timescope]=\"countdownInterval + 'ms'\"\n [title]=\"'{{ secondsLeft }} s / {{ interval }} s' | translate: { secondsLeft: secondsUntilRefresh$ | async, interval: countdownInterval / 1000 }\"\n>\n <svg\n [ngClass]=\"{ 'time-on': runSpinner$ | async }\"\n viewBox=\"0 0 40 40\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <circle\n cx=\"20\"\n cy=\"20\"\n r=\"15.8\"\n stroke=\"var(--c8y-brand-primary)\"\n stroke-width=\"6\"\n />\n </svg>\n\n <span>{{ secondsUntilRefresh$ | async }}</span>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CountdownIntervalComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-countdown-interval', template: "<div\n class=\"time-elapsed\"\n data-cy=\"c8y-countdown-interval--countdown\"\n [style.--timescope]=\"countdownInterval + 'ms'\"\n [title]=\"'{{ secondsLeft }} s / {{ interval }} s' | translate: { secondsLeft: secondsUntilRefresh$ | async, interval: countdownInterval / 1000 }\"\n>\n <svg\n [ngClass]=\"{ 'time-on': runSpinner$ | async }\"\n viewBox=\"0 0 40 40\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <circle\n cx=\"20\"\n cy=\"20\"\n r=\"15.8\"\n stroke=\"var(--c8y-brand-primary)\"\n stroke-width=\"6\"\n />\n </svg>\n\n <span>{{ secondsUntilRefresh$ | async }}</span>\n</div>\n" }]
}], propDecorators: { countdownInterval: [{
type: Input
}], countdownEnded: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY291bnRkb3duLWludGVydmFsLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2NvcmUvY291bnRkb3duLWludGVydmFsL2NvdW50ZG93bi1pbnRlcnZhbC5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9jb3JlL2NvdW50ZG93bi1pbnRlcnZhbC9jb3VudGRvd24taW50ZXJ2YWwuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsS0FBSyxFQUFxQixNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDMUYsT0FBTyxFQUFFLGVBQWUsRUFBRSxTQUFTLEVBQWMsRUFBRSxFQUFFLE9BQU8sRUFBZ0IsS0FBSyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ2hHLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQzs7OztBQUUvRjs7O0dBR0c7QUFLSCxNQUFNLE9BQU8sMEJBQTBCO0lBSnZDO1FBZUU7O1dBRUc7UUFDTyxtQkFBYyxHQUFHLElBQUksWUFBWSxFQUFRLENBQUM7UUFFcEQsZ0JBQVcsR0FBRyxJQUFJLGVBQWUsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUlqQyxhQUFRLEdBQUcsSUFBSSxPQUFPLEVBQVEsQ0FBQztRQUN0QixxQkFBZ0IsR0FBRyxLQUFLLENBQUM7UUFDekIsZUFBVSxHQUFHLElBQUksQ0FBQyxDQUFDLDZCQUE2QjtRQUN6RCxXQUFNLEdBQUcsSUFBSSxPQUFPLEVBQUUsQ0FBQztLQTZHaEM7SUFuSUM7O09BRUc7SUFDSCxJQUFhLGlCQUFpQixDQUFDLEtBQWE7UUFDMUMsSUFBSSxDQUFDLGtCQUFrQixHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxnQkFBZ0Is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IENvbXBvbmVudCwgRXZlbnRFbWl0dGVyLCBJbnB1dCwgT25EZXN0cm95LCBPbkluaXQsIE91dHB1dCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBmcm9tRXZlbnQsIE9ic2VydmFibGUsIG9mLCBTdWJqZWN0LCBTdWJzY3JpcHRpb24sIHRpbWVyIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBkZWJvdW5jZVRpbWUsIGZpbHRlciwgZmlyc3QsIG1hcFRvLCBzd2l0Y2hNYXAsIHRha2VVbnRpbCwgdGFwIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuXG4vKipcbiAqIENvbXBvbmVudCB0byBjb3VudCBkb3duIHNwZWNpZmljIHRpbWUgaW50ZXJ2YWwsIHRoZW4gZW1pdCB3aGVuIGNvdW50ZG93biBlbmRzLlxuICogTmV4dCBjb3VudGRvd24gaXMgc3RhcnRlZCByaWdodCBhZnRlciBwcmV2aW91cyBvbmUuXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2M4eS1jb3VudGRvd24taW50ZXJ2YWwnLFxuICB0ZW1wbGF0ZVVybDogJy4vY291bnRkb3duLWludGVydmFsLmNvbXBvbmVudC5odG1sJ1xufSlcbmV4cG9ydCBjbGFzcyBDb3VudGRvd25JbnRlcnZhbENvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25EZXN0cm95IHtcbiAgLyoqXG4gICAqIFRpbWUgaW4gbWlsbGlzZWNvbmRzIHRvIGNvdW50IGRvd24gZnJvbS5cbiAgICovXG4gIEBJbnB1dCgpIHNldCBjb3VudGRvd25JbnRlcnZhbCh2YWx1ZTogbnVtYmVyKSB7XG4gICAgdGhpcy5fY291bnRkb3duSW50ZXJ2YWwgPSBNYXRoLm1heCh2YWx1ZSwgdGhpcy5NSU5JTVVNX0lOVEVSVkFMKTtcbiAgfVxuXG4gIGdldCBjb3VudGRvd25JbnRlcnZhbCgpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLl9jb3VudGRvd25JbnRlcnZhbCB8fCB0aGlzLk1JTklNVU1fSU5URVJWQUw7XG4gIH1cbiAgLyoqXG4gICAqIEVtaXRzIHdoZW4gY291bnRkb3duIGVuZHMuXG4gICAqL1xuICBAT3V0cHV0KCkgY291bnRkb3duRW5kZWQgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG5cbiAgcnVuU3Bpbm5lciQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0KGZhbHNlKTtcbiAgc2Vjb25kc1VudGlsUmVmcmVzaCQ6IEJlaGF2aW9yU3ViamVjdDxzdHJpbmc+O1xuXG4gIHByaXZhdGUgX2NvdW50ZG93bkludGVydmFsOiBudW1iZXI7XG4gIHByaXZhdGUgZGVzdHJveSQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuICBwcml2YXRlIHJlYWRvbmx5IE1JTklNVU1fSU5URVJWQUwgPSA1XzAwMDtcbiAgcHJpdmF0ZSByZWFkb25seSBPTkVfU0VDT05EID0gMTAwMDsgLy8gT25lIHNlY29uZCBpbiBtaWxsaXNlY29uZHNcbiAgcHJpdmF0ZSBzdGFydCQgPSBuZXcgU3ViamVjdCgpO1xuICBwcml2YXRlIGNvdW50ZG93blN1YnNjcmlwdGlvbjogU3Vic2NyaXB0aW9uO1xuXG4gIG5nT25Jbml0KCk6IHZvaWQge1xuICAgIHRoaXMuc2Vjb25kc1VudGlsUmVmcmVzaCQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0KGAke01hdGguZmxvb3IodGhpcy5jb3VudGRvd25JbnRlcnZhbCAvIDEwMDApfWApO1xuICAgIHRoaXMuc3RhcnQkXG4gICAgICAucGlwZShkZWJvdW5jZVRpbWUoMTAwKSwgdGFrZVVudGlsKHRoaXMuZGVzdHJveSQpKVxuICAgICAgLnN1YnNjcmliZSgoKSA9PiB0aGlzLmluaXRpYWxpemVDb3VudGRvd25JbnRlcnZhbCgpKTtcbiAgfVxuXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIHRoaXMuZGVzdHJveSQubmV4dCgpO1xuICAgIHRoaXMuZGVzdHJveSQuY29tcGxldGUoKTtcbiAgfVxuXG4gIHN0YXJ0KCk6IHZvaWQge1xuICAgIHRoaXMuY291bnRkb3duU3Vic2NyaXB0aW9uPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMuc3RhcnQkLm5leHQobnVsbCk7XG4gIH1cblxuICBzdG9wKHN0b3BBdFplcm8gPSBmYWxzZSk6IHZvaWQge1xuICAgIHRoaXMuY291bnRkb3duU3Vic2NyaXB0aW9uPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMucnVuU3Bpbm5lciQubmV4dChmYWxzZSk7XG4gICAgaWYgKHN0b3BBdFplcm8pIHtcbiAgICAgIHRoaXMuc2Vjb25kc1VudGlsUmVmcmVzaCQubmV4dCgnMCcpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnNlY29uZHNVbnRpbFJlZnJlc2gkLm5leHQoYCR7TWF0aC5mbG9vcih0aGlzLmNvdW50ZG93bkludGVydmFsIC8gMTAwMCl9YCk7XG4gICAgfVxuICB9XG5cbiAgcmVzZXQoKTogdm9pZCB7XG4gICAgdGhpcy5zdG9wKCk7XG4gICAgdGhpcy5zdGFydCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEluaXRpYWxpemVzIHRoZSBjb3VudGRvd24gdGltZXIuXG4gICAqIEtpY2tzIG9mZiB0aGUgdGltZXIgbG9naWMgYW5kIGFjdGl2YXRlcyBhbnkgYXNzb2NpYXRlZCBVSSBlbGVtZW50cy5cbiAgICovXG4gIHByaXZhdGUgaW5pdGlhbGl6ZUNvdW50ZG93bkludGVydmFsKCk6IHZvaWQge1xuICAgIGNvbnN0IHRpbWVyU3RhcnQkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcbiAgICBjb25zdCBkb2N1bWVudEhpZGRlbkV2ZW50JCA9IHRoaXMuY3JlYXRlRG9jdW1lbnRIaWRkZW5FdmVudFN0cmVhbSgpO1xuXG4gICAgY29uc3QgaW50ZXJ2YWwkID0gdGhpcy5jcmVhdGVJbnRlcnZhbFN0cmVhbSh0aW1lclN0YXJ0JCwgZG9jdW1lbnRIaWRkZW5FdmVudCQpO1xuXG4gICAgdGhpcy5zdWJzY3JpYmVUb0ludGVydmFsKGludGVydmFsJCk7XG5cbiAgICB0aW1lclN0YXJ0JC5uZXh0KCk7XG4gICAgdGhpcy5ydW5TcGlubmVyJC5uZXh0KHRydWUpO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYW4gT2JzZXJ2YWJsZSBzdHJlYW0gdGhhdCBlbWl0cyBldmVudHMgd2hlbiB0aGUgZG9jdW1lbnQncyB2aXNpYmlsaXR5IGNoYW5nZXMuXG4gICAqIEByZXR1cm5zIEFuIE9ic2VydmFibGUgZW1pdHRpbmcgYG51bGxgIHdoZW5ldmVyIHRoZSB2aXNpYmlsaXR5IG9mIHRoZSBkb2N1bWVudCBjaGFuZ2VzLlxuICAgKi9cbiAgcHJpdmF0ZSBjcmVhdGVEb2N1bWVudEhpZGRlbkV2ZW50U3RyZWFtKCk6IE9ic2VydmFibGU8bnVsbD4ge1xuICAgIHJldHVybiBmcm9tRXZlbnQ8bnVsbD4oZG9jdW1lbnQsICd2aXNpYmlsaXR5Y2hhbmdlJykucGlwZShcbiAgICAgIG1hcFRvKG51bGwpLFxuICAgICAgdGFrZVVudGlsKHRoaXMuZGVzdHJveSQpXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIHRoZSBtYWluIGludGVydmFsIE9ic2VydmFibGUgZm9yIHRoZSBjb3VudGRvd24gdGltZXIuXG4gICAqIEBwYXJhbSB0aW1lclN0YXJ0JCAtIEEgU3ViamVjdCB0byBzaWduYWwgdGhlIHN0YXJ0IG9mIHRoZSB0aW1lci5cbiAgICogQHBhcmFtIGRvY3VtZW50SGlkZGVuRXZlbnQkIC0gQW4gT2JzZXJ2YWJsZSBmb3IgZG9jdW1lbnQgdmlzaWJpbGl0eSBjaGFuZ2VzLlxuICAgKiBAcmV0dXJucyBBbiBPYnNlcnZhYmxlIHRoYXQgb3JjaGVzdHJhdGVzIHRoZSB0aW1lciBsb2dpYy5cbiAgICovXG4gIHByaXZhdGUgY3JlYXRlSW50ZXJ2YWxTdHJlYW0oXG4gICAgdGltZXJTdGFydCQ6IFN1YmplY3Q8dm9pZD4sXG4gICAgZG9jdW1lbnRIaWRkZW5FdmVudCQ6IE9ic2VydmFibGU8bnVsbD5cbiAgKTogT2JzZXJ2YWJsZTxudWxsPiB7XG4gICAgcmV0dXJuIHRpbWVyU3RhcnQkLnBpcGUoXG4gICAgICBzd2l0Y2hNYXAoKCkgPT4gdGhpcy5jcmVhdGVUaW1lcigpKSxcbiAgICAgIHN3aXRjaE1hcCgoKSA9PiAoZG9jdW1lbnQuaGlkZGVuID8gZG9jdW1lbnRIaWRkZW5FdmVudCQgOiBvZihudWxsKSkpLFxuICAgICAgdGFrZVVudGlsKHRoaXMuZGVzdHJveSQpXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgdGltZXIgT2JzZXJ2YWJsZSB0aGF0IGVtaXRzIGV2ZXJ5IHNlY29uZC5cbiAgICogQHJldHVybnMgQW4gT2JzZXJ2YWJsZSB0aGF0IGVtaXRzIGEgbnVtYmVyIGV2ZXJ5IHNlY29uZCwgc3RhcnRpbmcgZnJvbSAwLlxuICAgKi9cbiAgcHJpdmF0ZSBjcmVhdGVUaW1lcigpOiBPYnNlcnZhYmxlPG51bWJlcj4ge1xuICAgIHJldHVybiB0aW1lcigwLCB0aGlzLk9ORV9TRUNPTkQpLnBpcGUoXG4gICAgICB0YXAodmFsdWUgPT4gdGhpcy51cGRhdGVSZW1haW5pbmdUaW1lKHZhbHVlKSksXG4gICAgICBmaWx0ZXIodmFsdWUgPT4gdmFsdWUgPj0gdGhpcy5jb3VudGRvd25JbnRlcnZhbCAvIHRoaXMuT05FX1NFQ09ORCksXG4gICAgICBmaXJzdCgpXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGVzIHRoZSByZW1haW5pbmcgdGltZSBpbiB0aGUgY291bnRkb3duLlxuICAgKiBAcGFyYW0gdmFsdWUgLSBUaGUgY3VycmVudCB0aW1lciB2YWx1ZSBpbiBzZWNvbmRzLlxuICAgKi9cbiAgcHJpdmF0ZSB1cGRhdGVSZW1haW5pbmdUaW1lKHZhbHVlOiBudW1iZXIpOiB2b2lkIHtcbiAgICBjb25zdCByZW1haW5pbmdTZWNvbmRzID0gTWF0aC5mbG9vcih0aGlzLmNvdW50ZG93bkludGVydmFsIC8gdGhpcy5PTkVfU0VDT05EIC0gdmFsdWUpO1xuICAgIHRoaXMuc2Vjb25kc1VudGlsUmVmcmVzaCQubmV4dChgJHtyZW1haW5pbmdTZWNvbmRzfWApO1xuICB9XG5cbiAgLyoqXG4gICAqIFN1YnNjcmliZXMgdG8gdGhlIGludGVydmFsIE9ic2VydmFibGUgYW5kIHNldHMgdXAgdGhlIGV2ZW50IGhhbmRsZXJzIGZvciB0aGUgY291bnRkb3duLlxuICAgKiBAcGFyYW0gaW50ZXJ2YWwkIC0gVGhlIGNvdW50ZG93biBpbnRlcnZhbCBPYnNlcnZhYmxlIHRvIHN1YnNjcmliZSB0by5cbiAgICovXG4gIHByaXZhdGUgc3Vic2NyaWJlVG9JbnRlcnZhbChpbnRlcnZhbCQ6IE9ic2VydmFibGU8bnVsbD4pOiB2b2lkIHtcbiAgICB0aGlzLmNvdW50ZG93blN1YnNjcmlwdGlvbiA9IGludGVydmFsJC5waXBlKHRha2VVbnRpbCh0aGlzLmRlc3Ryb3kkKSkuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgIHRoaXMuY291bnRkb3duRW5kZWQuZW1pdCgpO1xuICAgIH0pO1xuICB9XG59XG4iLCI8ZGl2XG4gIGNsYXNzPVwidGltZS1lbGFwc2VkXCJcbiAgZGF0YS1jeT1cImM4eS1jb3VudGRvd24taW50ZXJ2YWwtLWNvdW50ZG93blwiXG4gIFtzdHlsZS4tLXRpbWVzY29wZV09XCJjb3VudGRvd25JbnRlcnZhbCArICdtcydcIlxuICBbdGl0bGVdPVwiJ3t7IHNlY29uZHNMZWZ0IH19IHMgLyB7eyBpbnRlcnZhbCB9fSBzJyB8IHRyYW5zbGF0ZTogeyBzZWNvbmRzTGVmdDogc2Vjb25kc1VudGlsUmVmcmVzaCQgfCBhc3luYywgaW50ZXJ2YWw6IGNvdW50ZG93bkludGVydmFsIC8gMTAwMCB9XCJcbj5cbiAgPHN2Z1xuICAgIFtuZ0NsYXNzXT1cInsgJ3RpbWUtb24nOiBydW5TcGlubmVyJCB8IGFzeW5jIH1cIlxuICAgIHZpZXdCb3g9XCIwIDAgNDAgNDBcIlxuICAgIGZpbGw9XCJub25lXCJcbiAgICB4bWxucz1cImh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnXCJcbiAgPlxuICAgIDxjaXJjbGVcbiAgICAgIGN4PVwiMjBcIlxuICAgICAgY3k9XCIyMFwiXG4gICAgICByPVwiMTUuOFwiXG4gICAgICBzdHJva2U9XCJ2YXIoLS1jOHktYnJhbmQtcHJpbWFyeSlcIlxuICAgICAgc3Ryb2tlLXdpZHRoPVwiNlwiXG4gICAgLz5cbiAgPC9zdmc+XG5cbiAgPHNwYW4+e3sgc2Vjb25kc1VudGlsUmVmcmVzaCQgfCBhc3luYyB9fTwvc3Bhbj5cbjwvZGl2PlxuIl19