]> arthur.barton.de Git - netdata.git/commitdiff
fixed syncronization of dygraphs on zoom-out
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 6 Dec 2015 02:21:39 +0000 (04:21 +0200)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 6 Dec 2015 02:21:39 +0000 (04:21 +0200)
web/dashboard.js

index 444667183d32a0f0661d2d4ee9408a4b440db39f..cd91e4491df53ed1b3ff418393b03fc119b9c722 100755 (executable)
@@ -99,7 +99,8 @@
                        idle_between_loops: 200,
                        idle_lost_focus: 500,
                        global_pan_sync_time: 500,
-                       fast_render_timeframe: 200 // render continously for these many ms
+                       fast_render_timeframe: 200, // render continously for these many ms
+                       sync_delay: 1500                        // how much time after an operation to setup synced selections?
                },
 
                debug: {
                        validated: false,               // boolean - has the chart been validated?
                        enabled: true,                  // boolean - is the chart enabled for refresh?
                        paused: false,                  // boolean - is the chart paused for any reason?
+                       selected: false,                // boolean - is the chart shown a selection?
                        debug: false,
                        updates_counter: 0,             // numeric - the number of refreshes made so far
 
                        },
 
                        setSelection: function(t) {
-                               if(typeof this.library.setSelection == 'function')
-                                       return this.library.setSelection(this, t);
-                               else
-                                       return false;
+                               if(typeof this.library.setSelection == 'function') {
+                                       if(this.library.setSelection(this, t))
+                                               this.selected = true;
+                                       else
+                                               this.selected = false;
+                               }
+                               else this.selected = true;
+
+                               if(this.selected && this.debug) this.log('selection set to ' + t.toString());
+
+                               return this.selected;
                        },
 
                        clearSelection: function() {
-                               if(typeof this.library.clearSelection == 'function')
-                                       return this.library.clearSelection(this);
-                               else
-                                       return false;
+                               if(this.selected) {
+                                       if(typeof this.library.clearSelection == 'function') {
+                                               if(this.library.clearSelection(this))
+                                                       this.selected = false;
+                                               else
+                                                       this.selected = true;
+                                       }
+                                       else this.selected = false;
+                                       
+                                       if(!this.selected && this.debug) this.log('selection cleared');
+                               }
+
+                               return this.selected;
                        },
 
                        timeIsVisible: function(t) {
                        },
 
                        pauseChart: function() {
-                               this.paused = true;
+                               if(!this.paused) {
+                                       if(this.debug) this.log('paused');
+                                       this.paused = true;
+                               }
                        },
 
                        unpauseChart: function() {
-                               this.paused = false;
+                               if(this.paused) {
+                                       if(this.debug) this.log('unpaused');
+                                       this.paused = false;
+                               }
                        },
 
                        resetChart: function() {
                                this.mode.last_updated_ms = 0;
                                this.follows_global = 0;
                                this.paused = false;
+                               this.selected = false;
                                this.enabled = true;
                                this.debug = false;
 
                                        var now = new Date().getTime();
 
                                        if(this.updates_counter && !NETDATA.options.page_is_visible) {
-                                               if(NETDATA.options.debug.focus || this.debug) this.log('page does not have focus');
+                                               if(NETDATA.options.debug.focus || this.debug) this.log('canBeAutoRefreshed(): page does not have focus');
                                                return false;
                                        }
 
                                        if(!auto_refresher) return true;
 
-                                       if(!self.visible(true)) return false;
+                                       if(!self.visible(true)) {
+                                               if(this.debug) this.log('canBeAutoRefreshed(): I am not visible.');
+                                               return false;
+                                       }
 
                                        // options valid only for autoRefresh()
                                        if(NETDATA.options.auto_refresher_stop_until == 0 || NETDATA.options.auto_refresher_stop_until < now) {
                                                if(NETDATA.globalPanAndZoom.state) {
-                                                       if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this))
+                                                       if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
+                                                               if(this.debug) this.log('canBeAutoRefreshed(): global panning: I need an update.');
                                                                return true;
-                                                       else
+                                                       }
+                                                       else {
+                                                               if(this.debug) this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
                                                                return false;
+                                                       }
                                                }
 
-                                               if(this.paused) return false;
+                                               if(this.selected) {
+                                                       if(this.debug) this.log('canBeAutoRefreshed(): I have a selection in place.');
+                                                       return false;
+                                               }
 
