@cryptotaxi247 / netdata-1 / commits / 5a2e0c2c6

fixed an issue where zooming could lead to total zoom-out of a chart; added dashboard help

Costa Tsaousis (ktsaou) committed Jan 27, 2016 at 23:58 UTC 5a2e0c2c6c78a9b99cd49acbb0650df7ac3df781
2 files changed +167 -19
web/dashboard.js
+56 -17
@@ -970,6 +970,8 @@
970
971 this.dimensions_visibility = new dimensionsVisibility(this);
972
973 + this._updating = false;
974 +
975 // ============================================================================================================
976 // PRIVATE FUNCTIONS
977
@@ -1749,7 +1751,7 @@
1751 // we do it, but we adjust to minimum size and return false
1752 // when the wanted size is below the current and the minimum
1753 // and we zoom
1752 - if(this.current === this.zoom && wanted_duration < current_duration && wanted_duration < min_duration) {
1754 + if(wanted_duration < current_duration && wanted_duration < min_duration) {
1755 if(this.debug === true)
1756 this.log(logme + 'too small: min_duration: ' + (min_duration / 1000).toString() + ', wanted: ' + (wanted_duration / 1000).toString());
1757
@@ -1758,15 +1760,16 @@
1760 var dt = (min_duration - wanted_duration) / 2;
1761 before += dt;
1762 after -= dt;
1763 + wanted_duration = before - after;
1764 ret = false;
1765 }
1766
1767 var tolerance = this.data_update_every * 2;
1768 var movement = Math.abs(before - this.view_before);
1769
1767 - if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance) {
1770 + if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance && ret === true) {
1771 if(this.debug === true)
1769 - this.log(logme + 'REJECTING UPDATE: current duration: ' + (current_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + false);
1772 + this.log(logme + 'REJECTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + false);
1773 return false;
1774 }
1775
@@ -1776,7 +1779,7 @@
1779 }
1780
1781 if(this.debug === true)
1779 - this.log(logme + 'ACCEPTING UPDATE: current duration: ' + (current_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + ret);
1782 + this.log(logme + 'ACCEPTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + ret);
1783
1784 this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay;
1785 this.current.force_after_ms = after;
@@ -2311,6 +2314,8 @@
2314 if(this.debug === true)
2315 this.log('updateChartWithData() called.');
2316
2317 + this._updating = false;
2318 +
2319 // this may force the chart to be re-created
2320 resizeChart();
2321
@@ -2422,6 +2427,14 @@
2427 if(this.debug === true)
2428 this.log('updateChart() called.');
2429
2430 + if(this._updating === true) {
2431 + if(this.debug === true)
2432 + this.log('I am already updating...');
2433 +
2434 + if(typeof callback === 'function') callback();
2435 + return false;
2436 + }
2437 +
2438 // due to late initialization of charts and libraries
2439 // we need to check this too
2440 if(this.enabled === false) {
@@ -2460,6 +2473,8 @@
2473 if(this.debug === true)
2474 this.log('updating from ' + this.data_url);
2475
2476 + this._updating = true;
2477 +
2478 this.xhr = $.ajax( {
2479 url: this.data_url,
2480 crossDomain: NETDATA.options.crossDomainAjax,
@@ -2476,6 +2491,7 @@
2491 error('data download failed for url: ' + that.data_url);
2492 })
2493 .always(function() {
2494 + this._updating = false;
2495 if(typeof callback === 'function') callback();
2496 });
2497
@@ -3348,9 +3364,18 @@
3364 labels: data.result.labels,
3365 labelsDivWidth: state.chartWidth() - 70,
3366 visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names)
3351 - }
3367 + };
3368
3353 - if(state.current.name !== 'auto') {
3369 + if(state.dygraph_force_zoom === true) {
3370 + if(NETDATA.options.debug.dygraph === true || state.debug === true)
3371 + state.log('dygraphChartUpdate() forced zoom update');
3372 +
3373 + options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
3374 + options.valueRange = null;
3375 + options.isZoomedIgnoreProgrammaticZoom = true;
3376 + state.dygraph_force_zoom = false;
3377 + }
3378 + else if(state.current.name !== 'auto') {
3379 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3380 state.log('dygraphChartUpdate() loose update');
3381 }
@@ -3358,8 +3383,9 @@
3383 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3384 state.log('dygraphChartUpdate() strict update');
3385
3361 - options.dateWindow = null;
3386 + options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
3387 options.valueRange = null;
3388 + options.isZoomedIgnoreProgrammaticZoom = true;
3389 }
3390
3391 if(state.dygraph_smooth_eligible === true) {
@@ -3371,6 +3397,7 @@
3397 }
3398
3399 dygraph.updateOptions(options);
3400 +
3401 state.dygraph_last_rendered = new Date().getTime();
3402 return true;
3403 };
@@ -3516,14 +3543,16 @@
3543 return '';
3544 },
3545 drawCallback: function(dygraph, is_initial) {
3519 - if(state.current.name !== 'auto') {
3520 - if(NETDATA.options.debug.dygraph === true)
3521 - state.log('dygraphDrawCallback()');
3546 + if(state.current.name !== 'auto' && state.dygraph_user_action === true) {
3547 + state.dygraph_user_action = false;
3548
3549 var x_range = dygraph.xAxisRange();
3550 var after = Math.round(x_range[0]);
3551 var before = Math.round(x_range[1]);
3552
3553 + if(NETDATA.options.debug.dygraph === true)
3554 + state.log('dygraphDrawCallback(dygraph, ' + is_initial + '): ' + (after / 1000).toString() + ' - ' + (before / 1000).toString());
3555 +
3556 if(before <= state.netdata_last && after >= state.netdata_first)
3557 state.updateChartPanOrZoom(after, before);
3558 }
@@ -3534,14 +3563,12 @@
3563
3564 state.globalSelectionSyncStop();
3565 state.globalSelectionSyncDelay();
3566 + state.setMode('zoom');
3567
3538 - if(state.updateChartPanOrZoom(minDate, maxDate) === false) {
3539 - // we should not zoom that much
3540 - state.dygraph_instance.updateOptions({
3541 - dateWindow: null,
3542 - valueRange: null
3543 - });
3544 - }
3568 + // refresh it to the greatest possible zoom level
3569 + state.dygraph_user_action = true;
3570 + state.dygraph_force_zoom = true;
3571 + state.updateChartPanOrZoom(minDate, maxDate);
3572 },
3573 highlightCallback: function(event, x, points, row, seriesName) {
3574 if(NETDATA.options.debug.dygraph === true || state.debug === true)
@@ -3574,6 +3601,7 @@
3601 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3602 state.log('interactionModel.mousedown()');
3603
3604 + state.dygraph_user_action = true;
3605 state.globalSelectionSyncStop();
3606
3607 if(NETDATA.options.debug.dygraph === true)
@@ -3614,12 +3642,14 @@
3642 state.log('interactionModel.mousemove()');
3643
3644 if(context.isPanning) {
3645 + state.dygraph_user_action = true;
3646 state.globalSelectionSyncStop();
3647 state.globalSelectionSyncDelay();
3648 state.setMode('pan');
3649 Dygraph.movePan(event, dygraph, context);
3650 }
3651 else if(context.isZooming) {
3652 + state.dygraph_user_action = true;
3653 state.globalSelectionSyncStop();
3654 state.globalSelectionSyncDelay();
3655 state.setMode('zoom');
@@ -3631,10 +3661,12 @@
3661 state.log('interactionModel.mouseup()');
3662
3663 if (context.isPanning) {
3664 + state.dygraph_user_action = true;
3665 state.globalSelectionSyncDelay();
3666 Dygraph.endPan(event, dygraph, context);
3667 }
3668 else if (context.isZooming) {
3669 + state.dygraph_user_action = true;
3670 state.globalSelectionSyncDelay();
3671 Dygraph.endZoom(event, dygraph, context);
3672 }
@@ -3714,6 +3746,8 @@
3746 }
3747
3748 if(event.altKey || event.shiftKey) {
3749 + state.dygraph_user_action = true;
3750 +
3751 state.globalSelectionSyncStop();
3752 state.globalSelectionSyncDelay();
3753
@@ -3757,6 +3791,7 @@
3791 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3792 state.log('interactionModel.touchstart()');
3793
3794 + state.dygraph_user_action = true;
3795 state.setMode('zoom');
3796 state.pauseChart();
3797
@@ -3778,6 +3813,7 @@
3813 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3814 state.log('interactionModel.touchmove()');
3815
3816 + state.dygraph_user_action = true;
3817 Dygraph.defaultInteractionModel.touchmove(event, dygraph, context);
3818
3819 state.dygraph_last_touch_move = new Date().getTime();
@@ -3786,6 +3822,7 @@
3822 if(NETDATA.options.debug.dygraph === true || state.debug === true)
3823 state.log('interactionModel.touchend()');
3824
3825 + state.dygraph_user_action = true;
3826 Dygraph.defaultInteractionModel.touchend(event, dygraph, context);
3827
3828 // if it didn't move, it is a selection
@@ -3837,6 +3874,8 @@
3874 state.dygraph_instance = new Dygraph(state.element_chart,
3875 data.result.data, state.dygraph_options);
3876
3877 + state.dygraph_force_zoom = false;
3878 + state.dygraph_user_action = false;
3879 state.dygraph_last_rendered = new Date().getTime();
3880 return true;
3881 };
web/index.html
+111 -2
@@ -281,7 +281,7 @@
281 <!-- <script> netdataServer = "http://box:19999"; </script> -->
282
283 <!-- load the dashboard manager - it will do the rest -->
284 - <script type="text/javascript" src="dashboard.js?v22"></script>
284 + <script type="text/javascript" src="dashboard.js?v23"></script>
285 </head>
286
287 <body data-spy="scroll" data-target="#sidebar">
@@ -300,7 +300,8 @@
300 <ul class="nav navbar-nav">
301 <li><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal"><i class="fa fa-cog"></i> settings</a></li>
302 <li><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank"><i class="fa fa-github"></i> community</a></li>
303 - <li><a href="old/" class="btn" target="_blank"><i class="fa fa-step-backward"></i> old dashboard</a></li>
303 +<!-- <li><a href="old/" class="btn" target="_blank"><i class="fa fa-step-backward"></i> old dashboard</a></li> -->
304 + <li><a href="#" class="btn" data-toggle="modal" data-target="#helpModal"><i class="fa fa-question-circle"></i> help</a></li>
305 <!-- <li><a href="#sec">Visualize</a></li>
306 <li><a href="#sec">Prototype</a></li>
307 --> </ul>
@@ -446,6 +447,114 @@
447 </div>
448 </div>
449
450 + <div class="modal fade" id="helpModal" tabindex="-1" role="dialog" aria-labelledby="helpModalLabel">
451 + <div class="modal-dialog modal-lg" role="document">
452 + <div class="modal-content">
453 + <div class="modal-header">
454 + <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
455 + <h4 class="modal-title" id="helpModalLabel">Dashboard Help</h4>
456 + </div>
457 + <div class="modal-body">
458 +
459 + <h4>Dygraphs (line, area and stacked area charts)</h4>
460 +
461 + <!-- Nav tabs -->
462 + <ul class="nav nav-tabs" role="tablist">
463 + <li role="presentation" class="active"><a href="#help_mouse" aria-controls="help_mouse" role="tab" data-toggle="tab">Mouse Interface</a></li>
464 + <li role="presentation"><a href="#help_touch" aria-controls="help_touch" role="tab" data-toggle="tab">Touch Interface</a></li>
465 + </ul>
466 +
467 + <!-- Tab panes -->
468 + <div class="tab-content">
469 + <div role="tabpanel" class="tab-pane active" id="help_mouse">
470 + <div class="p">
471 + <h4>Mouse Over / Hover</h4>
472 + Mouse over on a chart to show, at its legend, the values for the timestamp under the mouse (the chart will also highlight the point at the chart).
473 + <br/>
474 + All the other visible charts will also show and highlight their values for the same timestamp.
475 + </div>
476 + <hr/>
477 + <div class="p">
478 + <h4>Trag Chart Contents</h4>
479 + Trag the contents of a chart to pan it horizontally.
480 + <br/>
481 + All the charts will follow soon after you let the chart alone (this little delay is by design: it speeds up your browser and lets you focus on what you are exploring).
482 + <br/>
483 + Once a chart is panned, auto refreshing stops for all charts. To enable it again, <b>double click</b> a panned chart.
484 + </div>
485 + <hr/>
486 + <div class="p">
487 + <h4>Double Click</h4>
488 + Double Click a chart to reset all the charts to their default auto-refreshing state.
489 + </div>
490 + <hr/>
491 + <div class="p">
492 + <h4>SHIFT + Trag</h4>
493 + While pressing the shift key, select with the mouse and area of a chart, to zoom in. The other charts will follow too. Zooming is performed in two phases:
494 + <ul>
495 + <li>The already loaded chart contents are zoomed (low resolution)</li>
496 + <li>New data are transfered from the netdata server, to refresh the chart with possibly more detail.</li>
497 + </ul>
498 + Once a chart is zoomed, auto refreshing stops for all charts. To enable it again, <b>double click</b> a zoomed chart.
499 + </div>
500 + <hr/>
501 + <div class="p">
502 + <h4>SHIFT + Mouse Wheel <small>(does not work on firefox and IE/Edge)</small></h4>
503 + While pressing the shift key and the mouse pointer is over the contents of a chart, scroll the mouse wheel to zoom in or out. This kind of zooming is aligned to center below the mouse pointer. The other charts will follow too.
504 + <br/>
505 + Once a chart is zoomed, auto refreshing stops for all charts. To enable it again, <b>double click</b> a zoomed chart.
506 + </div>
507 + <hr/>
508 + <div class="p">
509 + <h4>Legend Operations</h4>
510 + Click on the label or value of a dimension, will select / unselect this dimension.
511 + <br/>
512 + You can press any of the shift or control keys and the click on legend labels or values, to select / unselect multiple dimensions.
513 + </div>
514 + </div>
515 + <div role="tabpanel" class="tab-pane" id="help_touch">
516 + <div class="p">
517 + <h4>Single Tap</h4>
518 + Single Tap on the contents of a chart to show, at its legend, the values for the timestamp tapped (the chart will also highlight the point at the chart).
519 + <br/>
520 + All the other visible charts will also show and highlight their values for the same timestamp.
521 + </div>
522 + <hr/>
523 + <div class="p">
524 + <h4>Trag Chart Contents</h4>
525 + Touch and Trag the contents of a chart to pan it horizontally.
526 + <br/>
527 + All the charts will follow soon after you let the chart alone (this little delay is by design: it speeds up your browser and lets you focus on what you are exploring).
528 + <br/>
529 + Once a chart is panned, auto refreshing stops for all charts. To enable it again, <b>double tap</b> a panned chart.
530 + </div>
531 + <hr/>
532 + <div class="p">
533 + <h4>Double Tap</h4>
534 + Double tap a chart to reset all the charts to their default auto-refreshing state.
535 + </div>
536 + <hr/>
537 + <div class="p">
538 + <h4>Zoom <small>(does not work on firefox and IE/Edge)</small></h4>
539 + With two fingers, zoom in or out.
540 + <br/>
541 + Once a chart is zoomed, auto refreshing stops for all charts. To enable it again, <b>double click</b> a zoomed chart.
542 + </div>
543 + <hr/>
544 + <div class="p">
545 + <h4>Legend Operations</h4>
546 + Tap on the label or value of a dimension, will select / unselect this dimension.
547 + </div>
548 + </div>
549 + </div>
550 + </div>
551 + <div class="modal-footer">
552 + <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
553 + </div>
554 + </div>
555 + </div>
556 + </div>
557 +
558 <div class="modal fade" id="optionsModal" tabindex="-1" role="dialog" aria-labelledby="optionsModalLabel">
559 <div class="modal-dialog modal-lg" role="document">
560 <div class="modal-content">