Skip to content
Open
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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