@ux-aspects/ux-aspects
Version:
Open source user interface framework for building modern, responsive, mobile big data applications
90 lines • 21.5 kB
JavaScript
import { Component, inject, ViewChild } from '@angular/core';
import { fromEvent, Subject } from 'rxjs';
import { switchMap, takeUntil } from 'rxjs/operators';
import { MediaPlayerService } from '../../media-player.service';
import * as i0 from "@angular/core";
import * as i1 from "../../../tooltip/tooltip.directive";
import * as i2 from "@angular/common";
import * as i3 from "../../../../pipes/duration/duration.pipe";
export class MediaPlayerTimelineExtensionComponent {
constructor() {
this.mediaPlayerService = inject(MediaPlayerService);
this.current = 0;
this.position = 0;
this.buffered = [];
this.mouseDown = false;
this.scrub = { visible: false, position: 0, time: 0 };
this._onDestroy = new Subject();
}
ngOnInit() {
// watch for changes to the current time
this.mediaPlayerService.fullscreenEvent.pipe(takeUntil(this._onDestroy)).subscribe(() => {
this.scrub.position = 0;
});
this.mediaPlayerService.timeUpdateEvent.pipe(takeUntil(this._onDestroy)).subscribe(current => {
this.current = current;
this.position = (this.current / this.mediaPlayerService.duration) * 100;
});
this.mediaPlayerService.progressEvent.pipe(takeUntil(this._onDestroy)).subscribe((buffered) => {
this.buffered = [];
for (let idx = 0; idx < buffered.length; idx++) {
this.buffered.push({
start: (buffered.start(idx) / this.mediaPlayerService.duration) * 100,
end: (buffered.end(idx) / this.mediaPlayerService.duration) * 100
});
}
});
}
ngAfterViewInit() {
const mousedown$ = fromEvent(this.thumb.nativeElement, 'mousedown');
const mousemove$ = fromEvent(document, 'mousemove');
const mouseup$ = fromEvent(document, 'mouseup');
mousedown$.pipe(switchMap(() => mousemove$.pipe(takeUntil(mouseup$))), takeUntil(this._onDestroy)).subscribe(() => this.scrub.visible = false);
}
ngOnDestroy() {
this._onDestroy.next();
this._onDestroy.complete();
}
updateScrub(event) {
const target = event.target;
if (target.classList.contains('media-progress-bar-thumb')) {
return;
}
const timeline = this.timelineRef.nativeElement;
const bounds = timeline.getBoundingClientRect();
this.scrub.position = event.offsetX;
this.scrub.time = (event.offsetX / bounds.width) * this.mediaPlayerService.duration;
if (this.mouseDown) {
this.mediaPlayerService.pause();
this.mediaPlayerService.currentTime = this.scrub.time;
}
}
/** Skip a number of seconds in any direction */
skip(seconds) {
let target = this.current + seconds;
// ensure that the target position is within the bounds of the clip
if (target < 0) {
target = 0;
}
if (target > this.mediaPlayerService.duration) {
target = this.mediaPlayerService.duration;
}
this.mediaPlayerService.currentTime = target;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: MediaPlayerTimelineExtensionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", type: MediaPlayerTimelineExtensionComponent, selector: "ux-media-player-timeline", host: { listeners: { "document:mouseup": "mouseDown = false" }, properties: { "class.quiet": "mediaPlayerService.quietMode || mediaPlayerService.fullscreen" } }, viewQueries: [{ propertyName: "thumb", first: true, predicate: ["progressThumb"], descendants: true, static: true }, { propertyName: "timelineRef", first: true, predicate: ["timeline"], descendants: true, static: true }], ngImport: i0, template: "<p class=\"current-time\">{{ current | duration }}</p>\n\n<div #timeline\n class=\"timeline-bar\"\n tabindex=\"0\"\n role=\"slider\"\n [attr.aria-label]=\"mediaPlayerService.seekAriaLabel\"\n aria-valuemin=\"0\"\n [attr.aria-valuemax]=\"mediaPlayerService.duration | number: '0.0-0'\"\n [attr.aria-valuenow]=\"mediaPlayerService.currentTime | number: '0.0-0'\"\n attr.aria-valuetext=\"{{ mediaPlayerService.currentTime | duration }} of {{ mediaPlayerService.duration | duration }}\"\n (keydown.ArrowLeft)=\"skip(-5)\"\n (keydown.ArrowRight)=\"skip(5)\"\n (mouseenter)=\"scrub.visible = true; tooltip.show()\"\n (mouseleave)=\"scrub.visible = false; tooltip.hide()\"\n (mousemove)=\"updateScrub($event); tooltip.reposition()\"\n (mouseup)=\"updateScrub($event)\"\n (mousedown)=\"mouseDown = true; $event.preventDefault()\">\n\n @for (buffer of buffered; track buffer) {\n <div class=\"buffered-bar\"\n [style.left.%]=\"buffer.start\"\n [style.width.%]=\"buffer.end - buffer.start\">\n </div>\n }\n\n <div class=\"media-progress-bar\" [style.width.%]=\"position\">\n <div #progressThumb\n class=\"media-progress-bar-thumb\"\n (mouseenter)=\"scrub.visible = false; tooltip.hide(); $event.stopPropagation()\"\n (mouseleave)=\"scrub.visible = true; tooltip.show(); $event.stopPropagation()\">\n </div>\n </div>\n\n <div #tooltip=\"ux-tooltip\"\n class=\"scrub-handle\"\n [class.scrub-handle-hidden]=\"!scrub.visible\"\n [style.left.px]=\"scrub.position\"\n [uxTooltip]=\"popTemplate\"\n tooltipClass=\"ux-media-player-timeline-tooltip\"\n placement=\"top\"\n [showTriggers]=\"[]\"\n [hideTriggers]=\"[]\"\n [tooltipDelay]=\"100\"\n [tooltipDisabled]=\"mediaPlayerService.duration === 0\"></div>\n</div>\n\n<p class=\"duration-time\">{{ mediaPlayerService.duration | duration }}</p>\n\n<ng-template #popTemplate>\n <span>{{ scrub.time | duration }}</span>\n</ng-template>", dependencies: [{ kind: "directive", type: i1.TooltipDirective, selector: "[uxTooltip]", inputs: ["uxTooltip", "tooltipDisabled", "tooltipClass", "tooltipRole", "tooltipContext", "tooltipDelay", "isOpen", "placement", "fallbackPlacement", "alignment", "showTriggers", "hideTriggers"], outputs: ["shown", "hidden", "isOpenChange"], exportAs: ["ux-tooltip"] }, { kind: "pipe", type: i2.DecimalPipe, name: "number" }, { kind: "pipe", type: i3.DurationPipe, name: "duration" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: MediaPlayerTimelineExtensionComponent, decorators: [{
type: Component,
args: [{ selector: 'ux-media-player-timeline', host: {
'(document:mouseup)': 'mouseDown = false',
'[class.quiet]': 'mediaPlayerService.quietMode || mediaPlayerService.fullscreen'
}, template: "<p class=\"current-time\">{{ current | duration }}</p>\n\n<div #timeline\n class=\"timeline-bar\"\n tabindex=\"0\"\n role=\"slider\"\n [attr.aria-label]=\"mediaPlayerService.seekAriaLabel\"\n aria-valuemin=\"0\"\n [attr.aria-valuemax]=\"mediaPlayerService.duration | number: '0.0-0'\"\n [attr.aria-valuenow]=\"mediaPlayerService.currentTime | number: '0.0-0'\"\n attr.aria-valuetext=\"{{ mediaPlayerService.currentTime | duration }} of {{ mediaPlayerService.duration | duration }}\"\n (keydown.ArrowLeft)=\"skip(-5)\"\n (keydown.ArrowRight)=\"skip(5)\"\n (mouseenter)=\"scrub.visible = true; tooltip.show()\"\n (mouseleave)=\"scrub.visible = false; tooltip.hide()\"\n (mousemove)=\"updateScrub($event); tooltip.reposition()\"\n (mouseup)=\"updateScrub($event)\"\n (mousedown)=\"mouseDown = true; $event.preventDefault()\">\n\n @for (buffer of buffered; track buffer) {\n <div class=\"buffered-bar\"\n [style.left.%]=\"buffer.start\"\n [style.width.%]=\"buffer.end - buffer.start\">\n </div>\n }\n\n <div class=\"media-progress-bar\" [style.width.%]=\"position\">\n <div #progressThumb\n class=\"media-progress-bar-thumb\"\n (mouseenter)=\"scrub.visible = false; tooltip.hide(); $event.stopPropagation()\"\n (mouseleave)=\"scrub.visible = true; tooltip.show(); $event.stopPropagation()\">\n </div>\n </div>\n\n <div #tooltip=\"ux-tooltip\"\n class=\"scrub-handle\"\n [class.scrub-handle-hidden]=\"!scrub.visible\"\n [style.left.px]=\"scrub.position\"\n [uxTooltip]=\"popTemplate\"\n tooltipClass=\"ux-media-player-timeline-tooltip\"\n placement=\"top\"\n [showTriggers]=\"[]\"\n [hideTriggers]=\"[]\"\n [tooltipDelay]=\"100\"\n [tooltipDisabled]=\"mediaPlayerService.duration === 0\"></div>\n</div>\n\n<p class=\"duration-time\">{{ mediaPlayerService.duration | duration }}</p>\n\n<ng-template #popTemplate>\n <span>{{ scrub.time | duration }}</span>\n</ng-template>" }]
}], propDecorators: { thumb: [{
type: ViewChild,
args: ['progressThumb', { static: true }]
}], timelineRef: [{
type: ViewChild,
args: ['timeline', { static: true }]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZWxpbmUuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvbWVkaWEtcGxheWVyL2V4dGVuc2lvbnMvdGltZWxpbmUvdGltZWxpbmUuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvbWVkaWEtcGxheWVyL2V4dGVuc2lvbnMvdGltZWxpbmUvdGltZWxpbmUuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFpQixTQUFTLEVBQWMsTUFBTSxFQUFxQixTQUFTLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDM0csT0FBTyxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDMUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUN0RCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQzs7Ozs7QUFXaEUsTUFBTSxPQUFPLHFDQUFxQztJQVJsRDtRQVNhLHVCQUFrQixHQUFHLE1BQU0sQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO1FBS3pELFlBQU8sR0FBVyxDQUFDLENBQUM7UUFDcEIsYUFBUSxHQUFXLENBQUMsQ0FBQztRQUNyQixhQUFRLEdBQTBCLEVBQUUsQ0FBQztRQUNyQyxjQUFTLEdBQVksS0FBSyxDQUFDO1FBQzNCLFVBQUssR0FBRyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFFLENBQUM7UUFFaEMsZUFBVSxHQUFHLElBQUksT0FBTyxFQUFRLENBQUM7S0E4RXJEO0lBM0VHLFFBQVE7UUFFSix3Q0FBd0M7UUFDeEMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxVQUFV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aW5qZWN0LCBPbkRlc3Ryb3ksIE9uSW5pdCwgVmlld0NoaWxkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBmcm9tRXZlbnQsIFN1YmplY3QgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IHN3aXRjaE1hcCwgdGFrZVVudGlsIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgTWVkaWFQbGF5ZXJTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vbWVkaWEtcGxheWVyLnNlcnZpY2UnO1xuXG5cbkBDb21wb25lbnQoe1xuICAgIHNlbGVjdG9yOiAndXgtbWVkaWEtcGxheWVyLXRpbWVsaW5lJyxcbiAgICB0ZW1wbGF0ZVVybDogJy4vdGltZWxpbmUuY29tcG9uZW50Lmh0bWwnLFxuICAgIGhvc3Q6IHtcbiAgICAgICAgJyhkb2N1bWVudDptb3VzZXVwKSc6ICdtb3VzZURvd24gPSBmYWxzZScsXG4gICAgICAgICdbY2xhc3MucXVpZXRdJzogJ21lZGlhUGxheWVyU2VydmljZS5xdWlldE1vZGUgfHwgbWVkaWFQbGF5ZXJTZXJ2aWNlLmZ1bGxzY3JlZW4nXG4gICAgfVxufSlcbmV4cG9ydCBjbGFzcyBNZWRpYVBsYXllclRpbWVsaW5lRXh0ZW5zaW9uQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBBZnRlclZpZXdJbml0LCBPbkRlc3Ryb3kge1xuICAgIHJlYWRvbmx5IG1lZGlhUGxheWVyU2VydmljZSA9IGluamVjdChNZWRpYVBsYXllclNlcnZpY2UpO1xuXG4gICAgQFZpZXdDaGlsZCgncHJvZ3Jlc3NUaHVtYicsIHsgc3RhdGljOiB0cnVlIH0pIHRodW1iOiBFbGVtZW50UmVmO1xuICAgIEBWaWV3Q2hpbGQoJ3RpbWVsaW5lJywgeyBzdGF0aWM6IHRydWUgfSkgdGltZWxpbmVSZWY6IEVsZW1lbnRSZWY7XG5cbiAgICBjdXJyZW50OiBudW1iZXIgPSAwO1xuICAgIHBvc2l0aW9uOiBudW1iZXIgPSAwO1xuICAgIGJ1ZmZlcmVkOiBNZWRpYVBsYXllckJ1ZmZlcmVkW10gPSBbXTtcbiAgICBtb3VzZURvd246IGJvb2xlYW4gPSBmYWxzZTtcbiAgICBzY3J1YiA9IHsgdmlzaWJsZTogZmFsc2UsIHBvc2l0aW9uOiAwLCB0aW1lOiAwIH07XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9vbkRlc3Ryb3kgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG5cbiAgICBuZ09uSW5pdCgpOiB2b2lkIHtcblxuICAgICAgICAvLyB3YXRjaCBmb3IgY2hhbmdlcyB0byB0aGUgY3VycmVudCB0aW1lXG4gICAgICAgIHRoaXMubWVkaWFQbGF5ZXJTZXJ2aWNlLmZ1bGxzY3JlZW5FdmVudC5waXBlKHRha2VVbnRpbCh0aGlzLl9vbkRlc3Ryb3kpKS5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5zY3J1Yi5wb3NpdGlvbiA9IDA7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMubWVkaWFQbGF5ZXJTZXJ2aWNlLnRpbWVVcGRhdGVFdmVudC5waXBlKHRha2VVbnRpbCh0aGlzLl9vbkRlc3Ryb3kpKS5zdWJzY3JpYmUoY3VycmVudCA9PiB7XG4gICAgICAgICAgICB0aGlzLmN1cnJlbnQgPSBjdXJyZW50O1xuICAgICAgICAgICAgdGhpcy5wb3NpdGlvbiA9ICh0aGlzLmN1cnJlbnQgLyB0aGlzLm1lZGlhUGxheWVyU2VydmljZS5kdXJhdGlvbikgKiAxMDA7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMubWVkaWFQbGF5ZXJTZXJ2aWNlLnByb2dyZXNzRXZlbnQucGlwZSh0YWtlVW50aWwodGhpcy5fb25EZXN0cm95KSkuc3Vic2NyaWJlKChidWZmZXJlZDogVGltZVJhbmdlcykgPT4ge1xuICAgICAgICAgICAgdGhpcy5idWZmZXJlZCA9IFtdO1xuXG4gICAgICAgICAgICBmb3IgKGxldCBpZHggPSAwOyBpZHggPCBidWZmZXJlZC5sZW5ndGg7IGlkeCsrKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5idWZmZXJlZC5wdXNoKHtcbiAgICAgICAgICAgICAgICAgICAgc3RhcnQ6IChidWZmZXJlZC5zdGFydChpZHgpIC8gdGhpcy5tZWRpYVBsYXllclNlcnZpY2UuZHVyYXRpb24pICogMTAwLFxuICAgICAgICAgICAgICAgICAgICBlbmQ6IChidWZmZXJlZC5lbmQoaWR4KSAvIHRoaXMubWVkaWFQbGF5ZXJTZXJ2aWNlLmR1cmF0aW9uKSAqIDEwMFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBuZ0FmdGVyVmlld0luaXQoKTogdm9pZCB7XG4gICAgICAgIGNvbnN0IG1vdXNlZG93biQgPSBmcm9tRXZlbnQodGhpcy50aHVtYi5uYXRpdmVFbGVtZW50LCAnbW91c2Vkb3duJyk7XG4gICAgICAgIGNvbnN0IG1vdXNlbW92ZSQgPSBmcm9tRXZlbnQoZG9jdW1lbnQsICdtb3VzZW1vdmUnKTtcbiAgICAgICAgY29uc3QgbW91c2V1cCQgPSBmcm9tRXZlbnQoZG9jdW1lbnQsICdtb3VzZXVwJyk7XG5cbiAgICAgICAgbW91c2Vkb3duJC5waXBlKFxuICAgICAgICAgICAgc3dpdGNoTWFwKCgpID0+IG1vdXNlbW92ZSQucGlwZSh0YWtlVW50aWwobW91c2V1cCQpKSksXG4gICAgICAgICAgICB0YWtlVW50aWwodGhpcy5fb25EZXN0cm95KVxuICAgICAgICApLnN1YnNjcmliZSgoKSA9PiB0aGlzLnNjcnViLnZpc2libGUgPSBmYWxzZSk7XG4gICAgfVxuXG4gICAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgICAgIHRoaXMuX29uRGVzdHJveS5uZXh0KCk7XG4gICAgICAgIHRoaXMuX29uRGVzdHJveS5jb21wbGV0ZSgpO1xuICAgIH1cblxuICAgIHVwZGF0ZVNjcnViKGV2ZW50OiBNb3VzZUV2ZW50KTogdm9pZCB7XG5cbiAgICAgICAgY29uc3QgdGFyZ2V0ID0gZXZlbnQudGFyZ2V0IGFzIEhUTUxFbGVtZW50O1xuXG4gICAgICAgIGlmICh0YXJnZXQuY2xhc3NMaXN0LmNvbnRhaW5zKCdtZWRpYS1wcm9ncmVzcy1iYXItdGh1bWInKSkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgdGltZWxpbmUgPSB0aGlzLnRpbWVsaW5lUmVmLm5hdGl2ZUVsZW1lbnQgYXMgSFRNTERpdkVsZW1lbnQ7XG4gICAgICAgIGNvbnN0IGJvdW5kcyA9IHRpbWVsaW5lLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXG4gICAgICAgIHRoaXMuc2NydWIucG9zaXRpb24gPSBldmVudC5vZmZzZXRYO1xuICAgICAgICB0aGlzLnNjcnViLnRpbWUgPSAoZXZlbnQub2Zmc2V0WCAvIGJvdW5kcy53aWR0aCkgKiB0aGlzLm1lZGlhUGxheWVyU2VydmljZS5kdXJhdGlvbjtcblxuICAgICAgICBpZiAodGhpcy5tb3VzZURvd24pIHtcbiAgICAgICAgICAgIHRoaXMubWVkaWFQbGF5ZXJTZXJ2aWNlLnBhdXNlKCk7XG4gICAgICAgICAgICB0aGlzLm1lZGlhUGxheWVyU2VydmljZS5jdXJyZW50VGltZSA9IHRoaXMuc2NydWIudGltZTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKiBTa2lwIGEgbnVtYmVyIG9mIHNlY29uZHMgaW4gYW55IGRpcmVjdGlvbiAqL1xuICAgIHNraXAoc2Vjb25kczogbnVtYmVyKTogdm9pZCB7XG4gICAgICAgIGxldCB0YXJnZXQgPSB0aGlzLmN1cnJlbnQgKyBzZWNvbmRzO1xuXG4gICAgICAgIC8vIGVuc3VyZSB0aGF0IHRoZSB0YXJnZXQgcG9zaXRpb24gaXMgd2l0aGluIHRoZSBib3VuZHMgb2YgdGhlIGNsaXBcbiAgICAgICAgaWYgKHRhcmdldCA8IDApIHtcbiAgICAgICAgICAgIHRhcmdldCA9IDA7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGFyZ2V0ID4gdGhpcy5tZWRpYVBsYXllclNlcnZpY2UuZHVyYXRpb24pIHtcbiAgICAgICAgICAgIHRhcmdldCA9IHRoaXMubWVkaWFQbGF5ZXJTZXJ2aWNlLmR1cmF0aW9uO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5tZWRpYVBsYXllclNlcnZpY2UuY3VycmVudFRpbWUgPSB0YXJnZXQ7XG4gICAgfVxufVxuXG5leHBvcnQgaW50ZXJmYWNlIE1lZGlhUGxheWVyQnVmZmVyZWQge1xuICAgIHN0YXJ0OiBudW1iZXI7XG4gICAgZW5kOiBudW1iZXI7XG59XG4iLCI8cCBjbGFzcz1cImN1cnJlbnQtdGltZVwiPnt7IGN1cnJlbnQgfCBkdXJhdGlvbiB9fTwvcD5cblxuPGRpdiAjdGltZWxpbmVcbiAgY2xhc3M9XCJ0aW1lbGluZS1iYXJcIlxuICB0YWJpbmRleD1cIjBcIlxuICByb2xlPVwic2xpZGVyXCJcbiAgW2F0dHIuYXJpYS1sYWJlbF09XCJtZWRpYVBsYXllclNlcnZpY2Uuc2Vla0FyaWFMYWJlbFwiXG4gIGFyaWEtdmFsdWVtaW49XCIwXCJcbiAgW2F0dHIuYXJpYS12YWx1ZW1heF09XCJtZWRpYVBsYXllclNlcnZpY2UuZHVyYXRpb24gfCBudW1iZXI6ICcwLjAtMCdcIlxuICBbYXR0ci5hcmlhLXZhbHVlbm93XT1cIm1lZGlhUGxheWVyU2VydmljZS5jdXJyZW50VGltZSB8IG51bWJlcjogJzAuMC0wJ1wiXG4gIGF0dHIuYXJpYS12YWx1ZXRleHQ9XCJ7eyBtZWRpYVBsYXllclNlcnZpY2UuY3VycmVudFRpbWUgfCBkdXJhdGlvbiB9fSBvZiB7eyBtZWRpYVBsYXllclNlcnZpY2UuZHVyYXRpb24gfCBkdXJhdGlvbiB9fVwiXG4gIChrZXlkb3duLkFycm93TGVmdCk9XCJza2lwKC01KVwiXG4gIChrZXlkb3duLkFycm93UmlnaHQpPVwic2tpcCg1KVwiXG4gIChtb3VzZWVudGVyKT1cInNjcnViLnZpc2libGUgPSB0cnVlOyB0b29sdGlwLnNob3coKVwiXG4gIChtb3VzZWxlYXZlKT1cInNjcnViLnZpc2libGUgPSBmYWxzZTsgdG9vbHRpcC5oaWRlKClcIlxuICAobW91c2Vtb3ZlKT1cInVwZGF0ZVNjcnViKCRldmVudCk7IHRvb2x0aXAucmVwb3NpdGlvbigpXCJcbiAgKG1vdXNldXApPVwidXBkYXRlU2NydWIoJGV2ZW50KVwiXG4gIChtb3VzZWRvd24pPVwibW91c2VEb3duID0gdHJ1ZTsgJGV2ZW50LnByZXZlbnREZWZhdWx0KClcIj5cblxuICBAZm9yIChidWZmZXIgb2YgYnVmZmVyZWQ7IHRyYWNrIGJ1ZmZlcikge1xuICAgIDxkaXYgY2xhc3M9XCJidWZmZXJlZC1iYXJcIlxuICAgICAgW3N0eWxlLmxlZnQuJV09XCJidWZmZXIuc3RhcnRcIlxuICAgICAgW3N0eWxlLndpZHRoLiVdPVwiYnVmZmVyLmVuZCAtIGJ1ZmZlci5zdGFydFwiPlxuICAgIDwvZGl2PlxuICB9XG5cbiAgPGRpdiBjbGFzcz1cIm1lZGlhLXByb2dyZXNzLWJhclwiIFtzdHlsZS53aWR0aC4lXT1cInBvc2l0aW9uXCI+XG4gICAgPGRpdiAjcHJvZ3Jlc3NUaHVtYlxuICAgICAgY2xhc3M9XCJtZWRpYS1wcm9ncmVzcy1iYXItdGh1bWJcIlxuICAgICAgKG1vdXNlZW50ZXIpPVwic2NydWIudmlzaWJsZSA9IGZhbHNlOyB0b29sdGlwLmhpZGUoKTsgJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCJcbiAgICAgIChtb3VzZWxlYXZlKT1cInNjcnViLnZpc2libGUgPSB0cnVlOyB0b29sdGlwLnNob3coKTsgJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCI+XG4gICAgPC9kaXY+XG4gIDwvZGl2PlxuXG4gIDxkaXYgI3Rvb2x0aXA9XCJ1eC10b29sdGlwXCJcbiAgICBjbGFzcz1cInNjcnViLWhhbmRsZVwiXG4gICAgW2NsYXNzLnNjcnViLWhhbmRsZS1oaWRkZW5dPVwiIXNjcnViLnZpc2libGVcIlxuICAgIFtzdHlsZS5sZWZ0LnB4XT1cInNjcnViLnBvc2l0aW9uXCJcbiAgICBbdXhUb29sdGlwXT1cInBvcFRlbXBsYXRlXCJcbiAgICB0b29sdGlwQ2xhc3M9XCJ1eC1tZWRpYS1wbGF5ZXItdGltZWxpbmUtdG9vbHRpcFwiXG4gICAgcGxhY2VtZW50PVwidG9wXCJcbiAgICBbc2hvd1RyaWdnZXJzXT1cIltdXCJcbiAgICBbaGlkZVRyaWdnZXJzXT1cIltdXCJcbiAgICBbdG9vbHRpcERlbGF5XT1cIjEwMFwiXG4gIFt0b29sdGlwRGlzYWJsZWRdPVwibWVkaWFQbGF5ZXJTZXJ2aWNlLmR1cmF0aW9uID09PSAwXCI+PC9kaXY+XG48L2Rpdj5cblxuPHAgY2xhc3M9XCJkdXJhdGlvbi10aW1lXCI+e3sgbWVkaWFQbGF5ZXJTZXJ2aWNlLmR1cmF0aW9uIHwgZHVyYXRpb24gfX08L3A+XG5cbjxuZy10ZW1wbGF0ZSAjcG9wVGVtcGxhdGU+XG4gIDxzcGFuPnt7IHNjcnViLnRpbWUgfCBkdXJhdGlvbiB9fTwvc3Bhbj5cbjwvbmctdGVtcGxhdGU+Il19