67e5d5c6676cc49b82b86d4625fd768e8dec2e1b
2 * Synchronize zooming and/or selections between a set of dygraphs.
6 * var g1 = new Dygraph(...),
7 * g2 = new Dygraph(...),
9 * var sync = Dygraph.synchronize(g1, g2, ...);
10 * // charts are now synchronized
12 * // charts are no longer synchronized
14 * You can set options using the last parameter, for example:
16 * var sync = Dygraph.synchronize(g1, g2, g3, {
21 * The default is to synchronize both of these.
23 * Instead of passing one Dygraph object as each parameter, you may also pass an
26 * var sync = Dygraph.synchronize([g1, g2, g3], {
31 * You may also set `range: false` if you wish to only sync the x-axis.
32 * The `range` option has no effect unless `zoom` is true (the default).
35 /* global Dygraph:false */
38 Dygraph
.synchronize
= function(/* dygraphs..., opts */) {
39 if (arguments
.length
=== 0) {
40 throw 'Invalid invocation of Dygraph.synchronize(). Need >= 1 argument.';
43 var OPTIONS
= ['selection', 'zoom', 'range'];
51 var parseOpts
= function(obj
) {
52 if (!(obj
instanceof Object
)) {
53 throw 'Last argument must be either Dygraph or Object.';
55 for (var i
= 0; i
< OPTIONS
.length
; i
++) {
56 var optName
= OPTIONS
[i
];
57 if (obj
.hasOwnProperty(optName
)) opts
[optName
] = obj
[optName
];
62 if (arguments
[0] instanceof Dygraph
) {
63 // Arguments are Dygraph objects.
64 for (var i
= 0; i
< arguments
.length
; i
++) {
65 if (arguments
[i
] instanceof Dygraph
) {
66 dygraphs
.push(arguments
[i
]);
71 if (i
< arguments
.length
- 1) {
72 throw 'Invalid invocation of Dygraph.synchronize(). ' +
73 'All but the last argument must be Dygraph objects.';
74 } else if (i
== arguments
.length
- 1) {
75 parseOpts(arguments
[arguments
.length
- 1]);
77 } else if (arguments
[0].length
) {
78 // Invoked w/ list of dygraphs
, options
79 for (var i
= 0; i
< arguments
[0].length
; i
++) {
80 dygraphs
.push(arguments
[0][i
]);
82 if (arguments
.length
== 2) {
83 parseOpts(arguments
[1]);
84 } else if (arguments
.length
> 2) {
85 throw 'Invalid invocation of Dygraph.synchronize(). ' +
86 'Expected two arguments: array and optional options argument.';
87 } // otherwise arguments.length == 1, which is fine.
89 throw 'Invalid invocation of Dygraph.synchronize(). ' +
90 'First parameter must be either Dygraph or list of Dygraphs.';
93 if (dygraphs
.length
< 2) {
94 throw 'Invalid invocation of Dygraph.synchronize(). ' +
95 'Need two or more dygraphs to synchronize.';
98 // Listen for draw, highlight, unhighlight callbacks.
100 attachZoomHandlers(dygraphs
, opts
);
103 if (opts
.selection
) {
104 attachSelectionHandlers(dygraphs
);
109 for (var i
= 0; i
< dygraphs
.length
; i
++) {
112 g
.updateOptions({drawCallback
: null});
114 if (opts
.selection
) {
116 highlightCallback
: null,
117 unhighlightCallback
: null
121 // release references & make subsequent calls throw.
128 function attachZoomHandlers(gs
, syncOpts
) {
130 for (var i
= 0; i
< gs
.length
; i
++) {
132 var oldDC
= g
.getFunctionOption('drawCallback');
134 drawCallback
: function(me
, initial
) {
135 if (oldDC
) oldDC(me
, initial
);
136 if (block
|| initial
) return;
139 dateWindow
: me
.xAxisRange()
141 if (syncOpts
.range
) opts
.valueRange
= me
.yAxisRange();
143 for (var j
= 0; j
< gs
.length
; j
++) {
144 if (gs
[j
] == me
) continue;
145 gs
[j
].updateOptions(opts
);
149 }, false /* no need to redraw */);
153 function attachSelectionHandlers(gs
) {
155 for (var i
= 0; i
< gs
.length
; i
++) {
157 var oldHC
= g
.getFunctionOption('highlightCallback');
158 var oldUHC
= g
.getFunctionOption('unhighlightCallback');
160 highlightCallback
: function(event
, x
, points
, row
, seriesName
) {
161 if (oldHC
) oldHC(event
, x
, points
, row
, seriesName
);
165 for (var i
= 0; i
< gs
.length
; i
++) {
166 if (me
== gs
[i
]) continue;
167 var idx
= dygraphsBinarySearch(gs
[i
], x
);
169 gs
[i
].setSelection(idx
, seriesName
);
174 unhighlightCallback
: function(event
) {
175 if (oldUHC
) oldUHC(event
);
179 for (var i
= 0; i
< gs
.length
; i
++) {
180 if (me
== gs
[i
]) continue;
181 gs
[i
].clearSelection();
189 // Returns the index corresponding to xVal, or null if there is none.
190 function dygraphsBinarySearch(g
, xVal
) {
192 high
= g
.numRows() - 1;
195 var idx
= (high
+ low
) >> 1;
196 var x
= g
.getValue(idx
, 0);
199 } else if (x
> xVal
) {
206 // TODO: give an option to find the closest point, i.e. not demand an exact match.