@katoid/angular-grid-layout
Version:
Grid Layout with draggable and resizable items for Angular
233 lines • 36.3 kB
JavaScript
import { animationFrameScheduler, fromEvent, interval, NEVER } from 'rxjs';
import { distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators';
import { ktdNormalizePassiveListenerOptions } from './passive-listeners';
import { getMutableClientRect } from './client-rect';
import { ktdNoEmit } from './operators';
/**
* Proximity, as a ratio to width/height at which to start auto-scrolling.
* The value comes from trying it out manually until it feels right.
*/
const SCROLL_PROXIMITY_THRESHOLD = 0.05;
/**
* 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) {
const { top, bottom, height } = clientRect;
const yThreshold = height * SCROLL_PROXIMITY_THRESHOLD;
if (pointerY >= top - yThreshold && pointerY <= top + yThreshold) {
return 1 /* AutoScrollVerticalDirection.UP */;
}
else if (pointerY >= bottom - yThreshold && pointerY <= bottom + yThreshold) {
return 2 /* AutoScrollVerticalDirection.DOWN */;
}
return 0 /* AutoScrollVerticalDirection.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) {
const { left, right, width } = clientRect;
const xThreshold = width * SCROLL_PROXIMITY_THRESHOLD;
if (pointerX >= left - xThreshold && pointerX <= left + xThreshold) {
return 1 /* AutoScrollHorizontalDirection.LEFT */;
}
else if (pointerX >= right - xThreshold && pointerX <= right + xThreshold) {
return 2 /* AutoScrollHorizontalDirection.RIGHT */;
}
return 0 /* AutoScrollHorizontalDirection.NONE */;
}
/**
* Returns an observable that schedules a loop and apply scroll on the scrollNode into the specified direction/s.
* This observable doesn't emit, it just performs the 'scroll' side effect.
* @param scrollNode, node where the scroll would be applied.
* @param verticalScrollDirection, vertical direction of the scroll.
* @param horizontalScrollDirection, horizontal direction of the scroll.
* @param scrollStep, scroll step in CSS pixels that would be applied in every loop.
*/
function scrollToDirectionInterval$(scrollNode, verticalScrollDirection, horizontalScrollDirection, scrollStep = 2) {
return interval(0, animationFrameScheduler)
.pipe(tap(() => {
if (verticalScrollDirection === 1 /* AutoScrollVerticalDirection.UP */) {
incrementVerticalScroll(scrollNode, -scrollStep);
}
else if (verticalScrollDirection === 2 /* AutoScrollVerticalDirection.DOWN */) {
incrementVerticalScroll(scrollNode, scrollStep);
}
if (horizontalScrollDirection === 1 /* AutoScrollHorizontalDirection.LEFT */) {
incrementHorizontalScroll(scrollNode, -scrollStep);
}
else if (horizontalScrollDirection === 2 /* AutoScrollHorizontalDirection.RIGHT */) {
incrementHorizontalScroll(scrollNode, scrollStep);
}
}), ktdNoEmit());
}
/**
* Given a source$ observable with pointer location, scroll the scrollNode if the pointer is near to it.
* This observable doesn't emit, it just performs a 'scroll' side effect.
* @param scrollableParent, parent node in which the scroll would be performed.
* @param options, configuration options.
*/
export function ktdScrollIfNearElementClientRect$(scrollableParent, options) {
let scrollNode;
let scrollableParentClientRect;
let scrollableParentScrollWidth;
if (scrollableParent === document) {
scrollNode = document.defaultView;
const { width, height } = getViewportSize();
scrollableParentClientRect = { width, height, top: 0, right: width, bottom: height, left: 0 };
scrollableParentScrollWidth = getDocumentScrollWidth();
}
else {
scrollNode = scrollableParent;
scrollableParentClientRect = getMutableClientRect(scrollableParent);
scrollableParentScrollWidth = scrollableParent.scrollWidth;
}
/**
* IMPORTANT: By design, only let scroll horizontal if the scrollable parent has explicitly an scroll horizontal.
* This layout solution is not designed in mind to have any scroll horizontal, but exceptionally we allow it in this
* specific use case.
*/
options = options || {};
if (options.disableHorizontal == null && scrollableParentScrollWidth <= scrollableParentClientRect.width) {
options.disableHorizontal = true;
}
return (source$) => source$.pipe(map(({ pointerX, pointerY }) => {
let verticalScrollDirection = getVerticalScrollDirection(scrollableParentClientRect, pointerY);
let horizontalScrollDirection = getHorizontalScrollDirection(scrollableParentClientRect, pointerX);
// Check if scroll directions are disabled.
if (options?.disableVertical) {
verticalScrollDirection = 0 /* AutoScrollVerticalDirection.NONE */;
}
if (options?.disableHorizontal) {
horizontalScrollDirection = 0 /* AutoScrollHorizontalDirection.NONE */;
}
return { verticalScrollDirection, horizontalScrollDirection };
}), distinctUntilChanged((prev, actual) => {
return prev.verticalScrollDirection === actual.verticalScrollDirection
&& prev.horizontalScrollDirection === actual.horizontalScrollDirection;
}), switchMap(({ verticalScrollDirection, horizontalScrollDirection }) => {
if (verticalScrollDirection || horizontalScrollDirection) {
return scrollToDirectionInterval$(scrollNode, verticalScrollDirection, horizontalScrollDirection, options?.scrollStep);
}
else {
return NEVER;
}
}));
}
/**
* Emits on EVERY scroll event and returns the accumulated scroll offset relative to the initial scroll position.
* @param scrollableParent, node in which scroll events would be listened.
*/
export function ktdGetScrollTotalRelativeDifference$(scrollableParent) {
let scrollInitialPosition;
// Calculate initial scroll position
if (scrollableParent === document) {
scrollInitialPosition = getViewportScrollPosition();
}
else {
scrollInitialPosition = {
top: scrollableParent.scrollTop,
left: scrollableParent.scrollLeft
};
}
return fromEvent(scrollableParent, 'scroll', ktdNormalizePassiveListenerOptions({ capture: true })).pipe(map(() => {
let newTop;
let newLeft;
if (scrollableParent === document) {
const viewportScrollPosition = getViewportScrollPosition();
newTop = viewportScrollPosition.top;
newLeft = viewportScrollPosition.left;
}
else {
newTop = scrollableParent.scrollTop;
newLeft = scrollableParent.scrollLeft;
}
const topDifference = scrollInitialPosition.top - newTop;
const leftDifference = scrollInitialPosition.left - newLeft;
return { top: topDifference, left: leftDifference };
}));
}
/** Returns the viewport's width and height. */
function getViewportSize() {
const _window = document.defaultView || window;
return {
width: _window.innerWidth,
height: _window.innerHeight
};
}
/** Gets a ClientRect for the viewport's bounds. */
function getViewportRect() {
// Use the document element's bounding rect rather than the window scroll properties
// (e.g. pageYOffset, scrollY) due to in issue in Chrome and IE where window scroll
// properties and client coordinates (boundingClientRect, clientX/Y, etc.) are in different
// conceptual viewports. Under most circumstances these viewports are equivalent, but they
// can disagree when the page is pinch-zoomed (on devices that support touch).
// See https://bugs.chromium.org/p/chromium/issues/detail?id=489206#c4
// We use the documentElement instead of the body because, by default (without a css reset)
// browsers typically give the document body an 8px margin, which is not included in
// getBoundingClientRect().
const scrollPosition = getViewportScrollPosition();
const { width, height } = getViewportSize();
return {
top: scrollPosition.top,
left: scrollPosition.left,
bottom: scrollPosition.top + height,
right: scrollPosition.left + width,
height,
width,
};
}
/** Gets the (top, left) scroll position of the viewport. */
function getViewportScrollPosition() {
// The top-left-corner of the viewport is determined by the scroll position of the document
// body, normally just (scrollLeft, scrollTop). However, Chrome and Firefox disagree about
// whether `document.body` or `document.documentElement` is the scrolled element, so reading
// `scrollTop` and `scrollLeft` is inconsistent. However, using the bounding rect of
// `document.documentElement` works consistently, where the `top` and `left` values will
// equal negative the scroll position.
const windowRef = document.defaultView || window;
const documentElement = document.documentElement;
const documentRect = documentElement.getBoundingClientRect();
const top = -documentRect.top || document.body.scrollTop || windowRef.scrollY ||
documentElement.scrollTop || 0;
const left = -documentRect.left || document.body.scrollLeft || windowRef.scrollX ||
documentElement.scrollLeft || 0;
return { top, left };
}
/** Returns the document scroll width */
function getDocumentScrollWidth() {
return Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2Nyb2xsLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvYW5ndWxhci1ncmlkLWxheW91dC9zcmMvbGliL3V0aWxzL3Njcm9sbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQWMsTUFBTSxNQUFNLENBQUM7QUFDdkYsT0FBTyxFQUFFLG9CQUFvQixFQUFFLEdBQUcsRUFBRSxTQUFTLEVBQUUsR0FBRyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDM0UsT0FBTyxFQUFFLGtDQUFrQyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDekUsT0FBTyxFQUFFLG9CQUFvQixFQUFpQixNQUFNLGVBQWUsQ0FBQztBQUNwRSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBRXhDOzs7R0FHRztBQUNILE1BQU0sMEJBQTBCLEdBQUcsSUFBSSxDQUFDO0FBY3hDOzs7O0dBSUc7QUFDSCxTQUFTLHVCQUF1QixDQUFDLElBQTBCLEVBQUUsTUFBYztJQUN2RSxJQUFJLElBQUksS0FBSyxNQUFNLEVBQUU7UUFDaEIsSUFBZSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsTUFBTSxDQUFDLENBQUM7S0FDeEM7U0FBTTtRQUNILDBGQUEwRjtRQUN6RixJQUFvQixDQUFDLFNBQVMsSUFBSSxNQUFNLENBQUM7S0FDN0M7QUFDTCxDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILFNBQVMseUJBQXlCLENBQUMsSUFBMEIsRUFBRSxNQUFjO0lBQ3pFLElBQUksSUFBSSxLQUFLLE1BQU0sRUFBRTtRQUNoQixJQUFlLENBQUMsUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQztLQUN4QztTQUFNO1FBQ0gsMEZBQTBGO1FBQ3pGLElBQW9CLENBQUMsVUFBVSxJQUFJLE1BQU0sQ0FBQztLQUM5QztBQUNMLENBQUM7QUFHRDs7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QWUsQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO0lBRTdELE1BQU0sR0FBRyxHQUFHLENBQUMsWUFBWSxDQUFDLEdBQUcsSUFBSSxRQUFRLENBQUMsSUFBSSxDQUFDLFNBQVMsSUFBSSxTQUFTLENBQUMsT0FBTztRQUN6RSxlQUFlLENBQUMsU0FBUyxJQUFJLENBQUMsQ0FBQztJQUVuQyxNQUFNLElBQUksR0FBRyxDQUFDLFlBQVksQ0FBQyxJQUFJLElBQUksUUFBUSxDQUFDLElBQUksQ0FBQyxVQUFVLElBQUksU0FBUyxDQUFDLE9BQU87UUFDNUUsZUFBZSxDQUFDLFVBQVUsSUFBSSxDQUFDLENBQUM7SUFFcEMsT0FBTyxFQUFDLEdBQUcsRUFBRSxJQUFJLEVBQUMsQ0FBQztBQUN2QixDQUFDO0FBRUQsd0NBQXdDO0FBQ3hDLFNBQVMsc0JBQXNCO0lBQzNCLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxRQUFRLENBQUMsZUFBZSxDQUFDLFdBQVcsQ0FBQyxDQUFDO0FBQ3JGLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBhbmltYXRpb25GcmFtZVNjaGVkdWxlciwgZnJvbUV2ZW50LCBpbnRlcnZhbCwgTkVWRVIsIE9ic2VydmFibGUgfSBmcm9tICdyeGpzJztcclxuaW1wb3J0IHsgZGlzdGluY3RVbnRpbENoYW5nZWQsIG1hcCwgc3dpdGNoTWFwLCB0YXAgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XHJcbmltcG9ydCB7IGt0ZE5vcm1hbGl6ZVBhc3NpdmVMaXN0ZW5lck9wdGlvbnMgfSBmcm9tICcuL3Bhc3NpdmUtbGlzdGVuZXJzJztcclxuaW1wb3J0IHsgZ2V0TXV0YWJsZUNsaWVudFJlY3QsIEt0ZENsaWVudFJlY3QgfSBmcm9tICcuL2NsaWVudC1yZWN0JztcclxuaW1wb3J0IHsga3RkTm9FbWl0IH0gZnJvbSAnLi9vcGVyYXRvcnMnO1xyXG5cclxuLyoqXHJcbiAqIFByb3hpbWl0eSwgYXMgYSByYXRpbyB0byB3aWR0aC9oZWlnaHQgYXQgd2hpY2ggdG8gc3RhcnQgYXV0by1zY3JvbGxpbmcuXHJcbiAqIFRoZSB2YWx1ZSBjb21lcyBmcm9tIHRyeWluZyBpdCBvdXQgbWFudWFsbHkgdW50aWwgaXQgZmVlbHMgcmlnaHQuXHJcbiAqL1xyXG5jb25zdCBTQ1JPTExfUFJPWElNSVRZX1RIUkVTSE9MRCA9IDAuMDU7XHJcblxyXG4vKiogVmVydGljYWwgZGlyZWN0aW9uIGluIHdoaWNoIHdlIGNhbiBhdXRvLXNjcm9sbC4gKi9cclxuY29uc3QgZW51bSBBdXRvU2Nyb2xsVmVydGljYWxEaXJlY3Rpb24ge05PTkUsIFVQLCBET1dOfVxyXG5cclxuLyoqIEhvcml6b250YWwgZGlyZWN0aW9uIGluIHdoaWNoIHdlIGNhbiBhdXRvLXNjcm9sbC4gKi9cclxuY29uc3QgZW51bSBBdXRvU2Nyb2xsSG9yaXpvbnRhbERpcmVjdGlvbiB7Tk9ORSwgTEVGVCwgUklHSFR9XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIEt0ZFNjcm9sbFBvc2l0aW9uIHtcclxuICAgIHRvcDogbnVtYmVyO1xyXG4gICAgbGVmdDogbnVtYmVyO1xyXG59XHJcblxyXG5cclxuLyoqXHJcbiAqIEluY3JlbWVudHMgdGhlIHZlcnRpY2FsIHNjcm9sbCBwb3NpdGlvbiBvZiBhIG5vZGUuXHJcbiAqIEBwYXJhbSBub2RlIE5vZGUgd2hvc2Ugc2Nyb2xsIHBvc2l0aW9uIHNob3VsZCBjaGFuZ2UuXHJcbiAqIEBwYXJhbSBhbW91bnQgQW1vdW50IG9mIHBpeGVscyB0aGF0IHRoZSBgbm9kZWAgc2hvdWxkIGJlIHNjcm9sbGVkLlxyXG4gKi9cclxuZnVuY3Rpb24gaW5jcmVtZW50VmVydGljYWxTY3JvbGwobm9kZTogSFRNTEVsZW1lbnQgfCBXaW5kb3csIGFtb3VudDogbnVtYmVyKSB7XHJcbiAgICBpZiAobm9kZSA9PT0gd2luZG93KSB7XHJcbiAgICAgICAgKG5vZGUgYXMgV2luZG93KS5zY3JvbGxCeSgwLCBhbW91bnQpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBJZGVhbGx5IHdlIGNvdWxkIHVzZSBgRWxlbWVudC5zY3JvbGxCeWAgaGVyZSBhcyB3ZWxsLCBidXQgSUUgYW5kIEVkZ2UgZG9uJ3Qgc3VwcG9ydCBpdC5cclxuICAgICAgICAobm9kZSBhcyBIVE1MRWxlbWVudCkuc2Nyb2xsVG9wICs9IGFtb3VudDtcclxuICAgIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEluY3JlbWVudHMgdGhlIGhvcml6b250YWwgc2Nyb2xsIHBvc2l0aW9uIG9mIGEgbm9kZS5cclxuICogQHBhcmFtIG5vZGUgTm9kZSB3aG9zZSBzY3JvbGwgcG9zaXRpb24gc2hvdWxkIGNoYW5nZS5cclxuICogQHBhcmFtIGFtb3VudCBBbW91bnQgb2YgcGl4ZWxzIHRoYXQgdGhlIGBub2RlYCBzaG91bGQgYmUgc2Nyb2xsZWQuXHJcbiAqL1xyXG5mdW5jdGlvbiBpbmNyZW1lbnRIb3Jpem9udGFsU2Nyb2xsKG5vZGU6IEhUTUxFbGVtZW50IHwgV2luZG93LCBhbW91bnQ6IG51bWJlcikge1xyXG4gICAgaWYgKG5vZGUgPT09IHdpbmRvdykge1xyXG4gICAgICAgIChub2RlIGFzIFdpbmRvdykuc2Nyb2xsQnkoYW1vdW50LCAwKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gSWRlYWxseSB3ZSBjb3VsZCB1c2UgYEVsZW1lbnQuc2Nyb2xsQnlgIGhlcmUgYXMgd2VsbCwgYnV0IElFIGFuZCBFZGdlIGRvbid0IHN1cHBvcnQgaXQuXHJcbiAgICAgICAgKG5vZGUgYXMgSFRNTEVsZW1lbnQpLnNjcm9sbExlZnQgKz0gYW1vdW50O1xyXG4gICAgfVxyXG59XHJcblxyXG5cclxuLyoqXHJcbiAqIEdldHMgd2hldGhlciB0aGUgdmVydGljYWwgYXV0by1zY3JvbGwgZGlyZWN0aW9uIG9mIGEgbm9kZS5cclxuICogQHBhcmFtIGNsaWVudFJlY3QgRGltZW5zaW9ucyBvZiB0aGUgbm9kZS5cclxuICogQHBhcmFtIHBvaW50ZXJZIFBvc2l0aW9uIG9mIHRoZSB1c2VyJ3MgcG9pbnRlciBhbG9uZyB0aGUgeSBheGlzLlxyXG4gKi9cclxuZnVuY3Rpb24gZ2V0VmVydGljYWxTY3JvbGxEaXJlY3Rpb24oY2xpZW50UmVjdDogS3RkQ2xpZW50UmVjdCwgcG9pbnRlclk6IG51bWJlcikge1xyXG4gICAgY29uc3Qge3RvcCwgYm90dG9tLCBoZWlnaHR9ID0gY2xpZW50UmVjdDtcclxuICAgIGNvbnN0IHlUaHJlc2hvbGQgPSBoZWlnaHQgKiBTQ1JPTExfUFJPWElNSVRZX1RIUkVTSE9MRDtcclxuXHJcbiAgICBpZiAocG9pbnRlclkgPj0gdG9wIC0geVRocmVzaG9sZCAmJiBwb2ludGVyWSA8PSB0b3AgKyB5VGhyZXNob2xkKSB7XHJcbiAgICAgICAgcmV0dXJuIEF1dG9TY3JvbGxWZXJ0aWNhbERpcmVjdGlvbi5VUDtcclxuICAgIH0gZWxzZSBpZiAocG9pbnRlclkgPj0gYm90dG9tIC0geVRocmVzaG9sZCAmJiBwb2ludGVyWSA8PSBib3R0b20gKyB5VGhyZXNob2xkKSB7XHJcbiAgICAgICAgcmV0dXJuIEF1dG9TY3JvbGxWZXJ0aWNhbERpcmVjdGlvbi5ET1dOO1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiBBdXRvU2Nyb2xsVmVydGljYWxEaXJlY3Rpb24uTk9ORTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldHMgd2hldGhlciB0aGUgaG9yaXpvbnRhbCBhdXRvLXNjcm9sbCBkaXJlY3Rpb24gb2YgYSBub2RlLlxyXG4gKiBAcGFyYW0gY2xpZW50UmVjdCBEaW1lbnNpb25zIG9mIHRoZSBub2RlLlxyXG4gKiBAcGFyYW0gcG9pbnRlclggUG9zaXRpb24gb2YgdGhlIHVzZXIncyBwb2ludGVyIGFsb25nIHRoZSB4IGF4aXMuXHJcbiAqL1xyXG5mdW5jdGlvbiBnZXRIb3Jpem9udGFsU2Nyb2xsRGlyZWN0aW9uKGNsaWVudFJlY3Q6IEt0ZENsaWVudFJlY3QsIHBvaW50ZXJYOiBudW1iZXIpIHtcclxuICAgIGNvbnN0IHtsZWZ0LCByaWdodCwgd2lkdGh9ID0gY2xpZW50UmVjdDtcclxuICAgIGNvbnN0IHhUaHJlc2hvbGQgPSB3aWR0aCAqIFNDUk9MTF9QUk9YSU1JVFlfVEhSRVNIT0xEO1xyXG5cclxuICAgIGlmIChwb2ludGVyWCA+PSBsZWZ0IC0geFRocmVzaG9sZCAmJiBwb2ludGVyWCA8PSBsZWZ0ICsgeFRocmVzaG9sZCkge1xyXG4gICAgICAgIHJldHVybiBBdXRvU2Nyb2xsSG9yaXpvbnRhbERpcmVjdGlvbi5MRUZUO1xyXG4gICAgfSBlbHNlIGlmIChwb2ludGVyWCA+PSByaWdodCAtIHhUaHJlc2hvbGQgJiYgcG9pbnRlclggPD0gcmlnaHQgKyB4VGhyZXNob2xkKSB7XHJcbiAgICAgICAgcmV0dXJuIEF1dG9TY3JvbGxIb3Jpem9udGFsRGlyZWN0aW9uLlJJR0hUO1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiBBdXRvU2Nyb2xsSG9yaXpvbnRhbERpcmVjdGlvbi5OT05FO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyBhbiBvYnNlcnZhYmxlIHRoYXQgc2NoZWR1bGVzIGEgbG9vcCBhbmQgYXBwbHkgc2Nyb2xsIG9uIHRoZSBzY3JvbGxOb2RlIGludG8gdGhlIHNwZWNpZmllZCBkaXJlY3Rpb24vcy5cclxuICogVGhpcyBvYnNlcnZhYmxlIGRvZXNuJ3QgZW1pdCwgaXQganVzdCBwZXJmb3JtcyB0aGUgJ3Njcm9sbCcgc2lkZSBlZmZlY3QuXHJcbiAqIEBwYXJhbSBzY3JvbGxOb2RlLCBub2RlIHdoZXJlIHRoZSBzY3JvbGwgd291bGQgYmUgYXBwbGllZC5cclxuICogQHBhcmFtIHZlcnRpY2FsU2Nyb2xsRGlyZWN0aW9uLCB2ZXJ0aWNhbCBkaXJlY3Rpb24gb2YgdGhlIHNjcm9sbC5cclxuICogQHBhcmFtIGhvcml6b250YWxTY3JvbGxEaXJlY3Rpb24sIGhvcml6b250YWwgZGlyZWN0aW9uIG9mIHRoZSBzY3JvbGwuXHJcbiAqIEBwYXJhbSBzY3JvbGxTdGVwLCBzY3JvbGwgc3RlcCBpbiBDU1MgcGl4ZWxzIHRoYXQgd291bGQgYmUgYXBwbGllZCBpbiBldmVyeSBsb29wLlxyXG4gKi9cclxuZnVuY3Rpb24gc2Nyb2xsVG9EaXJlY3Rpb25JbnRlcnZhbCQoc2Nyb2xsTm9kZTogSFRNTEVsZW1lbnQgfCBXaW5kb3csIHZlcnRpY2FsU2Nyb2xsRGlyZWN0aW9uOiBBdXRvU2Nyb2xsVmVydGljYWxEaXJlY3Rpb24sIGhvcml6b250YWxTY3JvbGxEaXJlY3Rpb246IEF1dG9TY3JvbGxIb3Jpem9udGFsRGlyZWN0aW9uLCBzY3JvbGxTdGVwOiBudW1iZXIgPSAyKSB7XHJcbiAgICByZXR1cm4gaW50ZXJ2YWwoMCwgYW5pbWF0aW9uRnJhbWVTY2hlZHVsZXIpXHJcbiAgICAgICAgLnBpcGUoXHJcbiAgICAgICAgICAgIHRhcCgoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICBpZiAodmVydGljYWxTY3JvbGxEaXJlY3Rpb24gPT09IEF1dG9TY3JvbGxWZXJ0aWNhbERpcmVjdGlvbi5VUCkge1xyXG4gICAgICAgICAgICAgICAgICAgIGluY3JlbWVudFZlcnRpY2FsU2Nyb2xsKHNjcm9sbE5vZGUsIC1zY3JvbGxTdGVwKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodmVydGljYWxTY3JvbGxEaXJlY3Rpb24gPT09IEF1dG9TY3JvbGxWZXJ0aWNhbERpcmVjdGlvbi5ET1dOKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgaW5jcmVtZW50VmVydGljYWxTY3JvbGwoc2Nyb2xsTm9kZSwgc2Nyb2xsU3RlcCk7XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgaWYgKGhvcml6b250YWxTY3JvbGxEaXJlY3Rpb24gPT09IEF1dG9TY3JvbGxIb3Jpem9udGFsRGlyZWN0aW9uLkxFRlQpIHtcclxuICAgICAgICAgICAgICAgICAgICBpbmNyZW1lbnRIb3Jpem9udGFsU2Nyb2xsKHNjcm9sbE5vZGUsIC1zY3JvbGxTdGVwKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoaG9yaXpvbnRhbFNjcm9sbERpcmVjdGlvbiA9PT0gQXV0b1Njcm9sbEhvcml6b250YWxEaXJlY3Rpb24uUklHSFQpIHtcclxuICAgICAgICAgICAgICAgICAgICBpbmNyZW1lbnRIb3Jpem9udGFsU2Nyb2xsKHNjcm9sbE5vZGUsIHNjcm9sbFN0ZXApO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9KSxcclxuICAgICAgICAgICAga3RkTm9FbWl0KClcclxuICAgICAgICApO1xyXG59XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIEt0ZFNjcm9sbElmTmVhckVsZW1lbnRPcHRpb25zIHtcclxuICAgIHNjcm9sbFN0ZXA/OiBudW1iZXI7XHJcbiAgICBkaXNhYmxlVmVydGljYWw/OiBib29sZWFuO1xyXG4gICAgZGlzYWJsZUhvcml6b250YWw/OiBib29sZWFuO1xyXG59XHJcblxyXG4vKipcclxuICogR2l2ZW4gYSBzb3VyY2UkIG9ic2VydmFibGUgd2l0aCBwb2ludGVyIGxvY2F0aW9uLCBzY3JvbGwgdGhlIHNjcm9sbE5vZGUgaWYgdGhlIHBvaW50ZXIgaXMgbmVhciB0byBpdC5cclxuICogVGhpcyBvYnNlcnZhYmxlIGRvZXNuJ3QgZW1pdCwgaXQganVzdCBwZXJmb3JtcyBhICdzY3JvbGwnIHNpZGUgZWZmZWN0LlxyXG4gKiBAcGFyYW0gc2Nyb2xsYWJsZVBhcmVudCwgcGFyZW50IG5vZGUgaW4gd2hpY2ggdGhlIHNjcm9sbCB3b3VsZCBiZSBwZXJmb3JtZWQuXHJcbiAqIEBwYXJhbSBvcHRpb25zLCBjb25maWd1cmF0aW9uIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24ga3RkU2Nyb2xsSWZOZWFyRWxlbWVudENsaWVudFJlY3QkKHNjcm9sbGFibGVQYXJlbnQ6IEhUTUxFbGVtZW50IHwgRG9jdW1lbnQsIG9wdGlvbnM/OiBLdGRTY3JvbGxJZk5lYXJFbGVtZW50T3B0aW9ucyk6IChzb3VyY2UkOiBPYnNlcnZhYmxlPHsgcG9pbnRlclg6IG51bWJlciwgcG9pbnRlclk6IG51bWJlciB9PikgPT4gT2JzZXJ2YWJsZTxhbnk+IHtcclxuXHJcbiAgICBsZXQgc2Nyb2xsTm9kZTogV2luZG93IHwgSFRNTEVsZW1lbnQ7XHJcbiAgICBsZXQgc2Nyb2xsYWJsZVBhcmVudENsaWVudFJlY3Q6IEt0ZENsaWVudFJlY3Q7XHJcbiAgICBsZXQgc2Nyb2xsYWJsZVBhcmVudFNjcm9sbFdpZHRoOiBudW1iZXI7XHJcblxyXG4gICAgaWYgKHNjcm9sbGFibGVQYXJlbnQgPT09IGRvY3VtZW50KSB7XHJcbiAgICAgICAgc2Nyb2xsTm9kZSA9IGRvY3VtZW50LmRlZmF1bHRWaWV3IGFzIFdpbmRvdztcclxuICAgICAgICBjb25zdCB7d2lkdGgsIGhlaWdodH0gPSBnZXRWaWV3cG9ydFNpemUoKTtcclxuICAgICAgICBzY3JvbGxhYmxlUGFyZW50Q2xpZW50UmVjdCA9IHt3aWR0aCwgaGVpZ2h0LCB0b3A6IDAsIHJpZ2h0OiB3aWR0aCwgYm90dG9tOiBoZWlnaHQsIGxlZnQ6IDB9O1xyXG4gICAgICAgIHNjcm9sbGFibGVQYXJlbnRTY3JvbGxXaWR0aCA9IGdldERvY3VtZW50U2Nyb2xsV2lkdGgoKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgICAgc2Nyb2xsTm9kZSA9IHNjcm9sbGFibGVQYXJlbnQgYXMgSFRNTEVsZW1lbnQ7XHJcbiAgICAgICAgc2Nyb2xsYWJsZVBhcmVudENsaWVudFJlY3QgPSBnZXRNdXRhYmxlQ2xpZW50UmVjdChzY3JvbGxhYmxlUGFyZW50IGFzIEhUTUxFbGVtZW50KTtcclxuICAgICAgICBzY3JvbGxhYmxlUGFyZW50U2Nyb2xsV2lkdGggPSAoc2Nyb2xsYWJsZVBhcmVudCBhcyBIVE1MRWxlbWVudCkuc2Nyb2xsV2lkdGg7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBJTVBPUlRBTlQ6IEJ5IGRlc2lnbiwgb25seSBsZXQgc2Nyb2xsIGhvcml6b250YWwgaWYgdGhlIHNjcm9sbGFibGUgcGFyZW50IGhhcyBleHBsaWNpdGx5IGFuIHNjcm9sbCBob3Jpem9udGFsLlxyXG4gICAgICogVGhpcyBsYXlvdXQgc29sdXRpb24gaXMgbm90IGRlc2lnbmVkIGluIG1pbmQgdG8gaGF2ZSBhbnkgc2Nyb2xsIGhvcml6b250YWwsIGJ1dCBleGNlcHRpb25hbGx5IHdlIGFsbG93IGl0IGluIHRoaXNcclxuICAgICAqIHNwZWNpZmljIHVzZSBjYXNlLlxyXG4gICAgICovXHJcbiAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcclxuICAgIGlmIChvcHRpb25zLmRpc2FibGVIb3Jpem9udGFsID09IG51bGwgJiYgc2Nyb2xsYWJsZVBhcmVudFNjcm9sbFdpZHRoIDw9IHNjcm9sbGFibGVQYXJlbnRDbGllbnRSZWN0LndpZHRoKSB7XHJcbiAgICAgICAgb3B0aW9ucy5kaXNhYmxlSG9yaXpvbnRhbCA9IHRydWU7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIChzb3VyY2UkKSA9PiBzb3VyY2UkLnBpcGUoXHJcbiAgICAgICAgbWFwKCh7cG9pbnRlclgsIHBvaW50ZXJZfSkgPT4ge1xyXG4gICAgICAgICAgICBsZXQgdmVydGljYWxTY3JvbGxEaXJlY3Rpb24gPSBnZXRWZXJ0aWNhbFNjcm9sbERpcmVjdGlvbihzY3JvbGxhYmxlUGFyZW50Q2xpZW50UmVjdCwgcG9pbnRlclkpO1xyXG4gICAgICAgICAgICBsZXQgaG9yaXpvbnRhbFNjcm9sbERpcmVjdGlvbiA9IGdldEhvcml6b250YWxTY3JvbGxEaXJlY3Rpb24oc2Nyb2xsYWJsZVBhcmVudENsaWVudFJlY3QsIHBvaW50ZXJYKTtcclxuXHJcbiAgICAgICAgICAgIC8vIENoZWNrIGlmIHNjcm9sbCBkaXJlY3Rpb25zIGFyZSBkaXNhYmxlZC5cclxuICAgICAgICAgICAgaWYgKG9wdGlvbnM/LmRpc2FibGVWZXJ0aWNhbCkge1xyXG4gICAgICAgICAgICAgICAgdmVydGljYWxTY3JvbGxEaXJlY3Rpb24gPSBBdXRvU2Nyb2xsVmVydGljYWxEaXJlY3Rpb24uTk9ORTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBpZiAob3B0aW9ucz8uZGlzYWJsZUhvcml6b250YWwpIHtcclxuICAgICAgICAgICAgICAgIGhvcml6b250YWxTY3JvbGxEaXJlY3Rpb24gPSBBdXRvU2Nyb2xsSG9yaXpvbnRhbERpcmVjdGlvbi5OT05FO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICByZXR1cm4ge3ZlcnRpY2FsU2Nyb2xsRGlyZWN0aW9uLCBob3Jpem9udGFsU2Nyb2xsRGlyZWN0aW9ufTtcclxuICAgICAgICB9KSxcclxuICAgICAgICBkaXN0aW5jdFVudGlsQ2hhbmdlZCgocHJldiwgYWN0dWFsKSA9PiB7XHJcbiAgICAgICAgICAgIHJldHVybiBwcmV2LnZlcnRpY2FsU2Nyb2xsRGlyZWN0aW9uID09PSBhY3R1YWwudmVydGljYWxTY3JvbGxEaXJlY3Rpb25cclxuICAgICAgICAgICAgICAgICYmIHByZXYuaG9yaXpvbnRhbFNjcm9sbERpcmVjdGlvbiA9PT0gYWN0dWFsLmhvcml6b250YWxTY3JvbGxEaXJlY3Rpb247XHJcbiAgICAgICAgfSksXHJcbiAgICAgICAgc3dpdGNoTWFwKCh7dmVydGljYWxTY3JvbGxEaXJlY3Rpb24sIGhvcml6b250YWxTY3JvbGxEaXJlY3Rpb259KSA9PiB7XHJcbiAgICAgICAgICAgIGlmICh2ZXJ0aWNhbFNjcm9sbERpcmVjdGlvbiB8fCBob3Jpem9udGFsU2Nyb2xsRGlyZWN0aW9uKSB7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gc2Nyb2xsVG9EaXJlY3Rpb25JbnRlcnZhbCQoc2Nyb2xsTm9kZSwgdmVydGljYWxTY3JvbGxEaXJlY3Rpb24sIGhvcml6b250YWxTY3JvbGxEaXJlY3Rpb24sIG9wdGlvbnM/LnNjcm9sbFN0ZXApO1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIE5FVkVSO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfSlcclxuICAgICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbWl0cyBvbiBFVkVSWSBzY3JvbGwgZXZlbnQgYW5kIHJldHVybnMgdGhlIGFjY3VtdWxhdGVkIHNjcm9sbCBvZmZzZXQgcmVsYXRpdmUgdG8gdGhlIGluaXRpYWwgc2Nyb2xsIHBvc2l0aW9uLlxyXG4gKiBAcGFyYW0gc2Nyb2xsYWJsZVBhcmVudCwgbm9kZSBpbiB3aGljaCBzY3JvbGwgZXZlbnRzIHdvdWxkIGJlIGxpc3RlbmVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGt0ZEdldFNjcm9sbFRvdGFsUmVsYXRpdmVEaWZmZXJlbmNlJChzY3JvbGxhYmxlUGFyZW50OiBIVE1MRWxlbWVudCB8IERvY3VtZW50KTogT2JzZXJ2YWJsZTx7IHRvcDogbnVtYmVyLCBsZWZ0OiBudW1iZXIgfT4ge1xyXG4gICAgbGV0IHNjcm9sbEluaXRpYWxQb3NpdGlvbjtcclxuXHJcbiAgICAvLyBDYWxjdWxhdGUgaW5pdGlhbCBzY3JvbGwgcG9zaXRpb25cclxuICAgIGlmIChzY3JvbGxhYmxlUGFyZW50ID09PSBkb2N1bWVudCkge1xyXG4gICAgICAgIHNjcm9sbEluaXRpYWxQb3NpdGlvbiA9IGdldFZpZXdwb3J0U2Nyb2xsUG9zaXRpb24oKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgICAgc2Nyb2xsSW5pdGlhbFBvc2l0aW9uID0ge1xyXG4gICAgICAgICAgICB0b3A6IChzY3JvbGxhYmxlUGFyZW50IGFzIEhUTUxFbGVtZW50KS5zY3JvbGxUb3AsXHJcbiAgICAgICAgICAgIGxlZnQ6IChzY3JvbGxhYmxlUGFyZW50IGFzIEhUTUxFbGVtZW50KS5zY3JvbGxMZWZ0XHJcbiAgICAgICAgfTtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gZnJvbUV2ZW50KHNjcm9sbGFibGVQYXJlbnQsICdzY3JvbGwnLCBrdGROb3JtYWxpemVQYXNzaXZlTGlzdGVuZXJPcHRpb25zKHtjYXB0dXJlOiB0cnVlfSkgYXMgQWRkRXZlbnRMaXN0ZW5lck9wdGlvbnMpLnBpcGUoXHJcbiAgICAgICAgbWFwKCgpID0+IHtcclxuICAgICAgICAgICAgbGV0IG5ld1RvcDogbnVtYmVyO1xyXG4gICAgICAgICAgICBsZXQgbmV3TGVmdDogbnVtYmVyO1xyXG5cclxuICAgICAgICAgICAgaWYgKHNjcm9sbGFibGVQYXJlbnQgPT09IGRvY3VtZW50KSB7XHJcbiAgICAgICAgICAgICAgICBjb25zdCB2aWV3cG9ydFNjcm9sbFBvc2l0aW9uID0gZ2V0Vmlld3BvcnRTY3JvbGxQb3NpdGlvbigpO1xyXG4gICAgICAgICAgICAgICAgbmV3VG9wID0gdmlld3BvcnRTY3JvbGxQb3NpdGlvbi50b3A7XHJcbiAgICAgICAgICAgICAgICBuZXdMZWZ0ID0gdmlld3BvcnRTY3JvbGxQb3NpdGlvbi5sZWZ0O1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgbmV3VG9wID0gKHNjcm9sbGFibGVQYXJlbnQgYXMgSFRNTEVsZW1lbnQpLnNjcm9sbFRvcDtcclxuICAgICAgICAgICAgICAgIG5ld0xlZnQgPSAoc2Nyb2xsYWJsZVBhcmVudCBhcyBIVE1MRWxlbWVudCkuc2Nyb2xsTGVmdDtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgY29uc3QgdG9wRGlmZmVyZW5jZSA9IHNjcm9sbEluaXRpYWxQb3NpdGlvbi50b3AgLSBuZXdUb3A7XHJcbiAgICAgICAgICAgIGNvbnN0IGxlZnREaWZmZXJlbmNlID0gc2Nyb2xsSW5pdGlhbFBvc2l0aW9uLmxlZnQgLSBuZXdMZWZ0O1xyXG5cclxuICAgICAgICAgICAgcmV0dXJuIHt0b3A6IHRvcERpZmZlcmVuY2UsIGxlZnQ6IGxlZnREaWZmZXJlbmNlfTtcclxuICAgICAgICB9KVxyXG4gICAgKTtcclxuXHJcbn1cclxuXHJcbi8qKiBSZXR1cm5zIHRoZSB2aWV3cG9ydCdzIHdpZHRoIGFuZCBoZWlnaHQuICovXHJcbmZ1bmN0aW9uIGdldFZpZXdwb3J0U2l6ZSgpOiB7IHdpZHRoOiBudW1iZXIsIGhlaWdodDogbnVtYmVyIH0ge1xyXG4gICAgY29uc3QgX3dpbmRvdyA9IGRvY3VtZW50LmRlZmF1bHRWaWV3IHx8IHdpbmRvdztcclxuICAgIHJldHVybiB7XHJcbiAgICAgICAgd2lkdGg6IF93aW5kb3cuaW5uZXJXaWR0aCxcclxuICAgICAgICBoZWlnaHQ6IF93aW5kb3cuaW5uZXJIZWlnaHRcclxuICAgIH07XHJcblxyXG59XHJcblxyXG4vKiogR2V0cyBhIENsaWVudFJlY3QgZm9yIHRoZSB2aWV3cG9ydCdzIGJvdW5kcy4gKi9cclxuZnVuY3Rpb24gZ2V0Vmlld3BvcnRSZWN0KCk6IEt0ZENsaWVudFJlY3Qge1xyXG4gICAgLy8gVXNlIHRoZSBkb2N1bWVudCBlbGVtZW50J3MgYm91bmRpbmcgcmVjdCByYXRoZXIgdGhhbiB0aGUgd2luZG93IHNjcm9sbCBwcm9wZXJ0aWVzXHJcbiAgICAvLyAoZS5nLiBwYWdlWU9mZnNldCwgc2Nyb2xsWSkgZHVlIHRvIGluIGlzc3VlIGluIENocm9tZSBhbmQgSUUgd2hlcmUgd2luZG93IHNjcm9sbFxyXG4gICAgLy8gcHJvcGVydGllcyBhbmQgY2xpZW50IGNvb3JkaW5hdGVzIChib3VuZGluZ0NsaWVudFJlY3QsIGNsaWVudFgvWSwgZXRjLikgYXJlIGluIGRpZmZlcmVudFxyXG4gICAgLy8gY29uY2VwdHVhbCB2aWV3cG9ydHMuIFVuZGVyIG1vc3QgY2lyY3Vtc3RhbmNlcyB0aGVzZSB2aWV3cG9ydHMgYXJlIGVxdWl2YWxlbnQsIGJ1dCB0aGV5XHJcbiAgICAvLyBjYW4gZGlzYWdyZWUgd2hlbiB0aGUgcGFnZSBpcyBwaW5jaC16b29tZWQgKG9uIGRldmljZXMgdGhhdCBzdXBwb3J0IHRvdWNoKS5cclxuICAgIC8vIFNlZSBodHRwczovL2J1Z3MuY2hyb21pdW0ub3JnL3AvY2hyb21pdW0vaXNzdWVzL2RldGFpbD9pZD00ODkyMDYjYzRcclxuICAgIC8vIFdlIHVzZSB0aGUgZG9jdW1lbnRFbGVtZW50IGluc3RlYWQgb2YgdGhlIGJvZHkgYmVjYXVzZSwgYnkgZGVmYXVsdCAod2l0aG91dCBhIGNzcyByZXNldClcclxuICAgIC8vIGJyb3dzZXJzIHR5cGljYWxseSBnaXZlIHRoZSBkb2N1bWVudCBib2R5IGFuIDhweCBtYXJnaW4sIHdoaWNoIGlzIG5vdCBpbmNsdWRlZCBpblxyXG4gICAgLy8gZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkuXHJcbiAgICBjb25zdCBzY3JvbGxQb3NpdGlvbiA9IGdldFZpZXdwb3J0U2Nyb2xsUG9zaXRpb24oKTtcclxuICAgIGNvbnN0IHt3aWR0aCwgaGVpZ2h0fSA9IGdldFZpZXdwb3J0U2l6ZSgpO1xyXG5cclxuICAgIHJldHVybiB7XHJcbiAgICAgICAgdG9wOiBzY3JvbGxQb3NpdGlvbi50b3AsXHJcbiAgICAgICAgbGVmdDogc2Nyb2xsUG9zaXRpb24ubGVmdCxcclxuICAgICAgICBib3R0b206IHNjcm9sbFBvc2l0aW9uLnRvcCArIGhlaWdodCxcclxuICAgICAgICByaWdodDogc2Nyb2xsUG9zaXRpb24ubGVmdCArIHdpZHRoLFxyXG4gICAgICAgIGhlaWdodCxcclxuICAgICAgICB3aWR0aCxcclxuICAgIH07XHJcbn1cclxuXHJcbi8qKiBHZXRzIHRoZSAodG9wLCBsZWZ0KSBzY3JvbGwgcG9zaXRpb24gb2YgdGhlIHZpZXdwb3J0LiAqL1xyXG5mdW5jdGlvbiBnZXRWaWV3cG9ydFNjcm9sbFBvc2l0aW9uKCk6IHsgdG9wOiBudW1iZXIsIGxlZnQ6IG51bWJlciB9IHtcclxuXHJcbiAgICAvLyBUaGUgdG9wLWxlZnQtY29ybmVyIG9mIHRoZSB2aWV3cG9ydCBpcyBkZXRlcm1pbmVkIGJ5IHRoZSBzY3JvbGwgcG9zaXRpb24gb2YgdGhlIGRvY3VtZW50XHJcbiAgICAvLyBib2R5LCBub3JtYWxseSBqdXN0IChzY3JvbGxMZWZ0LCBzY3JvbGxUb3ApLiBIb3dldmVyLCBDaHJvbWUgYW5kIEZpcmVmb3ggZGlzYWdyZWUgYWJvdXRcclxuICAgIC8vIHdoZXRoZXIgYGRvY3VtZW50LmJvZHlgIG9yIGBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnRgIGlzIHRoZSBzY3JvbGxlZCBlbGVtZW50LCBzbyByZWFkaW5nXHJcbiAgICAvLyBgc2Nyb2xsVG9wYCBhbmQgYHNjcm9sbExlZnRgIGlzIGluY29uc2lzdGVudC4gSG93ZXZlciwgdXNpbmcgdGhlIGJvdW5kaW5nIHJlY3Qgb2ZcclxuICAgIC8vIGBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnRgIHdvcmtzIGNvbnNpc3RlbnRseSwgd2hlcmUgdGhlIGB0b3BgIGFuZCBgbGVmdGAgdmFsdWVzIHdpbGxcclxuICAgIC8vIGVxdWFsIG5lZ2F0aXZlIHRoZSBzY3JvbGwgcG9zaXRpb24uXHJcbiAgICBjb25zdCB3aW5kb3dSZWYgPSBkb2N1bWVudC5kZWZhdWx0VmlldyB8fCB3aW5kb3c7XHJcbiAgICBjb25zdCBkb2N1bWVudEVsZW1lbnQgPSBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQhO1xyXG4gICAgY29uc3QgZG9jdW1lbnRSZWN0ID0gZG9jdW1lbnRFbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG5cclxuICAgIGNvbnN0IHRvcCA9IC1kb2N1bWVudFJlY3QudG9wIHx8IGRvY3VtZW50LmJvZHkuc2Nyb2xsVG9wIHx8IHdpbmRvd1JlZi5zY3JvbGxZIHx8XHJcbiAgICAgICAgZG9jdW1lbnRFbGVtZW50LnNjcm9sbFRvcCB8fCAwO1xyXG5cclxuICAgIGNvbnN0IGxlZnQgPSAtZG9jdW1lbnRSZWN0LmxlZnQgfHwgZG9jdW1lbnQuYm9keS5zY3JvbGxMZWZ0IHx8IHdpbmRvd1JlZi5zY3JvbGxYIHx8XHJcbiAgICAgICAgZG9jdW1lbnRFbGVtZW50LnNjcm9sbExlZnQgfHwgMDtcclxuXHJcbiAgICByZXR1cm4ge3RvcCwgbGVmdH07XHJcbn1cclxuXHJcbi8qKiBSZXR1cm5zIHRoZSBkb2N1bWVudCBzY3JvbGwgd2lkdGggKi9cclxuZnVuY3Rpb24gZ2V0RG9jdW1lbnRTY3JvbGxXaWR0aCgpIHtcclxuICAgIHJldHVybiBNYXRoLm1heChkb2N1bWVudC5ib2R5LnNjcm9sbFdpZHRoLCBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuc2Nyb2xsV2lkdGgpO1xyXG59XHJcblxyXG4iXX0=