carbon-components-angular
Version:
Next generation components
273 lines • 26.3 kB
JavaScript
import { Component, EventEmitter, HostListener, Input, Output, ViewChild, Inject } from "@angular/core";
import { DOCUMENT } from "@angular/common";
import { cycleTabs, getFocusElementList } from "carbon-components-angular/common";
import * as i0 from "@angular/core";
import * as i1 from "./base-modal.service";
import * as i2 from "@angular/common";
import * as i3 from "./overlay.component";
/**
* Component to create modals for presenting content.
*
* [See demo](../../?path=/story/components-modal--basic)
*
* Using a modal in your application requires `cds-placeholder` which would generally be
* placed near the end of your app component template (app.component.ts or app.component.html) as:
*
```html
<cds-placeholder></cds-placeholder>
```
*
* A more complete example for `Modal` is given as follows:
*
* Example modal definition:
*
```typescript
@Component({
selector: "app-sample-modal",
template: `
<cds-modal size="xl" (overlaySelected)="closeModal()">
<cds-modal-header (closeSelect)="closeModal()">Header text</cds-modal-header>
<section class="modal-body">
<h1>Sample modal works.</h1>
<button class="btn--icon-link" nPopover="Hello there" title="Popover title" placement="right" appendInline="true">
<svg cdsIcon="info" size="sm"></svg>
</button>
{{modalText}}
</section>
<cds-modal-footer><button cdsButton="primary" (click)="closeModal()">Close</button></cds-modal-footer>
</cds-modal>`,
styleUrls: ["./sample-modal.component.scss"]
})
export class SampleModal extends BaseModal {
modalText: string;
constructor(protected injector: Injector) {
super();
this.modalText = this.injector.get("modalText");
}
}
```
*
* Example of opening the modal:
*
```typescript
@Component({
selector: "app-modal-demo",
template: `
<button cdsButton="primary" (click)="openModal('drill')">Drill-down modal</button>
<cds-placeholder></cds-placeholder>`
})
export class ModalDemo {
openModal() {
this.modalService.create({component: SampleModal, inputs: {modalText: "Hello universe."}});
}
}
```
*/
export class Modal {
/**
* Creates an instance of `Modal`.
*/
constructor(modalService, document, renderer) {
this.modalService = modalService;
this.document = document;
this.renderer = renderer;
/**
* Size of the modal to display.
*/
this.size = "md";
/**
* Classification of the modal.
*/
this.theme = "default";
/**
* Label for the modal.
*/
this.ariaLabel = "default";
/**
* Controls the visibility of the modal when used directly in a template
*/
this.open = false;
/**
* Specify whether the modal contains scrolling content. This property overrides the automatic
* detection of the existence of scrolling content. Set this property to `true` to force
* overflow indicator to show up or to `false` to force overflow indicator to disappear.
* It is set to `null` by default which indicates not to override automatic detection.
*/
this.hasScrollingContent = null;
/**
* Emits event when click occurs within `n-overlay` element. This is to track click events occurring outside bounds of the `Modal` object.
*/
this.overlaySelected = new EventEmitter();
/**
* To emit the closing event of the modal window.
*/
this.close = new EventEmitter();
/**
* An element should have 'modal-primary-focus' as an attribute to receive initial focus within the `Modal` component.
*/
this.selectorPrimaryFocus = "[modal-primary-focus]";
}
ngOnChanges({ open }) {
if (open) {
if (open.currentValue) {
// `100` is just enough time to allow the modal
// to become visible, so that we can set focus
setTimeout(() => this.focusInitialElement(), 100);
// Prevent scrolling on open
this.renderer.addClass(this.document.body, "cds--body--with-modal-open");
}
else if (!open.currentValue) {
// Enable scrolling on close
this.renderer.removeClass(this.document.body, "cds--body--with-modal-open");
}
else if (this.trigger) {
this.trigger.focus();
}
}
}
/**
* Set document focus to be on the modal component after it is initialized.
*/
ngAfterViewInit() {
this.focusInitialElement();
}
/**
* Handle keyboard events to close modal and tab through the content within the modal.
*/
handleKeyboardEvent(event) {
switch (event.key) {
case "Escape": {
event.stopImmediatePropagation(); // prevents events being fired for multiple modals if more than 2 open
// Manually close modal
this.open = false;
this.close.emit();
this.modalService.destroy(); // destroy top (latest) modal
break;
}
case "Tab": {
cycleTabs(event, this.modal.nativeElement);
break;
}
}
}
/**
* This detects whether or not the modal contains scrolling content.
*
* To force trigger a detection (ie. on window resize), change or reset the value of the modal content.
*
* Use the `hasScrollingContent` input to manually override the overflow indicator.
*/
get shouldShowScrollbar() {
const modalContent = this.modal ? this.modal.nativeElement.querySelector(".cds--modal-content") : null;
if (modalContent) {
// get rounded value from height to match integer returned from scrollHeight
const modalContentHeight = Math.ceil(modalContent.getBoundingClientRect().height);
const modalContentScrollHeight = modalContent.scrollHeight;
return modalContentScrollHeight > modalContentHeight;
}
else {
return false;
}
}
// Remove class preventing scrolling
ngOnDestroy() {
this.renderer.removeClass(this.document.body, "cds--body--with-modal-open");
}
focusInitialElement() {
const primaryFocusElement = this.modal.nativeElement.querySelector(this.selectorPrimaryFocus);
if (primaryFocusElement && primaryFocusElement.focus) {
setTimeout(() => primaryFocusElement.focus());
}
else if (getFocusElementList(this.modal.nativeElement).length > 0) {
setTimeout(() => getFocusElementList(this.modal.nativeElement)[0].focus());
}
else {
setTimeout(() => this.modal.nativeElement.focus());
}
}
}
Modal.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: Modal, deps: [{ token: i1.BaseModalService }, { token: DOCUMENT }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
Modal.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: Modal, selector: "cds-modal, ibm-modal", inputs: { size: "size", theme: "theme", ariaLabel: "ariaLabel", open: "open", trigger: "trigger", hasScrollingContent: "hasScrollingContent" }, outputs: { overlaySelected: "overlaySelected", close: "close" }, host: { listeners: { "keydown": "handleKeyboardEvent($event)" } }, viewQueries: [{ propertyName: "modal", first: true, predicate: ["modal"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `
<cds-overlay
[theme]="theme"
[open]="open"
(overlaySelect)="overlaySelected.emit()">
<div
class="cds--modal-container"
[ngClass]="{
'cds--modal-container--xs': size === 'xs',
'cds--modal-container--sm': size === 'sm',
'cds--modal-container--md': size === 'md',
'cds--modal-container--lg': size === 'lg'
}"
role="dialog"
aria-modal="true"
style="z-index:1;"
[attr.aria-label]="ariaLabel"
#modal>
<ng-content></ng-content>
<div
*ngIf="hasScrollingContent !== null ? hasScrollingContent : shouldShowScrollbar"
class="cds--modal-content--overflow-indicator">
</div>
</div>
</cds-overlay>
`, isInline: true, dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.Overlay, selector: "cds-overlay, ibm-overlay", inputs: ["theme", "open"], outputs: ["overlaySelect"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: Modal, decorators: [{
type: Component,
args: [{
selector: "cds-modal, ibm-modal",
template: `
<cds-overlay
[theme]="theme"
[open]="open"
(overlaySelect)="overlaySelected.emit()">
<div
class="cds--modal-container"
[ngClass]="{
'cds--modal-container--xs': size === 'xs',
'cds--modal-container--sm': size === 'sm',
'cds--modal-container--md': size === 'md',
'cds--modal-container--lg': size === 'lg'
}"
role="dialog"
aria-modal="true"
style="z-index:1;"
[attr.aria-label]="ariaLabel"
#modal>
<ng-content></ng-content>
<div
*ngIf="hasScrollingContent !== null ? hasScrollingContent : shouldShowScrollbar"
class="cds--modal-content--overflow-indicator">
</div>
</div>
</cds-overlay>
`
}]
}], ctorParameters: function () { return [{ type: i1.BaseModalService }, { type: Document, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }, { type: i0.Renderer2 }]; }, propDecorators: { size: [{
type: Input
}], theme: [{
type: Input
}], ariaLabel: [{
type: Input
}], open: [{
type: Input
}], trigger: [{
type: Input
}], hasScrollingContent: [{
type: Input
}], overlaySelected: [{
type: Output
}], close: [{
type: Output
}], modal: [{
type: ViewChild,
args: ["modal", { static: true }]
}], handleKeyboardEvent: [{
type: HostListener,
args: ["keydown", ["$event"]]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kYWwuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL21vZGFsL21vZGFsLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBRU4sU0FBUyxFQUNULFlBQVksRUFDWixZQUFZLEVBQ1osS0FBSyxFQUNMLE1BQU0sRUFFTixTQUFTLEVBSVQsTUFBTSxFQUVOLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUMzQyxPQUFPLEVBQUUsU0FBUyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sa0NBQWtDLENBQUM7Ozs7O0FBR2xGOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F5REc7QUE4QkgsTUFBTSxPQUFPLEtBQUs7SUFtRGpCOztPQUVHO0lBQ0gsWUFDUSxZQUE4QixFQUNYLFFBQWtCLEVBQ3BDLFFBQW1CO1FBRnBCLGlCQUFZLEdBQVosWUFBWSxDQUFrQjtRQUNYLGFBQVEsR0FBUixRQUFRLENBQVU7UUFDcEMsYUFBUSxHQUFSLFFBQVEsQ0FBVztRQXhENUI7O1dBRUc7UUFDTSxTQUFJLEdBQTZCLElBQUksQ0FBQztRQUMvQzs7V0FFRztRQUNNLFVBQUssR0FBeUIsU0FBUyxDQUFDO1FBRWpEOztXQUVHO1FBQ00sY0FBUyxHQUFHLFNBQVMsQ0FBQztRQUUvQjs7V0FFRztRQUNNLFNBQUksR0FBRyxLQUFLLENBQUM7UUFPdEI7Ozs7O1dBS0c7UUFDTSx3QkFBbUIsR0FBWSxJQUFJLENBQUM7UUFFN0M7O1dBRUc7UUFDTyxvQkFBZSxHQUFHLElBQUksWUFBWSxFQUFFLENBQUM7UUFDL0M7O1dBRUc7UUFDTyxVQUFLLEdBQUcsSUFBSSxZQUFZLEVBQUUsQ0FBQztRQU1yQzs7V0FFRztRQUNILHlCQUFvQixHQUFHLHVCQUF1QixDQUFDO0lBUzNDLENBQUM7SUFFTCxXQUFXLENBQUMsRUFBRSxJQUFJLEVBQWlCO1FBQ2xDLElBQUksSUFBSSxFQUFFO1lBQ1QsSUFBSSxJQUFJLENBQUMsWUFBWSxFQUFFO2dCQUN0QiwrQ0FBK0M7Z0JBQy9DLDhDQUE4QztnQkFDOUMsVUFBVSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDO2dCQUNsRCw0QkFBNEI7Z0JBQzVCLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLDRCQUE0QixDQUFDLENBQUM7YUFDekU7aUJBQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUU7Z0JBQzlCLDRCQUE0QjtnQkFDNUIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsNEJBQTRCLENBQUMsQ0FBQzthQUM1RTtpQkFBTSxJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUU7Z0JBQ3hCLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7YUFDckI7U0FDRDtJQUNGLENBQUM7SUFFRDs7T0FFRztJQUNILGVBQWU7UUFDZCxJQUFJLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztJQUM1QixDQUFDO0lBRUQ7O09BRUc7SUFFSCxtQkFBbUIsQ0FBQyxLQUFvQjtRQUN2QyxRQUFRLEtBQUssQ0FBQyxHQUFHLEVBQUU7WUFDbEIsS0FBSyxRQUFRLENBQUMsQ0FBQztnQkFDZCxLQUFLLENBQUMsd0JBQXdCLEVBQUUsQ0FBQyxDQUFFLHNFQUFzRTtnQkFDekcsdUJBQXVCO2dCQUN2QixJQUFJLENBQUMsSUFBSSxHQUFHLEtBQUssQ0FBQztnQkFDbEIsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQztnQkFDbEIsSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFFLDZCQUE2QjtnQkFDM0QsTUFBTTthQUNOO1lBRUQsS0FBSyxLQUFLLENBQUMsQ0FBQztnQkFDWCxTQUFTLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsYUFBYSxDQUFDLENBQUM7Z0JBQzNDLE1BQU07YUFDTjtTQUNEO0lBQ0YsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNILElBQUksbUJBQW1CO1FBQ3RCLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsYUFBYSxDQUFDLGFBQWEsQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7UUFDdkcsSUFBSSxZQUFZLEVBQUU7WUFDakIsNEVBQTRFO1lBQzVFLE1BQU0sa0JBQWtCLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMscUJBQXFCLEVBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUNsRixNQUFNLHdCQUF3QixHQUFHLFlBQVksQ0FBQyxZQUFZLENBQUM7WUFDM0QsT0FBTyx3QkFBd0IsR0FBRyxrQkFBa0IsQ0FBQztTQUNyRDthQUFNO1lBQ04sT0FBTyxLQUFLLENBQUM7U0FDYjtJQUNGLENBQUM7SUFFRCxvQ0FBb0M7SUFDcEMsV0FBVztRQUNWLElBQUksQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLDRCQUE0QixDQUFDLENBQUM7SUFDN0UsQ0FBQztJQUVTLG1CQUFtQjtRQUM1QixNQUFNLG1CQUFtQixHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsYUFBYSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsb0JBQW9CLENBQUMsQ0FBQztRQUM5RixJQUFJLG1CQUFtQixJQUFJLG1CQUFtQixDQUFDLEtBQUssRUFBRTtZQUNyRCxVQUFVLENBQUMsR0FBRyxFQUFFLENBQUMsbUJBQW1CLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztTQUM5QzthQUFNLElBQUksbUJBQW1CLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxhQUFhLENBQUMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFO1lBQ3BFLFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7U0FDM0U7YUFBTTtZQUNOLFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO1NBQ25EO0lBQ0YsQ0FBQzs7a0dBM0lXLEtBQUssa0RBd0RSLFFBQVE7c0ZBeERMLEtBQUssbWRBM0JQOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0VBeUJUOzJGQUVXLEtBQUs7a0JBN0JqQixTQUFTO21CQUFDO29CQUNWLFFBQVEsRUFBRSxzQkFBc0I7b0JBQ2hDLFFBQVEsRUFBRTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztFQXlCVDtpQkFDRDs7MEJBeURFLE1BQU07MkJBQUMsUUFBUTtvRUFwRFIsSUFBSTtzQkFBWixLQUFLO2dCQUlHLEtBQUs7c0JBQWIsS0FBSztnQkFLRyxTQUFTO3NCQUFqQixLQUFLO2dCQUtHLElBQUk7c0JBQVosS0FBSztnQkFLRyxPQUFPO3NCQUFmLEtBQUs7Z0JBUUcsbUJBQW1CO3NCQUEzQixLQUFLO2dCQUtJLGVBQWU7c0JBQXhCLE1BQU07Z0JBSUcsS0FBSztzQkFBZCxNQUFNO2dCQUkrQixLQUFLO3NCQUExQyxTQUFTO3VCQUFDLE9BQU8sRUFBRSxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUU7Z0JBNENwQyxtQkFBbUI7c0JBRGxCLFlBQVk7dUJBQUMsU0FBUyxFQUFFLENBQUMsUUFBUSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcblx0QWZ0ZXJWaWV3SW5pdCxcblx0Q29tcG9uZW50LFxuXHRFdmVudEVtaXR0ZXIsXG5cdEhvc3RMaXN0ZW5lcixcblx0SW5wdXQsXG5cdE91dHB1dCxcblx0RWxlbWVudFJlZixcblx0Vmlld0NoaWxkLFxuXHRTaW1wbGVDaGFuZ2VzLFxuXHRPbkNoYW5nZXMsXG5cdFJlbmRlcmVyMixcblx0SW5qZWN0LFxuXHRPbkRlc3Ryb3lcbn0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcbmltcG9ydCB7IERPQ1VNRU5UIH0gZnJvbSBcIkBhbmd1bGFyL2NvbW1vblwiO1xuaW1wb3J0IHsgY3ljbGVUYWJzLCBnZXRGb2N1c0VsZW1lbnRMaXN0IH0gZnJvbSBcImNhcmJvbi1jb21wb25lbnRzLWFuZ3VsYXIvY29tbW9uXCI7XG5pbXBvcnQgeyBCYXNlTW9kYWxTZXJ2aWNlIH0gZnJvbSBcIi4vYmFzZS1tb2RhbC5zZXJ2aWNlXCI7XG5cbi8qKlxuICogQ29tcG9uZW50IHRvIGNyZWF0ZSBtb2RhbHMgZm9yIHByZXNlbnRpbmcgY29udGVudC5cbiAqXG4gKiBbU2VlIGRlbW9dKC4uLy4uLz9wYXRoPS9zdG9yeS9jb21wb25lbnRzLW1vZGFsLS1iYXNpYylcbiAqXG4gKiBVc2luZyBhIG1vZGFsIGluIHlvdXIgYXBwbGljYXRpb24gcmVxdWlyZXMgYGNkcy1wbGFjZWhvbGRlcmAgd2hpY2ggd291bGQgZ2VuZXJhbGx5IGJlXG4gKiBwbGFjZWQgbmVhciB0aGUgZW5kIG9mIHlvdXIgYXBwIGNvbXBvbmVudCB0ZW1wbGF0ZSAoYXBwLmNvbXBvbmVudC50cyBvciBhcHAuY29tcG9uZW50Lmh0bWwpIGFzOlxuICpcbmBgYGh0bWxcbjxjZHMtcGxhY2Vob2xkZXI+PC9jZHMtcGxhY2Vob2xkZXI+XG5gYGBcbiAqXG4gKiBBIG1vcmUgY29tcGxldGUgZXhhbXBsZSBmb3IgYE1vZGFsYCBpcyBnaXZlbiBhcyBmb2xsb3dzOlxuICpcbiAqIEV4YW1wbGUgbW9kYWwgZGVmaW5pdGlvbjpcbiAqXG5gYGB0eXBlc2NyaXB0XG5AQ29tcG9uZW50KHtcblx0c2VsZWN0b3I6IFwiYXBwLXNhbXBsZS1tb2RhbFwiLFxuXHR0ZW1wbGF0ZTogYFxuXHRcdFx0XHQ8Y2RzLW1vZGFsIHNpemU9XCJ4bFwiIChvdmVybGF5U2VsZWN0ZWQpPVwiY2xvc2VNb2RhbCgpXCI+XG5cdFx0XHRcdFx0PGNkcy1tb2RhbC1oZWFkZXIgKGNsb3NlU2VsZWN0KT1cImNsb3NlTW9kYWwoKVwiPkhlYWRlciB0ZXh0PC9jZHMtbW9kYWwtaGVhZGVyPlxuXHRcdFx0XHRcdFx0PHNlY3Rpb24gY2xhc3M9XCJtb2RhbC1ib2R5XCI+XG5cdFx0XHRcdFx0XHRcdDxoMT5TYW1wbGUgbW9kYWwgd29ya3MuPC9oMT5cblx0XHRcdFx0XHRcdFx0PGJ1dHRvbiBjbGFzcz1cImJ0bi0taWNvbi1saW5rXCIgblBvcG92ZXI9XCJIZWxsbyB0aGVyZVwiIHRpdGxlPVwiUG9wb3ZlciB0aXRsZVwiIHBsYWNlbWVudD1cInJpZ2h0XCIgYXBwZW5kSW5saW5lPVwidHJ1ZVwiPlxuXHRcdFx0XHRcdFx0XHRcdDxzdmcgY2RzSWNvbj1cImluZm9cIiBzaXplPVwic21cIj48L3N2Zz5cblx0XHRcdFx0XHRcdFx0PC9idXR0b24+XG5cdFx0XHRcdFx0XHRcdHt7bW9kYWxUZXh0fX1cblx0XHRcdFx0XHRcdDwvc2VjdGlvbj5cblx0XHRcdFx0XHQ8Y2RzLW1vZGFsLWZvb3Rlcj48YnV0dG9uIGNkc0J1dHRvbj1cInByaW1hcnlcIiAoY2xpY2spPVwiY2xvc2VNb2RhbCgpXCI+Q2xvc2U8L2J1dHRvbj48L2Nkcy1tb2RhbC1mb290ZXI+XG5cdFx0XHRcdDwvY2RzLW1vZGFsPmAsXG5cdHN0eWxlVXJsczogW1wiLi9zYW1wbGUtbW9kYWwuY29tcG9uZW50LnNjc3NcIl1cbn0pXG5leHBvcnQgY2xhc3MgU2FtcGxlTW9kYWwgZXh0ZW5kcyBCYXNlTW9kYWwge1xuXHRtb2RhbFRleHQ6IHN0cmluZztcblx0Y29uc3RydWN0b3IocHJvdGVjdGVkIGluamVjdG9yOiBJbmplY3Rvcikge1xuXHRcdHN1cGVyKCk7XG5cdFx0dGhpcy5tb2RhbFRleHQgPSB0aGlzLmluamVjdG9yLmdldChcIm1vZGFsVGV4dFwiKTtcblx0fVxufVxuYGBgXG4gKlxuICogRXhhbXBsZSBvZiBvcGVuaW5nIHRoZSBtb2RhbDpcbiAqXG5gYGB0eXBlc2NyaXB0XG5AQ29tcG9uZW50KHtcblx0c2VsZWN0b3I6IFwiYXBwLW1vZGFsLWRlbW9cIixcblx0dGVtcGxhdGU6IGBcblx0XHRcdFx0PGJ1dHRvbiBjZHNCdXR0b249XCJwcmltYXJ5XCIgKGNsaWNrKT1cIm9wZW5Nb2RhbCgnZHJpbGwnKVwiPkRyaWxsLWRvd24gbW9kYWw8L2J1dHRvbj5cblx0XHRcdFx0PGNkcy1wbGFjZWhvbGRlcj48L2Nkcy1wbGFjZWhvbGRlcj5gXG59KVxuZXhwb3J0IGNsYXNzIE1vZGFsRGVtbyB7XG5cdG9wZW5Nb2RhbCgpIHtcblx0XHR0aGlzLm1vZGFsU2VydmljZS5jcmVhdGUoe2NvbXBvbmVudDogU2FtcGxlTW9kYWwsIGlucHV0czoge21vZGFsVGV4dDogXCJIZWxsbyB1bml2ZXJzZS5cIn19KTtcblx0fVxufVxuYGBgXG4gKi9cbkBDb21wb25lbnQoe1xuXHRzZWxlY3RvcjogXCJjZHMtbW9kYWwsIGlibS1tb2RhbFwiLFxuXHR0ZW1wbGF0ZTogYFxuXHRcdDxjZHMtb3ZlcmxheVxuXHRcdFx0W3RoZW1lXT1cInRoZW1lXCJcblx0XHRcdFtvcGVuXT1cIm9wZW5cIlxuXHRcdFx0KG92ZXJsYXlTZWxlY3QpPVwib3ZlcmxheVNlbGVjdGVkLmVtaXQoKVwiPlxuXHRcdFx0PGRpdlxuXHRcdFx0XHRjbGFzcz1cImNkcy0tbW9kYWwtY29udGFpbmVyXCJcblx0XHRcdFx0W25nQ2xhc3NdPVwie1xuXHRcdFx0XHRcdCdjZHMtLW1vZGFsLWNvbnRhaW5lci0teHMnOiBzaXplID09PSAneHMnLFxuXHRcdFx0XHRcdCdjZHMtLW1vZGFsLWNvbnRhaW5lci0tc20nOiBzaXplID09PSAnc20nLFxuXHRcdFx0XHRcdCdjZHMtLW1vZGFsLWNvbnRhaW5lci0tbWQnOiBzaXplID09PSAnbWQnLFxuXHRcdFx0XHRcdCdjZHMtLW1vZGFsLWNvbnRhaW5lci0tbGcnOiBzaXplID09PSAnbGcnXG5cdFx0XHRcdH1cIlxuXHRcdFx0XHRyb2xlPVwiZGlhbG9nXCJcblx0XHRcdFx0YXJpYS1tb2RhbD1cInRydWVcIlxuXHRcdFx0XHRzdHlsZT1cInotaW5kZXg6MTtcIlxuXHRcdFx0XHRbYXR0ci5hcmlhLWxhYmVsXT1cImFyaWFMYWJlbFwiXG5cdFx0XHRcdCNtb2RhbD5cblx0XHRcdFx0PG5nLWNvbnRlbnQ+PC9uZy1jb250ZW50PlxuXHRcdFx0XHQ8ZGl2XG5cdFx0XHRcdFx0Km5nSWY9XCJoYXNTY3JvbGxpbmdDb250ZW50ICE9PSBudWxsID8gaGFzU2Nyb2xsaW5nQ29udGVudCA6IHNob3VsZFNob3dTY3JvbGxiYXJcIlxuXHRcdFx0XHRcdGNsYXNzPVwiY2RzLS1tb2RhbC1jb250ZW50LS1vdmVyZmxvdy1pbmRpY2F0b3JcIj5cblx0XHRcdFx0PC9kaXY+XG5cdFx0XHQ8L2Rpdj5cblx0XHQ8L2Nkcy1vdmVybGF5PlxuXHRgXG59KVxuZXhwb3J0IGNsYXNzIE1vZGFsIGltcGxlbWVudHMgQWZ0ZXJWaWV3SW5pdCwgT25DaGFuZ2VzLCBPbkRlc3Ryb3kge1xuXHQvKipcblx0ICogU2l6ZSBvZiB0aGUgbW9kYWwgdG8gZGlzcGxheS5cblx0ICovXG5cdEBJbnB1dCgpIHNpemU6IFwieHNcIiB8IFwic21cInwgXCJtZFwiIHwgXCJsZ1wiID0gXCJtZFwiO1xuXHQvKipcblx0ICogQ2xhc3NpZmljYXRpb24gb2YgdGhlIG1vZGFsLlxuXHQgKi9cblx0QElucHV0KCkgdGhlbWU6IFwiZGVmYXVsdFwiIHwgXCJkYW5nZXJcIiA9IFwiZGVmYXVsdFwiO1xuXG5cdC8qKlxuXHQgKiBMYWJlbCBmb3IgdGhlIG1vZGFsLlxuXHQgKi9cblx0QElucHV0KCkgYXJpYUxhYmVsID0gXCJkZWZhdWx0XCI7XG5cblx0LyoqXG5cdCAqIENvbnRyb2xzIHRoZSB2aXNpYmlsaXR5IG9mIHRoZSBtb2RhbCB3aGVuIHVzZWQgZGlyZWN0bHkgaW4gYSB0ZW1wbGF0ZVxuXHQgKi9cblx0QElucHV0KCkgb3BlbiA9IGZhbHNlO1xuXG5cdC8qKlxuXHQgKiBUaGUgZWxlbWVudCB0aGF0IHRyaWdnZXJzIHRoZSBtb2RhbCwgd2hpY2ggc2hvdWxkIHJlY2VpdmUgZm9jdXMgd2hlbiB0aGUgbW9kYWwgY2xvc2VzXG5cdCAqL1xuXHRASW5wdXQoKSB0cmlnZ2VyOiBIVE1MRWxlbWVudDtcblxuXHQvKipcblx0ICogU3BlY2lmeSB3aGV0aGVyIHRoZSBtb2RhbCBjb250YWlucyBzY3JvbGxpbmcgY29udGVudC4gVGhpcyBwcm9wZXJ0eSBvdmVycmlkZXMgdGhlIGF1dG9tYXRpY1xuXHQgKiBkZXRlY3Rpb24gb2YgdGhlIGV4aXN0ZW5jZSBvZiBzY3JvbGxpbmcgY29udGVudC4gU2V0IHRoaXMgcHJvcGVydHkgdG8gYHRydWVgIHRvIGZvcmNlXG5cdCAqIG92ZXJmbG93IGluZGljYXRvciB0byBzaG93IHVwIG9yIHRvIGBmYWxzZWAgdG8gZm9yY2Ugb3ZlcmZsb3cgaW5kaWNhdG9yIHRvIGRpc2FwcGVhci5cblx0ICogSXQgaXMgc2V0IHRvIGBudWxsYCBieSBkZWZhdWx0IHdoaWNoIGluZGljYXRlcyBub3QgdG8gb3ZlcnJpZGUgYXV0b21hdGljIGRldGVjdGlvbi5cblx0ICovXG5cdEBJbnB1dCgpIGhhc1Njcm9sbGluZ0NvbnRlbnQ6IGJvb2xlYW4gPSBudWxsO1xuXG5cdC8qKlxuXHQgKiBFbWl0cyBldmVudCB3aGVuIGNsaWNrIG9jY3VycyB3aXRoaW4gYG4tb3ZlcmxheWAgZWxlbWVudC4gVGhpcyBpcyB0byB0cmFjayBjbGljayBldmVudHMgb2NjdXJyaW5nIG91dHNpZGUgYm91bmRzIG9mIHRoZSBgTW9kYWxgIG9iamVjdC5cblx0ICovXG5cdEBPdXRwdXQoKSBvdmVybGF5U2VsZWN0ZWQgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cdC8qKlxuXHQgKiBUbyBlbWl0IHRoZSBjbG9zaW5nIGV2ZW50IG9mIHRoZSBtb2RhbCB3aW5kb3cuXG5cdCAqL1xuXHRAT3V0cHV0KCkgY2xvc2UgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cdC8qKlxuXHQgKiBNYWludGFpbnMgYSByZWZlcmVuY2UgdG8gdGhlIHZpZXcgRE9NIGVsZW1lbnQgb2YgdGhlIGBNb2RhbGAuXG5cdCAqL1xuXHRAVmlld0NoaWxkKFwibW9kYWxcIiwgeyBzdGF0aWM6IHRydWUgfSkgbW9kYWw6IEVsZW1lbnRSZWY7XG5cblx0LyoqXG5cdCAqIEFuIGVsZW1lbnQgc2hvdWxkIGhhdmUgJ21vZGFsLXByaW1hcnktZm9jdXMnIGFzIGFuIGF0dHJpYnV0ZSB0byByZWNlaXZlIGluaXRpYWwgZm9jdXMgd2l0aGluIHRoZSBgTW9kYWxgIGNvbXBvbmVudC5cblx0ICovXG5cdHNlbGVjdG9yUHJpbWFyeUZvY3VzID0gXCJbbW9kYWwtcHJpbWFyeS1mb2N1c11cIjtcblxuXHQvKipcblx0ICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBgTW9kYWxgLlxuXHQgKi9cblx0Y29uc3RydWN0b3IoXG5cdFx0cHVibGljIG1vZGFsU2VydmljZTogQmFzZU1vZGFsU2VydmljZSxcblx0XHRASW5qZWN0KERPQ1VNRU5UKSBwcml2YXRlIGRvY3VtZW50OiBEb2N1bWVudCxcblx0XHRwcml2YXRlIHJlbmRlcmVyOiBSZW5kZXJlcjJcblx0KSB7IH1cblxuXHRuZ09uQ2hhbmdlcyh7IG9wZW4gfTogU2ltcGxlQ2hhbmdlcykge1xuXHRcdGlmIChvcGVuKSB7XG5cdFx0XHRpZiAob3Blbi5jdXJyZW50VmFsdWUpIHtcblx0XHRcdFx0Ly8gYDEwMGAgaXMganVzdCBlbm91Z2ggdGltZSB0byBhbGxvdyB0aGUgbW9kYWxcblx0XHRcdFx0Ly8gdG8gYmVjb21lIHZpc2libGUsIHNvIHRoYXQgd2UgY2FuIHNldCBmb2N1c1xuXHRcdFx0XHRzZXRUaW1lb3V0KCgpID0+IHRoaXMuZm9jdXNJbml0aWFsRWxlbWVudCgpLCAxMDApO1xuXHRcdFx0XHQvLyBQcmV2ZW50IHNjcm9sbGluZyBvbiBvcGVuXG5cdFx0XHRcdHRoaXMucmVuZGVyZXIuYWRkQ2xhc3ModGhpcy5kb2N1bWVudC5ib2R5LCBcImNkcy0tYm9keS0td2l0aC1tb2RhbC1vcGVuXCIpO1xuXHRcdFx0fSBlbHNlIGlmICghb3Blbi5jdXJyZW50VmFsdWUpIHtcblx0XHRcdFx0Ly8gRW5hYmxlIHNjcm9sbGluZyBvbiBjbG9zZVxuXHRcdFx0XHR0aGlzLnJlbmRlcmVyLnJlbW92ZUNsYXNzKHRoaXMuZG9jdW1lbnQuYm9keSwgXCJjZHMtLWJvZHktLXdpdGgtbW9kYWwtb3BlblwiKTtcblx0XHRcdH0gZWxzZSBpZiAodGhpcy50cmlnZ2VyKSB7XG5cdFx0XHRcdHRoaXMudHJpZ2dlci5mb2N1cygpO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdC8qKlxuXHQgKiBTZXQgZG9jdW1lbnQgZm9jdXMgdG8gYmUgb24gdGhlIG1vZGFsIGNvbXBvbmVudCBhZnRlciBpdCBpcyBpbml0aWFsaXplZC5cblx0ICovXG5cdG5nQWZ0ZXJWaWV3SW5pdCgpIHtcblx0XHR0aGlzLmZvY3VzSW5pdGlhbEVsZW1lbnQoKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBIYW5kbGUga2V5Ym9hcmQgZXZlbnRzIHRvIGNsb3NlIG1vZGFsIGFuZCB0YWIgdGhyb3VnaCB0aGUgY29udGVudCB3aXRoaW4gdGhlIG1vZGFsLlxuXHQgKi9cblx0QEhvc3RMaXN0ZW5lcihcImtleWRvd25cIiwgW1wiJGV2ZW50XCJdKVxuXHRoYW5kbGVLZXlib2FyZEV2ZW50KGV2ZW50OiBLZXlib2FyZEV2ZW50KSB7XG5cdFx0c3dpdGNoIChldmVudC5rZXkpIHtcblx0XHRcdGNhc2UgXCJFc2NhcGVcIjoge1xuXHRcdFx0XHRldmVudC5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTsgIC8vIHByZXZlbnRzIGV2ZW50cyBiZWluZyBmaXJlZCBmb3IgbXVsdGlwbGUgbW9kYWxzIGlmIG1vcmUgdGhhbiAyIG9wZW5cblx0XHRcdFx0Ly8gTWFudWFsbHkgY2xvc2UgbW9kYWxcblx0XHRcdFx0dGhpcy5vcGVuID0gZmFsc2U7XG5cdFx0XHRcdHRoaXMuY2xvc2UuZW1pdCgpO1xuXHRcdFx0XHR0aGlzLm1vZGFsU2VydmljZS5kZXN0cm95KCk7ICAvLyBkZXN0cm95IHRvcCAobGF0ZXN0KSBtb2RhbFxuXHRcdFx0XHRicmVhaztcblx0XHRcdH1cblxuXHRcdFx0Y2FzZSBcIlRhYlwiOiB7XG5cdFx0XHRcdGN5Y2xlVGFicyhldmVudCwgdGhpcy5tb2RhbC5uYXRpdmVFbGVtZW50KTtcblx0XHRcdFx0YnJlYWs7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cblx0LyoqXG5cdCAqIFRoaXMgZGV0ZWN0cyB3aGV0aGVyIG9yIG5vdCB0aGUgbW9kYWwgY29udGFpbnMgc2Nyb2xsaW5nIGNvbnRlbnQuXG5cdCAqXG5cdCAqIFRvIGZvcmNlIHRyaWdnZXIgYSBkZXRlY3Rpb24gKGllLiBvbiB3aW5kb3cgcmVzaXplKSwgY2hhbmdlIG9yIHJlc2V0IHRoZSB2YWx1ZSBvZiB0aGUgbW9kYWwgY29udGVudC5cblx0ICpcblx0ICogVXNlIHRoZSBgaGFzU2Nyb2xsaW5nQ29udGVudGAgaW5wdXQgdG8gbWFudWFsbHkgb3ZlcnJpZGUgdGhlIG92ZXJmbG93IGluZGljYXRvci5cblx0ICovXG5cdGdldCBzaG91bGRTaG93U2Nyb2xsYmFyKCkge1xuXHRcdGNvbnN0IG1vZGFsQ29udGVudCA9IHRoaXMubW9kYWwgPyB0aGlzLm1vZGFsLm5hdGl2ZUVsZW1lbnQucXVlcnlTZWxlY3RvcihcIi5jZHMtLW1vZGFsLWNvbnRlbnRcIikgOiBudWxsO1xuXHRcdGlmIChtb2RhbENvbnRlbnQpIHtcblx0XHRcdC8vIGdldCByb3VuZGVkIHZhbHVlIGZyb20gaGVpZ2h0IHRvIG1hdGNoIGludGVnZXIgcmV0dXJuZWQgZnJvbSBzY3JvbGxIZWlnaHRcblx0XHRcdGNvbnN0IG1vZGFsQ29udGVudEhlaWdodCA9IE1hdGguY2VpbChtb2RhbENvbnRlbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkuaGVpZ2h0KTtcblx0XHRcdGNvbnN0IG1vZGFsQ29udGVudFNjcm9sbEhlaWdodCA9IG1vZGFsQ29udGVudC5zY3JvbGxIZWlnaHQ7XG5cdFx0XHRyZXR1cm4gbW9kYWxDb250ZW50U2Nyb2xsSGVpZ2h0ID4gbW9kYWxDb250ZW50SGVpZ2h0O1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXHR9XG5cblx0Ly8gUmVtb3ZlIGNsYXNzIHByZXZlbnRpbmcgc2Nyb2xsaW5nXG5cdG5nT25EZXN0cm95KCkge1xuXHRcdHRoaXMucmVuZGVyZXIucmVtb3ZlQ2xhc3ModGhpcy5kb2N1bWVudC5ib2R5LCBcImNkcy0tYm9keS0td2l0aC1tb2RhbC1vcGVuXCIpO1xuXHR9XG5cblx0cHJvdGVjdGVkIGZvY3VzSW5pdGlhbEVsZW1lbnQoKSB7XG5cdFx0Y29uc3QgcHJpbWFyeUZvY3VzRWxlbWVudCA9IHRoaXMubW9kYWwubmF0aXZlRWxlbWVudC5xdWVyeVNlbGVjdG9yKHRoaXMuc2VsZWN0b3JQcmltYXJ5Rm9jdXMpO1xuXHRcdGlmIChwcmltYXJ5Rm9jdXNFbGVtZW50ICYmIHByaW1hcnlGb2N1c0VsZW1lbnQuZm9jdXMpIHtcblx0XHRcdHNldFRpbWVvdXQoKCkgPT4gcHJpbWFyeUZvY3VzRWxlbWVudC5mb2N1cygpKTtcblx0XHR9IGVsc2UgaWYgKGdldEZvY3VzRWxlbWVudExpc3QodGhpcy5tb2RhbC5uYXRpdmVFbGVtZW50KS5sZW5ndGggPiAwKSB7XG5cdFx0XHRzZXRUaW1lb3V0KCgpID0+IGdldEZvY3VzRWxlbWVudExpc3QodGhpcy5tb2RhbC5uYXRpdmVFbGVtZW50KVswXS5mb2N1cygpKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0c2V0VGltZW91dCgoKSA9PiB0aGlzLm1vZGFsLm5hdGl2ZUVsZW1lbnQuZm9jdXMoKSk7XG5cdFx0fVxuXHR9XG59XG4iXX0=