You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
493 lines
71 KiB
493 lines
71 KiB
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
import { ChangeDetectorRef, ElementRef, NgZone, Optional, EventEmitter, Directive, Inject, Input, } from '@angular/core';
|
|
import { Directionality } from '@angular/cdk/bidi';
|
|
import { coerceNumberProperty } from '@angular/cdk/coercion';
|
|
import { ViewportRuler } from '@angular/cdk/scrolling';
|
|
import { FocusKeyManager } from '@angular/cdk/a11y';
|
|
import { ENTER, SPACE, hasModifierKey } from '@angular/cdk/keycodes';
|
|
import { merge, of as observableOf, Subject, timer, fromEvent } from 'rxjs';
|
|
import { takeUntil } from 'rxjs/operators';
|
|
import { Platform, normalizePassiveListenerOptions } from '@angular/cdk/platform';
|
|
import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations';
|
|
/** Config used to bind passive event listeners */
|
|
import * as ɵngcc0 from '@angular/core';
|
|
import * as ɵngcc1 from '@angular/cdk/scrolling';
|
|
import * as ɵngcc2 from '@angular/cdk/bidi';
|
|
import * as ɵngcc3 from '@angular/cdk/platform';
|
|
const passiveEventListenerOptions = normalizePassiveListenerOptions({ passive: true });
|
|
/**
|
|
* The distance in pixels that will be overshot when scrolling a tab label into view. This helps
|
|
* provide a small affordance to the label next to it.
|
|
*/
|
|
const EXAGGERATED_OVERSCROLL = 60;
|
|
/**
|
|
* Amount of milliseconds to wait before starting to scroll the header automatically.
|
|
* Set a little conservatively in order to handle fake events dispatched on touch devices.
|
|
*/
|
|
const HEADER_SCROLL_DELAY = 650;
|
|
/**
|
|
* Interval in milliseconds at which to scroll the header
|
|
* while the user is holding their pointer.
|
|
*/
|
|
const HEADER_SCROLL_INTERVAL = 100;
|
|
/**
|
|
* Base class for a tab header that supported pagination.
|
|
* @docs-private
|
|
*/
|
|
export class MatPaginatedTabHeader {
|
|
constructor(_elementRef, _changeDetectorRef, _viewportRuler, _dir, _ngZone, _platform, _animationMode) {
|
|
this._elementRef = _elementRef;
|
|
this._changeDetectorRef = _changeDetectorRef;
|
|
this._viewportRuler = _viewportRuler;
|
|
this._dir = _dir;
|
|
this._ngZone = _ngZone;
|
|
this._platform = _platform;
|
|
this._animationMode = _animationMode;
|
|
/** The distance in pixels that the tab labels should be translated to the left. */
|
|
this._scrollDistance = 0;
|
|
/** Whether the header should scroll to the selected index after the view has been checked. */
|
|
this._selectedIndexChanged = false;
|
|
/** Emits when the component is destroyed. */
|
|
this._destroyed = new Subject();
|
|
/** Whether the controls for pagination should be displayed */
|
|
this._showPaginationControls = false;
|
|
/** Whether the tab list can be scrolled more towards the end of the tab label list. */
|
|
this._disableScrollAfter = true;
|
|
/** Whether the tab list can be scrolled more towards the beginning of the tab label list. */
|
|
this._disableScrollBefore = true;
|
|
/** Stream that will stop the automated scrolling. */
|
|
this._stopScrolling = new Subject();
|
|
/**
|
|
* Whether pagination should be disabled. This can be used to avoid unnecessary
|
|
* layout recalculations if it's known that pagination won't be required.
|
|
*/
|
|
this.disablePagination = false;
|
|
this._selectedIndex = 0;
|
|
/** Event emitted when the option is selected. */
|
|
this.selectFocusedIndex = new EventEmitter();
|
|
/** Event emitted when a label is focused. */
|
|
this.indexFocused = new EventEmitter();
|
|
// Bind the `mouseleave` event on the outside since it doesn't change anything in the view.
|
|
_ngZone.runOutsideAngular(() => {
|
|
fromEvent(_elementRef.nativeElement, 'mouseleave')
|
|
.pipe(takeUntil(this._destroyed))
|
|
.subscribe(() => {
|
|
this._stopInterval();
|
|
});
|
|
});
|
|
}
|
|
/** The index of the active tab. */
|
|
get selectedIndex() { return this._selectedIndex; }
|
|
set selectedIndex(value) {
|
|
value = coerceNumberProperty(value);
|
|
if (this._selectedIndex != value) {
|
|
this._selectedIndexChanged = true;
|
|
this._selectedIndex = value;
|
|
if (this._keyManager) {
|
|
this._keyManager.updateActiveItem(value);
|
|
}
|
|
}
|
|
}
|
|
ngAfterViewInit() {
|
|
// We need to handle these events manually, because we want to bind passive event listeners.
|
|
fromEvent(this._previousPaginator.nativeElement, 'touchstart', passiveEventListenerOptions)
|
|
.pipe(takeUntil(this._destroyed))
|
|
.subscribe(() => {
|
|
this._handlePaginatorPress('before');
|
|
});
|
|
fromEvent(this._nextPaginator.nativeElement, 'touchstart', passiveEventListenerOptions)
|
|
.pipe(takeUntil(this._destroyed))
|
|
.subscribe(() => {
|
|
this._handlePaginatorPress('after');
|
|
});
|
|
}
|
|
ngAfterContentInit() {
|
|
const dirChange = this._dir ? this._dir.change : observableOf('ltr');
|
|
const resize = this._viewportRuler.change(150);
|
|
const realign = () => {
|
|
this.updatePagination();
|
|
this._alignInkBarToSelectedTab();
|
|
};
|
|
this._keyManager = new FocusKeyManager(this._items)
|
|
.withHorizontalOrientation(this._getLayoutDirection())
|
|
.withHomeAndEnd()
|
|
.withWrap();
|
|
this._keyManager.updateActiveItem(this._selectedIndex);
|
|
// Defer the first call in order to allow for slower browsers to lay out the elements.
|
|
// This helps in cases where the user lands directly on a page with paginated tabs.
|
|
typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame(realign) : realign();
|
|
// On dir change or window resize, realign the ink bar and update the orientation of
|
|
// the key manager if the direction has changed.
|
|
merge(dirChange, resize, this._items.changes).pipe(takeUntil(this._destroyed)).subscribe(() => {
|
|
// We need to defer this to give the browser some time to recalculate
|
|
// the element dimensions. The call has to be wrapped in `NgZone.run`,
|
|
// because the viewport change handler runs outside of Angular.
|
|
this._ngZone.run(() => Promise.resolve().then(realign));
|
|
this._keyManager.withHorizontalOrientation(this._getLayoutDirection());
|
|
});
|
|
// If there is a change in the focus key manager we need to emit the `indexFocused`
|
|
// event in order to provide a public event that notifies about focus changes. Also we realign
|
|
// the tabs container by scrolling the new focused tab into the visible section.
|
|
this._keyManager.change.pipe(takeUntil(this._destroyed)).subscribe(newFocusIndex => {
|
|
this.indexFocused.emit(newFocusIndex);
|
|
this._setTabFocus(newFocusIndex);
|
|
});
|
|
}
|
|
ngAfterContentChecked() {
|
|
// If the number of tab labels have changed, check if scrolling should be enabled
|
|
if (this._tabLabelCount != this._items.length) {
|
|
this.updatePagination();
|
|
this._tabLabelCount = this._items.length;
|
|
this._changeDetectorRef.markForCheck();
|
|
}
|
|
// If the selected index has changed, scroll to the label and check if the scrolling controls
|
|
// should be disabled.
|
|
if (this._selectedIndexChanged) {
|
|
this._scrollToLabel(this._selectedIndex);
|
|
this._checkScrollingControls();
|
|
this._alignInkBarToSelectedTab();
|
|
this._selectedIndexChanged = false;
|
|
this._changeDetectorRef.markForCheck();
|
|
}
|
|
// If the scroll distance has been changed (tab selected, focused, scroll controls activated),
|
|
// then translate the header to reflect this.
|
|
if (this._scrollDistanceChanged) {
|
|
this._updateTabScrollPosition();
|
|
this._scrollDistanceChanged = false;
|
|
this._changeDetectorRef.markForCheck();
|
|
}
|
|
}
|
|
ngOnDestroy() {
|
|
this._destroyed.next();
|
|
this._destroyed.complete();
|
|
this._stopScrolling.complete();
|
|
}
|
|
/** Handles keyboard events on the header. */
|
|
_handleKeydown(event) {
|
|
// We don't handle any key bindings with a modifier key.
|
|
if (hasModifierKey(event)) {
|
|
return;
|
|
}
|
|
switch (event.keyCode) {
|
|
case ENTER:
|
|
case SPACE:
|
|
if (this.focusIndex !== this.selectedIndex) {
|
|
this.selectFocusedIndex.emit(this.focusIndex);
|
|
this._itemSelected(event);
|
|
}
|
|
break;
|
|
default:
|
|
this._keyManager.onKeydown(event);
|
|
}
|
|
}
|
|
/**
|
|
* Callback for when the MutationObserver detects that the content has changed.
|
|
*/
|
|
_onContentChanges() {
|
|
const textContent = this._elementRef.nativeElement.textContent;
|
|
// We need to diff the text content of the header, because the MutationObserver callback
|
|
// will fire even if the text content didn't change which is inefficient and is prone
|
|
// to infinite loops if a poorly constructed expression is passed in (see #14249).
|
|
if (textContent !== this._currentTextContent) {
|
|
this._currentTextContent = textContent || '';
|
|
// The content observer runs outside the `NgZone` by default, which
|
|
// means that we need to bring the callback back in ourselves.
|
|
this._ngZone.run(() => {
|
|
this.updatePagination();
|
|
this._alignInkBarToSelectedTab();
|
|
this._changeDetectorRef.markForCheck();
|
|
});
|
|
}
|
|
}
|
|
/**
|
|
* Updates the view whether pagination should be enabled or not.
|
|
*
|
|
* WARNING: Calling this method can be very costly in terms of performance. It should be called
|
|
* as infrequently as possible from outside of the Tabs component as it causes a reflow of the
|
|
* page.
|
|
*/
|
|
updatePagination() {
|
|
this._checkPaginationEnabled();
|
|
this._checkScrollingControls();
|
|
this._updateTabScrollPosition();
|
|
}
|
|
/** Tracks which element has focus; used for keyboard navigation */
|
|
get focusIndex() {
|
|
return this._keyManager ? this._keyManager.activeItemIndex : 0;
|
|
}
|
|
/** When the focus index is set, we must manually send focus to the correct label */
|
|
set focusIndex(value) {
|
|
if (!this._isValidIndex(value) || this.focusIndex === value || !this._keyManager) {
|
|
return;
|
|
}
|
|
this._keyManager.setActiveItem(value);
|
|
}
|
|
/**
|
|
* Determines if an index is valid. If the tabs are not ready yet, we assume that the user is
|
|
* providing a valid index and return true.
|
|
*/
|
|
_isValidIndex(index) {
|
|
if (!this._items) {
|
|
return true;
|
|
}
|
|
const tab = this._items ? this._items.toArray()[index] : null;
|
|
return !!tab && !tab.disabled;
|
|
}
|
|
/**
|
|
* Sets focus on the HTML element for the label wrapper and scrolls it into the view if
|
|
* scrolling is enabled.
|
|
*/
|
|
_setTabFocus(tabIndex) {
|
|
if (this._showPaginationControls) {
|
|
this._scrollToLabel(tabIndex);
|
|
}
|
|
if (this._items && this._items.length) {
|
|
this._items.toArray()[tabIndex].focus();
|
|
// Do not let the browser manage scrolling to focus the element, this will be handled
|
|
// by using translation. In LTR, the scroll left should be 0. In RTL, the scroll width
|
|
// should be the full width minus the offset width.
|
|
const containerEl = this._tabListContainer.nativeElement;
|
|
const dir = this._getLayoutDirection();
|
|
if (dir == 'ltr') {
|
|
containerEl.scrollLeft = 0;
|
|
}
|
|
else {
|
|
containerEl.scrollLeft = containerEl.scrollWidth - containerEl.offsetWidth;
|
|
}
|
|
}
|
|
}
|
|
/** The layout direction of the containing app. */
|
|
_getLayoutDirection() {
|
|
return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr';
|
|
}
|
|
/** Performs the CSS transformation on the tab list that will cause the list to scroll. */
|
|
_updateTabScrollPosition() {
|
|
if (this.disablePagination) {
|
|
return;
|
|
}
|
|
const scrollDistance = this.scrollDistance;
|
|
const translateX = this._getLayoutDirection() === 'ltr' ? -scrollDistance : scrollDistance;
|
|
// Don't use `translate3d` here because we don't want to create a new layer. A new layer
|
|
// seems to cause flickering and overflow in Internet Explorer. For example, the ink bar
|
|
// and ripples will exceed the boundaries of the visible tab bar.
|
|
// See: https://github.com/angular/components/issues/10276
|
|
// We round the `transform` here, because transforms with sub-pixel precision cause some
|
|
// browsers to blur the content of the element.
|
|
this._tabList.nativeElement.style.transform = `translateX(${Math.round(translateX)}px)`;
|
|
// Setting the `transform` on IE will change the scroll offset of the parent, causing the
|
|
// position to be thrown off in some cases. We have to reset it ourselves to ensure that
|
|
// it doesn't get thrown off. Note that we scope it only to IE and Edge, because messing
|
|
// with the scroll position throws off Chrome 71+ in RTL mode (see #14689).
|
|
if (this._platform.TRIDENT || this._platform.EDGE) {
|
|
this._tabListContainer.nativeElement.scrollLeft = 0;
|
|
}
|
|
}
|
|
/** Sets the distance in pixels that the tab header should be transformed in the X-axis. */
|
|
get scrollDistance() { return this._scrollDistance; }
|
|
set scrollDistance(value) {
|
|
this._scrollTo(value);
|
|
}
|
|
/**
|
|
* Moves the tab list in the 'before' or 'after' direction (towards the beginning of the list or
|
|
* the end of the list, respectively). The distance to scroll is computed to be a third of the
|
|
* length of the tab list view window.
|
|
*
|
|
* This is an expensive call that forces a layout reflow to compute box and scroll metrics and
|
|
* should be called sparingly.
|
|
*/
|
|
_scrollHeader(direction) {
|
|
const viewLength = this._tabListContainer.nativeElement.offsetWidth;
|
|
// Move the scroll distance one-third the length of the tab list's viewport.
|
|
const scrollAmount = (direction == 'before' ? -1 : 1) * viewLength / 3;
|
|
return this._scrollTo(this._scrollDistance + scrollAmount);
|
|
}
|
|
/** Handles click events on the pagination arrows. */
|
|
_handlePaginatorClick(direction) {
|
|
this._stopInterval();
|
|
this._scrollHeader(direction);
|
|
}
|
|
/**
|
|
* Moves the tab list such that the desired tab label (marked by index) is moved into view.
|
|
*
|
|
* This is an expensive call that forces a layout reflow to compute box and scroll metrics and
|
|
* should be called sparingly.
|
|
*/
|
|
_scrollToLabel(labelIndex) {
|
|
if (this.disablePagination) {
|
|
return;
|
|
}
|
|
const selectedLabel = this._items ? this._items.toArray()[labelIndex] : null;
|
|
if (!selectedLabel) {
|
|
return;
|
|
}
|
|
// The view length is the visible width of the tab labels.
|
|
const viewLength = this._tabListContainer.nativeElement.offsetWidth;
|
|
const { offsetLeft, offsetWidth } = selectedLabel.elementRef.nativeElement;
|
|
let labelBeforePos, labelAfterPos;
|
|
if (this._getLayoutDirection() == 'ltr') {
|
|
labelBeforePos = offsetLeft;
|
|
labelAfterPos = labelBeforePos + offsetWidth;
|
|
}
|
|
else {
|
|
labelAfterPos = this._tabList.nativeElement.offsetWidth - offsetLeft;
|
|
labelBeforePos = labelAfterPos - offsetWidth;
|
|
}
|
|
const beforeVisiblePos = this.scrollDistance;
|
|
const afterVisiblePos = this.scrollDistance + viewLength;
|
|
if (labelBeforePos < beforeVisiblePos) {
|
|
// Scroll header to move label to the before direction
|
|
this.scrollDistance -= beforeVisiblePos - labelBeforePos + EXAGGERATED_OVERSCROLL;
|
|
}
|
|
else if (labelAfterPos > afterVisiblePos) {
|
|
// Scroll header to move label to the after direction
|
|
this.scrollDistance += labelAfterPos - afterVisiblePos + EXAGGERATED_OVERSCROLL;
|
|
}
|
|
}
|
|
/**
|
|
* Evaluate whether the pagination controls should be displayed. If the scroll width of the
|
|
* tab list is wider than the size of the header container, then the pagination controls should
|
|
* be shown.
|
|
*
|
|
* This is an expensive call that forces a layout reflow to compute box and scroll metrics and
|
|
* should be called sparingly.
|
|
*/
|
|
_checkPaginationEnabled() {
|
|
if (this.disablePagination) {
|
|
this._showPaginationControls = false;
|
|
}
|
|
else {
|
|
const isEnabled = this._tabList.nativeElement.scrollWidth > this._elementRef.nativeElement.offsetWidth;
|
|
if (!isEnabled) {
|
|
this.scrollDistance = 0;
|
|
}
|
|
if (isEnabled !== this._showPaginationControls) {
|
|
this._changeDetectorRef.markForCheck();
|
|
}
|
|
this._showPaginationControls = isEnabled;
|
|
}
|
|
}
|
|
/**
|
|
* Evaluate whether the before and after controls should be enabled or disabled.
|
|
* If the header is at the beginning of the list (scroll distance is equal to 0) then disable the
|
|
* before button. If the header is at the end of the list (scroll distance is equal to the
|
|
* maximum distance we can scroll), then disable the after button.
|
|
*
|
|
* This is an expensive call that forces a layout reflow to compute box and scroll metrics and
|
|
* should be called sparingly.
|
|
*/
|
|
_checkScrollingControls() {
|
|
if (this.disablePagination) {
|
|
this._disableScrollAfter = this._disableScrollBefore = true;
|
|
}
|
|
else {
|
|
// Check if the pagination arrows should be activated.
|
|
this._disableScrollBefore = this.scrollDistance == 0;
|
|
this._disableScrollAfter = this.scrollDistance == this._getMaxScrollDistance();
|
|
this._changeDetectorRef.markForCheck();
|
|
}
|
|
}
|
|
/**
|
|
* Determines what is the maximum length in pixels that can be set for the scroll distance. This
|
|
* is equal to the difference in width between the tab list container and tab header container.
|
|
*
|
|
* This is an expensive call that forces a layout reflow to compute box and scroll metrics and
|
|
* should be called sparingly.
|
|
*/
|
|
_getMaxScrollDistance() {
|
|
const lengthOfTabList = this._tabList.nativeElement.scrollWidth;
|
|
const viewLength = this._tabListContainer.nativeElement.offsetWidth;
|
|
return (lengthOfTabList - viewLength) || 0;
|
|
}
|
|
/** Tells the ink-bar to align itself to the current label wrapper */
|
|
_alignInkBarToSelectedTab() {
|
|
const selectedItem = this._items && this._items.length ?
|
|
this._items.toArray()[this.selectedIndex] : null;
|
|
const selectedLabelWrapper = selectedItem ? selectedItem.elementRef.nativeElement : null;
|
|
if (selectedLabelWrapper) {
|
|
this._inkBar.alignToElement(selectedLabelWrapper);
|
|
}
|
|
else {
|
|
this._inkBar.hide();
|
|
}
|
|
}
|
|
/** Stops the currently-running paginator interval. */
|
|
_stopInterval() {
|
|
this._stopScrolling.next();
|
|
}
|
|
/**
|
|
* Handles the user pressing down on one of the paginators.
|
|
* Starts scrolling the header after a certain amount of time.
|
|
* @param direction In which direction the paginator should be scrolled.
|
|
*/
|
|
_handlePaginatorPress(direction, mouseEvent) {
|
|
// Don't start auto scrolling for right mouse button clicks. Note that we shouldn't have to
|
|
// null check the `button`, but we do it so we don't break tests that use fake events.
|
|
if (mouseEvent && mouseEvent.button != null && mouseEvent.button !== 0) {
|
|
return;
|
|
}
|
|
// Avoid overlapping timers.
|
|
this._stopInterval();
|
|
// Start a timer after the delay and keep firing based on the interval.
|
|
timer(HEADER_SCROLL_DELAY, HEADER_SCROLL_INTERVAL)
|
|
// Keep the timer going until something tells it to stop or the component is destroyed.
|
|
.pipe(takeUntil(merge(this._stopScrolling, this._destroyed)))
|
|
.subscribe(() => {
|
|
const { maxScrollDistance, distance } = this._scrollHeader(direction);
|
|
// Stop the timer if we've reached the start or the end.
|
|
if (distance === 0 || distance >= maxScrollDistance) {
|
|
this._stopInterval();
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Scrolls the header to a given position.
|
|
* @param position Position to which to scroll.
|
|
* @returns Information on the current scroll distance and the maximum.
|
|
*/
|
|
_scrollTo(position) {
|
|
if (this.disablePagination) {
|
|
return { maxScrollDistance: 0, distance: 0 };
|
|
}
|
|
const maxScrollDistance = this._getMaxScrollDistance();
|
|
this._scrollDistance = Math.max(0, Math.min(maxScrollDistance, position));
|
|
// Mark that the scroll distance has changed so that after the view is checked, the CSS
|
|
// transformation can move the header.
|
|
this._scrollDistanceChanged = true;
|
|
this._checkScrollingControls();
|
|
return { maxScrollDistance, distance: this._scrollDistance };
|
|
}
|
|
}
|
|
MatPaginatedTabHeader.ɵfac = function MatPaginatedTabHeader_Factory(t) { return new (t || MatPaginatedTabHeader)(ɵngcc0.ɵɵdirectiveInject(ɵngcc0.ElementRef), ɵngcc0.ɵɵdirectiveInject(ɵngcc0.ChangeDetectorRef), ɵngcc0.ɵɵdirectiveInject(ɵngcc1.ViewportRuler), ɵngcc0.ɵɵdirectiveInject(ɵngcc2.Directionality, 8), ɵngcc0.ɵɵdirectiveInject(ɵngcc0.NgZone), ɵngcc0.ɵɵdirectiveInject(ɵngcc3.Platform), ɵngcc0.ɵɵdirectiveInject(ANIMATION_MODULE_TYPE, 8)); };
|
|
MatPaginatedTabHeader.ɵdir = /*@__PURE__*/ ɵngcc0.ɵɵdefineDirective({ type: MatPaginatedTabHeader, inputs: { disablePagination: "disablePagination" } });
|
|
MatPaginatedTabHeader.ctorParameters = () => [
|
|
{ type: ElementRef },
|
|
{ type: ChangeDetectorRef },
|
|
{ type: ViewportRuler },
|
|
{ type: Directionality, decorators: [{ type: Optional }] },
|
|
{ type: NgZone },
|
|
{ type: Platform },
|
|
{ type: String, decorators: [{ type: Optional }, { type: Inject, args: [ANIMATION_MODULE_TYPE,] }] }
|
|
];
|
|
MatPaginatedTabHeader.propDecorators = {
|
|
disablePagination: [{ type: Input }]
|
|
};
|
|
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && ɵngcc0.ɵsetClassMetadata(MatPaginatedTabHeader, [{
|
|
type: Directive
|
|
}], function () { return [{ type: ɵngcc0.ElementRef }, { type: ɵngcc0.ChangeDetectorRef }, { type: ɵngcc1.ViewportRuler }, { type: ɵngcc2.Directionality, decorators: [{
|
|
type: Optional
|
|
}] }, { type: ɵngcc0.NgZone }, { type: ɵngcc3.Platform }, { type: String, decorators: [{
|
|
type: Optional
|
|
}, {
|
|
type: Inject,
|
|
args: [ANIMATION_MODULE_TYPE]
|
|
}] }]; }, { disablePagination: [{
|
|
type: Input
|
|
}] }); })();
|
|
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFnaW5hdGVkLXRhYi1oZWFkZXIuanMiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9tYXRlcmlhbC90YWJzL3BhZ2luYXRlZC10YWItaGVhZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFFSCxPQUFPLEVBQ0wsaUJBQWlCLEVBQ2pCLFVBQVUsRUFDVixNQUFNLEVBQ04sUUFBUSxFQUVSLFlBQVksRUFLWixTQUFTLEVBQ1QsTUFBTSxFQUNOLEtBQUssR0FDTixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQVksY0FBYyxFQUFDLE1BQU0sbUJBQW1CLENBQUM7QUFDNUQsT0FBTyxFQUFDLG9CQUFvQixFQUFjLE1BQU0sdUJBQXVCLENBQUM7QUFDeEUsT0FBTyxFQUFDLGFBQWEsRUFBQyxNQUFNLHdCQUF3QixDQUFDO0FBQ3JELE9BQU8sRUFBQyxlQUFlLEVBQWtCLE1BQU0sbUJBQW1CLENBQUM7QUFDbkUsT0FBTyxFQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsY0FBYyxFQUFDLE1BQU0sdUJBQXVCLENBQUM7QUFDbkUsT0FBTyxFQUFDLEtBQUssRUFBRSxFQUFFLElBQUksWUFBWSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFDLE1BQU0sTUFBTSxDQUFDO0FBQzFFLE9BQU8sRUFBQyxTQUFTLEVBQUMsTUFBTSxnQkFBZ0IsQ0FBQztBQUN6QyxPQUFPLEVBQUMsUUFBUSxFQUFFLCtCQUErQixFQUFDLE1BQU0sdUJBQXVCLENBQUM7QUFDaEYsT0FBTyxFQUFDLHFCQUFxQixFQUFDLE1BQU0sc0NBQXNDLENBQUM7QUFHM0Usa0RBQWtEOzs7OztBQUNsRCxNQUFNLDJCQUEyQixHQUM3QiwrQkFBK0IsQ0FBQyxFQUFDLE9BQU8sRUFBRSxJQUFJLEVBQUMsQ0FBeUIsQ0FBQztBQVM3RTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0gsTUFBTSxzQkFBc0Is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QVM7QUFDVCxRQUFNLENBQUMsQ0FBQyxDQUFDO0FBQ1QsSUFBRSxDQUFDO0FBQ0gsSUFDRTtBQUNGO0FBQ0U7QUFDRTtBQUVKLE9BREs7QUFDTCxJQUFVLFNBQVMsQ0FBQyxRQUFnQjtBQUNwQyxRQUFJLElBQUksSUFBSSxDQUFDLGlCQUFpQixFQUFFO0FBQ2hDLFlBQU0sT0FBTyxFQUFDLGlCQUFpQixFQUFFLENBQUMsRUFBRSxRQUFRLEVBQUUsQ0FBQyxFQUFDLENBQUM7QUFDakQsU0FBSztBQUNMLFFBQ0ksTUFBTSxpQkFBaUIsR0FBRyxJQUFJLENBQUMscUJBQXFCLEVBQUUsQ0FBQztBQUMzRCxRQUFJLElBQUksQ0FBQyxlQUFlLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDO0FBQzlFLFFBQ0ksdUZBQXVGO0FBQzNGLFFBQUksc0NBQXNDO0FBQzFDLFFBQUksSUFBSSxDQUFDLHNCQUFzQixHQUFHLElBQUksQ0FBQztBQUN2QyxRQUFJLElBQUksQ0FBQyx1QkFBdUIsRUFBRSxDQUFDO0FBQ25DLFFBQ0ksT0FBTyxFQUFDLGlCQUFpQixFQUFFLFFBQVEsRUFBRSxJQUFJLENBQUMsZUFBZSxFQUFDLENBQUM7QUFDL0QsSUFBRSxDQUFDO0FBQ0g7aURBcmdCQyxTQUFTO3lKQUNSO0FBQUM7QUFBK0MsWUE3RGhELFVBQVU7QUFDVixZQUZBLGlCQUFpQjtBQUNqQixZQWVNLGFBQWE7QUFBSSxZQUZOLGNBQWMsdUJBNkhsQixRQUFRO0FBQU8sWUF6STVCLE1BQU07QUFDTixZQWtCTSxRQUFRO0FBQUkseUNBeUhMLFFBQVEsWUFBSSxNQUFNLFNBQUMscUJBQXFCO0FBQVE7QUFBRztBQUVuRCxnQ0FqQ1osS0FBSztBQUNQOzs7Ozs7Ozs7Ozs7b0JBQUU7QUFBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCBHb29nbGUgTExDIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYW4gTUlULXN0eWxlIGxpY2Vuc2UgdGhhdCBjYW4gYmVcbiAqIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgYXQgaHR0cHM6Ly9hbmd1bGFyLmlvL2xpY2Vuc2VcbiAqL1xuXG5pbXBvcnQge1xuICBDaGFuZ2VEZXRlY3RvclJlZixcbiAgRWxlbWVudFJlZixcbiAgTmdab25lLFxuICBPcHRpb25hbCxcbiAgUXVlcnlMaXN0LFxuICBFdmVudEVtaXR0ZXIsXG4gIEFmdGVyQ29udGVudENoZWNrZWQsXG4gIEFmdGVyQ29udGVudEluaXQsXG4gIEFmdGVyVmlld0luaXQsXG4gIE9uRGVzdHJveSxcbiAgRGlyZWN0aXZlLFxuICBJbmplY3QsXG4gIElucHV0LFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7RGlyZWN0aW9uLCBEaXJlY3Rpb25hbGl0eX0gZnJvbSAnQGFuZ3VsYXIvY2RrL2JpZGknO1xuaW1wb3J0IHtjb2VyY2VOdW1iZXJQcm9wZXJ0eSwgTnVtYmVySW5wdXR9IGZyb20gJ0Bhbmd1bGFyL2Nkay9jb2VyY2lvbic7XG5pbXBvcnQge1ZpZXdwb3J0UnVsZXJ9IGZyb20gJ0Bhbmd1bGFyL2Nkay9zY3JvbGxpbmcnO1xuaW1wb3J0IHtGb2N1c0tleU1hbmFnZXIsIEZvY3VzYWJsZU9wdGlvbn0gZnJvbSAnQGFuZ3VsYXIvY2RrL2ExMXknO1xuaW1wb3J0IHtFTlRFUiwgU1BBQ0UsIGhhc01vZGlmaWVyS2V5fSBmcm9tICdAYW5ndWxhci9jZGsva2V5Y29kZXMnO1xuaW1wb3J0IHttZXJnZSwgb2YgYXMgb2JzZXJ2YWJsZU9mLCBTdWJqZWN0LCB0aW1lciwgZnJvbUV2ZW50fSBmcm9tICdyeGpzJztcbmltcG9ydCB7dGFrZVVudGlsfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQge1BsYXRmb3JtLCBub3JtYWxpemVQYXNzaXZlTGlzdGVuZXJPcHRpb25zfSBmcm9tICdAYW5ndWxhci9jZGsvcGxhdGZvcm0nO1xuaW1wb3J0IHtBTklNQVRJT05fTU9EVUxFX1RZUEV9IGZyb20gJ0Bhbmd1bGFyL3BsYXRmb3JtLWJyb3dzZXIvYW5pbWF0aW9ucyc7XG5cblxuLyoqIENvbmZpZyB1c2VkIHRvIGJpbmQgcGFzc2l2ZSBldmVudCBsaXN0ZW5lcnMgKi9cbmNvbnN0IHBhc3NpdmVFdmVudExpc3RlbmVyT3B0aW9ucyA9XG4gICAgbm9ybWFsaXplUGFzc2l2ZUxpc3RlbmVyT3B0aW9ucyh7cGFzc2l2ZTogdHJ1ZX0pIGFzIEV2ZW50TGlzdGVuZXJPcHRpb25zO1xuXG4vKipcbiAqIFRoZSBkaXJlY3Rpb25zIHRoYXQgc2Nyb2xsaW5nIGNhbiBnbyBpbiB3aGVuIHRoZSBoZWFkZXIncyB0YWJzIGV4Y2VlZCB0aGUgaGVhZGVyIHdpZHRoLiAnQWZ0ZXInXG4gKiB3aWxsIHNjcm9sbCB0aGUgaGVhZGVyIHRvd2FyZHMgdGhlIGVuZCBvZiB0aGUgdGFicyBsaXN0IGFuZCAnYmVmb3JlJyB3aWxsIHNjcm9sbCB0b3dhcmRzIHRoZVxuICogYmVnaW5uaW5nIG9mIHRoZSBsaXN0LlxuICovXG5leHBvcnQgdHlwZSBTY3JvbGxEaXJlY3Rpb24gPSAnYWZ0ZXInIHwgJ2JlZm9yZSc7XG5cbi8qKlxuICogVGhlIGRpc3RhbmNlIGluIHBpeGVscyB0aGF0IHdpbGwgYmUgb3ZlcnNob3Qgd2hlbiBzY3JvbGxpbmcgYSB0YWIgbGFiZWwgaW50byB2aWV3LiBUaGlzIGhlbHBzXG4gKiBwcm92aWRlIGEgc21hbGwgYWZmb3JkYW5jZSB0byB0aGUgbGFiZWwgbmV4dCB0byBpdC5cbiAqL1xuY29uc3QgRVhBR0dFUkFURURfT1ZFUlNDUk9MTCA9IDYwO1xuXG4vKipcbiAqIEFtb3VudCBvZiBtaWxsaXNlY29uZHMgdG8gd2FpdCBiZWZvcmUgc3RhcnRpbmcgdG8gc2Nyb2xsIHRoZSBoZWFkZXIgYXV0b21hdGljYWxseS5cbiAqIFNldCBhIGxpdHRsZSBjb25zZXJ2YXRpdmVseSBpbiBvcmRlciB0byBoYW5kbGUgZmFrZSBldmVudHMgZGlzcGF0Y2hlZCBvbiB0b3VjaCBkZXZpY2VzLlxuICovXG5jb25zdCBIRUFERVJfU0NST0xMX0RFTEFZID0gNjUwO1xuXG4vKipcbiAqIEludGVydmFsIGluIG1pbGxpc2Vjb25kcyBhdCB3aGljaCB0byBzY3JvbGwgdGhlIGhlYWRlclxuICogd2hpbGUgdGhlIHVzZXIgaXMgaG9sZGluZyB0aGVpciBwb2ludGVyLlxuICovXG5jb25zdCBIRUFERVJfU0NST0xMX0lOVEVSVkFMID0gMTAwO1xuXG4vKiogSXRlbSBpbnNpZGUgYSBwYWdpbmF0ZWQgdGFiIGhlYWRlci4gKi9cbmV4cG9ydCB0eXBlIE1hdFBhZ2luYXRlZFRhYkhlYWRlckl0ZW0gPSBGb2N1c2FibGVPcHRpb24gJiB7ZWxlbWVudFJlZjogRWxlbWVudFJlZn07XG5cbi8qKlxuICogQmFzZSBjbGFzcyBmb3IgYSB0YWIgaGVhZGVyIHRoYXQgc3VwcG9ydGVkIHBhZ2luYXRpb24uXG4gKiBAZG9jcy1wcml2YXRlXG4gKi9cbkBEaXJlY3RpdmUoKVxuZXhwb3J0IGFic3RyYWN0IGNsYXNzIE1hdFBhZ2luYXRlZFRhYkhlYWRlciBpbXBsZW1lbnRzIEFmdGVyQ29udGVudENoZWNrZWQsIEFmdGVyQ29udGVudEluaXQsXG4gIEFmdGVyVmlld0luaXQsIE9uRGVzdHJveSB7XG4gIGFic3RyYWN0IF9pdGVtczogUXVlcnlMaXN0PE1hdFBhZ2luYXRlZFRhYkhlYWRlckl0ZW0+O1xuICBhYnN0cmFjdCBfaW5rQmFyOiB7aGlkZTogKCkgPT4gdm9pZCwgYWxpZ25Ub0VsZW1lbnQ6IChlbGVtZW50OiBIVE1MRWxlbWVudCkgPT4gdm9pZH07XG4gIGFic3RyYWN0IF90YWJMaXN0Q29udGFpbmVyOiBFbGVtZW50UmVmPEhUTUxFbGVtZW50PjtcbiAgYWJzdHJhY3QgX3RhYkxpc3Q6IEVsZW1lbnRSZWY8SFRNTEVsZW1lbnQ+O1xuICBhYnN0cmFjdCBfbmV4dFBhZ2luYXRvcjogRWxlbWVudFJlZjxIVE1MRWxlbWVudD47XG4gIGFic3RyYWN0IF9wcmV2aW91c1BhZ2luYXRvcjogRWxlbWVudFJlZjxIVE1MRWxlbWVudD47XG5cbiAgLyoqIFRoZSBkaXN0YW5jZSBpbiBwaXhlbHMgdGhhdCB0aGUgdGFiIGxhYmVscyBzaG91bGQgYmUgdHJhbnNsYXRlZCB0byB0aGUgbGVmdC4gKi9cbiAgcHJpdmF0ZSBfc2Nyb2xsRGlzdGFuY2UgPSAwO1xuXG4gIC8qKiBXaGV0aGVyIHRoZSBoZWFkZXIgc2hvdWxkIHNjcm9sbCB0byB0aGUgc2VsZWN0ZWQgaW5kZXggYWZ0ZXIgdGhlIHZpZXcgaGFzIGJlZW4gY2hlY2tlZC4gKi9cbiAgcHJpdmF0ZSBfc2VsZWN0ZWRJbmRleENoYW5nZWQgPSBmYWxzZTtcblxuICAvKiogRW1pdHMgd2hlbiB0aGUgY29tcG9uZW50IGlzIGRlc3Ryb3llZC4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IF9kZXN0cm95ZWQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gIC8qKiBXaGV0aGVyIHRoZSBjb250cm9scyBmb3IgcGFnaW5hdGlvbiBzaG91bGQgYmUgZGlzcGxheWVkICovXG4gIF9zaG93UGFnaW5hdGlvbkNvbnRyb2xzID0gZmFsc2U7XG5cbiAgLyoqIFdoZXRoZXIgdGhlIHRhYiBsaXN0IGNhbiBiZSBzY3JvbGxlZCBtb3JlIHRvd2FyZHMgdGhlIGVuZCBvZiB0aGUgdGFiIGxhYmVsIGxpc3QuICovXG4gIF9kaXNhYmxlU2Nyb2xsQWZ0ZXIgPSB0cnVlO1xuXG4gIC8qKiBXaGV0aGVyIHRoZSB0YWIgbGlzdCBjYW4gYmUgc2Nyb2xsZWQgbW9yZSB0b3dhcmRzIHRoZSBiZWdpbm5pbmcgb2YgdGhlIHRhYiBsYWJlbCBsaXN0LiAqL1xuICBfZGlzYWJsZVNjcm9sbEJlZm9yZSA9IHRydWU7XG5cbiAgLyoqXG4gICAqIFRoZSBudW1iZXIgb2YgdGFiIGxhYmVscyB0aGF0IGFyZSBkaXNwbGF5ZWQgb24gdGhlIGhlYWRlci4gV2hlbiB0aGlzIGNoYW5nZXMsIHRoZSBoZWFkZXJcbiAgICogc2hvdWxkIHJlLWV2YWx1YXRlIHRoZSBzY3JvbGwgcG9zaXRpb24uXG4gICAqL1xuICBwcml2YXRlIF90YWJMYWJlbENvdW50OiBudW1iZXI7XG5cbiAgLyoqIFdoZXRoZXIgdGhlIHNjcm9sbCBkaXN0YW5jZSBoYXMgY2hhbmdlZCBhbmQgc2hvdWxkIGJlIGFwcGxpZWQgYWZ0ZXIgdGhlIHZpZXcgaXMgY2hlY2tlZC4gKi9cbiAgcHJpdmF0ZSBfc2Nyb2xsRGlzdGFuY2VDaGFuZ2VkOiBib29sZWFuO1xuXG4gIC8qKiBVc2VkIHRvIG1hbmFnZSBmb2N1cyBiZXR3ZWVuIHRoZSB0YWJzLiAqL1xuICBwcml2YXRlIF9rZXlNYW5hZ2VyOiBGb2N1c0tleU1hbmFnZXI8TWF0UGFnaW5hdGVkVGFiSGVhZGVySXRlbT47XG5cbiAgLyoqIENhY2hlZCB0ZXh0IGNvbnRlbnQgb2YgdGhlIGhlYWRlci4gKi9cbiAgcHJpdmF0ZSBfY3VycmVudFRleHRDb250ZW50OiBzdHJpbmc7XG5cbiAgLyoqIFN0cmVhbSB0aGF0IHdpbGwgc3RvcCB0aGUgYXV0b21hdGVkIHNjcm9sbGluZy4gKi9cbiAgcHJpdmF0ZSBfc3RvcFNjcm9sbGluZyA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgcGFnaW5hdGlvbiBzaG91bGQgYmUgZGlzYWJsZWQuIFRoaXMgY2FuIGJlIHVzZWQgdG8gYXZvaWQgdW5uZWNlc3NhcnlcbiAgICogbGF5b3V0IHJlY2FsY3VsYXRpb25zIGlmIGl0J3Mga25vd24gdGhhdCBwYWdpbmF0aW9uIHdvbid0IGJlIHJlcXVpcmVkLlxuICAgKi9cbiAgQElucHV0KClcbiAgZGlzYWJsZVBhZ2luYXRpb246IGJvb2xlYW4gPSBmYWxzZTtcblxuICAvKiogVGhlIGluZGV4IG9mIHRoZSBhY3RpdmUgdGFiLiAqL1xuICBnZXQgc2VsZWN0ZWRJbmRleCgpOiBudW1iZXIgeyByZXR1cm4gdGhpcy5fc2VsZWN0ZWRJbmRleDsgfVxuICBzZXQgc2VsZWN0ZWRJbmRleCh2YWx1ZTogbnVtYmVyKSB7XG4gICAgdmFsdWUgPSBjb2VyY2VOdW1iZXJQcm9wZXJ0eSh2YWx1ZSk7XG5cbiAgICBpZiAodGhpcy5fc2VsZWN0ZWRJbmRleCAhPSB2YWx1ZSkge1xuICAgICAgdGhpcy5fc2VsZWN0ZWRJbmRleENoYW5nZWQgPSB0cnVlO1xuICAgICAgdGhpcy5fc2VsZWN0ZWRJbmRleCA9IHZhbHVlO1xuXG4gICAgICBpZiAodGhpcy5fa2V5TWFuYWdlcikge1xuICAgICAgICB0aGlzLl9rZXlNYW5hZ2VyLnVwZGF0ZUFjdGl2ZUl0ZW0odmFsdWUpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBwcml2YXRlIF9zZWxlY3RlZEluZGV4OiBudW1iZXIgPSAwO1xuXG4gIC8qKiBFdmVudCBlbWl0dGVkIHdoZW4gdGhlIG9wdGlvbiBpcyBzZWxlY3RlZC4gKi9cbiAgcmVhZG9ubHkgc2VsZWN0Rm9jdXNlZEluZGV4OiBFdmVudEVtaXR0ZXI8bnVtYmVyPiA9IG5ldyBFdmVudEVtaXR0ZXI8bnVtYmVyPigpO1xuXG4gIC8qKiBFdmVudCBlbWl0dGVkIHdoZW4gYSBsYWJlbCBpcyBmb2N1c2VkLiAqL1xuICByZWFkb25seSBpbmRleEZvY3VzZWQ6IEV2ZW50RW1pdHRlcjxudW1iZXI+ID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXI+KCk7XG5cbiAgY29uc3RydWN0b3IocHJvdGVjdGVkIF9lbGVtZW50UmVmOiBFbGVtZW50UmVmPEhUTUxFbGVtZW50PixcbiAgICAgICAgICAgICAgcHJvdGVjdGVkIF9jaGFuZ2VEZXRlY3RvclJlZjogQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gICAgICAgICAgICAgIHByaXZhdGUgX3ZpZXdwb3J0UnVsZXI6IFZpZXdwb3J0UnVsZXIsXG4gICAgICAgICAgICAgIEBPcHRpb25hbCgpIHByaXZhdGUgX2RpcjogRGlyZWN0aW9uYWxpdHksXG4gICAgICAgICAgICAgIHByaXZhdGUgX25nWm9uZTogTmdab25lLFxuICAgICAgICAgICAgICBwcml2YXRlIF9wbGF0Zm9ybTogUGxhdGZvcm0sXG4gICAgICAgICAgICAgIEBPcHRpb25hbCgpIEBJbmplY3QoQU5JTUFUSU9OX01PRFVMRV9UWVBFKSBwdWJsaWMgX2FuaW1hdGlvbk1vZGU/OiBzdHJpbmcpIHtcblxuICAgIC8vIEJpbmQgdGhlIGBtb3VzZWxlYXZlYCBldmVudCBvbiB0aGUgb3V0c2lkZSBzaW5jZSBpdCBkb2Vzbid0IGNoYW5nZSBhbnl0aGluZyBpbiB0aGUgdmlldy5cbiAgICBfbmdab25lLnJ1bk91dHNpZGVBbmd1bGFyKCgpID0+IHtcbiAgICAgIGZyb21FdmVudChfZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50LCAnbW91c2VsZWF2ZScpXG4gICAgICAgIC5waXBlKHRha2VVbnRpbCh0aGlzLl9kZXN0cm95ZWQpKVxuICAgICAgICAuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgICB0aGlzLl9zdG9wSW50ZXJ2YWwoKTtcbiAgICAgICAgfSk7XG4gICAgfSk7XG4gIH1cblxuICAvKiogQ2FsbGVkIHdoZW4gdGhlIHVzZXIgaGFzIHNlbGVjdGVkIGFuIGl0ZW0gdmlhIHRoZSBrZXlib2FyZC4gKi9cbiAgcHJvdGVjdGVkIGFic3RyYWN0IF9pdGVtU2VsZWN0ZWQoZXZlbnQ6IEtleWJvYXJkRXZlbnQpOiB2b2lkO1xuXG4gIG5nQWZ0ZXJWaWV3SW5pdCgpIHtcbiAgICAvLyBXZSBuZWVkIHRvIGhhbmRsZSB0aGVzZSBldmVudHMgbWFudWFsbHksIGJlY2F1c2Ugd2Ugd2FudCB0byBiaW5kIHBhc3NpdmUgZXZlbnQgbGlzdGVuZXJzLlxuICAgIGZyb21FdmVudCh0aGlzLl9wcmV2aW91c1BhZ2luYXRvci5uYXRpdmVFbGVtZW50LCAndG91Y2hzdGFydCcsIHBhc3NpdmVFdmVudExpc3RlbmVyT3B0aW9ucylcbiAgICAgIC5waXBlKHRha2VVbnRpbCh0aGlzLl9kZXN0cm95ZWQpKVxuICAgICAgLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICAgIHRoaXMuX2hhbmRsZVBhZ2luYXRvclByZXNzKCdiZWZvcmUnKTtcbiAgICAgIH0pO1xuXG4gICAgZnJvbUV2ZW50KHRoaXMuX25leHRQYWdpbmF0b3IubmF0aXZlRWxlbWVudCwgJ3RvdWNoc3RhcnQnLCBwYXNzaXZlRXZlbnRMaXN0ZW5lck9wdGlvbnMpXG4gICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5fZGVzdHJveWVkKSlcbiAgICAgIC5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgICB0aGlzLl9oYW5kbGVQYWdpbmF0b3JQcmVzcygnYWZ0ZXInKTtcbiAgICAgIH0pO1xuICB9XG5cbiAgbmdBZnRlckNvbnRlbnRJbml0KCkge1xuICAgIGNvbnN0IGRpckNoYW5nZSA9IHRoaXMuX2RpciA/IHRoaXMuX2Rpci5jaGFuZ2UgOiBvYnNlcnZhYmxlT2YoJ2x0cicpO1xuICAgIGNvbnN0IHJlc2l6ZSA9IHRoaXMuX3ZpZXdwb3J0UnVsZXIuY2hhbmdlKDE1MCk7XG4gICAgY29uc3QgcmVhbGlnbiA9ICgpID0+IHtcbiAgICAgIHRoaXMudXBkYXRlUGFnaW5hdGlvbigpO1xuICAgICAgdGhpcy5fYWxpZ25JbmtCYXJUb1NlbGVjdGVkVGFiKCk7XG4gICAgfTtcblxuICAgIHRoaXMuX2tleU1hbmFnZXIgPSBuZXcgRm9jdXNLZXlNYW5hZ2VyPE1hdFBhZ2luYXRlZFRhYkhlYWRlckl0ZW0+KHRoaXMuX2l0ZW1zKVxuICAgICAgLndpdGhIb3Jpem9udGFsT3JpZW50YXRpb24odGhpcy5fZ2V0TGF5b3V0RGlyZWN0aW9uKCkpXG4gICAgICAud2l0aEhvbWVBbmRFbmQoKVxuICAgICAgLndpdGhXcmFwKCk7XG5cbiAgICB0aGlzLl9rZXlNYW5hZ2VyLnVwZGF0ZUFjdGl2ZUl0ZW0odGhpcy5fc2VsZWN0ZWRJbmRleCk7XG5cbiAgICAvLyBEZWZlciB0aGUgZmlyc3QgY2FsbCBpbiBvcmRlciB0byBhbGxvdyBmb3Igc2xvd2VyIGJyb3dzZXJzIHRvIGxheSBvdXQgdGhlIGVsZW1lbnRzLlxuICAgIC8vIFRoaXMgaGVscHMgaW4gY2FzZXMgd2hlcmUgdGhlIHVzZXIgbGFuZHMgZGlyZWN0bHkgb24gYSBwYWdlIHdpdGggcGFnaW5hdGVkIHRhYnMuXG4gICAgdHlwZW9mIHJlcXVlc3RBbmltYXRpb25GcmFtZSAhPT0gJ3VuZGVmaW5lZCcgPyByZXF1ZXN0QW5pbWF0aW9uRnJhbWUocmVhbGlnbikgOiByZWFsaWduKCk7XG5cbiAgICAvLyBPbiBkaXIgY2hhbmdlIG9yIHdpbmRvdyByZXNpemUsIHJlYWxpZ24gdGhlIGluayBiYXIgYW5kIHVwZGF0ZSB0aGUgb3JpZW50YXRpb24gb2ZcbiAgICAvLyB0aGUga2V5IG1hbmFnZXIgaWYgdGhlIGRpcmVjdGlvbiBoYXMgY2hhbmdlZC5cbiAgICBtZXJnZShkaXJDaGFuZ2UsIHJlc2l6ZSwgdGhpcy5faXRlbXMuY2hhbmdlcykucGlwZSh0YWtlVW50aWwodGhpcy5fZGVzdHJveWVkKSkuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgIC8vIFdlIG5lZWQgdG8gZGVmZXIgdGhpcyB0byBnaXZlIHRoZSBicm93c2VyIHNvbWUgdGltZSB0byByZWNhbGN1bGF0ZVxuICAgICAgLy8gdGhlIGVsZW1lbnQgZGltZW5zaW9ucy4gVGhlIGNhbGwgaGFzIHRvIGJlIHdyYXBwZWQgaW4gYE5nWm9uZS5ydW5gLFxuICAgICAgLy8gYmVjYXVzZSB0aGUgdmlld3BvcnQgY2hhbmdlIGhhbmRsZXIgcnVucyBvdXRzaWRlIG9mIEFuZ3VsYXIuXG4gICAgICB0aGlzLl9uZ1pvbmUucnVuKCgpID0+IFByb21pc2UucmVzb2x2ZSgpLnRoZW4ocmVhbGlnbikpO1xuICAgICAgdGhpcy5fa2V5TWFuYWdlci53aXRoSG9yaXpvbnRhbE9yaWVudGF0aW9uKHRoaXMuX2dldExheW91dERpcmVjdGlvbigpKTtcbiAgICB9KTtcblxuICAgIC8vIElmIHRoZXJlIGlzIGEgY2hhbmdlIGluIHRoZSBmb2N1cyBrZXkgbWFuYWdlciB3ZSBuZWVkIHRvIGVtaXQgdGhlIGBpbmRleEZvY3VzZWRgXG4gICAgLy8gZXZlbnQgaW4gb3JkZXIgdG8gcHJvdmlkZSBhIHB1YmxpYyBldmVudCB0aGF0IG5vdGlmaWVzIGFib3V0IGZvY3VzIGNoYW5nZXMuIEFsc28gd2UgcmVhbGlnblxuICAgIC8vIHRoZSB0YWJzIGNvbnRhaW5lciBieSBzY3JvbGxpbmcgdGhlIG5ldyBmb2N1c2VkIHRhYiBpbnRvIHRoZSB2aXNpYmxlIHNlY3Rpb24uXG4gICAgdGhpcy5fa2V5TWFuYWdlci5jaGFuZ2UucGlwZSh0YWtlVW50aWwodGhpcy5fZGVzdHJveWVkKSkuc3Vic2NyaWJlKG5ld0ZvY3VzSW5kZXggPT4ge1xuICAgICAgdGhpcy5pbmRleEZvY3VzZWQuZW1pdChuZXdGb2N1c0luZGV4KTtcbiAgICAgIHRoaXMuX3NldFRhYkZvY3VzKG5ld0ZvY3VzSW5kZXgpO1xuICAgIH0pO1xuICB9XG5cbiAgbmdBZnRlckNvbnRlbnRDaGVja2VkKCk6IHZvaWQge1xuICAgIC8vIElmIHRoZSBudW1iZXIgb2YgdGFiIGxhYmVscyBoYXZlIGNoYW5nZWQsIGNoZWNrIGlmIHNjcm9sbGluZyBzaG91bGQgYmUgZW5hYmxlZFxuICAgIGlmICh0aGlzLl90YWJMYWJlbENvdW50ICE9IHRoaXMuX2l0ZW1zLmxlbmd0aCkge1xuICAgICAgdGhpcy51cGRhdGVQYWdpbmF0aW9uKCk7XG4gICAgICB0aGlzLl90YWJMYWJlbENvdW50ID0gdGhpcy5faXRlbXMubGVuZ3RoO1xuICAgICAgdGhpcy5fY2hhbmdlRGV0ZWN0b3JSZWYubWFya0ZvckNoZWNrKCk7XG4gICAgfVxuXG4gICAgLy8gSWYgdGhlIHNlbGVjdGVkIGluZGV4IGhhcyBjaGFuZ2VkLCBzY3JvbGwgdG8gdGhlIGxhYmVsIGFuZCBjaGVjayBpZiB0aGUgc2Nyb2xsaW5nIGNvbnRyb2xzXG4gICAgLy8gc2hvdWxkIGJlIGRpc2FibGVkLlxuICAgIGlmICh0aGlzLl9zZWxlY3RlZEluZGV4Q2hhbmdlZCkge1xuICAgICAgdGhpcy5fc2Nyb2xsVG9MYWJlbCh0aGlzLl9zZWxlY3RlZEluZGV4KTtcbiAgICAgIHRoaXMuX2NoZWNrU2Nyb2xsaW5nQ29udHJvbHMoKTtcbiAgICAgIHRoaXMuX2FsaWduSW5rQmFyVG9TZWxlY3RlZFRhYigpO1xuICAgICAgdGhpcy5fc2VsZWN0ZWRJbmRleENoYW5nZWQgPSBmYWxzZTtcbiAgICAgIHRoaXMuX2NoYW5nZURldGVjdG9yUmVmLm1hcmtGb3JDaGVjaygpO1xuICAgIH1cblxuICAgIC8vIElmIHRoZSBzY3JvbGwgZGlzdGFuY2UgaGFzIGJlZW4gY2hhbmdlZCAodGFiIHNlbGVjdGVkLCBmb2N1c2VkLCBzY3JvbGwgY29udHJvbHMgYWN0aXZhdGVkKSxcbiAgICAvLyB0aGVuIHRyYW5zbGF0ZSB0aGUgaGVhZGVyIHRvIHJlZmxlY3QgdGhpcy5cbiAgICBpZiAodGhpcy5fc2Nyb2xsRGlzdGFuY2VDaGFuZ2VkKSB7XG4gICAgICB0aGlzLl91cGRhdGVUYWJTY3JvbGxQb3NpdGlvbigpO1xuICAgICAgdGhpcy5fc2Nyb2xsRGlzdGFuY2VDaGFuZ2VkID0gZmFsc2U7XG4gICAgICB0aGlzLl9jaGFuZ2VEZXRlY3RvclJlZi5tYXJrRm9yQ2hlY2soKTtcbiAgICB9XG4gIH1cblxuICBuZ09uRGVzdHJveSgpIHtcbiAgICB0aGlzLl9kZXN0cm95ZWQubmV4dCgpO1xuICAgIHRoaXMuX2Rlc3Ryb3llZC5jb21wbGV0ZSgpO1xuICAgIHRoaXMuX3N0b3BTY3JvbGxpbmcuY29tcGxldGUoKTtcbiAgfVxuXG4gIC8qKiBIYW5kbGVzIGtleWJvYXJkIGV2ZW50cyBvbiB0aGUgaGVhZGVyLiAqL1xuICBfaGFuZGxlS2V5ZG93bihldmVudDogS2V5Ym9hcmRFdmVudCkge1xuICAgIC8vIFdlIGRvbid0IGhhbmRsZSBhbnkga2V5IGJpbmRpbmdzIHdpdGggYSBtb2RpZmllciBrZXkuXG4gICAgaWYgKGhhc01vZGlmaWVyS2V5KGV2ZW50KSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHN3aXRjaCAoZXZlbnQua2V5Q29kZSkge1xuICAgICAgY2FzZSBFTlRFUjpcbiAgICAgIGNhc2UgU1BBQ0U6XG4gICAgICAgIGlmICh0aGlzLmZvY3VzSW5kZXggIT09IHRoaXMuc2VsZWN0ZWRJbmRleCkge1xuICAgICAgICAgIHRoaXMuc2VsZWN0Rm9jdXNlZEluZGV4LmVtaXQodGhpcy5mb2N1c0luZGV4KTtcbiAgICAgICAgICB0aGlzLl9pdGVtU2VsZWN0ZWQoZXZlbnQpO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgdGhpcy5fa2V5TWFuYWdlci5vbktleWRvd24oZXZlbnQpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDYWxsYmFjayBmb3Igd2hlbiB0aGUgTXV0YXRpb25PYnNlcnZlciBkZXRlY3RzIHRoYXQgdGhlIGNvbnRlbnQgaGFzIGNoYW5nZWQuXG4gICAqL1xuICBfb25Db250ZW50Q2hhbmdlcygpIHtcbiAgICBjb25zdCB0ZXh0Q29udGVudCA9IHRoaXMuX2VsZW1lbnRSZWYubmF0aXZlRWxlbWVudC50ZXh0Q29udGVudDtcblxuICAgIC8vIFdlIG5lZWQgdG8gZGlmZiB0aGUgdGV4dCBjb250ZW50IG9mIHRoZSBoZWFkZXIsIGJlY2F1c2UgdGhlIE11dGF0aW9uT2JzZXJ2ZXIgY2FsbGJhY2tcbiAgICAvLyB3aWxsIGZpcmUgZXZlbiBpZiB0aGUgdGV4dCBjb250ZW50IGRpZG4ndCBjaGFuZ2Ugd2hpY2ggaXMgaW5lZmZpY2llbnQgYW5kIGlzIHByb25lXG4gICAgLy8gdG8gaW5maW5pdGUgbG9vcHMgaWYgYSBwb29ybHkgY29uc3RydWN0ZWQgZXhwcmVzc2lvbiBpcyBwYXNzZWQgaW4gKHNlZSAjMTQyNDkpLlxuICAgIGlmICh0ZXh0Q29udGVudCAhPT0gdGhpcy5fY3VycmVudFRleHRDb250ZW50KSB7XG4gICAgICB0aGlzLl9jdXJyZW50VGV4dENvbnRlbnQgPSB0ZXh0Q29udGVudCB8fCAnJztcblxuICAgICAgLy8gVGhlIGNvbnRlbnQgb2JzZXJ2ZXIgcnVucyBvdXRzaWRlIHRoZSBgTmdab25lYCBieSBkZWZhdWx0LCB3aGljaFxuICAgICAgLy8gbWVhbnMgdGhhdCB3ZSBuZWVkIHRvIGJyaW5nIHRoZSBjYWxsYmFjayBiYWNrIGluIG91cnNlbHZlcy5cbiAgICAgIHRoaXMuX25nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgICB0aGlzLnVwZGF0ZVBhZ2luYXRpb24oKTtcbiAgICAgICAgdGhpcy5fYWxpZ25JbmtCYXJUb1NlbGVjdGVkVGFiKCk7XG4gICAgICAgIHRoaXMuX2NoYW5nZURldGVjdG9yUmVmLm1hcmtGb3JDaGVjaygpO1xuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZXMgdGhlIHZpZXcgd2hldGhlciBwYWdpbmF0aW9uIHNob3VsZCBiZSBlbmFibGVkIG9yIG5vdC5cbiAgICpcbiAgICogV0FSTklORzogQ2FsbGluZyB0aGlzIG1ldGhvZCBjYW4gYmUgdmVyeSBjb3N0bHkgaW4gdGVybXMgb2YgcGVyZm9ybWFuY2UuIEl0IHNob3VsZCBiZSBjYWxsZWRcbiAgICogYXMgaW5mcmVxdWVudGx5IGFzIHBvc3NpYmxlIGZyb20gb3V0c2lkZSBvZiB0aGUgVGFicyBjb21wb25lbnQgYXMgaXQgY2F1c2VzIGEgcmVmbG93IG9mIHRoZVxuICAgKiBwYWdlLlxuICAgKi9cbiAgdXBkYXRlUGFnaW5hdGlvbigpIHtcbiAgICB0aGlzLl9jaGVja1BhZ2luYXRpb25FbmFibGVkKCk7XG4gICAgdGhpcy5fY2hlY2tTY3JvbGxpbmdDb250cm9scygpO1xuICAgIHRoaXMuX3VwZGF0ZVRhYlNjcm9sbFBvc2l0aW9uKCk7XG4gIH1cblxuICAvKiogVHJhY2tzIHdoaWNoIGVsZW1lbnQgaGFzIGZvY3VzOyB1c2VkIGZvciBrZXlib2FyZCBuYXZpZ2F0aW9uICovXG4gIGdldCBmb2N1c0luZGV4KCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuX2tleU1hbmFnZXIgPyB0aGlzLl9rZXlNYW5hZ2VyLmFjdGl2ZUl0ZW1JbmRleCEgOiAwO1xuICB9XG5cbiAgLyoqIFdoZW4gdGhlIGZvY3VzIGluZGV4IGlzIHNldCwgd2UgbXVzdCBtYW51YWxseSBzZW5kIGZvY3VzIHRvIHRoZSBjb3JyZWN0IGxhYmVsICovXG4gIHNldCBmb2N1c0luZGV4KHZhbHVlOiBudW1iZXIpIHtcbiAgICBpZiAoIXRoaXMuX2lzVmFsaWRJbmRleCh2YWx1ZSkgfHwgdGhpcy5mb2N1c0luZGV4ID09PSB2YWx1ZSB8fCAhdGhpcy5fa2V5TWFuYWdlcikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuX2tleU1hbmFnZXIuc2V0QWN0aXZlSXRlbSh2YWx1ZSk7XG4gIH1cblxuICAvKipcbiAgICogRGV0ZXJtaW5lcyBpZiBhbiBpbmRleCBpcyB2YWxpZC4gIElmIHRoZSB0YWJzIGFyZSBub3QgcmVhZHkgeWV0LCB3ZSBhc3N1bWUgdGhhdCB0aGUgdXNlciBpc1xuICAgKiBwcm92aWRpbmcgYSB2YWxpZCBpbmRleCBhbmQgcmV0dXJuIHRydWUuXG4gICAqL1xuICBfaXNWYWxpZEluZGV4KGluZGV4OiBudW1iZXIpOiBib29sZWFuIHtcbiAgICBpZiAoIXRoaXMuX2l0ZW1zKSB7IHJldHVybiB0cnVlOyB9XG5cbiAgICBjb25zdCB0YWIgPSB0aGlzLl9pdGVtcyA/IHRoaXMuX2l0ZW1zLnRvQXJyYXkoKVtpbmRleF0gOiBudWxsO1xuICAgIHJldHVybiAhIXRhYiAmJiAhdGFiLmRpc2FibGVkO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldHMgZm9jdXMgb24gdGhlIEhUTUwgZWxlbWVudCBmb3IgdGhlIGxhYmVsIHdyYXBwZXIgYW5kIHNjcm9sbHMgaXQgaW50byB0aGUgdmlldyBpZlxuICAgKiBzY3JvbGxpbmcgaXMgZW5hYmxlZC5cbiAgICovXG4gIF9zZXRUYWJGb2N1cyh0YWJJbmRleDogbnVtYmVyKSB7XG4gICAgaWYgKHRoaXMuX3Nob3dQYWdpbmF0aW9uQ29udHJvbHMpIHtcbiAgICAgIHRoaXMuX3Njcm9sbFRvTGFiZWwodGFiSW5kZXgpO1xuICAgIH1cblxuICAgIGlmICh0aGlzLl9pdGVtcyAmJiB0aGlzLl9pdGVtcy5sZW5ndGgpIHtcbiAgICAgIHRoaXMuX2l0ZW1zLnRvQXJyYXkoKVt0YWJJbmRleF0uZm9jdXMoKTtcblxuICAgICAgLy8gRG8gbm90IGxldCB0aGUgYnJvd3NlciBtYW5hZ2Ugc2Nyb2xsaW5nIHRvIGZvY3VzIHRoZSBlbGVtZW50LCB0aGlzIHdpbGwgYmUgaGFuZGxlZFxuICAgICAgLy8gYnkgdXNpbmcgdHJhbnNsYXRpb24uIEluIExUUiwgdGhlIHNjcm9sbCBsZWZ0IHNob3VsZCBiZSAwLiBJbiBSVEwsIHRoZSBzY3JvbGwgd2lkdGhcbiAgICAgIC8vIHNob3VsZCBiZSB0aGUgZnVsbCB3aWR0aCBtaW51cyB0aGUgb2Zmc2V0IHdpZHRoLlxuICAgICAgY29uc3QgY29udGFpbmVyRWwgPSB0aGlzLl90YWJMaXN0Q29udGFpbmVyLm5hdGl2ZUVsZW1lbnQ7XG4gICAgICBjb25zdCBkaXIgPSB0aGlzLl9nZXRMYXlvdXREaXJlY3Rpb24oKTtcblxuICAgICAgaWYgKGRpciA9PSAnbHRyJykge1xuICAgICAgICBjb250YWluZXJFbC5zY3JvbGxMZWZ0ID0gMDtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnRhaW5lckVsLnNjcm9sbExlZnQgPSBjb250YWluZXJFbC5zY3JvbGxXaWR0aCAtIGNvbnRhaW5lckVsLm9mZnNldFdpZHRoO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKiBUaGUgbGF5b3V0IGRpcmVjdGlvbiBvZiB0aGUgY29udGFpbmluZyBhcHAuICovXG4gIF9nZXRMYXlvdXREaXJlY3Rpb24oKTogRGlyZWN0aW9uIHtcbiAgICByZXR1cm4gdGhpcy5fZGlyICYmIHRoaXMuX2Rpci52YWx1ZSA9PT0gJ3J0bCcgPyAncnRsJyA6ICdsdHInO1xuICB9XG5cbiAgLyoqIFBlcmZvcm1zIHRoZSBDU1MgdHJhbnNmb3JtYXRpb24gb24gdGhlIHRhYiBsaXN0IHRoYXQgd2lsbCBjYXVzZSB0aGUgbGlzdCB0byBzY3JvbGwuICovXG4gIF91cGRhdGVUYWJTY3JvbGxQb3NpdGlvbigpIHtcbiAgICBpZiAodGhpcy5kaXNhYmxlUGFnaW5hdGlvbikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHNjcm9sbERpc3RhbmNlID0gdGhpcy5zY3JvbGxEaXN0YW5jZTtcbiAgICBjb25zdCB0cmFuc2xhdGVYID0gdGhpcy5fZ2V0TGF5b3V0RGlyZWN0aW9uKCkgPT09ICdsdHInID8gLXNjcm9sbERpc3RhbmNlIDogc2Nyb2xsRGlzdGFuY2U7XG5cbiAgICAvLyBEb24ndCB1c2UgYHRyYW5zbGF0ZTNkYCBoZXJlIGJlY2F1c2Ugd2UgZG9uJ3Qgd2FudCB0byBjcmVhdGUgYSBuZXcgbGF5ZXIuIEEgbmV3IGxheWVyXG4gICAgLy8gc2VlbXMgdG8gY2F1c2UgZmxpY2tlcmluZyBhbmQgb3ZlcmZsb3cgaW4gSW50ZXJuZXQgRXhwbG9yZXIuIEZvciBleGFtcGxlLCB0aGUgaW5rIGJhclxuICAgIC8vIGFuZCByaXBwbGVzIHdpbGwgZXhjZWVkIHRoZSBib3VuZGFyaWVzIG9mIHRoZSB2aXNpYmxlIHRhYiBiYXIuXG4gICAgLy8gU2VlOiBodHRwczovL2dpdGh1Yi5jb20vYW5ndWxhci9jb21wb25lbnRzL2lzc3Vlcy8xMDI3NlxuICAgIC8vIFdlIHJvdW5kIHRoZSBgdHJhbnNmb3JtYCBoZXJlLCBiZWNhdXNlIHRyYW5zZm9ybXMgd2l0aCBzdWItcGl4ZWwgcHJlY2lzaW9uIGNhdXNlIHNvbWVcbiAgICAvLyBicm93c2VycyB0byBibHVyIHRoZSBjb250ZW50IG9mIHRoZSBlbGVtZW50LlxuICAgIHRoaXMuX3RhYkxpc3QubmF0aXZlRWxlbWVudC5zdHlsZS50cmFuc2Zvcm0gPSBgdHJhbnNsYXRlWCgke01hdGgucm91bmQodHJhbnNsYXRlWCl9cHgpYDtcblxuICAgIC8vIFNldHRpbmcgdGhlIGB0cmFuc2Zvcm1gIG9uIElFIHdpbGwgY2hhbmdlIHRoZSBzY3JvbGwgb2Zmc2V0IG9mIHRoZSBwYXJlbnQsIGNhdXNpbmcgdGhlXG4gICAgLy8gcG9zaXRpb24gdG8gYmUgdGhyb3duIG9mZiBpbiBzb21lIGNhc2VzLiBXZSBoYXZlIHRvIHJlc2V0IGl0IG91cnNlbHZlcyB0byBlbnN1cmUgdGhhdFxuICAgIC8vIGl0IGRvZXNuJ3QgZ2V0IHRocm93biBvZmYuIE5vdGUgdGhhdCB3ZSBzY29wZSBpdCBvbmx5IHRvIElFIGFuZCBFZGdlLCBiZWNhdXNlIG1lc3NpbmdcbiAgICAvLyB3aXRoIHRoZSBzY3JvbGwgcG9zaXRpb24gdGhyb3dzIG9mZiBDaHJvbWUgNzErIGluIFJUTCBtb2RlIChzZWUgIzE0Njg5KS5cbiAgICBpZiAodGhpcy5fcGxhdGZvcm0uVFJJREVOVCB8fCB0aGlzLl9wbGF0Zm9ybS5FREdFKSB7XG4gICAgICB0aGlzLl90YWJMaXN0Q29udGFpbmVyLm5hdGl2ZUVsZW1lbnQuc2Nyb2xsTGVmdCA9IDA7XG4gICAgfVxuICB9XG5cbiAgLyoqIFNldHMgdGhlIGRpc3RhbmNlIGluIHBpeGVscyB0aGF0IHRoZSB0YWIgaGVhZGVyIHNob3VsZCBiZSB0cmFuc2Zvcm1lZCBpbiB0aGUgWC1heGlzLiAqL1xuICBnZXQgc2Nyb2xsRGlzdGFuY2UoKTogbnVtYmVyIHsgcmV0dXJuIHRoaXMuX3Njcm9sbERpc3RhbmNlOyB9XG4gIHNldCBzY3JvbGxEaXN0YW5jZSh2YWx1ZTogbnVtYmVyKSB7XG4gICAgdGhpcy5fc2Nyb2xsVG8odmFsdWUpO1xuICB9XG5cbiAgLyoqXG4gICAqIE1vdmVzIHRoZSB0YWIgbGlzdCBpbiB0aGUgJ2JlZm9yZScgb3IgJ2FmdGVyJyBkaXJlY3Rpb24gKHRvd2FyZHMgdGhlIGJlZ2lubmluZyBvZiB0aGUgbGlzdCBvclxuICAgKiB0aGUgZW5kIG9mIHRoZSBsaXN0LCByZXNwZWN0aXZlbHkpLiBUaGUgZGlzdGFuY2UgdG8gc2Nyb2xsIGlzIGNvbXB1dGVkIHRvIGJlIGEgdGhpcmQgb2YgdGhlXG4gICAqIGxlbmd0aCBvZiB0aGUgdGFiIGxpc3QgdmlldyB3aW5kb3cuXG4gICAqXG4gICAqIFRoaXMgaXMgYW4gZXhwZW5zaXZlIGNhbGwgdGhhdCBmb3JjZXMgYSBsYXlvdXQgcmVmbG93IHRvIGNvbXB1dGUgYm94IGFuZCBzY3JvbGwgbWV0cmljcyBhbmRcbiAgICogc2hvdWxkIGJlIGNhbGxlZCBzcGFyaW5nbHkuXG4gICAqL1xuICBfc2Nyb2xsSGVhZGVyKGRpcmVjdGlvbjogU2Nyb2xsRGlyZWN0aW9uKSB7XG4gICAgY29uc3Qgdmlld0xlbmd0aCA9IHRoaXMuX3RhYkxpc3RDb250YWluZXIubmF0aXZlRWxlbWVudC5vZmZzZXRXaWR0aDtcblxuICAgIC8vIE1vdmUgdGhlIHNjcm9sbCBkaXN0YW5jZSBvbmUtdGhpcmQgdGhlIGxlbmd0aCBvZiB0aGUgdGFiIGxpc3QncyB2aWV3cG9ydC5cbiAgICBjb25zdCBzY3JvbGxBbW91bnQgPSAoZGlyZWN0aW9uID09ICdiZWZvcmUnID8gLTEgOiAxKSAqIHZpZXdMZW5ndGggLyAzO1xuXG4gICAgcmV0dXJuIHRoaXMuX3Njcm9sbFRvKHRoaXMuX3Njcm9sbERpc3RhbmNlICsgc2Nyb2xsQW1vdW50KTtcbiAgfVxuXG4gIC8qKiBIYW5kbGVzIGNsaWNrIGV2ZW50cyBvbiB0aGUgcGFnaW5hdGlvbiBhcnJvd3MuICovXG4gIF9oYW5kbGVQYWdpbmF0b3JDbGljayhkaXJlY3Rpb246IFNjcm9sbERpcmVjdGlvbikge1xuICAgIHRoaXMuX3N0b3BJbnRlcnZhbCgpO1xuICAgIHRoaXMuX3Njcm9sbEhlYWRlcihkaXJlY3Rpb24pO1xuICB9XG5cbiAgLyoqXG4gICAqIE1vdmVzIHRoZSB0YWIgbGlzdCBzdWNoIHRoYXQgdGhlIGRlc2lyZWQgdGFiIGxhYmVsIChtYXJrZWQgYnkgaW5kZXgpIGlzIG1vdmVkIGludG8gdmlldy5cbiAgICpcbiAgICogVGhpcyBpcyBhbiBleHBlbnNpdmUgY2FsbCB0aGF0IGZvcmNlcyBhIGxheW91dCByZWZsb3cgdG8gY29tcHV0ZSBib3ggYW5kIHNjcm9sbCBtZXRyaWNzIGFuZFxuICAgKiBzaG91bGQgYmUgY2FsbGVkIHNwYXJpbmdseS5cbiAgICovXG4gIF9zY3JvbGxUb0xhYmVsKGxhYmVsSW5kZXg6IG51bWJlcikge1xuICAgIGlmICh0aGlzLmRpc2FibGVQYWdpbmF0aW9uKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3Qgc2VsZWN0ZWRMYWJlbCA9IHRoaXMuX2l0ZW1zID8gdGhpcy5faXRlbXMudG9BcnJheSgpW2xhYmVsSW5kZXhdIDogbnVsbDtcblxuICAgIGlmICghc2VsZWN0ZWRMYWJlbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIFRoZSB2aWV3IGxlbmd0aCBpcyB0aGUgdmlzaWJsZSB3aWR0aCBvZiB0aGUgdGFiIGxhYmVscy5cbiAgICBjb25zdCB2aWV3TGVuZ3RoID0gdGhpcy5fdGFiTGlzdENvbnRhaW5lci5uYXRpdmVFbGVtZW50Lm9mZnNldFdpZHRoO1xuICAgIGNvbnN0IHtvZmZzZXRMZWZ0LCBvZmZzZXRXaWR0aH0gPSBzZWxlY3RlZExhYmVsLmVsZW1lbnRSZWYubmF0aXZlRWxlbWVudDtcblxuICAgIGxldCBsYWJlbEJlZm9yZVBvczogbnVtYmVyLCBsYWJlbEFmdGVyUG9zOiBudW1iZXI7XG4gICAgaWYgKHRoaXMuX2dldExheW91dERpcmVjdGlvbigpID09ICdsdHInKSB7XG4gICAgICBsYWJlbEJlZm9yZVBvcyA9IG9mZnNldExlZnQ7XG4gICAgICBsYWJlbEFmdGVyUG9zID0gbGFiZWxCZWZvcmVQb3MgKyBvZmZzZXRXaWR0aDtcbiAgICB9IGVsc2Uge1xuICAgICAgbGFiZWxBZnRlclBvcyA9IHRoaXMuX3RhYkxpc3QubmF0aXZlRWxlbWVudC5vZmZzZXRXaWR0aCAtIG9mZnNldExlZnQ7XG4gICAgICBsYWJlbEJlZm9yZVBvcyA9IGxhYmVsQWZ0ZXJQb3MgLSBvZmZzZXRXaWR0aDtcbiAgICB9XG5cbiAgICBjb25zdCBiZWZvcmVWaXNpYmxlUG9zID0gdGhpcy5zY3JvbGxEaXN0YW5jZTtcbiAgICBjb25zdCBhZnRlclZpc2libGVQb3MgPSB0aGlzLnNjcm9sbERpc3RhbmNlICsgdmlld0xlbmd0aDtcblxuICAgIGlmIChsYWJlbEJlZm9yZVBvcyA8IGJlZm9yZVZpc2libGVQb3MpIHtcbiAgICAgIC8vIFNjcm9sbCBoZWFkZXIgdG8gbW92ZSBsYWJlbCB0byB0aGUgYmVmb3JlIGRpcmVjdGlvblxuICAgICAgdGhpcy5zY3JvbGxEaXN0YW5jZSAtPSBiZWZvcmVWaXNpYmxlUG9zIC0gbGFiZWxCZWZvcmVQb3MgKyBFWEFHR0VSQVRFRF9PVkVSU0NST0xMO1xuICAgIH0gZWxzZSBpZiAobGFiZWxBZnRlclBvcyA+IGFmdGVyVmlzaWJsZVBvcykge1xuICAgICAgLy8gU2Nyb2xsIGhlYWRlciB0byBtb3ZlIGxhYmVsIHRvIHRoZSBhZnRlciBkaXJlY3Rpb25cbiAgICAgIHRoaXMuc2Nyb2xsRGlzdGFuY2UgKz0gbGFiZWxBZnRlclBvcyAtIGFmdGVyVmlzaWJsZVBvcyArIEVYQUdHRVJBVEVEX09WRVJTQ1JPTEw7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEV2YWx1YXRlIHdoZXRoZXIgdGhlIHBhZ2luYXRpb24gY29udHJvbHMgc2hvdWxkIGJlIGRpc3BsYXllZC4gSWYgdGhlIHNjcm9sbCB3aWR0aCBvZiB0aGVcbiAgICogdGFiIGxpc3QgaXMgd2lkZXIgdGhhbiB0aGUgc2l6ZSBvZiB0aGUgaGVhZGVyIGNvbnRhaW5lciwgdGhlbiB0aGUgcGFnaW5hdGlvbiBjb250cm9scyBzaG91bGRcbiAgICogYmUgc2hvd24uXG4gICAqXG4gICAqIFRoaXMgaXMgYW4gZXhwZW5zaXZlIGNhbGwgdGhhdCBmb3JjZXMgYSBsYXlvdXQgcmVmbG93IHRvIGNvbXB1dGUgYm94IGFuZCBzY3JvbGwgbWV0cmljcyBhbmRcbiAgICogc2hvdWxkIGJlIGNhbGxlZCBzcGFyaW5nbHkuXG4gICAqL1xuICBfY2hlY2tQYWdpbmF0aW9uRW5hYmxlZCgpIHtcbiAgICBpZiAodGhpcy5kaXNhYmxlUGFnaW5hdGlvbikge1xuICAgICAgdGhpcy5fc2hvd1BhZ2luYXRpb25Db250cm9scyA9IGZhbHNlO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBpc0VuYWJsZWQgPVxuICAgICAgICAgIHRoaXMuX3RhYkxpc3QubmF0aXZlRWxlbWVudC5zY3JvbGxXaWR0aCA+IHRoaXMuX2VsZW1lbnRSZWYubmF0aXZlRWxlbWVudC5vZmZzZXRXaWR0aDtcblxuICAgICAgaWYgKCFpc0VuYWJsZWQpIHtcbiAgICAgICAgdGhpcy5zY3JvbGxEaXN0YW5jZSA9IDA7XG4gICAgICB9XG5cbiAgICAgIGlmIChpc0VuYWJsZWQgIT09IHRoaXMuX3Nob3dQYWdpbmF0aW9uQ29udHJvbHMpIHtcbiAgICAgICAgdGhpcy5fY2hhbmdlRGV0ZWN0b3JSZWYubWFya0ZvckNoZWNrKCk7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuX3Nob3dQYWdpbmF0aW9uQ29udHJvbHMgPSBpc0VuYWJsZWQ7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEV2YWx1YXRlIHdoZXRoZXIgdGhlIGJlZm9yZSBhbmQgYWZ0ZXIgY29udHJvbHMgc2hvdWxkIGJlIGVuYWJsZWQgb3IgZGlzYWJsZWQuXG4gICAqIElmIHRoZSBoZWFkZXIgaXMgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGUgbGlzdCAoc2Nyb2xsIGRpc3RhbmNlIGlzIGVxdWFsIHRvIDApIHRoZW4gZGlzYWJsZSB0aGVcbiAgICogYmVmb3JlIGJ1dHRvbi4gSWYgdGhlIGhlYWRlciBpcyBhdCB0aGUgZW5kIG9mIHRoZSBsaXN0IChzY3JvbGwgZGlzdGFuY2UgaXMgZXF1YWwgdG8gdGhlXG4gICAqIG1heGltdW0gZGlzdGFuY2Ugd2UgY2FuIHNjcm9sbCksIHRoZW4gZGlzYWJsZSB0aGUgYWZ0ZXIgYnV0dG9uLlxuICAgKlxuICAgKiBUaGlzIGlzIGFuIGV4cGVuc2l2ZSBjYWxsIHRoYXQgZm9yY2VzIGEgbGF5b3V0IHJlZmxvdyB0byBjb21wdXRlIGJveCBhbmQgc2Nyb2xsIG1ldHJpY3MgYW5kXG4gICAqIHNob3VsZCBiZSBjYWxsZWQgc3BhcmluZ2x5LlxuICAgKi9cbiAgX2NoZWNrU2Nyb2xsaW5nQ29udHJvbHMoKSB7XG4gICAgaWYgKHRoaXMuZGlzYWJsZVBhZ2luYXRpb24pIHtcbiAgICAgIHRoaXMuX2Rpc2FibGVTY3JvbGxBZnRlciA9IHRoaXMuX2Rpc2FibGVTY3JvbGxCZWZvcmUgPSB0cnVlO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBDaGVjayBpZiB0aGUgcGFnaW5hdGlvbiBhcnJvd3Mgc2hvdWxkIGJlIGFjdGl2YXRlZC5cbiAgICAgIHRoaXMuX2Rpc2FibGVTY3JvbGxCZWZvcmUgPSB0aGlzLnNjcm9sbERpc3RhbmNlID09IDA7XG4gICAgICB0aGlzLl9kaXNhYmxlU2Nyb2xsQWZ0ZXIgPSB0aGlzLnNjcm9sbERpc3RhbmNlID09IHRoaXMuX2dldE1heFNjcm9sbERpc3RhbmNlKCk7XG4gICAgICB0aGlzLl9jaGFuZ2VEZXRlY3RvclJlZi5tYXJrRm9yQ2hlY2soKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRGV0ZXJtaW5lcyB3aGF0IGlzIHRoZSBtYXhpbXVtIGxlbmd0aCBpbiBwaXhlbHMgdGhhdCBjYW4gYmUgc2V0IGZvciB0aGUgc2Nyb2xsIGRpc3RhbmNlLiBUaGlzXG4gICAqIGlzIGVxdWFsIHRvIHRoZSBkaWZmZXJlbmNlIGluIHdpZHRoIGJldHdlZW4gdGhlIHRhYiBsaXN0IGNvbnRhaW5lciBhbmQgdGFiIGhlYWRlciBjb250YWluZXIuXG4gICAqXG4gICAqIFRoaXMgaXMgYW4gZXhwZW5zaXZlIGNhbGwgdGhhdCBmb3JjZXMgYSBsYXlvdXQgcmVmbG93IHRvIGNvbXB1dGUgYm94IGFuZCBzY3JvbGwgbWV0cmljcyBhbmRcbiAgICogc2hvdWxkIGJlIGNhbGxlZCBzcGFyaW5nbHkuXG4gICAqL1xuICBfZ2V0TWF4U2Nyb2xsRGlzdGFuY2UoKTogbnVtYmVyIHtcbiAgICBjb25zdCBsZW5ndGhPZlRhYkxpc3QgPSB0aGlzLl90YWJMaXN0Lm5hdGl2ZUVsZW1lbnQuc2Nyb2xsV2lkdGg7XG4gICAgY29uc3Qgdmlld0xlbmd0aCA9IHRoaXMuX3RhYkxpc3RDb250YWluZXIubmF0aXZlRWxlbWVudC5vZmZzZXRXaWR0aDtcbiAgICByZXR1cm4gKGxlbmd0aE9mVGFiTGlzdCAtIHZpZXdMZW5ndGgpIHx8IDA7XG4gIH1cblxuICAvKiogVGVsbHMgdGhlIGluay1iYXIgdG8gYWxpZ24gaXRzZWxmIHRvIHRoZSBjdXJyZW50IGxhYmVsIHdyYXBwZXIgKi9cbiAgX2FsaWduSW5rQmFyVG9TZWxlY3RlZFRhYigpOiB2b2lkIHtcbiAgICBjb25zdCBzZWxlY3RlZEl0ZW0gPSB0aGlzLl9pdGVtcyAmJiB0aGlzLl9pdGVtcy5sZW5ndGggP1xuICAgICAgICB0aGlzLl9pdGVtcy50b0FycmF5KClbdGhpcy5zZWxlY3RlZEluZGV4XSA6IG51bGw7XG4gICAgY29uc3Qgc2VsZWN0ZWRMYWJlbFdyYXBwZXIgPSBzZWxlY3RlZEl0ZW0gPyBzZWxlY3RlZEl0ZW0uZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50IDogbnVsbDtcblxuICAgIGlmIChzZWxlY3RlZExhYmVsV3JhcHBlcikge1xuICAgICAgdGhpcy5faW5rQmFyLmFsaWduVG9FbGVtZW50KHNlbGVjdGVkTGFiZWxXcmFwcGVyKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5faW5rQmFyLmhpZGUoKTtcbiAgICB9XG4gIH1cblxuICAvKiogU3RvcHMgdGhlIGN1cnJlbnRseS1ydW5uaW5nIHBhZ2luYXRvciBpbnRlcnZhbC4gICovXG4gIF9zdG9wSW50ZXJ2YWwoKSB7XG4gICAgdGhpcy5fc3RvcFNjcm9sbGluZy5uZXh0KCk7XG4gIH1cblxuICAvKipcbiAgICogSGFuZGxlcyB0aGUgdXNlciBwcmVzc2luZyBkb3duIG9uIG9uZSBvZiB0aGUgcGFnaW5hdG9ycy5cbiAgICogU3RhcnRzIHNjcm9sbGluZyB0aGUgaGVhZGVyIGFmdGVyIGEgY2VydGFpbiBhbW91bnQgb2YgdGltZS5cbiAgICogQHBhcmFtIGRpcmVjdGlvbiBJbiB3aGljaCBkaXJlY3Rpb24gdGhlIHBhZ2luYXRvciBzaG91bGQgYmUgc2Nyb2xsZWQuXG4gICAqL1xuICBfaGFuZGxlUGFnaW5hdG9yUHJlc3MoZGlyZWN0aW9uOiBTY3JvbGxEaXJlY3Rpb24sIG1vdXNlRXZlbnQ/OiBNb3VzZUV2ZW50KSB7XG4gICAgLy8gRG9uJ3Qgc3RhcnQgYXV0byBzY3JvbGxpbmcgZm9yIHJpZ2h0IG1vdXNlIGJ1dHRvbiBjbGlja3MuIE5vdGUgdGhhdCB3ZSBzaG91bGRuJ3QgaGF2ZSB0b1xuICAgIC8vIG51bGwgY2hlY2sgdGhlIGBidXR0b25gLCBidXQgd2UgZG8gaXQgc28gd2UgZG9uJ3QgYnJlYWsgdGVzdHMgdGhhdCB1c2UgZmFrZSBldmVudHMuXG4gICAgaWYgKG1vdXNlRXZlbnQgJiYgbW91c2VFdmVudC5idXR0b24gIT0gbnVsbCAmJiBtb3VzZUV2ZW50LmJ1dHRvbiAhPT0gMCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIEF2b2lkIG92ZXJsYXBwaW5nIHRpbWVycy5cbiAgICB0aGlzLl9zdG9wSW50ZXJ2YWwoKTtcblxuICAgIC8vIFN0YXJ0IGEgdGltZXIgYWZ0ZXIgdGhlIGRlbGF5IGFuZCBrZWVwIGZpcmluZyBiYXNlZCBvbiB0aGUgaW50ZXJ2YWwuXG4gICAgdGltZXIoSEVBREVSX1NDUk9MTF9ERUxBWSwgSEVBREVSX1NDUk9MTF9JTlRFUlZBTClcbiAgICAgIC8vIEtlZXAgdGhlIHRpbWVyIGdvaW5nIHVudGlsIHNvbWV0aGluZyB0ZWxscyBpdCB0byBzdG9wIG9yIHRoZSBjb21wb25lbnQgaXMgZGVzdHJveWVkLlxuICAgICAgLnBpcGUodGFrZVVudGlsKG1lcmdlKHRoaXMuX3N0b3BTY3JvbGxpbmcsIHRoaXMuX2Rlc3Ryb3llZCkpKVxuICAgICAgLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICAgIGNvbnN0IHttYXhTY3JvbGxEaXN0YW5jZSwgZGlzdGFuY2V9ID0gdGhpcy5fc2Nyb2xsSGVhZGVyKGRpcmVjdGlvbik7XG5cbiAgICAgICAgLy8gU3RvcCB0aGUgdGltZXIgaWYgd2UndmUgcmVhY2hlZCB0aGUgc3RhcnQgb3IgdGhlIGVuZC5cbiAgICAgICAgaWYgKGRpc3RhbmNlID09PSAwIHx8IGRpc3RhbmNlID49IG1heFNjcm9sbERpc3RhbmNlKSB7XG4gICAgICAgICAgdGhpcy5fc3RvcEludGVydmFsKCk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFNjcm9sbHMgdGhlIGhlYWRlciB0byBhIGdpdmVuIHBvc2l0aW9uLlxuICAgKiBAcGFyYW0gcG9zaXRpb24gUG9zaXRpb24gdG8gd2hpY2ggdG8gc2Nyb2xsLlxuICAgKiBAcmV0dXJucyBJbmZvcm1hdGlvbiBvbiB0aGUgY3VycmVudCBzY3JvbGwgZGlzdGFuY2UgYW5kIHRoZSBtYXhpbXVtLlxuICAgKi9cbiAgcHJpdmF0ZSBfc2Nyb2xsVG8ocG9zaXRpb246IG51bWJlcikge1xuICAgIGlmICh0aGlzLmRpc2FibGVQYWdpbmF0aW9uKSB7XG4gICAgICByZXR1cm4ge21heFNjcm9sbERpc3RhbmNlOiAwLCBkaXN0YW5jZTogMH07XG4gICAgfVxuXG4gICAgY29uc3QgbWF4U2Nyb2xsRGlzdGFuY2UgPSB0aGlzLl9nZXRNYXhTY3JvbGxEaXN0YW5jZSgpO1xuICAgIHRoaXMuX3Njcm9sbERpc3RhbmNlID0gTWF0aC5tYXgoMCwgTWF0aC5taW4obWF4U2Nyb2xsRGlzdGFuY2UsIHBvc2l0aW9uKSk7XG5cbiAgICAvLyBNYXJrIHRoYXQgdGhlIHNjcm9sbCBkaXN0YW5jZSBoYXMgY2hhbmdlZCBzbyB0aGF0IGFmdGVyIHRoZSB2aWV3IGlzIGNoZWNrZWQsIHRoZSBDU1NcbiAgICAvLyB0cmFuc2Zvcm1hdGlvbiBjYW4gbW92ZSB0aGUgaGVhZGVyLlxuICAgIHRoaXMuX3Njcm9sbERpc3RhbmNlQ2hhbmdlZCA9IHRydWU7XG4gICAgdGhpcy5fY2hlY2tTY3JvbGxpbmdDb250cm9scygpO1xuXG4gICAgcmV0dXJuIHttYXhTY3JvbGxEaXN0YW5jZSwgZGlzdGFuY2U6IHRoaXMuX3Njcm9sbERpc3RhbmNlfTtcbiAgfVxuXG4gIHN0YXRpYyBuZ0FjY2VwdElucHV0VHlwZV9zZWxlY3RlZEluZGV4OiBOdW1iZXJJbnB1dDtcbn1cbiJdfQ==
|