UNPKG

ion-video-player

Version:

HTML video player for Ionic 6 applications using angular

195 lines 39.4 kB
import { Component, ViewChild, Input, Output, EventEmitter } from '@angular/core'; import * as i0 from "@angular/core"; import * as i1 from "@angular/common"; import * as i2 from "@ionic/angular"; import * as i3 from "./pipes/seconds-to-hours.pipe"; export class IonVideoPlayerComponent { constructor() { this.isPlaying = false; this.volume = 100; //Output Events this.play = new EventEmitter(); this.pause = new EventEmitter(); this.ended = new EventEmitter(); this.volumechange = new EventEmitter(); this.playing = new EventEmitter(); this.error = new EventEmitter(); } ngOnChanges() { } setVolume() { if (this.volumeBar.nativeElement.value == '100') { this.volume = 1; } else { this.volume = parseFloat('0.' + (this.volumeBar.nativeElement.value)); } this.videoPlayer.nativeElement.volume = this.volume; } playPauseVideo() { if (this.videoPlayer.nativeElement.paused || this.videoPlayer.nativeElement.ended) { // Change the button to a pause button this.videoPlayer.nativeElement.play(); this.isPlaying = true; } else { // Change the button to a play button this.videoPlayer.nativeElement.pause(); this.isPlaying = false; } } // Stop the current media from playing, and return it to the start position stopVideo() { this.videoPlayer.nativeElement.pause(); this.isPlaying = false; if (this.videoPlayer.nativeElement.currentTime) this.videoPlayer.nativeElement.currentTime = 0; } // Toggles the media player's mute and unmute status muteVolume() { if (this.videoPlayer.nativeElement.muted) { this.videoPlayer.nativeElement.muted = false; this.volume = this.videoPlayer.nativeElement.volume; } else { this.videoPlayer.nativeElement.muted = true; this.volume = 0; } } // Replays the media currently loaded in the player replayVideo() { this.resetPlayer(); this.videoPlayer.nativeElement.play(); } // Update the progress bar updateProgressBar() { // Work out how much of the media has played via the duration and currentTime parameters var percentage = Math.floor((100 / this.videoPlayer.nativeElement.duration) * this.videoPlayer.nativeElement.currentTime); // Update the progress bar's value if (this.options.controls) { this.progressBar.nativeElement.value = percentage; } } seek(e) { var percent = e.offsetX / this.progressBar.nativeElement.offsetWidth; this.videoPlayer.nativeElement.currentTime = percent * this.videoPlayer.nativeElement.duration; e.target.value = Math.floor(percent / 100); e.target.innerHTML = this.progressBar.nativeElement.value + '% played'; } //replay Video from start resetPlayer() { this.progressBar.nativeElement.value = 0; // Move the media back to the start this.videoPlayer.nativeElement.currentTime = 0; } //Open Player in Full screen mode toggleFullScreen() { if (document.fullscreenElement) { document.exitFullscreen(); } else { if (this.playerContainer.nativeElement.requestFullscreen) { this.playerContainer.nativeElement.requestFullscreen(); } else if (this.playerContainer.nativeElement.webkitRequestFullscreen) { /* Safari */ this.playerContainer.nativeElement.webkitRequestFullscreen(); } else if (this.playerContainer.nativeElement.msRequestFullscreen) { /* IE11 */ this.playerContainer.nativeElement.msRequestFullscreen(); } } } async openPip() { try { if (!document.pictureInPictureElement) { this.videoPlayer.nativeElement.requestPictureInPicture(); } else { document.exitPictureInPicture(); } } catch (reason) { console.error(reason); } } // Fires when the audio/video has been started or is no longer paused onPlay() { console.log('onPlay'); this.play.emit(); } // Fires when the audio/video has been paused onPause() { console.log('onPause'); this.pause.emit(); } // Fires when the current playlist is ended onEnded() { console.log('onEnded'); this.ended.emit(); } // Fires when the volume has been changed onVolumechange() { console.log('onVolumechange'); this.volumechange.emit(); } onPlaying() { console.log('onPlaying'); this.playing.emit(); } onError() { console.log('onError'); this.error.emit(); } onProgress() { // this.videoPlayer.nativeElement.addEventListener('progress', () => { // const duration = this.videoPlayer.nativeElement.duration; // console.log(duration) // if (duration > 0) { // for (let i = 0; i < this.videoPlayer.nativeElement.buffered.length; i++) { // if ( // this.videoPlayer.nativeElement.buffered.start(this.videoPlayer.nativeElement.buffered.length - 1 - i) < // this.videoPlayer.nativeElement.currentTime // ) { // console.log( // (this.videoPlayer.nativeElement.buffered.end(this.videoPlayer.nativeElement.buffered.length - 1 - i) * 100) / duration // ); // break; // } // } // } // }); } } IonVideoPlayerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: IonVideoPlayerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); IonVideoPlayerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.1", type: IonVideoPlayerComponent, selector: "ion-video-player", inputs: { options: "options" }, outputs: { play: "play", pause: "pause", ended: "ended", volumechange: "volumechange", playing: "playing", error: "error" }, viewQueries: [{ propertyName: "playerContainer", first: true, predicate: ["playerContainer"], descendants: true }, { propertyName: "videoPlayer", first: true, predicate: ["player"], descendants: true }, { propertyName: "progressBar", first: true, predicate: ["progressBar"], descendants: true }, { propertyName: "volumeBar", first: true, predicate: ["volumeBar"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div id=\"player\" #playerContainer class=\"ion-video-player\">\n <video id=\"video-element\" #player\n (timeupdate)=\"updateProgressBar()\"\n [poster]=\"options.poster\"\n [autoplay]=\"options.autoplay\"\n [muted]=\"options.muted\"\n (play)=\"onPlay()\"\n (pause)=\"onPause()\"\n (ended)=\"onEnded()\"\n (volumechange)=\"onVolumechange()\"\n (playing)=\"onPlaying()\"\n (error)=\"onError()\"\n (progress)=\"onProgress()\">\n <source [src]=\"options.src\" [type]=\"options.type\">\n </video>\n\n <div id=\"controls\" *ngIf=\"options.controls\">\n\n <progress id=\"progress-bar\" (click)=\"seek($event)\" min=\"0\" max=\"100\" value='1' #progressBar></progress>\n\n <div class=\"controls-btns\">\n <div class=\"start-stop\">\n <ion-button fill=\"clear\" size=\"small\" slot=\"icon-only\" class='play' (click)=\"playPauseVideo()\">\n <ion-icon [name]=\"isPlaying ? 'pause' : 'play'\"></ion-icon>\n </ion-button>\n <span *ngIf=\"player.duration\">\n {{player.currentTime | secondsToHours}} / {{player.duration | secondsToHours }}\n </span>\n </div>\n\n <div class=\"volume-box\">\n <input type=\"range\" class=\"volumeRange\"\n (change)=\"setVolume()\" \n min=\"0\"\n max=\"100\"\n step=\"1\"\n [value]=\"volumeBar.value\"\n #volumeBar\n [ngStyle]=\"{\n 'background-size': volumeBar.value + '% 100%'\n }\"\n />\n\n <ion-button fill=\"clear\" size=\"small\" slot=\"icon-only\" class=\"mute\" title=\"mute\" (click)=\"muteVolume()\">\n <ion-icon [name]=\"\n volume == 0 ? 'volume-mute'\n :\n (volume > 0 && volume < 0.50) ? 'volume-low'\n :\n (volume > 0.50 && volume < 0.80) ? 'volume-medium'\n : 'volume-high'\">\n </ion-icon>\n </ion-button>\n\n <ion-button fill=\"clear\" size=\"small\" slot=\"icon-only\" (click)=\"toggleFullScreen()\">\n <ion-icon name=\"expand\"></ion-icon>\n </ion-button>\n\n <ion-button fill=\"clear\" size=\"small\" slot=\"icon-only\" (click)=\"openPip()\">\n <ion-icon name=\"open\"></ion-icon>\n </ion-button>\n <!-- <ion-button size=\"small\" slot=\"icon-only\" id=\"btnStop\" class=\"\" title=\"stop\" accesskey=\"X\" (click)=\"stopVideo()\">\n <ion-icon name=\"stop\"></ion-icon>\n </ion-button>\n <ion-button size=\"small\" slot=\"icon-only\" id=\"btnReplay\" class=\"replay\" title=\"replay\" accesskey=\"R\" (click)=\"replayVideo()\">\n <ion-icon name=\"reload\"></ion-icon>\n </ion-button> -->\n </div>\n </div>\n </div>\n</div>", styles: ["#player{position:relative}#player:hover #controls{visibility:visible}.ion-video-player{width:100%}.ion-video-player video{width:inherit;height:inherit}.stop{float:right;text-align:end}ion-button{color:#fff}#controls{background-color:#0000004f;color:#fff;width:100%;position:absolute;bottom:0;padding:0 5px;box-sizing:border-box;visibility:hidden}#controls .controls-btns{display:flex;align-items:center;justify-content:space-between}#controls .controls-btns .start-stop{display:flex;align-items:center;color:#fff;font-size:10px}#controls .controls-btns .start-stop ion-button{color:#fff}progress{height:8px;width:100%;border:1.5px solid #fff;border-radius:5px}progress[value]::-moz-progress-bar{background:rgba(54,47,47,.4);border-radius:5px;box-shadow:0 2px 5px #00000040 inset}progress[value]::-webkit-progress-bar{background-color:#362f2f66;border-radius:5px;box-shadow:0 2px 5px #00000040 inset}progress[value]::-webkit-progress-value{background-color:#e74747}progress[value]::-moz-progress-value{background-color:#e74747}.volume-box{display:flex;align-items:center}.volume-box input{display:inline-block;vertical-align:middle;font-size:1em}.volume-box output{display:inline-block;vertical-align:middle;font-size:1em;background:#ff4500;padding:5px 16px;border-radius:3px;color:#fff}.volume-box .volumeRange{display:none;background-color:transparent;-webkit-appearance:none;width:50px;height:7px;background:rgba(255,255,255,.6);border-radius:5px;background-image:linear-gradient(rgb(231,71,71),rgb(231,71,71));background-repeat:no-repeat}.volume-box .volumeRange::-webkit-slider-thumb{-webkit-appearance:none;height:10px;width:10px;border-radius:50%;background:red;cursor:ew-resize;box-shadow:0 0 2px #555;-webkit-transition:background .3s ease-in-out;transition:background .3s ease-in-out}.volume-box .volumeRange::-webkit-slider-thumb:hover{background:rgb(231,71,71)}.volume-box .volumeRange::-moz-range-thumb{-webkit-appearance:none;height:10px;width:10px;border-radius:50%;background:red;cursor:ew-resize;box-shadow:0 0 2px #555;-moz-transition:background .3s ease-in-out;transition:background .3s ease-in-out}.volume-box .volumeRange::-moz-range-thumb:hover{background:rgb(231,71,71)}.volume-box .volumeRange::-ms-thumb{-webkit-appearance:none;height:10px;width:10px;border-radius:50%;background:red;cursor:ew-resize;box-shadow:0 0 2px #555;-ms-transition:background .3s ease-in-out;transition:background .3s ease-in-out}.volume-box .volumeRange::-ms-thumb:hover{background:rgb(231,71,71)}.volume-box .volumeRange::-ms-track{-webkit-appearance:none;box-shadow:none;border:none;background:transparent}.volume-box .volumeRange::-webkit-slider-runnable-track{-webkit-appearance:none;box-shadow:none;border:none;background:transparent}.volume-box .volumeRange::-moz-range-track{-webkit-appearance:none;box-shadow:none;border:none;background:transparent}.volume-box:hover .volumeRange{display:block}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "pipe", type: i3.SecondsToHoursPipe, name: "secondsToHours" }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: IonVideoPlayerComponent, decorators: [{ type: Component, args: [{ selector: 'ion-video-player', template: "<div id=\"player\" #playerContainer class=\"ion-video-player\">\n <video id=\"video-element\" #player\n (timeupdate)=\"updateProgressBar()\"\n [poster]=\"options.poster\"\n [autoplay]=\"options.autoplay\"\n [muted]=\"options.muted\"\n (play)=\"onPlay()\"\n (pause)=\"onPause()\"\n (ended)=\"onEnded()\"\n (volumechange)=\"onVolumechange()\"\n (playing)=\"onPlaying()\"\n (error)=\"onError()\"\n (progress)=\"onProgress()\">\n <source [src]=\"options.src\" [type]=\"options.type\">\n </video>\n\n <div id=\"controls\" *ngIf=\"options.controls\">\n\n <progress id=\"progress-bar\" (click)=\"seek($event)\" min=\"0\" max=\"100\" value='1' #progressBar></progress>\n\n <div class=\"controls-btns\">\n <div class=\"start-stop\">\n <ion-button fill=\"clear\" size=\"small\" slot=\"icon-only\" class='play' (click)=\"playPauseVideo()\">\n <ion-icon [name]=\"isPlaying ? 'pause' : 'play'\"></ion-icon>\n </ion-button>\n <span *ngIf=\"player.duration\">\n {{player.currentTime | secondsToHours}} / {{player.duration | secondsToHours }}\n </span>\n </div>\n\n <div class=\"volume-box\">\n <input type=\"range\" class=\"volumeRange\"\n (change)=\"setVolume()\" \n min=\"0\"\n max=\"100\"\n step=\"1\"\n [value]=\"volumeBar.value\"\n #volumeBar\n [ngStyle]=\"{\n 'background-size': volumeBar.value + '% 100%'\n }\"\n />\n\n <ion-button fill=\"clear\" size=\"small\" slot=\"icon-only\" class=\"mute\" title=\"mute\" (click)=\"muteVolume()\">\n <ion-icon [name]=\"\n volume == 0 ? 'volume-mute'\n :\n (volume > 0 && volume < 0.50) ? 'volume-low'\n :\n (volume > 0.50 && volume < 0.80) ? 'volume-medium'\n : 'volume-high'\">\n </ion-icon>\n </ion-button>\n\n <ion-button fill=\"clear\" size=\"small\" slot=\"icon-only\" (click)=\"toggleFullScreen()\">\n <ion-icon name=\"expand\"></ion-icon>\n </ion-button>\n\n <ion-button fill=\"clear\" size=\"small\" slot=\"icon-only\" (click)=\"openPip()\">\n <ion-icon name=\"open\"></ion-icon>\n </ion-button>\n <!-- <ion-button size=\"small\" slot=\"icon-only\" id=\"btnStop\" class=\"\" title=\"stop\" accesskey=\"X\" (click)=\"stopVideo()\">\n <ion-icon name=\"stop\"></ion-icon>\n </ion-button>\n <ion-button size=\"small\" slot=\"icon-only\" id=\"btnReplay\" class=\"replay\" title=\"replay\" accesskey=\"R\" (click)=\"replayVideo()\">\n <ion-icon name=\"reload\"></ion-icon>\n </ion-button> -->\n </div>\n </div>\n </div>\n</div>", styles: ["#player{position:relative}#player:hover #controls{visibility:visible}.ion-video-player{width:100%}.ion-video-player video{width:inherit;height:inherit}.stop{float:right;text-align:end}ion-button{color:#fff}#controls{background-color:#0000004f;color:#fff;width:100%;position:absolute;bottom:0;padding:0 5px;box-sizing:border-box;visibility:hidden}#controls .controls-btns{display:flex;align-items:center;justify-content:space-between}#controls .controls-btns .start-stop{display:flex;align-items:center;color:#fff;font-size:10px}#controls .controls-btns .start-stop ion-button{color:#fff}progress{height:8px;width:100%;border:1.5px solid #fff;border-radius:5px}progress[value]::-moz-progress-bar{background:rgba(54,47,47,.4);border-radius:5px;box-shadow:0 2px 5px #00000040 inset}progress[value]::-webkit-progress-bar{background-color:#362f2f66;border-radius:5px;box-shadow:0 2px 5px #00000040 inset}progress[value]::-webkit-progress-value{background-color:#e74747}progress[value]::-moz-progress-value{background-color:#e74747}.volume-box{display:flex;align-items:center}.volume-box input{display:inline-block;vertical-align:middle;font-size:1em}.volume-box output{display:inline-block;vertical-align:middle;font-size:1em;background:#ff4500;padding:5px 16px;border-radius:3px;color:#fff}.volume-box .volumeRange{display:none;background-color:transparent;-webkit-appearance:none;width:50px;height:7px;background:rgba(255,255,255,.6);border-radius:5px;background-image:linear-gradient(rgb(231,71,71),rgb(231,71,71));background-repeat:no-repeat}.volume-box .volumeRange::-webkit-slider-thumb{-webkit-appearance:none;height:10px;width:10px;border-radius:50%;background:red;cursor:ew-resize;box-shadow:0 0 2px #555;-webkit-transition:background .3s ease-in-out;transition:background .3s ease-in-out}.volume-box .volumeRange::-webkit-slider-thumb:hover{background:rgb(231,71,71)}.volume-box .volumeRange::-moz-range-thumb{-webkit-appearance:none;height:10px;width:10px;border-radius:50%;background:red;cursor:ew-resize;box-shadow:0 0 2px #555;-moz-transition:background .3s ease-in-out;transition:background .3s ease-in-out}.volume-box .volumeRange::-moz-range-thumb:hover{background:rgb(231,71,71)}.volume-box .volumeRange::-ms-thumb{-webkit-appearance:none;height:10px;width:10px;border-radius:50%;background:red;cursor:ew-resize;box-shadow:0 0 2px #555;-ms-transition:background .3s ease-in-out;transition:background .3s ease-in-out}.volume-box .volumeRange::-ms-thumb:hover{background:rgb(231,71,71)}.volume-box .volumeRange::-ms-track{-webkit-appearance:none;box-shadow:none;border:none;background:transparent}.volume-box .volumeRange::-webkit-slider-runnable-track{-webkit-appearance:none;box-shadow:none;border:none;background:transparent}.volume-box .volumeRange::-moz-range-track{-webkit-appearance:none;box-shadow:none;border:none;background:transparent}.volume-box:hover .volumeRange{display:block}\n"] }] }], propDecorators: { playerContainer: [{ type: ViewChild, args: ['playerContainer'] }], videoPlayer: [{ type: ViewChild, args: ['player'] }], progressBar: [{ type: ViewChild, args: ['progressBar'] }], volumeBar: [{ type: ViewChild, args: ['volumeBar'] }], options: [{ type: Input }], play: [{ type: Output }], pause: [{ type: Output }], ended: [{ type: Output }], volumechange: [{ type: Output }], playing: [{ type: Output }], error: [{ type: Output }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW9uLXZpZGVvLXBsYXllci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9pb24tdmlkZW8tcGxheWVyL3NyYy9saWIvaW9uLXZpZGVvLXBsYXllci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9wcm9qZWN0cy9pb24tdmlkZW8tcGxheWVyL3NyYy9saWIvaW9uLXZpZGVvLXBsYXllci5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQWEsU0FBUyxFQUFjLEtBQUssRUFBRSxNQUFNLEVBQUUsWUFBWSxFQUFFLE1BQU0sZUFBZSxDQUFDOzs7OztBQU96RyxNQUFNLE9BQU8sdUJBQXVCO0lBTHBDO1FBWUUsY0FBUyxHQUFZLEtBQUssQ0FBQztRQUMzQixXQUFNLEdBQVcsR0FBRyxDQUFDO1FBWXJCLGVBQWU7UUFDTCxTQUFJLEdBQUcsSUFBSSxZQUFZLEVBQU8sQ0FBQztRQUMvQixVQUFLLEdBQUcsSUFBSSxZQUFZLEVBQU8sQ0FBQztRQUNoQyxVQUFLLEdBQUcsSUFBSSxZQUFZLEVBQU8sQ0FBQztRQUNoQyxpQkFBWSxHQUFHLElBQUksWUFBWSxFQUFPLENBQUM7UUFDdkMsWUFBTyxHQUFHLElBQUksWUFBWSxFQUFPLENBQUM7UUFDbEMsVUFBSyxHQUFHLElBQUksWUFBWSxFQUFPLENBQUM7S0E2SjNDO0lBM0pDLFdBQVc7SUFDWCxDQUFDO0lBRUQsU0FBUztRQUNQLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxhQUFhLENBQUMsS0FBSyxJQUFJLEtBQUssRUFBRTtZQUMvQyxJQUFJLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztTQUNqQjthQUFNO1lBQ0wsSUFBSSxDQUFDLE1BQU0sR0FBRyxVQUFVLENBQUMsSUFBSSxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztTQUN2RTtRQUNELElBQUksQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDO0lBQ3RELENBQUM7SUFFRCxjQUFjO1FBQ1osSUFBSSxJQUFJLENBQUMsV0FBVyxDQUFDLGFBQWEsQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsS0FBSyxFQUFFO1lBQ2pGLHNDQUFzQztZQUN0QyxJQUFJLENBQUMsV0FBVyxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUN0QyxJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztTQUN2QjthQUNJO1lBQ0gscUNBQXFDO1lBQ3JDLElBQUksQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ3ZDLElBQUksQ0FBQyxTQUFTLEdBQUcsS0FBSyxDQUFDO1NBQ3hCO0lBQ0gsQ0FBQztJQUVELDJFQUEyRTtJQUMzRSxTQUFTO1FBQ1AsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDdkMsSUFBSSxDQUFDLFNBQVMsR0FBRyxLQUFLLENBQUM7UUFDdkIsSUFBSSxJQUFJLENBQUMsV0FBVyxDQUFDLGFBQWEsQ0FBQyxXQUFXO1lBQUUsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQztJQUNqRyxDQUFDO0lBRUQsb0RBQW9EO0lBQ3BELFVBQVU7UUFDUixJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRTtZQUN4QyxJQUFJLENBQUMsV0FBVyxDQUFDLGFBQWEsQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1lBQzdDLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDO1NBQ3JEO2FBQ0k7WUFDSCxJQUFJLENBQUMsV0FBVyxDQUFDLGFBQWEsQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDO1lBQzVDLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO1NBQ2pCO0lBQ0gsQ0FBQztJQUVELG1EQUFtRDtJQUNuRCxXQUFXO1FBQ1QsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ25CLElBQUksQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ3hDLENBQUM7SUFFRCwwQkFBMEI7SUFDMUIsaUJBQWlCO1FBQ2Ysd0ZBQXdGO1FBQ3hGLElBQUksVUFBVSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsV0FBVyxDQUFDLENBQUM7UUFDMUgsa0NBQWtDO1FBQ2xDLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLEVBQUU7WUFDekIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsS0FBSyxHQUFHLFVBQVUsQ0FBQztTQUNuRDtJQUNILENBQUM7SUFFRCxJQUFJLENBQUMsQ0FBTTtRQUNULElBQUksT0FBTyxHQUFHLENBQUMsQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsV0FBVyxDQUFDO1FBQ3JFLElBQUksQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLFdBQVcsR0FBRyxPQUFPLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDO1FBQy9GLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxHQUFHLEdBQUcsQ0FBQyxDQUFDO1FBQzNDLENBQUMsQ0FBQyxNQUFNLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLEtBQUssR0FBRyxVQUFVLENBQUM7SUFDekUsQ0FBQztJQUVELHlCQUF5QjtJQUN6QixXQUFXO1FBQ1QsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQztRQUN6QyxtQ0FBbUM7UUFDbkMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQztJQUNqRCxDQUFDO0lBRUQsaUNBQWlDO0lBQ2pDLGdCQUFnQjtRQUNkLElBQUksUUFBUSxDQUFDLGlCQUFpQixFQUFFO1lBQzlCLFFBQVEsQ0FBQyxjQUFjLEVBQUUsQ0FBQztTQUMzQjthQUFNO1lBQ0wsSUFBSSxJQUFJLENBQUMsZUFBZSxDQUFDLGFBQWEsQ0FBQyxpQkFBaUIsRUFBRTtnQkFDeEQsSUFBSSxDQUFDLGVBQWUsQ0FBQyxhQUFhLENBQUMsaUJBQWlCLEVBQUUsQ0FBQzthQUN4RDtpQkFBTSxJQUFJLElBQUksQ0FBQyxlQUFlLENBQUMsYUFBYSxDQUFDLHVCQUF1QixFQUFFLEVBQUUsWUFBWTtnQkFDbkYsSUFBSSxDQUFDLGVBQWUsQ0FBQyxhQUFhLENBQUMsdUJBQXVCLEVBQUUsQ0FBQzthQUM5RDtpQkFBTSxJQUFJLElBQUksQ0FBQyxlQUFlLENBQUMsYUFBYSxDQUFDLG1CQUFtQixFQUFFLEVBQUUsVUFBVTtnQkFDN0UsSUFBSSxDQUFDLGVBQWUsQ0FBQyxhQUFhLENBQUMsbUJBQW1CLEVBQUUsQ0FBQzthQUMxRDtTQUNGO0lBQ0gsQ0FBQztJQUVELEtBQUssQ0FBQyxPQUFPO1FBQ1gsSUFBSTtZQUNGLElBQUksQ0FBQyxRQUFRLENBQUMsdUJBQXVCLEVBQUU7Z0JBQ3JDLElBQUksQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLHVCQUF1QixFQUFFLENBQUE7YUFDekQ7aUJBQU07Z0JBQ0wsUUFBUSxDQUFDLG9CQUFvQixFQUFFLENBQUM7YUFDakM7U0FDRjtRQUFDLE9BQU8sTUFBTSxFQUFFO1lBQ2YsT0FBTyxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQztTQUN2QjtJQUNILENBQUM7SUFFRCxxRUFBcUU7SUFDckUsTUFBTTtRQUNKLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUE7UUFDckIsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUNuQixDQUFDO0lBRUQsNkNBQTZDO0lBQzdDLE9BQU87UUFDTCxPQUFPLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFBO1FBQ3RCLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDcEIsQ0FBQztJQUVELDJDQUEyQztJQUMzQyxPQUFPO1FBQ0wsT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQTtRQUN0QixJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ3BCLENBQUM7SUFFRCx5Q0FBeUM7SUFDekMsY0FBYztRQUNaLE9BQU8sQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLENBQUMsQ0FBQTtRQUM3QixJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksRUFBRSxDQUFDO0lBQzNCLENBQUM7SUFFRCxTQUFTO1FBQ1AsT0FBTyxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQTtRQUN4QixJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ3RCLENBQUM7SUFFRCxPQUFPO1FBQ0wsT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQTtRQUN0QixJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ3BCLENBQUM7SUFFRCxVQUFVO1FBQ1Isc0VBQXNFO1FBQ3RFLDhEQUE4RDtRQUM5RCwwQkFBMEI7UUFDMUIsd0JBQXdCO1FBQ3hCLGlGQUFpRjtRQUNqRixhQUFhO1FBQ2Isa0hBQWtIO1FBQ2xILHFEQUFxRDtRQUNyRCxZQUFZO1FBQ1osdUJBQXVCO1FBQ3ZCLG1JQUFtSTtRQUNuSSxhQUFhO1FBQ2IsaUJBQWlCO1FBQ2pCLFVBQVU7UUFDVixRQUFRO1FBQ1IsTUFBTTtRQUNOLE1BQU07SUFDUixDQUFDOztvSEFyTFUsdUJBQXVCO3dHQUF2Qix1QkFBdUIsNG1CQ1BwQyw4cEdBc0VNOzJGRC9ETyx1QkFBdUI7a0JBTG5DLFNBQVM7K0JBQ0Usa0JBQWtCOzhCQU1FLGVBQWU7c0JBQTVDLFNBQVM7dUJBQUMsaUJBQWlCO2dCQUNQLFdBQVc7c0JBQS9CLFNBQVM7dUJBQUMsUUFBUTtnQkFDTyxXQUFXO3NCQUFwQyxTQUFTO3VCQUFDLGFBQWE7Z0JBQ0EsU0FBUztzQkFBaEMsU0FBUzt1QkFBQyxXQUFXO2dCQU1iLE9BQU87c0JBQWYsS0FBSztnQkFVSSxJQUFJO3NCQUFiLE1BQU07Z0JBQ0csS0FBSztzQkFBZCxNQUFNO2dCQUNHLEtBQUs7c0JBQWQsTUFBTTtnQkFDRyxZQUFZO3NCQUFyQixNQUFNO2dCQUNHLE9BQU87c0JBQWhCLE1BQU07Z0JBQ0csS0FBSztzQkFBZCxNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBPbkNoYW5nZXMsIFZpZXdDaGlsZCwgRWxlbWVudFJlZiwgSW5wdXQsIE91dHB1dCwgRXZlbnRFbWl0dGVyIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2lvbi12aWRlby1wbGF5ZXInLFxuICB0ZW1wbGF0ZVVybDogJ2lvbi12aWRlby1wbGF5ZXIuaHRtbCcsXG4gIHN0eWxlVXJsczogWydpb24tdmlkZW8tcGxheWVyLnNjc3MnXVxufSlcbmV4cG9ydCBjbGFzcyBJb25WaWRlb1BsYXllckNvbXBvbmVudCBpbXBsZW1lbnRzIE9uQ2hhbmdlcyB7XG4gIC8vIEdldCBhIGhhbmRsZSB0byB0aGUgcGxheWVyXG4gIEBWaWV3Q2hpbGQoJ3BsYXllckNvbnRhaW5lcicpIHBsYXllckNvbnRhaW5lciE6IEVsZW1lbnRSZWY7XG4gIEBWaWV3Q2hpbGQoJ3BsYXllcicpIHZpZGVvUGxheWVyITogRWxlbWVudFJlZjtcbiAgQFZpZXdDaGlsZCgncHJvZ3Jlc3NCYXInKSBwcm9ncmVzc0JhciE6IEVsZW1lbnRSZWY7XG4gIEBWaWV3Q2hpbGQoJ3ZvbHVtZUJhcicpIHZvbHVtZUJhciE6IEVsZW1lbnRSZWY7XG5cbiAgaXNQbGF5aW5nOiBib29sZWFuID0gZmFsc2U7XG4gIHZvbHVtZTogbnVtYmVyID0gMTAwO1xuXG4gIC8vSW5wdXQgb3B0aW9uc1xuICBASW5wdXQoKSBvcHRpb25zIToge1xuICAgIHNyYzogc3RyaW5nO1xuICAgIHR5cGU6IHN0cmluZztcbiAgICBwb3N0ZXI6IHN0cmluZztcbiAgICBhdXRvcGxheTogYm9vbGVhbjtcbiAgICBtdXRlZDogYm9vbGVhbjtcbiAgICBjb250cm9sczogYm9vbGVhblxuICB9O1xuXG4gIC8vT3V0cHV0IEV2ZW50c1xuICBAT3V0cHV0KCkgcGxheSA9IG5ldyBFdmVudEVtaXR0ZXI8YW55PigpO1xuICBAT3V0cHV0KCkgcGF1c2UgPSBuZXcgRXZlbnRFbWl0dGVyPGFueT4oKTtcbiAgQE91dHB1dCgpIGVuZGVkID0gbmV3IEV2ZW50RW1pdHRlcjxhbnk+KCk7XG4gIEBPdXRwdXQoKSB2b2x1bWVjaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPGFueT4oKTtcbiAgQE91dHB1dCgpIHBsYXlpbmcgPSBuZXcgRXZlbnRFbWl0dGVyPGFueT4oKTtcbiAgQE91dHB1dCgpIGVycm9yID0gbmV3IEV2ZW50RW1pdHRlcjxhbnk+KCk7XG5cbiAgbmdPbkNoYW5nZXMoKSB7XG4gIH1cblxuICBzZXRWb2x1bWUoKSB7XG4gICAgaWYgKHRoaXMudm9sdW1lQmFyLm5hdGl2ZUVsZW1lbnQudmFsdWUgPT0gJzEwMCcpIHtcbiAgICAgIHRoaXMudm9sdW1lID0gMTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy52b2x1bWUgPSBwYXJzZUZsb2F0KCcwLicgKyAodGhpcy52b2x1bWVCYXIubmF0aXZlRWxlbWVudC52YWx1ZSkpO1xuICAgIH1cbiAgICB0aGlzLnZpZGVvUGxheWVyLm5hdGl2ZUVsZW1lbnQudm9sdW1lID0gdGhpcy52b2x1bWU7XG4gIH1cblxuICBwbGF5UGF1c2VWaWRlbygpIHtcbiAgICBpZiAodGhpcy52aWRlb1BsYXllci5uYXRpdmVFbGVtZW50LnBhdXNlZCB8fCB0aGlzLnZpZGVvUGxheWVyLm5hdGl2ZUVsZW1lbnQuZW5kZWQpIHtcbiAgICAgIC8vIENoYW5nZSB0aGUgYnV0dG9uIHRvIGEgcGF1c2UgYnV0dG9uXG4gICAgICB0aGlzLnZpZGVvUGxheWVyLm5hdGl2ZUVsZW1lbnQucGxheSgpO1xuICAgICAgdGhpcy5pc1BsYXlpbmcgPSB0cnVlO1xuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgIC8vIENoYW5nZSB0aGUgYnV0dG9uIHRvIGEgcGxheSBidXR0b25cbiAgICAgIHRoaXMudmlkZW9QbGF5ZXIubmF0aXZlRWxlbWVudC5wYXVzZSgpO1xuICAgICAgdGhpcy5pc1BsYXlpbmcgPSBmYWxzZTtcbiAgICB9XG4gIH1cblxuICAvLyBTdG9wIHRoZSBjdXJyZW50IG1lZGlhIGZyb20gcGxheWluZywgYW5kIHJldHVybiBpdCB0byB0aGUgc3RhcnQgcG9zaXRpb25cbiAgc3RvcFZpZGVvKCkge1xuICAgIHRoaXMudmlkZW9QbGF5ZXIubmF0aXZlRWxlbWVudC5wYXVzZSgpO1xuICAgIHRoaXMuaXNQbGF5aW5nID0gZmFsc2U7XG4gICAgaWYgKHRoaXMudmlkZW9QbGF5ZXIubmF0aXZlRWxlbWVudC5jdXJyZW50VGltZSkgdGhpcy52aWRlb1BsYXllci5uYXRpdmVFbGVtZW50LmN1cnJlbnRUaW1lID0gMDtcbiAgfVxuXG4gIC8vIFRvZ2dsZXMgdGhlIG1lZGlhIHBsYXllcidzIG11dGUgYW5kIHVubXV0ZSBzdGF0dXNcbiAgbXV0ZVZvbHVtZSgpIHtcbiAgICBpZiAodGhpcy52aWRlb1BsYXllci5uYXRpdmVFbGVtZW50Lm11dGVkKSB7XG4gICAgICB0aGlzLnZpZGVvUGxheWVyLm5hdGl2ZUVsZW1lbnQubXV0ZWQgPSBmYWxzZTtcbiAgICAgIHRoaXMudm9sdW1lID0gdGhpcy52aWRlb1BsYXllci5uYXRpdmVFbGVtZW50LnZvbHVtZTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICB0aGlzLnZpZGVvUGxheWVyLm5hdGl2ZUVsZW1lbnQubXV0ZWQgPSB0cnVlO1xuICAgICAgdGhpcy52b2x1bWUgPSAwO1xuICAgIH1cbiAgfVxuXG4gIC8vIFJlcGxheXMgdGhlIG1lZGlhIGN1cnJlbnRseSBsb2FkZWQgaW4gdGhlIHBsYXllclxuICByZXBsYXlWaWRlbygpIHtcbiAgICB0aGlzLnJlc2V0UGxheWVyKCk7XG4gICAgdGhpcy52aWRlb1BsYXllci5uYXRpdmVFbGVtZW50LnBsYXkoKTtcbiAgfVxuXG4gIC8vIFVwZGF0ZSB0aGUgcHJvZ3Jlc3MgYmFyXG4gIHVwZGF0ZVByb2dyZXNzQmFyKCkge1xuICAgIC8vIFdvcmsgb3V0IGhvdyBtdWNoIG9mIHRoZSBtZWRpYSBoYXMgcGxheWVkIHZpYSB0aGUgZHVyYXRpb24gYW5kIGN1cnJlbnRUaW1lIHBhcmFtZXRlcnNcbiAgICB2YXIgcGVyY2VudGFnZSA9IE1hdGguZmxvb3IoKDEwMCAvIHRoaXMudmlkZW9QbGF5ZXIubmF0aXZlRWxlbWVudC5kdXJhdGlvbikgKiB0aGlzLnZpZGVvUGxheWVyLm5hdGl2ZUVsZW1lbnQuY3VycmVudFRpbWUpO1xuICAgIC8vIFVwZGF0ZSB0aGUgcHJvZ3Jlc3MgYmFyJ3MgdmFsdWVcbiAgICBpZiAodGhpcy5vcHRpb25zLmNvbnRyb2xzKSB7XG4gICAgICB0aGlzLnByb2dyZXNzQmFyLm5hdGl2ZUVsZW1lbnQudmFsdWUgPSBwZXJjZW50YWdlO1xuICAgIH1cbiAgfVxuXG4gIHNlZWsoZTogYW55KSB7XG4gICAgdmFyIHBlcmNlbnQgPSBlLm9mZnNldFggLyB0aGlzLnByb2dyZXNzQmFyLm5hdGl2ZUVsZW1lbnQub2Zmc2V0V2lkdGg7XG4gICAgdGhpcy52aWRlb1BsYXllci5uYXRpdmVFbGVtZW50LmN1cnJlbnRUaW1lID0gcGVyY2VudCAqIHRoaXMudmlkZW9QbGF5ZXIubmF0aXZlRWxlbWVudC5kdXJhdGlvbjtcbiAgICBlLnRhcmdldC52YWx1ZSA9IE1hdGguZmxvb3IocGVyY2VudCAvIDEwMCk7XG4gICAgZS50YXJnZXQuaW5uZXJIVE1MID0gdGhpcy5wcm9ncmVzc0Jhci5uYXRpdmVFbGVtZW50LnZhbHVlICsgJyUgcGxheWVkJztcbiAgfVxuXG4gIC8vcmVwbGF5IFZpZGVvIGZyb20gc3RhcnRcbiAgcmVzZXRQbGF5ZXIoKSB7XG4gICAgdGhpcy5wcm9ncmVzc0Jhci5uYXRpdmVFbGVtZW50LnZhbHVlID0gMDtcbiAgICAvLyBNb3ZlIHRoZSBtZWRpYSBiYWNrIHRvIHRoZSBzdGFydFxuICAgIHRoaXMudmlkZW9QbGF5ZXIubmF0aXZlRWxlbWVudC5jdXJyZW50VGltZSA9IDA7XG4gIH1cblxuICAvL09wZW4gUGxheWVyIGluIEZ1bGwgc2NyZWVuIG1vZGVcbiAgdG9nZ2xlRnVsbFNjcmVlbigpIHtcbiAgICBpZiAoZG9jdW1lbnQuZnVsbHNjcmVlbkVsZW1lbnQpIHtcbiAgICAgIGRvY3VtZW50LmV4aXRGdWxsc2NyZWVuKCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGlmICh0aGlzLnBsYXllckNvbnRhaW5lci5uYXRpdmVFbGVtZW50LnJlcXVlc3RGdWxsc2NyZWVuKSB7XG4gICAgICAgIHRoaXMucGxheWVyQ29udGFpbmVyLm5hdGl2ZUVsZW1lbnQucmVxdWVzdEZ1bGxzY3JlZW4oKTtcbiAgICAgIH0gZWxzZSBpZiAodGhpcy5wbGF5ZXJDb250YWluZXIubmF0aXZlRWxlbWVudC53ZWJraXRSZXF1ZXN0RnVsbHNjcmVlbikgeyAvKiBTYWZhcmkgKi9cbiAgICAgICAgdGhpcy5wbGF5ZXJDb250YWluZXIubmF0aXZlRWxlbWVudC53ZWJraXRSZXF1ZXN0RnVsbHNjcmVlbigpO1xuICAgICAgfSBlbHNlIGlmICh0aGlzLnBsYXllckNvbnRhaW5lci5uYXRpdmVFbGVtZW50Lm1zUmVxdWVzdEZ1bGxzY3JlZW4pIHsgLyogSUUxMSAqL1xuICAgICAgICB0aGlzLnBsYXllckNvbnRhaW5lci5uYXRpdmVFbGVtZW50Lm1zUmVxdWVzdEZ1bGxzY3JlZW4oKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBhc3luYyBvcGVuUGlwKCkge1xuICAgIHRyeSB7XG4gICAgICBpZiAoIWRvY3VtZW50LnBpY3R1cmVJblBpY3R1cmVFbGVtZW50KSB7XG4gICAgICAgIHRoaXMudmlkZW9QbGF5ZXIubmF0aXZlRWxlbWVudC5yZXF1ZXN0UGljdHVyZUluUGljdHVyZSgpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBkb2N1bWVudC5leGl0UGljdHVyZUluUGljdHVyZSgpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKHJlYXNvbikge1xuICAgICAgY29uc29sZS5lcnJvcihyZWFzb24pO1xuICAgIH1cbiAgfVxuXG4gIC8vIEZpcmVzIHdoZW4gdGhlIGF1ZGlvL3ZpZGVvIGhhcyBiZWVuIHN0YXJ0ZWQgb3IgaXMgbm8gbG9uZ2VyIHBhdXNlZFxuICBvblBsYXkoKSB7XG4gICAgY29uc29sZS5sb2coJ29uUGxheScpXG4gICAgdGhpcy5wbGF5LmVtaXQoKTtcbiAgfVxuXG4gIC8vIEZpcmVzIHdoZW4gdGhlIGF1ZGlvL3ZpZGVvIGhhcyBiZWVuIHBhdXNlZFxuICBvblBhdXNlKCkge1xuICAgIGNvbnNvbGUubG9nKCdvblBhdXNlJylcbiAgICB0aGlzLnBhdXNlLmVtaXQoKTtcbiAgfVxuXG4gIC8vIEZpcmVzIHdoZW4gdGhlIGN1cnJlbnQgcGxheWxpc3QgaXMgZW5kZWRcbiAgb25FbmRlZCgpIHtcbiAgICBjb25zb2xlLmxvZygnb25FbmRlZCcpXG4gICAgdGhpcy5lbmRlZC5lbWl0KCk7XG4gIH1cblxuICAvLyBGaXJlcyB3aGVuIHRoZSB2b2x1bWUgaGFzIGJlZW4gY2hhbmdlZFxuICBvblZvbHVtZWNoYW5nZSgpIHtcbiAgICBjb25zb2xlLmxvZygnb25Wb2x1bWVjaGFuZ2UnKVxuICAgIHRoaXMudm9sdW1lY2hhbmdlLmVtaXQoKTtcbiAgfVxuXG4gIG9uUGxheWluZygpIHtcbiAgICBjb25zb2xlLmxvZygnb25QbGF5aW5nJylcbiAgICB0aGlzLnBsYXlpbmcuZW1pdCgpO1xuICB9XG5cbiAgb25FcnJvcigpIHtcbiAgICBjb25zb2xlLmxvZygnb25FcnJvcicpXG4gICAgdGhpcy5lcnJvci5lbWl0KCk7XG4gIH1cblxuICBvblByb2dyZXNzKCkge1xuICAgIC8vIHRoaXMudmlkZW9QbGF5ZXIubmF0aXZlRWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCdwcm9ncmVzcycsICgpID0+IHtcbiAgICAvLyAgIGNvbnN0IGR1cmF0aW9uID0gdGhpcy52aWRlb1BsYXllci5uYXRpdmVFbGVtZW50LmR1cmF0aW9uO1xuICAgIC8vICAgY29uc29sZS5sb2coZHVyYXRpb24pXG4gICAgLy8gICBpZiAoZHVyYXRpb24gPiAwKSB7XG4gICAgLy8gICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy52aWRlb1BsYXllci5uYXRpdmVFbGVtZW50LmJ1ZmZlcmVkLmxlbmd0aDsgaSsrKSB7XG4gICAgLy8gICAgICAgaWYgKFxuICAgIC8vICAgICAgICAgdGhpcy52aWRlb1BsYXllci5uYXRpdmVFbGVtZW50LmJ1ZmZlcmVkLnN0YXJ0KHRoaXMudmlkZW9QbGF5ZXIubmF0aXZlRWxlbWVudC5idWZmZXJlZC5sZW5ndGggLSAxIC0gaSkgPFxuICAgIC8vICAgICAgICAgdGhpcy52aWRlb1BsYXllci5uYXRpdmVFbGVtZW50LmN1cnJlbnRUaW1lXG4gICAgLy8gICAgICAgKSB7XG4gICAgLy8gICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAvLyAgICAgICAgICAgKHRoaXMudmlkZW9QbGF5ZXIubmF0aXZlRWxlbWVudC5idWZmZXJlZC5lbmQodGhpcy52aWRlb1BsYXllci5uYXRpdmVFbGVtZW50LmJ1ZmZlcmVkLmxlbmd0aCAtIDEgLSBpKSAqIDEwMCkgLyBkdXJhdGlvblxuICAgIC8vICAgICAgICAgKTtcbiAgICAvLyAgICAgICAgIGJyZWFrO1xuICAgIC8vICAgICAgIH1cbiAgICAvLyAgICAgfVxuICAgIC8vICAgfVxuICAgIC8vIH0pO1xuICB9XG5cbn1cbiIsIjxkaXYgaWQ9XCJwbGF5ZXJcIiAjcGxheWVyQ29udGFpbmVyIGNsYXNzPVwiaW9uLXZpZGVvLXBsYXllclwiPlxuICAgIDx2aWRlbyBpZD1cInZpZGVvLWVsZW1lbnRcIiAjcGxheWVyXG4gICAgICAgICh0aW1ldXBkYXRlKT1cInVwZGF0ZVByb2dyZXNzQmFyKClcIlxuICAgICAgICBbcG9zdGVyXT1cIm9wdGlvbnMucG9zdGVyXCJcbiAgICAgICAgW2F1dG9wbGF5XT1cIm9wdGlvbnMuYXV0b3BsYXlcIlxuICAgICAgICBbbXV0ZWRdPVwib3B0aW9ucy5tdXRlZFwiXG4gICAgICAgIChwbGF5KT1cIm9uUGxheSgpXCJcbiAgICAgICAgKHBhdXNlKT1cIm9uUGF1c2UoKVwiXG4gICAgICAgIChlbmRlZCk9XCJvbkVuZGVkKClcIlxuICAgICAgICAodm9sdW1lY2hhbmdlKT1cIm9uVm9sdW1lY2hhbmdlKClcIlxuICAgICAgICAocGxheWluZyk9XCJvblBsYXlpbmcoKVwiXG4gICAgICAgIChlcnJvcik9XCJvbkVycm9yKClcIlxuICAgICAgICAocHJvZ3Jlc3MpPVwib25Qcm9ncmVzcygpXCI+XG4gICAgICAgIDxzb3VyY2UgW3NyY109XCJvcHRpb25zLnNyY1wiIFt0eXBlXT1cIm9wdGlvbnMudHlwZVwiPlxuICAgIDwvdmlkZW8+XG5cbiAgICA8ZGl2IGlkPVwiY29udHJvbHNcIiAqbmdJZj1cIm9wdGlvbnMuY29udHJvbHNcIj5cblxuICAgICAgICA8cHJvZ3Jlc3MgaWQ9XCJwcm9ncmVzcy1iYXJcIiAoY2xpY2spPVwic2VlaygkZXZlbnQpXCIgbWluPVwiMFwiIG1heD1cIjEwMFwiIHZhbHVlPScxJyAjcHJvZ3Jlc3NCYXI+PC9wcm9ncmVzcz5cblxuICAgICAgICA8ZGl2IGNsYXNzPVwiY29udHJvbHMtYnRuc1wiPlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cInN0YXJ0LXN0b3BcIj5cbiAgICAgICAgICAgICAgICA8aW9uLWJ1dHRvbiBmaWxsPVwiY2xlYXJcIiBzaXplPVwic21hbGxcIiBzbG90PVwiaWNvbi1vbmx5XCIgY2xhc3M9J3BsYXknIChjbGljayk9XCJwbGF5UGF1c2VWaWRlbygpXCI+XG4gICAgICAgICAgICAgICAgICAgIDxpb24taWNvbiBbbmFtZV09XCJpc1BsYXlpbmcgPyAncGF1c2UnIDogJ3BsYXknXCI+PC9pb24taWNvbj5cbiAgICAgICAgICAgICAgICA8L2lvbi1idXR0b24+XG4gICAgICAgICAgICAgICAgPHNwYW4gKm5nSWY9XCJwbGF5ZXIuZHVyYXRpb25cIj5cbiAgICAgICAgICAgICAgICAgICAge3twbGF5ZXIuY3VycmVudFRpbWUgfCBzZWNvbmRzVG9Ib3Vyc319IC8ge3twbGF5ZXIuZHVyYXRpb24gfCBzZWNvbmRzVG9Ib3VycyB9fVxuICAgICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwidm9sdW1lLWJveFwiPlxuICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPVwicmFuZ2VcIiBjbGFzcz1cInZvbHVtZVJhbmdlXCJcbiAgICAgICAgICAgICAgICAgICAgKGNoYW5nZSk9XCJzZXRWb2x1bWUoKVwiIFxuICAgICAgICAgICAgICAgICAgICBtaW49XCIwXCJcbiAgICAgICAgICAgICAgICAgICAgbWF4PVwiMTAwXCJcbiAgICAgICAgICAgICAgICAgICAgc3RlcD1cIjFcIlxuICAgICAgICAgICAgICAgICAgICBbdmFsdWVdPVwidm9sdW1lQmFyLnZhbHVlXCJcbiAgICAgICAgICAgICAgICAgICAgI3ZvbHVtZUJhclxuICAgICAgICAgICAgICAgICAgICBbbmdTdHlsZV09XCJ7XG4gICAgICAgICAgICAgICAgICAgICAgICAnYmFja2dyb3VuZC1zaXplJzogdm9sdW1lQmFyLnZhbHVlICsgJyUgMTAwJSdcbiAgICAgICAgICAgICAgICAgICAgfVwiXG4gICAgICAgICAgICAgICAgLz5cblxuICAgICAgICAgICAgICAgIDxpb24tYnV0dG9uIGZpbGw9XCJjbGVhclwiIHNpemU9XCJzbWFsbFwiIHNsb3Q9XCJpY29uLW9ubHlcIiBjbGFzcz1cIm11dGVcIiB0aXRsZT1cIm11dGVcIiAoY2xpY2spPVwibXV0ZVZvbHVtZSgpXCI+XG4gICAgICAgICAgICAgICAgICAgIDxpb24taWNvbiBbbmFtZV09XCJcbiAgICAgICAgICAgICAgICAgICAgICAgIHZvbHVtZSA9PSAwID8gJ3ZvbHVtZS1tdXRlJ1xuICAgICAgICAgICAgICAgICAgICAgICAgOlxuICAgICAgICAgICAgICAgICAgICAgICAgKHZvbHVtZSA+IDAgJiYgdm9sdW1lIDwgMC41MCkgPyAndm9sdW1lLWxvdydcbiAgICAgICAgICAgICAgICAgICAgICAgIDpcbiAgICAgICAgICAgICAgICAgICAgICAgICh2b2x1bWUgPiAwLjUwICYmIHZvbHVtZSA8IDAuODApID8gJ3ZvbHVtZS1tZWRpdW0nXG4gICAgICAgICAgICAgICAgICAgICAgICA6ICd2b2x1bWUtaGlnaCdcIj5cbiAgICAgICAgICAgICAgICAgICAgPC9pb24taWNvbj5cbiAgICAgICAgICAgICAgICA8L2lvbi1idXR0b24+XG5cbiAgICAgICAgICAgICAgICA8aW9uLWJ1dHRvbiBmaWxsPVwiY2xlYXJcIiBzaXplPVwic21hbGxcIiBzbG90PVwiaWNvbi1vbmx5XCIgKGNsaWNrKT1cInRvZ2dsZUZ1bGxTY3JlZW4oKVwiPlxuICAgICAgICAgICAgICAgICAgICA8aW9uLWljb24gbmFtZT1cImV4cGFuZFwiPjwvaW9uLWljb24+XG4gICAgICAgICAgICAgICAgPC9pb24tYnV0dG9uPlxuXG4gICAgICAgICAgICAgICAgPGlvbi1idXR0b24gZmlsbD1cImNsZWFyXCIgc2l6ZT1cInNtYWxsXCIgc2xvdD1cImljb24tb25seVwiIChjbGljayk9XCJvcGVuUGlwKClcIj5cbiAgICAgICAgICAgICAgICAgICAgPGlvbi1pY29uIG5hbWU9XCJvcGVuXCI+PC9pb24taWNvbj5cbiAgICAgICAgICAgICAgICA8L2lvbi1idXR0b24+XG4gICAgICAgICAgICAgICAgPCEtLSA8aW9uLWJ1dHRvbiBzaXplPVwic21hbGxcIiBzbG90PVwiaWNvbi1vbmx5XCIgIGlkPVwiYnRuU3RvcFwiIGNsYXNzPVwiXCIgdGl0bGU9XCJzdG9wXCIgYWNjZXNza2V5PVwiWFwiIChjbGljayk9XCJzdG9wVmlkZW8oKVwiPlxuICAgICAgICAgICAgICAgICAgICA8aW9uLWljb24gbmFtZT1cInN0b3BcIj48L2lvbi1pY29uPlxuICAgICAgICAgICAgICAgIDwvaW9uLWJ1dHRvbj5cbiAgICAgICAgICAgICAgICA8aW9uLWJ1dHRvbiBzaXplPVwic21hbGxcIiBzbG90PVwiaWNvbi1vbmx5XCIgIGlkPVwiYnRuUmVwbGF5XCIgY2xhc3M9XCJyZXBsYXlcIiB0aXRsZT1cInJlcGxheVwiIGFjY2Vzc2tleT1cIlJcIiAoY2xpY2spPVwicmVwbGF5VmlkZW8oKVwiPlxuICAgICAgICAgICAgICAgICAgICA8aW9uLWljb24gbmFtZT1cInJlbG9hZFwiPjwvaW9uLWljb24+XG4gICAgICAgICAgICAgICAgPC9pb24tYnV0dG9uPiAtLT5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbjwvZGl2PiJdfQ==