UNPKG

@future-grid/fgp-graph

Version:

fgp-graph is a chart lib based on Dygraphs

219 lines (199 loc) 8.68 kB
import Dygraph from 'dygraphs'; export class Synchronizer { args: Array<any>; graphs: Array<Dygraph>; constructor(graphs: any[]) { this.graphs = graphs; this.args = graphs.concat([{ zoom: true, selection: false }]); } synchronize = () => { if (this.args.length === 0) { throw 'Invalid invocation of Dygraph.synchronize(). Need >= 1 argument.'; } let OPTIONS = ['selection', 'zoom', 'range']; let opts: any = { selection: true, zoom: true, range: false }; let dygraphs: any[] = []; let prevCallbacks: any[] = []; let parseOpts = function (obj: any) { if (!(obj instanceof Object)) { throw 'Last argument must be either Dygraph or Object.'; } else { for (let i = 0; i < OPTIONS.length; i++) { let optName = OPTIONS[i]; if (obj.hasOwnProperty(optName)) opts[optName] = obj[optName]; } } }; let arraysAreEqual = (a: any, b: any) => { if (!Array.isArray(a) || !Array.isArray(b)) return false; let i = a.length; if (i !== b.length) return false; while (i--) { if (a[i] !== b[i]) return false; } return true; }; let attachZoomHandlers = (gs: Dygraph[], syncOpts: any, prevCallbacks: any) => { let block = false; for (let i = 0; i < gs.length; i++) { let g = gs[i]; g.updateOptions({ drawCallback: function (me: Dygraph, initial: boolean) { if (block || initial) return; block = true; let opts: { dateWindow: any, valueRange?: any } = { dateWindow: me.xAxisRange() }; if (syncOpts.range) { opts.valueRange = me.yAxisRange(); } for (let j = 0; j < gs.length; j++) { if (gs[j] == me) { if (prevCallbacks[j] && prevCallbacks[j].drawCallback) { prevCallbacks[j].drawCallback.apply(this, arguments); } continue; } // Only redraw if there are new options (in this case only dateWindow changed!) //&& arraysAreEqual(opts.valueRange, gs[j].getOption('valueRange')) if (arraysAreEqual(opts.dateWindow, gs[j].getOption('dateWindow'))) { continue; } gs[j].updateOptions(opts); } block = false; } }, true /* no need to redraw */); } } let attachSelectionHandlers = (gs: any[], prevCallbacks: any) => { let block = false; for (let i = 0; i < gs.length; i++) { let g = gs[i]; g.updateOptions({ highlightCallback: function (event: Event, x: any, points: any[], row: any, seriesName: any) { if (block) return; block = true; let me: any = this; for (let i = 0; i < gs.length; i++) { if (me == gs[i]) { if (prevCallbacks[i] && prevCallbacks[i].highlightCallback) { prevCallbacks[i].highlightCallback.apply(this, arguments); } continue; } let idx = gs[i].getRowForX(x); if (idx !== null) { gs[i].setSelection(idx, seriesName); } } block = false; }, unhighlightCallback: function (e: Event) { if (block) return; block = true; let me = this; for (let i = 0; i < gs.length; i++) { if (me == gs[i]) { if (prevCallbacks[i] && prevCallbacks[i].unhighlightCallback) { prevCallbacks[i].unhighlightCallback.apply(this, arguments); } continue; } gs[i].clearSelection(); } block = false; } }, true /* no need to redraw */); } }; if (this.args[0] instanceof Dygraph) { // Arguments are Dygraph objects. let i; for (i = 0; i < this.args.length; i++) { if (this.args[i] instanceof Dygraph) { dygraphs.push(this.args[i]); } else { break; } } if (i < this.args.length - 1) { throw 'Invalid invocation of Dygraph.synchronize(). ' + 'All but the last argument must be Dygraph objects.'; } else if (i == this.args.length - 1) { parseOpts(this.args[this.args.length - 1]); } } else if (this.args[0].length) { // Invoked w/ list of dygraphs, options for (let i = 0; i < this.args[0].length; i++) { dygraphs.push(this.args[0][i]); } if (this.args.length == 2) { parseOpts(this.args[1]); } else if (this.args.length > 2) { throw 'Invalid invocation of Dygraph.synchronize(). ' + 'Expected two arguments: array and optional options argument.'; } // otherwise arguments.length == 1, which is fine. } else { throw 'Invalid invocation of Dygraph.synchronize(). ' + 'First parameter must be either Dygraph or list of Dygraphs.'; } if (dygraphs.length < 2) { throw 'Invalid invocation of Dygraph.synchronize(). ' + 'Need two or more dygraphs to synchronize.'; } let readycount = dygraphs.length; for (let i = 0; i < dygraphs.length; i++) { let g = dygraphs[i]; g.ready(function () { if (--readycount == 0) { // store original callbacks let callBackTypes = ['drawCallback', 'highlightCallback', 'unhighlightCallback']; for (let j = 0; j < dygraphs.length; j++) { if (!prevCallbacks[j]) { prevCallbacks[j] = {}; } for (let k = callBackTypes.length - 1; k >= 0; k--) { prevCallbacks[j][callBackTypes[k]] = dygraphs[j].getFunctionOption(callBackTypes[k]); } } // Listen for draw, highlight, unhighlight callbacks. if (opts.zoom) { attachZoomHandlers(dygraphs, opts, prevCallbacks); } if (opts.selection) { attachSelectionHandlers(dygraphs, prevCallbacks); } } }); } return { detach: function () { for (let i = 0; i < dygraphs.length; i++) { let g = dygraphs[i]; if (opts.zoom) { g.updateOptions({ drawCallback: prevCallbacks[i].drawCallback }); } if (opts.selection) { g.updateOptions({ highlightCallback: prevCallbacks[i].highlightCallback, unhighlightCallback: prevCallbacks[i].unhighlightCallback }); } } // release references & make subsequent calls throw. dygraphs = []; opts = null; prevCallbacks = []; }, graphs: [this.graphs] }; }; }