-                                               if(now - this.mode.last_updated_ms > this.mode.view_update_every)
+                                               if(this.paused) {
+                                                       if(this.debug) this.log('canBeAutoRefreshed(): I am paused.');
+                                                       return false;
+                                               }
+
+                                               if(now - this.mode.last_updated_ms > this.mode.view_update_every) {
+                                                       if(this.debug) this.log('canBeAutoRefreshed(): It is time to update me.');
                                                        return true;
+                                               }
                                        }
                                }
 
        // dygraph
 
        NETDATA.dygraph = {
+               state: null,
                sync: false,
-               paused: []
+               dont_sync_before: 0,
+               slaves: []
        };
 
        NETDATA.dygraph.syncStart = function(state, event, x, points, row, seriesName) {
-               if(NETDATA.options.debug.dygraph || state.debug) console.log('dygraph.syncStart()');
-               state.pauseChart();
+               if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraph.syncStart()');
+
+               //if(NETDATA.dygraph.state && NETDATA.dygraph.state != state) {
+               //      state.log('sync: I am not the sync master.');
+               //      return;
+               //}
+               // state.debug = true;
+
+               var t = state.mode.after_ms + row * state.mode.view_update_every;
+               // console.log('row = ' + row + ', x = ' + x + ', t = ' + t + ' ' + ((t == x)?'SAME':'DIFFERENT'));
+
+               now = new Date().getTime();
+               if(now < NETDATA.dygraph.dont_sync_before) {
+                       if(state.debug) st.log('sync: cannot sync yet.');
+                       return;
+               }
+
+               // since we are the sync master, we should not call state.setSelection()
+               // dygraphs is taking care of visualizing our selection.
+               state.selected = true;
 
                var dygraph = state.dygraph_instance;
 
                if(!NETDATA.dygraph.sync) {
+                       if(state.debug) st.log('sync: setting up...');
                        $.each(NETDATA.options.targets, function(i, target) {
                                var st = NETDATA.chartState(target);
-                               if(typeof st.dygraph_instance == 'object' && st.library_name == state.library_name && st.canBeAutoRefreshed(false)) {
-                                       NETDATA.dygraph.paused.push(st);
+                               if(st == state) {
+                                       if(state.debug) st.log('sync: not adding me to sync');
+                               }
+                               else {
+                                       if(typeof st.dygraph_instance == 'object' && st.library_name == state.library_name && st.canBeAutoRefreshed(false)) {
+                                               NETDATA.dygraph.slaves.push(st);
+                                               if(state.debug) st.log('sync: added slave to sync');
+                                       }
                                }
                        });
+                       NETDATA.dygraph.sync = true;
                }
 
-               $.each(NETDATA.dygraph.paused, function(i, st) {
-                       st.setSelection(x);
+               $.each(NETDATA.dygraph.slaves, function(i, st) {
+                       if(st == state) {
+                               if(state.debug) st.log('sync: ignoring me from set selection');
+                       }
+                       else {
+                               if(state.debug) st.log('sync: showing master selection');
+                               st.setSelection(t);
+                       }
                });
-
        }
 
        NETDATA.dygraph.syncStop = function(state) {
-               if(NETDATA.options.debug.dygraph || state.debug) console.log('dygraph.syncStop()');
-
-               if(!NETDATA.dygraph.sync) {
-                       $.each(NETDATA.dygraph.paused, function(i, st) {
-                               st.clearSelection();
+               if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraph.syncStop()');
+
+               //if(NETDATA.dygraph.state && NETDATA.dygraph.state != state) {
+               //      state.log('sync: I am not the sync master.');
+               //      return;
+               //}
+
+               if(NETDATA.dygraph.sync) {
+                       if(state.debug) st.log('sync: cleaning up...');
+                       $.each(NETDATA.dygraph.slaves, function(i, st) {
+                               if(st == state) {
+                                       if(state.debug) st.log('sync: not adding me to sync stop');
+                               }
+                               else {
+                                       if(state.debug) st.log('sync: removed slave from sync');
+                                       st.clearSelection();
+                               }
                        });
 
+                       NETDATA.dygraph.slaves = [];
                        NETDATA.dygraph.sync = false;
-                       NETDATA.dygraph.paused = [];
                }
 
-               state.unpauseChart();
+               // since we are the sync master, we should not call state.clearSelection()
+               // dygraphs is taking care of visualizing our selection.
+               state.selected = false;
        }
 
-       NETDATA.dygraph.resetChart = function(element, dygraph) {
-               if(NETDATA.options.debug.dygraph) console.log('dygraph.resetChart()');
+       NETDATA.dygraph.resetChart = function(state, dygraph, context) {
+               if(NETDATA.options.debug.dygraph) state.log('dygraph.resetChart()');
 
-               state = NETDATA.chartState(element);
                state.resetChart();
                if(NETDATA.globalPanAndZoom.clearMaster());
        }
        }
 
        NETDATA.dygraphSetSelection = function(state, t) {
-               var r = state.calculateRowForTime(t);
-               if(r != -1) {
-                       state.dygraph_instance.setSelection(r);
-                       state.pauseChart();
-               }
-               else {
-                       state.dygraph_instance.clearSelection();
-                       state.unpauseChart();
+               if(typeof state.dygraph_instance != 'undefined') {
+                       var r = state.calculateRowForTime(t);
+                       if(r != -1) {
+                               state.dygraph_instance.setSelection(r);
+                               return true;
+                       }
+                       else {
+                               state.dygraph_instance.clearSelection();
+                               return false;
+                       }
                }
        }
 
-       NETDATA.dygraphclearSelection = function(state, t) {
-               state.dygraph_instance.clearSelection();
-               state.unpauseChart();
+       NETDATA.dygraphClearSelection = function(state, t) {
+               if(typeof state.dygraph_instance != 'undefined') {
+                       state.dygraph_instance.clearSelection();
+               }
+               return true;
        }
 
        NETDATA.dygraphInitialize = function(callback) {
                        },
                        drawCallback: function(dygraph, is_initial) {
                                if(data.state.mode.name != 'auto') {
-                                       if(NETDATA.options.debug.dygraph) console.log('dygraphDrawCallback()');
+                                       if(NETDATA.options.debug.dygraph) data.state.log('dygraphDrawCallback()');
 
                                        var x_range = dygraph.xAxisRange();
                                        var after = Math.round(x_range[0]);
                                }
                        },
                        zoomCallback: function(minDate, maxDate, yRanges) {
-                               if(NETDATA.options.debug.dygraph) console.log('dygraphZoomCallback()');
+                               if(NETDATA.options.debug.dygraph) data.state.log('dygraphZoomCallback()');
+                               NETDATA.dygraph.syncStop(data.state);
+                               NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
                                NETDATA.dygraph.zoomOrPan(element, this, minDate, maxDate);
                        },
                        highlightCallback: function(event, x, points, row, seriesName) {
-                               if(NETDATA.options.debug.dygraph) console.log('dygraphHighlightCallback()');
+                               if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('dygraphHighlightCallback()');
+                               data.state.pauseChart();
                                NETDATA.dygraph.syncStart(data.state, event, x, points, row, seriesName);
                        },
                        unhighlightCallback: function(event) {
-                               if(NETDATA.options.debug.dygraph) console.log('dygraphUnhighlightCallback()');
+                               if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('dygraphUnhighlightCallback()');
+                               data.state.unpauseChart();
                                NETDATA.dygraph.syncStop(data.state);
                        },
                        interactionModel : {
                                mousedown: function(event, dygraph, context) {
-                                       if(NETDATA.options.debug.dygraph) console.log('dygraphMouseDown()');
+                                       if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mousedown()');
+                                       NETDATA.dygraph.syncStop(data.state);
+
+                                       if(NETDATA.options.debug.dygraph) data.state.log('dygraphMouseDown()');
 
                                        // Right-click should not initiate a zoom.
-                                       if (event.button && event.button == 2) return;
+                                       if(event.button && event.button == 2) return;
 
                                        context.initializeMouseDown(event, dygraph, context);
                                        
-                                       if (event.altKey || event.shiftKey) {
-                                               data.state.setMode('zoom');
-                                               Dygraph.startZoom(event, dygraph, context);
+                                       if(event.button && event.button == 1) {
+                                               if (event.altKey || event.shiftKey) {
+                                                       data.state.setMode('pan');
+                                                       NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
+                                                       Dygraph.startPan(event, dygraph, context);
+                                               }
+                                               else {
+                                                       data.state.setMode('zoom');
+                                                       NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
+                                                       Dygraph.startZoom(event, dygraph, context);
+                                               }
                                        }
                                        else {
-                                               data.state.setMode('pan');
-                                               Dygraph.startPan(event, dygraph, context);
+                                               if (event.altKey || event.shiftKey) {
+                                                       data.state.setMode('zoom');
+                                                       NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
+                                                       Dygraph.startZoom(event, dygraph, context);
+                                               }
+                                               else {
+                                                       data.state.setMode('pan');
+                                                       NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
+                                                       Dygraph.startPan(event, dygraph, context);
+                                               }
                                        }
                                },
                                mousemove: function(event, dygraph, context) {
-                                       if(NETDATA.options.debug.dygraph) console.log('dygraphMouseMove()');
+                                       if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mousemove()');
 
-                                       if (context.isPanning) {
+                                       if(context.isPanning) {
+                                               NETDATA.dygraph.syncStop(data.state);
+                                               NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
                                                data.state.setMode('pan');
                                                Dygraph.movePan(event, dygraph, context);
                                        }
-                                       else if (context.isZooming) {
+                                       else if(context.isZooming) {
+                                               NETDATA.dygraph.syncStop(data.state);
+                                               NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
                                                data.state.setMode('zoom');
                                                Dygraph.moveZoom(event, dygraph, context);
                                        }
                                },
                                mouseup: function(event, dygraph, context) {
-                                       if(NETDATA.options.debug.dygraph) console.log('dygraphMouseUp()');
+                                       if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mouseup()');
 
-                                       if (context.isPanning)
+                                       if (context.isPanning) {
+                                               NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
                                                Dygraph.endPan(event, dygraph, context);
-                                       else if (context.isZooming)
+                                       }
+                                       else if (context.isZooming) {
+                                               NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
                                                Dygraph.endZoom(event, dygraph, context);
+                                       }
                                },
                                click: function(event, dygraph, context) {
-                                       if(NETDATA.options.debug.dygraph) console.log('dygraphMouseClick()');
+                                       if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.click()');
                                        Dygraph.cancelEvent(event);
                                },
                                dblclick: function(event, dygraph, context) {
-                                       if(NETDATA.options.debug.dygraph) console.log('dygraphMouseDoubleClick()');
-                                       NETDATA.dygraph.resetChart(element, dygraph);
+                                       if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.dblclick()');
+                                       NETDATA.dygraph.syncStop(data.state);
+                                       NETDATA.dygraph.resetChart(data.state, dygraph, context);
                                },
                                mousewheel: function(event, dygraph, context) {
-                                       if(NETDATA.options.debug.dygraph) console.log('dygraphMouseWheel()');
+                                       if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mousewheel()');
+
+                                       NETDATA.dygraph.syncStop(data.state);
+                                       NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
 
                                        if(event.altKey || event.shiftKey) {
                                                // http://dygraphs.com/gallery/interaction-api.js
                                                var before = before_old - dt;
                                                var after  = after_old  + dt;
 
-                                               if(NETDATA.options.debug.dygraph) console.log('percent: ' + percentage + ' from ' + after_old + ' - ' + before_old + ' to ' + after + ' - ' + before + ', range from ' + (before_old - after_old).toString() + ' to ' + (before - after).toString());
+                                               if(NETDATA.options.debug.dygraph) data.state.log('percent: ' + percentage + ' from ' + after_old + ' - ' + before_old + ' to ' + after + ' - ' + before + ', range from ' + (before_old - after_old).toString() + ' to ' + (before - after).toString());
 
                                                data.state.setMode('zoom');
                                                NETDATA.dygraph.zoomOrPan(element, dygraph, after, before);
                                        }                                       
                                },
                                touchstart: function(event, dygraph, context) {
+                                       if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.touchstart()');
+                                       NETDATA.dygraph.syncStop(data.state);
+                                       NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
                                        Dygraph.Interaction.startTouch(event, dygraph, context);
                                        context.touchDirections = { x: true, y: false };
                                        data.state.setMode('zoom');
                                },
                                touchmove: function(event, dygraph, context) {
+                                       if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.touchmove()');
                                        //Dygraph.cancelEvent(event);
+                                       NETDATA.dygraph.syncStop(data.state);
                                        Dygraph.Interaction.moveTouch(event, dygraph, context);
                                },
                                touchend: function(event, dygraph, context) {
+                                       if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.touchend()');
                                        Dygraph.Interaction.endTouch(event, dygraph, context);
                                }
                        }