@cryptotaxi247 / netdata-1 / commits / 276065a13

fixed syncronization of dygraphs on zoom-out

Costa Tsaousis (ktsaou) committed Dec 6, 2015 at 04:21 UTC 276065a13caf893ae1ca7b5da766058d073ab872
1 file changed +201 -68
web/dashboard.js
+201 -68
@@ -99,7 +99,8 @@
99 idle_between_loops: 200,
100 idle_lost_focus: 500,
101 global_pan_sync_time: 500,
102 - fast_render_timeframe: 200 // render continously for these many ms
102 + fast_render_timeframe: 200, // render continously for these many ms
103 + sync_delay: 1500 // how much time after an operation to setup synced selections?
104 },
105
106 debug: {
@@ -271,6 +272,7 @@
272 validated: false, // boolean - has the chart been validated?
273 enabled: true, // boolean - is the chart enabled for refresh?
274 paused: false, // boolean - is the chart paused for any reason?
275 + selected: false, // boolean - is the chart shown a selection?
276 debug: false,
277 updates_counter: 0, // numeric - the number of refreshes made so far
278
@@ -337,17 +339,33 @@
339 },
340
341 setSelection: function(t) {
340 - if(typeof this.library.setSelection == 'function')
341 - return this.library.setSelection(this, t);
342 - else
343 - return false;
342 + if(typeof this.library.setSelection == 'function') {
343 + if(this.library.setSelection(this, t))
344 + this.selected = true;
345 + else
346 + this.selected = false;
347 + }
348 + else this.selected = true;
349 +
350 + if(this.selected && this.debug) this.log('selection set to ' + t.toString());
351 +
352 + return this.selected;
353 },
354
355 clearSelection: function() {
347 - if(typeof this.library.clearSelection == 'function')
348 - return this.library.clearSelection(this);
349 - else
350 - return false;
356 + if(this.selected) {
357 + if(typeof this.library.clearSelection == 'function') {
358 + if(this.library.clearSelection(this))
359 + this.selected = false;
360 + else
361 + this.selected = true;
362 + }
363 + else this.selected = false;
364 +
365 + if(!this.selected && this.debug) this.log('selection cleared');
366 + }
367 +
368 + return this.selected;
369 },
370
371 timeIsVisible: function(t) {
@@ -366,11 +384,17 @@
384 },
385
386 pauseChart: function() {
369 - this.paused = true;
387 + if(!this.paused) {
388 + if(this.debug) this.log('paused');
389 + this.paused = true;
390 + }
391 },
392
393 unpauseChart: function() {
373 - this.paused = false;
394 + if(this.paused) {
395 + if(this.debug) this.log('unpaused');
396 + this.paused = false;
397 + }
398 },
399
400 resetChart: function() {
@@ -385,6 +409,7 @@
409 this.mode.last_updated_ms = 0;
410 this.follows_global = 0;
411 this.paused = false;
412 + this.selected = false;
413 this.enabled = true;
414 this.debug = false;
415
@@ -674,27 +699,44 @@
699 var now = new Date().getTime();
700
701 if(this.updates_counter && !NETDATA.options.page_is_visible) {
677 - if(NETDATA.options.debug.focus || this.debug) this.log('page does not have focus');
702 + if(NETDATA.options.debug.focus || this.debug) this.log('canBeAutoRefreshed(): page does not have focus');
703 return false;
704 }
705
706 if(!auto_refresher) return true;
707
683 - if(!self.visible(true)) return false;
708 + if(!self.visible(true)) {
709 + if(this.debug) this.log('canBeAutoRefreshed(): I am not visible.');
710 + return false;
711 + }
712
713 // options valid only for autoRefresh()
714 if(NETDATA.options.auto_refresher_stop_until == 0 || NETDATA.options.auto_refresher_stop_until < now) {
715 if(NETDATA.globalPanAndZoom.state) {
688 - if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this))
716 + if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
717 + if(this.debug) this.log('canBeAutoRefreshed(): global panning: I need an update.');
718 return true;
690 - else
719 + }
720 + else {
721 + if(this.debug) this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
722 return false;
723 + }
724 }
725
694 - if(this.paused) return false;
726 + if(this.selected) {
727 + if(this.debug) this.log('canBeAutoRefreshed(): I have a selection in place.');
728 + return false;
729 + }
730
696 - if(now - this.mode.last_updated_ms > this.mode.view_update_every)
731 + if(this.paused) {
732 + if(this.debug) this.log('canBeAutoRefreshed(): I am paused.');
733 + return false;
734 + }
735 +
736 + if(now - this.mode.last_updated_ms > this.mode.view_update_every) {
737 + if(this.debug) this.log('canBeAutoRefreshed(): It is time to update me.');
738 return true;
739 + }
740 }
741 }
742
@@ -1163,50 +1205,96 @@
1205 // dygraph
1206
1207 NETDATA.dygraph = {
1208 + state: null,
1209 sync: false,
1167 - paused: []
1210 + dont_sync_before: 0,
1211 + slaves: []
1212 };
1213
1214 NETDATA.dygraph.syncStart = function(state, event, x, points, row, seriesName) {
1171 - if(NETDATA.options.debug.dygraph || state.debug) console.log('dygraph.syncStart()');
1172 - state.pauseChart();
1215 + if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraph.syncStart()');
1216 +
1217 + //if(NETDATA.dygraph.state && NETDATA.dygraph.state != state) {
1218 + // state.log('sync: I am not the sync master.');
1219 + // return;
1220 + //}
1221 + // state.debug = true;
1222 +
1223 + var t = state.mode.after_ms + row * state.mode.view_update_every;
1224 + // console.log('row = ' + row + ', x = ' + x + ', t = ' + t + ' ' + ((t == x)?'SAME':'DIFFERENT'));
1225 +
1226 + now = new Date().getTime();
1227 + if(now < NETDATA.dygraph.dont_sync_before) {
1228 + if(state.debug) st.log('sync: cannot sync yet.');
1229 + return;
1230 + }
1231 +
1232 + // since we are the sync master, we should not call state.setSelection()
1233 + // dygraphs is taking care of visualizing our selection.
1234 + state.selected = true;
1235
1236 var dygraph = state.dygraph_instance;
1237
1238 if(!NETDATA.dygraph.sync) {
1239 + if(state.debug) st.log('sync: setting up...');
1240 $.each(NETDATA.options.targets, function(i, target) {
1241 var st = NETDATA.chartState(target);
1179 - if(typeof st.dygraph_instance == 'object' && st.library_name == state.library_name && st.canBeAutoRefreshed(false)) {
1180 - NETDATA.dygraph.paused.push(st);
1242 + if(st == state) {
1243 + if(state.debug) st.log('sync: not adding me to sync');
1244 + }
1245 + else {
1246 + if(typeof st.dygraph_instance == 'object' && st.library_name == state.library_name && st.canBeAutoRefreshed(false)) {
1247 + NETDATA.dygraph.slaves.push(st);
1248 + if(state.debug) st.log('sync: added slave to sync');
1249 + }
1250 }
1251 });
1252 + NETDATA.dygraph.sync = true;
1253 }
1254
1185 - $.each(NETDATA.dygraph.paused, function(i, st) {
1186 - st.setSelection(x);
1255 + $.each(NETDATA.dygraph.slaves, function(i, st) {
1256 + if(st == state) {
1257 + if(state.debug) st.log('sync: ignoring me from set selection');
1258 + }
1259 + else {
1260 + if(state.debug) st.log('sync: showing master selection');
1261 + st.setSelection(t);
1262 + }
1263 });
1188 -
1264 }
1265
1266 NETDATA.dygraph.syncStop = function(state) {
1192 - if(NETDATA.options.debug.dygraph || state.debug) console.log('dygraph.syncStop()');
1193 -
1194 - if(!NETDATA.dygraph.sync) {
1195 - $.each(NETDATA.dygraph.paused, function(i, st) {
1196 - st.clearSelection();
1267 + if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraph.syncStop()');
1268 +
1269 + //if(NETDATA.dygraph.state && NETDATA.dygraph.state != state) {
1270 + // state.log('sync: I am not the sync master.');
1271 + // return;
1272 + //}
1273 +
1274 + if(NETDATA.dygraph.sync) {
1275 + if(state.debug) st.log('sync: cleaning up...');
1276 + $.each(NETDATA.dygraph.slaves, function(i, st) {
1277 + if(st == state) {
1278 + if(state.debug) st.log('sync: not adding me to sync stop');
1279 + }
1280 + else {
1281 + if(state.debug) st.log('sync: removed slave from sync');
1282 + st.clearSelection();
1283 + }
1284 });
1285
1286 + NETDATA.dygraph.slaves = [];
1287 NETDATA.dygraph.sync = false;
1200 - NETDATA.dygraph.paused = [];
1288 }
1289
1203 - state.unpauseChart();
1290 + // since we are the sync master, we should not call state.clearSelection()
1291 + // dygraphs is taking care of visualizing our selection.
1292 + state.selected = false;
1293 }
1294
1206 - NETDATA.dygraph.resetChart = function(element, dygraph) {
1207 - if(NETDATA.options.debug.dygraph) console.log('dygraph.resetChart()');
1295 + NETDATA.dygraph.resetChart = function(state, dygraph, context) {
1296 + if(NETDATA.options.debug.dygraph) state.log('dygraph.resetChart()');
1297
1209 - state = NETDATA.chartState(element);
1298 state.resetChart();
1299 if(NETDATA.globalPanAndZoom.clearMaster());
1300 }
@@ -1220,20 +1308,24 @@
1308 }
1309
1310 NETDATA.dygraphSetSelection = function(state, t) {
1223 - var r = state.calculateRowForTime(t);
1224 - if(r != -1) {
1225 - state.dygraph_instance.setSelection(r);
1226 - state.pauseChart();
1227 - }
1228 - else {
1229 - state.dygraph_instance.clearSelection();
1230 - state.unpauseChart();
1311 + if(typeof state.dygraph_instance != 'undefined') {
1312 + var r = state.calculateRowForTime(t);
1313 + if(r != -1) {
1314 + state.dygraph_instance.setSelection(r);
1315 + return true;
1316 + }
1317 + else {
1318 + state.dygraph_instance.clearSelection();
1319 + return false;
1320 + }
1321 }
1322 }
1323
1234 - NETDATA.dygraphclearSelection = function(state, t) {
1235 - state.dygraph_instance.clearSelection();
1236 - state.unpauseChart();
1324 + NETDATA.dygraphClearSelection = function(state, t) {
1325 + if(typeof state.dygraph_instance != 'undefined') {
1326 + state.dygraph_instance.clearSelection();
1327 + }
1328 + return true;
1329 }
1330
1331 NETDATA.dygraphInitialize = function(callback) {
@@ -1400,7 +1492,7 @@
1492 },
1493 drawCallback: function(dygraph, is_initial) {
1494 if(data.state.mode.name != 'auto') {
1403 - if(NETDATA.options.debug.dygraph) console.log('dygraphDrawCallback()');
1495 + if(NETDATA.options.debug.dygraph) data.state.log('dygraphDrawCallback()');
1496
1497 var x_range = dygraph.xAxisRange();
1498 var after = Math.round(x_range[0]);
@@ -1410,65 +1502,100 @@
1502 }
1503 },
1504 zoomCallback: function(minDate, maxDate, yRanges) {
1413 - if(NETDATA.options.debug.dygraph) console.log('dygraphZoomCallback()');
1505 + if(NETDATA.options.debug.dygraph) data.state.log('dygraphZoomCallback()');
1506 + NETDATA.dygraph.syncStop(data.state);
1507 + NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1508 NETDATA.dygraph.zoomOrPan(element, this, minDate, maxDate);
1509 },
1510 highlightCallback: function(event, x, points, row, seriesName) {
1417 - if(NETDATA.options.debug.dygraph) console.log('dygraphHighlightCallback()');
1511 + if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('dygraphHighlightCallback()');
1512 + data.state.pauseChart();
1513 NETDATA.dygraph.syncStart(data.state, event, x, points, row, seriesName);
1514 },
1515 unhighlightCallback: function(event) {
1421 - if(NETDATA.options.debug.dygraph) console.log('dygraphUnhighlightCallback()');
1516 + if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('dygraphUnhighlightCallback()');
1517 + data.state.unpauseChart();
1518 NETDATA.dygraph.syncStop(data.state);
1519 },
1520 interactionModel : {
1521 mousedown: function(event, dygraph, context) {
1426 - if(NETDATA.options.debug.dygraph) console.log('dygraphMouseDown()');
1522 + if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mousedown()');
1523 + NETDATA.dygraph.syncStop(data.state);
1524 +
1525 + if(NETDATA.options.debug.dygraph) data.state.log('dygraphMouseDown()');
1526
1527 // Right-click should not initiate a zoom.
1429 - if (event.button && event.button == 2) return;
1528 + if(event.button && event.button == 2) return;
1529
1530 context.initializeMouseDown(event, dygraph, context);
1531
1433 - if (event.altKey || event.shiftKey) {
1434 - data.state.setMode('zoom');
1435 - Dygraph.startZoom(event, dygraph, context);
1532 + if(event.button && event.button == 1) {
1533 + if (event.altKey || event.shiftKey) {
1534 + data.state.setMode('pan');
1535 + NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1536 + Dygraph.startPan(event, dygraph, context);
1537 + }
1538 + else {
1539 + data.state.setMode('zoom');
1540 + NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1541 + Dygraph.startZoom(event, dygraph, context);
1542 + }
1543 }
1544 else {
1438 - data.state.setMode('pan');
1439 - Dygraph.startPan(event, dygraph, context);
1545 + if (event.altKey || event.shiftKey) {
1546 + data.state.setMode('zoom');
1547 + NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1548 + Dygraph.startZoom(event, dygraph, context);
1549 + }
1550 + else {
1551 + data.state.setMode('pan');
1552 + NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1553 + Dygraph.startPan(event, dygraph, context);
1554 + }
1555 }
1556 },
1557 mousemove: function(event, dygraph, context) {
1443 - if(NETDATA.options.debug.dygraph) console.log('dygraphMouseMove()');
1558 + if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mousemove()');
1559
1445 - if (context.isPanning) {
1560 + if(context.isPanning) {
1561 + NETDATA.dygraph.syncStop(data.state);
1562 + NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1563 data.state.setMode('pan');
1564 Dygraph.movePan(event, dygraph, context);
1565 }
1449 - else if (context.isZooming) {
1566 + else if(context.isZooming) {
1567 + NETDATA.dygraph.syncStop(data.state);
1568 + NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1569 data.state.setMode('zoom');
1570 Dygraph.moveZoom(event, dygraph, context);
1571 }
1572 },
1573 mouseup: function(event, dygraph, context) {
1455 - if(NETDATA.options.debug.dygraph) console.log('dygraphMouseUp()');
1574 + if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mouseup()');
1575
1457 - if (context.isPanning)
1576 + if (context.isPanning) {
1577 + NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1578 Dygraph.endPan(event, dygraph, context);
1459 - else if (context.isZooming)
1579 + }
1580 + else if (context.isZooming) {
1581 + NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1582 Dygraph.endZoom(event, dygraph, context);
1583 + }
1584 },
1585 click: function(event, dygraph, context) {
1463 - if(NETDATA.options.debug.dygraph) console.log('dygraphMouseClick()');
1586 + if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.click()');
1587 Dygraph.cancelEvent(event);
1588 },
1589 dblclick: function(event, dygraph, context) {
1467 - if(NETDATA.options.debug.dygraph) console.log('dygraphMouseDoubleClick()');
1468 - NETDATA.dygraph.resetChart(element, dygraph);
1590 + if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.dblclick()');
1591 + NETDATA.dygraph.syncStop(data.state);
1592 + NETDATA.dygraph.resetChart(data.state, dygraph, context);
1593 },
1594 mousewheel: function(event, dygraph, context) {
1471 - if(NETDATA.options.debug.dygraph) console.log('dygraphMouseWheel()');
1595 + if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.mousewheel()');
1596 +
1597 + NETDATA.dygraph.syncStop(data.state);
1598 + NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1599
1600 if(event.altKey || event.shiftKey) {
1601 // http://dygraphs.com/gallery/interaction-api.js
@@ -1485,22 +1612,28 @@
1612 var before = before_old - dt;
1613 var after = after_old + dt;
1614
1488 - 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());
1615 + 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());
1616
1617 data.state.setMode('zoom');
1618 NETDATA.dygraph.zoomOrPan(element, dygraph, after, before);
1619 }
1620 },
1621 touchstart: function(event, dygraph, context) {
1622 + if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.touchstart()');
1623 + NETDATA.dygraph.syncStop(data.state);
1624 + NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_delay;
1625 Dygraph.Interaction.startTouch(event, dygraph, context);
1626 context.touchDirections = { x: true, y: false };
1627 data.state.setMode('zoom');
1628 },
1629 touchmove: function(event, dygraph, context) {
1630 + if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.touchmove()');
1631 //Dygraph.cancelEvent(event);
1632 + NETDATA.dygraph.syncStop(data.state);
1633 Dygraph.Interaction.moveTouch(event, dygraph, context);
1634 },
1635 touchend: function(event, dygraph, context) {
1636 + if(NETDATA.options.debug.dygraph || data.state.debug) data.state.log('interactionModel.touchend()');
1637 Dygraph.Interaction.endTouch(event, dygraph, context);
1638 }
1639 }