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'];
50 var prevCallbacks
= [];
52 var parseOpts
= function(obj
) {
53 if (!(obj
instanceof Object
)) {
54 throw 'Last argument must be either Dygraph or Object.';
56 for (var i
= 0; i
< OPTIONS
.length
; i
++) {
57 var optName
= OPTIONS
[i
];
58 if (obj
.hasOwnProperty(optName
)) opts
[optName
] = obj
[optName
];
63 if (arguments
[0] instanceof Dygraph
) {
64 // Arguments are Dygraph objects.
65 for (var i
= 0; i
< arguments
.length
; i
++) {
66 if (arguments
[i
] instanceof Dygraph
) {
67 dygraphs
.push(arguments
[i
]);
72 if (i
< arguments
.length
- 1) {
73 throw 'Invalid invocation of Dygraph.synchronize(). ' +
74 'All but the last argument must be Dygraph objects.';
75 } else if (i
== arguments
.length
- 1) {
76 parseOpts(arguments
[arguments
.length
- 1]);
78 } else if (arguments
[0].length
) {
79 // Invoked w/ list of dygraphs
, options
80 for (var i
= 0; i
< arguments
[0].length
; i
++) {
81 dygraphs
.push(arguments
[0][i
]);
83 if (arguments
.length
== 2) {
84 parseOpts(arguments
[1]);
85 } else if (arguments
.length
> 2) {
86 throw 'Invalid invocation of Dygraph.synchronize(). ' +
87 'Expected two arguments: array and optional options argument.';
88 } // otherwise arguments.length == 1, which is fine.
90 throw 'Invalid invocation of Dygraph.synchronize(). ' +
91 'First parameter must be either Dygraph or list of Dygraphs.';
94 if (dygraphs
.length
< 2) {
95 throw 'Invalid invocation of Dygraph.synchronize(). ' +
96 'Need two or more dygraphs to synchronize.';
99 var readycount
= dygraphs
.length
;
100 for (var i
= 0; i
< dygraphs
.length
; i
++) {
102 g
.ready( function() {
103 if (--readycount
== 0) {
104 // store original callbacks
105 var callBackTypes
= ['drawCallback', 'highlightCallback', 'unhighlightCallback'];
106 for (var j
= 0; j
< dygraphs
.length
; j
++) {
107 if (!prevCallbacks
[j
]) {
108 prevCallbacks
[j
] = {};
110 for (var k
= callBackTypes
.length
- 1; k
>= 0; k
--) {
111 prevCallbacks
[j
][callBackTypes
[k
]] = dygraphs
[j
].getFunctionOption(callBackTypes
[k
]);
115 // Listen for draw, highlight, unhighlight callbacks.
117 attachZoomHandlers(dygraphs
, opts
, prevCallbacks
);
120 if (opts
.selection
) {
121 attachSelectionHandlers(dygraphs
, prevCallbacks
);
129 for (var i
= 0; i
< dygraphs
.length
; i
++) {
132 g
.updateOptions({drawCallback
: prevCallbacks
[i
].drawCallback
});
134 if (opts
.selection
) {
136 highlightCallback
: prevCallbacks
[i
].highlightCallback
,
137 unhighlightCallback
: prevCallbacks
[i
].unhighlightCallback
141 // release references & make subsequent calls throw.
144 prevCallbacks
= null;
149 function attachZoomHandlers(gs
, syncOpts
, prevCallbacks
) {
151 for (var i
= 0; i
< gs
.length
; i
++) {
154 drawCallback
: function(me
, initial
) {
155 if (block
|| initial
) return;
158 dateWindow
: me
.xAxisRange()
160 if (syncOpts
.range
) opts
.valueRange
= me
.yAxisRange();
162 for (var j
= 0; j
< gs
.length
; j
++) {
164 if (prevCallbacks
[j
] && prevCallbacks
[j
].drawCallback
) {
165 prevCallbacks
[j
].drawCallback
.apply(this, arguments
);
169 gs
[j
].updateOptions(opts
);
173 }, false /* no need to redraw */);
177 function attachSelectionHandlers(gs
, prevCallbacks
) {
179 for (var i
= 0; i
< gs
.length
; i
++) {
183 highlightCallback
: function(event
, x
, points
, row
, seriesName
) {
187 for (var i
= 0; i
< gs
.length
; i
++) {
189 if (prevCallbacks
[i
] && prevCallbacks
[i
].highlightCallback
) {
190 prevCallbacks
[i
].highlightCallback
.apply(this, arguments
);
194 var idx
= gs
[i
].getRowForX(x
);
196 gs
[i
].setSelection(idx
, seriesName
);
201 unhighlightCallback
: function(event
) {
205 for (var i
= 0; i
< gs
.length
; i
++) {
207 if (prevCallbacks
[i
] && prevCallbacks
[i
].unhighlightCallback
) {
208 prevCallbacks
[i
].unhighlightCallback
.apply(this, arguments
);
212 gs
[i
].clearSelection();