@qogni/dygraphs
Version:
dygraphs is a fast, flexible open source JavaScript charting library.
712 lines (668 loc) • 96.8 kB
JavaScript
/**
* @license
* Copyright 2011 Robert Konigsberg (konigsberg@google.com)
* MIT-licenced: https://opensource.org/licenses/MIT
*/
/**
* @fileoverview The default interaction model for Dygraphs. This is kept out
* of dygraph.js for better navigability.
* @author Robert Konigsberg (konigsberg@google.com)
*/
/*global Dygraph:false */
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var utils = _interopRequireWildcard(require("./dygraph-utils"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
/**
* You can drag this many pixels past the edge of the chart and still have it
* be considered a zoom. This makes it easier to zoom to the exact edge of the
* chart, a fairly common operation.
*/
var DRAG_EDGE_MARGIN = 100;
/**
* A collection of functions to facilitate build custom interaction models.
* @class
*/
var DygraphInteraction = {};
/**
* Checks whether the beginning & ending of an event were close enough that it
* should be considered a click. If it should, dispatch appropriate events.
* Returns true if the event was treated as a click.
*
* @param {Event} event
* @param {Dygraph} g
* @param {Object} context
*/
DygraphInteraction.maybeTreatMouseOpAsClick = function (event, g, context) {
context.dragEndX = utils.dragGetX_(event, context);
context.dragEndY = utils.dragGetY_(event, context);
var regionWidth = Math.abs(context.dragEndX - context.dragStartX);
var regionHeight = Math.abs(context.dragEndY - context.dragStartY);
if (regionWidth < 2 && regionHeight < 2 && g.lastx_ !== undefined && g.lastx_ !== null) {
DygraphInteraction.treatMouseOpAsClick(g, event, context);
}
context.regionWidth = regionWidth;
context.regionHeight = regionHeight;
};
/**
* Called in response to an interaction model operation that
* should start the default panning behavior.
*
* It's used in the default callback for "mousedown" operations.
* Custom interaction model builders can use it to provide the default
* panning behavior.
*
* @param {Event} event the event object which led to the startPan call.
* @param {Dygraph} g The dygraph on which to act.
* @param {Object} context The dragging context object (with
* dragStartX/dragStartY/etc. properties). This function modifies the
* context.
*/
DygraphInteraction.startPan = function (event, g, context) {
var i, axis;
context.isPanning = true;
var xRange = g.xAxisRange();
if (g.getOptionForAxis("logscale", "x")) {
context.initialLeftmostDate = utils.log10(xRange[0]);
context.dateRange = utils.log10(xRange[1]) - utils.log10(xRange[0]);
} else {
context.initialLeftmostDate = xRange[0];
context.dateRange = xRange[1] - xRange[0];
}
context.xUnitsPerPixel = context.dateRange / (g.plotter_.area.w - 1);
if (g.getNumericOption("panEdgeFraction")) {
var maxXPixelsToDraw = g.width_ * g.getNumericOption("panEdgeFraction");
var xExtremes = g.xAxisExtremes(); // I REALLY WANT TO CALL THIS xTremes!
var boundedLeftX = g.toDomXCoord(xExtremes[0]) - maxXPixelsToDraw;
var boundedRightX = g.toDomXCoord(xExtremes[1]) + maxXPixelsToDraw;
var boundedLeftDate = g.toDataXCoord(boundedLeftX);
var boundedRightDate = g.toDataXCoord(boundedRightX);
context.boundedDates = [boundedLeftDate, boundedRightDate];
var boundedValues = [];
var maxYPixelsToDraw = g.height_ * g.getNumericOption("panEdgeFraction");
for (i = 0; i < g.axes_.length; i++) {
axis = g.axes_[i];
var yExtremes = axis.extremeRange;
var boundedTopY = g.toDomYCoord(yExtremes[0], i) + maxYPixelsToDraw;
var boundedBottomY = g.toDomYCoord(yExtremes[1], i) - maxYPixelsToDraw;
var boundedTopValue = g.toDataYCoord(boundedTopY, i);
var boundedBottomValue = g.toDataYCoord(boundedBottomY, i);
boundedValues[i] = [boundedTopValue, boundedBottomValue];
}
context.boundedValues = boundedValues;
} else {
// undo effect if it was once set
context.boundedDates = null;
context.boundedValues = null;
}
// Record the range of each y-axis at the start of the drag.
// If any axis has a valueRange, then we want a 2D pan.
// We can't store data directly in g.axes_, because it does not belong to us
// and could change out from under us during a pan (say if there's a data
// update).
context.is2DPan = false;
context.axes = [];
for (i = 0; i < g.axes_.length; i++) {
axis = g.axes_[i];
var axis_data = {};
var yRange = g.yAxisRange(i);
// TODO(konigsberg): These values should be in |context|.
// In log scale, initialTopValue, dragValueRange and unitsPerPixel are log scale.
var logscale = g.attributes_.getForAxis("logscale", i);
if (logscale) {
axis_data.initialTopValue = utils.log10(yRange[1]);
axis_data.dragValueRange = utils.log10(yRange[1]) - utils.log10(yRange[0]);
} else {
axis_data.initialTopValue = yRange[1];
axis_data.dragValueRange = yRange[1] - yRange[0];
}
axis_data.unitsPerPixel = axis_data.dragValueRange / (g.plotter_.area.h - 1);
context.axes.push(axis_data);
// While calculating axes, set 2dpan.
if (axis.valueRange) context.is2DPan = true;
}
};
/**
* Called in response to an interaction model operation that
* responds to an event that pans the view.
*
* It's used in the default callback for "mousemove" operations.
* Custom interaction model builders can use it to provide the default
* panning behavior.
*
* @param {Event} event the event object which led to the movePan call.
* @param {Dygraph} g The dygraph on which to act.
* @param {Object} context The dragging context object (with
* dragStartX/dragStartY/etc. properties). This function modifies the
* context.
*/
DygraphInteraction.movePan = function (event, g, context) {
context.dragEndX = utils.dragGetX_(event, context);
context.dragEndY = utils.dragGetY_(event, context);
var minDate = context.initialLeftmostDate - (context.dragEndX - context.dragStartX) * context.xUnitsPerPixel;
if (context.boundedDates) {
minDate = Math.max(minDate, context.boundedDates[0]);
}
var maxDate = minDate + context.dateRange;
if (context.boundedDates) {
if (maxDate > context.boundedDates[1]) {
// Adjust minDate, and recompute maxDate.
minDate = minDate - (maxDate - context.boundedDates[1]);
maxDate = minDate + context.dateRange;
}
}
if (g.getOptionForAxis("logscale", "x")) {
g.dateWindow_ = [Math.pow(utils.LOG_SCALE, minDate), Math.pow(utils.LOG_SCALE, maxDate)];
} else {
g.dateWindow_ = [minDate, maxDate];
}
// y-axis scaling is automatic unless this is a full 2D pan.
if (context.is2DPan) {
var pixelsDragged = context.dragEndY - context.dragStartY;
// Adjust each axis appropriately.
for (var i = 0; i < g.axes_.length; i++) {
var axis = g.axes_[i];
var axis_data = context.axes[i];
var unitsDragged = pixelsDragged * axis_data.unitsPerPixel;
var boundedValue = context.boundedValues ? context.boundedValues[i] : null;
// In log scale, maxValue and minValue are the logs of those values.
var maxValue = axis_data.initialTopValue + unitsDragged;
if (boundedValue) {
maxValue = Math.min(maxValue, boundedValue[1]);
}
var minValue = maxValue - axis_data.dragValueRange;
if (boundedValue) {
if (minValue < boundedValue[0]) {
// Adjust maxValue, and recompute minValue.
maxValue = maxValue - (minValue - boundedValue[0]);
minValue = maxValue - axis_data.dragValueRange;
}
}
if (g.attributes_.getForAxis("logscale", i)) {
axis.valueRange = [Math.pow(utils.LOG_SCALE, minValue), Math.pow(utils.LOG_SCALE, maxValue)];
} else {
axis.valueRange = [minValue, maxValue];
}
}
}
g.drawGraph_(false);
};
/**
* Called in response to an interaction model operation that
* responds to an event that ends panning.
*
* It's used in the default callback for "mouseup" operations.
* Custom interaction model builders can use it to provide the default
* panning behavior.
*
* @param {Event} event the event object which led to the endPan call.
* @param {Dygraph} g The dygraph on which to act.
* @param {Object} context The dragging context object (with
* dragStartX/dragStartY/etc. properties). This function modifies the
* context.
*/
DygraphInteraction.endPan = DygraphInteraction.maybeTreatMouseOpAsClick;
/**
* Called in response to an interaction model operation that
* responds to an event that starts zooming.
*
* It's used in the default callback for "mousedown" operations.
* Custom interaction model builders can use it to provide the default
* zooming behavior.
*
* @param {Event} event the event object which led to the startZoom call.
* @param {Dygraph} g The dygraph on which to act.
* @param {Object} context The dragging context object (with
* dragStartX/dragStartY/etc. properties). This function modifies the
* context.
*/
DygraphInteraction.startZoom = function (event, g, context) {
context.isZooming = true;
context.zoomMoved = false;
};
/**
* Called in response to an interaction model operation that
* responds to an event that defines zoom boundaries.
*
* It's used in the default callback for "mousemove" operations.
* Custom interaction model builders can use it to provide the default
* zooming behavior.
*
* @param {Event} event the event object which led to the moveZoom call.
* @param {Dygraph} g The dygraph on which to act.
* @param {Object} context The dragging context object (with
* dragStartX/dragStartY/etc. properties). This function modifies the
* context.
*/
DygraphInteraction.moveZoom = function (event, g, context) {
context.zoomMoved = true;
context.dragEndX = utils.dragGetX_(event, context);
context.dragEndY = utils.dragGetY_(event, context);
var xDelta = Math.abs(context.dragStartX - context.dragEndX);
var yDelta = Math.abs(context.dragStartY - context.dragEndY);
// drag direction threshold for y axis is twice as large as x axis
context.dragDirection = xDelta < yDelta / 2 ? utils.VERTICAL : utils.HORIZONTAL;
g.drawZoomRect_(context.dragDirection, context.dragStartX, context.dragEndX, context.dragStartY, context.dragEndY, context.prevDragDirection, context.prevEndX, context.prevEndY);
context.prevEndX = context.dragEndX;
context.prevEndY = context.dragEndY;
context.prevDragDirection = context.dragDirection;
};
/**
* TODO(danvk): move this logic into dygraph.js
* @param {Dygraph} g
* @param {Event} event
* @param {Object} context
*/
DygraphInteraction.treatMouseOpAsClick = function (g, event, context) {
var clickCallback = g.getFunctionOption('clickCallback');
var pointClickCallback = g.getFunctionOption('pointClickCallback');
var selectedPoint = null;
// Find out if the click occurs on a point.
var closestIdx = -1;
var closestDistance = Number.MAX_VALUE;
// check if the click was on a particular point.
for (var i = 0; i < g.selPoints_.length; i++) {
var p = g.selPoints_[i];
var distance = Math.pow(p.canvasx - context.dragEndX, 2) + Math.pow(p.canvasy - context.dragEndY, 2);
if (!isNaN(distance) && (closestIdx == -1 || distance < closestDistance)) {
closestDistance = distance;
closestIdx = i;
}
}
// Allow any click within two pixels of the dot.
var radius = g.getNumericOption('highlightCircleSize') + 2;
if (closestDistance <= radius * radius) {
selectedPoint = g.selPoints_[closestIdx];
}
if (selectedPoint) {
var e = {
cancelable: true,
point: selectedPoint,
canvasx: context.dragEndX,
canvasy: context.dragEndY
};
var defaultPrevented = g.cascadeEvents_('pointClick', e);
if (defaultPrevented) {
// Note: this also prevents click / clickCallback from firing.
return;
}
if (pointClickCallback) {
pointClickCallback.call(g, event, selectedPoint);
}
}
var e = {
cancelable: true,
xval: g.lastx_,
// closest point by x value
pts: g.selPoints_,
canvasx: context.dragEndX,
canvasy: context.dragEndY
};
if (!g.cascadeEvents_('click', e)) {
if (clickCallback) {
// TODO(danvk): pass along more info about the points, e.g. 'x'
clickCallback.call(g, event, g.lastx_, g.selPoints_);
}
}
};
/**
* Called in response to an interaction model operation that
* responds to an event that performs a zoom based on previously defined
* bounds..
*
* It's used in the default callback for "mouseup" operations.
* Custom interaction model builders can use it to provide the default
* zooming behavior.
*
* @param {Event} event the event object which led to the endZoom call.
* @param {Dygraph} g The dygraph on which to end the zoom.
* @param {Object} context The dragging context object (with
* dragStartX/dragStartY/etc. properties). This function modifies the
* context.
*/
DygraphInteraction.endZoom = function (event, g, context) {
g.clearZoomRect_();
context.isZooming = false;
DygraphInteraction.maybeTreatMouseOpAsClick(event, g, context);
// The zoom rectangle is visibly clipped to the plot area, so its behavior
// should be as well.
// See http://code.google.com/p/dygraphs/issues/detail?id=280
var plotArea = g.getArea();
if (context.regionWidth >= 10 && context.dragDirection == utils.HORIZONTAL) {
var left = Math.min(context.dragStartX, context.dragEndX),
right = Math.max(context.dragStartX, context.dragEndX);
left = Math.max(left, plotArea.x);
right = Math.min(right, plotArea.x + plotArea.w);
if (left < right) {
g.doZoomX_(left, right);
}
context.cancelNextDblclick = true;
} else if (context.regionHeight >= 10 && context.dragDirection == utils.VERTICAL) {
var top = Math.min(context.dragStartY, context.dragEndY),
bottom = Math.max(context.dragStartY, context.dragEndY);
top = Math.max(top, plotArea.y);
bottom = Math.min(bottom, plotArea.y + plotArea.h);
if (top < bottom) {
g.doZoomY_(top, bottom);
}
context.cancelNextDblclick = true;
}
context.dragStartX = null;
context.dragStartY = null;
};
/**
* @private
*/
DygraphInteraction.startTouch = function (event, g, context) {
event.preventDefault(); // touch browsers are all nice.
if (event.touches.length > 1) {
// If the user ever puts two fingers down, it's not a double tap.
context.startTimeForDoubleTapMs = null;
}
var touches = [];
for (var i = 0; i < event.touches.length; i++) {
var t = event.touches[i];
var rect = t.target.getBoundingClientRect();
// we dispense with 'dragGetX_' because all touchBrowsers support pageX
touches.push({
pageX: t.pageX,
pageY: t.pageY,
dataX: g.toDataXCoord(t.clientX - rect.left),
dataY: g.toDataYCoord(t.clientY - rect.top)
// identifier: t.identifier
});
}
context.initialTouches = touches;
if (touches.length == 1) {
// This is just a swipe.
context.initialPinchCenter = touches[0];
context.touchDirections = {
x: true,
y: true
};
} else if (touches.length >= 2) {
// It's become a pinch!
// In case there are 3+ touches, we ignore all but the "first" two.
// only screen coordinates can be averaged (data coords could be log scale).
context.initialPinchCenter = {
pageX: 0.5 * (touches[0].pageX + touches[1].pageX),
pageY: 0.5 * (touches[0].pageY + touches[1].pageY),
// TODO(danvk): remove
dataX: 0.5 * (touches[0].dataX + touches[1].dataX),
dataY: 0.5 * (touches[0].dataY + touches[1].dataY)
};
// Make pinches in a 45-degree swath around either axis 1-dimensional zooms.
var initialAngle = 180 / Math.PI * Math.atan2(context.initialPinchCenter.pageY - touches[0].pageY, touches[0].pageX - context.initialPinchCenter.pageX);
// use symmetry to get it into the first quadrant.
initialAngle = Math.abs(initialAngle);
if (initialAngle > 90) initialAngle = 90 - initialAngle;
context.touchDirections = {
x: initialAngle < 90 - 45 / 2,
y: initialAngle > 45 / 2
};
}
// save the full x & y ranges.
context.initialRange = {
x: g.xAxisRange(),
y: g.yAxisRange()
};
};
/**
* @private
*/
DygraphInteraction.moveTouch = function (event, g, context) {
// If the tap moves, then it's definitely not part of a double-tap.
context.startTimeForDoubleTapMs = null;
var i,
touches = [];
for (i = 0; i < event.touches.length; i++) {
var t = event.touches[i];
touches.push({
pageX: t.pageX,
pageY: t.pageY
});
}
var initialTouches = context.initialTouches;
var c_now;
// old and new centers.
var c_init = context.initialPinchCenter;
if (touches.length == 1) {
c_now = touches[0];
} else {
c_now = {
pageX: 0.5 * (touches[0].pageX + touches[1].pageX),
pageY: 0.5 * (touches[0].pageY + touches[1].pageY)
};
}
// this is the "swipe" component
// we toss it out for now, but could use it in the future.
var swipe = {
pageX: c_now.pageX - c_init.pageX,
pageY: c_now.pageY - c_init.pageY
};
var dataWidth = context.initialRange.x[1] - context.initialRange.x[0];
var dataHeight = context.initialRange.y[0] - context.initialRange.y[1];
swipe.dataX = swipe.pageX / g.plotter_.area.w * dataWidth;
swipe.dataY = swipe.pageY / g.plotter_.area.h * dataHeight;
var xScale, yScale;
// The residual bits are usually split into scale & rotate bits, but we split
// them into x-scale and y-scale bits.
if (touches.length == 1) {
xScale = 1.0;
yScale = 1.0;
} else if (touches.length >= 2) {
var initHalfWidth = initialTouches[1].pageX - c_init.pageX;
xScale = (touches[1].pageX - c_now.pageX) / initHalfWidth;
var initHalfHeight = initialTouches[1].pageY - c_init.pageY;
yScale = (touches[1].pageY - c_now.pageY) / initHalfHeight;
}
// Clip scaling to [1/8, 8] to prevent too much blowup.
xScale = Math.min(8, Math.max(0.125, xScale));
yScale = Math.min(8, Math.max(0.125, yScale));
var didZoom = false;
if (context.touchDirections.x) {
var cFactor = c_init.dataX - swipe.dataX / xScale;
g.dateWindow_ = [cFactor + (context.initialRange.x[0] - c_init.dataX) / xScale, cFactor + (context.initialRange.x[1] - c_init.dataX) / xScale];
didZoom = true;
}
if (context.touchDirections.y) {
for (i = 0; i < 1 /*g.axes_.length*/; i++) {
var axis = g.axes_[i];
var logscale = g.attributes_.getForAxis("logscale", i);
if (logscale) {
// TODO(danvk): implement
} else {
var cFactor = c_init.dataY - swipe.dataY / yScale;
axis.valueRange = [cFactor + (context.initialRange.y[0] - c_init.dataY) / yScale, cFactor + (context.initialRange.y[1] - c_init.dataY) / yScale];
didZoom = true;
}
}
}
g.drawGraph_(false);
// We only call zoomCallback on zooms, not pans, to mirror desktop behavior.
if (didZoom && touches.length > 1 && g.getFunctionOption('zoomCallback')) {
var viewWindow = g.xAxisRange();
g.getFunctionOption("zoomCallback").call(g, viewWindow[0], viewWindow[1], g.yAxisRanges());
}
};
/**
* @private
*/
DygraphInteraction.endTouch = function (event, g, context) {
if (event.touches.length !== 0) {
// this is effectively a "reset"
DygraphInteraction.startTouch(event, g, context);
} else if (event.changedTouches.length == 1) {
// Could be part of a "double tap"
// The heuristic here is that it's a double-tap if the two touchend events
// occur within 500ms and within a 50x50 pixel box.
var now = new Date().getTime();
var t = event.changedTouches[0];
if (context.startTimeForDoubleTapMs && now - context.startTimeForDoubleTapMs < 500 && context.doubleTapX && Math.abs(context.doubleTapX - t.screenX) < 50 && context.doubleTapY && Math.abs(context.doubleTapY - t.screenY) < 50) {
g.resetZoom();
} else {
context.startTimeForDoubleTapMs = now;
context.doubleTapX = t.screenX;
context.doubleTapY = t.screenY;
}
}
};
// Determine the distance from x to [left, right].
var distanceFromInterval = function distanceFromInterval(x, left, right) {
if (x < left) {
return left - x;
} else if (x > right) {
return x - right;
} else {
return 0;
}
};
/**
* Returns the number of pixels by which the event happens from the nearest
* edge of the chart. For events in the interior of the chart, this returns zero.
*/
var distanceFromChart = function distanceFromChart(event, g) {
var chartPos = utils.findPos(g.canvas_);
var box = {
left: chartPos.x,
right: chartPos.x + g.canvas_.offsetWidth,
top: chartPos.y,
bottom: chartPos.y + g.canvas_.offsetHeight
};
var pt = {
x: utils.pageX(event),
y: utils.pageY(event)
};
var dx = distanceFromInterval(pt.x, box.left, box.right),
dy = distanceFromInterval(pt.y, box.top, box.bottom);
return Math.max(dx, dy);
};
/**
* Default interation model for dygraphs. You can refer to specific elements of
* this when constructing your own interaction model, e.g.:
* g.updateOptions( {
* interactionModel: {
* mousedown: DygraphInteraction.defaultInteractionModel.mousedown
* }
* } );
*/
DygraphInteraction.defaultModel = {
// Track the beginning of drag events
mousedown: function mousedown(event, g, context) {
// Right-click should not initiate a zoom.
if (event.button && event.button == 2) return;
context.initializeMouseDown(event, g, context);
if (event.altKey || event.shiftKey) {
DygraphInteraction.startPan(event, g, context);
} else {
DygraphInteraction.startZoom(event, g, context);
}
// Note: we register mousemove/mouseup on document to allow some leeway for
// events to move outside of the chart. Interaction model events get
// registered on the canvas, which is too small to allow this.
var mousemove = function mousemove(event) {
if (context.isZooming) {
// When the mouse moves >200px from the chart edge, cancel the zoom.
var d = distanceFromChart(event, g);
if (d < DRAG_EDGE_MARGIN) {
DygraphInteraction.moveZoom(event, g, context);
} else {
if (context.dragEndX !== null) {
context.dragEndX = null;
context.dragEndY = null;
g.clearZoomRect_();
}
}
} else if (context.isPanning) {
DygraphInteraction.movePan(event, g, context);
}
};
var _mouseup = function mouseup(event) {
if (context.isZooming) {
if (context.dragEndX !== null) {
DygraphInteraction.endZoom(event, g, context);
} else {
DygraphInteraction.maybeTreatMouseOpAsClick(event, g, context);
}
} else if (context.isPanning) {
DygraphInteraction.endPan(event, g, context);
}
utils.removeEvent(document, 'mousemove', mousemove);
utils.removeEvent(document, 'mouseup', _mouseup);
context.destroy();
};
g.addAndTrackEvent(document, 'mousemove', mousemove);
g.addAndTrackEvent(document, 'mouseup', _mouseup);
},
willDestroyContextMyself: true,
touchstart: function touchstart(event, g, context) {
DygraphInteraction.startTouch(event, g, context);
},
touchmove: function touchmove(event, g, context) {
DygraphInteraction.moveTouch(event, g, context);
},
touchend: function touchend(event, g, context) {
DygraphInteraction.endTouch(event, g, context);
},
// Disable zooming out if panning.
dblclick: function dblclick(event, g, context) {
if (context.cancelNextDblclick) {
context.cancelNextDblclick = false;
return;
}
// Give plugins a chance to grab this event.
var e = {
canvasx: context.dragEndX,
canvasy: context.dragEndY,
cancelable: true
};
if (g.cascadeEvents_('dblclick', e)) {
return;
}
if (event.altKey || event.shiftKey) {
return;
}
g.resetZoom();
}
};
/*
Dygraph.DEFAULT_ATTRS.interactionModel = DygraphInteraction.defaultModel;
// old ways of accessing these methods/properties
Dygraph.defaultInteractionModel = DygraphInteraction.defaultModel;
Dygraph.endZoom = DygraphInteraction.endZoom;
Dygraph.moveZoom = DygraphInteraction.moveZoom;
Dygraph.startZoom = DygraphInteraction.startZoom;
Dygraph.endPan = DygraphInteraction.endPan;
Dygraph.movePan = DygraphInteraction.movePan;
Dygraph.startPan = DygraphInteraction.startPan;
*/
DygraphInteraction.nonInteractiveModel_ = {
mousedown: function mousedown(event, g, context) {
context.initializeMouseDown(event, g, context);
},
mouseup: DygraphInteraction.maybeTreatMouseOpAsClick
};
// Default interaction model when using the range selector.
DygraphInteraction.dragIsPanInteractionModel = {
mousedown: function mousedown(event, g, context) {
context.initializeMouseDown(event, g, context);
DygraphInteraction.startPan(event, g, context);
},
mousemove: function mousemove(event, g, context) {
if (context.isPanning) {
DygraphInteraction.movePan(event, g, context);
}
},
mouseup: function mouseup(event, g, context) {
if (context.isPanning) {
DygraphInteraction.endPan(event, g, context);
}
}
};
var _default = exports["default"] = DygraphInteraction;
module.exports = exports.default;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJPYmplY3QiLCJkZWZpbmVQcm9wZXJ0eSIsImV4cG9ydHMiLCJ2YWx1ZSIsInV0aWxzIiwiX2ludGVyb3BSZXF1aXJlV2lsZGNhcmQiLCJyZXF1aXJlIiwiX2dldFJlcXVpcmVXaWxkY2FyZENhY2hlIiwiZSIsIldlYWtNYXAiLCJyIiwidCIsIl9fZXNNb2R1bGUiLCJoYXMiLCJnZXQiLCJuIiwiX19wcm90b19fIiwiYSIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsInUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJpIiwic2V0IiwiRFJBR19FREdFX01BUkdJTiIsIkR5Z3JhcGhJbnRlcmFjdGlvbiIsIm1heWJlVHJlYXRNb3VzZU9wQXNDbGljayIsImV2ZW50IiwiZyIsImNvbnRleHQiLCJkcmFnRW5kWCIsImRyYWdHZXRYXyIsImRyYWdFbmRZIiwiZHJhZ0dldFlfIiwicmVnaW9uV2lkdGgiLCJNYXRoIiwiYWJzIiwiZHJhZ1N0YXJ0WCIsInJlZ2lvbkhlaWdodCIsImRyYWdTdGFydFkiLCJsYXN0eF8iLCJ1bmRlZmluZWQiLCJ0cmVhdE1vdXNlT3BBc0NsaWNrIiwic3RhcnRQYW4iLCJheGlzIiwiaXNQYW5uaW5nIiwieFJhbmdlIiwieEF4aXNSYW5nZSIsImdldE9wdGlvbkZvckF4aXMiLCJpbml0aWFsTGVmdG1vc3REYXRlIiwibG9nMTAiLCJkYXRlUmFuZ2UiLCJ4VW5pdHNQZXJQaXhlbCIsInBsb3R0ZXJfIiwiYXJlYSIsInciLCJnZXROdW1lcmljT3B0aW9uIiwibWF4WFBpeGVsc1RvRHJhdyIsIndpZHRoXyIsInhFeHRyZW1lcyIsInhBeGlzRXh0cmVtZXMiLCJib3VuZGVkTGVmdFgiLCJ0b0RvbVhDb29yZCIsImJvdW5kZWRSaWdodFgiLCJib3VuZGVkTGVmdERhdGUiLCJ0b0RhdGFYQ29vcmQiLCJib3VuZGVkUmlnaHREYXRlIiwiYm91bmRlZERhdGVzIiwiYm91bmRlZFZhbHVlcyIsIm1heFlQaXhlbHNUb0RyYXciLCJoZWlnaHRfIiwiYXhlc18iLCJsZW5ndGgiLCJ5RXh0cmVtZXMiLCJleHRyZW1lUmFuZ2UiLCJib3VuZGVkVG9wWSIsInRvRG9tWUNvb3JkIiwiYm91bmRlZEJvdHRvbVkiLCJib3VuZGVkVG9wVmFsdWUiLCJ0b0RhdGFZQ29vcmQiLCJib3VuZGVkQm90dG9tVmFsdWUiLCJpczJEUGFuIiwiYXhlcyIsImF4aXNfZGF0YSIsInlSYW5nZSIsInlBeGlzUmFuZ2UiLCJsb2dzY2FsZSIsImF0dHJpYnV0ZXNfIiwiZ2V0Rm9yQXhpcyIsImluaXRpYWxUb3BWYWx1ZSIsImRyYWdWYWx1ZVJhbmdlIiwidW5pdHNQZXJQaXhlbCIsImgiLCJwdXNoIiwidmFsdWVSYW5nZSIsIm1vdmVQYW4iLCJtaW5EYXRlIiwibWF4IiwibWF4RGF0ZSIsImRhdGVXaW5kb3dfIiwicG93IiwiTE9HX1NDQUxFIiwicGl4ZWxzRHJhZ2dlZCIsInVuaXRzRHJhZ2dlZCIsImJvdW5kZWRWYWx1ZSIsIm1heFZhbHVlIiwibWluIiwibWluVmFsdWUiLCJkcmF3R3JhcGhfIiwiZW5kUGFuIiwic3RhcnRab29tIiwiaXNab29taW5nIiwiem9vbU1vdmVkIiwibW92ZVpvb20iLCJ4RGVsdGEiLCJ5RGVsdGEiLCJkcmFnRGlyZWN0aW9uIiwiVkVSVElDQUwiLCJIT1JJWk9OVEFMIiwiZHJhd1pvb21SZWN0XyIsInByZXZEcmFnRGlyZWN0aW9uIiwicHJldkVuZFgiLCJwcmV2RW5kWSIsImNsaWNrQ2FsbGJhY2siLCJnZXRGdW5jdGlvbk9wdGlvbiIsInBvaW50Q2xpY2tDYWxsYmFjayIsInNlbGVjdGVkUG9pbnQiLCJjbG9zZXN0SWR4IiwiY2xvc2VzdERpc3RhbmNlIiwiTnVtYmVyIiwiTUFYX1ZBTFVFIiwic2VsUG9pbnRzXyIsInAiLCJkaXN0YW5jZSIsImNhbnZhc3giLCJjYW52YXN5IiwiaXNOYU4iLCJyYWRpdXMiLCJjYW5jZWxhYmxlIiwicG9pbnQiLCJkZWZhdWx0UHJldmVudGVkIiwiY2FzY2FkZUV2ZW50c18iLCJ4dmFsIiwicHRzIiwiZW5kWm9vbSIsImNsZWFyWm9vbVJlY3RfIiwicGxvdEFyZWEiLCJnZXRBcmVhIiwibGVmdCIsInJpZ2h0IiwieCIsImRvWm9vbVhfIiwiY2FuY2VsTmV4dERibGNsaWNrIiwidG9wIiwiYm90dG9tIiwieSIsImRvWm9vbVlfIiwic3RhcnRUb3VjaCIsInByZXZlbnREZWZhdWx0IiwidG91Y2hlcyIsInN0YXJ0VGltZUZvckRvdWJsZVRhcE1zIiwicmVjdCIsInRhcmdldCIsImdldEJvdW5kaW5nQ2xpZW50UmVjdCIsInBhZ2VYIiwicGFnZVkiLCJkYXRhWCIsImNsaWVudFgiLCJkYXRhWSIsImNsaWVudFkiLCJpbml0aWFsVG91Y2hlcyIsImluaXRpYWxQaW5jaENlbnRlciIsInRvdWNoRGlyZWN0aW9ucyIsImluaXRpYWxBbmdsZSIsIlBJIiwiYXRhbjIiLCJpbml0aWFsUmFuZ2UiLCJtb3ZlVG91Y2giLCJjX25vdyIsImNfaW5pdCIsInN3aXBlIiwiZGF0YVdpZHRoIiwiZGF0YUhlaWdodCIsInhTY2FsZSIsInlTY2FsZSIsImluaXRIYWxmV2lkdGgiLCJpbml0SGFsZkhlaWdodCIsImRpZFpvb20iLCJjRmFjdG9yIiwidmlld1dpbmRvdyIsInlBeGlzUmFuZ2VzIiwiZW5kVG91Y2giLCJjaGFuZ2VkVG91Y2hlcyIsIm5vdyIsIkRhdGUiLCJnZXRUaW1lIiwiZG91YmxlVGFwWCIsInNjcmVlblgiLCJkb3VibGVUYXBZIiwic2NyZWVuWSIsInJlc2V0Wm9vbSIsImRpc3RhbmNlRnJvbUludGVydmFsIiwiZGlzdGFuY2VGcm9tQ2hhcnQiLCJjaGFydFBvcyIsImZpbmRQb3MiLCJjYW52YXNfIiwiYm94Iiwib2Zmc2V0V2lkdGgiLCJvZmZzZXRIZWlnaHQiLCJwdCIsImR4IiwiZHkiLCJkZWZhdWx0TW9kZWwiLCJtb3VzZWRvd24iLCJidXR0b24iLCJpbml0aWFsaXplTW91c2VEb3duIiwiYWx0S2V5Iiwic2hpZnRLZXkiLCJtb3VzZW1vdmUiLCJkIiwibW91c2V1cCIsInJlbW92ZUV2ZW50IiwiZG9jdW1lbnQiLCJkZXN0cm95IiwiYWRkQW5kVHJhY2tFdmVudCIsIndpbGxEZXN0cm95Q29udGV4dE15c2VsZiIsInRvdWNoc3RhcnQiLCJ0b3VjaG1vdmUiLCJ0b3VjaGVuZCIsImRibGNsaWNrIiwibm9uSW50ZXJhY3RpdmVNb2RlbF8iLCJkcmFnSXNQYW5JbnRlcmFjdGlvbk1vZGVsIiwiX2RlZmF1bHQiLCJtb2R1bGUiLCJkZWZhdWx0Il0sInNvdXJjZXMiOlsiLi4vc3JjL2R5Z3JhcGgtaW50ZXJhY3Rpb24tbW9kZWwuanMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IDIwMTEgUm9iZXJ0IEtvbmlnc2JlcmcgKGtvbmlnc2JlcmdAZ29vZ2xlLmNvbSlcbiAqIE1JVC1saWNlbmNlZDogaHR0cHM6Ly9vcGVuc291cmNlLm9yZy9saWNlbnNlcy9NSVRcbiAqL1xuXG4vKipcbiAqIEBmaWxlb3ZlcnZpZXcgVGhlIGRlZmF1bHQgaW50ZXJhY3Rpb24gbW9kZWwgZm9yIER5Z3JhcGhzLiBUaGlzIGlzIGtlcHQgb3V0XG4gKiBvZiBkeWdyYXBoLmpzIGZvciBiZXR0ZXIgbmF2aWdhYmlsaXR5LlxuICogQGF1dGhvciBSb2JlcnQgS29uaWdzYmVyZyAoa29uaWdzYmVyZ0Bnb29nbGUuY29tKVxuICovXG5cbi8qZ2xvYmFsIER5Z3JhcGg6ZmFsc2UgKi9cblwidXNlIHN0cmljdFwiO1xuXG5pbXBvcnQgKiBhcyB1dGlscyBmcm9tICcuL2R5Z3JhcGgtdXRpbHMnO1xuXG4vKipcbiAqIFlvdSBjYW4gZHJhZyB0aGlzIG1hbnkgcGl4ZWxzIHBhc3QgdGhlIGVkZ2Ugb2YgdGhlIGNoYXJ0IGFuZCBzdGlsbCBoYXZlIGl0XG4gKiBiZSBjb25zaWRlcmVkIGEgem9vbS4gVGhpcyBtYWtlcyBpdCBlYXNpZXIgdG8gem9vbSB0byB0aGUgZXhhY3QgZWRnZSBvZiB0aGVcbiAqIGNoYXJ0LCBhIGZhaXJseSBjb21tb24gb3BlcmF0aW9uLlxuICovXG52YXIgRFJBR19FREdFX01BUkdJTiA9IDEwMDtcblxuLyoqXG4gKiBBIGNvbGxlY3Rpb24gb2YgZnVuY3Rpb25zIHRvIGZhY2lsaXRhdGUgYnVpbGQgY3VzdG9tIGludGVyYWN0aW9uIG1vZGVscy5cbiAqIEBjbGFzc1xuICovXG52YXIgRHlncmFwaEludGVyYWN0aW9uID0ge307XG5cbi8qKlxuICogQ2hlY2tzIHdoZXRoZXIgdGhlIGJlZ2lubmluZyAmIGVuZGluZyBvZiBhbiBldmVudCB3ZXJlIGNsb3NlIGVub3VnaCB0aGF0IGl0XG4gKiBzaG91bGQgYmUgY29uc2lkZXJlZCBhIGNsaWNrLiBJZiBpdCBzaG91bGQsIGRpc3BhdGNoIGFwcHJvcHJpYXRlIGV2ZW50cy5cbiAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgZXZlbnQgd2FzIHRyZWF0ZWQgYXMgYSBjbGljay5cbiAqXG4gKiBAcGFyYW0ge0V2ZW50fSBldmVudFxuICogQHBhcmFtIHtEeWdyYXBofSBnXG4gKiBAcGFyYW0ge09iamVjdH0gY29udGV4dFxuICovXG5EeWdyYXBoSW50ZXJhY3Rpb24ubWF5YmVUcmVhdE1vdXNlT3BBc0NsaWNrID0gZnVuY3Rpb24oZXZlbnQsIGcsIGNvbnRleHQpIHtcbiAgY29udGV4dC5kcmFnRW5kWCA9IHV0aWxzLmRyYWdHZXRYXyhldmVudCwgY29udGV4dCk7XG4gIGNvbnRleHQuZHJhZ0VuZFkgPSB1dGlscy5kcmFnR2V0WV8oZXZlbnQsIGNvbnRleHQpO1xuICB2YXIgcmVnaW9uV2lkdGggPSBNYXRoLmFicyhjb250ZXh0LmRyYWdFbmRYIC0gY29udGV4dC5kcmFnU3RhcnRYKTtcbiAgdmFyIHJlZ2lvbkhlaWdodCA9IE1hdGguYWJzKGNvbnRleHQuZHJhZ0VuZFkgLSBjb250ZXh0LmRyYWdTdGFydFkpO1xuXG4gIGlmIChyZWdpb25XaWR0aCA8IDIgJiYgcmVnaW9uSGVpZ2h0IDwgMiAmJlxuICAgICAgZy5sYXN0eF8gIT09IHVuZGVmaW5lZCAmJiBnLmxhc3R4XyAhPT0gbnVsbCkge1xuICAgIER5Z3JhcGhJbnRlcmFjdGlvbi50cmVhdE1vdXNlT3BBc0NsaWNrKGcsIGV2ZW50LCBjb250ZXh0KTtcbiAgfVxuXG4gIGNvbnRleHQucmVnaW9uV2lkdGggPSByZWdpb25XaWR0aDtcbiAgY29udGV4dC5yZWdpb25IZWlnaHQgPSByZWdpb25IZWlnaHQ7XG59O1xuXG4vKipcbiAqIENhbGxlZCBpbiByZXNwb25zZSB0byBhbiBpbnRlcmFjdGlvbiBtb2RlbCBvcGVyYXRpb24gdGhhdFxuICogc2hvdWxkIHN0YXJ0IHRoZSBkZWZhdWx0IHBhbm5pbmcgYmVoYXZpb3IuXG4gKlxuICogSXQncyB1c2VkIGluIHRoZSBkZWZhdWx0IGNhbGxiYWNrIGZvciBcIm1vdXNlZG93blwiIG9wZXJhdGlvbnMuXG4gKiBDdXN0b20gaW50ZXJhY3Rpb24gbW9kZWwgYnVpbGRlcnMgY2FuIHVzZSBpdCB0byBwcm92aWRlIHRoZSBkZWZhdWx0XG4gKiBwYW5uaW5nIGJlaGF2aW9yLlxuICpcbiAqIEBwYXJhbSB7RXZlbnR9IGV2ZW50IHRoZSBldmVudCBvYmplY3Qgd2hpY2ggbGVkIHRvIHRoZSBzdGFydFBhbiBjYWxsLlxuICogQHBhcmFtIHtEeWdyYXBofSBnIFRoZSBkeWdyYXBoIG9uIHdoaWNoIHRvIGFjdC5cbiAqIEBwYXJhbSB7T2JqZWN0fSBjb250ZXh0IFRoZSBkcmFnZ2luZyBjb250ZXh0IG9iamVjdCAod2l0aFxuICogICAgIGRyYWdTdGFydFgvZHJhZ1N0YXJ0WS9ldGMuIHByb3BlcnRpZXMpLiBUaGlzIGZ1bmN0aW9uIG1vZGlmaWVzIHRoZVxuICogICAgIGNvbnRleHQuXG4gKi9cbkR5Z3JhcGhJbnRlcmFjdGlvbi5zdGFydFBhbiA9IGZ1bmN0aW9uKGV2ZW50LCBnLCBjb250ZXh0KSB7XG4gIHZhciBpLCBheGlzO1xuICBjb250ZXh0LmlzUGFubmluZyA9IHRydWU7XG4gIHZhciB4UmFuZ2UgPSBnLnhBeGlzUmFuZ2UoKTtcblxuICBpZiAoZy5nZXRPcHRpb25Gb3JBeGlzKFwibG9nc2NhbGVcIiwgXCJ4XCIpKSB7XG4gICAgY29udGV4dC5pbml0aWFsTGVmdG1vc3REYXRlID0gdXRpbHMubG9nMTAoeFJhbmdlWzBdKTtcbiAgICBjb250ZXh0LmRhdGVSYW5nZSA9IHV0aWxzLmxvZzEwKHhSYW5nZVsxXSkgLSB1dGlscy5sb2cxMCh4UmFuZ2VbMF0pO1xuICB9IGVsc2Uge1xuICAgIGNvbnRleHQuaW5pdGlhbExlZnRtb3N0RGF0ZSA9IHhSYW5nZVswXTtcbiAgICBjb250ZXh0LmRhdGVSYW5nZSA9IHhSYW5nZVsxXSAtIHhSYW5nZVswXTtcbiAgfVxuICBjb250ZXh0LnhVbml0c1BlclBpeGVsID0gY29udGV4dC5kYXRlUmFuZ2UgLyAoZy5wbG90dGVyXy5hcmVhLncgLSAxKTtcblxuICBpZiAoZy5nZXROdW1lcmljT3B0aW9uKFwicGFuRWRnZUZyYWN0aW9uXCIpKSB7XG4gICAgdmFyIG1heFhQaXhlbHNUb0RyYXcgPSBnLndpZHRoXyAqIGcuZ2V0TnVtZXJpY09wdGlvbihcInBhbkVkZ2VGcmFjdGlvblwiKTtcbiAgICB2YXIgeEV4dHJlbWVzID0gZy54QXhpc0V4dHJlbWVzKCk7IC8vIEkgUkVBTExZIFdBTlQgVE8gQ0FMTCBUSElTIHhUcmVtZXMhXG5cbiAgICB2YXIgYm91bmRlZExlZnRYID0gZy50b0RvbVhDb29yZCh4RXh0cmVtZXNbMF0pIC0gbWF4WFBpeGVsc1RvRHJhdztcbiAgICB2YXIgYm91bmRlZFJpZ2h0WCA9IGcudG9Eb21YQ29vcmQoeEV4dHJlbWVzWzFdKSArIG1heFhQaXhlbHNUb0RyYXc7XG5cbiAgICB2YXIgYm91bmRlZExlZnREYXRlID0gZy50b0RhdGFYQ29vcmQoYm91bmRlZExlZnRYKTtcbiAgICB2YXIgYm91bmRlZFJpZ2h0RGF0ZSA9IGcudG9EYXRhWENvb3JkKGJvdW5kZWRSaWdodFgpO1xuICAgIGNvbnRleHQuYm91bmRlZERhdGVzID0gW2JvdW5kZWRMZWZ0RGF0ZSwgYm91bmRlZFJpZ2h0RGF0ZV07XG5cbiAgICB2YXIgYm91bmRlZFZhbHVlcyA9IFtdO1xuICAgIHZhciBtYXhZUGl4ZWxzVG9EcmF3ID0gZy5oZWlnaHRfICogZy5nZXROdW1lcmljT3B0aW9uKFwicGFuRWRnZUZyYWN0aW9uXCIpO1xuXG4gICAgZm9yIChpID0gMDsgaSA8IGcuYXhlc18ubGVuZ3RoOyBpKyspIHtcbiAgICAgIGF4aXMgPSBnLmF4ZXNfW2ldO1xuICAgICAgdmFyIHlFeHRyZW1lcyA9IGF4aXMuZXh0cmVtZVJhbmdlO1xuXG4gICAgICB2YXIgYm91bmRlZFRvcFkgPSBnLnRvRG9tWUNvb3JkKHlFeHRyZW1lc1swXSwgaSkgKyBtYXhZUGl4ZWxzVG9EcmF3O1xuICAgICAgdmFyIGJvdW5kZWRCb3R0b21ZID0gZy50b0RvbVlDb29yZCh5RXh0cmVtZXNbMV0sIGkpIC0gbWF4WVBpeGVsc1RvRHJhdztcblxuICAgICAgdmFyIGJvdW5kZWRUb3BWYWx1ZSA9IGcudG9EYXRhWUNvb3JkKGJvdW5kZWRUb3BZLCBpKTtcbiAgICAgIHZhciBib3VuZGVkQm90dG9tVmFsdWUgPSBnLnRvRGF0YVlDb29yZChib3VuZGVkQm90dG9tWSwgaSk7XG5cbiAgICAgIGJvdW5kZWRWYWx1ZXNbaV0gPSBbYm91bmRlZFRvcFZhbHVlLCBib3VuZGVkQm90dG9tVmFsdWVdO1xuICAgIH1cbiAgICBjb250ZXh0LmJvdW5kZWRWYWx1ZXMgPSBib3VuZGVkVmFsdWVzO1xuICB9IGVsc2Uge1xuICAgIC8vIHVuZG8gZWZmZWN0IGlmIGl0IHdhcyBvbmNlIHNldFxuICAgIGNvbnRleHQuYm91bmRlZERhdGVzID0gbnVsbDtcbiAgICBjb250ZXh0LmJvdW5kZWRWYWx1ZXMgPSBudWxsO1xuICB9XG5cbiAgLy8gUmVjb3JkIHRoZSByYW5nZSBvZiBlYWNoIHktYXhpcyBhdCB0aGUgc3RhcnQgb2YgdGhlIGRyYWcuXG4gIC8vIElmIGFueSBheGlzIGhhcyBhIHZhbHVlUmFuZ2UsIHRoZW4gd2Ugd2FudCBhIDJEIHBhbi5cbiAgLy8gV2UgY2FuJ3Qgc3RvcmUgZGF0YSBkaXJlY3RseSBpbiBnLmF4ZXNfLCBiZWNhdXNlIGl0IGRvZXMgbm90IGJlbG9uZyB0byB1c1xuICAvLyBhbmQgY291bGQgY2hhbmdlIG91dCBmcm9tIHVuZGVyIHVzIGR1cmluZyBhIHBhbiAoc2F5IGlmIHRoZXJlJ3MgYSBkYXRhXG4gIC8vIHVwZGF0ZSkuXG4gIGNvbnRleHQuaXMyRFBhbiA9IGZhbHNlO1xuICBjb250ZXh0LmF4ZXMgPSBbXTtcbiAgZm9yIChpID0gMDsgaSA8IGcuYXhlc18ubGVuZ3RoOyBpKyspIHtcbiAgICBheGlzID0gZy5heGVzX1tpXTtcbiAgICB2YXIgYXhpc19kYXRhID0ge307XG4gICAgdmFyIHlSYW5nZSA9IGcueUF4aXNSYW5nZShpKTtcbiAgICAvLyBUT0RPKGtvbmlnc2JlcmcpOiBUaGVzZSB2YWx1ZXMgc2hvdWxkIGJlIGluIHxjb250ZXh0fC5cbiAgICAvLyBJbiBsb2cgc2NhbGUsIGluaXRpYWxUb3BWYWx1ZSwgZHJhZ1ZhbHVlUmFuZ2UgYW5kIHVuaXRzUGVyUGl4ZWwgYXJlIGxvZyBzY2FsZS5cbiAgICB2YXIgbG9nc2NhbGUgPSBnLmF0dHJpYnV0ZXNfLmdldEZvckF4aXMoXCJsb2dzY2FsZVwiLCBpKTtcbiAgICBpZiAobG9nc2NhbGUpIHtcbiAgICAgIGF4aXNfZGF0YS5pbml0aWFsVG9wVmFsdWUgPSB1dGlscy5sb2cxMCh5UmFuZ2VbMV0pO1xuICAgICAgYXhpc19kYXRhLmRyYWdWYWx1ZVJhbmdlID0gdXRpbHMubG9nMTAoeVJhbmdlWzFdKSAtIHV0aWxzLmxvZzEwKHlSYW5nZVswXSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGF4aXNfZGF0YS5pbml0aWFsVG9wVmFsdWUgPSB5UmFuZ2VbMV07XG4gICAgICBheGlzX2RhdGEuZHJhZ1ZhbHVlUmFuZ2UgPSB5UmFuZ2VbMV0gLSB5UmFuZ2VbMF07XG4gICAgfVxuICAgIGF4aXNfZGF0YS51bml0c1BlclBpeGVsID0gYXhpc19kYXRhLmRyYWdWYWx1ZVJhbmdlIC8gKGcucGxvdHRlcl8uYXJlYS5oIC0gMSk7XG4gICAgY29udGV4dC5heGVzLnB1c2goYXhpc19kYXRhKTtcblxuICAgIC8vIFdoaWxlIGNhbGN1bGF0aW5nIGF4ZXMsIHNldCAyZHBhbi5cbiAgICBpZiAoYXhpcy52YWx1ZVJhbmdlKSBjb250ZXh0LmlzMkRQYW4gPSB0cnVlO1xuICB9XG59O1xuXG4vKipcbiAqIENhbGxlZCBpbiByZXNwb25zZSB0byBhbiBpbnRlcmFjdGlvbiBtb2RlbCBvcGVyYXRpb24gdGhhdFxuICogcmVzcG9uZHMgdG8gYW4gZXZlbnQgdGhhdCBwYW5zIHRoZSB2aWV3LlxuICpcbiAqIEl0J3MgdXNlZCBpbiB0aGUgZGVmYXVsdCBjYWxsYmFjayBmb3IgXCJtb3VzZW1vdmVcIiBvcGVyYXRpb25zLlxuICogQ3VzdG9tIGludGVyYWN0aW9uIG1vZGVsIGJ1aWxkZXJzIGNhbiB1c2UgaXQgdG8gcHJvdmlkZSB0aGUgZGVmYXVsdFxuICogcGFubmluZyBiZWhhdmlvci5cbiAqXG4gKiBAcGFyYW0ge0V2ZW50fSBldmVudCB0aGUgZXZlbnQgb2JqZWN0IHdoaWNoIGxlZCB0byB0aGUgbW92ZVBhbiBjYWxsLlxuICogQHBhcmFtIHtEeWdyYXBofSBnIFRoZSBkeWdyYXBoIG9uIHdoaWNoIHRvIGFjdC5cbiAqIEBwYXJhbSB7T2JqZWN0fSBjb250ZXh0IFRoZSBkcmFnZ2luZyBjb250ZXh0IG9iamVjdCAod2l0aFxuICogICAgIGRyYWdTdGFydFgvZHJhZ1N0YXJ0WS9ldGMuIHByb3BlcnRpZXMpLiBUaGlzIGZ1bmN0aW9uIG1vZGlmaWVzIHRoZVxuICogICAgIGNvbnRleHQuXG4gKi9cbkR5Z3JhcGhJbnRlcmFjdGlvbi5tb3ZlUGFuID0gZnVuY3Rpb24oZXZlbnQsIGcsIGNvbnRleHQpIHtcbiAgY29udGV4dC5kcmFnRW5kWCA9IHV0aWxzLmRyYWdHZXRYXyhldmVudCwgY29udGV4dCk7XG4gIGNvbnRleHQuZHJhZ0VuZFkgPSB1dGlscy5kcmFnR2V0WV8oZXZlbnQsIGNvbnRleHQpO1xuXG4gIHZhciBtaW5EYXRlID0gY29udGV4dC5pbml0aWFsTGVmdG1vc3REYXRlIC1cbiAgICAoY29udGV4dC5kcmFnRW5kWCAtIGNvbnRleHQuZHJhZ1N0YXJ0WCkgKiBjb250ZXh0LnhVbml0c1BlclBpeGVsO1xuICBpZiAoY29udGV4dC5ib3VuZGVkRGF0ZXMpIHtcbiAgICBtaW5EYXRlID0gTWF0aC5tYXgobWluRGF0ZSwgY29udGV4dC5ib3VuZGVkRGF0ZXNbMF0pO1xuICB9XG4gIHZhciBtYXhEYXRlID0gbWluRGF0ZSArIGNvbnRleHQuZGF0ZVJhbmdlO1xuICBpZiAoY29udGV4dC5ib3VuZGVkRGF0ZXMpIHtcbiAgICBpZiAobWF4RGF0ZSA+IGNvbnRleHQuYm91bmRlZERhdGVzWzFdKSB7XG4gICAgICAvLyBBZGp1c3QgbWluRGF0ZSwgYW5kIHJlY29tcHV0ZSBtYXhEYXRlLlxuICAgICAgbWluRGF0ZSA9IG1pbkRhdGUgLSAobWF4RGF0ZSAtIGNvbnRleHQuYm91bmRlZERhdGVzWzFdKTtcbiAgICAgIG1heERhdGUgPSBtaW5EYXRlICsgY29udGV4dC5kYXRlUmFuZ2U7XG4gICAgfVxuICB9XG5cbiAgaWYgKGcuZ2V0T3B0aW9uRm9yQXhpcyhcImxvZ3NjYWxlXCIsIFwieFwiKSkge1xuICAgIGcuZGF0ZVdpbmRvd18gPSBbIE1hdGgucG93KHV0aWxzLkxPR19TQ0FMRSwgbWluRGF0ZSksXG4gICAgICAgICAgICAgICAgICAgICAgTWF0aC5wb3codXRpbHMuTE9HX1NDQUxFLCBtYXhEYXRlKSBdO1xuICB9IGVsc2Uge1xuICAgIGcuZGF0ZVdpbmRvd18gPSBbbWluRGF0ZSwgbWF4RGF0ZV07XG4gIH1cblxuICAvLyB5LWF4aXMgc2NhbGluZyBpcyBhdXRvbWF0aWMgdW5sZXNzIHRoaXMgaXMgYSBmdWxsIDJEIHBhbi5cbiAgaWYgKGNvbnRleHQuaXMyRFBhbikge1xuXG4gICAgdmFyIHBpeGVsc0RyYWdnZWQgPSBjb250ZXh0LmRyYWdFbmRZIC0gY29udGV4dC5kcmFnU3RhcnRZO1xuXG4gICAgLy8gQWRqdXN0IGVhY2ggYXhpcyBhcHByb3ByaWF0ZWx5LlxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgZy5heGVzXy5sZW5ndGg7IGkrKykge1xuICAgICAgdmFyIGF4aXMgPSBnLmF4ZXNfW2ldO1xuICAgICAgdmFyIGF4aXNfZGF0YSA9IGNvbnRleHQuYXhlc1tpXTtcbiAgICAgIHZhciB1bml0c0RyYWdnZWQgPSBwaXhlbHNEcmFnZ2VkICogYXhpc19kYXRhLnVuaXRzUGVyUGl4ZWw7XG5cbiAgICAgIHZhciBib3VuZGVkVmFsdWUgPSBjb250ZXh0LmJvdW5kZWRWYWx1ZXMgPyBjb250ZXh0LmJvdW5kZWRWYWx1ZXNbaV0gOiBudWxsO1xuXG4gICAgICAvLyBJbiBsb2cgc2NhbGUsIG1heFZhbHVlIGFuZCBtaW5WYWx1ZSBhcmUgdGhlIGxvZ3Mgb2YgdGhvc2UgdmFsdWVzLlxuICAgICAgdmFyIG1heFZhbHVlID0gYXhpc19kYXRhLmluaXRpYWxUb3BWYWx1ZSArIHVuaXRzRHJhZ2dlZDtcbiAgICAgIGlmIChib3VuZGVkVmFsdWUpIHtcbiAgICAgICAgbWF4VmFsdWUgPSBNYXRoLm1pbihtYXhWYWx1ZSwgYm91bmRlZFZhbHVlWzFdKTtcbiAgICAgIH1cbiAgICAgIHZhciBtaW5WYWx1ZSA9IG1heFZhbHVlIC0gYXhpc19kYXRhLmRyYWdWYWx1ZVJhbmdlO1xuICAgICAgaWYgKGJvdW5kZWRWYWx1ZSkge1xuICAgICAgICBpZiAobWluVmFsdWUgPCBib3VuZGVkVmFsdWVbMF0pIHtcbiAgICAgICAgICAvLyBBZGp1c3QgbWF4VmFsdWUsIGFuZCByZWNvbXB1dGUgbWluVmFsdWUuXG4gICAgICAgICAgbWF4VmFsdWUgPSBtYXhWYWx1ZSAtIChtaW5WYWx1ZSAtIGJvdW5kZWRWYWx1ZVswXSk7XG4gICAgICAgICAgbWluVmFsdWUgPSBtYXhWYWx1ZSAtIGF4aXNfZGF0YS5kcmFnVmFsdWVSYW5nZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaWYgKGcuYXR0cmlidXRlc18uZ2V0Rm9yQXhpcyhcImxvZ3NjYWxlXCIsIGkpKSB7XG4gICAgICAgIGF4aXMudmFsdWVSYW5nZSA9IFsgTWF0aC5wb3codXRpbHMuTE9HX1NDQUxFLCBtaW5WYWx1ZSksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgTWF0aC5wb3codXRpbHMuTE9HX1NDQUxFLCBtYXhWYWx1ZSkgXTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGF4aXMudmFsdWVSYW5nZSA9IFsgbWluVmFsdWUsIG1heFZhbHVlIF07XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgZy5kcmF3R3JhcGhfKGZhbHNlKTtcbn07XG5cbi8qKlxuICogQ2FsbGVkIGluIHJlc3BvbnNlIHRvIGFuIGludGVyYWN0aW9uIG1vZGVsIG9wZXJhdGlvbiB0aGF0XG4gKiByZXNwb25kcyB0byBhbiBldmVudCB0aGF0IGVuZHMgcGFubmluZy5cbiAqXG4gKiBJdCdzIHVzZWQgaW4gdGhlIGRlZmF1bHQgY2FsbGJhY2sgZm9yIFwibW91c2V1cFwiIG9wZXJhdGlvbnMuXG4gKiBDdXN0b20gaW50ZXJhY3Rpb24gbW9kZWwgYnVpbGRlcnMgY2FuIHVzZSBpdCB0byBwcm92aWRlIHRoZSBkZWZhdWx0XG4gKiBwYW5uaW5nIGJlaGF2aW9yLlxuICpcbiAqIEBwYXJhbSB7RXZlbnR9IGV2ZW50IHRoZSBldmVudCBvYmplY3Qgd2hpY2ggbGVkIHRvIHRoZSBlbmRQYW4gY2FsbC5cbiAqIEBwYXJhbSB7RHlncmFwaH0gZyBUaGUgZHlncmFwaCBvbiB3aGljaCB0byBhY3QuXG4gKiBAcGFyYW0ge09iamVjdH0gY29udGV4dCBUaGUgZHJhZ2dpbmcgY29udGV4dCBvYmplY3QgKHdpdGhcbiAqICAgICBkcmFnU3RhcnRYL2RyYWdTdGFydFkvZXRjLiBwcm9wZXJ0aWVzKS4gVGhpcyBmdW5jdGlvbiBtb2RpZmllcyB0aGVcbiAqICAgICBjb250ZXh0LlxuICovXG5EeWdyYXBoSW50ZXJhY3Rpb24uZW5kUGFuID0gRHlncmFwaEludGVyYWN0aW9uLm1heWJlVHJlYXRNb3VzZU9wQXNDbGljaztcblxuLyoqXG4gKiBDYWxsZWQgaW4gcmVzcG9uc2UgdG8gYW4gaW50ZXJhY3Rpb24gbW9kZWwgb3BlcmF0aW9uIHRoYXRcbiAqIHJlc3BvbmRzIHRvIGFuIGV2ZW50IHRoYXQgc3RhcnRzIHpvb21pbmcuXG4gKlxuICogSXQncyB1c2VkIGluIHRoZSBkZWZhdWx0IGNhbGxiYWNrIGZvciBcIm1vdXNlZG93blwiIG9wZXJhdGlvbnMuXG4gKiBDdXN0b20gaW50ZXJhY3Rpb24gbW9kZWwgYnVpbGRlcnMgY2FuIHVzZSBpdCB0byBwcm92aWRlIHRoZSBkZWZhdWx0XG4gKiB6b29taW5nIGJlaGF2aW9yLlxuICpcbiAqIEBwYXJhbSB7RXZlbnR9IGV2ZW50IHRoZSBldmVudCBvYmplY3Qgd2hpY2ggbGVkIHRvIHRoZSBzdGFydFpvb20gY2FsbC5cbiAqIEBwYXJhbSB7RHlncmFwaH0gZyBUaGUgZHlncmFwaCBvbiB3aGljaCB0byBhY3QuXG4gKiBAcGFyYW0ge09iamVjdH0gY29udGV4dCBUaGUgZHJhZ2dpbmcgY29udGV4dCBvYmplY3QgKHdpdGhcbiAqICAgICBkcmFnU3RhcnRYL2RyYWdTdGFydFkvZXRjLiBwcm9wZXJ0aWVzKS4gVGhpcyBmdW5jdGlvbiBtb2RpZmllcyB0aGVcbiAqICAgICBjb250ZXh0LlxuICovXG5EeWdyYXBoSW50ZXJhY3Rpb24uc3RhcnRab29tID0gZnVuY3Rpb24oZXZlbnQsIGcsIGNvbnRleHQpIHtcbiAgY29udGV4dC5pc1pvb21pbmcgPSB0cnVlO1xuICBjb250ZXh0Lnpvb21Nb3ZlZCA9IGZhbHNlO1xufTtcblxuLyoqXG4gKiBDYWxsZWQgaW4gcmVzcG9uc2UgdG8gYW4gaW50ZXJhY3Rpb24gbW9kZWwgb3BlcmF0aW9uIHRoYXRcbiAqIHJlc3BvbmRzIHRvIGFuIGV2ZW50IHRoYXQgZGVmaW5lcyB6b29tIGJvdW5kYXJpZXMuXG4gKlxuICogSXQncyB1c2VkIGluIHRoZSBkZWZhdWx0IGNhbGxiYWNrIGZvciBcIm1vdXNlbW92ZVwiIG9wZXJhdGlvbnMuXG4gKiBDdXN0b20gaW50ZXJhY3Rpb24gbW9kZWwgYnVpbGRlcnMgY2FuIHVzZSBpdCB0byBwcm92aWRlIHRoZSBkZWZhdWx0XG4gKiB6b29taW5nIGJlaGF2aW9yLlxuICpcbiAqIEBwYXJhbSB7RXZlbnR9IGV2ZW50IHRoZSBldmVudCBvYmplY3Qgd2hpY2ggbGVkIHRvIHRoZSBtb3ZlWm9vbSBjYWxsLlxuICogQHBhcmFtIHtEeWdyYXBofSBnIFRoZSBkeWdyYXBoIG9uIHdoaWNoIHRvIGFjdC5cbiAqIEBwYXJhbSB7T2JqZWN0fSBjb250ZXh0IFRoZSBkcmFnZ2luZyBjb250ZXh0IG9iamVjdCAod2l0aFxuICogICAgIGRyYWdTdGFydFgvZHJhZ1N0YXJ0WS9ldGMuIHByb3BlcnRpZXMpLiBUaGlzIGZ1bmN0aW9uIG1vZGlmaWVzIHRoZVxuICogICAgIGNvbnRleHQuXG4gKi9cbkR5Z3JhcGhJbnRlcmFjdGlvbi5tb3ZlWm9vbSA9IGZ1bmN0aW9uKGV2ZW50LCBnLCBjb250ZXh0KSB7XG4gIGNvbnRleHQuem9vbU1vdmVkID0gdHJ1ZTtcbiAgY29udGV4dC5kcmFnRW5kWCA9IHV0aWxzLmRyYWdHZXRYXyhldmVudCwgY29udGV4dCk7XG4gIGNvbnRleHQuZHJhZ0VuZFkgPSB1dGlscy5kcmFnR2V0WV8oZXZlbnQsIGNvbnRleHQpO1xuXG4gIHZhciB4RGVsdGEgPSBNYXRoLmFicyhjb250ZXh0LmRyYWdTdGFydFggLSBjb250ZXh0LmRyYWdFbmRYKTtcbiAgdmFyIHlEZWx0YSA9IE1hdGguYWJzKGNvbnRleHQuZHJhZ1N0YXJ0WSAtIGNvbnRleHQuZHJhZ0VuZFkpO1xuXG4gIC8vIGRyYWcgZGlyZWN0aW9uIHRocmVzaG9sZCBmb3IgeSBheGlzIGlzIHR3aWNlIGFzIGxhcmdlIGFzIHggYXhpc1xuICBjb250ZXh0LmRyYWdEaXJlY3Rpb24gPSAoeERlbHRhIDwgeURlbHRhIC8gMikgPyB1dGlscy5WRVJUSUNBTCA6IHV0aWxzLkhPUklaT05UQUw7XG5cbiAgZy5kcmF3Wm9vbVJlY3RfKFxuICAgICAgY29udGV4dC5kcmFnRGlyZWN0aW9uLFxuICAgICAgY29udGV4dC5kcmFnU3RhcnRYLFxuICAgICAgY29udGV4dC5kcmFnRW5kWCxcbiAgICAgIGNvbnRleHQuZHJhZ1N0YXJ0WSxcbiAgICAgIGNvbnRleHQuZHJhZ0VuZFksXG4gICAgICBjb250ZXh0LnByZXZEcmFnRGlyZWN0aW9uLFxuICAgICAgY29udGV4dC5wcmV2RW5kWCxcbiAgICAgIGNvbnRleHQucHJldkVuZFkpO1xuXG4gIGNvbnRleHQucHJldkVuZFggPSBjb250ZXh0LmRyYWdFbmRYO1xuICBjb250ZXh0LnByZXZFbmRZID0gY29udGV4dC5kcmFnRW5kWTtcbiAgY29udGV4dC5wcmV2RHJhZ0RpcmVjdGlvbiA9IGNvbnRleHQuZHJhZ0RpcmVjdGlvbjtcbn07XG5cbi8qKlxuICogVE9ETyhkYW52ayk6IG1vdmUgdGhpcyBsb2dpYyBpbnRvIGR5Z3JhcGguanNcbiAqIEBwYXJhbSB7RHlncmFwaH0gZ1xuICogQHBhcmFtIHtFdmVudH0gZXZlbnRcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb250ZXh0XG4gKi9cbkR5Z3JhcGhJbnRlcmFjdGlvbi50cmVhdE1vdXNlT3BBc0NsaWNrID0gZnVuY3Rpb24oZywgZXZlbnQsIGNvbnRleHQpIHtcbiAgdmFyIGNsaWNrQ2FsbGJhY2sgPSBnLmdldEZ1bmN0aW9uT3B0aW9uKCdjbGlja0NhbGxiYWNrJyk7XG4gIHZhciBwb2ludENsaWNrQ2FsbGJhY2sgPSBnLmdldEZ1bmN0aW9uT3B0aW9uKCdwb2ludENsaWNrQ2FsbGJhY2snKTtcblxuICB2YXIgc2VsZWN0ZWRQb2ludCA9IG51bGw7XG5cbiAgLy8gRmluZCBvdXQgaWYgdGhlIGNsaWNrIG9jY3VycyBvbiBhIHBvaW50LlxuICB2YXIgY2xvc2VzdElkeCA9IC0xO1xuICB2YXIgY2xvc2VzdERpc3RhbmNlID0gTnVtYmVyLk1BWF9WQUxVRTtcblxuICAvLyBjaGVjayBpZiB0aGUgY2xpY2sgd2FzIG9uIGEgcGFydGljdWxhciBwb2ludC5cbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBnLnNlbFBvaW50c18ubGVuZ3RoOyBpKyspIHtcbiAgICB2YXIgcCA9IGcuc2VsUG9pbnRzX1tpXTtcbiAgICB2YXIgZGlzdGFuY2UgPSBNYXRoLnBvdyhwLmNhbnZhc3ggLSBjb250ZXh0LmRyYWdFbmRYLCAyKSArXG4gICAgICAgICAgICAgICAgICAgTWF0aC5wb3cocC5jYW52YXN5IC0gY29udGV4dC5kcmFnRW5kWSwgMik7XG4gICAgaWYgKCFpc05hTihkaXN0YW5jZSkgJiZcbiAgICAgICAgKGNsb3Nlc3RJZHggPT0gLTEgfHwgZGlzdGFuY2UgPCBjbG9zZXN0RGlzdGFuY2UpKSB7XG4gICAgICBjbG9zZXN0RGlzdGFuY2UgPSBkaXN0YW5jZTtcbiAgICAgIGNsb3Nlc3RJZHggPSBpO1xuICAgIH1cbiAgfVxuXG4gIC8vIEFsbG93IGFueSBjbGljayB3aXRoaW4gdHdvIHBpeGVscyBvZiB0aGUgZG90LlxuICB2YXIgcmFkaXVzID0gZy5nZXROdW1lcmljT3B0aW9uKCdoaWdobGlnaHRDaXJjbGVTaXplJykgKyAyO1xuICBpZiAoY2xvc2VzdERpc3RhbmNlIDw9IHJhZGl1cyAqIHJhZGl1cykge1xuICAgIHNlbGVjdGVkUG9pbnQgPSBnLnNlbFBvaW50c19bY2xvc2VzdElkeF07XG4gIH1cblxuICBpZiAoc2VsZWN0ZWRQb2ludCkge1xuICAgIHZhciBlID0ge1xuICAgICAgY2FuY2VsYWJsZTogdHJ1ZSxcbiAgICAgIHBvaW50OiBzZWxlY3RlZFBvaW50LFxuICAgICAgY2FudmFzeDogY29udGV4dC5kcmFnRW5kWCxcbiAgICAgIGNhbnZhc3k6IGNvbnRleHQuZHJhZ0VuZFlcbiAgICB9O1xuICAgIHZhciBkZWZhdWx0UHJldmVudGVkID0gZy5jYXNjYWRlRXZlbnRzXygncG9pbnRDbGljaycsIGUpO1xuICAgIGlmIChkZWZhdWx0UHJldmVudGVkKSB7XG4gICAgICAvLyBOb3RlOiB0aGlzIGFsc28gcHJldmVudHMgY2xpY2sgLyBjbGlja0NhbGxiYWNrIGZyb20gZmlyaW5nLlxuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAocG9pbnRDbGlja0NhbGxiYWNrKSB7XG4gICAgICBwb2ludENsaWNrQ2FsbGJhY2suY2FsbChnLCBldmVudCwgc2VsZWN0ZWRQb2ludCk7XG4gICAgfVxuICB9XG5cbiAgdmFyIGUgPSB7XG4gICAgY2FuY2VsYWJsZTogdHJ1ZSxcbiAgICB4dmFsOiBnLmxhc3R4XywgIC8vIGNsb3Nlc3QgcG9pbnQgYnkgeCB2YWx1ZVxuICAgIHB0czogZy5zZWxQb2ludHNfLFxuICAgIGNhbnZhc3g6IGNvbnRleHQuZHJhZ0VuZFgsXG4gICAgY2FudmFzeTogY29udGV4dC5kcmFnRW5kWVxuICB9O1xuICBpZiAoIWcuY2FzY2FkZUV2ZW50c18oJ2NsaWNrJywgZSkpIHtcbiAgICBpZiAoY2xpY2tDYWxsYmFjaykge1xuICAgICAgLy8gVE9ETyhkYW52ayk6IHBhc3MgYWxvbmcgbW9yZSBpbmZvIGFib3V0IHRoZSBwb2ludHMsIGUuZy4gJ3gnXG4gICAgICBjbGlja0NhbGxiYWNrLmNhbGwoZywgZXZlbnQsIGcubGFzdHhfLCBnLnNlbFBvaW50c18pO1xuICAgIH1cbiAgfVxufTtcblxuLyoqXG4gKiBDYWxsZWQgaW4gcmVzcG9uc2UgdG8gYW4gaW50ZXJhY3Rpb24gbW9kZWwgb3BlcmF0aW9uIHRoYXRcbiAqIHJlc3BvbmRzIHRvIGFuIGV2ZW50IHRoYXQgcGVyZm9ybXMgYSB6b29tIGJhc2VkIG9uIHByZXZpb3VzbHkgZGVmaW5lZFxuICogYm91bmRzLi5cbiAqXG4gKiBJdCdzIHVzZWQgaW4gdGhlIGRlZmF1bHQgY2FsbGJhY2sgZm9yIFwibW91c2V1cFwiIG9wZXJhdGlvbnMuXG4gKiBDdXN0b20gaW50ZXJhY3Rpb24gbW9kZWwgYnVpbGRlcnMgY2FuIHVzZSBpdCB0byBwcm92aWRlIHRoZSBkZWZhdWx0XG4gKiB6b29taW5nIGJlaGF2aW9yLlxuICpcbiAqIEBwYXJhbSB7RXZlbnR9IGV2ZW50IHRoZSBldmVudCBvYmplY3Qgd2hpY2ggbGVkIHRvIHRoZSBlbmRab29tIGNhbGwuXG4gKiBAcGFyYW0ge0R5Z3JhcGh9IGcgVGhlIGR5Z3JhcGggb24gd2hpY2ggdG8gZW5kIHRoZSB6b29tLlxuICogQHBhcmFtIHtPYmplY3R9IGNvbnRleHQgVGhlIGRyYWdnaW5nIGNvbnRleHQgb2JqZWN0ICh3aXRoXG4gKiAgICAgZHJhZ1N0YXJ0WC9kcmFnU3RhcnRZL2V0Yy4gcHJvcGVydGllcykuIFRoaXMgZnVuY3Rpb24gbW9kaWZpZXMgdGhlXG4gKiAgICAgY29udGV4dC5cbiAqL1xuRHlncmFwaEludGVyYWN0aW9uLmVuZFpvb20gPSBmdW5jdGlvbihldmVudCwgZywgY29udGV4dCkge1xuICBnLmNsZWFyWm9vbVJlY3RfKCk7XG4gIGNvbnRleHQuaXNab29taW5nID0gZmFsc2U7XG4gIER5Z3JhcGhJbnRlcmFjdGlvbi5tYXliZVRyZWF0TW91c2VPcEFzQ2xpY2soZXZlbnQsIGcsIGNvbnRleHQpO1xuXG4gIC8vIFRoZSB6b29tIHJlY3RhbmdsZSBpcyB2aXNpYmx5IGNsaXBwZWQgdG8gdGhlIHBsb3QgYXJlYSwgc28gaXRzIGJlaGF2aW9yXG4gIC8vIHNob3VsZCBiZSBhcyB3ZWxsLlxuICAvLyBTZWUgaHR0cDovL2NvZGUuZ29vZ2xlLmNvbS9wL2R5Z3JhcGhzL2lzc3Vlcy9kZXRhaWw/aWQ9MjgwXG4gIHZhciBwbG90QXJlYSA9IGcuZ2V0QXJlYSgpO1xuICBpZiAoY29udGV4dC5yZWdpb25XaWR0aCA+PSAxMCAmJlxuICAgICAgY29udGV4dC5kcmFnRGlyZWN0aW9uID09IHV0aWxzLkhPUklaT05UQUwpIHtcbiAgICB2YXIgbGVmdCA9IE1hdGgubWluKGNvbnRleHQuZHJhZ1N0YXJ0WCwgY29udGV4dC5kcmFnRW5kWCksXG4gICAgICAgIHJpZ2h0ID0gTWF0aC5tYXgoY29udGV4dC5kcmFnU3RhcnRYLCBjb250ZXh0LmRyYWdFbmRYKTtcbiAgICBsZWZ0ID0gTWF0aC5tYXgobGVmdCwgcGxvdEFyZWEueCk7XG4gICAgcmlnaHQgPSBNYXRoLm1pbihyaWdodCwgcGxvdEFyZWEueCArIHBsb3RBcmVhLncpO1xuICAgIGlmIChsZWZ0IDwgcmlnaHQpIHtcbiAgICAgIGcuZG9ab29tWF8obGVmdCwgcmlnaHQpO1xuICAgIH1cbiAgICBjb250ZXh0LmNhbmNlbE5leHREYmxjbGljayA9IHRydWU7XG4gIH0gZWxzZSBpZiAoY29udGV4dC5yZWdpb25IZWlnaHQgPj0gMTAgJiZcbiAgICAgICAgICAgICBjb250ZXh0LmRyYWdEaXJlY3Rpb24gPT0gdXRpbHMuVkVSVElDQUwpIHtcbiAgICB2YXIgdG9wID0gTWF0aC5taW4oY29udGV4dC5kcmFnU3RhcnRZLCBjb250ZXh0LmRyYWdFbmRZKSxcbiAgICAgICAgYm90dG9tID0gTWF0aC5tYXgoY29udGV4dC5kcmFnU3RhcnRZLCBjb250ZXh0LmRyYWdFbmRZKTtcbiAgICB0b3AgPSBNYXRoLm1heCh0b3AsIHBsb3RBcmVhLnkpO1xuICAgIGJvdHRvbSA9IE1hdGgubWluKGJvdHRvbSwgcGxvdEFyZWEueSArIHBsb3RBcmVhLmgpO1xuICAgIGlmICh0b3AgPCBib3R0b20pIHtcbiAgICAgIGcuZG9ab29tWV8odG9wLCBib3R0b20pO1xuICAgIH1cbiAgICBjb250ZXh0LmNhbmNlbE5leHREYmxjbGljayA9IHRydWU7XG4gIH1cbiAgY29udGV4dC5kcmFnU3RhcnRYID0gbnVsbDtcbiAgY29udGV4dC5kcmFnU3RhcnRZID0gbnVsbDtcbn07XG5cbi8qKlxuICogQHByaXZhdGVcbiAqL1xuRHlncmFwaEludGVyYWN0aW9uLnN0YXJ0VG91Y2ggPSBmdW5jdGlvbihldmVudCwgZywgY29udGV