ngx-modialog-11
Version:
Modal / Dialog for Angular
223 lines • 27.3 kB
JavaScript
import { Component, ElementRef, ViewChild, ViewContainerRef, ViewEncapsulation, Renderer2, TemplateRef, HostListener } from '@angular/core';
import { PromiseCompleter, supportsKey } from '../framework/utils';
import { DialogRef } from '../models/dialog-ref';
import { BaseDynamicComponent } from '../components/index';
// TODO: use DI factory for this.
// TODO: consolidate dup code
const isDoc = !(typeof document === 'undefined' || !document);
/**
* Represents the modal overlay.
*/
// tslint:disable-next-line:component-class-suffix
export class ModalOverlay extends BaseDynamicComponent {
constructor(dialogRef, vcr, el, renderer) {
super(el, renderer);
this.dialogRef = dialogRef;
this.vcr = vcr;
this.activateAnimationListener();
}
/**
* @internal
*/
getProjectables(content) {
let nodes;
if (typeof content === 'string') {
nodes = [[this.renderer.createText(`${content}`)]];
}
else if (content instanceof TemplateRef) {
nodes = [this.vcr.createEmbeddedView(content, { $implicit: this.dialogRef.context, dialogRef: this.dialogRef }).rootNodes];
}
else {
nodes = [this.embedComponent({ component: content }).rootNodes];
}
return nodes;
}
embedComponent(config) {
const ctx = config;
return this.vcr.createEmbeddedView(this.template, {
$implicit: ctx
});
}
addComponent(type, projectableNodes = []) {
return super._addComponent({
component: type,
vcRef: this.innerVcr,
projectableNodes
});
}
fullscreen() {
const style = {
position: 'fixed',
top: 0,
left: 0,
bottom: 0,
right: 0,
'z-index': 1500
};
Object.keys(style).forEach(k => this.setStyle(k, style[k]));
}
insideElement() {
const style = {
position: 'absolute',
overflow: 'hidden',
width: '100%',
height: '100%',
top: 0,
left: 0,
bottom: 0,
right: 0
};
Object.keys(style).forEach(k => this.setStyle(k, style[k]));
}
/**
* Set a specific inline style for the container of the whole dialog component
* The dialog component root element is the host of this component, it contains only 1 direct
* child which is the container.
*
* Structure:
*
* ```html
* <modal-overlay>
* <div>
* <!-- BACKDROP ELEMENT -->
* <!-- DIALOG CONTAINER ELEMENT -->
* </div>
* </modal-overlay>
* ```
*
* @param prop The style key
* @param value The value, undefined to remove
*/
setContainerStyle(prop, value) {
this.renderer.setStyle(this.container.nativeElement, prop, value);
return this;
}
/**
* Define an element that click inside it will not trigger modal close.
* Since events bubble, clicking on a dialog will bubble up to the overlay, a plugin
* must define an element that represent the dialog, the overlay will make sure no to close when
* it was clicked.
* @param element
*/
setClickBoundary(element) {
let target;
const elListener = event => target = event.target;
const docListener = event => {
if (this.dialogRef.context.isBlocking || !this.dialogRef.overlay.isTopMost(this.dialogRef)) {
return;
}
let current = event.target;
// on click, this will hit.
if (current === target) {
return;
}
// on mouse down -> drag -> release the current might not be 'target', it might be
// a sibling or a child (i.e: not part of the tree-up direction). It might also be a release
// outside the dialog... so we compare to the boundary element
do {
if (current === element) {
return;
}
} while (current.parentNode && (current = current.parentNode));
this.dialogRef.dismiss();
};
if (isDoc) {
this.dialogRef.onDestroy.subscribe(() => {
element.removeEventListener('click', elListener, false);
element.removeEventListener('touchstart', elListener, false);
document.removeEventListener('click', docListener, false);
document.removeEventListener('touchend', docListener, false);
});
setTimeout(() => {
element.addEventListener('mousedown', elListener, false);
element.addEventListener('touchstart', docListener, false);
document.addEventListener('click', docListener, false);
document.addEventListener('touchend', docListener, false);
});
}
}
/**
* Temp workaround for animation where destruction of the top level component does not
* trigger child animations. Solution should be found either in animation module or in design
* of the modal component tree.
*/
canDestroy() {
const completer = new PromiseCompleter();
if (!Array.isArray(this.beforeDestroyHandlers)) {
completer.resolve();
}
else {
// run destroy notification but protect against halt.
let id = setTimeout(() => {
id = null;
completer.reject();
}, 1000);
const resolve = () => {
if (id === null) {
return;
}
clearTimeout(id);
completer.resolve();
};
Promise.all(this.beforeDestroyHandlers.map(fn => fn()))
.then(resolve)
.catch(resolve);
}
return completer.promise;
}
/**
* A handler running before destruction of the overlay
* use to delay destruction due to animation.
* This is part of the workaround for animation, see canDestroy.
*
* NOTE: There is no guarantee that the listeners will fire, use dialog.onDestory for that.
* @param fn
*/
beforeDestroy(fn) {
if (!this.beforeDestroyHandlers) {
this.beforeDestroyHandlers = [];
}
this.beforeDestroyHandlers.push(fn);
}
documentKeypress(event) {
// check that this modal is the last in the stack.
if (!this.dialogRef.overlay.isTopMost(this.dialogRef)) {
return;
}
if (supportsKey(event.keyCode, this.dialogRef.context.keyboard)) {
this.dialogRef.dismiss();
}
}
ngOnDestroy() {
super.ngOnDestroy();
if (this.dialogRef.destroyed !== true) {
// if we're here the overlay is destroyed by an external event that is not user invoked.
// i.e: The user did no call dismiss or close and dialogRef.destroy() did not invoke.
// this will happen when routing or killing an element containing a blocked overlay (ngIf)
// we bail out, i.e gracefully shutting down.
this.dialogRef.bailOut();
}
}
}
ModalOverlay.decorators = [
{ type: Component, args: [{
// tslint:disable-next-line:component-selector
selector: 'modal-overlay',
encapsulation: ViewEncapsulation.None,
template: "<div #container>\r\n <ng-template #innerView></ng-template>\r\n</div>\r\n<ng-template #template let-ctx>\r\n <ng-container *ngComponentOutlet=\"ctx.component; injector: ctx.injector; content: ctx.projectableNodes\"></ng-container>\r\n</ng-template>"
},] }
];
/** @nocollapse */
ModalOverlay.ctorParameters = () => [
{ type: DialogRef },
{ type: ViewContainerRef },
{ type: ElementRef },
{ type: Renderer2 }
];
ModalOverlay.propDecorators = {
container: [{ type: ViewChild, args: ['container', { read: ElementRef, static: true },] }],
innerVcr: [{ type: ViewChild, args: ['innerView', { read: ViewContainerRef, static: true },] }],
template: [{ type: ViewChild, args: ['template', { static: true },] }],
documentKeypress: [{ type: HostListener, args: ['body:keydown', ['$event'],] }]
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3ZlcmxheS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3gtbW9kaWFsb2ctMTEvc3JjL2xpYi9vdmVybGF5L292ZXJsYXkuY29tcG9uZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFDTCxTQUFTLEVBRVQsVUFBVSxFQUdWLFNBQVMsRUFDVCxnQkFBZ0IsRUFDaEIsaUJBQWlCLEVBQ2pCLFNBQVMsRUFDVCxXQUFXLEVBQUUsWUFBWSxFQUMxQixNQUFNLGVBQWUsQ0FBQztBQUV2QixPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsV0FBVyxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDbkUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBRWpELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRTNELGlDQUFpQztBQUNqQyw2QkFBNkI7QUFDN0IsTUFBTSxLQUFLLEdBQVksQ0FBQyxDQUFDLE9BQU8sUUFBUSxLQUFLLFdBQVcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0FBT3ZFOztHQUVHO0FBT0gsa0RBQWtEO0FBQ2xELE1BQU0sT0FBTyxZQUFhLFNBQVEsb0JBQW9CO0lBT3BELFlBQW9CLFNBQXlCLEVBQ3pCLEdBQXFCLEVBQzdCLEVBQWMsRUFDZCxRQUFtQjtRQUM3QixLQUFLLENBQUMsRUFBRSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBSkYsY0FBUyxHQUFULFNBQVMsQ0FBZ0I7UUFDekIsUUFBRyxHQUFILEdBQUcsQ0FBa0I7UUFJdkMsSUFBSSxDQUFDLHlCQUF5QixFQUFFLENBQUM7SUFDbkMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsZUFBZSxDQUFJLE9BQXlCO1FBRTFDLElBQUksS0FBWSxDQUFDO1FBQ2pCLElBQUksT0FBTyxPQUFPLEtBQUssUUFBUSxFQUFFO1lBQy9CLEtBQUssR0FBRyxDQUFDLENBQUMsSUFB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QUMsVUFBVSxFQUFFLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRTsrQkFtTXRDLFlBQVksU0FBQyxjQUFjLEVBQUUsQ0FBQyxRQUFRLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJkZWNsYXJlIGNvbnN0IGNsZWFyVGltZW91dDogYW55O1xyXG5cclxuaW1wb3J0IHtcclxuICBDb21wb25lbnQsXHJcbiAgQ29tcG9uZW50UmVmLFxyXG4gIEVsZW1lbnRSZWYsXHJcbiAgRW1iZWRkZWRWaWV3UmVmLFxyXG4gIEluamVjdG9yLFxyXG4gIFZpZXdDaGlsZCxcclxuICBWaWV3Q29udGFpbmVyUmVmLFxyXG4gIFZpZXdFbmNhcHN1bGF0aW9uLFxyXG4gIFJlbmRlcmVyMixcclxuICBUZW1wbGF0ZVJlZiwgSG9zdExpc3RlbmVyXHJcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcblxyXG5pbXBvcnQgeyBQcm9taXNlQ29tcGxldGVyLCBzdXBwb3J0c0tleSB9IGZyb20gJy4uL2ZyYW1ld29yay91dGlscyc7XHJcbmltcG9ydCB7IERpYWxvZ1JlZiB9IGZyb20gJy4uL21vZGVscy9kaWFsb2ctcmVmJztcclxuaW1wb3J0IHsgQ29udGFpbmVyQ29udGVudCB9IGZyb20gJy4uL21vZGVscy90b2tlbnMnO1xyXG5pbXBvcnQgeyBCYXNlRHluYW1pY0NvbXBvbmVudCB9IGZyb20gJy4uL2NvbXBvbmVudHMvaW5kZXgnO1xyXG5cclxuLy8gVE9ETzogdXNlIERJIGZhY3RvcnkgZm9yIHRoaXMuXHJcbi8vIFRPRE86IGNvbnNvbGlkYXRlIGR1cCBjb2RlXHJcbmNvbnN0IGlzRG9jOiBib29sZWFuID0gISh0eXBlb2YgZG9jdW1lbnQgPT09ICd1bmRlZmluZWQnIHx8ICFkb2N1bWVudCk7XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIEVtYmVkQ29tcG9uZW50Q29uZmlnIHtcclxuICBjb21wb25lbnQ6IGFueTtcclxuICBwcm9qZWN0YWJsZU5vZGVzPzogYW55W11bXTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJlcHJlc2VudHMgdGhlIG1vZGFsIG92ZXJsYXkuXHJcbiAqL1xyXG5AQ29tcG9uZW50KHtcclxuICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6Y29tcG9uZW50LXNlbGVjdG9yXHJcbiAgc2VsZWN0b3I6ICdtb2RhbC1vdmVybGF5JyxcclxuICBlbmNhcHN1bGF0aW9uOiBWaWV3RW5jYXBzdWxhdGlvbi5Ob25lLFxyXG4gIHRlbXBsYXRlVXJsOiAnLi9vdmVybGF5LmNvbXBvbmVudC5odG1sJ1xyXG59KVxyXG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6Y29tcG9uZW50LWNsYXNzLXN1ZmZpeFxyXG5leHBvcnQgY2xhc3MgTW9kYWxPdmVybGF5IGV4dGVuZHMgQmFzZUR5bmFtaWNDb21wb25lbnQge1xyXG4gIHByaXZhdGUgYmVmb3JlRGVzdHJveUhhbmRsZXJzOiBBcnJheTwoKSA9PiBQcm9taXNlPHZvaWQ+PjtcclxuXHJcbiAgQFZpZXdDaGlsZCgnY29udGFpbmVyJywge3JlYWQ6IEVsZW1lbnRSZWYsIHN0YXRpYzogdHJ1ZX0pIHB1YmxpYyBjb250YWluZXI6IEVsZW1lbnRSZWY7XHJcbiAgQFZpZXdDaGlsZCgnaW5uZXJWaWV3Jywge3JlYWQ6IFZpZXdDb250YWluZXJSZWYsIHN0YXRpYzogdHJ1ZSB9KSBwdWJsaWMgaW5uZXJWY3I6IFZpZXdDb250YWluZXJSZWY7XHJcbiAgQFZpZXdDaGlsZCgndGVtcGxhdGUnLCB7IHN0YXRpYzogdHJ1ZSB9KSBwdWJsaWMgdGVtcGxhdGU6IFRlbXBsYXRlUmVmPGFueT47XHJcblxyXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgZGlhbG9nUmVmOiBEaWFsb2dSZWY8YW55PixcclxuICAgICAgICAgICAgICBwcml2YXRlIHZjcjogVmlld0NvbnRhaW5lclJlZixcclxuICAgICAgICAgICAgICBlbDogRWxlbWVudFJlZixcclxuICAgICAgICAgICAgICByZW5kZXJlcjogUmVuZGVyZXIyKSB7XHJcbiAgICBzdXBlcihlbCwgcmVuZGVyZXIpO1xyXG4gICAgdGhpcy5hY3RpdmF0ZUFuaW1hdGlvbkxpc3RlbmVyKCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBAaW50ZXJuYWxcclxuICAgKi9cclxuICBnZXRQcm9qZWN0YWJsZXM8VD4oY29udGVudDogQ29udGFpbmVyQ29udGVudCk6IGFueVtdW10ge1xyXG5cclxuICAgIGxldCBub2RlczogYW55W107XHJcbiAgICBpZiAodHlwZW9mIGNvbnRlbnQgPT09ICdzdHJpbmcnKSB7XHJcbiAgICAgIG5vZGVzID0gW1t0aGlzLnJlbmRlcmVyLmNyZWF0ZVRleHQoYCR7Y29udGVudH1gKV1dO1xyXG4gICAgfSBlbHNlIGlmIChjb250ZW50IGluc3RhbmNlb2YgVGVtcGxhdGVSZWYpIHtcclxuICAgICAgbm9kZXMgPSBbdGhpcy52Y3IuY3JlYXRlRW1iZWRkZWRWaWV3KGNvbnRlbnQsIHskaW1wbGljaXQ6IHRoaXMuZGlhbG9nUmVmLmNvbnRleHQsIGRpYWxvZ1JlZjogdGhpcy5kaWFsb2dSZWZ9KS5yb290Tm9kZXNdO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbm9kZXMgPSBbdGhpcy5lbWJlZENvbXBvbmVudCh7Y29tcG9uZW50OiBjb250ZW50fSkucm9vdE5vZGVzXTtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gbm9kZXM7XHJcbiAgfVxyXG5cclxuICBlbWJlZENvbXBvbmVudChjb25maWc6IEVtYmVkQ29tcG9uZW50Q29uZmlnKTogRW1iZWRkZWRWaWV3UmVmPEVtYmVkQ29tcG9uZW50Q29uZmlnPiB7XHJcbiAgICBjb25zdCBjdHg6IEVtYmVkQ29tcG9uZW50Q29uZmlnICYgeyBpbmplY3RvcjogSW5qZWN0b3IgfSA9IDxhbnk+Y29uZmlnO1xyXG5cclxuICAgIHJldHVybiB0aGlzLnZjci5jcmVhdGVFbWJlZGRlZFZpZXcodGhpcy50ZW1wbGF0ZSwgPGFueT57XHJcbiAgICAgICRpbXBsaWNpdDogY3R4XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIGFkZENvbXBvbmVudDxUPih0eXBlOiBhbnksIHByb2plY3RhYmxlTm9kZXM6IGFueVtdW10gPSBbXSk6IENvbXBvbmVudFJlZjxUPiB7XHJcbiAgICByZXR1cm4gc3VwZXIuX2FkZENvbXBvbmVudDxUPih7XHJcbiAgICAgIGNvbXBvbmVudDogdHlwZSxcclxuICAgICAgdmNSZWY6IHRoaXMuaW5uZXJWY3IsXHJcbiAgICAgIHByb2plY3RhYmxlTm9kZXNcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgZnVsbHNjcmVlbigpOiB2b2lkIHtcclxuICAgIGNvbnN0IHN0eWxlID0ge1xyXG4gICAgICBwb3NpdGlvbjogJ2ZpeGVkJyxcclxuICAgICAgdG9wOiAwLFxyXG4gICAgICBsZWZ0OiAwLFxyXG4gICAgICBib3R0b206IDAsXHJcbiAgICAgIHJpZ2h0OiAwLFxyXG4gICAgICAnei1pbmRleCc6IDE1MDBcclxuICAgIH07XHJcbiAgICBPYmplY3Qua2V5cyhzdHlsZSkuZm9yRWFjaChrID0+IHRoaXMuc2V0U3R5bGUoaywgc3R5bGVba10pKTtcclxuICB9XHJcblxyXG4gIGluc2lkZUVsZW1lbnQoKTogdm9pZCB7XHJcbiAgICBjb25zdCBzdHlsZSA9IHtcclxuICAgICAgcG9zaXRpb246ICdhYnNvbHV0ZScsXHJcbiAgICAgIG92ZXJmbG93OiAnaGlkZGVuJyxcclxuICAgICAgd2lkdGg6ICcxMDAlJyxcclxuICAgICAgaGVpZ2h0OiAnMTAwJScsXHJcbiAgICAgIHRvcDogMCxcclxuICAgICAgbGVmdDogMCxcclxuICAgICAgYm90dG9tOiAwLFxyXG4gICAgICByaWdodDogMFxyXG4gICAgfTtcclxuICAgIE9iamVjdC5rZXlzKHN0eWxlKS5mb3JFYWNoKGsgPT4gdGhpcy5zZXRTdHlsZShrLCBzdHlsZVtrXSkpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogU2V0IGEgc3BlY2lmaWMgaW5saW5lIHN0eWxlIGZvciB0aGUgY29udGFpbmVyIG9mIHRoZSB3aG9sZSBkaWFsb2cgY29tcG9uZW50XHJcbiAgICogVGhlIGRpYWxvZyBjb21wb25lbnQgcm9vdCBlbGVtZW50IGlzIHRoZSBob3N0IG9mIHRoaXMgY29tcG9uZW50LCBpdCBjb250YWlucyBvbmx5IDEgZGlyZWN0XHJcbiAgICogY2hpbGQgd2hpY2ggaXMgdGhlIGNvbnRhaW5lci5cclxuICAgKlxyXG4gICAqIFN0cnVjdHVyZTpcclxuICAgKlxyXG4gICAqIGBgYGh0bWxcclxuICAgKiA8bW9kYWwtb3ZlcmxheT5cclxuICAgKiAgIDxkaXY+XHJcbiAgICogICAgIDwhLS0gQkFDS0RST1AgRUxFTUVOVCAtLT5cclxuICAgKiAgICAgPCEtLSBESUFMT0cgQ09OVEFJTkVSIEVMRU1FTlQgLS0+XHJcbiAgICogICA8L2Rpdj5cclxuICAgKiA8L21vZGFsLW92ZXJsYXk+XHJcbiAgICogYGBgXHJcbiAgICpcclxuICAgKiBAcGFyYW0gcHJvcCBUaGUgc3R5bGUga2V5XHJcbiAgICogQHBhcmFtIHZhbHVlIFRoZSB2YWx1ZSwgdW5kZWZpbmVkIHRvIHJlbW92ZVxyXG4gICAqL1xyXG4gIHNldENvbnRhaW5lclN0eWxlKHByb3A6IHN0cmluZywgdmFsdWU6IHN0cmluZyk6IHRoaXMge1xyXG4gICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZSh0aGlzLmNvbnRhaW5lci5uYXRpdmVFbGVtZW50LCBwcm9wLCB2YWx1ZSk7XHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIERlZmluZSBhbiBlbGVtZW50IHRoYXQgY2xpY2sgaW5zaWRlIGl0IHdpbGwgbm90IHRyaWdnZXIgbW9kYWwgY2xvc2UuXHJcbiAgICogU2luY2UgZXZlbnRzIGJ1YmJsZSwgY2xpY2tpbmcgb24gYSBkaWFsb2cgd2lsbCBidWJibGUgdXAgdG8gdGhlIG92ZXJsYXksIGEgcGx1Z2luXHJcbiAgICogbXVzdCBkZWZpbmUgYW4gZWxlbWVudCB0aGF0IHJlcHJlc2VudCB0aGUgZGlhbG9nLCB0aGUgb3ZlcmxheSB3aWxsIG1ha2Ugc3VyZSBubyB0byBjbG9zZSB3aGVuXHJcbiAgICogaXQgd2FzIGNsaWNrZWQuXHJcbiAgICogQHBhcmFtIGVsZW1lbnRcclxuICAgKi9cclxuICBzZXRDbGlja0JvdW5kYXJ5KGVsZW1lbnQ6IEVsZW1lbnQpOiB2b2lkIHtcclxuICAgIGxldCB0YXJnZXQ6IEVsZW1lbnQ7XHJcbiAgICBjb25zdCBlbExpc3RlbmVyID0gZXZlbnQgPT4gdGFyZ2V0ID0gZXZlbnQudGFyZ2V0IGFzIGFueTtcclxuICAgIGNvbnN0IGRvY0xpc3RlbmVyID0gZXZlbnQgPT4ge1xyXG4gICAgICBpZiAodGhpcy5kaWFsb2dSZWYuY29udGV4dC5pc0Jsb2NraW5nIHx8ICF0aGlzLmRpYWxvZ1JlZi5vdmVybGF5LmlzVG9wTW9zdCh0aGlzLmRpYWxvZ1JlZikpIHtcclxuICAgICAgICByZXR1cm47XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGxldCBjdXJyZW50OiBhbnkgPSBldmVudC50YXJnZXQ7XHJcblxyXG4gICAgICAvLyBvbiBjbGljaywgdGhpcyB3aWxsIGhpdC5cclxuICAgICAgaWYgKGN1cnJlbnQgPT09IHRhcmdldCkge1xyXG4gICAgICAgIHJldHVybjtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gb24gbW91c2UgZG93biAtPiBkcmFnIC0+IHJlbGVhc2UgdGhlIGN1cnJlbnQgbWlnaHQgbm90IGJlICd0YXJnZXQnLCBpdCBtaWdodCBiZVxyXG4gICAgICAvLyBhIHNpYmxpbmcgb3IgYSBjaGlsZCAoaS5lOiBub3QgcGFydCBvZiB0aGUgdHJlZS11cCBkaXJlY3Rpb24pLiBJdCBtaWdodCBhbHNvIGJlIGEgcmVsZWFzZVxyXG4gICAgICAvLyBvdXRzaWRlIHRoZSBkaWFsb2cuLi4gc28gd2UgY29tcGFyZSB0byB0aGUgYm91bmRhcnkgZWxlbWVudFxyXG4gICAgICBkbyB7XHJcbiAgICAgICAgaWYgKGN1cnJlbnQgPT09IGVsZW1lbnQpIHtcclxuICAgICAgICAgIHJldHVybjtcclxuICAgICAgICB9XHJcbiAgICAgIH0gd2hpbGUgKGN1cnJlbnQucGFyZW50Tm9kZSAmJiAoY3VycmVudCA9IGN1cnJlbnQucGFyZW50Tm9kZSkpO1xyXG4gICAgICB0aGlzLmRpYWxvZ1JlZi5kaXNtaXNzKCk7XHJcbiAgICB9O1xyXG5cclxuICAgIGlmIChpc0RvYykge1xyXG4gICAgICB0aGlzLmRpYWxvZ1JlZi5vbkRlc3Ryb3kuc3Vic2NyaWJlKCgpID0+IHtcclxuICAgICAgICBlbGVtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZWxMaXN0ZW5lciwgZmFsc2UpO1xyXG4gICAgICAgIGVsZW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcigndG91Y2hzdGFydCcsIGVsTGlzdGVuZXIsIGZhbHNlKTtcclxuICAgICAgICBkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCdjbGljaycsIGRvY0xpc3RlbmVyLCBmYWxzZSk7XHJcbiAgICAgICAgZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcigndG91Y2hlbmQnLCBkb2NMaXN0ZW5lciwgZmFsc2UpO1xyXG4gICAgICB9KTtcclxuXHJcblxyXG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlZG93bicsIGVsTGlzdGVuZXIsIGZhbHNlKTtcclxuICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3RvdWNoc3RhcnQnLCBkb2NMaXN0ZW5lciwgZmFsc2UpO1xyXG4gICAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZG9jTGlzdGVuZXIsIGZhbHNlKTtcclxuICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCd0b3VjaGVuZCcsIGRvY0xpc3RlbmVyLCBmYWxzZSk7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogVGVtcCB3b3JrYXJvdW5kIGZvciBhbmltYXRpb24gd2hlcmUgZGVzdHJ1Y3Rpb24gb2YgdGhlIHRvcCBsZXZlbCBjb21wb25lbnQgZG9lcyBub3RcclxuICAgKiB0cmlnZ2VyIGNoaWxkIGFuaW1hdGlvbnMuIFNvbHV0aW9uIHNob3VsZCBiZSBmb3VuZCBlaXRoZXIgaW4gYW5pbWF0aW9uIG1vZHVsZSBvciBpbiBkZXNpZ25cclxuICAgKiBvZiB0aGUgbW9kYWwgY29tcG9uZW50IHRyZWUuXHJcbiAgICovXHJcbiAgY2FuRGVzdHJveSgpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgIGNvbnN0IGNvbXBsZXRlciA9IG5ldyBQcm9taXNlQ29tcGxldGVyPHZvaWQ+KCk7XHJcblxyXG4gICAgaWYgKCFBcnJheS5pc0FycmF5KHRoaXMuYmVmb3JlRGVzdHJveUhhbmRsZXJzKSkge1xyXG4gICAgICBjb21wbGV0ZXIucmVzb2x2ZSgpO1xyXG4gICAgfSBlbHNlIHtcclxuXHJcbiAgICAgIC8vIHJ1biBkZXN0cm95IG5vdGlmaWNhdGlvbiBidXQgcHJvdGVjdCBhZ2FpbnN0IGhhbHQuXHJcbiAgICAgIGxldCBpZCA9IHNldFRpbWVvdXQoKCkgPT4ge1xyXG4gICAgICAgIGlkID0gbnVsbDtcclxuICAgICAgICBjb21wbGV0ZXIucmVqZWN0KCk7XHJcbiAgICAgIH0sIDEwMDApO1xyXG5cclxuICAgICAgY29uc3QgcmVzb2x2ZSA9ICgpID0+IHtcclxuICAgICAgICBpZiAoaWQgPT09IG51bGwpIHtcclxuICAgICAgICAgIHJldHVybjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGNsZWFyVGltZW91dChpZCk7XHJcbiAgICAgICAgY29tcGxldGVyLnJlc29sdmUoKTtcclxuICAgICAgfTtcclxuXHJcbiAgICAgIFByb21pc2UuYWxsKHRoaXMuYmVmb3JlRGVzdHJveUhhbmRsZXJzLm1hcChmbiA9PiBmbigpKSlcclxuICAgICAgICAudGhlbihyZXNvbHZlKVxyXG4gICAgICAgIC5jYXRjaChyZXNvbHZlKTtcclxuXHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIGNvbXBsZXRlci5wcm9taXNlO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogQSBoYW5kbGVyIHJ1bm5pbmcgYmVmb3JlIGRlc3RydWN0aW9uIG9mIHRoZSBvdmVybGF5XHJcbiAgICogdXNlIHRvIGRlbGF5IGRlc3RydWN0aW9uIGR1ZSB0byBhbmltYXRpb24uXHJcbiAgICogVGhpcyBpcyBwYXJ0IG9mIHRoZSB3b3JrYXJvdW5kIGZvciBhbmltYXRpb24sIHNlZSBjYW5EZXN0cm95LlxyXG4gICAqXHJcbiAgICogTk9URTogVGhlcmUgaXMgbm8gZ3VhcmFudGVlIHRoYXQgdGhlIGxpc3RlbmVycyB3aWxsIGZpcmUsIHVzZSBkaWFsb2cub25EZXN0b3J5IGZvciB0aGF0LlxyXG4gICAqIEBwYXJhbSBmblxyXG4gICAqL1xyXG4gIGJlZm9yZURlc3Ryb3koZm46ICgpID0+IFByb21pc2U8dm9pZD4pIHtcclxuICAgIGlmICghdGhpcy5iZWZvcmVEZXN0cm95SGFuZGxlcnMpIHtcclxuICAgICAgdGhpcy5iZWZvcmVEZXN0cm95SGFuZGxlcnMgPSBbXTtcclxuICAgIH1cclxuICAgIHRoaXMuYmVmb3JlRGVzdHJveUhhbmRsZXJzLnB1c2goZm4pO1xyXG4gIH1cclxuXHJcbiAgQEhvc3RMaXN0ZW5lcignYm9keTprZXlkb3duJywgWyckZXZlbnQnXSlcclxuICBkb2N1bWVudEtleXByZXNzKGV2ZW50OiBLZXlib2FyZEV2ZW50KSB7XHJcbiAgICAvLyBjaGVjayB0aGF0IHRoaXMgbW9kYWwgaXMgdGhlIGxhc3QgaW4gdGhlIHN0YWNrLlxyXG4gICAgaWYgKCF0aGlzLmRpYWxvZ1JlZi5vdmVybGF5LmlzVG9wTW9zdCh0aGlzLmRpYWxvZ1JlZikpIHtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuXHJcbiAgICBpZiAoc3VwcG9ydHNLZXkoZXZlbnQua2V5Q29kZSwgPGFueT50aGlzLmRpYWxvZ1JlZi5jb250ZXh0LmtleWJvYXJkKSkge1xyXG4gICAgICB0aGlzLmRpYWxvZ1JlZi5kaXNtaXNzKCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBuZ09uRGVzdHJveSgpOiB2b2lkIHtcclxuICAgIHN1cGVyLm5nT25EZXN0cm95KCk7XHJcbiAgICBpZiAodGhpcy5kaWFsb2dSZWYuZGVzdHJveWVkICE9PSB0cnVlKSB7XHJcbiAgICAgIC8vIGlmIHdlJ3JlIGhlcmUgdGhlIG92ZXJsYXkgaXMgZGVzdHJveWVkIGJ5IGFuIGV4dGVybmFsIGV2ZW50IHRoYXQgaXMgbm90IHVzZXIgaW52b2tlZC5cclxuICAgICAgLy8gaS5lOiBUaGUgdXNlciBkaWQgbm8gY2FsbCBkaXNtaXNzIG9yIGNsb3NlIGFuZCBkaWFsb2dSZWYuZGVzdHJveSgpIGRpZCBub3QgaW52b2tlLlxyXG4gICAgICAvLyB0aGlzIHdpbGwgaGFwcGVuIHdoZW4gcm91dGluZyBvciBraWxsaW5nIGFuIGVsZW1lbnQgY29udGFpbmluZyBhIGJsb2NrZWQgb3ZlcmxheSAobmdJZilcclxuICAgICAgLy8gd2UgYmFpbCBvdXQsIGkuZSBncmFjZWZ1bGx5IHNodXR0aW5nIGRvd24uXHJcbiAgICAgIHRoaXMuZGlhbG9nUmVmLmJhaWxPdXQoKTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuIl19