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">×</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">