UNPKG

carbon-components-angular

Version:
302 lines (300 loc) 31.2 kB
import { ChangeDetectionStrategy, Component, ContentChildren, EventEmitter, HostBinding, Input, Output, ViewChild } from "@angular/core"; import { autoUpdate, computePosition, flip } from "@floating-ui/dom"; import { ContextMenuItemComponent } from "carbon-components-angular/context-menu"; import * as i0 from "@angular/core"; import * as i1 from "carbon-components-angular/button"; import * as i2 from "carbon-components-angular/icon"; import * as i3 from "carbon-components-angular/context-menu"; export class ComboButtonComponent { constructor(ngZone, renderer, hostElement, viewContainerRef, changeDetectorRef) { this.ngZone = ngZone; this.renderer = renderer; this.hostElement = hostElement; this.viewContainerRef = viewContainerRef; this.changeDetectorRef = changeDetectorRef; this.comboId = `combo-button-${ComboButtonComponent.comboButtonCounter++}`; this.size = "lg"; this.disabled = false; this.menuAlignment = "bottom"; this.tooltipAutoAlign = false; this.tooltipPlacement = "bottom"; this.open = false; this.actionClick = new EventEmitter(); this.comboButtonContainer = true; this.documentClick = this.handleFocusOut.bind(this); this.subscriptions = []; this._alignment = "bottom"; } // Listen for click & determine if menu should close set projectedMenuItems(itemList) { // Reset in case user dynamically updates menu item this.subscriptions.forEach((sub) => sub?.unsubscribe()); this.subscriptions = []; itemList.forEach((item) => { this.subscriptions.push(item.itemClick.subscribe((clickEvent) => this.handleMenuItemClick(clickEvent))); }); } get sizeLg() { return this.size === "lg"; } get sizeMd() { return this.size === "md"; } get sizeSm() { return this.size === "sm"; } get ariaOwns() { return this.open ? this.comboId : undefined; } /** * In case user updates alignment, store initial value. * This allows us to test user passed alignment on each open */ ngOnChanges(changes) { if (changes.menuAlignment) { this._alignment = changes.menuAlignment.currentValue; } } /** * If user has passed in true for open, we dynamically open the menu */ ngAfterViewInit() { if (this.open) { this.open = !this.open; this.toggleMenu(); } } /** * Clean up Floating-ui & subscriptions */ ngOnDestroy() { this.cleanUp(); this.subscriptions.forEach((sub) => sub.unsubscribe()); } /** * As of now, menu button does not support nexted menu, on button click it should close */ handleMenuItemClick(event) { // If event is not type radio/checkbox, we close the menu if (!event.type) { this.toggleMenu(); } } /** * On body click, close the menu * @param event */ handleFocusOut(event) { if (!this.hostElement.nativeElement.contains(event.target)) { this.toggleMenu(); } } /** * Clean up `autoUpdate` if auto alignment is enabled */ cleanUp() { document.removeEventListener("click", this.documentClick); if (this.unmountFloatingElement) { this.menuRef.remove(); this.viewContainerRef.clear(); this.unmountFloatingElement(); } this.unmountFloatingElement = undefined; // On all instances of menu closing, make sure icon direction is correct this.changeDetectorRef.markForCheck(); } /** * On action click, notify user * If the menu is open, close the menu * @param event */ onActionClick(event) { if (this.open) { this.toggleMenu(); } this.actionClick.emit(event); } /** * Handles emitting open/close event */ toggleMenu() { this.open = !this.open; if (this.open) { // Render the template & append to view const view = this.viewContainerRef.createEmbeddedView(this.menuTemplate); this.menuRef = document.body.appendChild(view.rootNodes[0]); // Assign button width to the menu ref to align with button Object.assign(this.menuRef.style, { width: `${this.hostElement.nativeElement.clientWidth}px`, top: "0", left: "0" }); // Reset & test alignment every open this.menuAlignment = this._alignment; document.addEventListener("click", this.documentClick); // Listen for events such as scrolling to keep menu aligned this.unmountFloatingElement = autoUpdate(this.hostElement.nativeElement, this.menuRef, this.recomputePosition.bind(this)); } else { this.cleanUp(); } } roundByDPR(value) { const dpr = window.devicePixelRatio || 1; return Math.round(value * dpr) / dpr; } /** * Compute position of menu */ recomputePosition() { if (this.menuTemplate && this.hostElement) { // Run outside of angular zone to avoid unnecessary change detection and rely on floating-ui this.ngZone.runOutsideAngular(async () => { const { x, y, placement } = await computePosition(this.hostElement.nativeElement, this.menuRef, { placement: this.menuAlignment, strategy: "fixed", middleware: [ flip({ crossAxis: false }) ] }); this.menuAlignment = placement; // Using CSSOM to manipulate CSS to avoid content security policy inline-src // https://github.com/w3c/webappsec-csp/issues/212 Object.assign(this.menuRef.style, { position: "fixed", // Using transform instead of top/left position to improve performance transform: `translate(${this.roundByDPR(x)}px,${this.roundByDPR(y)}px)` }); this.changeDetectorRef.markForCheck(); }); } } } ComboButtonComponent.comboButtonCounter = 0; ComboButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ComboButtonComponent, deps: [{ token: i0.NgZone }, { token: i0.Renderer2 }, { token: i0.ElementRef }, { token: i0.ViewContainerRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); ComboButtonComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: ComboButtonComponent, selector: "cds-combo-button", inputs: { comboId: "comboId", size: "size", label: "label", disabled: "disabled", menuAlignment: "menuAlignment", description: "description", tooltipAutoAlign: "tooltipAutoAlign", tooltipPlacement: "tooltipPlacement", open: "open" }, outputs: { actionClick: "actionClick" }, host: { properties: { "class.cds--combo-button__container--open": "this.open", "class.cds--combo-button__container": "this.comboButtonContainer", "class.cds--combo-button__container--lg": "this.sizeLg", "class.cds--combo-button__container--md": "this.sizeMd", "class.cds--combo-button__container--sm": "this.sizeSm", "attr.aria-owns": "this.ariaOwns" } }, queries: [{ propertyName: "projectedMenuItems", predicate: ContextMenuItemComponent }], viewQueries: [{ propertyName: "menuTemplate", first: true, predicate: ["menuTemplate"], descendants: true }], usesOnChanges: true, ngImport: i0, template: ` <div class="cds--combo-button__primary-action" [attr.aria-owns]="open ? comboId : undefined"> <button cdsButton="primary" [size]="size" [attr.title]="label" [disabled]="disabled" type="button" (click)="onActionClick($event)"> {{label}} </button> </div> <cds-icon-button [buttonNgClass]="{ 'cds--combo-button__trigger': true }" [buttonAttributes]="{ 'aria-haspopup': true, 'aria-expanded': open, 'aria-controls': open ? comboId : undefined }" [size]="size" [description]="description" [disabled]="disabled" [autoAlign]="tooltipAutoAlign" [align]="tooltipPlacement" (click)="toggleMenu()"> <svg cdsIcon="chevron--down" size="16"> </svg> </cds-icon-button> <ng-template #menuTemplate> <cds-menu mode="basic" [size]="size" [open]="open" [attr.id]="comboId"> <ng-content select="cds-menu-item, cds-menu-divider"></ng-content> </cds-menu> </ng-template> `, isInline: true, dependencies: [{ kind: "directive", type: i1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "component", type: i1.IconButton, selector: "cds-icon-button, ibm-icon-button", inputs: ["buttonNgClass", "buttonAttributes", "buttonId", "kind", "size", "type", "isExpressive", "disabled", "description", "showTooltipWhenDisabled"], outputs: ["click", "focus", "blur", "tooltipClick"] }, { kind: "directive", type: i2.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "component", type: i3.ContextMenuComponent, selector: "cds-menu, cds-context-menu, ibm-context-menu", inputs: ["open", "position", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ComboButtonComponent, decorators: [{ type: Component, args: [{ selector: "cds-combo-button", template: ` <div class="cds--combo-button__primary-action" [attr.aria-owns]="open ? comboId : undefined"> <button cdsButton="primary" [size]="size" [attr.title]="label" [disabled]="disabled" type="button" (click)="onActionClick($event)"> {{label}} </button> </div> <cds-icon-button [buttonNgClass]="{ 'cds--combo-button__trigger': true }" [buttonAttributes]="{ 'aria-haspopup': true, 'aria-expanded': open, 'aria-controls': open ? comboId : undefined }" [size]="size" [description]="description" [disabled]="disabled" [autoAlign]="tooltipAutoAlign" [align]="tooltipPlacement" (click)="toggleMenu()"> <svg cdsIcon="chevron--down" size="16"> </svg> </cds-icon-button> <ng-template #menuTemplate> <cds-menu mode="basic" [size]="size" [open]="open" [attr.id]="comboId"> <ng-content select="cds-menu-item, cds-menu-divider"></ng-content> </cds-menu> </ng-template> `, changeDetection: ChangeDetectionStrategy.OnPush }] }], ctorParameters: function () { return [{ type: i0.NgZone }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { comboId: [{ type: Input }], projectedMenuItems: [{ type: ContentChildren, args: [ContextMenuItemComponent] }], size: [{ type: Input }], label: [{ type: Input }], disabled: [{ type: Input }], menuAlignment: [{ type: Input }], description: [{ type: Input }], tooltipAutoAlign: [{ type: Input }], tooltipPlacement: [{ type: Input }], open: [{ type: Input }, { type: HostBinding, args: ["class.cds--combo-button__container--open"] }], actionClick: [{ type: Output }], comboButtonContainer: [{ type: HostBinding, args: ["class.cds--combo-button__container"] }], sizeLg: [{ type: HostBinding, args: ["class.cds--combo-button__container--lg"] }], sizeMd: [{ type: HostBinding, args: ["class.cds--combo-button__container--md"] }], sizeSm: [{ type: HostBinding, args: ["class.cds--combo-button__container--sm"] }], ariaOwns: [{ type: HostBinding, args: ["attr.aria-owns"] }], menuTemplate: [{ type: ViewChild, args: ["menuTemplate"] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tYm8tYnV0dG9uLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb21iby1idXR0b24vY29tYm8tYnV0dG9uLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBRU4sdUJBQXVCLEVBRXZCLFNBQVMsRUFDVCxlQUFlLEVBRWYsWUFBWSxFQUNaLFdBQVcsRUFDWCxLQUFLLEVBSUwsTUFBTSxFQUtOLFNBQVMsRUFFVCxNQUFNLGVBQWUsQ0FBQztBQUV2QixPQUFPLEVBQ04sVUFBVSxFQUNWLGVBQWUsRUFDZixJQUFJLEVBQ0osTUFBTSxrQkFBa0IsQ0FBQztBQUMxQixPQUFPLEVBQUUsd0JBQXdCLEVBQWtCLE1BQU0sd0NBQXdDLENBQUM7Ozs7O0FBaURsRyxNQUFNLE9BQU8sb0JBQW9CO0lBMENoQyxZQUNXLE1BQWMsRUFDZCxRQUFtQixFQUNuQixXQUF1QixFQUN2QixnQkFBa0MsRUFDbEMsaUJBQW9DO1FBSnBDLFdBQU0sR0FBTixNQUFNLENBQVE7UUFDZCxhQUFRLEdBQVIsUUFBUSxDQUFXO1FBQ25CLGdCQUFXLEdBQVgsV0FBVyxDQUFZO1FBQ3ZCLHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBa0I7UUFDbEMsc0JBQWlCLEdBQWpCLGlCQUFpQixDQUFtQjtRQTdDdEMsWUFBTyxHQUFHLGdCQUFnQixvQkFBb0IsQ0FBQyxrQkFBa0IsRUFBRSxFQUFFLENBQUM7UUFjdEUsU0FBSSxHQUF1QixJQUFJLENBQUM7UUFFaEMsYUFBUSxHQUFHLEtBQUssQ0FBQztRQUNqQixrQkFBYSxHQUF5QixRQUFRLENBQUM7UUFFL0MscUJBQWdCLEdBQUcsS0FBSyxDQUFDO1FBQ3pCLHFCQUFnQixHQUFHLFFBQVEsQ0FBQztRQUM2QixTQUFJLEdBQUcsS0FBSyxDQUFDO1FBQ3JFLGdCQUFXLEdBQUcsSUFBSSxZQUFZLEVBQVMsQ0FBQztRQUNDLHlCQUFvQixHQUFHLElBQUksQ0FBQztRQVVy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QixRQUFRO3NCQUExQyxXQUFXO3VCQUFDLGdCQUFnQjtnQkFJRixZQUFZO3NCQUF0QyxTQUFTO3VCQUFDLGNBQWMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuXHRBZnRlclZpZXdJbml0LFxuXHRDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSxcblx0Q2hhbmdlRGV0ZWN0b3JSZWYsXG5cdENvbXBvbmVudCxcblx0Q29udGVudENoaWxkcmVuLFxuXHRFbGVtZW50UmVmLFxuXHRFdmVudEVtaXR0ZXIsXG5cdEhvc3RCaW5kaW5nLFxuXHRJbnB1dCxcblx0Tmdab25lLFxuXHRPbkNoYW5nZXMsXG5cdE9uRGVzdHJveSxcblx0T3V0cHV0LFxuXHRRdWVyeUxpc3QsXG5cdFJlbmRlcmVyMixcblx0U2ltcGxlQ2hhbmdlcyxcblx0VGVtcGxhdGVSZWYsXG5cdFZpZXdDaGlsZCxcblx0Vmlld0NvbnRhaW5lclJlZlxufSBmcm9tIFwiQGFuZ3VsYXIvY29yZVwiO1xuaW1wb3J0IHsgU3Vic2NyaXB0aW9uIH0gZnJvbSBcInJ4anNcIjtcbmltcG9ydCB7XG5cdGF1dG9VcGRhdGUsXG5cdGNvbXB1dGVQb3NpdGlvbixcblx0ZmxpcFxufSBmcm9tIFwiQGZsb2F0aW5nLXVpL2RvbVwiO1xuaW1wb3J0IHsgQ29udGV4dE1lbnVJdGVtQ29tcG9uZW50LCBJdGVtQ2xpY2tFdmVudCB9IGZyb20gXCJjYXJib24tY29tcG9uZW50cy1hbmd1bGFyL2NvbnRleHQtbWVudVwiO1xuXG50eXBlIENvbWJvQnV0dG9uUGxhY2VtZW50ID0gXCJ0b3BcIiB8IFwidG9wLXN0YXJ0XCIgfCBcInRvcC1lbmRcIiB8IFwiYm90dG9tXCIgfCBcImJvdHRvbS1zdGFydFwiIHwgXCJib3R0b20tZW5kXCI7XG5cbkBDb21wb25lbnQoe1xuXHRzZWxlY3RvcjogXCJjZHMtY29tYm8tYnV0dG9uXCIsXG5cdHRlbXBsYXRlOiBgXG5cdFx0PGRpdiBjbGFzcz1cImNkcy0tY29tYm8tYnV0dG9uX19wcmltYXJ5LWFjdGlvblwiIFthdHRyLmFyaWEtb3duc109XCJvcGVuID8gY29tYm9JZCA6IHVuZGVmaW5lZFwiPlxuXHRcdFx0PGJ1dHRvblxuXHRcdFx0XHRjZHNCdXR0b249XCJwcmltYXJ5XCJcblx0XHRcdFx0W3NpemVdPVwic2l6ZVwiXG5cdFx0XHRcdFthdHRyLnRpdGxlXT1cImxhYmVsXCJcblx0XHRcdFx0W2Rpc2FibGVkXT1cImRpc2FibGVkXCJcblx0XHRcdFx0dHlwZT1cImJ1dHRvblwiXG5cdFx0XHRcdChjbGljayk9XCJvbkFjdGlvbkNsaWNrKCRldmVudClcIj5cblx0XHRcdFx0e3tsYWJlbH19XG5cdFx0XHQ8L2J1dHRvbj5cblx0XHQ8L2Rpdj5cblx0XHQ8Y2RzLWljb24tYnV0dG9uXG5cdFx0XHRbYnV0dG9uTmdDbGFzc109XCJ7ICdjZHMtLWNvbWJvLWJ1dHRvbl9fdHJpZ2dlcic6IHRydWUgfVwiXG5cdFx0XHRbYnV0dG9uQXR0cmlidXRlc109XCJ7XG5cdFx0XHRcdCdhcmlhLWhhc3BvcHVwJzogdHJ1ZSxcblx0XHRcdFx0J2FyaWEtZXhwYW5kZWQnOiBvcGVuLFxuXHRcdFx0XHQnYXJpYS1jb250cm9scyc6IG9wZW4gPyBjb21ib0lkIDogdW5kZWZpbmVkXG5cdFx0XHR9XCJcblx0XHRcdFtzaXplXT1cInNpemVcIlxuXHRcdFx0W2Rlc2NyaXB0aW9uXT1cImRlc2NyaXB0aW9uXCJcblx0XHRcdFtkaXNhYmxlZF09XCJkaXNhYmxlZFwiXG5cdFx0XHRbYXV0b0FsaWduXT1cInRvb2x0aXBBdXRvQWxpZ25cIlxuXHRcdFx0W2FsaWduXT1cInRvb2x0aXBQbGFjZW1lbnRcIlxuXHRcdFx0KGNsaWNrKT1cInRvZ2dsZU1lbnUoKVwiPlxuXHRcdFx0PHN2Z1xuXHRcdFx0XHRjZHNJY29uPVwiY2hldnJvbi0tZG93blwiXG5cdFx0XHRcdHNpemU9XCIxNlwiPlxuXHRcdFx0PC9zdmc+XG5cdFx0PC9jZHMtaWNvbi1idXR0b24+XG5cblx0XHQ8bmctdGVtcGxhdGUgI21lbnVUZW1wbGF0ZT5cblx0XHRcdDxjZHMtbWVudVxuXHRcdFx0XHRtb2RlPVwiYmFzaWNcIlxuXHRcdFx0XHRbc2l6ZV09XCJzaXplXCJcblx0XHRcdFx0W29wZW5dPVwib3BlblwiXG5cdFx0XHRcdFthdHRyLmlkXT1cImNvbWJvSWRcIj5cblx0XHRcdFx0PG5nLWNvbnRlbnQgc2VsZWN0PVwiY2RzLW1lbnUtaXRlbSwgY2RzLW1lbnUtZGl2aWRlclwiPjwvbmctY29udGVudD5cblx0XHRcdDwvY2RzLW1lbnU+XG5cdFx0PC9uZy10ZW1wbGF0ZT5cblx0YCxcblx0Y2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2hcbn0pXG5leHBvcnQgY2xhc3MgQ29tYm9CdXR0b25Db21wb25lbnQgaW1wbGVtZW50cyBPbkNoYW5nZXMsIEFmdGVyVmlld0luaXQsIE9uRGVzdHJveSB7XG5cdHN0YXRpYyBjb21ib0J1dHRvbkNvdW50ZXIgPSAwO1xuXHRASW5wdXQoKSBjb21ib0lkID0gYGNvbWJvLWJ1dHRvbi0ke0NvbWJvQnV0dG9uQ29tcG9uZW50LmNvbWJvQnV0dG9uQ291bnRlcisrfWA7XG5cblx0Ly8gTGlzdGVuIGZvciBjbGljayAmIGRldGVybWluZSBpZiBtZW51IHNob3VsZCBjbG9zZVxuXHRAQ29udGVudENoaWxkcmVuKENvbnRleHRNZW51SXRlbUNvbXBvbmVudCkgc2V0IHByb2plY3RlZE1lbnVJdGVtcyhpdGVtTGlzdDogUXVlcnlMaXN0PENvbnRleHRNZW51SXRlbUNvbXBvbmVudD4pIHtcblx0XHQvLyBSZXNldCBpbiBjYXNlIHVzZXIgZHluYW1pY2FsbHkgdXBkYXRlcyBtZW51IGl0ZW1cblx0XHR0aGlzLnN1YnNjcmlwdGlvbnMuZm9yRWFjaCgoc3ViKSA9PiBzdWI/LnVuc3Vic2NyaWJlKCkpO1xuXHRcdHRoaXMuc3Vic2NyaXB0aW9ucyA9IFtdO1xuXHRcdGl0ZW1MaXN0LmZvckVhY2goKGl0ZW0pID0+IHtcblx0XHRcdHRoaXMuc3Vic2NyaXB0aW9ucy5wdXNoKFxuXHRcdFx0XHRpdGVtLml0ZW1DbGljay5zdWJzY3JpYmUoKGNsaWNrRXZlbnQpID0+IHRoaXMuaGFuZGxlTWVudUl0ZW1DbGljayhjbGlja0V2ZW50KSlcblx0XHRcdCk7XG5cdFx0fSk7XG5cdH1cblxuXHRASW5wdXQoKSBzaXplOiBcInNtXCIgfCBcIm1kXCIgfCBcImxnXCIgPSBcImxnXCI7XG5cdEBJbnB1dCgpIGxhYmVsOiBzdHJpbmc7XG5cdEBJbnB1dCgpIGRpc2FibGVkID0gZmFsc2U7XG5cdEBJbnB1dCgpIG1lbnVBbGlnbm1lbnQ6IENvbWJvQnV0dG9uUGxhY2VtZW50ID0gXCJib3R0b21cIjtcblx0QElucHV0KCkgZGVzY3JpcHRpb246IHN0cmluZztcblx0QElucHV0KCkgdG9vbHRpcEF1dG9BbGlnbiA9IGZhbHNlO1xuXHRASW5wdXQoKSB0b29sdGlwUGxhY2VtZW50ID0gXCJib3R0b21cIjtcblx0QElucHV0KCkgQEhvc3RCaW5kaW5nKFwiY2xhc3MuY2RzLS1jb21iby1idXR0b25fX2NvbnRhaW5lci0tb3BlblwiKSBvcGVuID0gZmFsc2U7XG5cdEBPdXRwdXQoKSBhY3Rpb25DbGljayA9IG5ldyBFdmVudEVtaXR0ZXI8RXZlbnQ+KCk7XG5cdEBIb3N0QmluZGluZyhcImNsYXNzLmNkcy0tY29tYm8tYnV0dG9uX19jb250YWluZXJcIikgY29tYm9CdXR0b25Db250YWluZXIgPSB0cnVlO1xuXHRASG9zdEJpbmRpbmcoXCJjbGFzcy5jZHMtLWNvbWJvLWJ1dHRvbl9fY29udGFpbmVyLS1sZ1wiKSBnZXQgc2l6ZUxnKCkgeyByZXR1cm4gdGhpcy5zaXplID09PSBcImxnXCI7IH1cblx0QEhvc3RCaW5kaW5nKFwiY2xhc3MuY2RzLS1jb21iby1idXR0b25fX2NvbnRhaW5lci0tbWRcIikgZ2V0IHNpemVNZCgpIHsgcmV0dXJuIHRoaXMuc2l6ZSA9PT0gXCJtZFwiOyB9XG5cdEBIb3N0QmluZGluZyhcImNsYXNzLmNkcy0tY29tYm8tYnV0dG9uX19jb250YWluZXItLXNtXCIpIGdldCBzaXplU20oKSB7IHJldHVybiB0aGlzLnNpemUgPT09IFwic21cIjsgfVxuXHRASG9zdEJpbmRpbmcoXCJhdHRyLmFyaWEtb3duc1wiKSBnZXQgYXJpYU93bnMoKSB7XG5cdFx0cmV0dXJuIHRoaXMub3BlbiA/IHRoaXMuY29tYm9JZCA6IHVuZGVmaW5lZDtcblx0fVxuXG5cdEBWaWV3Q2hpbGQoXCJtZW51VGVtcGxhdGVcIikgbWVudVRlbXBsYXRlOiBUZW1wbGF0ZVJlZjxhbnk+O1xuXG5cdHByb3RlY3RlZCBkb2N1bWVudENsaWNrID0gdGhpcy5oYW5kbGVGb2N1c091dC5iaW5kKHRoaXMpO1xuXHRwcm90ZWN0ZWQgdW5tb3VudEZsb2F0aW5nRWxlbWVudDogRnVuY3Rpb247XG5cblx0cHJpdmF0ZSBzdWJzY3JpcHRpb25zOiBTdWJzY3JpcHRpb25bXSA9IFtdO1xuXHRwcml2YXRlIF9hbGlnbm1lbnQ6IENvbWJvQnV0dG9uUGxhY2VtZW50ID0gXCJib3R0b21cIjtcblx0cHJpdmF0ZSBtZW51UmVmOiBIVE1MRWxlbWVudDtcblxuXHRjb25zdHJ1Y3Rvcihcblx0XHRwcm90ZWN0ZWQgbmdab25lOiBOZ1pvbmUsXG5cdFx0cHJvdGVjdGVkIHJlbmRlcmVyOiBSZW5kZXJlcjIsXG5cdFx0cHJvdGVjdGVkIGhvc3RFbGVtZW50OiBFbGVtZW50UmVmLFxuXHRcdHByb3RlY3RlZCB2aWV3Q29udGFpbmVyUmVmOiBWaWV3Q29udGFpbmVyUmVmLFxuXHRcdHByb3RlY3RlZCBjaGFuZ2VEZXRlY3RvclJlZjogQ2hhbmdlRGV0ZWN0b3JSZWZcblx0KSB7IH1cblxuXG5cdC8qKlxuXHQgKiBJbiBjYXNlIHVzZXIgdXBkYXRlcyBhbGlnbm1lbnQsIHN0b3JlIGluaXRpYWwgdmFsdWUuXG5cdCAqIFRoaXMgYWxsb3dzIHVzIHRvIHRlc3QgdXNlciBwYXNzZWQgYWxpZ25tZW50IG9uIGVhY2ggb3BlblxuXHQgKi9cblx0bmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcyk6IHZvaWQge1xuXHRcdGlmIChjaGFuZ2VzLm1lbnVBbGlnbm1lbnQpIHtcblx0XHRcdHRoaXMuX2FsaWdubWVudCA9IGNoYW5nZXMubWVudUFsaWdubWVudC5jdXJyZW50VmFsdWU7XG5cdFx0fVxuXHR9XG5cblxuXG5cdC8qKlxuXHQgKiBJZiB1c2VyIGhhcyBwYXNzZWQgaW4gdHJ1ZSBmb3Igb3Blbiwgd2UgZHluYW1pY2FsbHkgb3BlbiB0aGUgbWVudVxuXHQgKi9cblx0bmdBZnRlclZpZXdJbml0KCk6IHZvaWQge1xuXHRcdGlmICh0aGlzLm9wZW4pIHtcblx0XHRcdHRoaXMub3BlbiA9ICF0aGlzLm9wZW47XG5cdFx0XHR0aGlzLnRvZ2dsZU1lbnUoKTtcblx0XHR9XG5cdH1cblxuXG5cblx0LyoqXG5cdCogQ2xlYW4gdXAgRmxvYXRpbmctdWkgJiBzdWJzY3JpcHRpb25zXG5cdCovXG5cdG5nT25EZXN0cm95KCk6IHZvaWQge1xuXHRcdHRoaXMuY2xlYW5VcCgpO1xuXHRcdHRoaXMuc3Vic2NyaXB0aW9ucy5mb3JFYWNoKChzdWIpID0+IHN1Yi51bnN1YnNjcmliZSgpKTtcblx0fVxuXG5cblx0LyoqXG5cdCAqIEFzIG9mIG5vdywgbWVudSBidXR0b24gZG9lcyBub3Qgc3VwcG9ydCBuZXh0ZWQgbWVudSwgb24gYnV0dG9uIGNsaWNrIGl0IHNob3VsZCBjbG9zZVxuXHQgKi9cblx0aGFuZGxlTWVudUl0ZW1DbGljayhldmVudDogSXRlbUNsaWNrRXZlbnQpIHtcblx0XHQvLyBJZiBldmVudCBpcyBub3QgdHlwZSByYWRpby9jaGVja2JveCwgd2UgY2xvc2UgdGhlIG1lbnVcblx0XHRpZiAoIWV2ZW50LnR5cGUpIHtcblx0XHRcdHRoaXMudG9nZ2xlTWVudSgpO1xuXHRcdH1cblx0fVxuXG5cblxuXHQvKipcblx0ICogT24gYm9keSBjbGljaywgY2xvc2UgdGhlIG1lbnVcblx0ICogQHBhcmFtIGV2ZW50XG5cdCAqL1xuXHRoYW5kbGVGb2N1c091dChldmVudCkge1xuXHRcdGlmICghdGhpcy5ob3N0RWxlbWVudC5uYXRpdmVFbGVtZW50LmNvbnRhaW5zKGV2ZW50LnRhcmdldCkpIHtcblx0XHRcdHRoaXMudG9nZ2xlTWVudSgpO1xuXHRcdH1cblx0fVxuXG5cblxuXHQvKipcblx0ICogQ2xlYW4gdXAgYGF1dG9VcGRhdGVgIGlmIGF1dG8gYWxpZ25tZW50IGlzIGVuYWJsZWRcblx0ICovXG5cdGNsZWFuVXAoKSB7XG5cdFx0ZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImNsaWNrXCIsIHRoaXMuZG9jdW1lbnRDbGljayk7XG5cdFx0aWYgKHRoaXMudW5tb3VudEZsb2F0aW5nRWxlbWVudCkge1xuXHRcdFx0dGhpcy5tZW51UmVmLnJlbW92ZSgpO1xuXHRcdFx0dGhpcy52aWV3Q29udGFpbmVyUmVmLmNsZWFyKCk7XG5cdFx0XHR0aGlzLnVubW91bnRGbG9hdGluZ0VsZW1lbnQoKTtcblx0XHR9XG5cdFx0dGhpcy51bm1vdW50RmxvYXRpbmdFbGVtZW50ID0gdW5kZWZpbmVkO1xuXHRcdC8vIE9uIGFsbCBpbnN0YW5jZXMgb2YgbWVudSBjbG9zaW5nLCBtYWtlIHN1cmUgaWNvbiBkaXJlY3Rpb24gaXMgY29ycmVjdFxuXHRcdHRoaXMuY2hhbmdlRGV0ZWN0b3JSZWYubWFya0ZvckNoZWNrKCk7XG5cdH1cblxuXG5cdC8qKlxuXHQgKiBPbiBhY3Rpb24gY2xpY2ssIG5vdGlmeSB1c2VyXG5cdCAqIElmIHRoZSBtZW51IGlzIG9wZW4sIGNsb3NlIHRoZSBtZW51XG5cdCAqIEBwYXJhbSBldmVudFxuXHQgKi9cblx0b25BY3Rpb25DbGljayhldmVudDogUG9pbnRlckV2ZW50KSB7XG5cdFx0aWYgKHRoaXMub3Blbikge1xuXHRcdFx0dGhpcy50b2dnbGVNZW51KCk7XG5cdFx0fVxuXHRcdHRoaXMuYWN0aW9uQ2xpY2suZW1pdChldmVudCk7XG5cdH1cblxuXG5cblx0LyoqXG5cdCAqIEhhbmRsZXMgZW1pdHRpbmcgb3Blbi9jbG9zZSBldmVudFxuXHQgKi9cblx0dG9nZ2xlTWVudSgpIHtcblx0XHR0aGlzLm9wZW4gPSAhdGhpcy5vcGVuO1xuXHRcdGlmICh0aGlzLm9wZW4pIHtcblx0XHRcdC8vIFJlbmRlciB0aGUgdGVtcGxhdGUgJiBhcHBlbmQgdG8gdmlld1xuXHRcdFx0Y29uc3QgdmlldyA9IHRoaXMudmlld0NvbnRhaW5lclJlZi5jcmVhdGVFbWJlZGRlZFZpZXcodGhpcy5tZW51VGVtcGxhdGUpO1xuXHRcdFx0dGhpcy5tZW51UmVmID0gZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZCh2aWV3LnJvb3ROb2Rlc1swXSBhcyBIVE1MRWxlbWVudCk7XG5cdFx0XHQvLyBBc3NpZ24gYnV0dG9uIHdpZHRoIHRvIHRoZSBtZW51IHJlZiB0byBhbGlnbiB3aXRoIGJ1dHRvblxuXHRcdFx0T2JqZWN0LmFzc2lnbih0aGlzLm1lbnVSZWYuc3R5bGUsIHtcblx0XHRcdFx0d2lkdGg6IGAke3RoaXMuaG9zdEVsZW1lbnQubmF0aXZlRWxlbWVudC5jbGllbnRXaWR0aH1weGAsXG5cdFx0XHRcdHRvcDogXCIwXCIsXG5cdFx0XHRcdGxlZnQ6IFwiMFwiXG5cdFx0XHR9KTtcblxuXHRcdFx0Ly8gUmVzZXQgJiB0ZXN0IGFsaWdubWVudCBldmVyeSBvcGVuXG5cdFx0XHR0aGlzLm1lbnVBbGlnbm1lbnQgPSB0aGlzLl9hbGlnbm1lbnQ7XG5cblx0XHRcdGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoXCJjbGlja1wiLCB0aGlzLmRvY3VtZW50Q2xpY2spO1xuXG5cdFx0XHQvLyBMaXN0ZW4gZm9yIGV2ZW50cyBzdWNoIGFzIHNjcm9sbGluZyB0byBrZWVwIG1lbnUgYWxpZ25lZFxuXHRcdFx0dGhpcy51bm1vdW50RmxvYXRpbmdFbGVtZW50ID0gYXV0b1VwZGF0ZShcblx0XHRcdFx0dGhpcy5ob3N0RWxlbWVudC5uYXRpdmVFbGVtZW50LFxuXHRcdFx0XHR0aGlzLm1lbnVSZWYsXG5cdFx0XHRcdHRoaXMucmVjb21wdXRlUG9zaXRpb24uYmluZCh0aGlzKVxuXHRcdFx0KTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5jbGVhblVwKCk7XG5cdFx0fVxuXHR9XG5cblxuXG5cdHJvdW5kQnlEUFIodmFsdWUpIHtcblx0XHRjb25zdCBkcHIgPSB3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbyB8fCAxO1xuXHRcdHJldHVybiBNYXRoLnJvdW5kKHZhbHVlICogZHByKSAvIGRwcjtcblx0fVxuXG5cblxuXHQvKipcblx0ICogQ29tcHV0ZSBwb3NpdGlvbiBvZiBtZW51XG5cdCAqL1xuXHRyZWNvbXB1dGVQb3NpdGlvbigpIHtcblx0XHRpZiAodGhpcy5tZW51VGVtcGxhdGUgJiYgdGhpcy5ob3N0RWxlbWVudCkge1xuXHRcdFx0Ly8gUnVuIG91dHNpZGUgb2YgYW5ndWxhciB6b25lIHRvIGF2b2lkIHVubmVjZXNzYXJ5IGNoYW5nZSBkZXRlY3Rpb24gYW5kIHJlbHkgb24gZmxvYXRpbmctdWlcblx0XHRcdHRoaXMubmdab25lLnJ1bk91dHNpZGVBbmd1bGFyKGFzeW5jICgpID0+IHtcblx0XHRcdFx0Y29uc3QgeyB4LCB5LCBwbGFjZW1lbnQgfSA9IGF3YWl0IGNvbXB1dGVQb3NpdGlvbihcblx0XHRcdFx0XHR0aGlzLmhvc3RFbGVtZW50Lm5hdGl2ZUVsZW1lbnQsXG5cdFx0XHRcdFx0dGhpcy5tZW51UmVmLFxuXHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdHBsYWNlbWVudDogdGhpcy5tZW51QWxpZ25tZW50LFxuXHRcdFx0XHRcdFx0c3RyYXRlZ3k6IFwiZml4ZWRcIixcblx0XHRcdFx0XHRcdG1pZGRsZXdhcmU6IFtcblx0XHRcdFx0XHRcdFx0ZmxpcCh7IGNyb3NzQXhpczogZmFsc2UgfSlcblx0XHRcdFx0XHRcdF1cblx0XHRcdFx0XHR9KTtcblxuXHRcdFx0XHR0aGlzLm1lbnVBbGlnbm1lbnQgPSBwbGFjZW1lbnQgYXMgQ29tYm9CdXR0b25QbGFjZW1lbnQ7XG5cblx0XHRcdFx0Ly8gVXNpbmcgQ1NTT00gdG8gbWFuaXB1bGF0ZSBDU1MgdG8gYXZvaWQgY29udGVudCBzZWN1cml0eSBwb2xpY3kgaW5saW5lLXNyY1xuXHRcdFx0XHQvLyBodHRwczovL2dpdGh1Yi5jb20vdzNjL3dlYmFwcHNlYy1jc3AvaXNzdWVzLzIxMlxuXHRcdFx0XHRPYmplY3QuYXNzaWduKHRoaXMubWVudVJlZi5zdHlsZSwge1xuXHRcdFx0XHRcdHBvc2l0aW9uOiBcImZpeGVkXCIsXG5cdFx0XHRcdFx0Ly8gVXNpbmcgdHJhbnNmb3JtIGluc3RlYWQgb2YgdG9wL2xlZnQgcG9zaXRpb24gdG8gaW1wcm92ZSBwZXJmb3JtYW5jZVxuXHRcdFx0XHRcdHRyYW5zZm9ybTogYHRyYW5zbGF0ZSgke3RoaXMucm91bmRCeURQUih4KX1weCwke3RoaXMucm91bmRCeURQUih5KX1weClgXG5cdFx0XHRcdH0pO1xuXHRcdFx0XHR0aGlzLmNoYW5nZURldGVjdG9yUmVmLm1hcmtGb3JDaGVjaygpO1xuXHRcdFx0fSk7XG5cdFx0fVxuXHR9XG59XG4iXX0=