@angular/material
Version:
Angular Material
786 lines • 75.6 kB
JavaScript
/**
* @fileoverview added by tsickle
* Generated from: src/material/tabs/paginated-tab-header.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @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 { END, ENTER, HOME, 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
* @type {?}
*/
const passiveEventListenerOptions = (/** @type {?} */ (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.
* @type {?}
*/
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.
* @type {?}
*/
const HEADER_SCROLL_DELAY = 650;
/**
* Interval in milliseconds at which to scroll the header
* while the user is holding their pointer.
* @type {?}
*/
const HEADER_SCROLL_INTERVAL = 100;
/**
* Base class for a tab header that supported pagination.
* \@docs-private
* @abstract
*/
export class MatPaginatedTabHeader {
/**
* @param {?} _elementRef
* @param {?} _changeDetectorRef
* @param {?} _viewportRuler
* @param {?} _dir
* @param {?} _ngZone
* @param {?=} _platform
* @param {?=} _animationMode
*/
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((/**
* @return {?}
*/
() => {
fromEvent(_elementRef.nativeElement, 'mouseleave')
.pipe(takeUntil(this._destroyed))
.subscribe((/**
* @return {?}
*/
() => {
this._stopInterval();
}));
}));
}
/**
* The index of the active tab.
* @return {?}
*/
get selectedIndex() { return this._selectedIndex; }
/**
* @param {?} value
* @return {?}
*/
set selectedIndex(value) {
value = coerceNumberProperty(value);
if (this._selectedIndex != value) {
this._selectedIndexChanged = true;
this._selectedIndex = value;
if (this._keyManager) {
this._keyManager.updateActiveItem(value);
}
}
}
/**
* @return {?}
*/
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((/**
* @return {?}
*/
() => {
this._handlePaginatorPress('before');
}));
fromEvent(this._nextPaginator.nativeElement, 'touchstart', passiveEventListenerOptions)
.pipe(takeUntil(this._destroyed))
.subscribe((/**
* @return {?}
*/
() => {
this._handlePaginatorPress('after');
}));
}
/**
* @return {?}
*/
ngAfterContentInit() {
/** @type {?} */
const dirChange = this._dir ? this._dir.change : observableOf(null);
/** @type {?} */
const resize = this._viewportRuler.change(150);
/** @type {?} */
const realign = (/**
* @return {?}
*/
() => {
this.updatePagination();
this._alignInkBarToSelectedTab();
});
this._keyManager = new FocusKeyManager(this._items)
.withHorizontalOrientation(this._getLayoutDirection())
.withWrap();
this._keyManager.updateActiveItem(0);
// 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((/**
* @return {?}
*/
() => {
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((/**
* @param {?} newFocusIndex
* @return {?}
*/
newFocusIndex => {
this.indexFocused.emit(newFocusIndex);
this._setTabFocus(newFocusIndex);
}));
}
/**
* @return {?}
*/
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();
}
}
/**
* @return {?}
*/
ngOnDestroy() {
this._destroyed.next();
this._destroyed.complete();
this._stopScrolling.complete();
}
/**
* Handles keyboard events on the header.
* @param {?} event
* @return {?}
*/
_handleKeydown(event) {
// We don't handle any key bindings with a modifier key.
if (hasModifierKey(event)) {
return;
}
switch (event.keyCode) {
case HOME:
this._keyManager.setFirstItemActive();
event.preventDefault();
break;
case END:
this._keyManager.setLastItemActive();
event.preventDefault();
break;
case ENTER:
case SPACE:
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.
* @return {?}
*/
_onContentChanges() {
/** @type {?} */
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((/**
* @return {?}
*/
() => {
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.
* @return {?}
*/
updatePagination() {
this._checkPaginationEnabled();
this._checkScrollingControls();
this._updateTabScrollPosition();
}
/**
* Tracks which element has focus; used for keyboard navigation
* @return {?}
*/
get focusIndex() {
return this._keyManager ? (/** @type {?} */ (this._keyManager.activeItemIndex)) : 0;
}
/**
* When the focus index is set, we must manually send focus to the correct label
* @param {?} value
* @return {?}
*/
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.
* @param {?} index
* @return {?}
*/
_isValidIndex(index) {
if (!this._items) {
return true;
}
/** @type {?} */
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.
* @param {?} tabIndex
* @return {?}
*/
_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.
/** @type {?} */
const containerEl = this._tabListContainer.nativeElement;
/** @type {?} */
const dir = this._getLayoutDirection();
if (dir == 'ltr') {
containerEl.scrollLeft = 0;
}
else {
containerEl.scrollLeft = containerEl.scrollWidth - containerEl.offsetWidth;
}
}
}
/**
* The layout direction of the containing app.
* @return {?}
*/
_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.
* @return {?}
*/
_updateTabScrollPosition() {
if (this.disablePagination) {
return;
}
/** @type {?} */
const scrollDistance = this.scrollDistance;
/** @type {?} */
const platform = this._platform;
/** @type {?} */
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).
// @breaking-change 9.0.0 Remove null check for `platform` after it can no longer be undefined.
if (platform && (platform.TRIDENT || platform.EDGE)) {
this._tabListContainer.nativeElement.scrollLeft = 0;
}
}
/**
* Sets the distance in pixels that the tab header should be transformed in the X-axis.
* @return {?}
*/
get scrollDistance() { return this._scrollDistance; }
/**
* @param {?} value
* @return {?}
*/
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.
* @param {?} direction
* @return {?}
*/
_scrollHeader(direction) {
/** @type {?} */
const viewLength = this._tabListContainer.nativeElement.offsetWidth;
// Move the scroll distance one-third the length of the tab list's viewport.
/** @type {?} */
const scrollAmount = (direction == 'before' ? -1 : 1) * viewLength / 3;
return this._scrollTo(this._scrollDistance + scrollAmount);
}
/**
* Handles click events on the pagination arrows.
* @param {?} direction
* @return {?}
*/
_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.
* @param {?} labelIndex
* @return {?}
*/
_scrollToLabel(labelIndex) {
if (this.disablePagination) {
return;
}
/** @type {?} */
const selectedLabel = this._items ? this._items.toArray()[labelIndex] : null;
if (!selectedLabel) {
return;
}
// The view length is the visible width of the tab labels.
/** @type {?} */
const viewLength = this._tabListContainer.nativeElement.offsetWidth;
const { offsetLeft, offsetWidth } = selectedLabel.elementRef.nativeElement;
/** @type {?} */
let labelBeforePos;
/** @type {?} */
let labelAfterPos;
if (this._getLayoutDirection() == 'ltr') {
labelBeforePos = offsetLeft;
labelAfterPos = labelBeforePos + offsetWidth;
}
else {
labelAfterPos = this._tabList.nativeElement.offsetWidth - offsetLeft;
labelBeforePos = labelAfterPos - offsetWidth;
}
/** @type {?} */
const beforeVisiblePos = this.scrollDistance;
/** @type {?} */
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.
* @return {?}
*/
_checkPaginationEnabled() {
if (this.disablePagination) {
this._showPaginationControls = false;
}
else {
/** @type {?} */
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.
* @return {?}
*/
_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.
* @return {?}
*/
_getMaxScrollDistance() {
/** @type {?} */
const lengthOfTabList = this._tabList.nativeElement.scrollWidth;
/** @type {?} */
const viewLength = this._tabListContainer.nativeElement.offsetWidth;
return (lengthOfTabList - viewLength) || 0;
}
/**
* Tells the ink-bar to align itself to the current label wrapper
* @return {?}
*/
_alignInkBarToSelectedTab() {
/** @type {?} */
const selectedItem = this._items && this._items.length ?
this._items.toArray()[this.selectedIndex] : null;
/** @type {?} */
const selectedLabelWrapper = selectedItem ? selectedItem.elementRef.nativeElement : null;
if (selectedLabelWrapper) {
this._inkBar.alignToElement(selectedLabelWrapper);
}
else {
this._inkBar.hide();
}
}
/**
* Stops the currently-running paginator interval.
* @return {?}
*/
_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.
* @param {?=} mouseEvent
* @return {?}
*/
_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((/**
* @return {?}
*/
() => {
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.
* @private
* @param {?} position Position to which to scroll.
* @return {?} Information on the current scroll distance and the maximum.
*/
_scrollTo(position) {
if (this.disablePagination) {
return { maxScrollDistance: 0, distance: 0 };
}
/** @type {?} */
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.decorators = [
{ type: Directive }
];
/** @nocollapse */
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 }]
};
if (false) {
/** @type {?} */
MatPaginatedTabHeader.ngAcceptInputType_selectedIndex;
/** @type {?} */
MatPaginatedTabHeader.prototype._items;
/** @type {?} */
MatPaginatedTabHeader.prototype._inkBar;
/** @type {?} */
MatPaginatedTabHeader.prototype._tabListContainer;
/** @type {?} */
MatPaginatedTabHeader.prototype._tabList;
/** @type {?} */
MatPaginatedTabHeader.prototype._nextPaginator;
/** @type {?} */
MatPaginatedTabHeader.prototype._previousPaginator;
/**
* The distance in pixels that the tab labels should be translated to the left.
* @type {?}
* @private
*/
MatPaginatedTabHeader.prototype._scrollDistance;
/**
* Whether the header should scroll to the selected index after the view has been checked.
* @type {?}
* @private
*/
MatPaginatedTabHeader.prototype._selectedIndexChanged;
/**
* Emits when the component is destroyed.
* @type {?}
* @protected
*/
MatPaginatedTabHeader.prototype._destroyed;
/**
* Whether the controls for pagination should be displayed
* @type {?}
*/
MatPaginatedTabHeader.prototype._showPaginationControls;
/**
* Whether the tab list can be scrolled more towards the end of the tab label list.
* @type {?}
*/
MatPaginatedTabHeader.prototype._disableScrollAfter;
/**
* Whether the tab list can be scrolled more towards the beginning of the tab label list.
* @type {?}
*/
MatPaginatedTabHeader.prototype._disableScrollBefore;
/**
* The number of tab labels that are displayed on the header. When this changes, the header
* should re-evaluate the scroll position.
* @type {?}
* @private
*/
MatPaginatedTabHeader.prototype._tabLabelCount;
/**
* Whether the scroll distance has changed and should be applied after the view is checked.
* @type {?}
* @private
*/
MatPaginatedTabHeader.prototype._scrollDistanceChanged;
/**
* Used to manage focus between the tabs.
* @type {?}
* @private
*/
MatPaginatedTabHeader.prototype._keyManager;
/**
* Cached text content of the header.
* @type {?}
* @private
*/
MatPaginatedTabHeader.prototype._currentTextContent;
/**
* Stream that will stop the automated scrolling.
* @type {?}
* @private
*/
MatPaginatedTabHeader.prototype._stopScrolling;
/**
* Whether pagination should be disabled. This can be used to avoid unnecessary
* layout recalculations if it's known that pagination won't be required.
* @type {?}
*/
MatPaginatedTabHeader.prototype.disablePagination;
/**
* @type {?}
* @private
*/
MatPaginatedTabHeader.prototype._selectedIndex;
/**
* Event emitted when the option is selected.
* @type {?}
*/
MatPaginatedTabHeader.prototype.selectFocusedIndex;
/**
* Event emitted when a label is focused.
* @type {?}
*/
MatPaginatedTabHeader.prototype.indexFocused;
/**
* @type {?}
* @protected
*/
MatPaginatedTabHeader.prototype._elementRef;
/**
* @type {?}
* @protected
*/
MatPaginatedTabHeader.prototype._changeDetectorRef;
/**
* @type {?}
* @private
*/
MatPaginatedTabHeader.prototype._viewportRuler;
/**
* @type {?}
* @private
*/
MatPaginatedTabHeader.prototype._dir;
/**
* @type {?}
* @private
*/
MatPaginatedTabHeader.prototype._ngZone;
/**
* @deprecated \@breaking-change 9.0.0 `_platform` and `_animationMode`
* parameters to become required.
* @type {?}
* @private
*/
MatPaginatedTabHeader.prototype._platform;
/** @type {?} */
MatPaginatedTabHeader.prototype._animationMode;
/**
* Called when the user has selected an item via the keyboard.
* @abstract
* @protected
* @param {?} event
* @return {?}
*/
MatPaginatedTabHeader.prototype._itemSelected = function (event) { };
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFnaW5hdGVkLXRhYi1oZWFkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbWF0ZXJpYWwvdGFicy9wYWdpbmF0ZWQtdGFiLWhlYWRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7QUFRQSxPQUFPLEVBQ0wsaUJBQWlCLEVBQ2pCLFVBQVUsRUFDVixNQUFNLEVBQ04sUUFBUSxFQUVSLFlBQVksRUFLWixTQUFTLEVBQ1QsTUFBTSxFQUNOLEtBQUssR0FDTixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQVksY0FBYyxFQUFDLE1BQU0sbUJBQW1CLENBQUM7QUFDNUQsT0FBTyxFQUFDLG9CQUFvQixFQUFjLE1BQU0sdUJBQXVCLENBQUM7QUFDeEUsT0FBTyxFQUFDLGFBQWEsRUFBQyxNQUFNLHdCQUF3QixDQUFDO0FBQ3JELE9BQU8sRUFBQyxlQUFlLEVBQWtCLE1BQU0sbUJBQW1CLENBQUM7QUFDbkUsT0FBTyxFQUFDLEdBQUcsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxjQUFjLEVBQUMsTUFBTSx1QkFBdUIsQ0FBQztBQUM5RSxPQUFPLEVBQUMsS0FBSyxFQUFFLEVBQUUsSUFBSSxZQUFZLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUMsTUFBTSxNQUFNLENBQUM7QUFDMUUsT0FBTyxFQUFDLFNBQVMsRUFBQyxNQUFNLGdCQUFnQixDQUFDO0FBQ3pDLE9BQU8sRUFBQyxRQUFRLEVBQUUsK0JBQStCLEVBQUMsTUFBTSx1QkFBdUIsQ0FBQztBQUNoRixPQUFPLEVBQUMscUJBQXFCLEVBQUMsTUFBTSxzQ0FBc0MsQ0FBQzs7Ozs7TUFJckUsMkJBQTJCLEdBQzdCLG1CQUFBLCtCQUErQixDQUFDLEVBQUMsT0FBTyxFQUFFLElBQUksRUFBQyxDQUFDLEVBQXdCOzs7Ozs7TUFhdEUsc0JBQXNCLEdBQUcsRUFBRTs7Ozs7O01BTTNCLG1CQUFtQixH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K0NBQWlEOztJQUNqRCxtREFBcUQ7Ozs7OztJQUdyRCxnREFBNEI7Ozs7OztJQUc1QixzREFBc0M7Ozs7OztJQUd0QywyQ0FBb0Q7Ozs7O0lBR3BELHdEQUFnQzs7Ozs7SUFHaEMsb0RBQTJCOzs7OztJQUczQixxREFBNEI7Ozs7Ozs7SUFNNUIsK0NBQStCOzs7Ozs7SUFHL0IsdURBQXdDOzs7Ozs7SUFHeEMsNENBQWdFOzs7Ozs7SUFHaEUsb0RBQW9DOzs7Ozs7SUFHcEMsK0NBQTZDOzs7Ozs7SUFNN0Msa0RBQ21DOzs7OztJQWdCbkMsK0NBQW1DOzs7OztJQUduQyxtREFBK0U7Ozs7O0lBRy9FLDZDQUF5RTs7Ozs7SUFFN0QsNENBQThDOzs7OztJQUM5QyxtREFBK0M7Ozs7O0lBQy9DLCtDQUFxQzs7Ozs7SUFDckMscUNBQXdDOzs7OztJQUN4Qyx3Q0FBdUI7Ozs7Ozs7SUFLdkIsMENBQTRCOztJQUM1QiwrQ0FBeUU7Ozs7Ozs7O0lBYXJGLHFFQUE2RCIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCBHb29nbGUgTExDIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYW4gTUlULXN0eWxlIGxpY2Vuc2UgdGhhdCBjYW4gYmVcbiAqIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgYXQgaHR0cHM6Ly9hbmd1bGFyLmlvL2xpY2Vuc2VcbiAqL1xuXG5pbXBvcnQge1xuICBDaGFuZ2VEZXRlY3RvclJlZixcbiAgRWxlbWVudFJlZixcbiAgTmdab25lLFxuICBPcHRpb25hbCxcbiAgUXVlcnlMaXN0LFxuICBFdmVudEVtaXR0ZXIsXG4gIEFmdGVyQ29udGVudENoZWNrZWQsXG4gIEFmdGVyQ29udGVudEluaXQsXG4gIEFmdGVyVmlld0luaXQsXG4gIE9uRGVzdHJveSxcbiAgRGlyZWN0aXZlLFxuICBJbmplY3QsXG4gIElucHV0LFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7RGlyZWN0aW9uLCBEaXJlY3Rpb25hbGl0eX0gZnJvbSAnQGFuZ3VsYXIvY2RrL2JpZGknO1xuaW1wb3J0IHtjb2VyY2VOdW1iZXJQcm9wZXJ0eSwgTnVtYmVySW5wdXR9IGZyb20gJ0Bhbmd1bGFyL2Nkay9jb2VyY2lvbic7XG5pbXBvcnQge1ZpZXdwb3J0UnVsZXJ9IGZyb20gJ0Bhbmd1bGFyL2Nkay9zY3JvbGxpbmcnO1xuaW1wb3J0IHtGb2N1c0tleU1hbmFnZXIsIEZvY3VzYWJsZU9wdGlvbn0gZnJvbSAnQGFuZ3VsYXIvY2RrL2ExMXknO1xuaW1wb3J0IHtFTkQsIEVOVEVSLCBIT01FLCBTUEFDRSwgaGFzTW9kaWZpZXJLZXl9IGZyb20gJ0Bhbmd1bGFyL2Nkay9rZXljb2Rlcyc7XG5pbXBvcnQge21lcmdlLCBvZiBhcyBvYnNlcnZhYmxlT2YsIFN1YmplY3QsIHRpbWVyLCBmcm9tRXZlbnR9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHt0YWtlVW50aWx9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7UGxhdGZvcm0sIG5vcm1hbGl6ZVBhc3NpdmVMaXN0ZW5lck9wdGlvbnN9IGZyb20gJ0Bhbmd1bGFyL2Nkay9wbGF0Zm9ybSc7XG5pbXBvcnQge0FOSU1BVElPTl9NT0RVTEVfVFlQRX0gZnJvbSAnQGFuZ3VsYXIvcGxhdGZvcm0tYnJvd3Nlci9hbmltYXRpb25zJztcblxuXG4vKiogQ29uZmlnIHVzZWQgdG8gYmluZCBwYXNzaXZlIGV2ZW50IGxpc3RlbmVycyAqL1xuY29uc3QgcGFzc2l2ZUV2ZW50TGlzdGVuZXJPcHRpb25zID1cbiAgICBub3JtYWxpemVQYXNzaXZlTGlzdGVuZXJPcHRpb25zKHtwYXNzaXZlOiB0cnVlfSkgYXMgRXZlbnRMaXN0ZW5lck9wdGlvbnM7XG5cbi8qKlxuICogVGhlIGRpcmVjdGlvbnMgdGhhdCBzY3JvbGxpbmcgY2FuIGdvIGluIHdoZW4gdGhlIGhlYWRlcidzIHRhYnMgZXhjZWVkIHRoZSBoZWFkZXIgd2lkdGguICdBZnRlcidcbiAqIHdpbGwgc2Nyb2xsIHRoZSBoZWFkZXIgdG93YXJkcyB0aGUgZW5kIG9mIHRoZSB0YWJzIGxpc3QgYW5kICdiZWZvcmUnIHdpbGwgc2Nyb2xsIHRvd2FyZHMgdGhlXG4gKiBiZWdpbm5pbmcgb2YgdGhlIGxpc3QuXG4gKi9cbmV4cG9ydCB0eXBlIFNjcm9sbERpcmVjdGlvbiA9ICdhZnRlcicgfCAnYmVmb3JlJztcblxuLyoqXG4gKiBUaGUgZGlzdGFuY2UgaW4gcGl4ZWxzIHRoYXQgd2lsbCBiZSBvdmVyc2hvdCB3aGVuIHNjcm9sbGluZyBhIHRhYiBsYWJlbCBpbnRvIHZpZXcuIFRoaXMgaGVscHNcbiAqIHByb3ZpZGUgYSBzbWFsbCBhZmZvcmRhbmNlIHRvIHRoZSBsYWJlbCBuZXh0IHRvIGl0LlxuICovXG5jb25zdCBFWEFHR0VSQVRFRF9PVkVSU0NST0xMID0gNjA7XG5cbi8qKlxuICogQW1vdW50IG9mIG1pbGxpc2Vjb25kcyB0byB3YWl0IGJlZm9yZSBzdGFydGluZyB0byBzY3JvbGwgdGhlIGhlYWRlciBhdXRvbWF0aWNhbGx5LlxuICogU2V0IGEgbGl0dGxlIGNvbnNlcnZhdGl2ZWx5IGluIG9yZGVyIHRvIGhhbmRsZSBmYWtlIGV2ZW50cyBkaXNwYXRjaGVkIG9uIHRvdWNoIGRldmljZXMuXG4gKi9cbmNvbnN0IEhFQURFUl9TQ1JPTExfREVMQVkgPSA2NTA7XG5cbi8qKlxuICogSW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGF0IHdoaWNoIHRvIHNjcm9sbCB0aGUgaGVhZGVyXG4gKiB3aGlsZSB0aGUgdXNlciBpcyBob2xkaW5nIHRoZWlyIHBvaW50ZXIuXG4gKi9cbmNvbnN0IEhFQURFUl9TQ1JPTExfSU5URVJWQUwgPSAxMDA7XG5cbi8qKiBJdGVtIGluc2lkZSBhIHBhZ2luYXRlZCB0YWIgaGVhZGVyLiAqL1xuZXhwb3J0IHR5cGUgTWF0UGFnaW5hdGVkVGFiSGVhZGVySXRlbSA9IEZvY3VzYWJsZU9wdGlvbiAmIHtlbGVtZW50UmVmOiBFbGVtZW50UmVmfTtcblxuLyoqXG4gKiBCYXNlIGNsYXNzIGZvciBhIHRhYiBoZWFkZXIgdGhhdCBzdXBwb3J0ZWQgcGFnaW5hdGlvbi5cbiAqIEBkb2NzLXByaXZhdGVcbiAqL1xuQERpcmVjdGl2ZSgpXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgTWF0UGFnaW5hdGVkVGFiSGVhZGVyIGltcGxlbWVudHMgQWZ0ZXJDb250ZW50Q2hlY2tlZCwgQWZ0ZXJDb250ZW50SW5pdCxcbiAgQWZ0ZXJWaWV3SW5pdCwgT25EZXN0cm95IHtcbiAgYWJzdHJhY3QgX2l0ZW1zOiBRdWVyeUxpc3Q8TWF0UGFnaW5hdGVkVGFiSGVhZGVySXRlbT47XG4gIGFic3RyYWN0IF9pbmtCYXI6IHtoaWRlOiAoKSA9PiB2b2lkLCBhbGlnblRvRWxlbWVudDogKGVsZW1lbnQ6IEhUTUxFbGVtZW50KSA9PiB2b2lkfTtcbiAgYWJzdHJhY3QgX3RhYkxpc3RDb250YWluZXI6IEVsZW1lbnRSZWY8SFRNTEVsZW1lbnQ+O1xuICBhYnN0cmFjdCBfdGFiTGlzdDogRWxlbWVudFJlZjxIVE1MRWxlbWVudD47XG4gIGFic3RyYWN0IF9uZXh0UGFnaW5hdG9yOiBFbGVtZW50UmVmPEhUTUxFbGVtZW50PjtcbiAgYWJzdHJhY3QgX3ByZXZpb3VzUGFnaW5hdG9yOiBFbGVtZW50UmVmPEhUTUxFbGVtZW50PjtcblxuICAvKiogVGhlIGRpc3RhbmNlIGluIHBpeGVscyB0aGF0IHRoZSB0YWIgbGFiZWxzIHNob3VsZCBiZSB0cmFuc2xhdGVkIHRvIHRoZSBsZWZ0LiAqL1xuICBwcml2YXRlIF9zY3JvbGxEaXN0YW5jZSA9IDA7XG5cbiAgLyoqIFdoZXRoZXIgdGhlIGhlYWRlciBzaG91bGQgc2Nyb2xsIHRvIHRoZSBzZWxlY3RlZCBpbmRleCBhZnRlciB0aGUgdmlldyBoYXMgYmVlbiBjaGVja2VkLiAqL1xuICBwcml2YXRlIF9zZWxlY3RlZEluZGV4Q2hhbmdlZCA9IGZhbHNlO1xuXG4gIC8qKiBFbWl0cyB3aGVuIHRoZSBjb21wb25lbnQgaXMgZGVzdHJveWVkLiAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgX2Rlc3Ryb3llZCA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG5cbiAgLyoqIFdoZXRoZXIgdGhlIGNvbnRyb2xzIGZvciBwYWdpbmF0aW9uIHNob3VsZCBiZSBkaXNwbGF5ZWQgKi9cbiAgX3Nob3dQYWdpbmF0aW9uQ29udHJvbHMgPSBmYWxzZTtcblxuICAvKiogV2hldGhlciB0aGUgdGFiIGxpc3QgY2FuIGJlIHNjcm9sbGVkIG1vcmUgdG93YXJkcyB0aGUgZW5kIG9mIHRoZSB0YWIgbGFiZWwgbGlzdC4gKi9cbiAgX2Rpc2FibGVTY3JvbGxBZnRlciA9IHRydWU7XG5cbiAgLyoqIFdoZXRoZXIgdGhlIHRhYiBsaXN0IGNhbiBiZSBzY3JvbGxlZCBtb3JlIHRvd2FyZHMgdGhlIGJlZ2lubmluZyBvZiB0aGUgdGFiIGxhYmVsIGxpc3QuICovXG4gIF9kaXNhYmxlU2Nyb2xsQmVmb3JlID0gdHJ1ZTtcblx