UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

123 lines 19.2 kB
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