UNPKG

ng-toast-notify

Version:

Lightweight and flexible toast notifications for Angular

123 lines 39.8 kB
import { AsyncPipe, NgClass, NgComponentOutlet, NgStyle } from '@angular/common'; import { Component, HostListener, Input } from '@angular/core'; import { ToastType } from '../toasty.service'; import * as i0 from "@angular/core"; import * as i1 from "../toasty.service"; var ToastyContainerPosition; (function (ToastyContainerPosition) { ToastyContainerPosition["TOP_LEFT"] = "top-left"; ToastyContainerPosition["TOP"] = "top"; ToastyContainerPosition["TOP_RIGHT"] = "top-right"; ToastyContainerPosition["BOTTOM_LEFT"] = "bottom-left"; ToastyContainerPosition["BOTTOM"] = "bottom"; ToastyContainerPosition["BOTTOM_RIGHT"] = "bottom-right"; })(ToastyContainerPosition || (ToastyContainerPosition = {})); export class ToastyComponent { constructor(_toastService) { this._toastService = _toastService; this.position = ToastyContainerPosition.BOTTOM_RIGHT; this.duration = this._toastService.getDefaultDuration(); this.capacity = this._toastService.getCapacity(); this.grouping = this._toastService.getGrouping(); this.ToastType = ToastType; // to use in the template this.toasts$ = this._toastService.newToast$; this.positionMap = { 'top-left': ['top', 'left'], 'top': ['top', 'center'], 'top-right': ['top', 'right'], 'bottom-left': ['bottom', 'left'], 'bottom': ['bottom', 'center'], 'bottom-right': ['bottom', 'right'], }; } ngOnInit() { // Subscribe to the toast service to receive new toasts // this._toastService.newToast$.subscribe(t => { // if (t) { // // console.log(t); // } // }); this._toastService.deleteToast$.subscribe(t => { if (t) { // we dont need to delete toast, because internally in the service the toast has been deleted and in future toasts, only live toasts will be received from the service // console.log(t); } }); this._toastService.updateToast$.subscribe(t => { if (t) { // console.log(t); } }); } ngOnChanges(changes) { if (changes['duration']) { this._toastService.setDefaultDuration(this.duration); } if (changes['capacity']) { this._toastService.setCapacity(changes['capacity'].currentValue); } if (changes['grouping']) { this._toastService.setGrouping(changes['grouping'].currentValue); } } closeToast(id) { this._toastService.closeToast(id); } executeAction(action) { action.callback(); } containerClass() { return this.positionMap[this.position] ?? [ToastyContainerPosition.BOTTOM_RIGHT]; } expires(t) { return (t.expires < Date.now()); } getDurationUntilExpire(expires) { const duration = expires - Date.now(); return duration > 0 ? duration : 0; } // Swipe to close functionality onTouchStart(event) { const toastid = this.localizeToastIdFromTouch(event); this._toastService.updateTouchStart(toastid, event.changedTouches[0]); } onTouchEnd(event) { this._toastService.updateTouchEnd(event.changedTouches[0]); } onTouchMove(event) { this._toastService.updateTouchMove(event.changedTouches[0]); } localizeToastIdFromTouch(event) { let current = event.target; let maxnesting = 4; while (!current.classList.contains('toasty') && maxnesting > 0) { current = current.parentElement; maxnesting--; } return parseInt(current.getAttribute('data-toast-id') || '0'); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ToastyComponent, deps: [{ token: i1.ToastyService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: ToastyComponent, isStandalone: true, selector: "toasty", inputs: { position: "position", duration: "duration", capacity: "capacity", grouping: "grouping" }, host: { listeners: { "touchstart": "onTouchStart($event)", "touchend": "onTouchEnd($event)", "touchmove": "onTouchMove($event)" } }, usesOnChanges: true, ngImport: i0, template: "<div class=\"toasty-container\" [ngClass]=\"containerClass()\">\r\n @for (item of toasts$ | async; track item.id) {\r\n <div class=\"toasty-wrapper\" [style.transform]=\"'translateX(' + (item.gesture?.offsetX || 0) + 'px)'\"\r\n [style.transition]=\"item.gesture ? 'none' : 'transform 0.3s ease'\">\r\n <div class=\"toasty\" [attr.data-toast-id]=\"item.id\" [ngClass]=\"item.type\" [class.fadeout]=\"expires(item)\"\r\n [ngStyle]=\"item.config?.customStyle\">\r\n\r\n <!-- icon -->\r\n @if (item.config?.loading) {\r\n <div class=\"toasty-loading\"></div>\r\n } @else if (item.type == ToastType.Success) {\r\n <div class=\"toasty-icon\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-6 w-6 shrink-0 stroke-current\" fill=\"none\"\r\n viewBox=\"0 0 24 24\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"\r\n d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\" />\r\n </svg>\r\n </div>\r\n } @else if (item.type == ToastType.Error) {\r\n <div class=\"toasty-icon\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-6 w-6 shrink-0 stroke-current\" fill=\"none\"\r\n viewBox=\"0 0 24 24\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"\r\n d=\"M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z\" />\r\n </svg>\r\n </div>\r\n } @else if (item.type == ToastType.Info) {\r\n <div class=\"toasty-icon\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\"\r\n class=\"h-6 w-6 shrink-0 stroke-current\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"\r\n d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\r\n </svg>\r\n </div>\r\n } @else if (item.type == ToastType.Warning) {\r\n <div class=\"toasty-icon\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-6 w-6 shrink-0 stroke-current\" fill=\"none\"\r\n viewBox=\"0 0 24 24\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"\r\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\" />\r\n </svg>\r\n </div>\r\n } @else {\r\n <div class=\"toasty-noicon\"></div>\r\n }\r\n\r\n <div class=\"toasty-content\">\r\n @if (item.config?.type == ToastType.Component && item.config?.component) {\r\n <ng-container *ngComponentOutlet=\"item.config?.component ?? null; \r\n inputs: item.config?.componentParams\">\r\n </ng-container>\r\n } @else {\r\n @if (item.title) {\r\n <div class=\"toasty-header\">\r\n <strong class=\"toasty-title\">{{item.title}}</strong>\r\n @if (item.count > 1) {\r\n <span class=\"toasty-badge\">x{{item.count}}</span>\r\n }\r\n <!-- <small class=\"toasty-time\">{{item.timestamp}}</small> -->\r\n </div>\r\n }\r\n @if (item.config?.enableHtml) {\r\n <div class=\"toasty-body\" [innerHTML]=\"item.message\"></div>\r\n } @else {\r\n <div class=\"toasty-body\">{{item.message}}</div>\r\n }\r\n \r\n @if (item.config?.actions?.length) {\r\n <div class=\"toasty-actions\">\r\n @for (action of item.config?.actions; track $index) {\r\n <button type=\"button\" \r\n class=\"toasty-action-btn\"\r\n [ngClass]=\"action?.class\"\r\n (click)=\"executeAction(action)\">\r\n {{action?.label}}\r\n </button>\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n <div class=\"toasty-close\">\r\n <button type=\"button\" class=\"toasty-close\" (click)=\"closeToast(item.id)\">&times;</button>\r\n </div>\r\n @if (item.config?.progressBar) {\r\n <div class=\"toasty-progress\" [class.animate]=\"item.animatePbar\"\r\n [style.transition-duration.ms]=\"item.duration\"></div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>", styles: [".toasty-container{position:fixed;width:350px;padding:.5rem;right:0;bottom:0;pointer-events:none}.toasty-container.left{left:12px}.toasty-container.center{left:50%;transform:translate(-50%)}.toasty-container.right{right:12px}.toasty-container.top{top:10px;display:flex;flex-direction:column-reverse;justify-content:flex-end}.toasty-container.bottom{bottom:0}.toasty{padding:10px;border:1px solid rgba(0,0,0,.15);border-radius:8px;box-shadow:0 4px 10px #00000026;background-color:#fff;overflow:hidden;margin-bottom:1rem;font-family:Arial,Helvetica,sans-serif;animation-name:toasty_fadein;animation-duration:.3s;animation-timing-function:ease-out;animation-delay:0s;animation-iteration-count:1;animation-fill-mode:forwards;display:flex;align-items:center;pointer-events:auto;touch-action:manipulation}@keyframes toasty_fadein{0%{opacity:0;transform:translate(100%)}to{opacity:1;transform:translate(0)}}@keyframes toasty_fadeout{0%{opacity:1}to{opacity:0;pointer-events:none}}.toasty.fadeout{animation-name:toasty_fadeout;animation-duration:.5s;animation-timing-function:ease-in;animation-delay:0s;animation-iteration-count:1;animation-fill-mode:forwards;pointer-events:none}.toasty-icon{width:20px;height:20px;margin-right:.6rem;border-radius:4px}.toasty-loading{width:10px;height:10px;margin-right:.6rem;border-radius:50%;display:inline-block;border-top:3px solid;border-right:3px solid transparent;box-sizing:border-box;animation:loading-toasty-anim 1s linear infinite}@keyframes loading-toasty-anim{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.toasty-noicon{width:7px}.toasty-content{flex-direction:column;flex:1}.toasty-header{display:flex;align-items:center}.toasty-title{font-size:.95rem;font-weight:700;margin-right:auto}.toasty-time{font-size:.85rem;color:#6c757d;margin-right:1rem}.toasty-close{background:none;border:none;font-size:1.4rem;line-height:1;cursor:pointer;color:inherit;opacity:.6}.toasty-body{font-size:.9rem;margin-top:1px}.toasty-close:hover{opacity:1}.toasty.none{background-color:#fff;border-color:#ededed;color:#171717}.toasty.success{background-color:#ecfdf3;border-color:#bffcd9;color:#008a2e}.toasty.error{background-color:#fff0f0;border-color:#ffe0e1;color:#e60000}.toasty.info{background-color:#f0f8ff;border-color:#dde7fd;color:#0973dc}.toasty.warning{background-color:#fffcf0;border-color:#fbeeb1;color:#dc7609}.toasty.custom{background-color:#fff}.toasty-progress{position:absolute;bottom:0;left:0;height:4px;width:100%;background-color:currentColor;opacity:.3;transform-origin:left;transform:scaleX(1);transition-property:transform;transition-timing-function:linear}.toasty-progress.animate{transform:scaleX(0)}.toasty-badge{position:absolute;top:2px;right:8px;font-size:.65rem;padding:1px 5px;border-radius:999px;font-weight:700;background:#0000001a;color:currentColor}.toasty-actions{display:flex;gap:8px;margin-top:12px;justify-content:flex-end}.toasty-action-btn{padding:6px 12px;border:1px solid transparent;border-radius:4px;cursor:pointer;font-size:.85rem;font-weight:500;transition:all .2s ease;background:#0000000d;color:inherit}.toasty-action-btn:hover{transform:translateY(-1px)}.toasty-action-btn.confirm-btn{background:#0973dc;color:#fff;opacity:.7}.toasty-action-btn.confirm-btn:hover{opacity:1;transform:translateY(-1px)}.toasty-action-btn.cancel-btn{background:#0000001a;color:#000000b3}.toasty-action-btn.cancel-btn:hover{background:#00000040}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ToastyComponent, decorators: [{ type: Component, args: [{ selector: 'toasty', standalone: true, imports: [NgClass, NgStyle, AsyncPipe, NgComponentOutlet], template: "<div class=\"toasty-container\" [ngClass]=\"containerClass()\">\r\n @for (item of toasts$ | async; track item.id) {\r\n <div class=\"toasty-wrapper\" [style.transform]=\"'translateX(' + (item.gesture?.offsetX || 0) + 'px)'\"\r\n [style.transition]=\"item.gesture ? 'none' : 'transform 0.3s ease'\">\r\n <div class=\"toasty\" [attr.data-toast-id]=\"item.id\" [ngClass]=\"item.type\" [class.fadeout]=\"expires(item)\"\r\n [ngStyle]=\"item.config?.customStyle\">\r\n\r\n <!-- icon -->\r\n @if (item.config?.loading) {\r\n <div class=\"toasty-loading\"></div>\r\n } @else if (item.type == ToastType.Success) {\r\n <div class=\"toasty-icon\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-6 w-6 shrink-0 stroke-current\" fill=\"none\"\r\n viewBox=\"0 0 24 24\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"\r\n d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\" />\r\n </svg>\r\n </div>\r\n } @else if (item.type == ToastType.Error) {\r\n <div class=\"toasty-icon\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-6 w-6 shrink-0 stroke-current\" fill=\"none\"\r\n viewBox=\"0 0 24 24\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"\r\n d=\"M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z\" />\r\n </svg>\r\n </div>\r\n } @else if (item.type == ToastType.Info) {\r\n <div class=\"toasty-icon\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\"\r\n class=\"h-6 w-6 shrink-0 stroke-current\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"\r\n d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\r\n </svg>\r\n </div>\r\n } @else if (item.type == ToastType.Warning) {\r\n <div class=\"toasty-icon\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-6 w-6 shrink-0 stroke-current\" fill=\"none\"\r\n viewBox=\"0 0 24 24\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"\r\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\" />\r\n </svg>\r\n </div>\r\n } @else {\r\n <div class=\"toasty-noicon\"></div>\r\n }\r\n\r\n <div class=\"toasty-content\">\r\n @if (item.config?.type == ToastType.Component && item.config?.component) {\r\n <ng-container *ngComponentOutlet=\"item.config?.component ?? null; \r\n inputs: item.config?.componentParams\">\r\n </ng-container>\r\n } @else {\r\n @if (item.title) {\r\n <div class=\"toasty-header\">\r\n <strong class=\"toasty-title\">{{item.title}}</strong>\r\n @if (item.count > 1) {\r\n <span class=\"toasty-badge\">x{{item.count}}</span>\r\n }\r\n <!-- <small class=\"toasty-time\">{{item.timestamp}}</small> -->\r\n </div>\r\n }\r\n @if (item.config?.enableHtml) {\r\n <div class=\"toasty-body\" [innerHTML]=\"item.message\"></div>\r\n } @else {\r\n <div class=\"toasty-body\">{{item.message}}</div>\r\n }\r\n \r\n @if (item.config?.actions?.length) {\r\n <div class=\"toasty-actions\">\r\n @for (action of item.config?.actions; track $index) {\r\n <button type=\"button\" \r\n class=\"toasty-action-btn\"\r\n [ngClass]=\"action?.class\"\r\n (click)=\"executeAction(action)\">\r\n {{action?.label}}\r\n </button>\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n <div class=\"toasty-close\">\r\n <button type=\"button\" class=\"toasty-close\" (click)=\"closeToast(item.id)\">&times;</button>\r\n </div>\r\n @if (item.config?.progressBar) {\r\n <div class=\"toasty-progress\" [class.animate]=\"item.animatePbar\"\r\n [style.transition-duration.ms]=\"item.duration\"></div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>", styles: [".toasty-container{position:fixed;width:350px;padding:.5rem;right:0;bottom:0;pointer-events:none}.toasty-container.left{left:12px}.toasty-container.center{left:50%;transform:translate(-50%)}.toasty-container.right{right:12px}.toasty-container.top{top:10px;display:flex;flex-direction:column-reverse;justify-content:flex-end}.toasty-container.bottom{bottom:0}.toasty{padding:10px;border:1px solid rgba(0,0,0,.15);border-radius:8px;box-shadow:0 4px 10px #00000026;background-color:#fff;overflow:hidden;margin-bottom:1rem;font-family:Arial,Helvetica,sans-serif;animation-name:toasty_fadein;animation-duration:.3s;animation-timing-function:ease-out;animation-delay:0s;animation-iteration-count:1;animation-fill-mode:forwards;display:flex;align-items:center;pointer-events:auto;touch-action:manipulation}@keyframes toasty_fadein{0%{opacity:0;transform:translate(100%)}to{opacity:1;transform:translate(0)}}@keyframes toasty_fadeout{0%{opacity:1}to{opacity:0;pointer-events:none}}.toasty.fadeout{animation-name:toasty_fadeout;animation-duration:.5s;animation-timing-function:ease-in;animation-delay:0s;animation-iteration-count:1;animation-fill-mode:forwards;pointer-events:none}.toasty-icon{width:20px;height:20px;margin-right:.6rem;border-radius:4px}.toasty-loading{width:10px;height:10px;margin-right:.6rem;border-radius:50%;display:inline-block;border-top:3px solid;border-right:3px solid transparent;box-sizing:border-box;animation:loading-toasty-anim 1s linear infinite}@keyframes loading-toasty-anim{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.toasty-noicon{width:7px}.toasty-content{flex-direction:column;flex:1}.toasty-header{display:flex;align-items:center}.toasty-title{font-size:.95rem;font-weight:700;margin-right:auto}.toasty-time{font-size:.85rem;color:#6c757d;margin-right:1rem}.toasty-close{background:none;border:none;font-size:1.4rem;line-height:1;cursor:pointer;color:inherit;opacity:.6}.toasty-body{font-size:.9rem;margin-top:1px}.toasty-close:hover{opacity:1}.toasty.none{background-color:#fff;border-color:#ededed;color:#171717}.toasty.success{background-color:#ecfdf3;border-color:#bffcd9;color:#008a2e}.toasty.error{background-color:#fff0f0;border-color:#ffe0e1;color:#e60000}.toasty.info{background-color:#f0f8ff;border-color:#dde7fd;color:#0973dc}.toasty.warning{background-color:#fffcf0;border-color:#fbeeb1;color:#dc7609}.toasty.custom{background-color:#fff}.toasty-progress{position:absolute;bottom:0;left:0;height:4px;width:100%;background-color:currentColor;opacity:.3;transform-origin:left;transform:scaleX(1);transition-property:transform;transition-timing-function:linear}.toasty-progress.animate{transform:scaleX(0)}.toasty-badge{position:absolute;top:2px;right:8px;font-size:.65rem;padding:1px 5px;border-radius:999px;font-weight:700;background:#0000001a;color:currentColor}.toasty-actions{display:flex;gap:8px;margin-top:12px;justify-content:flex-end}.toasty-action-btn{padding:6px 12px;border:1px solid transparent;border-radius:4px;cursor:pointer;font-size:.85rem;font-weight:500;transition:all .2s ease;background:#0000000d;color:inherit}.toasty-action-btn:hover{transform:translateY(-1px)}.toasty-action-btn.confirm-btn{background:#0973dc;color:#fff;opacity:.7}.toasty-action-btn.confirm-btn:hover{opacity:1;transform:translateY(-1px)}.toasty-action-btn.cancel-btn{background:#0000001a;color:#000000b3}.toasty-action-btn.cancel-btn:hover{background:#00000040}\n"] }] }], ctorParameters: () => [{ type: i1.ToastyService }], propDecorators: { position: [{ type: Input }], duration: [{ type: Input }], capacity: [{ type: Input }], grouping: [{ type: Input }], onTouchStart: [{ type: HostListener, args: ['touchstart', ['$event']] }], onTouchEnd: [{ type: HostListener, args: ['touchend', ['$event']] }], onTouchMove: [{ type: HostListener, args: ['touchmove', ['$event']] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9hc3R5LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL25nLXRvYXN0LW5vdGlmeS9zcmMvbGliL3RvYXN0eS90b2FzdHkuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmctdG9hc3Qtbm90aWZ5L3NyYy9saWIvdG9hc3R5L3RvYXN0eS5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxPQUFPLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUNqRixPQUFPLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQWlCLE1BQU0sZUFBZSxDQUFDO0FBQzlFLE9BQU8sRUFBMEMsU0FBUyxFQUFFLE1BQU0sbUJBQW1CLENBQUM7OztBQUV0RixJQUFLLHVCQU9KO0FBUEQsV0FBSyx1QkFBdUI7SUFDMUIsZ0RBQXFCLENBQUE7SUFDckIsc0NBQVcsQ0FBQTtJQUNYLGtEQUF1QixDQUFBO0lBQ3ZCLHNEQUEyQixDQUFBO0lBQzNCLDRDQUFpQixDQUFBO0lBQ2pCLHdEQUE2QixDQUFBO0FBQy9CLENBQUMsRUFQSSx1QkFBdUIsS0FBdkIsdUJBQXVCLFFBTzNCO0FBU0QsTUFBTSxPQUFPLGVBQWU7SUFtQjFCLFlBQW9CLGFBQTRCO1FBQTVCLGtCQUFhLEdBQWIsYUFBYSxDQUFlO1FBakJ2QyxhQUFRLEdBQVcsdUJBQXVCLENBQUMsWUFBWSxDQUFDO1FBQ3hELGFBQVEsR0FBVyxJQUFJLENBQUMsYUFBYSxDQUFDLGtCQUFrQixFQUFFLENBQUM7UUFDM0QsYUFBUSxHQUFXLElBQUksQ0FBQyxhQUFhLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDcEQsYUFBUSxHQUFZLElBQUksQ0FBQyxhQUFhLENBQUMsV0FBVyxFQUFFLENBQUM7UUFFcEQsY0FBUyxHQUFHLFNBQVMsQ0FBQyxD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RFQsWUFBWTt1QkFBQyxVQUFVLEVBQUUsQ0FBQyxRQUFRLENBQUM7Z0JBTXBDLFdBQVc7c0JBRFYsWUFBWTt1QkFBQyxXQUFXLEVBQUUsQ0FBQyxRQUFRLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBBc3luY1BpcGUsIE5nQ2xhc3MsIE5nQ29tcG9uZW50T3V0bGV0LCBOZ1N0eWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcclxuaW1wb3J0IHsgQ29tcG9uZW50LCBIb3N0TGlzdGVuZXIsIElucHV0LCBTaW1wbGVDaGFuZ2VzIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IFRvYXN0QWN0aW9uLCBUb2FzdE1vZGVsLCBUb2FzdHlTZXJ2aWNlLCBUb2FzdFR5cGUgfSBmcm9tICcuLi90b2FzdHkuc2VydmljZSc7XHJcblxyXG5lbnVtIFRvYXN0eUNvbnRhaW5lclBvc2l0aW9uIHtcclxuICBUT1BfTEVGVCA9ICd0b3AtbGVmdCcsXHJcbiAgVE9QID0gJ3RvcCcsXHJcbiAgVE9QX1JJR0hUID0gJ3RvcC1yaWdodCcsXHJcbiAgQk9UVE9NX0xFRlQgPSAnYm90dG9tLWxlZnQnLFxyXG4gIEJPVFRPTSA9ICdib3R0b20nLFxyXG4gIEJPVFRPTV9SSUdIVCA9ICdib3R0b20tcmlnaHQnXHJcbn1cclxuXHJcbkBDb21wb25lbnQoe1xyXG4gIHNlbGVjdG9yOiAndG9hc3R5JyxcclxuICBzdGFuZGFsb25lOiB0cnVlLFxyXG4gIGltcG9ydHM6IFtOZ0NsYXNzLCBOZ1N0eWxlLCBBc3luY1BpcGUsIE5nQ29tcG9uZW50T3V0bGV0XSxcclxuICB0ZW1wbGF0ZVVybDogJy4vdG9hc3R5LmNvbXBvbmVudC5odG1sJyxcclxuICBzdHlsZVVybDogJy4vdG9hc3R5LmNvbXBvbmVudC5jc3MnXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBUb2FzdHlDb21wb25lbnQge1xyXG5cclxuICBASW5wdXQoKSBwb3NpdGlvbjogc3RyaW5nID0gVG9hc3R5Q29udGFpbmVyUG9zaXRpb24uQk9UVE9NX1JJR0hUO1xyXG4gIEBJbnB1dCgpIGR1cmF0aW9uOiBudW1iZXIgPSB0aGlzLl90b2FzdFNlcnZpY2UuZ2V0RGVmYXVsdER1cmF0aW9uKCk7XHJcbiAgQElucHV0KCkgY2FwYWNpdHk6IG51bWJlciA9IHRoaXMuX3RvYXN0U2VydmljZS5nZXRDYXBhY2l0eSgpO1xyXG4gIEBJbnB1dCgpIGdyb3VwaW5nOiBib29sZWFuID0gdGhpcy5fdG9hc3RTZXJ2aWNlLmdldEdyb3VwaW5nKCk7XHJcblxyXG4gIHByb3RlY3RlZCBUb2FzdFR5cGUgPSBUb2FzdFR5cGU7IC8vIHRvIHVzZSBpbiB0aGUgdGVtcGxhdGVcclxuXHJcbiAgcHJvdGVjdGVkIHRvYXN0cyQgPSB0aGlzLl90b2FzdFNlcnZpY2UubmV3VG9hc3QkO1xyXG5cclxuICBwcml2YXRlIHJlYWRvbmx5IHBvc2l0aW9uTWFwOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmdbXT4gPSB7XHJcbiAgICAndG9wLWxlZnQnOiBbJ3RvcCcsICdsZWZ0J10sXHJcbiAgICAndG9wJzogWyd0b3AnLCAnY2VudGVyJ10sXHJcbiAgICAndG9wLXJpZ2h0JzogWyd0b3AnLCAncmlnaHQnXSxcclxuICAgICdib3R0b20tbGVmdCc6IFsnYm90dG9tJywgJ2xlZnQnXSxcclxuICAgICdib3R0b20nOiBbJ2JvdHRvbScsICdjZW50ZXInXSxcclxuICAgICdib3R0b20tcmlnaHQnOiBbJ2JvdHRvbScsICdyaWdodCddLFxyXG4gIH07XHJcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBfdG9hc3RTZXJ2aWNlOiBUb2FzdHlTZXJ2aWNlKSB7XHJcblxyXG4gIH1cclxuXHJcbiAgbmdPbkluaXQoKTogdm9pZCB7XHJcbiAgICAvLyBTdWJzY3JpYmUgdG8gdGhlIHRvYXN0IHNlcnZpY2UgdG8gcmVjZWl2ZSBuZXcgdG9hc3RzXHJcbiAgICAvLyB0aGlzLl90b2FzdFNlcnZpY2UubmV3VG9hc3QkLnN1YnNjcmliZSh0ID0+IHtcclxuICAgIC8vICAgaWYgKHQpIHtcclxuICAgIC8vICAgICAvLyBjb25zb2xlLmxvZyh0KTtcclxuICAgIC8vICAgfVxyXG4gICAgLy8gfSk7XHJcblxyXG4gICAgdGhpcy5fdG9hc3RTZXJ2aWNlLmRlbGV0ZVRvYXN0JC5zdWJzY3JpYmUodCA9PiB7XHJcbiAgICAgIGlmICh0KSB7XHJcbiAgICAgICAgLy8gd2UgZG9udCBuZWVkIHRvIGRlbGV0ZSB0b2FzdCwgYmVjYXVzZSBpbnRlcm5hbGx5IGluIHRoZSBzZXJ2aWNlIHRoZSB0b2FzdCBoYXMgYmVlbiBkZWxldGVkIGFuZCBpbiBmdXR1cmUgdG9hc3RzLCBvbmx5IGxpdmUgdG9hc3RzIHdpbGwgYmUgcmVjZWl2ZWQgZnJvbSB0aGUgc2VydmljZVxyXG4gICAgICAgIC8vIGNvbnNvbGUubG9nKHQpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICB0aGlzLl90b2FzdFNlcnZpY2UudXBkYXRlVG9hc3QkLnN1YnNjcmliZSh0ID0+IHtcclxuICAgICAgaWYgKHQpIHtcclxuICAgICAgICAvLyBjb25zb2xlLmxvZyh0KTtcclxuICAgICAgfVxyXG4gICAgfSlcclxuICB9XHJcblxyXG4gIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpOiB2b2lkIHtcclxuICAgIGlmIChjaGFuZ2VzWydkdXJhdGlvbiddKSB7XHJcbiAgICAgIHRoaXMuX3RvYXN0U2VydmljZS5zZXREZWZhdWx0RHVyYXRpb24odGhpcy5kdXJhdGlvbik7XHJcbiAgICB9XHJcbiAgICBpZiAoY2hhbmdlc1snY2FwYWNpdHknXSkge1xyXG4gICAgICB0aGlzLl90b2FzdFNlcnZpY2Uuc2V0Q2FwYWNpdHkoY2hhbmdlc1snY2FwYWNpdHknXS5jdXJyZW50VmFsdWUpO1xyXG4gICAgfVxyXG4gICAgaWYgKGNoYW5nZXNbJ2dyb3VwaW5nJ10pIHtcclxuICAgICAgdGhpcy5fdG9hc3RTZXJ2aWNlLnNldEdyb3VwaW5nKGNoYW5nZXNbJ2dyb3VwaW5nJ10uY3VycmVudFZhbHVlKTtcclxuICAgIH1cclxuICB9XHJcblxyXG5cclxuICBjbG9zZVRvYXN0KGlkOiBudW1iZXIpOiB2b2lkIHtcclxuICAgIHRoaXMuX3RvYXN0U2VydmljZS5jbG9zZVRvYXN0KGlkKTtcclxuICB9XHJcblxyXG4gIGV4ZWN1dGVBY3Rpb24oYWN0aW9uOiBUb2FzdEFjdGlvbik6IHZvaWQge1xyXG4gICAgYWN0aW9uLmNhbGxiYWNrKCk7XHJcbiAgfVxyXG5cclxuICBjb250YWluZXJDbGFzcygpOiBzdHJpbmdbXSB7XHJcbiAgICByZXR1cm4gdGhpcy5wb3NpdGlvbk1hcFt0aGlzLnBvc2l0aW9uXSA/PyBbVG9hc3R5Q29udGFpbmVyUG9zaXRpb24uQk9UVE9NX1JJR0hUXTtcclxuICB9XHJcblxyXG4gIGV4cGlyZXModDogVG9hc3RNb2RlbCk6IGJvb2xlYW4ge1xyXG4gICAgcmV0dXJuICh0LmV4cGlyZXMgPCBEYXRlLm5vdygpKTtcclxuICB9XHJcblxyXG4gIGdldER1cmF0aW9uVW50aWxFeHBpcmUoZXhwaXJlczogbnVtYmVyKTogbnVtYmVyIHtcclxuICAgIGNvbnN0IGR1cmF0aW9uID0gZXhwaXJlcyAtIERhdGUubm93KCk7XHJcbiAgICByZXR1cm4gZHVyYXRpb24gPiAwID8gZHVyYXRpb24gOiAwO1xyXG4gIH1cclxuXHJcbiAgLy8gU3dpcGUgdG8gY2xvc2UgZnVuY3Rpb25hbGl0eVxyXG4gIEBIb3N0TGlzdGVuZXIoJ3RvdWNoc3RhcnQnLCBbJyRldmVudCddKVxyXG4gIG9uVG91Y2hTdGFydChldmVudDogVG91Y2hFdmVudCkge1xyXG4gICAgY29uc3QgdG9hc3RpZCA9IHRoaXMubG9jYWxpemVUb2FzdElkRnJvbVRvdWNoKGV2ZW50KTtcclxuICAgIHRoaXMuX3RvYXN0U2VydmljZS51cGRhdGVUb3VjaFN0YXJ0KHRvYXN0aWQsIGV2ZW50LmNoYW5nZWRUb3VjaGVzWzBdKTtcclxuICB9XHJcblxyXG4gIEBIb3N0TGlzdGVuZXIoJ3RvdWNoZW5kJywgWyckZXZlbnQnXSlcclxuICBvblRvdWNoRW5kKGV2ZW50OiBUb3VjaEV2ZW50KSB7XHJcbiAgICB0aGlzLl90b2FzdFNlcnZpY2UudXBkYXRlVG91Y2hFbmQoZXZlbnQuY2hhbmdlZFRvdWNoZXNbMF0pO1xyXG4gIH1cclxuXHJcbiAgQEhvc3RMaXN0ZW5lcigndG91Y2htb3ZlJywgWyckZXZlbnQnXSlcclxuICBvblRvdWNoTW92ZShldmVudDogVG91Y2hFdmVudCkge1xyXG4gICAgdGhpcy5fdG9hc3RTZXJ2aWNlLnVwZGF0ZVRvdWNoTW92ZShldmVudC5jaGFuZ2VkVG91Y2hlc1swXSk7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIGxvY2FsaXplVG9hc3RJZEZyb21Ub3VjaChldmVudDogVG91Y2hFdmVudCk6IG51bWJlciB7XHJcbiAgICBsZXQgY3VycmVudCA9IGV2ZW50LnRhcmdldCBhcyBIVE1MRWxlbWVudDtcclxuICAgIGxldCBtYXhuZXN0aW5nIDogbnVtYmVyID0gNDtcclxuICAgIHdoaWxlICghY3VycmVudC5jbGFzc0xpc3QuY29udGFpbnMoJ3RvYXN0eScpICYmIG1heG5lc3RpbmcgPiAwKSB7XHJcbiAgICAgIGN1cnJlbnQgPSBjdXJyZW50LnBhcmVudEVsZW1lbnQgYXMgSFRNTEVsZW1lbnQ7XHJcbiAgICAgIG1heG5lc3RpbmctLTtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gcGFyc2VJbnQoY3VycmVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtdG9hc3QtaWQnKSB8fCAnMCcpO1xyXG4gIH1cclxuXHJcbn1cclxuIiwiPGRpdiBjbGFzcz1cInRvYXN0eS1jb250YWluZXJcIiBbbmdDbGFzc109XCJjb250YWluZXJDbGFzcygpXCI+XHJcbiAgICBAZm9yIChpdGVtIG9mIHRvYXN0cyQgfCBhc3luYzsgdHJhY2sgaXRlbS5pZCkge1xyXG4gICAgPGRpdiBjbGFzcz1cInRvYXN0eS13cmFwcGVyXCIgW3N0eWxlLnRyYW5zZm9ybV09XCIndHJhbnNsYXRlWCgnICsgKGl0ZW0uZ2VzdHVyZT8ub2Zmc2V0WCB8fCAwKSArICdweCknXCJcclxuICAgICAgICBbc3R5bGUudHJhbnNpdGlvbl09XCJpdGVtLmdlc3R1cmUgPyAnbm9uZScgOiAndHJhbnNmb3JtIDAuM3MgZWFzZSdcIj5cclxuICAgICAgICA8ZGl2IGNsYXNzPVwidG9hc3R5XCIgW2F0dHIuZGF0YS10b2FzdC1pZF09XCJpdGVtLmlkXCIgW25nQ2xhc3NdPVwiaXRlbS50eXBlXCIgW2NsYXNzLmZhZGVvdXRdPVwiZXhwaXJlcyhpdGVtKVwiXHJcbiAgICAgICAgICAgIFtuZ1N0eWxlXT1cIml0ZW0uY29uZmlnPy5jdXN0b21TdHlsZVwiPlxyXG5cclxuICAgICAgICAgICAgPCEtLSBpY29uIC0tPlxyXG4gICAgICAgICAgICBAaWYgKGl0ZW0uY29uZmlnPy5sb2FkaW5nKSB7XHJcbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJ0b2FzdHktbG9hZGluZ1wiPjwvZGl2PlxyXG4gICAgICAgICAgICB9IEBlbHNlIGlmIChpdGVtLnR5cGUgPT0gVG9hc3RUeXBlLlN1Y2Nlc3MpIHtcclxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cInRvYXN0eS1pY29uXCI+XHJcbiAgICAgICAgICAgICAgICA8c3ZnIHhtbG5zPVwiaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmdcIiBjbGFzcz1cImgtNiB3LTYgc2hyaW5rLTAgc3Ryb2tlLWN1cnJlbnRcIiBmaWxsPVwibm9uZVwiXHJcbiAgICAgICAgICAgICAgICAgICAgdmlld0JveD1cIjAgMCAyNCAyNFwiPlxyXG4gICAgICAgICAgICAgICAgICAgIDxwYXRoIHN0cm9rZS1saW5lY2FwPVwicm91bmRcIiBzdHJva2UtbGluZWpvaW49XCJyb3VuZFwiIHN0cm9rZS13aWR0aD1cIjJcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICBkPVwiTTkgMTJsMiAyIDQtNG02IDJhOSA5IDAgMTEtMTggMCA5IDkgMCAwMTE4IDB6XCIgLz5cclxuICAgICAgICAgICAgICAgIDwvc3ZnPlxyXG4gICAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICAgICAgfSBAZWxzZSBpZiAoaXRlbS50eXBlID09IFRvYXN0VHlwZS5FcnJvcikge1xyXG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwidG9hc3R5LWljb25cIj5cclxuICAgICAgICAgICAgICAgIDxzdmcgeG1sbnM9XCJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Z1wiIGNsYXNzPVwiaC02IHctNiBzaHJpbmstMCBzdHJva2UtY3VycmVudFwiIGZpbGw9XCJub25lXCJcclxuICAgICAgICAgICAgICAgICAgICB2aWV3Qm94PVwiMCAwIDI0IDI0XCI+XHJcbiAgICAgICAgICAgICAgICAgICAgPHBhdGggc3Ryb2tlLWxpbmVjYXA9XCJyb3VuZFwiIHN0cm9rZS1saW5lam9pbj1cInJvdW5kXCIgc3Ryb2tlLXdpZHRoPVwiMlwiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGQ9XCJNMTAgMTRsMi0ybTAgMGwyLTJtLTIgMmwtMi0ybTIgMmwyIDJtNy0yYTkgOSAwIDExLTE4IDAgOSA5IDAgMDExOCAwelwiIC8+XHJcbiAgICAgICAgICAgICAgICA8L3N2Zz5cclxuICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgIH0gQGVsc2UgaWYgKGl0ZW0udHlwZSA9PSBUb2FzdFR5cGUuSW5mbykge1xyXG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwidG9hc3R5LWljb25cIj5cclxuICAgICAgICAgICAgICAgIDxzdmcgeG1sbnM9XCJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Z1wiIGZpbGw9XCJub25lXCIgdmlld0JveD1cIjAgMCAyNCAyNFwiXHJcbiAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJoLTYgdy02IHNocmluay0wIHN0cm9rZS1jdXJyZW50XCI+XHJcbiAgICAgICAgICAgICAgICAgICAgPHBhdGggc3Ryb2tlLWxpbmVjYXA9XCJyb3VuZFwiIHN0cm9rZS1saW5lam9pbj1cInJvdW5kXCIgc3Ryb2tlLXdpZHRoPVwiMlwiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGQ9XCJNMTMgMTZoLTF2LTRoLTFtMS00aC4wMU0yMSAxMmE5IDkgMCAxMS0xOCAwIDkgOSAwIDAxMTggMHpcIj48L3BhdGg+XHJcbiAgICAgICAgICAgICAgICA8L3N2Zz5cclxuICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgIH0gQGVsc2UgaWYgKGl0ZW0udHlwZSA9PSBUb2FzdFR5cGUuV2FybmluZykge1xyXG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwidG9hc3R5LWljb25cIj5cclxuICAgICAgICAgICAgICAgIDxzdmcgeG1sbnM9XCJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Z1wiIGNsYXNzPVwiaC02IHctNiBzaHJpbmstMCBzdHJva2UtY3VycmVudFwiIGZpbGw9XCJub25lXCJcclxuICAgICAgICAgICAgICAgICAgICB2aWV3Qm94PVwiMCAwIDI0IDI0XCI+XHJcbiAgICAgICAgICAgICAgICAgICAgPHBhdGggc3Ryb2tlLWxpbmVjYXA9XCJyb3VuZFwiIHN0cm9rZS1saW5lam9pbj1cInJvdW5kXCIgc3Ryb2tlLXdpZHRoPVwiMlwiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGQ9XCJNMTIgOXYybTAgNGguMDFtLTYuOTM4IDRoMTMuODU2YzEuNTQgMCAyLjUwMi0xLjY2NyAxLjczMi0zTDEzLjczMiA0Yy0uNzctMS4zMzMtMi42OTQtMS4zMzMtMy40NjQgMEwzLjM0IDE2Yy0uNzcgMS4zMzMuMTkyIDMgMS43MzIgM3pcIiAvPlxyXG4gICAgICAgICAgICAgICAgPC9zdmc+XHJcbiAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICB9IEBlbHNlIHtcclxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cInRvYXN0eS1ub2ljb25cIj48L2Rpdj5cclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cInRvYXN0eS1jb250ZW50XCI+XHJcbiAgICAgICAgICAgICAgICBAaWYgKGl0ZW0uY29uZmlnPy50eXBlID09IFRvYXN0VHlwZS5Db21wb25lbnQgJiYgaXRlbS5jb25maWc/LmNvbXBvbmVudCkge1xyXG4gICAgICAgICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdDb21wb25lbnRPdXRsZXQ9XCJpdGVtLmNvbmZpZz8uY29tcG9uZW50ID8/IG51bGw7IFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnB1dHM6IGl0ZW0uY29uZmlnPy5jb21wb25lbnRQYXJhbXNcIj5cclxuICAgICAgICAgICAgICAgIDwvbmctY29udGFpbmVyPlxyXG4gICAgICAgICAgICAgICAgfSBAZWxzZSB7XHJcbiAgICAgICAgICAgICAgICBAaWYgKGl0ZW0udGl0bGUpIHtcclxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJ0b2FzdHktaGVhZGVyXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgPHN0cm9uZyBjbGFzcz1cInRvYXN0eS10aXRsZVwiPnt7aXRlbS50aXRsZX19PC9zdHJvbmc+XHJcbiAgICAgICAgICAgICAgICAgICAgQGlmIChpdGVtLmNvdW50ID4gMSkge1xyXG4gICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidG9hc3R5LWJhZGdlXCI+eHt7aXRlbS5jb3VudH19PC9zcGFuPlxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICA8IS0tIDxzbWFsbCBjbGFzcz1cInRvYXN0eS10aW1lXCI+e3tpdGVtLnRpbWVzdGFtcH19PC9zbWFsbD4gLS0+XHJcbiAgICAgICAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIEBpZiAoaXRlbS5jb25maWc/LmVuYWJsZUh0bWwpIHtcclxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJ0b2FzdHktYm9keVwiIFtpbm5lckhUTUxdPVwiaXRlbS5tZXNzYWdlXCI+PC9kaXY+XHJcbiAgICAgICAgICAgICAgICB9IEBlbHNlIHtcclxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJ0b2FzdHktYm9keVwiPnt7aXRlbS5tZXNzYWdlfX08L2Rpdj5cclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIFxyXG4gICAgICAgICAgICAgICAgQGlmIChpdGVtLmNvbmZpZz8uYWN0aW9ucz8ubGVuZ3RoKSB7XHJcbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwidG9hc3R5LWFjdGlvbnNcIj5cclxuICAgICAgICAgICAgICAgICAgICBAZm9yIChhY3Rpb24gb2YgaXRlbS5jb25maWc/LmFjdGlvbnM7IHRyYWNrICRpbmRleCkge1xyXG4gICAgICAgICAgICAgICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJ0b2FzdHktYWN0aW9uLWJ0blwiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBbbmdDbGFzc109XCJhY3Rpb24/LmNsYXNzXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIChjbGljayk9XCJleGVjdXRlQWN0aW9uKGFjdGlvbilcIj5cclxuICAgICAgICAgICAgICAgICAgICAgICAge3thY3Rpb24/LmxhYmVsfX1cclxuICAgICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJ0b2FzdHktY2xvc2VcIj5cclxuICAgICAgICAgICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwidG9hc3R5LWNsb3NlXCIgKGNsaWNrKT1cImNsb3NlVG9hc3QoaXRlbS5pZClcIj4mdGltZXM7PC9idXR0b24+XHJcbiAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICBAaWYgKGl0ZW0uY29uZmlnPy5wcm9ncmVzc0Jhcikge1xyXG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwidG9hc3R5LXByb2dyZXNzXCIgW2NsYXNzLmFuaW1hdGVdPVwiaXRlbS5hbmltYXRlUGJhclwiXHJcbiAgICAgICAgICAgICAgICBbc3R5bGUudHJhbnNpdGlvbi1kdXJhdGlvbi5tc109XCJpdGVtLmR1cmF0aW9uXCI+PC9kaXY+XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICA8L2Rpdj5cclxuICAgIDwvZGl2PlxyXG4gICAgfVxyXG48L2Rpdj4iXX0=