dimensions can now be turned on and off using the legend
Costa Tsaousis (ktsaou) committed
Dec 25, 2015 at 01:38 UTC
5496d67ace272b7e640388b0e9cec5818f145f4a
2 files changed
+218
-8
web/dashboard.css
+4
@@ -225,6 +225,10 @@ html {
225
padding: 0px;
226
padding-left: 15px;
227
}
228
+.netdata-legend-name.not-selected {
229
+ font-weight: normal;
230
+ opacity: 0.3;
231
+}
232
233
.netdata-chart {
234
position: absolute; /* within .netdata-container */
web/dashboard.js
+214
-8
@@ -542,6 +542,205 @@
542
}
543
}
544
545
+ // ----------------------------------------------------------------------------------------------------------------
546
+ // dimensions selection
547
+
548
+ // FIXME
549
+
550
+ dimensionStatus = function(parent, label, name_div, value_div, color) {
551
+ this.enabled = false;
552
+ this.parent = parent;
553
+ this.label = label;
554
+ this.name_div = null;
555
+ this.value_div = null;
556
+ this.color = '#000';
557
+
558
+ if(parent.selected === parent.unselected)
559
+ this.selected = true;
560
+ else
561
+ this.selected = false;
562
+
563
+ this.setOptions(name_div, value_div, color);
564
+ }
565
+
566
+ dimensionStatus.prototype.invalidate = function() {
567
+ this.name_div = null;
568
+ this.value_div = null;
569
+ this.enabled = false;
570
+ }
571
+
572
+ dimensionStatus.prototype.setOptions = function(name_div, value_div, color) {
573
+ this.color = color;
574
+
575
+ if(this.name_div != name_div) {
576
+ this.name_div = name_div;
577
+ this.name_div.title = this.label;
578
+ this.name_div.style.color = this.color;
579
+ if(this.selected === false)
580
+ this.name_div.className = 'netdata-legend-name not-selected';
581
+ else
582
+ this.name_div.className = 'netdata-legend-name selected';
583
+ }
584
+
585
+ if(this.value_div != value_div) {
586
+ this.value_div = value_div;
587
+ this.value_div.title = this.label;
588
+ this.value_div.style.color = this.color;
589
+ if(this.selected === false)
590
+ this.value_div.className = 'netdata-legend-value not-selected';
591
+ else
592
+ this.value_div.className = 'netdata-legend-value selected';
593
+ }
594
+
595
+ this.enabled = true;
596
+ this.setHandler();
597
+ }
598
+
599
+ dimensionStatus.prototype.setHandler = function() {
600
+ if(this.enabled === false) return;
601
+
602
+ var ds = this;
603
+
604
+ // this.name_div.onmousedown = this.value_div.onmousedown = function(e) {
605
+ this.name_div.onclick = this.value_div.onclick = function(e) {
606
+ e.preventDefault();
607
+ if(ds.isSelected()) {
608
+ // this is selected
609
+ if(e.shiftKey === true || e.ctrlKey === true) {
610
+ // control or shift key is pressed -> unselect this (except is none will remain selected, in which case select all)
611
+ ds.unselect();
612
+
613
+ if(ds.parent.countSelected() === 0)
614
+ ds.parent.selectAll();
615
+ }
616
+ else {
617
+ // no key is pressed -> select only this (except if it is the only selected already, in which case select all)
618
+ if(ds.parent.countSelected() === 1) {
619
+ ds.parent.selectAll();
620
+ }
621
+ else {
622
+ ds.parent.selectNone();
623
+ ds.select();
624
+ }
625
+ }
626
+ }
627
+ else {
628
+ // this is not selected
629
+ if(e.shiftKey === true || e.ctrlKey === true) {
630
+ // control or shift key is pressed -> select this too
631
+ ds.select();
632
+ }
633
+ else {
634
+ // no key is pressed -> select only this
635
+ ds.parent.selectNone();
636
+ ds.select();
637
+ }
638
+ }
639
+
640
+ ds.parent.state.redrawChart();
641
+ }
642
+ }
643
+
644
+ dimensionStatus.prototype.select = function() {
645
+ if(this.enabled === false) return;
646
+
647
+ this.name_div.className = 'netdata-legend-name selected';
648
+ this.value_div.className = 'netdata-legend-value selected';
649
+ this.selected = true;
650
+ }
651
+
652
+ dimensionStatus.prototype.unselect = function() {
653
+ if(this.enabled === false) return;
654
+
655
+ this.name_div.className = 'netdata-legend-name not-selected';
656
+ this.value_div.className = 'netdata-legend-value hidden';
657
+ this.selected = false;
658
+ }
659
+
660
+ dimensionStatus.prototype.isSelected = function() {
661
+ return(this.enabled === true && this.selected === true);
662
+ }
663
+
664
+ // ----------------------------------------------------------------------------------------------------------------
665
+
666
+ dimensionsVisibility = function(state) {
667
+ this.state = state;
668
+ this.len = 0;
669
+ this.dimensions = {};
670
+ this.selected_count = 0;
671
+ this.unselected_count = 0;
672
+ }
673
+
674
+ dimensionsVisibility.prototype.dimensionAdd = function(label, name_div, value_div, color) {
675
+ if(typeof this.dimensions[label] === 'undefined') {
676
+ this.len++;
677
+ this.dimensions[label] = new dimensionStatus(this, label, name_div, value_div, color);
678
+ }
679
+ else
680
+ this.dimensions[label].setOptions(name_div, value_div, color);
681
+
682
+ return this.dimensions[label];
683
+ }
684
+
685
+ dimensionsVisibility.prototype.dimensionGet = function(label) {
686
+ return this.dimensions[label];
687
+ }
688
+
689
+ dimensionsVisibility.prototype.invalidateAll = function() {
690
+ for(var d in this.dimensions)
691
+ this.dimensions[d].invalidate();
692
+ }
693
+
694
+ dimensionsVisibility.prototype.selectAll = function() {
695
+ for(var d in this.dimensions)
696
+ this.dimensions[d].select();
697
+ }
698
+
699
+ dimensionsVisibility.prototype.countSelected = function() {
700
+ var i = 0;
701
+ for(var d in this.dimensions)
702
+ if(this.dimensions[d].isSelected()) i++;
703
+
704
+ return i;
705
+ }
706
+
707
+ dimensionsVisibility.prototype.selectNone = function() {
708
+ for(var d in this.dimensions)
709
+ this.dimensions[d].unselect();
710
+ }
711
+
712
+ dimensionsVisibility.prototype.selected2BooleanArray = function(array) {
713
+ var ret = new Array();
714
+ this.selected_count = 0;
715
+ this.unselected_count = 0;
716
+
717
+ for(var i = 0, len = array.length; i < len ; i++) {
718
+ var ds = this.dimensions[array[i]];
719
+ if(typeof ds === 'undefined') {
720
+ // console.log(array[i] + ' is not found');
721
+ ret.push(false);
722
+ continue;
723
+ }
724
+
725
+ if(ds.isSelected()) {
726
+ ret.push(true);
727
+ this.selected_count++;
728
+ }
729
+ else {
730
+ ret.push(false);
731
+ this.unselected_count++;
732
+ }
733
+ }
734
+
735
+ if(this.selected_count === 0 && this.unselected_count !== 0) {
736
+ this.selectAll();
737
+ return this.selected2BooleanArray(array);
738
+ }
739
+
740
+ return ret;
741
+ }
742
+
743
+
744
// ----------------------------------------------------------------------------------------------------------------
745
// Our state object, where all per-chart values are stored
746
@@ -640,6 +839,8 @@
839
netdata_first: 0, // milliseconds - the first timestamp in netdata
840
netdata_last: 0, // milliseconds - the last timestamp in netdata
841
842
+ dimensions_visibility: new dimensionsVisibility(this),
843
+
844
current: null, // auto, pan, zoom
845
// this is a pointer to one of the sub-classes below
846
@@ -1326,6 +1527,9 @@
1527
if(this.debug === true)
1528
this.log('updating Legend DOM');
1529
1530
+ // mark all dimensions as invalid
1531
+ this.dimensions_visibility.invalidateAll();
1532
+
1533
var self = $(this.element);
1534
var genLabel = function(state, parent, name, count) {
1535
var color = state._chartDimensionColor(name);
@@ -1347,10 +1551,8 @@
1551
1552
var label = state.element_legend_childs.series[name];
1553
1350
- label.name.className += ' netdata-legend-name';
1351
- label.value.className += ' netdata-legend-value';
1352
- label.name.title = name;
1353
- label.value.title = name;
1554
+ // create the dimension visibility tracking for this label
1555
+ var ds = state.dimensions_visibility.dimensionAdd(name, label.name, label.value, color);
1556
1557
var rgb = NETDATA.colorHex2Rgb(color);
1558
label.name.innerHTML = '<table class="netdata-legend-name-table-'
@@ -1362,9 +1564,6 @@
1564
var text = document.createTextNode(' ' + name);
1565
label.name.appendChild(text);
1566
1365
- label.name.style.color = color;
1366
- label.value.style.color = color;
1367
-
1567
if(count > 0)
1568
parent.appendChild(document.createElement('br'));
1569
@@ -1622,6 +1821,11 @@
1821
this.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.data_points + ' library: ' + this.library_name);
1822
}
1823
1824
+ chartState.prototype.redrawChart = function() {
1825
+ if(this.data !== null)
1826
+ this.updateChartWithData(this.data);
1827
+ }
1828
+
1829
chartState.prototype.updateChartWithData = function(data) {
1830
if(this.debug === true)
1831
this.log('got data from netdata server');
@@ -2858,7 +3062,8 @@
3062
file: data.result.data,
3063
colors: state.chartColors(),
3064
labels: data.result.labels,
2861
- labelsDivWidth: state.chartWidth() - 70
3065
+ labelsDivWidth: state.chartWidth() - 70,
3066
+ visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names)
3067
}
3068
3069
if(state.current.name === 'pan') {
@@ -2981,6 +3186,7 @@
3186
highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha') || null, // TOO SLOW: (chart_type === 'stacked')?0.7:0.5,
3187
3188
pointClickCallback: self.data('dygraph-pointclickcallback') || undefined,
3189
+ visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names),
3190
axes: {
3191
x: {
3192
pixelsPerLabel: 50,