Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronizer plugin - selectClosest option #1035

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
70 changes: 65 additions & 5 deletions src/extras/synchronizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ var synchronize = function synchronize(/* dygraphs..., opts */) {
throw 'Invalid invocation of Dygraph.synchronize(). Need >= 1 argument.';
}

var OPTIONS = ['selection', 'zoom', 'range'];
var OPTIONS = ['selection', 'selectionClosest', 'zoom', 'range'];
var opts = {
selection: true,
selectionClosest: false,
zoom: true,
range: true
};
Expand Down Expand Up @@ -134,7 +135,7 @@ var synchronize = function synchronize(/* dygraphs..., opts */) {
}

if (opts.selection) {
attachSelectionHandlers(dygraphs, prevCallbacks);
attachSelectionHandlers(dygraphs, opts, prevCallbacks);
}
}
});
Expand Down Expand Up @@ -172,6 +173,56 @@ function arraysAreEqual(a, b) {
return true;
}

function closestIdx(gs, x) {
var points = gs.layout_.points[0];

// if graph has no data or single entry
var highestI = points.length - 1;
if (highestI < 0) return null;
if (highestI === 0) return points[0].idx;

var lowestI = 0;

// if values of x axis are in descending order, reverse searching borders
if (points[0].xval > points[highestI].xval) {
lowestI = highestI;
highestI = 0;
}

while (true) {
var middleI = Math.round( (lowestI + highestI) * 0.5 );
if (middleI === lowestI || middleI === highestI) break;

var middleX = points[middleI].xval;
if (middleX === x) return points[middleI].idx;

if (x < middleX) {
highestI = middleI;
} else {
lowestI = middleI;
}
}

var closestI = x - points[lowestI].xval < points[highestI].xval - x ? lowestI : highestI;

return points[closestI].idx;
}

function isInsideDateWindow(gs, idx) {
if (idx === null) return false;
var xAxisRange = gs.xAxisRange();
var min, max;
if (xAxisRange[0] <= xAxisRange[1]) {
min = xAxisRange[0];
max = xAxisRange[1];
} else {
min = xAxisRange[1];
max = xAxisRange[0];
}
var xval = gs.getValue(idx, 0);
return xval >= min && xval <= max;
}

function attachZoomHandlers(gs, syncOpts, prevCallbacks) {
var block = false;
for (var i = 0; i < gs.length; i++) {
Expand Down Expand Up @@ -223,7 +274,7 @@ function attachZoomHandlers(gs, syncOpts, prevCallbacks) {
}
}

function attachSelectionHandlers(gs, prevCallbacks) {
function attachSelectionHandlers(gs, syncOpts, prevCallbacks) {
var block = false;
for (var i = 0; i < gs.length; i++) {
var g = gs[i];
Expand All @@ -240,9 +291,18 @@ function attachSelectionHandlers(gs, prevCallbacks) {
}
continue;
}
var idx = gs[i].getRowForX(x);
if (idx !== null) {
var idx;
if (!syncOpts.selectionClosest) {
idx = gs[i].getRowForX(x);
} else {
idx = null;
if (gs[i].numRows() === me.numRows()) idx = gs[i].getRowForX(x);
if (idx === null) idx = closestIdx(gs[i], x);
}
if (isInsideDateWindow(gs[i], idx)) {
gs[i].setSelection(idx, seriesName, undefined, true);
} else {
gs[i].clearSelection();
}
}
block = false;
Expand Down