UNPKG

@microsoft/windows-admin-center-sdk

Version:

Microsoft - Windows Admin Center Shell

462 lines 62.9 kB
import { __decorate, __metadata } from "tslib"; import { Directive, ElementRef, EventEmitter, HostBinding, HostListener, Injector, Input, Output, SecurityContext, TemplateRef } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { Debounce } from '@microsoft/windows-admin-center-sdk/core/base/decorators/debounce.decorators'; import { Dom } from '@microsoft/windows-admin-center-sdk/core/dom/dom'; import { SmeWebTelemetry } from '@microsoft/windows-admin-center-sdk/core/index'; import { decode } from 'html-entities'; import { CoreBaseComponent } from '../common/base.component'; import { TooltipShowMode } from './models/tooltip-options'; import { TooltipPlacementEnum } from './models/tooltip-placement'; import { TooltipHostInstance } from './tooltip-host-instance'; import { TooltipZoneDirective } from './tooltip-zone.directive'; import * as i0 from "@angular/core"; import * as i1 from "@angular/platform-browser"; export class ToolTipFocusEvent { } ToolTipFocusEvent.focusIn = 'focusin'; ToolTipFocusEvent.focusOut = 'focusout'; export class ToolTipMouseEvent { } ToolTipMouseEvent.mouseEnter = 'mouseenter'; ToolTipMouseEvent.mouseLeave = 'mouseleave'; /** * * @smeDoc {@label Tooltip @id sme-tooltip-directive} * * @overview * @file {@filepath ./examples/overview.md} * * @example {@label Basic Usage @id basic-usage} * @file {@filename component.html @filepath ./examples/basic-usage.component.html} * @file {@filename component.ts @filepath ./examples/tooltip-example.component.ts} * * @example {@label Custom Template @id custom-template} * @file {@filename component.html @filepath ./examples/template-reference.component.html} * @file {@filename component.ts @filepath ./examples/tooltip-example.component.ts} * * @example {@label Custom Implementation @id tooltip-zone} * @file {@filename component.html @filepath ./examples/tooltip-zone.component.html} */ /** * Directive to create tooltips in the tooltip host. */ export class TooltipDirective extends CoreBaseComponent { /** * Initializes a new instance of the @see PivotComponent class. * @param injector The angular injection service. required by @SmeInjectableBase() decorator in the base @see BaseComponent class */ constructor(elementRef, domSanitizer, injector) { super(injector); this.elementRef = elementRef; this.domSanitizer = domSanitizer; this.toolTipFocusEventTriggered = new EventEmitter(); this.toolTipMouseEventTriggered = new EventEmitter(); /** * Indicates when to explicitly not show the tooltip in cases where it needs to be shown only under certain conditions. */ this.smeTooltipShow = true; /** * Indicates if receiving hover and focus events. */ this.smeTooltipEnableEvents = false; /** * Indicates if hide the tooltip when inner HTML is the same as tooltip content * In certain cases, we always need the tooltip to show, such as the multi-tool component tool list. */ this.hideOnTooltipSameAsText = true; this.smeTooltipShowOn = MsftSme.applyBitwiseOr(TooltipShowMode.Hover, TooltipShowMode.KeyboardFocus); } /** * The source name to use for logging */ get logSourceName() { return 'TooltipDirective'; } /** * Sets the native tooltip property. * * This input is susceptible to XSS attacks, and is sanitized if necessary. * In cases the input is sanitized, the displayed tooltip to the user will only show the sanitized content. * This behavior (defense) is expected since users should not present XSS payloads in the [title] attribute. */ set title(input) { if (!MsftSme.isNullOrUndefined(input)) { // https://github.com/angular/angular/blob/main/packages/core/src/sanitization/html_sanitizer.ts const sanitizedInput = this.domSanitizer.sanitize(SecurityContext.HTML, input); // When sanitizing "safe" specialized inputs (e.g., localized inputs) // the DomSanitizer can return parts of the value as encoded, (e.g., 'olá' can be returned as 'ol&#225;'), // therefore, we compare the decoded values to see if any part of the input was actually sanitized and determine // what is displayed in the tooltip based on sanitation results. if (decode(sanitizedInput) !== decode(input)) { this.internalTitle = sanitizedInput; SmeWebTelemetry.traceAction(null, { content: { label: 'SecurityProtectionEvent', type: 'XSSPrevention' } }); } else { // Safe input this.internalTitle = input; } } } /** * Gets the native tooltip property. */ get title() { return this.internalTitle; } /** * Gets the id of the tooltip that will describe this element */ get ariaDescribedBy() { if (!this.shown || !MsftSme.isNullOrUndefined(this.smeTooltip) || !this.hideOnTooltipSameAsText) { return null; } return this.idBag && this.idBag.tooltip ? this.idBag.tooltip : null; } /** * The actual placement of the tooltip */ get placement() { return TooltipPlacementEnum.fromString(this.smeTooltipPlacement); } /** * The tooltip host */ get tooltipHost() { return TooltipHostInstance.activeHost; } /** * Indicates that tooltips should show on focus/hover. */ get showOnfocus() { return !!MsftSme.self().Resources.accessibilityMode; } /** * Indicates if the tooltip is being shown. */ get shown() { return this.tooltipHost.isShown(this.componentId); } /** * Indicates that this tooltip should show only when the text is truncated * We assume this is true if the title attribute is exactly the same as the text content of this node. */ get isTooltipSameAsText() { if (MsftSme.isNullOrUndefined(this.title)) { return false; } const nativehostElement = this.hostElement.nativeElement; return !!this.title && MsftSme.localeCompareIgnoreCase(String(this.title).trim(), nativehostElement.textContent.trim()) === 0; } /** * Indicates that this tooltip should be shown */ get shouldShow() { if (MsftSme.isNullOrWhiteSpace(this.title) && MsftSme.isNullOrUndefined(this.smeTooltip) || !this.smeTooltipShow) { return false; } if (MsftSme.applyBitwiseAnd(this.smeTooltipShowOn, TooltipShowMode.Click)) { // if we are hovered or our tooltip is hovered if (this.clicked) { return true; } // if we are shown and are focused or our tooltip if (this.shown && this.tooltipHost.isFocused) { return true; } } else if (MsftSme.applyBitwiseAnd(this.smeTooltipShowOn, (MsftSme.applyBitwiseOr(TooltipShowMode.Hover, TooltipShowMode.KeyboardFocus)))) { if (this.hideOnTooltipSameAsText && this.isTooltipSameAsText && !this.getIsTruncated()) { // Remove title attribute when true conditions are not met this.setAttribute('title', ''); return false; } // if we are hovered or our tooltip is hovered if (this.hovered || (this.shown && this.tooltipHost.isHovered)) { return true; } // if we show on focus and are focused or our tooltip has focus if (this.showOnfocus && (this.focused || (this.shown && this.tooltipHost.isFocused))) { return true; } } // Remove title attribute when true conditions are not met this.setAttribute('title', ''); return false; } /** * Implementation of angular OnInit interface */ ngOnInit() { if (this.tooltipZone) { this.tooltipZone.registerTooltipDirective(this); } // Set up observer if element can be disabled, watch for disabled changes if (this.elementRef && this.elementRef.nativeElement && // eslint-disable-next-line eqeqeq this.elementRef.nativeElement.disabled != null) { // We hide the tooltip if the element it is attached to becomes disabled this.observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.attributeName === 'disabled') { // Check if need to be hidden this.hide(); } }); }); const config = { attributes: true, childList: true, characterData: true }; this.observer.observe(this.elementRef.nativeElement, config); } } /** * Implementation of angular OnChanges interface * On any change we update the tooltip if it is shown */ ngOnChanges(changes) { super.ngOnChanges(changes); if (this.shown) { this.tooltipHost.update(this.getTooltipOptions()); } } /** * Implementation of angular OnDestroy interface */ ngOnDestroy() { super.ngOnDestroy(); this.showHideTooltip(false, false); if (this.tooltipZone) { this.tooltipZone.unregisterTooltipDirective(); } } /** * Creates the idBag used by this component to store unique element ids. * id values will be assigned be the @see BaseComponent super class. */ createIdBag() { return { tooltip: '' }; } /** * Host listeners for tooltip focus events */ onFocusEvent(event) { // on focus in/out change focused state const focused = event.type === ToolTipFocusEvent.focusIn || (this.focused && event.type !== ToolTipFocusEvent.focusOut); const lostClickFocus = this.clicked && event.type === ToolTipFocusEvent.focusOut; this.tooltipTrigger = this.clicked ? TooltipShowMode.Click : TooltipShowMode.KeyboardFocus; this.showHideTooltip(this.hovered, focused, lostClickFocus ? false : this.clicked); } /** * Host listeners for tooltip mouse events */ onMouseEvent(event) { if (MsftSme.applyBitwiseAnd(this.smeTooltipShowOn, TooltipShowMode.Hover)) { // on mouse enter/leave change hovered state const hovered = event.type === ToolTipMouseEvent.mouseEnter || (this.hovered && event.type !== ToolTipMouseEvent.mouseLeave); this.tooltipTrigger = TooltipShowMode.Hover; this.showHideTooltip(hovered, this.focused, this.clicked); } } /** * Host listeners for click and enter */ onClick(event) { if (MsftSme.applyBitwiseAnd(this.smeTooltipShowOn, TooltipShowMode.Click)) { this.showHideTooltip(this.hovered, this.focused, true); event.preventDefault(); event.stopPropagation(); } } /** * Shows or hides the tooltip if either the hover or focused states are true * @param hovered indicates the element is hovered * @param focused indicates the element is focused */ showHideTooltip(hovered = this.hovered, focused = this.focused, clicked = this.clicked) { // Only emits on status changed. if (this.smeTooltipEnableEvents) { if (focused !== this.focused) { focused ? this.toolTipFocusEventTriggered.emit(ToolTipFocusEvent.focusIn) : this.toolTipFocusEventTriggered.emit(ToolTipFocusEvent.focusOut); } if (hovered !== this.hovered) { hovered ? this.toolTipMouseEventTriggered.emit(ToolTipMouseEvent.mouseEnter) : this.toolTipMouseEventTriggered.emit(ToolTipMouseEvent.mouseLeave); } } this.hovered = hovered; this.focused = focused; this.clicked = clicked; if (this.shouldShow && !this.shown) { this.show(); } else if (!this.shouldShow && this.shown) { this.hide(); } } /** * Shows the tooltip. */ show(showOnShortCut = false, restoreFocusTo) { if ((this.shouldShow || showOnShortCut) && !this.shown) { // Explicitly set the tooltip to be read when using the shortcut, otherwise do not. this.tooltipHost.show(this.getTooltipOptions(restoreFocusTo, showOnShortCut)); } } /** * Hides the tooltip. */ hide() { if (this.shown && (!this.shouldShow || (this.elementRef.nativeElement && this.elementRef.nativeElement.disabled === true))) { // If it is showing and shouldn't be or if it is showing and disabled this.tooltipHost.hide(this.componentId); } } getTooltipOptions(restoreFocusTo, shouldRead) { return { targetid: this.componentId, contentId: this.idBag.tooltip, title: this.title, template: this.smeTooltip, context: this.smeTooltipContext, element: this.hostElement, restoreFocusTo: restoreFocusTo || document.activeElement, placement: this.placement, showOn: this.smeTooltipShowOn, shouldRead: shouldRead, onStateChanged: () => this.showHideTooltip(), onWindowClick: () => this.showHideTooltip(this.hovered, this.focused, this.clicked), triggeredFrom: this.tooltipTrigger }; } /** * Determines if the text node content is truncated. */ getIsTruncated() { const host = this.hostElement.nativeElement; const ellipsisParent = Dom.getAncestor(host, (e) => getComputedStyle(e).textOverflow === 'ellipsis'); if (!ellipsisParent) { return false; } const measureElement = document.createElement('span'); // position element off screen measureElement.style.display = 'inline'; measureElement.style.width = 'auto'; measureElement.style.visibility = 'hidden'; measureElement.style.position = 'absolute'; measureElement.style.right = '-10000px'; measureElement.style.bottom = '-10000px'; measureElement.innerHTML = host.textContent; // use same font as host element const hostStyle = getComputedStyle(host); const ellipsesParentStyle = getComputedStyle(ellipsisParent); measureElement.style.fontFamily = hostStyle.fontFamily; measureElement.style.fontVariant = hostStyle.fontVariant; measureElement.style.fontSize = hostStyle.fontSize; measureElement.style.fontStyle = hostStyle.fontStyle; measureElement.style.fontWeight = hostStyle.fontWeight; measureElement.style.lineHeight = hostStyle.lineHeight; measureElement.style.letterSpacing = hostStyle.letterSpacing; measureElement.style.padding = ellipsesParentStyle.padding; // append the measure element to the dom and compare width with the ellipsisParent document.body.append(measureElement); const ellipsesParentWidth = ellipsisParent.getBoundingClientRect().width; const measureWidth = measureElement.getBoundingClientRect().width; const isTruncated = ellipsesParentWidth < measureWidth; measureElement.remove(); // this logging is to verbose, but useful for debugging. // Logging.logDebug( // this.logSourceName, // `Truncation Calculated: ${isTruncated}`, // { // ellipsesParentWidth: ellipsesParentWidth, // measureWidth: measureWidth // } // ); return isTruncated; } } /** @nocollapse */ TooltipDirective.ɵfac = function TooltipDirective_Factory(t) { return new (t || TooltipDirective)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i1.DomSanitizer), i0.ɵɵdirectiveInject(i0.Injector)); }; /** @nocollapse */ TooltipDirective.ɵdir = /** @pureOrBreakMyCode */ i0.ɵɵdefineDirective({ type: TooltipDirective, selectors: [["", "smeTooltip", ""], ["", "title", ""]], hostVars: 1, hostBindings: function TooltipDirective_HostBindings(rf, ctx) { if (rf & 1) { i0.ɵɵlistener("focusin", function TooltipDirective_focusin_HostBindingHandler($event) { return ctx.onFocusEvent($event); })("focusout", function TooltipDirective_focusout_HostBindingHandler($event) { return ctx.onFocusEvent($event); })("mouseenter", function TooltipDirective_mouseenter_HostBindingHandler($event) { return ctx.onMouseEvent($event); })("mouseleave", function TooltipDirective_mouseleave_HostBindingHandler($event) { return ctx.onMouseEvent($event); })("click", function TooltipDirective_click_HostBindingHandler($event) { return ctx.onClick($event); })("keydown.enter", function TooltipDirective_keydown_enter_HostBindingHandler($event) { return ctx.onClick($event); })("keydown.space", function TooltipDirective_keydown_space_HostBindingHandler($event) { return ctx.onClick($event); }); } if (rf & 2) { i0.ɵɵattribute("aria-describedBy", ctx.ariaDescribedBy); } }, inputs: { title: "title", smeTooltip: "smeTooltip", smeTooltipContext: "smeTooltipContext", smeTooltipPlacement: "smeTooltipPlacement", smeTooltipShowOn: "smeTooltipShowOn", smeTooltipShow: "smeTooltipShow", smeTooltipEnableEvents: "smeTooltipEnableEvents", hideOnTooltipSameAsText: "hideOnTooltipSameAsText", tooltipZone: "tooltipZone" }, outputs: { toolTipFocusEventTriggered: "toolTipFocusEventTriggered", toolTipMouseEventTriggered: "toolTipMouseEventTriggered" }, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature] }); __decorate([ Debounce(), __metadata("design:type", Function), __metadata("design:paramtypes", [Boolean, HTMLElement]), __metadata("design:returntype", void 0) ], TooltipDirective.prototype, "show", null); __decorate([ Debounce(), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", void 0) ], TooltipDirective.prototype, "hide", null); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TooltipDirective, [{ type: Directive, args: [{ selector: '[smeTooltip],[title]' }] }], function () { return [{ type: i0.ElementRef }, { type: i1.DomSanitizer }, { type: i0.Injector }]; }, { toolTipFocusEventTriggered: [{ type: Output }], toolTipMouseEventTriggered: [{ type: Output }], title: [{ type: Input }], smeTooltip: [{ type: Input }], smeTooltipContext: [{ type: Input }], smeTooltipPlacement: [{ type: Input }], smeTooltipShowOn: [{ type: Input }], smeTooltipShow: [{ type: Input }], smeTooltipEnableEvents: [{ type: Input }], hideOnTooltipSameAsText: [{ type: Input }], tooltipZone: [{ type: Input }], ariaDescribedBy: [{ type: HostBinding, args: ['attr.aria-describedBy'] }], onFocusEvent: [{ type: HostListener, args: [ToolTipFocusEvent.focusIn, ['$event']] }, { type: HostListener, args: [ToolTipFocusEvent.focusOut, ['$event']] }], onMouseEvent: [{ type: HostListener, args: [ToolTipMouseEvent.mouseEnter, ['$event']] }, { type: HostListener, args: [ToolTipMouseEvent.mouseLeave, ['$event']] }], onClick: [{ type: HostListener, args: ['click', ['$event']] }, { type: HostListener, args: ['keydown.enter', ['$event']] }, { type: HostListener, args: ['keydown.space', ['$event']] }], show: [], hide: [] }); })(); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbHRpcC5kaXJlY3RpdmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9hbmd1bGFyL3NyYy9jb250cm9scy90b29sdGlwL3Rvb2x0aXAuZGlyZWN0aXZlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLEVBQ0gsU0FBUyxFQUNULFVBQVUsRUFDVixZQUFZLEVBQ1osV0FBVyxFQUNYLFlBQVksRUFDWixRQUFRLEVBQ1IsS0FBSyxFQUlMLE1BQU0sRUFDTixlQUFlLEVBRWYsV0FBVyxFQUNkLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUN6RCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sOEVBQThFLENBQUM7QUFDeEcsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLGtEQUFrRCxDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxnREFBZ0QsQ0FBQztBQUNqRixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3ZDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBRTdELE9BQU8sRUFBa0IsZUFBZSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDM0UsT0FBTyxFQUFvQixvQkFBb0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3BGLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQzlELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDBCQUEwQixDQUFDOzs7QUFFaEUsTUFBTSxPQUFPLGlCQUFpQjs7QUFDbkIseUJBQU8sR0FBRyxTQUFTLENBQUM7QUFDcEIsMEJBQVEsR0FBRyxVQUFVLENBQUM7QUFHakMsTUFBTSxPQUFPLGlCQUFpQjs7QUFDbkIsNEJBQVUsR0FBRyxZQUFZLENBQUM7QUFDMUIsNEJBQVUsR0FBRyxZQUFZLENBQUM7QUFH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WTttQkFBQyxpQkFBaUIsQ0FBQyxVQUFVLEVBQUUsQ0FBQyxRQUFRLENBQUM7O2tCQUNyRCxZQUFZO21CQUFDLGlCQUFpQixDQUFDLFVBQVUsRUFBRSxDQUFDLFFBQVEsQ0FBQztZQWdCL0MsT0FBTztrQkFIYixZQUFZO21CQUFDLE9BQU8sRUFBRSxDQUFDLFFBQVEsQ0FBQzs7a0JBQ2hDLFlBQVk7bUJBQUMsZUFBZSxFQUFFLENBQUMsUUFBUSxDQUFDOztrQkFDeEMsWUFBWTttQkFBQyxlQUFlLEVBQUUsQ0FBQyxRQUFRLENBQUM7WUE2Q2xDLElBQUksTUFXSixJQUFJIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcclxuICAgIERpcmVjdGl2ZSxcclxuICAgIEVsZW1lbnRSZWYsXHJcbiAgICBFdmVudEVtaXR0ZXIsXHJcbiAgICBIb3N0QmluZGluZyxcclxuICAgIEhvc3RMaXN0ZW5lcixcclxuICAgIEluamVjdG9yLFxyXG4gICAgSW5wdXQsXHJcbiAgICBPbkNoYW5nZXMsXHJcbiAgICBPbkRlc3Ryb3ksXHJcbiAgICBPbkluaXQsXHJcbiAgICBPdXRwdXQsXHJcbiAgICBTZWN1cml0eUNvbnRleHQsXHJcbiAgICBTaW1wbGVDaGFuZ2VzLFxyXG4gICAgVGVtcGxhdGVSZWZcclxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHsgRG9tU2FuaXRpemVyIH0gZnJvbSAnQGFuZ3VsYXIvcGxhdGZvcm0tYnJvd3Nlcic7XHJcbmltcG9ydCB7IERlYm91bmNlIH0gZnJvbSAnQG1pY3Jvc29mdC93aW5kb3dzLWFkbWluLWNlbnRlci1zZGsvY29yZS9iYXNlL2RlY29yYXRvcnMvZGVib3VuY2UuZGVjb3JhdG9ycyc7XHJcbmltcG9ydCB7IERvbSB9IGZyb20gJ0BtaWNyb3NvZnQvd2luZG93cy1hZG1pbi1jZW50ZXItc2RrL2NvcmUvZG9tL2RvbSc7XHJcbmltcG9ydCB7IFNtZVdlYlRlbGVtZXRyeSB9IGZyb20gJ0BtaWNyb3NvZnQvd2luZG93cy1hZG1pbi1jZW50ZXItc2RrL2NvcmUvaW5kZXgnO1xyXG5pbXBvcnQgeyBkZWNvZGUgfSBmcm9tICdodG1sLWVudGl0aWVzJztcclxuaW1wb3J0IHsgQ29yZUJhc2VDb21wb25lbnQgfSBmcm9tICcuLi9jb21tb24vYmFzZS5jb21wb25lbnQnO1xyXG5pbXBvcnQgeyBUb29sdGlwSG9zdCB9IGZyb20gJy4vbW9kZWxzL3Rvb2x0aXAtaG9zdCc7XHJcbmltcG9ydCB7IFRvb2x0aXBPcHRpb25zLCBUb29sdGlwU2hvd01vZGUgfSBmcm9tICcuL21vZGVscy90b29sdGlwLW9wdGlvbnMnO1xyXG5pbXBvcnQgeyBUb29sdGlwUGxhY2VtZW50LCBUb29sdGlwUGxhY2VtZW50RW51bSB9IGZyb20gJy4vbW9kZWxzL3Rvb2x0aXAtcGxhY2VtZW50JztcclxuaW1wb3J0IHsgVG9vbHRpcEhvc3RJbnN0YW5jZSB9IGZyb20gJy4vdG9vbHRpcC1ob3N0LWluc3RhbmNlJztcclxuaW1wb3J0IHsgVG9vbHRpcFpvbmVEaXJlY3RpdmUgfSBmcm9tICcuL3Rvb2x0aXAtem9uZS5kaXJlY3RpdmUnO1xyXG5cclxuZXhwb3J0IGNsYXNzIFRvb2xUaXBGb2N1c0V2ZW50IHtcclxuICAgIHN0YXRpYyBmb2N1c0luID0gJ2ZvY3VzaW4nO1xyXG4gICAgc3RhdGljIGZvY3VzT3V0ID0gJ2ZvY3Vzb3V0JztcclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIFRvb2xUaXBNb3VzZUV2ZW50IHtcclxuICAgIHN0YXRpYyBtb3VzZUVudGVyID0gJ21vdXNlZW50ZXInO1xyXG4gICAgc3RhdGljIG1vdXNlTGVhdmUgPSAnbW91c2VsZWF2ZSc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKlxyXG4gKiBAc21lRG9jIHtAbGFiZWwgVG9vbHRpcCBAaWQgc21lLXRvb2x0aXAtZGlyZWN0aXZlfVxyXG4gKlxyXG4gKiBAb3ZlcnZpZXdcclxuICogQGZpbGUge0BmaWxlcGF0aCAuL2V4YW1wbGVzL292ZXJ2aWV3Lm1kfVxyXG4gKlxyXG4gKiBAZXhhbXBsZSB7QGxhYmVsIEJhc2ljIFVzYWdlIEBpZCBiYXNpYy11c2FnZX1cclxuICogQGZpbGUge0BmaWxlbmFtZSBjb21wb25lbnQuaHRtbCBAZmlsZXBhdGggLi9leGFtcGxlcy9iYXNpYy11c2FnZS5jb21wb25lbnQuaHRtbH1cclxuICogQGZpbGUge0BmaWxlbmFtZSBjb21wb25lbnQudHMgQGZpbGVwYXRoIC4vZXhhbXBsZXMvdG9vbHRpcC1leGFtcGxlLmNvbXBvbmVudC50c31cclxuICpcclxuICogQGV4YW1wbGUge0BsYWJlbCBDdXN0b20gVGVtcGxhdGUgQGlkIGN1c3RvbS10ZW1wbGF0ZX1cclxuICogQGZpbGUge0BmaWxlbmFtZSBjb21wb25lbnQuaHRtbCBAZmlsZXBhdGggLi9leGFtcGxlcy90ZW1wbGF0ZS1yZWZlcmVuY2UuY29tcG9uZW50Lmh0bWx9XHJcbiAqIEBmaWxlIHtAZmlsZW5hbWUgY29tcG9uZW50LnRzIEBmaWxlcGF0aCAuL2V4YW1wbGVzL3Rvb2x0aXAtZXhhbXBsZS5jb21wb25lbnQudHN9XHJcbiAqXHJcbiAqIEBleGFtcGxlIHtAbGFiZWwgQ3VzdG9tIEltcGxlbWVudGF0aW9uIEBpZCB0b29sdGlwLXpvbmV9XHJcbiAqIEBmaWxlIHtAZmlsZW5hbWUgY29tcG9uZW50Lmh0bWwgQGZpbGVwYXRoIC4vZXhhbXBsZXMvdG9vbHRpcC16b25lLmNvbXBvbmVudC5odG1sfVxyXG4gKi9cclxuXHJcbi8qKlxyXG4gKiBEaXJlY3RpdmUgdG8gY3JlYXRlIHRvb2x0aXBzIGluIHRoZSB0b29sdGlwIGhvc3QuXHJcbiAqL1xyXG5ARGlyZWN0aXZlKHtcclxuICAgIHNlbGVjdG9yOiAnW3NtZVRvb2x0aXBdLFt0aXRsZV0nXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBUb29sdGlwRGlyZWN0aXZlIGV4dGVuZHMgQ29yZUJhc2VDb21wb25lbnQgaW1wbGVtZW50cyBPbkRlc3Ryb3ksIE9uQ2hhbmdlcywgT25Jbml0IHtcclxuXHJcbiAgICAvKipcclxuICAgICAqIFRoZSBzb3VyY2UgbmFtZSB0byB1c2UgZm9yIGxvZ2dpbmdcclxuICAgICAqL1xyXG4gICAgcHJvdGVjdGVkIGdldCBsb2dTb3VyY2VOYW1lKCkge1xyXG4gICAgICAgIHJldHVybiAnVG9vbHRpcERpcmVjdGl2ZSc7XHJcbiAgICB9XHJcblxyXG4gICAgQE91dHB1dCgpIHB1YmxpYyB0b29sVGlwRm9jdXNFdmVudFRyaWdnZXJlZDogRXZlbnRFbWl0dGVyPFRvb2xUaXBGb2N1c0V2ZW50PiA9IG5ldyBFdmVudEVtaXR0ZXI8VG9vbFRpcEZvY3VzRXZlbnQ+KCk7XHJcbiAgICBAT3V0cHV0KCkgcHVibGljIHRvb2xUaXBNb3VzZUV2ZW50VHJpZ2dlcmVkOiBFdmVudEVtaXR0ZXI8VG9vbFRpcE1vdXNlRXZlbnQ+ID0gbmV3IEV2ZW50RW1pdHRlcjxUb29sVGlwTW91c2VFdmVudD4oKTtcclxuXHJcbiAgICBwcml2YXRlIGludGVybmFsVGl0bGU6IHN0cmluZztcclxuXHJcbiAgICAvKipcclxuICAgICAqIFNldHMgdGhlIG5hdGl2ZSB0b29sdGlwIHByb3BlcnR5LlxyXG4gICAgICpcclxuICAgICAqIFRoaXMgaW5wdXQgaXMgc3VzY2VwdGlibGUgdG8gWFNTIGF0dGFja3MsIGFuZCBpcyBzYW5pdGl6ZWQgaWYgbmVjZXNzYXJ5LlxyXG4gICAgICogSW4gY2FzZXMgdGhlIGlucHV0IGlzIHNhbml0aXplZCwgdGhlIGRpc3BsYXllZCB0b29sdGlwIHRvIHRoZSB1c2VyIHdpbGwgb25seSBzaG93IHRoZSBzYW5pdGl6ZWQgY29udGVudC5cclxuICAgICAqIFRoaXMgYmVoYXZpb3IgKGRlZmVuc2UpIGlzIGV4cGVjdGVkIHNpbmNlIHVzZXJzIHNob3VsZCBub3QgcHJlc2VudCBYU1MgcGF5bG9hZHMgaW4gdGhlIFt0aXRsZV0gYXR0cmlidXRlLlxyXG4gICAgICovXHJcbiAgICBASW5wdXQoKVxyXG4gICAgcHVibGljIHNldCB0aXRsZShpbnB1dDogc3RyaW5nKSB7XHJcbiAgICAgICAgaWYgKCFNc2Z0U21lLmlzTnVsbE9yVW5kZWZpbmVkKGlucHV0KSkge1xyXG4gICAgICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vYW5ndWxhci9hbmd1bGFyL2Jsb2IvbWFpbi9wYWNrYWdlcy9jb3JlL3NyYy9zYW5pdGl6YXRpb24vaHRtbF9zYW5pdGl6ZXIudHNcclxuICAgICAgICAgICAgY29uc3Qgc2FuaXRpemVkSW5wdXQgPSB0aGlzLmRvbVNhbml0aXplci5zYW5pdGl6ZShTZWN1cml0eUNvbnRleHQuSFRNTCwgaW5wdXQpO1xyXG5cclxuICAgICAgICAgICAgLy8gV2hlbiBzYW5pdGl6aW5nIFwic2FmZVwiIHNwZWNpYWxpemVkIGlucHV0cyAoZS5nLiwgbG9jYWxpemVkIGlucHV0cylcclxuICAgICAgICAgICAgLy8gdGhlIERvbVNhbml0aXplciBjYW4gcmV0dXJuIHBhcnRzIG9mIHRoZSB2YWx1ZSBhcyBlbmNvZGVkLCAoZS5nLiwgJ29sw6EnIGNhbiBiZSByZXR1cm5lZCBhcyAnb2wmIzIyNTsnKSxcclxuICAgICAgICAgICAgLy8gdGhlcmVmb3JlLCB3ZSBjb21wYXJlIHRoZSBkZWNvZGVkIHZhbHVlcyB0byBzZWUgaWYgYW55IHBhcnQgb2YgdGhlIGlucHV0IHdhcyBhY3R1YWxseSBzYW5pdGl6ZWQgYW5kIGRldGVybWluZVxyXG4gICAgICAgICAgICAvLyB3aGF0IGlzIGRpc3BsYXllZCBpbiB0aGUgdG9vbHRpcCBiYXNlZCBvbiBzYW5pdGF0aW9uIHJlc3VsdHMuXHJcbiAgICAgICAgICAgIGlmIChkZWNvZGUoc2FuaXRpemVkSW5wdXQpICE9PSBkZWNvZGUoaW5wdXQpKSB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLmludGVybmFsVGl0bGUgPSBzYW5pdGl6ZWRJbnB1dDtcclxuXHJcbiAgICAgICAgICAgICAgICBTbWVXZWJUZWxlbWV0cnkudHJhY2VBY3Rpb24oXHJcbiAgICAgICAgICAgICAgICAgICAgbnVsbCxcclxuICAgICAgICAgICAgICAgICAgICB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRlbnQ6IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsOiAnU2VjdXJpdHlQcm90ZWN0aW9uRXZlbnQnLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ1hTU1ByZXZlbnRpb24nXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgIC8vIFNhZmUgaW5wdXRcclxuICAgICAgICAgICAgICAgIHRoaXMuaW50ZXJuYWxUaXRsZSA9IGlucHV0O1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogR2V0cyB0aGUgbmF0aXZlIHRvb2x0aXAgcHJvcGVydHkuXHJcbiAgICAgKi9cclxuICAgIHB1YmxpYyBnZXQgdGl0bGUoKTogc3RyaW5nIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5pbnRlcm5hbFRpdGxlO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogQSB0ZW1wbGF0ZSByZWZlcmVuY2UgZm9yIGEgY3VzdG9tIHRvb2x0aXAuXHJcbiAgICAgKi9cclxuICAgIEBJbnB1dCgpXHJcbiAgICBwdWJsaWMgc21lVG9vbHRpcDogVGVtcGxhdGVSZWY8YW55PjtcclxuXHJcbiAgICAvKipcclxuICAgICAqIEEgZGF0YSBvYmplY3QgdGhhdCBjYW4gYmUgYWNjZXNzZWQgaW4gYSB0ZW1wbGF0ZSByZWZlcmVuY2UgZm9yIGEgY3VzdG9tIHRvb2x0aXAuXHJcbiAgICAgKi9cclxuICAgIEBJbnB1dCgpXHJcbiAgICBwdWJsaWMgc21lVG9vbHRpcENvbnRleHQ6IGFueTtcclxuXHJcbiAgICAvKipcclxuICAgICAqIFRoZSBpbnB1dCBmb3IgdGhlIHBsYWNlbWVudCBvZiB0aGUgdG9vbHRpcC4gRGVmYXVsdHMgdG8gJ2JvdHRvbScuXHJcbiAgICAgKi9cclxuICAgIEBJbnB1dCgpXHJcbiAgICBwdWJsaWMgc21lVG9vbHRpcFBsYWNlbWVudDogc3RyaW5nO1xyXG5cclxuICAgIC8qKlxyXG4gICAgICogSW5kaWNhdGVzIHdoZW4gdG8gc2hvdyB0aGUgdG9vbHRpcC5cclxuICAgICAqL1xyXG4gICAgQElucHV0KClcclxuICAgIHB1YmxpYyBzbWVUb29sdGlwU2hvd09uOiBUb29sdGlwU2hvd01vZGU7XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBJbmRpY2F0ZXMgd2hlbiB0byBleHBsaWNpdGx5IG5vdCBzaG93IHRoZSB0b29sdGlwIGluIGNhc2VzIHdoZXJlIGl0IG5lZWRzIHRvIGJlIHNob3duIG9ubHkgdW5kZXIgY2VydGFpbiBjb25kaXRpb25zLlxyXG4gICAgICovXHJcbiAgICBASW5wdXQoKVxyXG4gICAgcHVibGljIHNtZVRvb2x0aXBTaG93ID0gdHJ1ZTtcclxuXHJcbiAgICAvKipcclxuICAgICAqIEluZGljYXRlcyBpZiByZWNlaXZpbmcgaG92ZXIgYW5kIGZvY3VzIGV2ZW50cy5cclxuICAgICAqL1xyXG4gICAgQElucHV0KClcclxuICAgIHB1YmxpYyBzbWVUb29sdGlwRW5hYmxlRXZlbnRzID0gZmFsc2U7XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBJbmRpY2F0ZXMgaWYgaGlkZSB0aGUgdG9vbHRpcCB3aGVuIGlubmVyIEhUTUwgaXMgdGhlIHNhbWUgYXMgdG9vbHRpcCBjb250ZW50XHJcbiAgICAgKiBJbiBjZXJ0YWluIGNhc2VzLCB3ZSBhbHdheXMgbmVlZCB0aGUgdG9vbHRpcCB0byBzaG93LCBzdWNoIGFzIHRoZSBtdWx0aS10b29sIGNvbXBvbmVudCB0b29sIGxpc3QuXHJcbiAgICAgKi9cclxuICAgIEBJbnB1dCgpXHJcbiAgICBwdWJsaWMgaGlkZU9uVG9vbHRpcFNhbWVBc1RleHQgPSB0cnVlO1xyXG5cclxuICAgIC8qKlxyXG4gICAgICogVGhlIGRlZmluZWQgem9uZSBmb3Igc2hvd2luZyB0b29sdGlwIHdpdGggYSBzaG9ydGN1dC4gVGhlIHpvbmUgaXMgYSB3cmFwcGVyIGZvciB0aGUgY29udGVudCB0aGF0IGlzIHJlbGV2YW50IHRvIHRoZSB0b29sdGlwLlxyXG4gICAgICovXHJcbiAgICBASW5wdXQoKVxyXG4gICAgcHVibGljIHRvb2x0aXBab25lOiBUb29sdGlwWm9uZURpcmVjdGl2ZTtcclxuXHJcbiAgICAvKipcclxuICAgICAqIEdldHMgdGhlIGlkIG9mIHRoZSB0b29sdGlwIHRoYXQgd2lsbCBkZXNjcmliZSB0aGlzIGVsZW1lbnRcclxuICAgICAqL1xyXG4gICAgQEhvc3RCaW5kaW5nKCdhdHRyLmFyaWEtZGVzY3JpYmVkQnknKVxyXG4gICAgcHVibGljIGdldCBhcmlhRGVzY3JpYmVkQnkoKTogc3RyaW5nIHtcclxuICAgICAgICBpZiAoIXRoaXMuc2hvd24gfHwgIU1zZnRTbWUuaXNOdWxsT3JVbmRlZmluZWQodGhpcy5zbWVUb29sdGlwKSB8fCAhdGhpcy5oaWRlT25Ub29sdGlwU2FtZUFzVGV4dCkge1xyXG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuaWRCYWcgJiYgdGhpcy5pZEJhZy50b29sdGlwID8gdGhpcy5pZEJhZy50b29sdGlwIDogbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIFRoZSBhY3R1YWwgcGxhY2VtZW50IG9mIHRoZSB0b29sdGlwXHJcbiAgICAgKi9cclxuICAgIHByaXZhdGUgZ2V0IHBsYWNlbWVudCgpOiBUb29sdGlwUGxhY2VtZW50IHtcclxuICAgICAgICByZXR1cm4gVG9vbHRpcFBsYWNlbWVudEVudW0uZnJvbVN0cmluZyh0aGlzLnNtZVRvb2x0aXBQbGFjZW1lbnQpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogVGhlIHRvb2x0aXAgaG9zdFxyXG4gICAgICovXHJcbiAgICBwcml2YXRlIGdldCB0b29sdGlwSG9zdCgpOiBUb29sdGlwSG9zdCB7XHJcbiAgICAgICAgcmV0dXJuIFRvb2x0aXBIb3N0SW5zdGFuY2UuYWN0aXZlSG9zdDtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEluZGljYXRlcyB0aGF0IHRvb2x0aXBzIHNob3VsZCBzaG93IG9uIGZvY3VzL2hvdmVyLlxyXG4gICAgICovXHJcbiAgICBwdWJsaWMgZ2V0IHNob3dPbmZvY3VzKCkge1xyXG4gICAgICAgIHJldHVybiAhIU1zZnRTbWUuc2VsZigpLlJlc291cmNlcy5hY2Nlc3NpYmlsaXR5TW9kZTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEludGVybmFsIHBsYWNlaG9sZGVyIGZvciB0aGUgY3VycmVudCBmb2N1c2VkIHN0YXRlXHJcbiAgICAgKi9cclxuICAgIHByaXZhdGUgZm9jdXNlZDogYm9vbGVhbjtcclxuXHJcbiAgICAvKipcclxuICAgICAqIEludGVybmFsIHBsYWNlaG9sZGVyIGZvciB0aGUgY3VycmVudCBjbGlja2VkIHN0YXRlXHJcbiAgICAgKi9cclxuICAgIHByaXZhdGUgY2xpY2tlZDogYm9vbGVhbjtcclxuXHJcbiAgICAvKipcclxuICAgICAqIEludGVybmFsIHBsYWNlaG9sZGVyIGZvciB0aGUgY3VycmVudCBob3ZlcmVkIHN0YXRlXHJcbiAgICAgKi9cclxuICAgIHByaXZhdGUgaG92ZXJlZDogYm9vbGVhbjtcclxuXHJcbiAgICAvKipcclxuICAgICAqIEludGVybmFsIG11dGF0aW9uIG9ic2VydmVyIHVzZWQgdG8gd2F0Y2ggZm9yIGRpc2FibGVkIGF0dHJpYnV0ZSBvbiBlbGVtZW50c1xyXG4gICAgICovXHJcbiAgICBwcml2YXRlIG9ic2VydmVyOiBNdXRhdGlvbk9ic2VydmVyO1xyXG5cclxuICAgIC8qKlxyXG4gICAgICogSW50ZXJuYWwgcGxhY2Vob2xkZXIgZm9yIHRvb2x0aXAgdHJpZ2dlcmluZyBjYXVzZSAoY2xpY2ssIGhvdmVyLCBmb2N1cylcclxuICAgICAqL1xyXG4gICAgcHJpdmF0ZSB0b29sdGlwVHJpZ2dlcjogVG9vbHRpcFNob3dNb2RlO1xyXG5cclxuICAgIC8qKlxyXG4gICAgICogSW5kaWNhdGVzIGlmIHRoZSB0b29sdGlwIGlzIGJlaW5nIHNob3duLlxyXG4gICAgICovXHJcbiAgICBwdWJsaWMgZ2V0IHNob3duKCk6IGJvb2xlYW4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzLnRvb2x0aXBIb3N0LmlzU2hvd24odGhpcy5jb21wb25lbnRJZCk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBJbmRpY2F0ZXMgdGhhdCB0aGlzIHRvb2x0aXAgc2hvdWxkIHNob3cgb25seSB3aGVuIHRoZSB0ZXh0IGlzIHRydW5jYXRlZFxyXG4gICAgICogV2UgYXNzdW1lIHRoaXMgaXMgdHJ1ZSBpZiB0aGUgdGl0bGUgYXR0cmlidXRlIGlzIGV4YWN0bHkgdGhlIHNhbWUgYXMgdGhlIHRleHQgY29udGVudCBvZiB0aGlzIG5vZGUuXHJcbiAgICAgKi9cclxuICAgIHByaXZhdGUgZ2V0IGlzVG9vbHRpcFNhbWVBc1RleHQoKTogYm9vbGVhbiB7XHJcbiAgICAgICAgaWYgKE1zZnRTbWUuaXNOdWxsT3JVbmRlZmluZWQodGhpcy50aXRsZSkpIHtcclxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgY29uc3QgbmF0aXZlaG9zdEVsZW1lbnQgPSA8SFRNTEVsZW1lbnQ+dGhpcy5ob3N0RWxlbWVudC5uYXRpdmVFbGVtZW50O1xyXG4gICAgICAgIHJldHVybiAhIXRoaXMudGl0bGUgJiYgTXNmdFNtZS5sb2NhbGVDb21wYXJlSWdub3JlQ2FzZShcclxuICAgICAgICAgICAgU3RyaW5nKHRoaXMudGl0bGUpLnRyaW0oKSwgbmF0aXZlaG9zdEVsZW1lbnQudGV4dENvbnRlbnQudHJpbSgpKSA9PT0gMDtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEluZGljYXRlcyB0aGF0IHRoaXMgdG9vbHRpcCBzaG91bGQgYmUgc2hvd25cclxuICAgICAqL1xyXG4gICAgcHJpdmF0ZSBnZXQgc2hvdWxkU2hvdygpIHtcclxuICAgICAgICBpZiAoTXNmdFNtZS5pc051bGxPcldoaXRlU3BhY2UodGhpcy50aXRsZSlcclxuICAgICAgICAgICAgJiYgTXNmdFNtZS5pc051bGxPclVuZGVmaW5lZCh0aGlzLnNtZVRvb2x0aXApIHx8ICF0aGlzLnNtZVRvb2x0aXBTaG93KSB7XHJcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmIChNc2Z0U21lLmFwcGx5Qml0d2lzZUFuZCh0aGlzLnNtZVRvb2x0aXBTaG93T24sIFRvb2x0aXBTaG93TW9kZS5DbGljaykpIHtcclxuICAgICAgICAgICAgLy8gaWYgd2UgYXJlIGhvdmVyZWQgb3Igb3VyIHRvb2x0aXAgaXMgaG92ZXJlZFxyXG4gICAgICAgICAgICBpZiAodGhpcy5jbGlja2VkKSB7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgLy8gaWYgd2UgYXJlIHNob3duIGFuZCBhcmUgZm9jdXNlZCBvciBvdXIgdG9vbHRpcFxyXG4gICAgICAgICAgICBpZiAodGhpcy5zaG93biAmJiB0aGlzLnRvb2x0aXBIb3N0LmlzRm9jdXNlZCkge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9IGVsc2UgaWYgKE1zZnRTbWUuYXBwbHlCaXR3aXNlQW5kKHRoaXMuc21lVG9vbHRpcFNob3dPbixcclxuICAgICAgICAgICAgKE1zZnRTbWUuYXBwbHlCaXR3aXNlT3IoVG9vbHRpcFNob3dNb2RlLkhvdmVyLCBUb29sdGlwU2hvd01vZGUuS2V5Ym9hcmRGb2N1cykpKSkge1xyXG4gICAgICAgICAgICBpZiAodGhpcy5oaWRlT25Ub29sdGlwU2FtZUFzVGV4dCAmJiB0aGlzLmlzVG9vbHRpcFNhbWVBc1RleHQgJiYgIXRoaXMuZ2V0SXNUcnVuY2F0ZWQoKSkge1xyXG5cclxuICAgICAgICAgICAgICAgIC8vIFJlbW92ZSB0aXRsZSBhdHRyaWJ1dGUgd2hlbiB0cnVlIGNvbmRpdGlvbnMgYXJlIG5vdCBtZXRcclxuICAgICAgICAgICAgICAgIHRoaXMuc2V0QXR0cmlidXRlKCd0aXRsZScsICcnKTtcclxuICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgLy8gaWYgd2UgYXJlIGhvdmVyZWQgb3Igb3VyIHRvb2x0aXAgaXMgaG92ZXJlZFxyXG4gICAgICAgICAgICBpZiAodGhpcy5ob3ZlcmVkIHx8ICh0aGlzLnNob3duICYmIHRoaXMudG9vbHRpcEhvc3QuaXNIb3ZlcmVkKSkge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIC8vIGlmIHdlIHNob3cgb24gZm9jdXMgYW5kIGFyZSBmb2N1c2VkIG9yIG91ciB0b29sdGlwIGhhcyBmb2N1c1xyXG4gICAgICAgICAgICBpZiAodGhpcy5zaG93T25mb2N1cyAmJiAodGhpcy5mb2N1c2VkIHx8ICh0aGlzLnNob3duICYmIHRoaXMudG9vbHRpcEhvc3QuaXNGb2N1c2VkKSkpIHtcclxuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBSZW1vdmUgdGl0bGUgYXR0cmlidXRlIHdoZW4gdHJ1ZSBjb25kaXRpb25zIGFyZSBub3QgbWV0XHJcbiAgICAgICAgdGhpcy5zZXRBdHRyaWJ1dGUoJ3RpdGxlJywgJycpO1xyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEluaXRpYWxpemVzIGEgbmV3IGluc3RhbmNlIG9mIHRoZSBAc2VlIFBpdm90Q29tcG9uZW50IGNsYXNzLlxyXG4gICAgICogQHBhcmFtIGluamVjdG9yIFRoZSBhbmd1bGFyIGluamVjdGlvbiBzZXJ2aWNlLiByZXF1aXJlZCBieSBAU21lSW5qZWN0YWJsZUJhc2UoKSBkZWNvcmF0b3IgaW4gdGhlIGJhc2UgQHNlZSBCYXNlQ29tcG9uZW50IGNsYXNzXHJcbiAgICAgKi9cclxuICAgIGNvbnN0cnVjdG9yKHByaXZhdGUgZWxlbWVudFJlZjogRWxlbWVudFJlZiwgcHJpdmF0ZSBkb21TYW5pdGl6ZXI6IERvbVNhbml0aXplciwgaW5qZWN0b3I6IEluamVjdG9yKSB7XHJcbiAgICAgICAgc3VwZXIoaW5qZWN0b3Ip