@microsoft/windows-admin-center-sdk
Version:
Microsoft - Windows Admin Center Shell
425 lines • 60.4 kB
JavaScript
import { Component, ElementRef, HostListener, Injector, NgZone, ViewChild } from '@angular/core';
import { Dom } from '@microsoft/windows-admin-center-sdk/core/dom/dom';
import { CoreBaseComponent } from '../common/base.component';
import { TooltipShowMode } from './models/tooltip-options';
import { TooltipHostInstance } from './tooltip-host-instance';
import * as i0 from "@angular/core";
import * as i1 from "@angular/common";
import * as i2 from "../../directives/template-outlet/template-outlet.directive";
const _c0 = ["tooltipContainer"];
const _c1 = ["tooltipPointer"];
function TooltipHostComponent_div_5_ng_container_1_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementContainerStart(0);
i0.ɵɵelementContainer(1, 10);
i0.ɵɵelementContainerEnd();
} if (rf & 2) {
const ctx_r4 = i0.ɵɵnextContext(2);
i0.ɵɵadvance(1);
i0.ɵɵproperty("template", ctx_r4.options.template)("data", ctx_r4.options.context)("hostData", ctx_r4.hostData);
} }
function TooltipHostComponent_div_5_ng_container_2_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementContainerStart(0);
i0.ɵɵelementStart(1, "p", 11)(2, "span");
i0.ɵɵtext(3);
i0.ɵɵelementEnd()();
i0.ɵɵelementContainerEnd();
} if (rf & 2) {
const ctx_r5 = i0.ɵɵnextContext(2);
i0.ɵɵadvance(1);
i0.ɵɵproperty("id", ctx_r5.idBag.label);
i0.ɵɵadvance(2);
i0.ɵɵtextInterpolate(ctx_r5.options.title);
} }
function TooltipHostComponent_div_5_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementStart(0, "div", 8);
i0.ɵɵtemplate(1, TooltipHostComponent_div_5_ng_container_1_Template, 2, 3, "ng-container", 9);
i0.ɵɵtemplate(2, TooltipHostComponent_div_5_ng_container_2_Template, 4, 2, "ng-container", 9);
i0.ɵɵelementEnd();
} if (rf & 2) {
const ctx_r2 = i0.ɵɵnextContext();
i0.ɵɵproperty("id", ctx_r2.options.contentId);
i0.ɵɵattribute("role", ctx_r2.ariaRole)("aria-modal", ctx_r2.ariaModal)("aria-labelledby", ctx_r2.idBag.label)("aria-describedby", ctx_r2.idBag.description);
i0.ɵɵadvance(1);
i0.ɵɵproperty("ngIf", !!ctx_r2.options.template);
i0.ɵɵadvance(1);
i0.ɵɵproperty("ngIf", ctx_r2.options.title);
} }
function TooltipHostComponent_div_7_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementStart(0, "div", 12)(1, "p", 11)(2, "span");
i0.ɵɵtext(3);
i0.ɵɵelementEnd()()();
} if (rf & 2) {
const ctx_r3 = i0.ɵɵnextContext();
i0.ɵɵproperty("id", ctx_r3.options.contentId);
i0.ɵɵattribute("role", ctx_r3.ariaRole)("aria-modal", ctx_r3.ariaModal)("aria-labelledby", ctx_r3.idBag.label)("aria-describedby", ctx_r3.idBag.description);
i0.ɵɵadvance(1);
i0.ɵɵproperty("id", ctx_r3.idBag.label);
i0.ɵɵadvance(2);
i0.ɵɵtextInterpolate(ctx_r3.options.title);
} }
/**
* Tooltip host component provides a host for tooltips.
*/
export class TooltipHostComponent extends CoreBaseComponent {
/**
* Initializes a new instance of the @see TooltipHostComponent class.
* @param injector The angular injection service. required by @SmeInjectableBase() decorator in the base @see BaseComponent class
*/
constructor(injector, zone) {
super(injector);
this.zone = zone;
TooltipHostInstance.register(this);
this.hostData = { ariaLabelId: this.idBag.label, ariaDescriptionId: this.idBag.description };
}
/**
* The source name to use for logging
*/
get logSourceName() {
return 'TooltipHostComponent';
}
/**
* indicates that the tooltip has focus
*/
get isFocused() {
return this.options && this.isShown(this.options.targetid) && this.focused;
}
/**
* indicates that the tooltip is hovered
*/
get isHovered() {
return this.options && this.isShown(this.options.targetid) && this.hovered;
}
/**
* Gets the role that this tooltip is playing. If the tooltip is text only,
* then it is treated as a dialog.
* otherwise it is treated as a tooltip
*/
get ariaRole() {
if (this.options && this.options.template) {
return 'alertdialog';
}
return 'tooltip';
}
/**
* Gets the aria modal attribute value
*/
get ariaModal() {
if (this.options && this.options.template) {
return true;
}
return null;
}
/**
* Indicates if the tooltip content outside the aria-live region will be shown
*/
get showNonAriaLiveContent() {
return this.options && (!!this.options.template || !this.options.shouldRead);
}
/**
* Indicates if the tooltip content inside the aria-live region will be shown
*/
get showAriaLiveContent() {
return this.options && this.options.shouldRead;
}
ngOnInit() {
super.ngOnInit();
this.onMouseMoveListener = this.onMouseMove.bind(this);
this.zone.runOutsideAngular(() => document.body.addEventListener('mousemove', this.onMouseMoveListener));
}
ngOnDestroy() {
super.ngOnDestroy();
document.body.removeEventListener('mousemove', this.onMouseMoveListener);
TooltipHostInstance.unregister(this);
}
/**
* Occurs when the tooltip container is focused or unfocused
* @param event the focusin or focusout event
*/
onFocusChanged(event) {
this.focused = this.options && (event.type === 'focusin' || (this.focused && event.type !== 'focusout'));
if (this.options) {
this.options.onStateChanged();
}
}
/**
* Occurs when the tooltip container is hovered or un-hovered
* @param event the mouseenter or mouseleave event
*/
onHoverChanged(event) {
this.hovered = this.options && (event.type === 'mouseenter' || (this.hovered && event.type !== 'mouseleave'));
if (this.options) {
this.options.onStateChanged();
}
}
/**
* Window resize listener
*/
onWindowResized(event) {
if (this.options) {
this.hide(this.options.targetid);
}
}
/**
* Escape key listener
*/
// document:keyup.escape is used so that a dialog is not closed when trying to close a tooltip
onEscape(event) {
if (this.options) {
// Stop propagation so that if in a dialog it will not close.
event.stopImmediatePropagation();
this.hide(this.options.targetid);
}
}
/**
* Mouse movement listener
* We do not use HostListener here to avoid invoking change detection for all mousemove events.
*/
onMouseMove(event) {
this.mousePosition = {
x: event.x,
y: event.y
};
}
/**
* Escape key listener
*/
onClick(event) {
if (this.options && this.options.showOn === TooltipShowMode.Click) {
this.options.onWindowClick();
this.hide(this.options.targetid);
}
if (MsftSme.isNullOrUndefined(this.mousePosition)) {
this.mousePosition = {
x: event.x,
y: event.y
};
}
}
/**
* Show a tooltip with the given options
*/
show(options) {
if (this.options) {
this.hide(this.options.targetid);
}
this.options = options;
this.reposition(false, options.triggeredFrom);
setTimeout(() => {
const next = Dom.getNextFocusableElement(this.hostElement.nativeElement);
if (next) {
next.focus();
}
});
}
/**
* Updates the active tooltip with new options
* @param id the tooltip id
*/
update(options) {
if (this.isShown(options.targetid)) {
this.options.contentId = options.contentId;
this.options.context = options.context;
this.options.template = options.template;
this.options.title = options.title;
this.options.element = options.element;
this.options.placement = options.placement;
}
this.reposition(false, options.triggeredFrom);
}
/**
* Hides the tooltip with the given id
* @param id the tooltip id
*/
isShown(id) {
return this.options && this.options.targetid === id;
}
/**
* Hides the tooltip with the given id
* @param id the tooltip id
*/
hide(id) {
if (this.isShown(id)) {
this.hovered = false;
this.focused = false;
// if we opened a template, restore focus to the original element when we close the tooltip
if (this.options.template && this.options.restoreFocusTo) {
// Check to see if the template had any focusable elements that caused focus to change
const tooltipFocusableElement = Dom.getFirstElement(this.hostElement.nativeElement, element => Dom.isFocusable(element));
if (tooltipFocusableElement) {
this.options.restoreFocusTo.focus();
}
}
// Reset positioning so that sme-layout-off-screen will not be overridden
// when the options are null.
const tooltip = this.tooltipContainer.nativeElement;
this.renderer.removeStyle(tooltip, 'left');
this.renderer.removeStyle(tooltip, 'top');
this.options = null;
}
}
/**
* Repositions the tooltip
*/
reposition(finalReposition, tooltipTrigger) {
if (this.options) {
// if the target element is no longer in the dom, cancel the tooltip
if (!document.body.contains(this.options.element.nativeElement)) {
this.hide(this.options.targetid);
return;
}
// Get elements for manipulation
const pointer = this.tooltipPointer.nativeElement;
const tooltip = this.tooltipContainer.nativeElement;
// get bounding rectangles for measurement
const targetRect = this.options.element.nativeElement.getBoundingClientRect();
const hostRect = this.hostElement.nativeElement.getBoundingClientRect();
const pointerRect = pointer.getBoundingClientRect();
const tooltipRect = tooltip.getBoundingClientRect();
// get initial top and left position of the tooltip
const leftAdjustment = 2; // Adjust the tooltip location a little bit to align with some icons (as target) better.
let left = targetRect.left - hostRect.left + leftAdjustment;
let top = (targetRect.top - hostRect.top) + targetRect.height;
// get the margin of the tooltip and include that in the calculation
const tooltipLeftMargin = parseInt(window.getComputedStyle(tooltip).marginLeft, 10);
const tooltipRightMargin = parseInt(window.getComputedStyle(tooltip).marginRight, 10);
let pointerLeft = ((targetRect.width / 2) - (pointerRect.width / 2)) - tooltipLeftMargin;
// if the pointer hangs off the left side of the tooltip, then adjust the left position of the tooltip and pointer
if (pointerLeft < tooltipLeftMargin) {
const leftOffset = tooltipLeftMargin - pointerLeft;
left -= leftOffset;
pointerLeft += leftOffset;
}
// on the second round, we can take the height and width of the tooltip into account.
if (finalReposition && tooltipRect.height > 0 && tooltipRect.width > 0) {
// if the target width is greater than the rendered tooltip width,
// then adjust the pointer and tooltip to align with the pointer
if (tooltipRect.width < targetRect.width) {
pointerLeft = (((tooltipRect.width / 2) - (pointerRect.width / 2)) - tooltipLeftMargin);
// If hovering mouse cursor, apply mouse repositioning logic.
if (MsftSme.applyBitwiseAnd(tooltipTrigger, TooltipShowMode.Hover)) {
if (this.mousePosition.x <= 0) {
left = (tooltipRect.width / 2) - tooltipLeftMargin;
}
else {
left = this.mousePosition.x - ((tooltipRect.width / 2) - tooltipLeftMargin);
}
}
}
// check if right edge of tooltip is off the screen
const right = (left + tooltipRect.width);
const rightOverflow = right - (hostRect.right - TooltipHostComponent.SCREEN_EDGE_MARGIN);
if (rightOverflow > 0) {
// adjust left position to account for right offset
left -= rightOverflow;
pointerLeft += rightOverflow;
}
// Adjust for tooltip hanging on left after adjustment on right
if (pointerLeft > tooltipRect.width) {
pointerLeft = (tooltipRect.width - pointerRect.width - tooltipRightMargin);
}
if (top + tooltipRect.height + TooltipHostComponent.SCREEN_EDGE_MARGIN > hostRect.bottom) {
// If the tooltip's bottom is out of the viewport's bottom edge,
// Show the tooltip above the target element.
const pointerTopOffset = 3; // Need 3px here for the pointer top to make it align well with the tooltip content box.
top = targetRect.top - tooltipRect.height - pointer.clientHeight;
this.renderer.setStyle(pointer, 'top', `${tooltipRect.height - pointer.clientHeight / 2 - pointerTopOffset}px`);
}
else {
this.renderer.setStyle(pointer, 'top', `-8px`);
}
// check if left edge of tooltip is off the screen
if (left < 0) {
const oldLeft = left;
left = -tooltipLeftMargin;
pointerLeft = (pointerLeft + oldLeft + left) < 4 ? 4 : pointerLeft + oldLeft;
}
this.renderer.setStyle(tooltip, 'top', `${top}px`);
this.renderer.setStyle(tooltip, 'left', `${left}px`);
this.renderer.setStyle(pointer, 'left', `${pointerLeft}px`);
// TODO: (as the need arises)
// Allow for tooltip placement (left right top bottom)
}
else {
// For the first round of rendering, just move the tooltip outside the viewport.
// But we still can get the size of the tooltip for the 2nd round of calculation.
// This way won't cause unnecessary content overflow and scrollbars.
this.renderer.setStyle(tooltip, 'left', `-999px`);
this.renderer.setStyle(tooltip, 'top', `-999px`);
}
if (!finalReposition) {
setTimeout(() => this.reposition(true, tooltipTrigger), 0);
}
}
}
/**
* 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 {
label: '',
description: ''
};
}
/**
* Gets the initial host classes to be applied to this element
*/
getInitialHostClasses() {
return super.getInitialHostClasses().concat([
'sme-tooltip-host',
'sme-layer-tooltip',
'sme-layout-fixed',
'sme-position-inset-none',
'sme-focus-zone'
]);
}
}
TooltipHostComponent.SCREEN_EDGE_MARGIN = 16;
/** @nocollapse */ TooltipHostComponent.ɵfac = function TooltipHostComponent_Factory(t) { return new (t || TooltipHostComponent)(i0.ɵɵdirectiveInject(i0.Injector), i0.ɵɵdirectiveInject(i0.NgZone)); };
/** @nocollapse */ TooltipHostComponent.ɵcmp = /** @pureOrBreakMyCode */ i0.ɵɵdefineComponent({ type: TooltipHostComponent, selectors: [["sme-tooltip-host"]], viewQuery: function TooltipHostComponent_Query(rf, ctx) { if (rf & 1) {
i0.ɵɵviewQuery(_c0, 5);
i0.ɵɵviewQuery(_c1, 5);
} if (rf & 2) {
let _t;
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.tooltipContainer = _t.first);
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.tooltipPointer = _t.first);
} }, hostBindings: function TooltipHostComponent_HostBindings(rf, ctx) { if (rf & 1) {
i0.ɵɵlistener("resize", function TooltipHostComponent_resize_HostBindingHandler($event) { return ctx.onWindowResized($event); }, false, i0.ɵɵresolveWindow)("blur", function TooltipHostComponent_blur_HostBindingHandler($event) { return ctx.onWindowResized($event); }, false, i0.ɵɵresolveWindow)("keyup.escape", function TooltipHostComponent_keyup_escape_HostBindingHandler($event) { return ctx.onEscape($event); }, false, i0.ɵɵresolveDocument)("click", function TooltipHostComponent_click_HostBindingHandler($event) { return ctx.onClick($event); }, false, i0.ɵɵresolveWindow);
} }, features: [i0.ɵɵInheritDefinitionFeature], decls: 8, vars: 4, consts: [[1, "sme-tooltip", "sme-tooltip-position-bottom", "sme-layer-tooltip", "sme-shadow-tooltip", 3, "focusin", "focusout", "mouseenter", "mouseleave"], ["tooltipContainer", ""], [1, "sme-tooltip-pointer"], ["tooltipPointer", ""], [1, "sme-tooltip-pointer-cover"], ["class", "sme-tooltip-content sme-focus-trap", 3, "id", 4, "ngIf"], ["aria-live", "assertive"], ["class", "sme-tooltip-content", 3, "id", 4, "ngIf"], [1, "sme-tooltip-content", "sme-focus-trap", 3, "id"], [4, "ngIf"], ["smeTemplateOutlet", "", 3, "template", "data", "hostData"], [1, "sme-font-label", 3, "id"], [1, "sme-tooltip-content", 3, "id"]], template: function TooltipHostComponent_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementStart(0, "div", 0, 1);
i0.ɵɵlistener("focusin", function TooltipHostComponent_Template_div_focusin_0_listener($event) { return ctx.onFocusChanged($event); })("focusout", function TooltipHostComponent_Template_div_focusout_0_listener($event) { return ctx.onFocusChanged($event); })("mouseenter", function TooltipHostComponent_Template_div_mouseenter_0_listener($event) { return ctx.onHoverChanged($event); })("mouseleave", function TooltipHostComponent_Template_div_mouseleave_0_listener($event) { return ctx.onHoverChanged($event); });
i0.ɵɵelement(2, "div", 2, 3)(4, "div", 4);
i0.ɵɵtemplate(5, TooltipHostComponent_div_5_Template, 3, 7, "div", 5);
i0.ɵɵelementStart(6, "div", 6);
i0.ɵɵtemplate(7, TooltipHostComponent_div_7_Template, 4, 7, "div", 7);
i0.ɵɵelementEnd()();
} if (rf & 2) {
i0.ɵɵclassProp("sme-screen-reader", !ctx.options);
i0.ɵɵadvance(5);
i0.ɵɵproperty("ngIf", ctx.showNonAriaLiveContent);
i0.ɵɵadvance(2);
i0.ɵɵproperty("ngIf", ctx.showAriaLiveContent);
} }, dependencies: [i1.NgIf, i2.TemplateOutletDirective], encapsulation: 2 });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TooltipHostComponent, [{
type: Component,
args: [{ selector: 'sme-tooltip-host', template: "<div #tooltipContainer class=\"sme-tooltip sme-tooltip-position-bottom sme-layer-tooltip sme-shadow-tooltip\" [class.sme-screen-reader]=\"!options\"\r\n (focusin)=\"onFocusChanged($event)\" (focusout)=\"onFocusChanged($event)\" (mouseenter)=\"onHoverChanged($event)\" (mouseleave)=\"onHoverChanged($event)\">\r\n <div #tooltipPointer class=\"sme-tooltip-pointer\"></div>\r\n <div class=\"sme-tooltip-pointer-cover\"></div>\r\n\r\n <div class=\"sme-tooltip-content sme-focus-trap\" *ngIf=\"showNonAriaLiveContent\" [attr.role]=\"ariaRole\" [attr.aria-modal]=\"ariaModal\"\r\n [id]=\"options.contentId\" [attr.aria-labelledby]=\"idBag.label\" [attr.aria-describedby]=\"idBag.description\">\r\n <ng-container *ngIf=\"!!options.template\">\r\n <ng-container smeTemplateOutlet [template]=\"options.template\" [data]=\"options.context\" [hostData]=\"hostData\">\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"options.title\">\r\n <p class=\"sme-font-label\" [id]=\"idBag.label\">\r\n <span>{{options.title}}</span>\r\n </p>\r\n </ng-container>\r\n </div>\r\n\r\n <div aria-live=\"assertive\">\r\n <div class=\"sme-tooltip-content\" *ngIf=\"showAriaLiveContent\" [attr.role]=\"ariaRole\" [attr.aria-modal]=\"ariaModal\"\r\n [id]=\"options.contentId\" [attr.aria-labelledby]=\"idBag.label\" [attr.aria-describedby]=\"idBag.description\">\r\n <p class=\"sme-font-label\" [id]=\"idBag.label\">\r\n <span>{{options.title}}</span>\r\n </p>\r\n </div>\r\n </div>\r\n</div>\r\n" }]
}], function () { return [{ type: i0.Injector }, { type: i0.NgZone }]; }, { tooltipContainer: [{
type: ViewChild,
args: ['tooltipContainer']
}], tooltipPointer: [{
type: ViewChild,
args: ['tooltipPointer']
}], onWindowResized: [{
type: HostListener,
args: ['window:resize', ['$event']]
}, {
type: HostListener,
args: ['window:blur', ['$event']]
}], onEscape: [{
type: HostListener,
args: ['document:keyup.escape', ['$event']]
}], onClick: [{
type: HostListener,
args: ['window:click', ['$event']]
}] }); })();
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbHRpcC1ob3N0LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL2FuZ3VsYXIvc3JjL2NvbnRyb2xzL3Rvb2x0aXAvdG9vbHRpcC1ob3N0LmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL2FuZ3VsYXIvc3JjL2NvbnRyb2xzL3Rvb2x0aXAvdG9vbHRpcC1ob3N0LmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFxQixTQUFTLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDcEgsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLGtEQUFrRCxDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBRTdELE9BQU8sRUFBa0IsZUFBZSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDM0UsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0seUJBQXlCLENBQUM7Ozs7Ozs7SUNFMUQsNkJBQXlDO0lBQ3ZDLDRCQUNlO0lBQ2pCLDBCQUFlOzs7SUFGbUIsZUFBNkI7SUFBN0Isa0RBQTZCLGdDQUFBLDZCQUFBOzs7SUFJL0QsNkJBQW9DO0lBQ2xDLDZCQUE2QyxXQUFBO0lBQ3JDLFlBQWlCO0lBQUEsaUJBQU8sRUFBQTtJQUVsQywwQkFBZTs7O0lBSGEsZUFBa0I7SUFBbEIsdUNBQWtCO0lBQ3BDLGVBQWlCO0lBQWpCLDBDQUFpQjs7O0lBVDdCLDhCQUM0RztJQUMxRyw2RkFHZTtJQUVmLDZGQUllO0lBQ2pCLGlCQUFNOzs7SUFYSiw2Q0FBd0I7SUFEcUQsdUNBQXNCLGdDQUFBLHVDQUFBLDhDQUFBO0lBRXBGLGVBQXdCO0lBQXhCLGdEQUF3QjtJQUt4QixlQUFtQjtJQUFuQiwyQ0FBbUI7OztJQVFsQywrQkFDNEcsWUFB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QW5KLHdHQUFXLDBCQUFzQixJQUFDLDZGQUFhLDBCQUFzQixJQUFuQyxpR0FBbUQsMEJBQXNCLElBQXpFLGlHQUF5RiwwQkFBc0IsSUFBL0c7UUFDbEMsNEJBQXVELGFBQUE7UUFHdkQscUVBWU07UUFFTiw4QkFBMkI7UUFDekIscUVBS007UUFDUixpQkFBTSxFQUFBOztRQTFCb0csaURBQW9DO1FBSzdGLGVBQTRCO1FBQTVCLGlEQUE0QjtRQWV6QyxlQUF5QjtRQUF6Qiw4Q0FBeUI7O3VGRERsRCxvQkFBb0I7Y0FKaEMsU0FBUzsyQkFDSSxrQkFBa0I7Z0ZBa0JyQixnQkFBZ0I7a0JBRHRCLFNBQVM7bUJBQUMsa0JBQWtCO1lBT3RCLGNBQWM7a0JBRHBCLFNBQVM7bUJBQUMsZ0JBQWdCO1lBcUlwQixlQUFlO2tCQUZyQixZQUFZO21CQUFDLGVBQWUsRUFBRSxDQUFDLFFBQVEsQ0FBQzs7a0JBQ3hDLFlBQVk7bUJBQUMsYUFBYSxFQUFFLENBQUMsUUFBUSxDQUFDO1lBWWhDLFFBQVE7a0JBRGQsWUFBWTttQkFBQyx1QkFBdUIsRUFBRSxDQUFDLFFBQVEsQ0FBQztZQXdCMUMsT0FBTztrQkFEYixZQUFZO21CQUFDLGNBQWMsRUFBRSxDQUFDLFFBQVEsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgRWxlbWVudFJlZiwgSG9zdExpc3RlbmVyLCBJbmplY3RvciwgTmdab25lLCBPbkRlc3Ryb3ksIE9uSW5pdCwgVmlld0NoaWxkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IERvbSB9IGZyb20gJ0BtaWNyb3NvZnQvd2luZG93cy1hZG1pbi1jZW50ZXItc2RrL2NvcmUvZG9tL2RvbSc7XHJcbmltcG9ydCB7IENvcmVCYXNlQ29tcG9uZW50IH0gZnJvbSAnLi4vY29tbW9uL2Jhc2UuY29tcG9uZW50JztcclxuaW1wb3J0IHsgVG9vbHRpcEhvc3QgfSBmcm9tICcuL21vZGVscy90b29sdGlwLWhvc3QnO1xyXG5pbXBvcnQgeyBUb29sdGlwT3B0aW9ucywgVG9vbHRpcFNob3dNb2RlIH0gZnJvbSAnLi9tb2RlbHMvdG9vbHRpcC1vcHRpb25zJztcclxuaW1wb3J0IHsgVG9vbHRpcEhvc3RJbnN0YW5jZSB9IGZyb20gJy4vdG9vbHRpcC1ob3N0LWluc3RhbmNlJztcclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgVG9vbHRpcE1vdXNlUG9zaXRpb24ge1xyXG4gICAgeDogbnVtYmVyO1xyXG4gICAgeTogbnVtYmVyO1xyXG59XHJcblxyXG4vKipcclxuICogVG9vbHRpcCBob3N0IGNvbXBvbmVudCBwcm92aWRlcyBhIGhvc3QgZm9yIHRvb2x0aXBzLlxyXG4gKi9cclxuQENvbXBvbmVudCh7XHJcbiAgICBzZWxlY3RvcjogJ3NtZS10b29sdGlwLWhvc3QnLFxyXG4gICAgdGVtcGxhdGVVcmw6ICcuL3Rvb2x0aXAtaG9zdC5jb21wb25lbnQuaHRtbCdcclxufSlcclxuZXhwb3J0IGNsYXNzIFRvb2x0aXBIb3N0Q29tcG9uZW50IGV4dGVuZHMgQ29yZUJhc2VDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSwgVG9vbHRpcEhvc3Qge1xyXG5cclxuICAgIC8qKlxyXG4gICAgICogVGhlIHNvdXJjZSBuYW1lIHRvIHVzZSBmb3IgbG9nZ2luZ1xyXG4gICAgICovXHJcbiAgICBwcm90ZWN0ZWQgZ2V0IGxvZ1NvdXJjZU5hbWUoKSB7XHJcbiAgICAgICAgcmV0dXJuICdUb29sdGlwSG9zdENvbXBvbmVudCc7XHJcbiAgICB9XHJcblxyXG4gICAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgU0NSRUVOX0VER0VfTUFSR0lOID0gMTY7XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBUaGUgZWxlbWVudCB0aGF0IGNvbnRhaW5zIGFuZCBwb3NpdGlvbnMgdGhlIHRvb2x0aXBcclxuICAgICAqL1xyXG4gICAgQFZpZXdDaGlsZCgndG9vbHRpcENvbnRhaW5lcicpXHJcbiAgICBwdWJsaWMgdG9vbHRpcENvbnRhaW5lcjogRWxlbWVudFJlZjtcclxuXHJcbiAgICAvKipcclxuICAgICAqIFRoZSBlbGVtZW50IHRoYXQgaXMgdXNlZCB0byBwb2ludCB0byB0aGUgdGFyZ2V0IG9mIHRoZSB0b29sdGlwXHJcbiAgICAgKi9cclxuICAgIEBWaWV3Q2hpbGQoJ3Rvb2x0aXBQb2ludGVyJylcclxuICAgIHB1YmxpYyB0b29sdGlwUG9pbnRlcjogRWxlbWVudFJlZjtcclxuXHJcbiAgICAvKipcclxuICAgICAqIFRoZSBjdXJyZW50IHRvb2x0aXAgb3B0aW9uc1xyXG4gICAgICovXHJcbiAgICBwdWJsaWMgb3B0aW9uczogVG9vbHRpcE9wdGlvbnM7XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBJbnRlcm5hbCBwbGFjZWhvbGRlciBmb3IgdGhlIGN1cnJlbnQgZm9jdXNlZCBzdGF0ZVxyXG4gICAgICovXHJcbiAgICBwcml2YXRlIGZvY3VzZWQ6IGJvb2xlYW47XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBJbnRlcm5hbCBwbGFjZWhvbGRlciBmb3IgdGhlIGN1cnJlbnQgaG92ZXJlZCBzdGF0ZVxyXG4gICAgICovXHJcbiAgICBwcml2YXRlIGhvdmVyZWQ6IGJvb2xlYW47XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBjb250YWluZXIgZm9yIGhvc3Qgc3BlY2lmaWMgZGF0YSBiZWluZyBwYXNzZWQgdG8gdGhlIHRvb2x0aXAgdGVtcGxhdGUgY29udGV4dFxyXG4gICAgICovXHJcbiAgICBwdWJsaWMgaG9zdERhdGE6IGFueTtcclxuXHJcbiAgICAvKipcclxuICAgICAqIFRoZSBjdXJyZW50IG1vdXNlIHBvc2l0aW9uIHJlbGF0aXZlIHRvIHRoaXMgaG9zdGluZyBlbGVtZW50XHJcbiAgICAgKi9cclxuICAgIHB1YmxpYyBtb3VzZVBvc2l0aW9uOiBUb29sdGlwTW91c2VQb3NpdGlvbjtcclxuXHJcbiAgICAvKipcclxuICAgICAqIE1vdXNlIG1vdmUgbGlzdGVuZXIgZXZlbnRcclxuICAgICAqL1xyXG4gICAgcHJpdmF0ZSBvbk1vdXNlTW92ZUxpc3RlbmVyOiAoZXZlbnQ6IE1vdXNlRXZlbnQpID0+IHZvaWQ7XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBpbmRpY2F0ZXMgdGhhdCB0aGUgdG9vbHRpcCBoYXMgZm9jdXNcclxuICAgICAqL1xyXG4gICAgcHVibGljIGdldCBpc0ZvY3VzZWQoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMub3B0aW9ucyAmJiB0aGlzLmlzU2hvd24odGhpcy5vcHRpb25zLnRhcmdldGlkKSAmJiB0aGlzLmZvY3VzZWQ7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBpbmRpY2F0ZXMgdGhhdCB0aGUgdG9vbHRpcCBpcyBob3ZlcmVkXHJcbiAgICAgKi9cclxuICAgIHB1YmxpYyBnZXQgaXNIb3ZlcmVkKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLm9wdGlvbnMgJiYgdGhpcy5pc1Nob3duKHRoaXMub3B0aW9ucy50YXJnZXRpZCkgJiYgdGhpcy5ob3ZlcmVkO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogR2V0cyB0aGUgcm9sZSB0aGF0IHRoaXMgdG9vbHRpcCBpcyBwbGF5aW5nLiBJZiB0aGUgdG9vbHRpcCBpcyB0ZXh0IG9ubHksXHJcbiAgICAgKiB0aGVuIGl0IGlzIHRyZWF0ZWQgYXMgYSBkaWFsb2cuXHJcbiAgICAgKiBvdGhlcndpc2UgaXQgaXMgdHJlYXRlZCBhcyBhIHRvb2x0aXBcclxuICAgICAqL1xyXG4gICAgcHVibGljIGdldCBhcmlhUm9sZSgpIHtcclxuICAgICAgICBpZiAodGhpcy5vcHRpb25zICYmIHRoaXMub3B0aW9ucy50ZW1wbGF0ZSkge1xyXG4gICAgICAgICAgICByZXR1cm4gJ2FsZXJ0ZGlhbG9nJztcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuICd0b29sdGlwJztcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEdldHMgdGhlIGFyaWEgbW9kYWwgYXR0cmlidXRlIHZhbHVlXHJcbiAgICAgKi9cclxuICAgIHB1YmxpYyBnZXQgYXJpYU1vZGFsKCk6IGJvb2xlYW4ge1xyXG4gICAgICAgIGlmICh0aGlzLm9wdGlvbnMgJiYgdGhpcy5vcHRpb25zLnRlbXBsYXRlKSB7XHJcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEluZGljYXRlcyBpZiB0aGUgdG9vbHRpcCBjb250ZW50IG91dHNpZGUgdGhlIGFyaWEtbGl2ZSByZWdpb24gd2lsbCBiZSBzaG93blxyXG4gICAgICovXHJcbiAgICBwdWJsaWMgZ2V0IHNob3dOb25BcmlhTGl2ZUNvbnRlbnQoKTogYm9vbGVhbiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMub3B0aW9ucyAmJiAoISF0aGlzLm9wdGlvbnMudGVtcGxhdGUgfHwgIXRoaXMub3B0aW9ucy5zaG91bGRSZWFkKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEluZGljYXRlcyBpZiB0aGUgdG9vbHRpcCBjb250ZW50IGluc2lkZSB0aGUgYXJpYS1saXZlIHJlZ2lvbiB3aWxsIGJlIHNob3duXHJcbiAgICAgKi9cclxuICAgIHB1YmxpYyBnZXQgc2hvd0FyaWFMaXZlQ29udGVudCgpOiBib29sZWFuIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5vcHRpb25zICYmIHRoaXMub3B0aW9ucy5zaG91bGRSZWFkO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogSW5pdGlhbGl6ZXMgYSBuZXcgaW5zdGFuY2Ugb2YgdGhlIEBzZWUgVG9vbHRpcEhvc3RDb21wb25lbnQgY2xhc3MuXHJcbiAgICAgKiBAcGFyYW0gaW5qZWN0b3IgVGhlIGFuZ3VsYXIgaW5qZWN0aW9uIHNlcnZpY2UuIHJlcXVpcmVkIGJ5IEBTbWVJbmplY3RhYmxlQmFzZSgpIGRlY29yYXRvciBpbiB0aGUgYmFzZSBAc2VlIEJhc2VDb21wb25lbnQgY2xhc3NcclxuICAgICAqL1xyXG4gICAgY29uc3RydWN0b3IoaW5qZWN0b3I6IEluamVjdG9yLCBwcml2YXRlIHpvbmU6IE5nWm9uZSkge1xyXG4gICAgICAgIHN1cGVyKGluamVjdG9yKTtcclxuICAgICAgICBUb29sdGlwSG9zdEluc3RhbmNlLnJlZ2lzdGVyKHRoaXMpO1xyXG4gICAgICAgIHRoaXMuaG9zdERhdGEgPSB7IGFyaWFMYWJlbElkOiB0aGlzLmlkQmFnLmxhYmVsLCBhcmlhRGVzY3JpcHRpb25JZDogdGhpcy5pZEJhZy5kZXNjcmlwdGlvbiB9O1xyXG4gICAgfVxyXG5cclxuICAgIHB1YmxpYyBuZ09uSW5pdCgpIHtcclxuICAgICAgICBzdXBlci5uZ09uSW5pdCgpO1xyXG4gICAgICAgIHRoaXMub25Nb3VzZU1vdmVMaXN0ZW5lciA9IHRoaXMub25Nb3VzZU1vdmUuYmluZCh0aGlzKTtcclxuICAgICAgICB0aGlzLnpvbmUucnVuT3V0c2lkZUFuZ3VsYXIoKCkgPT5cclxuICAgICAgICAgICAgZG9jdW1lbnQuYm9keS5hZGRFdmVudExpc3RlbmVyKCdtb3VzZW1vdmUnLCB0aGlzLm9uTW91c2VNb3ZlTGlzdGVuZXIpKTtcclxuICAgIH1cclxuXHJcbiAgICBwdWJsaWMgbmdPbkRlc3Ryb3koKSB7XHJcbiAgICAgICAgc3VwZXIubmdPbkRlc3Ryb3koKTtcclxuICAgICAgICBkb2N1bWVudC5ib2R5LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ21vdXNlbW92ZScsIHRoaXMub25Nb3VzZU1vdmVMaXN0ZW5lcik7XHJcbiAgICAgICAgVG9vbHRpcEhvc3RJbnN0YW5jZS51bnJlZ2lzdGVyKHRoaXMpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogT2NjdXJzIHdoZW4gdGhlIHRvb2x0aXAgY29udGFpbmVyIGlzIGZvY3VzZWQgb3IgdW5mb2N1c2VkXHJcbiAgICAgKiBAcGFyYW0gZXZlbnQgdGhlIGZvY3VzaW4gb3IgZm9jdXNvdXQgZXZlbnRcclxuICAgICAqL1xyXG4gICAgcHVibGljIG9uRm9jdXNDaGFuZ2VkKGV2ZW50OiBFdmVudCkge1xyXG4gICAgICAgIHRoaXMuZm9jdXNlZCA9IHRoaXMub3B0aW9ucyAmJiAoZXZlbnQudHlwZSA9PT0gJ2ZvY3VzaW4nIHx8ICh0aGlzLmZvY3VzZWQgJiYgZXZlbnQudHlwZSAhPT0gJ2ZvY3Vzb3V0JykpO1xyXG4gICAgICAgIGlmICh0aGlzLm9wdGlvbnMpIHtcclxuICAgICAgICAgICAgdGhpcy5vcHRpb25zLm9uU3RhdGVDaGFuZ2VkKCk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogT2NjdXJzIHdoZW4gdGhlIHRvb2x0aXAgY29udGFpbmVyIGlzIGhvdmVyZWQgb3IgdW4taG92ZXJlZFxyXG4gICAgICogQHBhcmFtIGV2ZW50IHRoZSBtb3VzZWVudGVyIG9yIG1vdXNlbGVhdmUgZXZlbnRcclxuICAgICAqL1xyXG4gICAgcHVibGljIG9uSG92ZXJDaGFuZ2VkKGV2ZW50OiBFdmVudCkge1xyXG4gICAgICAgIHRoaXMuaG92ZXJlZCA9IHRoaXMub3B0aW9ucyAmJiAoZXZlbnQudHlwZSA9PT0gJ21vdXNlZW50ZXInIHx8ICh0aGlzLmhvdmVyZWQgJiYgZXZlbnQudHlwZSAhPT0gJ21vdXNlbGVhdmUnKSk7XHJcbiAgICAgICAgaWYgKHRoaXMub3B0aW9ucykge1xyXG4gICAgICAgICAgICB0aGlzLm9wdGlvbnMub25TdGF0ZUNoYW5nZWQoKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBXaW5kb3cgcmVzaXplIGxpc3RlbmVyXHJcbiAgICAgKi9cclxuICAgIEBIb3N0TGlzdGVuZXIoJ3dpbmRvdzpyZXNpemUnLCBbJyRldmVudCddKVxyXG4gICAgQEhvc3RMaXN0ZW5lcignd2luZG93OmJsdXInLCBbJyRldmVudCddKVxyXG4gICAgcHVibGljIG9uV2luZG93UmVzaXplZChldmVudDogRXZlbnQpIHtcclxuICAgICAgICBpZiAodGhpcy5vcHRpb25zKSB7XHJcbiAgICAgICAgICAgIHRoaXMuaGlkZSh0aGlzLm9wdGlvbnMudGFyZ2V0aWQpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEVzY2FwZSBrZXkgbGlzdGVuZXJcclxuICAgICAqL1xyXG4gICAgLy8gZG9jdW1lbnQ6a2V5dXAuZXNjYXBlIGlzIHVzZWQgc28gdGhhdCBhIGRpYWxvZyBpcyBub3QgY2xvc2VkIHdoZW4gdHJ5aW5nIHRvIGNsb3NlIGEgdG9vbHRpcFxyXG4gICAgQEhvc3RMaXN0ZW5lcignZG9jdW1lbnQ6a2V5dXAuZXNjYXBlJywgWyckZXZlbnQnXSlcclxuICAgIHB1YmxpYyBvbkVzY2FwZShldmVudDogRXZlbnQpIHtcclxuICAgICAgICBpZiAodGhpcy5vcHRpb25zKSB7XHJcbiAgICAgICAgICAgIC8vIFN0b3AgcHJvcGFnYXRpb24gc28gdGhhdCBpZiBpbiBhIGRpYWxvZyBpdCB3aWxsIG5vdCBjbG9zZS5cclxuICAgICAgICAgICAgZXZlbnQuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKCk7XHJcbiAgICAgICAgICAgIHRoaXMuaGlkZSh0aGlzLm9wdGlvbnMudGFyZ2V0aWQpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIE1vdXNlIG1vdmVtZW50IGxpc3RlbmVyXHJcbiAgICAgKiBXZSBkbyBub3QgdXNlIEhvc3RMaXN0ZW5lciBoZXJlIHRvIGF2b2lkIGludm9raW5nIGNoYW5nZSBkZXRlY3Rpb24gZm9yIGFsbCBtb3VzZW1vdmUgZXZlbnRzLlxyXG4gICAgICovXHJcbiAgICBwcml2YXRlIG9uTW91c2VNb3ZlKGV2ZW50OiBNb3VzZUV2ZW50KSB7XHJcbiAgICAgICAgdGhpcy5tb3VzZVBvc2l0aW9uID0ge1xyXG4gICAgICAgICAgICB4OiBldmVudC54LFxyXG4gICAgICAgICAgICB5OiBldmVudC55XHJcbiAgICAgICAgfTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEVzY2FwZSBrZXkgbGlzdGVuZXJcclxuICAgICAqL1xyXG4gICAgQEhvc3RMaXN0ZW5lcignd2luZG93OmNsaWNrJywgWyckZXZlbnQnXSlcclxuICAgIHB1YmxpYyBvbkNsaWNrKGV2ZW50OiBQb2ludGVyRXZlbnQpIHtcclxuICAgICAgICBpZiAodGhpcy5vcHRpb25zICYmIHRoaXMub3B0aW9ucy5zaG93T24gPT09IFRvb2x0aXBTaG93TW9kZS5DbGljaykge1xyXG4gICAgICAgICAgICB0aGlzLm9wdGlvbnMub25XaW5kb3dDbGljaygpO1xyXG4gICAgICAgICAgICB0aGlzLmhpZGUodGhpcy5vcHRpb25zLnRhcmdldGlkKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmIChNc2Z0U21lLmlzTnVsbE9yVW5kZWZpbmVkKHRoaXMubW91c2VQb3NpdGlvbikpIHtcclxuICAgICAgICAgICAgdGhpcy5tb3VzZVBvc2l0aW9uID0ge1xyXG4gICAgICAgICAgICAgICAgeDogZXZlbnQueCxcclxuICAgICAgICAgICAgICAgIHk6IGV2ZW50LnlcclxuICAgICAgICAgICAgfTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBTaG93IGEgdG9vbHRpcCB3aXRoIHRoZSBnaXZlbiBvcHRpb25zXHJcbiAgICAgKi9cclxuICAgIHB1YmxpYyBzaG93KG9wdGlvbnM6IFRvb2x0aXBPcHRpb25zKTogdm9pZCB7XHJcbiAgICAgICAgaWYgKHRoaXMub3B0aW9ucykge1xyXG4gICAgICAgICAgICB0aGlzLmhpZGUodGhpcy5vcHRpb25zLnRhcmdldGlkKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgdGhpcy5vcHRpb25zID0gb3B0aW9ucztcclxuICAgICAgICB0aGlzLnJlcG9zaXRpb24oZmFsc2UsIG9wdGlvbnMudHJpZ2dlcmVkRnJvbSk7XHJcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XHJcbiAgICAgICAgICAgIGNvbnN0IG5leHQgPSBEb20uZ2V0TmV4dEZvY3VzYWJsZUVsZW1lbnQodGhpcy5ob3N0RWxlbWVudC5uYXRpdmVFbGVtZW50KTtcclxuICAgICAgICAgICAgaWYgKG5leHQpIHtcclxuICAgICAgICAgICAgICAgIG5leHQuZm9jdXMoKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogVXBkYXRlcyB0aGUgYWN0aXZlIHRvb2x0aXAgd2l0aCBuZXcgb3B0aW9uc1xyXG4gICAgICogQHBhcmFtIGlkIHRoZSB0b29sdGlwIGlkXHJcbiAgICAgKi9cclxuICAgIHB1YmxpYyB1cGRhdGUob3B0aW9uczogVG9vbHRpcE9wdGlvbnMpOiB2b2lkIHtcclxuICAgICAgICBpZiAodGhpcy5pc1Nob3duKG9wdGlvbnMudGFyZ2V0aWQpKSB7XHJcbiAgICAgICAgICAgIHRoaXMub3B0aW9ucy5jb250ZW50SWQgPSBvcHRpb25zLmNvbnRlbnRJZDtcclxuICAgICAgICAgICAgdGhpcy5vcHRpb25zLmNvbnRleHQgPSBvcHRpb25zLmNvbnRleHQ7XHJcbiAgICAgICAgICAgIHRoaXMub3B0aW9ucy50ZW1wbGF0ZSA9IG9wdGlvbnMudGVtcGxhdGU7XHJcbiAgICAgICAgICAgIHRoaXMub3B0aW9ucy50aXRsZSA9IG9wdGlvbnMudGl0bGU7XHJcbiAgICAgICAgICAgIHRoaXMub3B0aW9ucy5lbGVtZW50ID0gb3B0aW9ucy5lbGVtZW50O1xyXG4gICAgICAgICAgICB0aGlzLm9wdGlvbnMucGxhY2VtZW50ID0gb3B0aW9ucy5wbGFjZW1lbnQ7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRoaXMucmVwb3NpdGlvbihmYWxzZSwgb3B0aW9ucy50cmlnZ2VyZWRGcm9tKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEhpZGVzIHRoZSB0b29sdGlwIHdpdGggdGhlIGdpdmVuIGlkXHJcbiAgICAgKiBAcGFyYW0gaWQgdGhlIHRvb2x0aXAgaWRcclxuICAgICAqL1xyXG4gICAgcHVibGljIGlzU2hvd24oaWQ6IHN0cmluZyk6IGJvb2xlYW4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzLm9wdGlvbnMgJiYgdGhpcy5vcHRpb25zLnRhcmdldGlkID09PSBpZDtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEhpZGVzIHRoZSB0b29sdGlwIHdpdGggdGhlIGdpdmVuIGlkXHJcbiAgICAgKiBAcGFyYW0gaWQgdGhlIHRvb2x0aXAgaWRcclxuICAgICAqL1xyXG4gICAgcHVibGljIGhpZGUoaWQ6IHN0cmluZyk6IHZvaWQge1xyXG4gICAgICAgIGlmICh0aGlzLmlzU2hvd24oaWQpKSB7XHJcbiAgICAgICAgICAgIHRoaXMuaG92ZXJlZCA9IGZhbHNlO1xyXG4gICAgICAgICAgICB0aGlzLmZvY3VzZWQgPSBmYWxzZTtcclxuICAgICAgICAgICAgLy8gaWYgd2Ugb3BlbmVkIGEgdGVtcGxhdGUsIHJlc3RvcmUgZm9jdXMgdG8gdGhlIG9yaWdpbmFsIGVsZW1lbnQgd2hlbiB3ZSBjbG9zZSB0aGUgdG9vbHRpcFxyXG4gICAgICAgICAgICBpZiAodGhpcy5vcHRpb25zLnRlbXBsYXRlICYmIHRoaXMub3B0aW9ucy5yZXN0b3JlRm9jdXNUbykge1xyXG4gICAgICAgICAgICAgICAgLy8gQ2hlY2sgdG8gc2VlIGlmIHRoZSB0ZW1wbGF0ZSBoYWQgYW55IGZvY3VzYWJsZSBlbGVtZW50cyB0aGF0IGNhdXNlZCBmb2N1cyB0byBjaGFuZ2VcclxuICAgICAgICAgICAgICAgIGNvbnN0IHRvb2x0aXBGb2N1c2FibGVFbGVtZW50ID0gRG9tLmdldEZpcnN0RWxlbWVudChcclxuICAgICAgICAgICAgICAgICAgICA8SFRNTEVsZW1lbnQ+dGhpcy5ob3N0RWxlbWVudC5uYXRpdmVFbGVtZW50LFxyXG4gICAgICAgICAgICAgICAgICAgIGVsZW1lbnQgPT4gRG9tLmlzRm9jdXNhYmxlKGVsZW1lbnQpXHJcbiAgICAgICAgICAgICAgICApO1xyXG4gICAgICAgICAgICAgICAgaWYgKHRvb2x0aXBGb2N1c2FibGVFbGVtZW50KSB7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5vcHRpb25zLnJlc3RvcmVGb2N1c1RvLmZvY3VzKCk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIC8vIFJlc2V0IHBvc2l0aW9uaW5nIHNvIHRoYXQgc21lLWxheW91dC1vZmYtc2NyZWVuIHdpbGwgbm90IGJlIG92ZXJyaWRkZW5cclxuICAgICAgICAgICAgLy8gd2hlbiB0aGUgb3B0aW9ucyBhcmUgbnVsbC5cclxuICAgICAgICAgICAgY29uc3QgdG9vbHRpcCA9IDxIVE1MRWxlbWVudD50aGlzLnRvb2x0aXBDb250YWluZXIubmF0aXZlRWxlbWVudDtcclxuICAgICAgICAgICAgdGhpcy5yZW5kZXJlci5yZW1vdmVTdHlsZSh0b29sdGlwLCAnbGVmdCcpO1xyXG4gICAgICAgICAgICB0aGlzLnJlbmRlcmVyLnJlbW92ZVN0eWxlKHRvb2x0aXAsICd0b3AnKTtcclxuICAgICAgICAgICAgdGhpcy5vcHRpb25zID0gbnVsbDtcclxuICAgICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBSZXBvc2l0aW9ucyB0aGUgdG9vbHRpcFxyXG4gICAgICovXHJcbiAgICBwcml2YXRlIHJlcG9zaXRpb24oZmluYWxSZXBvc2l0aW9uOiBib29sZWFuLCB0b29sdGlwVHJpZ2dlcjogVG9vbHRpcFNob3dNb2RlKTogdm9pZCB7XHJcbiAgICAgICAgaWYgKHRoaXMub3B0aW9ucykge1xyXG4gICAgICAgICAgICAvLyBpZiB0aGUgdGFyZ2V0IGVsZW1lbnQgaXMgbm8gbG9uZ2VyIGluIHRoZSBkb20sIGNhbmNlbCB0aGUgdG9vbHRpcFxyXG4gICAgICAgICAgICBpZiAoIWRvY3VtZW50LmJvZHkuY29udGFpbnModGhpcy5vcHRpb25zLmVsZW1lbnQubmF0aXZlRWxlbWVudCkpIHtcclxuICAgICAgICAgICAgICAgIHRoaXMuaGlkZSh0aGlzLm9wdGlvbnMudGFyZ2V0aWQpO1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAvLyBHZXQgZWxlbWVudHMgZm9yIG1hbmlwdWxhdGlvblxyXG4gICAgICAgICAgICBjb25zdCBwb2ludGVyID0gPEhUTUxFbGVtZW50PnRoaXMudG9vbHRpcFBvaW50ZXIubmF0aXZlRWxlbWVudDtcclxuICAgICAgICAgICAgY29uc3QgdG9vbHRpcCA9IDxIVE1MRWxlbWVudD50aGlzLnRvb2x0aXBDb250YWluZXIubmF0aXZlRWxlbWVudDtcclxuXHJcbiAgICAgICAgICAgIC8vIGdldCBib3VuZGluZyByZWN0