UNPKG

carbon-components-angular

Version:
273 lines 26.3 kB
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kYWwuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL21vZGFsL21vZGFsLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBRU4sU0FBUyxFQUNULFlBQVksRUFDWixZQUFZLEVBQ1osS0FBSyxFQUNMLE1BQU0sRUFFTixTQUFTLEVBSVQsTUFBTSxFQUVOLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUMzQyxPQUFPLEVBQUUsU0FBUyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sa0NBQWtDLENBQUM7Ozs7O0FBR2xGOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F5REc7QUE4QkgsTUFBTSxPQUFPLEtBQUs7SUFtRGpCOztPQUVHO0lBQ0gsWUFDUSxZQUE4QixFQUNYLFFBQWtCLEVBQ3BDLFFBQW1CO1FBRnBCLGlCQUFZLEdBQVosWUFBWSxDQUFrQjtRQUNYLGFBQVEsR0FBUixRQUFRLENBQVU7UUFDcEMsYUFBUSxHQUFSLFFBQVEsQ0FBVztRQXhENUI7O1dBRUc7UUFDTSxTQUFJLEdBQTZCLElBQUksQ0FBQztRQUMvQzs7V0FFRztRQUNNLFVBQUssR0FBeUIsU0FBUyxDQUFDO1FBRWpEOztXQUVHO1FBQ00sY0FBUyxHQUFHLFNBQVMsQ0FBQztRQUUvQjs7V0FFRztRQUNNLFNBQUksR0FBRyxLQUFLLENBQUM7UUFPdEI7Ozs7O1dBS0c7UUFDTSx3QkFBbUIsR0FBWSxJQUFJLENBQUM7UUFFN0M7O1dBRUc7UUFDTyxvQkFBZSxHQUFHLElBQUksWUFBWSxFQUFFLENBQUM7UUFDL0M7O1dBRUc7UUFDTyxVQUFLLEdBQUcsSUFBSSxZQUFZLEVBQUUsQ0FBQztRQU1yQzs7V0FFRztRQUNILHlCQUFvQixHQUFHLHVCQUF1QixDQUFDO0lBUzNDLENBQUM7SUFFTCxX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Q0FBQyxFQUFFO1lBQ3BFLFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7U0FDM0U7YUFBTTtZQUNOLFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO1NBQ25EO0lBQ0YsQ0FBQzs7a0dBM0lXLEtBQUssa0RBd0RSLFFBQVE7c0ZBeERMLEtBQUssbWRBM0JQOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0VBeUJUOzJGQUVXLEtBQUs7a0JBN0JqQixTQUFTO21CQUFDO29CQUNWLFFBQVEsRUFBRSxzQkFBc0I7b0JBQ2hDLFFBQVEsRUFBRTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztFQXlCVDtpQkFDRDs7MEJBeURFLE1BQU07MkJBQUMsUUFBUTtvRUFwRFIsSUFBSTtzQkFBWixLQUFLO2dCQUlHLEtBQUs7c0JBQWIsS0FBSztnQkFLRyxTQUFTO3NCQUFqQixLQUFLO2dCQUtHLElBQUk7c0JBQVosS0FBSztnQkFLRyxPQUFPO3NCQUFmLEtBQUs7Z0JBUUcsbUJBQW1CO3NCQUEzQixLQUFLO2dCQUtJLGVBQWU7c0JBQXhCLE1BQU07Z0JBSUcsS0FBSztzQkFBZCxNQUFNO2dCQUkrQixLQUFLO3NCQUExQyxTQUFTO3VCQUFDLE9BQU8sRUFBRSxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUU7Z0JBNENwQyxtQkFBbUI7c0JBRGxCLFlBQVk7dUJBQUMsU0FBUyxFQUFFLENBQUMsUUFBUSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcblx0QWZ0ZXJWaWV3SW5pdCxcblx0Q29tcG9uZW50LFxuXHRFdmVudEVtaXR0ZXIsXG5cdEhvc3RMaXN0ZW5lcixcblx0SW5wdXQsXG5cdE91dHB1dCxcblx0RWxlbWVudFJlZixcblx0Vmlld0NoaWxkLFxuXHRTaW1wbGVDaGFuZ2VzLFxuXHRPbkNoYW5nZXMsXG5cdFJlbmRlcmVyMixcblx0SW5qZWN0LFxuXHRPbkRlc3Ryb3lcbn0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcbmltcG9ydCB7IERPQ1VNRU5UIH0gZnJvbSBcIkBhbmd1bGFyL2NvbW1vblwiO1xuaW1wb3J0IHsgY3ljbGVUYWJzLCBnZXRGb2N1c0VsZW1lbnRMaXN0IH0gZnJvbSBcImNhcmJvbi1jb21wb25lbnRzLWFuZ3VsYXIvY29tbW9uXCI7XG5pbXBvcnQgeyBCYXNlTW9kYWxTZXJ2aWNlIH0gZnJvbSBcIi4vYmFzZS1tb2RhbC5zZXJ2aWNlXCI7XG5cbi8qKlxuICogQ29tcG9uZW50IHRvIGNyZWF0ZSBtb2RhbHMgZm9yIHByZXNlbnRpbmcgY29udGVudC5cbiAqXG4gKiBbU2VlIGRlbW9dKC4uLy4uLz9wYXRoPS9zdG9yeS9jb21wb25lbnRzLW1vZGFsLS1iYXNpYylcbiAqXG4gKiBVc2luZyBhIG1vZGFsIGluIHlvdXIgYXBwbGljYXRpb24gcmVxdWlyZXMgYGNkcy1wbGFjZWhvbGRlcmAgd2hpY2ggd291bGQgZ2VuZXJhbGx5IGJlXG4gKiBwbGFjZWQgbmVhciB0aGUgZW5kIG9mIHlvdXIgYXBwIGNvbXBvbmVudCB0ZW1wbGF0ZSAoYXBwLmNvbXBvbmVudC50cyBvciBhcHAuY29tcG9uZW50Lmh0bWwpIGFzOlxuICpcbmBgYGh0bWxcbjxjZHMtcGxhY2Vob2xkZXI+PC9jZHMtcGxhY2Vob2xkZXI+XG5gYGBcbiAqXG4gKiBBIG1vcmUgY29tcGxldGUgZXhhbXBsZSBmb3IgYE1vZGFsYCBpcyBnaXZlbiBhcyBmb2xsb3dzOlxuICpcbiAqIEV4YW1wbGUgbW9kYWwgZGVmaW5pdGlvbjpcbiAqXG5gYGB0eXBlc2NyaXB0XG5AQ29tcG9uZW50KHtcblx0c2VsZWN0b3I6IFwiYXBwLXNhbXBsZS1tb2RhbFwiLFxuXHR0ZW1wbGF0ZTogYFxuXHRcdFx0XHQ8Y2RzLW1vZGFsIHNpemU9XCJ4bFwiIChvdmVybGF5U2VsZWN0ZWQpPVwiY2xvc2VNb2RhbCgpXCI+XG5cdFx0XHRcdFx0PGNkcy1tb2RhbC1oZWFkZXIgKGNsb3NlU2VsZWN0KT1cImNsb3NlTW9kYWwoKVwiPkhlYWRlciB0ZXh0PC9jZHMtbW9kYWwtaGVhZGVyPlxuXHRcdFx0XHRcdFx0PHNlY3Rpb24gY2xhc3M9XCJtb2RhbC1ib2R5XCI+XG5cdFx0XHRcdFx0XHRcdDxoMT5TYW1wbGUgbW9kYWwgd29ya3MuPC9oMT5cblx0XHRcdFx0XHRcdFx0PGJ1dHRvbiBjbGFzcz1cImJ0bi0taWNvbi1saW5rXCIgblBvcG92ZXI9XCJIZWxsbyB0aGVyZVwiIHRpdGxlPVwiUG9wb3ZlciB0aXRsZVwiIHBsYWNlbWVudD1cInJpZ2h0XCIgYXBwZW5kSW5saW5lPVwidHJ1ZVwiPlxuXHRcdFx0XHRcdFx0XHRcdDxzdmcgY2RzSWNvbj1cImluZm9cIiBzaXplPVwic21cIj48L3N2Zz5cblx0XHRcdFx0XHRcdFx0PC9idXR0b24+XG5cdFx0XHRcdFx0XHRcdHt7bW9kYWxUZXh0fX1cblx0XHRcdFx0XHRcdDwvc2VjdGlvbj5cblx0XHRcdFx0XHQ8Y2RzLW1vZGFsLWZvb3Rlcj48YnV0dG9uIGNkc0J1dHRvbj1cInByaW1hcnlcIiAoY2xpY2spPVwiY2xvc2VNb2RhbCgpXCI+Q2xvc2U8L2J1dHRvbj48L2Nkcy1tb2RhbC1mb290ZXI+XG5cdFx0XHRcdDwvY2RzLW1vZGFsPmAsXG5cdHN0eWxlVXJsczogW1wiLi9zYW1wbGUtbW9kYWwuY29tcG9uZW50LnNjc3NcIl1cbn0pXG5leHBvcnQgY2xhc3MgU2FtcGxlTW9kYWwgZXh0ZW5kcyBCYXNlTW9kYWwge1xuXHRtb2RhbFRleHQ6IHN0cmluZztcblx0Y29uc3RydWN0b3IocHJvdGVjdGVkIGluamVjdG9yOiBJbmplY3Rvcikge1xuXHRcdHN1cGVyKCk7XG5cdFx0dGhpcy5tb2RhbFRleHQgPSB0aGlzLmluamVjdG9yLmdldChcIm1vZGFsVGV4dFwiKTtcblx0fVxufVxuYGBgXG4gKlxuICogRXhhbXBsZSBvZiBvcGVuaW5nIHRoZSBtb2RhbDpcbiAqXG5gYGB0eXBlc2NyaXB0XG5AQ29tcG9uZW50KHtcblx0c2VsZWN0b3I6IFwiYXBwLW1vZGFsLWRlbW9cIixcblx0dGVtcGxhdGU6IGBcblx0XHRcdFx0PGJ1dHRvbiBjZHNCdXR0b249XCJwcmltYXJ5XCIgKGNsaWNrKT1cIm9wZW5Nb2RhbCgnZHJpbGwnKVwiPkRyaWxsLWRvd24gbW9kYWw8L2J1dHRvbj5cblx0XHRcdFx0PGNkcy1wbGFjZWhvbGRlcj48L2Nkcy1wbGFjZWhvbGRlcj5gXG59KVxuZXhwb3J0IGNsYXNzIE1vZGFsRGVtbyB7XG5cdG9wZW5Nb2RhbCgpIHtcblx0XHR0aGlzLm1vZGFsU2VydmljZS5jcmVhdGUoe2NvbXBvbmVudDogU2FtcGxlTW9kYWwsIGlucHV0czoge21vZGFsVGV4dDogXCJIZWxsbyB1bml2ZXJzZS5cIn19KTtcblx0fVxufVxuYGBgXG4gKi9cbkBDb21wb25lbnQoe1xuXHRzZWxlY3RvcjogXCJjZHMtbW9kYWwsIGlibS1tb2RhbFwiLFxuXHR0ZW1wbGF0ZTogYFxuXHRcdDxjZHMtb3ZlcmxheVxuXHRcdFx0W3RoZW1lXT1cInRoZW1lXCJcblx0XHRcdFtvcGVuXT1cIm9wZW5cIlxuXHRcdFx0KG92ZXJsYXlTZWxlY3QpPVwib3ZlcmxheVNlbGVjdGVkLmVtaXQoKVwiPlxuXHRcdFx0PGRpdlxuXHRcdFx0XHRjbGFzcz1cImNkcy0tbW9kYWwtY29udGFpbmVyXCJcblx0XHRcdFx0W25nQ2xhc3NdPVwie1xuXHRcdFx0XHRcdCdjZHMtLW1vZGFsLWNvbnRhaW5lci0teHMnOiBzaXplID09PSAneHMnLFxuXHRcdFx0XHRcdCdjZHMtLW1vZGFsLWNvbnRhaW5lci0tc20nOiBzaXplID09PSAnc20nLFxuXHRcdFx0XHRcdCdjZHMtLW1vZGFsLWNvbnRhaW5lci0tbWQnOiBzaXplID09PSAnbWQnLFxuXHRcdFx0XHRcdCdjZHMtLW1vZGFsLWNvbnRhaW5lci0tbGcnOiBzaXplID09PSAnbGcnXG5cdFx0XHRcdH1cIlxuXHRcdFx0XHRyb2xlPVwiZGlhbG9nXCJcblx0XHRcdFx0YXJpYS1tb2RhbD1cInRydWVcIlxuXHRcdFx0XHRzdHlsZT1cInotaW5kZXg6MTtcIlxuXHRcdFx0XHRbYXR0ci5hcmlhLWxhYmVsXT1cImFyaWFMYWJlbFwiXG5cdFx0XHRcdCNtb2RhbD5cblx0XHRcdFx0PG5nLWNvbnRlbnQ+PC9uZy1jb250ZW50PlxuXHRcdFx0XHQ8ZGl2XG5cdFx0XHRcdFx0Km5nSWY9XCJoYXNTY3JvbGxpbmdDb250ZW50ICE9PSBudWxsID8gaGFzU2Nyb2xsaW5nQ29udGVudCA6IHNob3VsZFNob3dTY3JvbGxiYXJcIlxuXHRcdFx0XHRcdGNsYXNzPVwiY2RzLS1tb2RhbC1jb250ZW50LS1vdmVyZmxvdy1pbmRpY2F0b3JcIj5cblx0XHRcdFx0PC9kaXY+XG5cdFx0XHQ8L2Rpdj5cblx0XHQ8L2Nkcy1vdmVybGF5PlxuXHRgXG59KVxuZXhwb3J0IGNsYXNzIE1vZGFsIGltcGxlbWVudHMgQWZ0ZXJWaWV3SW5pdCwgT25DaGFuZ2VzLCBPbkRlc3Ryb3kge1xuXHQvKipcblx0ICogU2l6ZSBvZiB0aGUgbW9kYWwgdG8gZGlzcGxheS5cblx0ICovXG5cdEBJbnB1dCgpIHNpemU6IFwieHNcIiB8IFwic21cInwgXCJtZFwiIHwgXCJsZ1wiID0gXCJtZFwiO1xuXHQvKipcblx0ICogQ2xhc3NpZmljYXRpb24gb2YgdGhlIG1vZGFsLlxuXHQgKi9cblx0QElucHV0KCkgdGhlbWU6IFwiZGVmYXVsdFwiIHwgXCJkYW5nZXJcIiA9IFwiZGVmYXVsdFwiO1xuXG5cdC8qKlxuXHQgKiBMYWJlbCBmb3IgdGhlIG1vZGFsLlxuXHQgKi9cblx0QElucHV0KCkgYXJpYUxhYmVsID0gXCJkZWZhdWx0XCI7XG5cblx0LyoqXG5cdCAqIENvbnRyb2xzIHRoZSB2aXNpYmlsaXR5IG9mIHRoZSBtb2RhbCB3aGVuIHVzZWQgZGlyZWN0bHkgaW4gYSB0ZW1wbGF0ZVxuXHQgKi9cblx0QElucHV0KCkgb3BlbiA9IGZhbHNlO1xuXG5cdC8qKlxuXHQgKiBUaGUgZWxlbWVudCB0aGF0IHRyaWdnZXJzIHRoZSBtb2RhbCwgd2hpY2ggc2hvdWxkIHJlY2VpdmUgZm9jdXMgd2hlbiB0aGUgbW9kYWwgY2xvc2VzXG5cdCAqL1xuXHRASW5wdXQoKSB0cmlnZ2VyOiBIVE1MRWxlbWVudDtcblxuXHQvKipcblx0ICogU3BlY2lmeSB3aGV0aGVyIHRoZSBtb2RhbCBjb250YWlucyBzY3JvbGxpbmcgY29udGVudC4gVGhpcyBwcm9wZXJ0eSBvdmVycmlkZXMgdGhlIGF1dG9tYXRpY1xuXHQgKiBkZXRlY3Rpb24gb2YgdGhlIGV4aXN0ZW5jZSBvZiBzY3JvbGxpbmcgY29udGVudC4gU2V0IHRoaXMgcHJvcGVydHkgdG8gYHRydWVgIHRvIGZvcmNlXG5cdCAqIG92ZXJmbG93IGluZGljYXRvciB0byBzaG93IHVwIG9yIHRvIGBmYWxzZWAgdG8gZm9yY2Ugb3ZlcmZsb3cgaW5kaWNhdG9yIHRvIGRpc2FwcGVhci5cblx0ICogSXQgaXMgc2V0IHRvIGBudWxsYCBieSBkZWZhdWx0IHdoaWNoIGluZGljYXRlcyBub3QgdG8gb3ZlcnJpZGUgYXV0b21hdGljIGRldGVjdGlvbi5cblx0ICovXG5cdEBJbnB1dCgpIGhhc1Njcm9sbGluZ0NvbnRlbnQ6IGJvb2xlYW4gPSBudWxsO1xuXG5cdC8qKlxuXHQgKiBFbWl0cyBldmVudCB3aGVuIGNsaWNrIG9jY3VycyB3aXRoaW4gYG4tb3ZlcmxheWAgZWxlbWVudC4gVGhpcyBpcyB0byB0cmFjayBjbGljayBldmVudHMgb2NjdXJyaW5nIG91dHNpZGUgYm91bmRzIG9mIHRoZSBgTW9kYWxgIG9iamVjdC5cblx0ICovXG5cdEBPdXRwdXQoKSBvdmVybGF5U2VsZWN0ZWQgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cdC8qKlxuXHQgKiBUbyBlbWl0IHRoZSBjbG9zaW5nIGV2ZW50IG9mIHRoZSBtb2RhbCB3aW5kb3cuXG5cdCAqL1xuXHRAT3V0cHV0KCkgY2xvc2UgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cdC8qKlxuXHQgKiBNYWludGFpbnMgYSByZWZlcmVuY2UgdG8gdGhlIHZpZXcgRE9NIGVsZW1lbnQgb2YgdGhlIGBNb2RhbGAuXG5cdCAqL1xuXHRAVmlld0NoaWxkKFwibW9kYWxcIiwgeyBzdGF0aWM6IHRydWUgfSkgbW9kYWw6IEVsZW1lbnRSZWY7XG5cblx0LyoqXG5cdCAqIEFuIGVsZW1lbnQgc2hvdWxkIGhhdmUgJ21vZGFsLXByaW1hcnktZm9jdXMnIGFzIGFuIGF0dHJpYnV0ZSB0byByZWNlaXZlIGluaXRpYWwgZm9jdXMgd2l0aGluIHRoZSBgTW9kYWxgIGNvbXBvbmVudC5cblx0ICovXG5cdHNlbGVjdG9yUHJpbWFyeUZvY3VzID0gXCJbbW9kYWwtcHJpbWFyeS1mb2N1c11cIjtcblxuXHQvKipcblx0ICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBgTW9kYWxgLlxuXHQgKi9cblx0Y29uc3RydWN0b3IoXG5cdFx0cHVibGljIG1vZGFsU2VydmljZTogQmFzZU1vZGFsU2VydmljZSxcblx0XHRASW5qZWN0KERPQ1VNRU5UKSBwcml2YXRlIGRvY3VtZW50OiBEb2N1bWVudCxcblx0XHRwcml2YXRlIHJlbmRlcmVyOiBSZW5kZXJlcjJcblx0KSB7IH1cblxuXHRuZ09uQ2hhbmdlcyh7IG9wZW4gfTogU2ltcGxlQ2hhbmdlcykge1xuXHRcdGlmIChvcGVuKSB7XG5cdFx0XHRpZiAob3Blbi5jdXJyZW50VmFsdWUpIHtcblx0XHRcdFx0Ly8gYDEwMGAgaXMganVzdCBlbm91Z2ggdGltZSB0byBhbGxvdyB0aGUgbW9kYWxcblx0XHRcdFx0Ly8gdG8gYmVjb21lIHZpc2libGUsIHNvIHRoYXQgd2UgY2FuIHNldCBmb2N1c1xuXHRcdFx0XHRzZXRUaW1lb3V0KCgpID0+IHRoaXMuZm9jdXNJbml0aWFsRWxlbWVudCgpLCAxMDApO1xuXHRcdFx0XHQvLyBQcmV2ZW50IHNjcm9sbGluZyBvbiBvcGVuXG5cdFx0XHRcdHRoaXMucmVuZGVyZXIuYWRkQ2xhc3ModGhpcy5kb2N1bWVudC5ib2R5LCBcImNkcy0tYm9keS0td2l0aC1tb2RhbC1vcGVuXCIpO1xuXHRcdFx0fSBlbHNlIGlmICghb3Blbi5jdXJyZW50VmFsdWUpIHtcblx0XHRcdFx0Ly8gRW5hYmxlIHNjcm9sbGluZyBvbiBjbG9zZVxuXHRcdFx0XHR0aGlzLnJlbmRlcmVyLnJlbW92ZUNsYXNzKHRoaXMuZG9jdW1lbnQuYm9keSwgXCJjZHMtLWJvZHktLXdpdGgtbW9kYWwtb3BlblwiKTtcblx0XHRcdH0gZWxzZSBpZiAodGhpcy50cmlnZ2VyKSB7XG5cdFx0XHRcdHRoaXMudHJpZ2dlci5mb2N1cygpO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdC8qKlxuXHQgKiBTZXQgZG9jdW1lbnQgZm9jdXMgdG8gYmUgb24gdGhlIG1vZGFsIGNvbXBvbmVudCBhZnRlciBpdCBpcyBpbml0aWFsaXplZC5cblx0ICovXG5cdG5nQWZ0ZXJWaWV3SW5pdCgpIHtcblx0XHR0aGlzLmZvY3VzSW5pdGlhbEVsZW1lbnQoKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBIYW5kbGUga2V5Ym9hcmQgZXZlbnRzIHRvIGNsb3NlIG1vZGFsIGFuZCB0YWIgdGhyb3VnaCB0aGUgY29udGVudCB3aXRoaW4gdGhlIG1vZGFsLlxuXHQgKi9cblx0QEhvc3RMaXN0ZW5lcihcImtleWRvd25cIiwgW1wiJGV2ZW50XCJdKVxuXHRoYW5kbGVLZXlib2FyZEV2ZW50KGV2ZW50OiBLZXlib2FyZEV2ZW50KSB7XG5cdFx0c3dpdGNoIChldmVudC5rZXkpIHtcblx0XHRcdGNhc2UgXCJFc2NhcGVcIjoge1xuXHRcdFx0XHRldmVudC5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTsgIC8vIHByZXZlbnRzIGV2ZW50cyBiZWluZyBmaXJlZCBmb3IgbXVsdGlwbGUgbW9kYWxzIGlmIG1vcmUgdGhhbiAyIG9wZW5cblx0XHRcdFx0Ly8gTWFudWFsbHkgY2xvc2UgbW9kYWxcblx0XHRcdFx0dGhpcy5vcGVuID0gZmFsc2U7XG5cdFx0XHRcdHRoaXMuY2xvc2UuZW1pdCgpO1xuXHRcdFx0XHR0aGlzLm1vZGFsU2VydmljZS5kZXN0cm95KCk7ICAvLyBkZXN0cm95IHRvcCAobGF0ZXN0KSBtb2RhbFxuXHRcdFx0XHRicmVhaztcblx0XHRcdH1cblxuXHRcdFx0Y2FzZSBcIlRhYlwiOiB7XG5cdFx0XHRcdGN5Y2xlVGFicyhldmVudCwgdGhpcy5tb2RhbC5uYXRpdmVFbGVtZW50KTtcblx0XHRcdFx0YnJlYWs7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cblx0LyoqXG5cdCAqIFRoaXMgZGV0ZWN0cyB3aGV0aGVyIG9yIG5vdCB0aGUgbW9kYWwgY29udGFpbnMgc2Nyb2xsaW5nIGNvbnRlbnQuXG5cdCAqXG5cdCAqIFRvIGZvcmNlIHRyaWdnZXIgYSBkZXRlY3Rpb24gKGllLiBvbiB3aW5kb3cgcmVzaXplKSwgY2hhbmdlIG9yIHJlc2V0IHRoZSB2YWx1ZSBvZiB0aGUgbW9kYWwgY29udGVudC5cblx0ICpcblx0ICogVXNlIHRoZSBgaGFzU2Nyb2xsaW5nQ29udGVudGAgaW5wdXQgdG8gbWFudWFsbHkgb3ZlcnJpZGUgdGhlIG92ZXJmbG93IGluZGljYXRvci5cblx0ICovXG5cdGdldCBzaG91bGRTaG93U2Nyb2xsYmFyKCkge1xuXHRcdGNvbnN0IG1vZGFsQ29udGVudCA9IHRoaXMubW9kYWwgPyB0aGlzLm1vZGFsLm5hdGl2ZUVsZW1lbnQucXVlcnlTZWxlY3RvcihcIi5jZHMtLW1vZGFsLWNvbnRlbnRcIikgOiBudWxsO1xuXHRcdGlmIChtb2RhbENvbnRlbnQpIHtcblx0XHRcdC8vIGdldCByb3VuZGVkIHZhbHVlIGZyb20gaGVpZ2h0IHRvIG1hdGNoIGludGVnZXIgcmV0dXJuZWQgZnJvbSBzY3JvbGxIZWlnaHRcblx0XHRcdGNvbnN0IG1vZGFsQ29udGVudEhlaWdodCA9IE1hdGguY2VpbChtb2RhbENvbnRlbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkuaGVpZ2h0KTtcblx0XHRcdGNvbnN0IG1vZGFsQ29udGVudFNjcm9sbEhlaWdodCA9IG1vZGFsQ29udGVudC5zY3JvbGxIZWlnaHQ7XG5cdFx0XHRyZXR1cm4gbW9kYWxDb250ZW50U2Nyb2xsSGVpZ2h0ID4gbW9kYWxDb250ZW50SGVpZ2h0O1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXHR9XG5cblx0Ly8gUmVtb3ZlIGNsYXNzIHByZXZlbnRpbmcgc2Nyb2xsaW5nXG5cdG5nT25EZXN0cm95KCkge1xuXHRcdHRoaXMucmVuZGVyZXIucmVtb3ZlQ2xhc3ModGhpcy5kb2N1bWVudC5ib2R5LCBcImNkcy0tYm9keS0td2l0aC1tb2RhbC1vcGVuXCIpO1xuXHR9XG5cblx0cHJvdGVjdGVkIGZvY3VzSW5pdGlhbEVsZW1lbnQoKSB7XG5cdFx0Y29uc3QgcHJpbWFyeUZvY3VzRWxlbWVudCA9IHRoaXMubW9kYWwubmF0aXZlRWxlbWVudC5xdWVyeVNlbGVjdG9yKHRoaXMuc2VsZWN0b3JQcmltYXJ5Rm9jdXMpO1xuXHRcdGlmIChwcmltYXJ5Rm9jdXNFbGVtZW50ICYmIHByaW1hcnlGb2N1c0VsZW1lbnQuZm9jdXMpIHtcblx0XHRcdHNldFRpbWVvdXQoKCkgPT4gcHJpbWFyeUZvY3VzRWxlbWVudC5mb2N1cygpKTtcblx0XHR9IGVsc2UgaWYgKGdldEZvY3VzRWxlbWVudExpc3QodGhpcy5tb2RhbC5uYXRpdmVFbGVtZW50KS5sZW5ndGggPiAwKSB7XG5cdFx0XHRzZXRUaW1lb3V0KCgpID0+IGdldEZvY3VzRWxlbWVudExpc3QodGhpcy5tb2RhbC5uYXRpdmVFbGVtZW50KVswXS5mb2N1cygpKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0c2V0VGltZW91dCgoKSA9PiB0aGlzLm1vZGFsLm5hdGl2ZUVsZW1lbnQuZm9jdXMoKSk7XG5cdFx0fVxuXHR9XG59XG4iXX0=