UNPKG

@angular/material

Version:
786 lines 75.6 kB
/** * @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