UNPKG

@angular/cdk

Version:

Angular Material Component Development Kit

840 lines 124 kB
/** * @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 { __read } from "tslib"; import { coerceElement } from '@angular/cdk/coercion'; import { _supportsShadowDom } from '@angular/cdk/platform'; import { Subject, Subscription, interval, animationFrameScheduler } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { moveItemInArray } from './drag-utils'; /** * Proximity, as a ratio to width/height, at which a * dragged item will affect the drop container. */ var DROP_PROXIMITY_THRESHOLD = 0.05; /** * Proximity, as a ratio to width/height at which to start auto-scrolling the drop list or the * viewport. The value comes from trying it out manually until it feels right. */ var SCROLL_PROXIMITY_THRESHOLD = 0.05; /** * Number of pixels to scroll for each frame when auto-scrolling an element. * The value comes from trying it out manually until it feels right. */ var AUTO_SCROLL_STEP = 2; /** * Reference to a drop list. Used to manipulate or dispose of the container. */ var DropListRef = /** @class */ (function () { function DropListRef(element, _dragDropRegistry, _document, _ngZone, _viewportRuler) { var _this = this; this._dragDropRegistry = _dragDropRegistry; this._ngZone = _ngZone; this._viewportRuler = _viewportRuler; /** Whether starting a dragging sequence from this container is disabled. */ this.disabled = false; /** Whether sorting items within the list is disabled. */ this.sortingDisabled = false; /** * Whether auto-scrolling the view when the user * moves their pointer close to the edges is disabled. */ this.autoScrollDisabled = false; /** * Function that is used to determine whether an item * is allowed to be moved into a drop container. */ this.enterPredicate = function () { return true; }; /** Emits right before dragging has started. */ this.beforeStarted = new Subject(); /** * Emits when the user has moved a new drag item into this container. */ this.entered = new Subject(); /** * Emits when the user removes an item from the container * by dragging it into another container. */ this.exited = new Subject(); /** Emits when the user drops an item inside the container. */ this.dropped = new Subject(); /** Emits as the user is swapping items while actively dragging. */ this.sorted = new Subject(); /** Whether an item in the list is being dragged. */ this._isDragging = false; /** Cache of the dimensions of all the items inside the container. */ this._itemPositions = []; /** Keeps track of the container's scroll position. */ this._scrollPosition = { top: 0, left: 0 }; /** Keeps track of the scroll position of the viewport. */ this._viewportScrollPosition = { top: 0, left: 0 }; /** * Keeps track of the item that was last swapped with the dragged item, as * well as what direction the pointer was moving in when the swap occured. */ this._previousSwap = { drag: null, delta: 0 }; /** Drop lists that are connected to the current one. */ this._siblings = []; /** Direction in which the list is oriented. */ this._orientation = 'vertical'; /** Connected siblings that currently have a dragged item. */ this._activeSiblings = new Set(); /** Layout direction of the drop list. */ this._direction = 'ltr'; /** Subscription to the window being scrolled. */ this._viewportScrollSubscription = Subscription.EMPTY; /** Vertical direction in which the list is currently scrolling. */ this._verticalScrollDirection = 0 /* NONE */; /** Horizontal direction in which the list is currently scrolling. */ this._horizontalScrollDirection = 0 /* NONE */; /** Used to signal to the current auto-scroll sequence when to stop. */ this._stopScrollTimers = new Subject(); /** Shadow root of the current element. Necessary for `elementFromPoint` to resolve correctly. */ this._cachedShadowRoot = null; /** Handles the container being scrolled. Has to be an arrow function to preserve the context. */ this._handleScroll = function () { if (!_this.isDragging()) { return; } var element = coerceElement(_this.element); _this._updateAfterScroll(_this._scrollPosition, element.scrollTop, element.scrollLeft); }; /** Starts the interval that'll auto-scroll the element. */ this._startScrollInterval = function () { _this._stopScrolling(); interval(0, animationFrameScheduler) .pipe(takeUntil(_this._stopScrollTimers)) .subscribe(function () { var node = _this._scrollNode; if (_this._verticalScrollDirection === 1 /* UP */) { incrementVerticalScroll(node, -AUTO_SCROLL_STEP); } else if (_this._verticalScrollDirection === 2 /* DOWN */) { incrementVerticalScroll(node, AUTO_SCROLL_STEP); } if (_this._horizontalScrollDirection === 1 /* LEFT */) { incrementHorizontalScroll(node, -AUTO_SCROLL_STEP); } else if (_this._horizontalScrollDirection === 2 /* RIGHT */) { incrementHorizontalScroll(node, AUTO_SCROLL_STEP); } }); }; this.element = coerceElement(element); this._document = _document; _dragDropRegistry.registerDropContainer(this); } /** Removes the drop list functionality from the DOM element. */ DropListRef.prototype.dispose = function () { this._stopScrolling(); this._stopScrollTimers.complete(); this._removeListeners(); this.beforeStarted.complete(); this.entered.complete(); this.exited.complete(); this.dropped.complete(); this.sorted.complete(); this._activeSiblings.clear(); this._scrollNode = null; this._dragDropRegistry.removeDropContainer(this); }; /** Whether an item from this list is currently being dragged. */ DropListRef.prototype.isDragging = function () { return this._isDragging; }; /** Starts dragging an item. */ DropListRef.prototype.start = function () { var _this = this; var element = coerceElement(this.element); this.beforeStarted.next(); this._isDragging = true; this._cacheItems(); this._siblings.forEach(function (sibling) { return sibling._startReceiving(_this); }); this._removeListeners(); this._ngZone.runOutsideAngular(function () { return element.addEventListener('scroll', _this._handleScroll); }); this._listenToScrollEvents(); }; /** * Emits an event to indicate that the user moved an item into the container. * @param item Item that was moved into the container. * @param pointerX Position of the item along the X axis. * @param pointerY Position of the item along the Y axis. */ DropListRef.prototype.enter = function (item, pointerX, pointerY) { this.start(); // If sorting is disabled, we want the item to return to its starting // position if the user is returning it to its initial container. var newIndex = this.sortingDisabled ? this._draggables.indexOf(item) : -1; if (newIndex === -1) { // We use the coordinates of where the item entered the drop // zone to figure out at which index it should be inserted. newIndex = this._getItemIndexFromPointerPosition(item, pointerX, pointerY); } var activeDraggables = this._activeDraggables; var currentIndex = activeDraggables.indexOf(item); var placeholder = item.getPlaceholderElement(); var newPositionReference = activeDraggables[newIndex]; // If the item at the new position is the same as the item that is being dragged, // it means that we're trying to restore the item to its initial position. In this // case we should use the next item from the list as the reference. if (newPositionReference === item) { newPositionReference = activeDraggables[newIndex + 1]; } // Since the item may be in the `activeDraggables` already (e.g. if the user dragged it // into another container and back again), we have to ensure that it isn't duplicated. if (currentIndex > -1) { activeDraggables.splice(currentIndex, 1); } // Don't use items that are being dragged as a reference, because // their element has been moved down to the bottom of the body. if (newPositionReference && !this._dragDropRegistry.isDragging(newPositionReference)) { var element = newPositionReference.getRootElement(); element.parentElement.insertBefore(placeholder, element); activeDraggables.splice(newIndex, 0, item); } else { coerceElement(this.element).appendChild(placeholder); activeDraggables.push(item); } // The transform needs to be cleared so it doesn't throw off the measurements. placeholder.style.transform = ''; // Note that the positions were already cached when we called `start` above, // but we need to refresh them since the amount of items has changed. this._cacheItemPositions(); this.entered.next({ item: item, container: this, currentIndex: this.getItemIndex(item) }); }; /** * Removes an item from the container after it was dragged into another container by the user. * @param item Item that was dragged out. */ DropListRef.prototype.exit = function (item) { this._reset(); this.exited.next({ item: item, container: this }); }; /** * Drops an item into this container. * @param item Item being dropped into the container. * @param currentIndex Index at which the item should be inserted. * @param previousContainer Container from which the item got dragged in. * @param isPointerOverContainer Whether the user's pointer was over the * container when the item was dropped. * @param distance Distance the user has dragged since the start of the dragging sequence. */ DropListRef.prototype.drop = function (item, currentIndex, previousContainer, isPointerOverContainer, distance) { this._reset(); this.dropped.next({ item: item, currentIndex: currentIndex, previousIndex: previousContainer.getItemIndex(item), container: this, previousContainer: previousContainer, isPointerOverContainer: isPointerOverContainer, distance: distance }); }; /** * Sets the draggable items that are a part of this list. * @param items Items that are a part of this list. */ DropListRef.prototype.withItems = function (items) { var _this = this; this._draggables = items; items.forEach(function (item) { return item._withDropContainer(_this); }); if (this.isDragging()) { this._cacheItems(); } return this; }; /** Sets the layout direction of the drop list. */ DropListRef.prototype.withDirection = function (direction) { this._direction = direction; return this; }; /** * Sets the containers that are connected to this one. When two or more containers are * connected, the user will be allowed to transfer items between them. * @param connectedTo Other containers that the current containers should be connected to. */ DropListRef.prototype.connectedTo = function (connectedTo) { this._siblings = connectedTo.slice(); return this; }; /** * Sets the orientation of the container. * @param orientation New orientation for the container. */ DropListRef.prototype.withOrientation = function (orientation) { this._orientation = orientation; return this; }; /** * Figures out the index of an item in the container. * @param item Item whose index should be determined. */ DropListRef.prototype.getItemIndex = function (item) { if (!this._isDragging) { return this._draggables.indexOf(item); } // Items are sorted always by top/left in the cache, however they flow differently in RTL. // The rest of the logic still stands no matter what orientation we're in, however // we need to invert the array when determining the index. var items = this._orientation === 'horizontal' && this._direction === 'rtl' ? this._itemPositions.slice().reverse() : this._itemPositions; return findIndex(items, function (currentItem) { return currentItem.drag === item; }); }; /** * Whether the list is able to receive the item that * is currently being dragged inside a connected drop list. */ DropListRef.prototype.isReceiving = function () { return this._activeSiblings.size > 0; }; /** * Sorts an item inside the container based on its position. * @param item Item to be sorted. * @param pointerX Position of the item along the X axis. * @param pointerY Position of the item along the Y axis. * @param pointerDelta Direction in which the pointer is moving along each axis. */ DropListRef.prototype._sortItem = function (item, pointerX, pointerY, pointerDelta) { // Don't sort the item if sorting is disabled or it's out of range. if (this.sortingDisabled || !this._isPointerNearDropContainer(pointerX, pointerY)) { return; } var siblings = this._itemPositions; var newIndex = this._getItemIndexFromPointerPosition(item, pointerX, pointerY, pointerDelta); if (newIndex === -1 && siblings.length > 0) { return; } var isHorizontal = this._orientation === 'horizontal'; var currentIndex = findIndex(siblings, function (currentItem) { return currentItem.drag === item; }); var siblingAtNewPosition = siblings[newIndex]; var currentPosition = siblings[currentIndex].clientRect; var newPosition = siblingAtNewPosition.clientRect; var delta = currentIndex > newIndex ? 1 : -1; this._previousSwap.drag = siblingAtNewPosition.drag; this._previousSwap.delta = isHorizontal ? pointerDelta.x : pointerDelta.y; // How many pixels the item's placeholder should be offset. var itemOffset = this._getItemOffsetPx(currentPosition, newPosition, delta); // How many pixels all the other items should be offset. var siblingOffset = this._getSiblingOffsetPx(currentIndex, siblings, delta); // Save the previous order of the items before moving the item to its new index. // We use this to check whether an item has been moved as a result of the sorting. var oldOrder = siblings.slice(); // Shuffle the array in place. moveItemInArray(siblings, currentIndex, newIndex); this.sorted.next({ previousIndex: currentIndex, currentIndex: newIndex, container: this, item: item }); siblings.forEach(function (sibling, index) { // Don't do anything if the position hasn't changed. if (oldOrder[index] === sibling) { return; } var isDraggedItem = sibling.drag === item; var offset = isDraggedItem ? itemOffset : siblingOffset; var elementToOffset = isDraggedItem ? item.getPlaceholderElement() : sibling.drag.getRootElement(); // Update the offset to reflect the new position. sibling.offset += offset; // Since we're moving the items with a `transform`, we need to adjust their cached // client rects to reflect their new position, as well as swap their positions in the cache. // Note that we shouldn't use `getBoundingClientRect` here to update the cache, because the // elements may be mid-animation which will give us a wrong result. if (isHorizontal) { // Round the transforms since some browsers will // blur the elements, for sub-pixel transforms. elementToOffset.style.transform = "translate3d(" + Math.round(sibling.offset) + "px, 0, 0)"; adjustClientRect(sibling.clientRect, 0, offset); } else { elementToOffset.style.transform = "translate3d(0, " + Math.round(sibling.offset) + "px, 0)"; adjustClientRect(sibling.clientRect, offset, 0); } }); }; /** * Checks whether the user's pointer is close to the edges of either the * viewport or the drop list and starts the auto-scroll sequence. * @param pointerX User's pointer position along the x axis. * @param pointerY User's pointer position along the y axis. */ DropListRef.prototype._startScrollingIfNecessary = function (pointerX, pointerY) { var _a; if (this.autoScrollDisabled) { return; } var scrollNode; var verticalScrollDirection = 0 /* NONE */; var horizontalScrollDirection = 0 /* NONE */; // Check whether we should start scrolling the container. if (this._isPointerNearDropContainer(pointerX, pointerY)) { var element = coerceElement(this.element); _a = __read(getElementScrollDirections(element, this._clientRect, pointerX, pointerY), 2), verticalScrollDirection = _a[0], horizontalScrollDirection = _a[1]; if (verticalScrollDirection || horizontalScrollDirection) { scrollNode = element; } } // Otherwise check if we can start scrolling the viewport. if (!verticalScrollDirection && !horizontalScrollDirection) { var _b = this._viewportRuler.getViewportSize(), width = _b.width, height = _b.height; var clientRect = { width: width, height: height, top: 0, right: width, bottom: height, left: 0 }; verticalScrollDirection = getVerticalScrollDirection(clientRect, pointerY); horizontalScrollDirection = getHorizontalScrollDirection(clientRect, pointerX); scrollNode = window; } if (scrollNode && (verticalScrollDirection !== this._verticalScrollDirection || horizontalScrollDirection !== this._horizontalScrollDirection || scrollNode !== this._scrollNode)) { this._verticalScrollDirection = verticalScrollDirection; this._horizontalScrollDirection = horizontalScrollDirection; this._scrollNode = scrollNode; if ((verticalScrollDirection || horizontalScrollDirection) && scrollNode) { this._ngZone.runOutsideAngular(this._startScrollInterval); } else { this._stopScrolling(); } } }; /** Stops any currently-running auto-scroll sequences. */ DropListRef.prototype._stopScrolling = function () { this._stopScrollTimers.next(); }; /** Caches the position of the drop list. */ DropListRef.prototype._cacheOwnPosition = function () { var element = coerceElement(this.element); this._clientRect = getMutableClientRect(element); this._scrollPosition = { top: element.scrollTop, left: element.scrollLeft }; }; /** Refreshes the position cache of the items and sibling containers. */ DropListRef.prototype._cacheItemPositions = function () { var _this = this; var isHorizontal = this._orientation === 'horizontal'; this._itemPositions = this._activeDraggables.map(function (drag) { var elementToMeasure = _this._dragDropRegistry.isDragging(drag) ? // If the element is being dragged, we have to measure the // placeholder, because the element is hidden. drag.getPlaceholderElement() : drag.getRootElement(); return { drag: drag, offset: 0, clientRect: getMutableClientRect(elementToMeasure) }; }).sort(function (a, b) { return isHorizontal ? a.clientRect.left - b.clientRect.left : a.clientRect.top - b.clientRect.top; }); }; /** Resets the container to its initial state. */ DropListRef.prototype._reset = function () { var _this = this; this._isDragging = false; // TODO(crisbeto): may have to wait for the animations to finish. this._activeDraggables.forEach(function (item) { return item.getRootElement().style.transform = ''; }); this._siblings.forEach(function (sibling) { return sibling._stopReceiving(_this); }); this._activeDraggables = []; this._itemPositions = []; this._previousSwap.drag = null; this._previousSwap.delta = 0; this._stopScrolling(); this._removeListeners(); }; /** * Gets the offset in pixels by which the items that aren't being dragged should be moved. * @param currentIndex Index of the item currently being dragged. * @param siblings All of the items in the list. * @param delta Direction in which the user is moving. */ DropListRef.prototype._getSiblingOffsetPx = function (currentIndex, siblings, delta) { var isHorizontal = this._orientation === 'horizontal'; var currentPosition = siblings[currentIndex].clientRect; var immediateSibling = siblings[currentIndex + delta * -1]; var siblingOffset = currentPosition[isHorizontal ? 'width' : 'height'] * delta; if (immediateSibling) { var start = isHorizontal ? 'left' : 'top'; var end = isHorizontal ? 'right' : 'bottom'; // Get the spacing between the start of the current item and the end of the one immediately // after it in the direction in which the user is dragging, or vice versa. We add it to the // offset in order to push the element to where it will be when it's inline and is influenced // by the `margin` of its siblings. if (delta === -1) { siblingOffset -= immediateSibling.clientRect[start] - currentPosition[end]; } else { siblingOffset += currentPosition[start] - immediateSibling.clientRect[end]; } } return siblingOffset; }; /** * Checks whether the pointer coordinates are close to the drop container. * @param pointerX Coordinates along the X axis. * @param pointerY Coordinates along the Y axis. */ DropListRef.prototype._isPointerNearDropContainer = function (pointerX, pointerY) { var _a = this._clientRect, top = _a.top, right = _a.right, bottom = _a.bottom, left = _a.left, width = _a.width, height = _a.height; var xThreshold = width * DROP_PROXIMITY_THRESHOLD; var yThreshold = height * DROP_PROXIMITY_THRESHOLD; return pointerY > top - yThreshold && pointerY < bottom + yThreshold && pointerX > left - xThreshold && pointerX < right + xThreshold; }; /** * Gets the offset in pixels by which the item that is being dragged should be moved. * @param currentPosition Current position of the item. * @param newPosition Position of the item where the current item should be moved. * @param delta Direction in which the user is moving. */ DropListRef.prototype._getItemOffsetPx = function (currentPosition, newPosition, delta) { var isHorizontal = this._orientation === 'horizontal'; var itemOffset = isHorizontal ? newPosition.left - currentPosition.left : newPosition.top - currentPosition.top; // Account for differences in the item width/height. if (delta === -1) { itemOffset += isHorizontal ? newPosition.width - currentPosition.width : newPosition.height - currentPosition.height; } return itemOffset; }; /** * Gets the index of an item in the drop container, based on the position of the user's pointer. * @param item Item that is being sorted. * @param pointerX Position of the user's pointer along the X axis. * @param pointerY Position of the user's pointer along the Y axis. * @param delta Direction in which the user is moving their pointer. */ DropListRef.prototype._getItemIndexFromPointerPosition = function (item, pointerX, pointerY, delta) { var _this = this; var isHorizontal = this._orientation === 'horizontal'; return findIndex(this._itemPositions, function (_a, _, array) { var drag = _a.drag, clientRect = _a.clientRect; if (drag === item) { // If there's only one item left in the container, it must be // the dragged item itself so we use it as a reference. return array.length < 2; } if (delta) { var direction = isHorizontal ? delta.x : delta.y; // If the user is still hovering over the same item as last time, and they didn't change // the direction in which they're dragging, we don't consider it a direction swap. if (drag === _this._previousSwap.drag && direction === _this._previousSwap.delta) { return false; } } return isHorizontal ? // Round these down since most browsers report client rects with // sub-pixel precision, whereas the pointer coordinates are rounded to pixels. pointerX >= Math.floor(clientRect.left) && pointerX <= Math.floor(clientRect.right) : pointerY >= Math.floor(clientRect.top) && pointerY <= Math.floor(clientRect.bottom); }); }; /** Caches the current items in the list and their positions. */ DropListRef.prototype._cacheItems = function () { this._activeDraggables = this._draggables.slice(); this._cacheItemPositions(); this._cacheOwnPosition(); }; /** * Updates the internal state of the container after a scroll event has happened. * @param scrollPosition Object that is keeping track of the scroll position. * @param newTop New top scroll position. * @param newLeft New left scroll position. * @param extraClientRect Extra `ClientRect` object that should be updated, in addition to the * ones of the drag items. Useful when the viewport has been scrolled and we also need to update * the `ClientRect` of the list. */ DropListRef.prototype._updateAfterScroll = function (scrollPosition, newTop, newLeft, extraClientRect) { var _this = this; var topDifference = scrollPosition.top - newTop; var leftDifference = scrollPosition.left - newLeft; if (extraClientRect) { adjustClientRect(extraClientRect, topDifference, leftDifference); } // Since we know the amount that the user has scrolled we can shift all of the client rectangles // ourselves. This is cheaper than re-measuring everything and we can avoid inconsistent // behavior where we might be measuring the element before its position has changed. this._itemPositions.forEach(function (_a) { var clientRect = _a.clientRect; adjustClientRect(clientRect, topDifference, leftDifference); }); // We need two loops for this, because we want all of the cached // positions to be up-to-date before we re-sort the item. this._itemPositions.forEach(function (_a) { var drag = _a.drag; if (_this._dragDropRegistry.isDragging(drag)) { // We need to re-sort the item manually, because the pointer move // events won't be dispatched while the user is scrolling. drag._sortFromLastPointerPosition(); } }); scrollPosition.top = newTop; scrollPosition.left = newLeft; }; /** Removes the event listeners associated with this drop list. */ DropListRef.prototype._removeListeners = function () { coerceElement(this.element).removeEventListener('scroll', this._handleScroll); this._viewportScrollSubscription.unsubscribe(); }; /** * Checks whether the user's pointer is positioned over the container. * @param x Pointer position along the X axis. * @param y Pointer position along the Y axis. */ DropListRef.prototype._isOverContainer = function (x, y) { return isInsideClientRect(this._clientRect, x, y); }; /** * Figures out whether an item should be moved into a sibling * drop container, based on its current position. * @param item Drag item that is being moved. * @param x Position of the item along the X axis. * @param y Position of the item along the Y axis. */ DropListRef.prototype._getSiblingContainerFromPosition = function (item, x, y) { return this._siblings.find(function (sibling) { return sibling._canReceive(item, x, y); }); }; /** * Checks whether the drop list can receive the passed-in item. * @param item Item that is being dragged into the list. * @param x Position of the item along the X axis. * @param y Position of the item along the Y axis. */ DropListRef.prototype._canReceive = function (item, x, y) { if (!isInsideClientRect(this._clientRect, x, y) || !this.enterPredicate(item, this)) { return false; } var elementFromPoint = this._getShadowRoot().elementFromPoint(x, y); // If there's no element at the pointer position, then // the client rect is probably scrolled out of the view. if (!elementFromPoint) { return false; } var nativeElement = coerceElement(this.element); // The `ClientRect`, that we're using to find the container over which the user is // hovering, doesn't give us any information on whether the element has been scrolled // out of the view or whether it's overlapping with other containers. This means that // we could end up transferring the item into a container that's invisible or is positioned // below another one. We use the result from `elementFromPoint` to get the top-most element // at the pointer position and to find whether it's one of the intersecting drop containers. return elementFromPoint === nativeElement || nativeElement.contains(elementFromPoint); }; /** * Called by one of the connected drop lists when a dragging sequence has started. * @param sibling Sibling in which dragging has started. */ DropListRef.prototype._startReceiving = function (sibling) { var activeSiblings = this._activeSiblings; if (!activeSiblings.has(sibling)) { activeSiblings.add(sibling); this._cacheOwnPosition(); this._listenToScrollEvents(); } }; /** * Called by a connected drop list when dragging has stopped. * @param sibling Sibling whose dragging has stopped. */ DropListRef.prototype._stopReceiving = function (sibling) { this._activeSiblings.delete(sibling); this._viewportScrollSubscription.unsubscribe(); }; /** * Starts listening to scroll events on the viewport. * Used for updating the internal state of the list. */ DropListRef.prototype._listenToScrollEvents = function () { var _this = this; this._viewportScrollPosition = this._viewportRuler.getViewportScrollPosition(); this._viewportScrollSubscription = this._dragDropRegistry.scroll.subscribe(function () { if (_this.isDragging()) { var newPosition = _this._viewportRuler.getViewportScrollPosition(); _this._updateAfterScroll(_this._viewportScrollPosition, newPosition.top, newPosition.left, _this._clientRect); } else if (_this.isReceiving()) { _this._cacheOwnPosition(); } }); }; /** * Lazily resolves and returns the shadow root of the element. We do this in a function, rather * than saving it in property directly on init, because we want to resolve it as late as possible * in order to ensure that the element has been moved into the shadow DOM. Doing it inside the * constructor might be too early if the element is inside of something like `ngFor` or `ngIf`. */ DropListRef.prototype._getShadowRoot = function () { if (!this._cachedShadowRoot) { this._cachedShadowRoot = getShadowRoot(coerceElement(this.element)) || this._document; } return this._cachedShadowRoot; }; return DropListRef; }()); export { DropListRef }; /** * Updates the top/left positions of a `ClientRect`, as well as their bottom/right counterparts. * @param clientRect `ClientRect` that should be updated. * @param top Amount to add to the `top` position. * @param left Amount to add to the `left` position. */ function adjustClientRect(clientRect, top, left) { clientRect.top += top; clientRect.bottom = clientRect.top + clientRect.height; clientRect.left += left; clientRect.right = clientRect.left + clientRect.width; } /** * Finds the index of an item that matches a predicate function. Used as an equivalent * of `Array.prototype.findIndex` which isn't part of the standard Google typings. * @param array Array in which to look for matches. * @param predicate Function used to determine whether an item is a match. */ function findIndex(array, predicate) { for (var i = 0; i < array.length; i++) { if (predicate(array[i], i, array)) { return i; } } return -1; } /** * Checks whether some coordinates are within a `ClientRect`. * @param clientRect ClientRect that is being checked. * @param x Coordinates along the X axis. * @param y Coordinates along the Y axis. */ function isInsideClientRect(clientRect, x, y) { var top = clientRect.top, bottom = clientRect.bottom, left = clientRect.left, right = clientRect.right; return y >= top && y <= bottom && x >= left && x <= right; } /** Gets a mutable version of an element's bounding `ClientRect`. */ function getMutableClientRect(element) { var clientRect = element.getBoundingClientRect(); // We need to clone the `clientRect` here, because all the values on it are readonly // and we need to be able to update them. Also we can't use a spread here, because // the values on a `ClientRect` aren't own properties. See: // https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect#Notes return { top: clientRect.top, right: clientRect.right, bottom: clientRect.bottom, left: clientRect.left, width: clientRect.width, height: clientRect.height }; } /** * Increments the vertical scroll position of a node. * @param node Node whose scroll position should change. * @param amount Amount of pixels that the `node` should be scrolled. */ function incrementVerticalScroll(node, amount) { if (node === window) { node.scrollBy(0, amount); } else { // Ideally we could use `Element.scrollBy` here as well, but IE and Edge don't support it. node.scrollTop += amount; } } /** * Increments the horizontal scroll position of a node. * @param node Node whose scroll position should change. * @param amount Amount of pixels that the `node` should be scrolled. */ function incrementHorizontalScroll(node, amount) { if (node === window) { node.scrollBy(amount, 0); } else { // Ideally we could use `Element.scrollBy` here as well, but IE and Edge don't support it. node.scrollLeft += amount; } } /** * Gets whether the vertical auto-scroll direction of a node. * @param clientRect Dimensions of the node. * @param pointerY Position of the user's pointer along the y axis. */ function getVerticalScrollDirection(clientRect, pointerY) { var top = clientRect.top, bottom = clientRect.bottom, height = clientRect.height; var yThreshold = height * SCROLL_PROXIMITY_THRESHOLD; if (pointerY >= top - yThreshold && pointerY <= top + yThreshold) { return 1 /* UP */; } else if (pointerY >= bottom - yThreshold && pointerY <= bottom + yThreshold) { return 2 /* DOWN */; } return 0 /* NONE */; } /** * Gets whether the horizontal auto-scroll direction of a node. * @param clientRect Dimensions of the node. * @param pointerX Position of the user's pointer along the x axis. */ function getHorizontalScrollDirection(clientRect, pointerX) { var left = clientRect.left, right = clientRect.right, width = clientRect.width; var xThreshold = width * SCROLL_PROXIMITY_THRESHOLD; if (pointerX >= left - xThreshold && pointerX <= left + xThreshold) { return 1 /* LEFT */; } else if (pointerX >= right - xThreshold && pointerX <= right + xThreshold) { return 2 /* RIGHT */; } return 0 /* NONE */; } /** * Gets the directions in which an element node should be scrolled, * assuming that the user's pointer is already within it scrollable region. * @param element Element for which we should calculate the scroll direction. * @param clientRect Bounding client rectangle of the element. * @param pointerX Position of the user's pointer along the x axis. * @param pointerY Position of the user's pointer along the y axis. */ function getElementScrollDirections(element, clientRect, pointerX, pointerY) { var computedVertical = getVerticalScrollDirection(clientRect, pointerY); var computedHorizontal = getHorizontalScrollDirection(clientRect, pointerX); var verticalScrollDirection = 0 /* NONE */; var horizontalScrollDirection = 0 /* NONE */; // Note that we here we do some extra checks for whether the element is actually scrollable in // a certain direction and we only assign the scroll direction if it is. We do this so that we // can allow other elements to be scrolled, if the current element can't be scrolled anymore. // This allows us to handle cases where the scroll regions of two scrollable elements overlap. if (computedVertical) { var scrollTop = element.scrollTop; if (computedVertical === 1 /* UP */) { if (scrollTop > 0) { verticalScrollDirection = 1 /* UP */; } } else if (element.scrollHeight - scrollTop > element.clientHeight) { verticalScrollDirection = 2 /* DOWN */; } } if (computedHorizontal) { var scrollLeft = element.scrollLeft; if (computedHorizontal === 1 /* LEFT */) { if (scrollLeft > 0) { horizontalScrollDirection = 1 /* LEFT */; } } else if (element.scrollWidth - scrollLeft > element.clientWidth) { horizontalScrollDirection = 2 /* RIGHT */; } } return [verticalScrollDirection, horizontalScrollDirection]; } /** Gets the shadow root of an element, if any. */ function getShadowRoot(element) { if (_supportsShadowDom()) { var rootNode = element.getRootNode ? element.getRootNode() : null; if (rootNode instanceof ShadowRoot) { return rootNode; } } return null; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJvcC1saXN0LXJlZi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3NyYy9jZGsvZHJhZy1kcm9wL2Ryb3AtbGlzdC1yZWYudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HOztBQUlILE9BQU8sRUFBQyxhQUFhLEVBQUMsTUFBTSx1QkFBdUIsQ0FBQztBQUVwRCxPQUFPLEVBQUMsa0JBQWtCLEVBQUMsTUFBTSx1QkFBdUIsQ0FBQztBQUN6RCxPQUFPLEVBQUMsT0FBTyxFQUFFLFlBQVksRUFBRSxRQUFRLEVBQUUsdUJBQXVCLEVBQUMsTUFBTSxNQUFNLENBQUM7QUFDOUUsT0FBTyxFQUFDLFNBQVMsRUFBQyxNQUFNLGdCQUFnQixDQUFDO0FBQ3pDLE9BQU8sRUFBQyxlQUFlLEVBQUMsTUFBTSxjQUFjLENBQUM7QUFJN0M7OztHQUdHO0FBQ0gsSUFBTSx3QkFBd0IsR0FBRyxJQUFJLENBQUM7QUFFdEM7OztHQUdHO0FBQ0gsSUFBTSwwQkFBMEIsR0FBRyxJQUFJLENBQUM7QUFFeEM7OztHQUdHO0FBQ0gsSUFBTSxnQkFBZ0IsR0FBRyxDQUFDLENBQUM7QUFrQzNCOztHQUVHO0FBQ0g7SUE2SEUscUJBQ0UsT0FBOEMsRUFDdEMsaUJBQXlELEVBQ2pFLFNBQWMsRUFDTixPQUFlLEVBQ2YsY0FBNkI7UUFMdkMsaUJBU0M7UUFQUyxzQkFBaUIsR0FBakIsaUJBQWlCLENBQXdDO1FBRXpELFlBQU8sR0FBUCxPQUFPLENBQVE7UUFDZixtQkFBYyxHQUFkLGNBQWMsQ0FBZTtRQTlIdkMsNEVBQTRFO1FBQzVFLGFBQVEsR0FBWSxLQUFLLENBQUM7UUFFMUIseURBQXlEO1FBQ3pELG9CQUFlLEdBQVksS0FBSyxDQUFDO1FBS2pDOzs7V0FHRztRQUNILHVCQUFrQixHQUFZLEtBQUssQ0FBQztRQUVwQzs7O1dBR0c7UUFDSCxtQkFBYyxHQUFrRCxjQUFNLE9BQUEsSUFBSSxFQUFKLENBQUksQ0FBQztRQUUzRSwrQ0FBK0M7UUFDL0Msa0JBQWEsR0FBRyxJQUFJLE9BQU8sRUFBUSxDQUFDO1FBRXBDOztXQUVHO1FBQ0gsWUFBTyxHQUFHLElBQUksT0FBTyxFQUFpRSxDQUFDO1FBRXZGOzs7V0FHRztRQUNILFdBQU0sR0FBRyxJQUFJLE9BQU8sRUFBMkMsQ0FBQztRQUVoRSw4REFBOEQ7UUFDOUQsWUFBTyxHQUFHLElBQUksT0FBTyxFQVFqQixDQUFDO1FBRUwsbUVBQW1FO1FBQ25FLFdBQU0sR0FBRyxJQUFJLE9BQU8sRUFLaEIsQ0FBQztRQUtMLG9EQUFvRDtRQUM1QyxnQkFBVyxHQUFHLEtBQUssQ0FBQztRQUU1QixxRUFBcUU7UUFDN0QsbUJBQWMsR0FBeUIsRUFBRSxDQUFDO1FBRWxELHNEQUFzRDtRQUM5QyxvQkFBZSxHQUFtQixFQUFDLEdBQUcsRUFBRSxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsRUFBQyxDQUFDO1FBRTVELDBEQUEwRDtRQUNsRCw0QkFBdUIsR0FBbUIsRUFBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEVBQUMsQ0FBQztRQVlwRTs7O1dBR0c7UUFDSyxrQkFBYSxHQUFHLEVBQUMsSUFBSSxFQUFFLElBQXNCLEVBQUUsS0FBSyxFQUFFLENBQUMsRUFBQyxDQUFDO1FBS2pFLHdEQUF3RDtRQUNoRCxjQUFTLEdBQStCLEVBQUUsQ0FBQztRQUVuRCwrQ0FBK0M7UUFDdkMsaUJBQVksR0FBOEIsVUFBVSxDQUFDO1FBRTdELDZEQUE2RDtRQUNyRCxvQkFBZSxHQUFHLElBQUksR0FBRyxFQUFlLENBQUM7UUFFakQseUNBQXlDO1FBQ2pDLGVBQVUsR0FBYyxLQUFLLENBQUM7UUFFdEMsaURBQWlEO1FBQ3pDLGdDQUEyQixHQUFHLFlBQVksQ0FBQyxLQUFLLENBQUM7UUFFekQsbUVBQW1FO1FBQzNELDZCQUF3QixnQkFBb0M7UUFFcEUscUVBQXFFO1FBQzdELCtCQUEwQixnQkFBc0M7UUFLeEUsdUVBQXVFO1FBQy9ELHNCQUFpQixHQUFHLElBQUksT0FBTyxFQUFRLENBQUM7UUFFaEQsaUdBQWlHO1FBQ3pGLHNCQUFpQixHQUFnQyxJQUFJLENBQUM7UUFnaEI5RCxpR0FBaUc7UUFDekYsa0JBQWEsR0FBRztZQUN0QixJQUFJLENBQUMsS0FBSSxDQUFDLFVBQVUsRUFBRSxFQUFFO2dCQUN0QixPQUFPO2FBQ1I7WUFFRCxJQUFNLE9BQU8sR0FBRyxhQUFhLENBQUMsS0FBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQzVDLEtBQUksQ0FBQyxrQkFBa0IsQ0FBQyxLQUFJLENBQUMsZUFBZSxFQUFFLE9BQU8sQ0FBQyxTQUFTLEVBQUUsT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ3ZGLENBQUMsQ0FBQTtRQVFELDJEQUEyRDtRQUNuRCx5QkFBb0IsR0FBRztZQUM3QixLQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7WUFFdEIsUUFBUSxDQUFDLENBQUMsRUFBRSx1QkFBdUIsQ0FBQztpQkFDakMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFJLENBQUMsaUJBQWlCLENBQUMsQ0FBQztpQkFDdkMsU0FBUyxDQUFDO2dCQUNULElBQU0sSUFBSSxHQUFHLEtBQUksQ0FBQyxXQUFXLENBQUM7Z0JBRTlCLElBQUksS0FBSSxDQUFDLHdCQUF3QixlQUFtQyxFQUFFO29CQUNwRSx1QkFBdUIsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO2lCQUNsRDtxQkFBTSxJQUFJLEtBQUksQ0FBQyx3QkFBd0IsaUJBQXFDLEVBQUU7b0JBQzdFLHVCQUF1QixDQUFDLElBQUksRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO2lCQUNqRDtnQkFFRCxJQUFJLEtBQUksQ0FBQywwQkFBMEIsaUJBQXVDLEVBQUU7b0JBQzFFLHlCQUF5QixDQUFDLElBQUksRUFBRSxDQUFDLGdCQUFnQixDQUFDLENBQUM7aUJBQ3BEO3FCQUFNLElBQUksS0FBSSxDQUFDLDBCQUEwQixrQkFBd0MsRUFBRTtvQkFDbEYseUJBQXlCLENBQUMsSUFBSSxFQUFFLGdCQUFnQixDQUFDLENBQUM7aUJBQ25EO1lBQ0gsQ0FBQyxDQUFDLENBQUM7UUFDUCxDQUFDLENBQUE7UUExaUJDLElBQUksQ0FBQyxPQUFPLEdBQUcsYUFBYSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ3RDLElBQUksQ0FBQyxTQUFTLEdBQUcsU0FBUyxDQUFDO1FBQzNCLGlCQUFpQixDQUFDLHFCQUFxQixDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ2hELENBQUM7SUFFRCxnRUFBZ0U7SUFDaEUsNkJBQU8sR0FBUDtRQUNFLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUN0QixJQUFJLENBQUMsaUJBQWlCLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDbEMsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7UUFDeEIsSUFBSSxDQUFDLGFBQWEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUM5QixJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQ3hCLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDdkIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUN4QixJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxlQUFlLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDN0IsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFLLENBQUM7UUFDekIsSUFBSSxDQUFDLGlCQUFpQixDQUFDLG1CQUFtQixDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ25ELENBQUM7SUFFRCxpRUFBaUU7SUFDakUsZ0NBQVUsR0FBVjtRQUNFLE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQztJQUMxQixDQUFDO0lBRUQsK0JBQStCO0lBQy9CLDJCQUFLLEdBQUw7UUFBQSxpQkFTQztRQVJDLElBQU0sT0FBTyxHQUFHLGFBQWEsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDNUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUMxQixJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQztRQUN4QixJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDbkIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsVUFBQSxPQUFPLElBQUksT0FBQSxPQUFPLENBQUMsZUFBZSxDQUFDLEtBQUksQ0FBQyxFQUE3QixDQUE2QixDQUFDLENBQUM7UUFDakUsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7UUFDeEIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxjQUFNLE9BQUEsT0FBTyxDQUFDLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxLQUFJLENBQUMsYUFBYSxDQUFDLEVBQXRELENBQXNELENBQUMsQ0FBQztRQUM3RixJQUFJLENBQUMscUJBQXFCLEVBQUUsQ0FBQztJQUMvQixDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSCwyQkFBSyxHQUFMLFVBQU0sSUFBYSxFQUFFLFFBQWdCLEVBQUUsUUFBZ0I7UUFDckQsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBRWIscUVBQXFFO1FBQ3JFLGlFQUFpRTtRQUNqRSxJQUFJLFFBQVEsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFFMUUsSUFBSSxRQUFRLEtBQUssQ0FBQyxDQUFDLEVBQUU7WUFDbkIsNERBQTREO1lBQzVELDJEQUEyRDtZQUMzRCxRQUFRLEdBQUcsSUFBSSxDQUFDLGdDQUFnQyxDQUFDLElBQUksRUFBRSxRQUFRLEVBQUUsUUFBUSxDQUFDLENBQUM7U0FDNUU7UUFFRCxJQUFNLGdCQUFnQixHQUFHLElBQUksQ0FBQyxpQkFBaUIsQ0FBQztRQUNoRCxJQUFNLFlBQVksR0FBRyxnQkFBZ0IsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDcEQsSUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLHFCQUFxQixFQUFFLENBQUM7UUFDakQsSUFBSSxvQkFBb0IsR0FBd0IsZ0JBQWdCLENBQUMsUUFBUSxDQUFDLENBQUM7UUFFM0UsaUZBQWlGO1FBQ2pGLGtGQUFrRjtRQUNsRixtRUFBbUU7UUFDbkUsSUFBSSxvQkFBb0IsS0FBSyxJQUFJLEVBQUU7WUFDakMsb0JBQW9CLEdBQUcsZ0JBQWdCLENBQUMsUUFBUSxHQUFHLENBQUMsQ0FBQyxDQUFDO1NBQ3ZEO1FBRUQsdUZBQXVGO1FBQ3ZGLHNGQUFzRjtRQUN0RixJQUFJLFlBQVksR0FBRyxDQUFDLENBQUMsRUFBRTtZQUNyQixnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsWUFBWSxFQUFFLENBQUMsQ0FBQyxDQUFDO1NBQzFDO1FBRUQsaUVBQWlFO1FBQ2pFLCtEQUErRDtRQUMvRCxJQUFJLG9CQUFvQixJQUFJLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLFVBQVUsQ0FBQyxvQkFBb0IsQ0FBQyxFQUFFO1lBQ3BGLElBQU0sT0FBTyxHQUFHLG9CQUFvQixDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ3RELE9BQU8sQ0FBQyxhQUFjLENBQUMsWUFBWSxDQUFDLFdBQVcsRUFBRSxPQUFPLENBQUMsQ0FBQztZQUMxRCxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztTQUM1QzthQUFNO1lBQ0wsYUFBYSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxXQUFXLENBQUMsV0FBVyxDQUFDLENBQUM7WUFDckQsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1NBQzdCO1FBRUQsOEVBQThFO1FBQzlFLFdBQVcsQ0FBQyxLQUFLLENBQUMsU0FBUyxHQUFHLEVBQUUsQ0FBQztRQUVqQyw0RUFBNEU7UUFDNUUscUVBQXFFO1FBQ3JFLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO1FBQzNCLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUMsSUFBSSxNQUFBLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUUsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsRUFBQyxDQUFDLENBQUM7SUFDcEYsQ0FBQztJQUVEOzs7T0FHRztJQUNILDBCQUFJLEdBQUosVUFBSyxJQUFhO1FBQ2hCLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNkLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUMsSUFBSSxNQUFBLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBQyxDQUFDLENBQUM7SUFDNUMsQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0gsMEJBQUksR0FBSixVQUFLLElBQWEsRUFBRSxZQUFvQixFQUFFLGlCQUE4QixFQUN0RSxzQkFBK0IsRUFBRSxRQUFlO1FBQ2hELElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNkLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDO1lBQ2hCLElBQUksTUFBQTtZQUNKLFlBQVksY0FBQTtZQUNaLGFBQWEsRUFBRSxpQkFBaUIsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDO1lBQ25ELFNBQVMsRUFBRSxJQUFJO1lBQ2YsaUJBQWlCLG1CQUFBO1lBQ2pCLHNCQUFzQix3QkFBQTtZQUN0QixRQUFRLFVBQUE7U0FDVCxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsK0JBQVMsR0FBVCxVQUFVLEtBQWdCO1FBQTFCLGlCQVNDO1FBUkMsSUFBSSxDQUFDLFdBQVcsR0FBRyxLQUFLLENBQUM7UUFDekIsS0FBSyxDQUFDLE9BQU8sQ0FBQyxVQUFBLElBQUksSUFBSSxPQUFBLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxLQUFJLENBQUMsRUFBN0IsQ0FBNkIsQ0FBQyxDQUFDO1FBRXJELElBQUksSUFBSSxDQUFDLFVBQVUsRUFBRSxFQUFFO1lBQ3JCLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztTQUNwQjtRQUVELE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVELGtEQUFrRDtJQUNsRCxtQ0FBYSxHQUFiLFVBQWMsU0FBb0I7UUFDaEMsSUFBSSxDQUFDLFVBQVUsR0FBRyxTQUFTLENBQUM7UUFDNUIsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILGlDQUFXLEdBQVgsVUFBWSxXQUEwQjtRQUNwQyxJQUFJLENBQUMsU0FBUyxHQUFHLFdBQVcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNyQyxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRDs7O09BR0c7SUFDSCxxQ0FBZSxHQUFmLFVBQWdCLFdBQXNDO1FBQ3BELElBQUksQ0FBQyxZQUFZLEdBQUcsV0FBVyxDQUFDO1FBQ2hDLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVEOzs7T0FHRztJQUNILGtDQUFZLEdBQVosVUFBYSxJQUFhO1FBQ3hCLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFO1lBQ3JCLE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7U0FDdkM7UUFFRCwwRkFBMEY7UUFDMUYsa0ZBQWtGO1FBQ2xGLDBEQUEwRDtRQUMxRCxJQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsWUFBWSxLQUFLLFlBQVksSUFBSSxJQUFJLENBQUMsVUFBVSxLQUFLLEtBQUssQ0FBQyxDQUFDO1lBQzNFLElBQUksQ0FBQyxjQUFjLENBQUMsS0FBSyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUM7UUFFaEUsT0FBTyxTQUFTLENBQUMsS0FBSyxFQUFFLFVBQUEsV0FBVyxJQUFJLE9BQUEsV0FBVyxDQUFDLElBQUksS0FBSyxJQUFJLEVBQXpCLENBQXlCLENBQUMsQ0FBQztJQUNwRSxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsaUNBQVcsR0FBWDtRQUNFLE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxDQUFDO0lBQ3ZDLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCwrQkFBUyxHQUFULFVBQVUsSUFBYSxFQUFFLFFBQWdCLEVBQUUsUUFBZ0IsRUFDakQsWUFBb0M7UUFDNUMsbUVBQW1FO1FBQ25FLElBQUksSUFBSSxDQUFDLGVBQWUsSUFBSSxDQUFDLElBQUksQ0FBQywyQkFBMkIsQ0FBQ