@cryptotaxi247 / netdata-1 / commits / a91d910ed

global selection sync moved as a common feature to all charts libraries; converted the state object to use javascript constructors and prototypes as it should

Costa Tsaousis (ktsaou) committed Dec 11, 2015 at 01:00 UTC a91d910edf2e8eda14c9567b88d11efecfd725c4
1 file changed +773 -757
web/dashboard.js
+773 -757
@@ -356,10 +356,10 @@
356 // ----------------------------------------------------------------------------------------------------------------
357 // Our state object, where all per-chart values are stored
358
359 - NETDATA.chartInitialState = function(element) {
359 + chartState = function(element) {
360 self = $(element);
361
362 - var state = {
362 + Object.assign(this, {
363 uuid: NETDATA.guid(), // GUID - a unique identifier for the chart
364 id: self.data('netdata'), // string - the name of chart
365
@@ -472,789 +472,898 @@
472
473 refresh_dt_ms: 0, // milliseconds - the time the last refresh took
474 refresh_dt_element_name: self.data('dt-element-name') || null, // string - the element to print refresh_dt_ms
475 - refresh_dt_element: null,
475 + refresh_dt_element: null
476 + });
477 + }
478
477 - log: function(msg) {
478 - console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
479 - },
479 + // ----------------------------------------------------------------------------------------------------------------
480 + // global selection sync
481
481 - delaySelectionSync: function() {
482 - NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
483 - },
482 + NETDATA.globalSelectionSync = {
483 + state: null,
484 + dont_sync_before: 0,
485 + slaves: []
486 + };
487
485 - setSelection: function(t) {
486 - if(typeof this.library.setSelection == 'function') {
487 - if(this.library.setSelection(this, t))
488 - this.selected = true;
489 - else
490 - this.selected = false;
491 - }
492 - else this.selected = true;
488 + // prevent to global selection sync for some time
489 + chartState.prototype.globalSelectionSyncDelay = function() {
490 + if(!NETDATA.options.current.sync_selection) return;
491 + NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
492 + }
493
494 - if(this.selected && this.debug) this.log('selection set to ' + t.toString());
494 + // can we globally apply selection sync?
495 + chartState.prototype.globalSelectionSyncAbility = function() {
496 + if(!NETDATA.options.current.sync_selection) return false;
497 + if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime()) return false;
498 + return true;
499 + }
500
496 - return this.selected;
497 - },
501 + // this chart is the master of the global selection sync
502 + chartState.prototype.globalSelectionSyncBeMaster = function() {
503 + // am I the master?
504 + if(NETDATA.globalSelectionSync.state == this) {
505 + if(this.debug) this.log('sync: I am the master already.');
506 + return;
507 + }
508
499 - clearSelection: function() {
500 - if(this.selected) {
501 - if(typeof this.library.clearSelection == 'function') {
502 - if(this.library.clearSelection(this))
503 - this.selected = false;
504 - else
505 - this.selected = true;
506 - }
507 - else this.selected = false;
508 -
509 - if(!this.selected && this.debug) this.log('selection cleared');
510 - }
509 + if(NETDATA.globalSelectionSync.state) {
510 + if(this.debug) this.log('sync: I am not the sync master. Resetting global sync.');
511 + this.globalSelectionSyncStop();
512 + }
513
512 - return this.selected;
513 - },
514 + // become the master
515 + if(this.debug) this.log('sync: becoming sync master.');
516 + this.selected = true;
517 + NETDATA.globalSelectionSync.state = this;
518
515 - // find if a timestamp (ms) is shown in the current chart
516 - timeIsVisible: function(t) {
517 - if(t >= this.current.after_ms && t <= this.current.before_ms)
518 - return true;
519 - return false;
520 - },
519 + // find the all slaves
520 + var self = this;
521 + $.each(NETDATA.options.targets, function(i, target) {
522 + var st = NETDATA.chartState(target);
523 + if(st == self) {
524 + if(self.debug) st.log('sync: not adding me to sync');
525 + }
526 + else if(st.globalSelectionSyncIsEligible()) {
527 + if(self.debug) st.log('sync: adding to sync as slave');
528 + st.globalSelectionSyncBeSlave();
529 + }
530 + });
531 + }
532
522 - calculateRowForTime: function(t) {
523 - if(!this.timeIsVisible(t)) return -1;
524 - return Math.floor((t - this.current.after_ms) / this.current.view_update_every);
525 - },
533 + // can the chart participate to the global selection sync as a slave?
534 + chartState.prototype.globalSelectionSyncIsEligible = function() {
535 + if(this.library != null && typeof this.library.setSelection == 'function' && this.isVisible())
536 + return true;
537 + return false;
538 + }
539
527 - pauseChart: function() {
528 - if(!this.paused) {
529 - if(this.debug) this.log('paused');
530 - this.paused = true;
531 - }
532 - },
540 + // this chart is a slave of the global selection sync
541 + chartState.prototype.globalSelectionSyncBeSlave = function() {
542 + if(NETDATA.globalSelectionSync.state != this)
543 + NETDATA.globalSelectionSync.slaves.push(this);
544 + }
545
534 - unpauseChart: function() {
535 - if(this.paused) {
536 - if(this.debug) this.log('unpaused');
537 - this.paused = false;
546 + // sync all the visible charts to the given time
547 + // this is to be called from the chart libraries
548 + chartState.prototype.globalSelectionSync = function(t) {
549 + if(!this.globalSelectionSyncAbility()) {
550 + if(this.debug) this.log('sync: cannot sync (yet?).');
551 + return;
552 + }
553 +
554 + if(this.debug) this.log('sync: trying to be sync master.');
555 + this.globalSelectionSyncBeMaster();
556 +
557 + var self = this;
558 + $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
559 + if(st == self) {
560 + // since we are the sync master, we should not call state.setSelection()
561 + // the chart library is taking care of visualizing our selection.
562 + if(self.debug) st.log('sync: ignoring me from set selection');
563 + }
564 + else {
565 + if(self.debug) st.log('sync: showing master selection');
566 + st.setSelection(t);
567 + }
568 + });
569 + }
570 +
571 + // stop syncing all charts to the given time
572 + chartState.prototype.globalSelectionSyncStop = function() {
573 + if(NETDATA.globalSelectionSync.slaves.length) {
574 + if(this.debug) this.log('sync: cleaning up...');
575 + var self = this;
576 + $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
577 + if(st == self) {
578 + if(self.debug) st.log('sync: not adding me to sync stop');
579 }
539 - },
580 + else {
581 + if(self.debug) st.log('sync: removed slave from sync');
582 + st.clearSelection();
583 + }
584 + });
585
541 - resetChart: function() {
542 - NETDATA.globalPanAndZoom.clearMaster();
543 - this.follows_global = 0;
586 + NETDATA.globalSelectionSync.slaves = [];
587 + NETDATA.globalSelectionSync.state = null;
588 + }
589
545 - this.clearSelection();
590 + // since we are the sync master, we should not call this.clearSelection()
591 + // dygraphs is taking care of visualizing our selection.
592 + this.selected = false;
593 + }
594
547 - this.setMode('auto');
548 - this.current.force_before_ms = null;
549 - this.current.force_after_ms = null;
550 - this.current.last_autorefreshed = 0;
551 - this.paused = false;
595 + chartState.prototype.setSelection = function(t) {
596 + if(typeof this.library.setSelection == 'function') {
597 + if(this.library.setSelection(this, t))
598 + this.selected = true;
599 + else
600 this.selected = false;
553 - this.enabled = true;
554 - this.debug = false;
555 -
556 - // do not update the chart here
557 - // or the chart will flip-flop when it is the master
558 - // of a selection sync and another chart becomes
559 - // the new master
560 - if(!NETDATA.options.current.sync_pan_and_zoom)
561 - state.updateChart();
562 - },
601 + }
602 + else this.selected = true;
603
564 - setMode: function(m) {
565 - if(this.current) {
566 - if(this.current.name == m) return;
567 -
568 - this[m].url = this.current.url;
569 - this[m].last_autorefreshed = this.current.last_autorefreshed;
570 - this[m].view_update_every = this.current.view_update_every;
571 - this[m].after_ms = this.current.after_ms;
572 - this[m].before_ms = this.current.before_ms;
573 - this[m].points = this.current.points;
574 - this[m].data = this.current.data;
575 - this[m].requested_before_ms = this.current.requested_before_ms;
576 - this[m].requested_after_ms = this.current.requested_after_ms;
577 - this[m].first_entry_ms = this.current.first_entry_ms;
578 - this[m].last_entry_ms = this.current.last_entry_ms;
579 - }
604 + if(this.selected && this.debug) this.log('selection set to ' + t.toString());
605 +
606 + return this.selected;
607 + }
608
581 - if(m == 'auto')
582 - this.current = this.auto;
583 - else if(m == 'pan')
584 - this.current = this.pan;
585 - else if(m == 'zoom')
586 - this.current = this.zoom;
609 + chartState.prototype.clearSelection = function() {
610 + if(this.selected) {
611 + if(typeof this.library.clearSelection == 'function') {
612 + if(this.library.clearSelection(this))
613 + this.selected = false;
614 else
588 - this.current = this.auto;
615 + this.selected = true;
616 + }
617 + else this.selected = false;
618 +
619 + if(!this.selected && this.debug) this.log('selection cleared');
620 + }
621
590 - this.current.force_before_ms = null;
591 - this.current.force_after_ms = null;
622 + return this.selected;
623 + }
624
593 - if(this.debug) this.log('mode set to ' + this.current.name);
594 - },
625 + // find if a timestamp (ms) is shown in the current chart
626 + chartState.prototype.timeIsVisible = function(t) {
627 + if(t >= this.current.after_ms && t <= this.current.before_ms)
628 + return true;
629 + return false;
630 + },
631
596 - _minPanOrZoomStep: function() {
597 - return (((this.current.before_ms - this.current.after_ms) / this.current.points) * ((this.current.points * 5 / 100) + 1) );
598 - // return this.current.view_update_every * 10;
599 - },
632 + chartState.prototype.calculateRowForTime = function(t) {
633 + if(!this.timeIsVisible(t)) return -1;
634 + return Math.floor((t - this.current.after_ms) / this.current.view_update_every);
635 + }
636
601 - _shouldBeMoved: function(old_after, old_before, new_after, new_before) {
602 - var dt_after = Math.abs(old_after - new_after);
603 - var dt_before = Math.abs(old_before - new_before);
604 - var old_range = old_before - old_after;
637 + // ----------------------------------------------------------------------------------------------------------------
638
606 - var new_range = new_before - new_after;
607 - var dt = Math.abs(old_range - new_range);
608 - var step = Math.max(dt_after, dt_before, dt);
639 + // console logging
640 + chartState.prototype.log = function(msg) {
641 + console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
642 + }
643
610 - var min_step = this._minPanOrZoomStep();
611 - if(new_range < old_range && new_range / this.chartWidth() < 100) {
612 - if(this.debug) this.log('_shouldBeMoved(' + (new_after / 1000).toString() + ' - ' + (new_before / 1000).toString() + '): minimum point size: 0.10, wanted point size: ' + (new_range / this.chartWidth() / 1000).toString() + ': TOO SMALL RANGE');
613 - return false;
614 - }
644 + chartState.prototype.pauseChart = function() {
645 + if(!this.paused) {
646 + if(this.debug) this.log('paused');
647 + this.paused = true;
648 + }
649 + }
650
616 - if(step >= min_step) {
617 - if(this.debug) this.log('_shouldBeMoved(' + (new_after / 1000).toString() + ' - ' + (new_before / 1000).toString() + '): minimum step: ' + (min_step / 1000).toString() + ', this step: ' + (step / 1000).toString() + ': YES');
618 - return true;
619 - }
620 - else {
621 - if(this.debug) this.log('_shouldBeMoved(' + (new_after / 1000).toString() + ' - ' + (new_before / 1000).toString() + '): minimum step: ' + (min_step / 1000).toString() + ', this step: ' + (step / 1000).toString() + ': NO');
622 - return false;
623 - }
624 - },
651 + chartState.prototype.unpauseChart = function() {
652 + if(this.paused) {
653 + if(this.debug) this.log('unpaused');
654 + this.paused = false;
655 + }
656 + }
657
626 - updateChartPanOrZoom: function(after, before, callback) {
627 - var move = false;
658 + chartState.prototype.resetChart = function() {
659 + NETDATA.globalPanAndZoom.clearMaster();
660 + this.follows_global = 0;
661 +
662 + this.clearSelection();
663 +
664 + this.setMode('auto');
665 + this.current.force_before_ms = null;
666 + this.current.force_after_ms = null;
667 + this.current.last_autorefreshed = 0;
668 + this.paused = false;
669 + this.selected = false;
670 + this.enabled = true;
671 + this.debug = false;
672 +
673 + // do not update the chart here
674 + // or the chart will flip-flop when it is the master
675 + // of a selection sync and another chart becomes
676 + // the new master
677 + if(!NETDATA.options.current.sync_pan_and_zoom)
678 + state.updateChart();
679 + }
680
629 - if(this.current.name == 'auto') {
630 - if(this.debug) this.log('updateChartPanOrZoom(): caller did not set proper mode');
631 - this.setMode('pan');
632 - }
681 + chartState.prototype.setMode = function(m) {
682 + if(this.current) {
683 + if(this.current.name == m) return;
684 +
685 + this[m].url = this.current.url;
686 + this[m].last_autorefreshed = this.current.last_autorefreshed;
687 + this[m].view_update_every = this.current.view_update_every;
688 + this[m].after_ms = this.current.after_ms;
689 + this[m].before_ms = this.current.before_ms;
690 + this[m].points = this.current.points;
691 + this[m].data = this.current.data;
692 + this[m].requested_before_ms = this.current.requested_before_ms;
693 + this[m].requested_after_ms = this.current.requested_after_ms;
694 + this[m].first_entry_ms = this.current.first_entry_ms;
695 + this[m].last_entry_ms = this.current.last_entry_ms;
696 + }
697
634 - if(!this.current.force_after_ms || !this.current.force_before_ms) {
635 - if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): INIT');
636 - move = true;
637 - }
638 - else if(this._shouldBeMoved(this.current.force_after_ms, this.current.force_before_ms, after, before) && this._shouldBeMoved(this.current.after_ms, this.current.before_ms, after, before)) {
639 - if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): FORCE CHANGE from ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
640 - move = true;
641 - }
642 - else if(this._shouldBeMoved(this.current.requested_after_ms, this.current.requested_before_ms, after, before) && this._shouldBeMoved(this.current.after_ms, this.current.before_ms, after, before)) {
643 - if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): REQUESTED CHANGE from ' + (this.current.requested_after_ms / 1000).toString() + ' - ' + (this.current.requested_before_ms / 1000).toString());
644 - move = true;
645 - }
698 + if(m == 'auto')
699 + this.current = this.auto;
700 + else if(m == 'pan')
701 + this.current = this.pan;
702 + else if(m == 'zoom')
703 + this.current = this.zoom;
704 + else
705 + this.current = this.auto;
706
647 - if(move) {
648 - var now = new Date().getTime();
649 - NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
650 - NETDATA.globalPanAndZoom.setMaster(this, after, before);
707 + this.current.force_before_ms = null;
708 + this.current.force_after_ms = null;
709
652 - this.current.force_after_ms = after;
653 - this.current.force_before_ms = before;
654 - this.updateChart(callback);
655 - return true;
656 - }
710 + if(this.debug) this.log('mode set to ' + this.current.name);
711 + }
712
658 - if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): IGNORE');
659 - if(typeof callback != 'undefined') callback();
660 - return false;
661 - },
713 + chartState.prototype._minPanOrZoomStep = function() {
714 + return (((this.current.before_ms - this.current.after_ms) / this.current.points) * ((this.current.points * 5 / 100) + 1) );
715 + // return this.current.view_update_every * 10;
716 + }
717
663 - updateLegendDOM: function() {
664 - if(!this.hasLegend()) return;
718 + chartState.prototype._shouldBeMoved = function(old_after, old_before, new_after, new_before) {
719 + var dt_after = Math.abs(old_after - new_after);
720 + var dt_before = Math.abs(old_before - new_before);
721 + var old_range = old_before - old_after;
722
666 - var needed = false;
723 + var new_range = new_before - new_after;
724 + var dt = Math.abs(old_range - new_range);
725 + var step = Math.max(dt_after, dt_before, dt);
726
668 - // check that the legend DOM is up to date for the downloaded dimensions
669 - if(typeof this.element_legend_childs.series != 'object')
670 - needed = true;
671 - else if(!this.current.data)
672 - needed = true;
673 - else {
674 - for(var i = 0; i < this.current.data.dimension_names.length; i++) {
675 - if(typeof this.element_legend_childs.series[this.current.data.dimension_names[i]] == 'undefined') {
676 - needed = true;
677 - break;
678 - }
679 - else if(!this.follows_global) {
680 - // FIXME update to latest value
681 - ;
682 - }
683 - }
684 - }
727 + var min_step = this._minPanOrZoomStep();
728 + if(new_range < old_range && new_range / this.chartWidth() < 100) {
729 + if(this.debug) this.log('_shouldBeMoved(' + (new_after / 1000).toString() + ' - ' + (new_before / 1000).toString() + '): minimum point size: 0.10, wanted point size: ' + (new_range / this.chartWidth() / 1000).toString() + ': TOO SMALL RANGE');
730 + return false;
731 + }
732
686 - if(!needed) return;
733 + if(step >= min_step) {
734 + if(this.debug) this.log('_shouldBeMoved(' + (new_after / 1000).toString() + ' - ' + (new_before / 1000).toString() + '): minimum step: ' + (min_step / 1000).toString() + ', this step: ' + (step / 1000).toString() + ': YES');
735 + return true;
736 + }
737 + else {
738 + if(this.debug) this.log('_shouldBeMoved(' + (new_after / 1000).toString() + ' - ' + (new_before / 1000).toString() + '): minimum step: ' + (min_step / 1000).toString() + ', this step: ' + (step / 1000).toString() + ': NO');
739 + return false;
740 + }
741 + }
742
688 - if(this.debug) this.log('updating Legend DOM');
743 + chartState.prototype.updateChartPanOrZoom = function(after, before, callback) {
744 + var move = false;
745
690 - this.element_legend_childs = {
691 - title_date: document.createElement('span'),
692 - title_time: document.createElement('span'),
693 - title_units: document.createElement('span'),
694 - series: {}
695 - };
746 + if(this.current.name == 'auto') {
747 + if(this.debug) this.log('updateChartPanOrZoom(): caller did not set proper mode');
748 + this.setMode('pan');
749 + }
750
697 - this.element_legend_childs.title_date.className += "netdata-legend-title-date";
698 - this.element_legend.appendChild(this.element_legend_childs.title_date);
751 + if(!this.current.force_after_ms || !this.current.force_before_ms) {
752 + if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): INIT');
753 + move = true;
754 + }
755 + else if(this._shouldBeMoved(this.current.force_after_ms, this.current.force_before_ms, after, before) && this._shouldBeMoved(this.current.after_ms, this.current.before_ms, after, before)) {
756 + if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): FORCE CHANGE from ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
757 + move = true;
758 + }
759 + else if(this._shouldBeMoved(this.current.requested_after_ms, this.current.requested_before_ms, after, before) && this._shouldBeMoved(this.current.after_ms, this.current.before_ms, after, before)) {
760 + if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): REQUESTED CHANGE from ' + (this.current.requested_after_ms / 1000).toString() + ' - ' + (this.current.requested_before_ms / 1000).toString());
761 + move = true;
762 + }
763
700 - this.element_legend.appendChild(document.createElement('br'));
764 + if(move) {
765 + var now = new Date().getTime();
766 + NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
767 + NETDATA.globalPanAndZoom.setMaster(this, after, before);
768
702 - this.element_legend_childs.title_time.className += "netdata-legend-title-time";
703 - this.element_legend.appendChild(this.element_legend_childs.title_time);
769 + this.current.force_after_ms = after;
770 + this.current.force_before_ms = before;
771 + this.updateChart(callback);
772 + return true;
773 + }
774
705 - this.element_legend.appendChild(document.createElement('br'));
775 + if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): IGNORE');
776 + if(typeof callback != 'undefined') callback();
777 + return false;
778 + }
779
707 - this.element_legend_childs.title_units.className += "netdata-legend-title-units";
708 - this.element_legend.appendChild(this.element_legend_childs.title_units);
780 + chartState.prototype.updateLegendDOM = function() {
781 + if(!this.hasLegend()) return;
782
710 - this.element_legend.appendChild(document.createElement('br'));
783 + var needed = false;
784
712 - var el = document.createElement('div');
713 - el.className += "netdata-legend-series";
714 - this.element_legend.appendChild(el);
785 + // check that the legend DOM is up to date for the downloaded dimensions
786 + if(typeof this.element_legend_childs.series != 'object')
787 + needed = true;
788 + else if(!this.current.data)
789 + needed = true;
790 + else {
791 + for(var i = 0; i < this.current.data.dimension_names.length; i++) {
792 + if(typeof this.element_legend_childs.series[this.current.data.dimension_names[i]] == 'undefined') {
793 + needed = true;
794 + break;
795 + }
796 + else if(!this.follows_global) {
797 + // FIXME update to latest value
798 + ;
799 + }
800 + }
801 + }
802
716 - var genLabel = function(state, parent, name, count) {
717 - var c = count % NETDATA.colors.length;
803 + if(!needed) return;
804
719 - state.element_legend_childs.series[name] = {
720 - name: document.createElement('span'),
721 - value: document.createElement('span')
722 - };
805 + if(this.debug) this.log('updating Legend DOM');
806
724 - var label = state.element_legend_childs.series[name];
807 + this.element_legend_childs = {
808 + title_date: document.createElement('span'),
809 + title_time: document.createElement('span'),
810 + title_units: document.createElement('span'),
811 + series: {}
812 + };
813
726 - label.name.className += 'netdata-legend-name';
727 - label.value.className += 'netdata-legend-value';
728 - label.value.title = name;
814 + this.element_legend_childs.title_date.className += "netdata-legend-title-date";
815 + this.element_legend.appendChild(this.element_legend_childs.title_date);
816
730 - var table = document.createElement('table');
731 - table.innerHTML = '<tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr>';
732 - table.className += 'netdata-legend-name-table-' + state.chart.chart_type;
817 + this.element_legend.appendChild(document.createElement('br'));
818
734 - var rgb = NETDATA.colorHex2Rgb(NETDATA.colors[c]);
735 - table.style.backgroundColor = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current.color_fill_opacity[state.chart.chart_type] + ')';
819 + this.element_legend_childs.title_time.className += "netdata-legend-title-time";
820 + this.element_legend.appendChild(this.element_legend_childs.title_time);
821
737 - var text = document.createTextNode(' ' + name);
822 + this.element_legend.appendChild(document.createElement('br'));
823
739 - label.name.appendChild(table);
740 - label.name.appendChild(text);
824 + this.element_legend_childs.title_units.className += "netdata-legend-title-units";
825 + this.element_legend.appendChild(this.element_legend_childs.title_units);
826
742 - label.name.style.color = NETDATA.colors[c];
743 - label.value.style.color = NETDATA.colors[c];
827 + this.element_legend.appendChild(document.createElement('br'));
828
745 - if(count > 0)
746 - parent.appendChild(document.createElement('br'));
829 + var el = document.createElement('div');
830 + el.className += "netdata-legend-series";
831 + this.element_legend.appendChild(el);
832
748 - parent.appendChild(label.name);
749 - parent.appendChild(label.value);
750 - };
833 + var genLabel = function(state, parent, name, count) {
834 + var c = count % NETDATA.colors.length;
835
752 - if(this.current.data) {
753 - var me = this;
754 - $.each(me.current.data.dimension_names, function(i, d) {
755 - genLabel(me, el, d, i);
756 - });
757 - }
758 - else {
759 - var me = this;
760 - $.each(me.chart.dimensions, function(i, d) {
761 - genLabel(me, el, d.name, i);
762 - });
763 - }
836 + state.element_legend_childs.series[name] = {
837 + name: document.createElement('span'),
838 + value: document.createElement('span')
839 + };
840
765 - // create a hidden div to be used for hidding
766 - // the original legend of the chart library
767 - el = document.createElement('div');
768 - this.element_legend.appendChild(el);
769 - el.style.display = 'none';
841 + var label = state.element_legend_childs.series[name];
842
771 - this.element_legend_childs.hidden = document.createElement('div');
772 - el.appendChild(this.element_legend_childs.hidden);
773 - },
843 + label.name.className += 'netdata-legend-name';
844 + label.value.className += 'netdata-legend-value';
845 + label.value.title = name;
846
775 - createChartDOM: function() {
776 - var html = "";
847 + var table = document.createElement('table');
848 + table.innerHTML = '<tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr>';
849 + table.className += 'netdata-legend-name-table-' + state.chart.chart_type;
850
778 - if(this.hasLegend()) {
779 - this.element_chart_id = this.library_name + '-' + this.uuid + '-chart';
780 - this.element_legend_id = this.library_name + '-' + this.uuid + '-legend';
851 + var rgb = NETDATA.colorHex2Rgb(NETDATA.colors[c]);
852 + table.style.backgroundColor = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current.color_fill_opacity[state.chart.chart_type] + ')';
853
782 - html += '<div class="netdata-chart-with-legend-right netdata-'
783 - + this.library_name + '-chart-with-legend-right" id="'
784 - + this.element_chart_id
785 - + '"></div>';
854 + var text = document.createTextNode(' ' + name);
855
787 - html += '<div class="netdata-chart-legend netdata-'
788 - + this.library_name + '-legend" id="'
789 - + this.element_legend_id
790 - + '"></div>';
791 - }
792 - else {
793 - this.element_chart_id = this.library_name + '-' + this.uuid + '-chart';
794 - html += '<div class="netdata-chart netdata-'
795 - + this.library_name + '-chart" id="'
796 - + this.element_chart_id
797 - + '"></div>';
798 - }
856 + label.name.appendChild(table);
857 + label.name.appendChild(text);
858
800 - element.innerHTML = html;
801 - this.element_chart = document.getElementById(this.element_chart_id);
802 - $(this.element_chart).data('netdata-state-object', this);
859 + label.name.style.color = NETDATA.colors[c];
860 + label.value.style.color = NETDATA.colors[c];
861
804 - if(this.hasLegend()) {
805 - this.element_legend = document.getElementById(this.element_legend_id);
806 - $(this.element_legend).data('netdata-state-object', this);
807 - this.updateLegendDOM();
808 - }
809 - },
862 + if(count > 0)
863 + parent.appendChild(document.createElement('br'));
864
811 - hasLegend: function() {
812 - if(this.element_legend) return true;
865 + parent.appendChild(label.name);
866 + parent.appendChild(label.value);
867 + };
868
814 - if(this.library && this.library.legend(this) == 'right-side') {
815 - var legend = $(this.element).data('legend') || 'yes';
816 - if(legend == 'no') return false;
817 - return true;
818 - }
869 + if(this.current.data) {
870 + var me = this;
871 + $.each(me.current.data.dimension_names, function(i, d) {
872 + genLabel(me, el, d, i);
873 + });
874 + }
875 + else {
876 + var me = this;
877 + $.each(me.chart.dimensions, function(i, d) {
878 + genLabel(me, el, d.name, i);
879 + });
880 + }
881
820 - return false;
821 - },
882 + // create a hidden div to be used for hidding
883 + // the original legend of the chart library
884 + el = document.createElement('div');
885 + this.element_legend.appendChild(el);
886 + el.style.display = 'none';
887
823 - legendWidth: function() {
824 - return (this.hasLegend())?110:0;
825 - },
888 + this.element_legend_childs.hidden = document.createElement('div');
889 + el.appendChild(this.element_legend_childs.hidden);
890 + }
891
827 - legendHeight: function() {
828 - return $(this.element).height();
829 - },
892 + chartState.prototype.createChartDOM = function() {
893 + var html = "";
894
831 - chartWidth: function() {
832 - return $(this.element).width() - this.legendWidth();
833 - },
895 + if(this.hasLegend()) {
896 + this.element_chart_id = this.library_name + '-' + this.uuid + '-chart';
897 + this.element_legend_id = this.library_name + '-' + this.uuid + '-legend';
898
835 - chartHeight: function() {
836 - return $(this.element).height();
837 - },
899 + html += '<div class="netdata-chart-with-legend-right netdata-'
900 + + this.library_name + '-chart-with-legend-right" id="'
901 + + this.element_chart_id
902 + + '"></div>';
903
839 - chartPixelsPerPoint: function() {
840 - // force an options provided detail
841 - var px = this.pixels_per_point;
904 + html += '<div class="netdata-chart-legend netdata-'
905 + + this.library_name + '-legend" id="'
906 + + this.element_legend_id
907 + + '"></div>';
908 + }
909 + else {
910 + this.element_chart_id = this.library_name + '-' + this.uuid + '-chart';
911 + html += '<div class="netdata-chart netdata-'
912 + + this.library_name + '-chart" id="'
913 + + this.element_chart_id
914 + + '"></div>';
915 + }
916
843 - if(this.library && px < this.library.pixels_per_point(this))
844 - px = this.library.pixels_per_point(this);
917 + this.element.innerHTML = html;
918 + this.element_chart = document.getElementById(this.element_chart_id);
919 + $(this.element_chart).data('netdata-state-object', this);
920
846 - if(px < NETDATA.options.current.pixels_per_point)
847 - px = NETDATA.options.current.pixels_per_point;
921 + if(this.hasLegend()) {
922 + this.element_legend = document.getElementById(this.element_legend_id);
923 + $(this.element_legend).data('netdata-state-object', this);
924 + this.updateLegendDOM();
925 + }
926 + }
927
849 - return px;
850 - },
928 + chartState.prototype.hasLegend = function() {
929 + if(this.element_legend) return true;
930
852 - needsResize: function() {
853 - return (this.library && !this.library.autoresize() && this.last_resized < NETDATA.options.last_resized);
854 - },
931 + if(this.library && this.library.legend(this) == 'right-side') {
932 + var legend = $(this.element).data('legend') || 'yes';
933 + if(legend == 'no') return false;
934 + return true;
935 + }
936
856 - resizeChart: function() {
857 - if(this.needsResize()) {
858 - if(this.debug) this.log('forcing re-generation due to window resize.');
859 - this.created_ms = 0;
860 - this.last_resized = new Date().getTime();
861 - }
862 - },
937 + return false;
938 + }
939
864 - chartURL: function() {
865 - var before;
866 - var after;
867 - if(NETDATA.globalPanAndZoom.isActive()) {
868 - after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
869 - before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
870 - this.follows_global = NETDATA.globalPanAndZoom.seq;
871 - }
872 - else {
873 - before = this.current.force_before_ms != null ? Math.round(this.current.force_before_ms / 1000) : this.before;
874 - after = this.current.force_after_ms != null ? Math.round(this.current.force_after_ms / 1000) : this.after;
875 - this.follows_global = 0;
876 - }
940 + chartState.prototype.legendWidth = function() {
941 + return (this.hasLegend())?110:0;
942 + }
943
878 - this.current.requested_after_ms = after * 1000;
879 - this.current.requested_before_ms = before * 1000;
944 + chartState.prototype.legendHeight = function() {
945 + return $(this.element).height();
946 + }
947
881 - this.current.points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
948 + chartState.prototype.chartWidth = function() {
949 + return $(this.element).width() - this.legendWidth();
950 + }
951
883 - // build the data URL
884 - this.current.url = this.chart.data_url;
885 - this.current.url += "&format=" + this.library.format();
886 - this.current.url += "&points=" + this.current.points.toString();
887 - this.current.url += "&group=" + this.method;
888 - this.current.url += "&options=" + this.library.options();
889 - this.current.url += '|jsonwrap';
952 + chartState.prototype.chartHeight = function() {
953 + return $(this.element).height();
954 + }
955
891 - if(after)
892 - this.current.url += "&after=" + after.toString();
956 + chartState.prototype.chartPixelsPerPoint = function() {
957 + // force an options provided detail
958 + var px = this.pixels_per_point;
959
894 - if(before)
895 - this.current.url += "&before=" + before.toString();
960 + if(this.library && px < this.library.pixels_per_point(this))
961 + px = this.library.pixels_per_point(this);
962
897 - if(this.dimensions)
898 - this.current.url += "&dimensions=" + this.dimensions;
963 + if(px < NETDATA.options.current.pixels_per_point)
964 + px = NETDATA.options.current.pixels_per_point;
965
900 - if(NETDATA.options.debug.chart_data_url || this.debug) this.log('chartURL(): ' + this.current.url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.current.points + ' library: ' + this.library_name);
901 - },
966 + return px;
967 + }
968
903 - updateChartWithData: function(data) {
904 - if(this.debug) this.log('got data from netdata server');
905 - this.current.data = data;
969 + chartState.prototype.needsResize = function() {
970 + return (this.library && !this.library.autoresize() && this.last_resized < NETDATA.options.last_resized);
971 + }
972
907 - var started = new Date().getTime();
973 + chartState.prototype.resizeChart = function() {
974 + if(this.needsResize()) {
975 + if(this.debug) this.log('forcing re-generation due to window resize.');
976 + this.created_ms = 0;
977 + this.last_resized = new Date().getTime();
978 + }
979 + }
980
909 - // if the result is JSON, find the latest update-every
910 - if(typeof data == 'object') {
911 - if(typeof data.view_update_every != 'undefined')
912 - this.current.view_update_every = data.view_update_every * 1000;
981 + chartState.prototype.chartURL = function() {
982 + var before;
983 + var after;
984 + if(NETDATA.globalPanAndZoom.isActive()) {
985 + after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
986 + before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
987 + this.follows_global = NETDATA.globalPanAndZoom.seq;
988 + }
989 + else {
990 + before = this.current.force_before_ms != null ? Math.round(this.current.force_before_ms / 1000) : this.before;
991 + after = this.current.force_after_ms != null ? Math.round(this.current.force_after_ms / 1000) : this.after;
992 + this.follows_global = 0;
993 + }
994
914 - if(typeof data.after != 'undefined')
915 - this.current.after_ms = data.after * 1000;
995 + this.current.requested_after_ms = after * 1000;
996 + this.current.requested_before_ms = before * 1000;
997
917 - if(typeof data.before != 'undefined')
918 - this.current.before_ms = data.before * 1000;
998 + this.current.points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
999
920 - if(typeof data.first_entry_t != 'undefined')
921 - this.current.first_entry_ms = data.first_entry_t * 1000;
1000 + // build the data URL
1001 + this.current.url = this.chart.data_url;
1002 + this.current.url += "&format=" + this.library.format();
1003 + this.current.url += "&points=" + this.current.points.toString();
1004 + this.current.url += "&group=" + this.method;
1005 + this.current.url += "&options=" + this.library.options();
1006 + this.current.url += '|jsonwrap';
1007
923 - if(typeof data.last_entry_t != 'undefined')
924 - this.current.last_entry_ms = data.last_entry_t * 1000;
1008 + if(after)
1009 + this.current.url += "&after=" + after.toString();
1010
926 - if(typeof data.points != 'undefined')
927 - this.current.points = data.points;
1011 + if(before)
1012 + this.current.url += "&before=" + before.toString();
1013
929 - data.state = this;
930 - }
1014 + if(this.dimensions)
1015 + this.current.url += "&dimensions=" + this.dimensions;
1016
932 - this.updates_counter++;
1017 + if(NETDATA.options.debug.chart_data_url || this.debug) this.log('chartURL(): ' + this.current.url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.current.points + ' library: ' + this.library_name);
1018 + }
1019
934 - if(this.debug) {
935 - this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
1020 + chartState.prototype.updateChartWithData = function(data) {
1021 + if(this.debug) this.log('got data from netdata server');
1022 + this.current.data = data;
1023
937 - if(this.current.force_after_ms)
938 - this.log('STATUS: forced : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
939 - else
940 - this.log('STATUS: forced: unset');
1024 + var started = new Date().getTime();
1025
942 - this.log('STATUS: requested: ' + (this.current.requested_after_ms / 1000).toString() + ' - ' + (this.current.requested_before_ms / 1000).toString());
943 - this.log('STATUS: rendered : ' + (this.current.after_ms / 1000).toString() + ' - ' + (this.current.before_ms / 1000).toString());
944 - this.log('STATUS: points : ' + (this.current.points).toString() + ', min step: ' + (this._minPanOrZoomStep() / 1000).toString());
945 - }
1026 + // if the result is JSON, find the latest update-every
1027 + if(typeof data == 'object') {
1028 + if(typeof data.view_update_every != 'undefined')
1029 + this.current.view_update_every = data.view_update_every * 1000;
1030
947 - // this may force the chart to be re-created
948 - this.resizeChart();
1031 + if(typeof data.after != 'undefined')
1032 + this.current.after_ms = data.after * 1000;
1033
950 - if(this.updates_since_last_creation >= this.library.max_updates_to_recreate()) {
951 - if(this.debug) this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
952 - this.created_ms = 0;
953 - }
1034 + if(typeof data.before != 'undefined')
1035 + this.current.before_ms = data.before * 1000;
1036
955 - if(this.created_ms && typeof this.library.update == 'function') {
956 - if(this.debug) this.log('updating chart...');
1037 + if(typeof data.first_entry_t != 'undefined')
1038 + this.current.first_entry_ms = data.first_entry_t * 1000;
1039
958 - // check and update the legend
959 - this.updateLegendDOM();
1040 + if(typeof data.last_entry_t != 'undefined')
1041 + this.current.last_entry_ms = data.last_entry_t * 1000;
1042
961 - this.updates_since_last_creation++;
962 - if(NETDATA.options.debug.chart_errors) {
963 - this.library.update(this, data);
964 - }
965 - else {
966 - try {
967 - this.library.update(this, data);
968 - }
969 - catch(err) {
970 - this.error('chart "' + this.id + '" failed to be updated as ' + this.library_name);
971 - }
972 - }
973 - }
974 - else {
975 - if(this.debug) this.log('creating chart...');
1043 + if(typeof data.points != 'undefined')
1044 + this.current.points = data.points;
1045
977 - this.createChartDOM();
978 - this.updates_since_last_creation = 0;
1046 + data.state = this;
1047 + }
1048
980 - if(NETDATA.options.debug.chart_errors) {
981 - this.library.create(this, data);
982 - this.created_ms = new Date().getTime();
983 - }
984 - else {
985 - try {
986 - this.library.create(this, data);
987 - this.created_ms = new Date().getTime();
988 - }
989 - catch(err) {
990 - this.error('chart "' + this.id + '" failed to be created as ' + this.library_name);
991 - }
992 - }
993 - }
1049 + this.updates_counter++;
1050
995 - // update the performance counters
996 - var now = new Date().getTime();
997 -
998 - // don't update last_autorefreshed if this chart is
999 - // forced to be updated with global PanAndZoom
1000 - if(NETDATA.globalPanAndZoom.isActive())
1001 - this.current.last_autorefreshed = 0;
1002 - else
1003 - this.current.last_autorefreshed = now;
1051 + if(this.debug) {
1052 + this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
1053
1005 - this.refresh_dt_ms = now - started;
1006 - NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
1054 + if(this.current.force_after_ms)
1055 + this.log('STATUS: forced : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
1056 + else
1057 + this.log('STATUS: forced: unset');
1058
1008 - if(this.refresh_dt_element)
1009 - this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
1010 - },
1059 + this.log('STATUS: requested: ' + (this.current.requested_after_ms / 1000).toString() + ' - ' + (this.current.requested_before_ms / 1000).toString());
1060 + this.log('STATUS: rendered : ' + (this.current.after_ms / 1000).toString() + ' - ' + (this.current.before_ms / 1000).toString());
1061 + this.log('STATUS: points : ' + (this.current.points).toString() + ', min step: ' + (this._minPanOrZoomStep() / 1000).toString());
1062 + }
1063
1012 - updateChart: function(callback) {
1013 - if(!this.library || !this.library.enabled) {
1014 - this.error('charting library "' + this.library_name + '" is not available');
1015 - if(typeof callback != 'undefined') callback();
1016 - return;
1017 - }
1064 + // this may force the chart to be re-created
1065 + this.resizeChart();
1066
1019 - if(!this.enabled) {
1020 - if(this.debug) this.error('chart "' + this.id + '" is not enabled');
1021 - if(typeof callback != 'undefined') callback();
1022 - return;
1023 - }
1067 + if(this.updates_since_last_creation >= this.library.max_updates_to_recreate()) {
1068 + if(this.debug) this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
1069 + this.created_ms = 0;
1070 + }
1071 +
1072 + if(this.created_ms && typeof this.library.update == 'function') {
1073 + if(this.debug) this.log('updating chart...');
1074 +
1075 + // check and update the legend
1076 + this.updateLegendDOM();
1077
1025 - if(!this.isVisible()) {
1026 - if(NETDATA.options.debug.visibility || this.debug) this.log('is not visible');
1027 - if(typeof callback != 'undefined') callback();
1028 - return;
1078 + this.updates_since_last_creation++;
1079 + if(NETDATA.options.debug.chart_errors) {
1080 + this.library.update(this, data);
1081 + }
1082 + else {
1083 + try {
1084 + this.library.update(this, data);
1085 }
1030 - if(!this.chart) {
1031 - var this_state_object = this;
1032 - this.getChart(function() { this_state_object.updateChart(callback); });
1033 - return;
1086 + catch(err) {
1087 + this.error('chart "' + this.id + '" failed to be updated as ' + this.library_name);
1088 }
1089 + }
1090 + }
1091 + else {
1092 + if(this.debug) this.log('creating chart...');
1093 +
1094 + this.createChartDOM();
1095 + this.updates_since_last_creation = 0;
1096
1036 - if(!this.library.initialized) {
1037 - var this_state_object = this;
1038 - this.library.initialize(function() { this_state_object.updateChart(callback); });
1039 - return;
1097 + if(NETDATA.options.debug.chart_errors) {
1098 + this.library.create(this, data);
1099 + this.created_ms = new Date().getTime();
1100 + }
1101 + else {
1102 + try {
1103 + this.library.create(this, data);
1104 + this.created_ms = new Date().getTime();
1105 + }
1106 + catch(err) {
1107 + this.error('chart "' + this.id + '" failed to be created as ' + this.library_name);
1108 }
1109 + }
1110 + }
1111
1042 - this.clearSelection();
1043 - this.chartURL();
1044 - if(this.debug) this.log('updating from ' + this.current.url);
1112 + // update the performance counters
1113 + var now = new Date().getTime();
1114 +
1115 + // don't update last_autorefreshed if this chart is
1116 + // forced to be updated with global PanAndZoom
1117 + if(NETDATA.globalPanAndZoom.isActive())
1118 + this.current.last_autorefreshed = 0;
1119 + else
1120 + this.current.last_autorefreshed = now;
1121
1046 - var this_state_object = this;
1047 - $.ajax( {
1048 - url: this.current.url,
1049 - crossDomain: true,
1050 - cache: false
1051 - })
1052 - .then(function(data) {
1053 - this_state_object.updateChartWithData(data);
1054 - })
1055 - .fail(function() {
1056 - this_state_object.error('cannot download chart from ' + this_state_object.current.url);
1057 - })
1058 - .always(function() {
1059 - if(typeof callback == 'function') callback();
1060 - });
1061 - },
1122 + this.refresh_dt_ms = now - started;
1123 + NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
1124
1063 - isVisible: function() {
1064 - if(NETDATA.options.current.update_only_visible)
1065 - return $(this.element).visible(true);
1066 - else
1067 - return true;
1068 - },
1125 + if(this.refresh_dt_element)
1126 + this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
1127 + }
1128
1070 - isAutoRefreshed: function() {
1071 - return this.current.autorefresh;
1072 - },
1129 + chartState.prototype.updateChart = function(callback) {
1130 + if(!this.library || !this.library.enabled) {
1131 + this.error('charting library "' + this.library_name + '" is not available');
1132 + if(typeof callback != 'undefined') callback();
1133 + return;
1134 + }
1135
1074 - canBeAutoRefreshed: function() {
1075 - if(this.isAutoRefreshed()) {
1076 - var now = new Date().getTime();
1136 + if(!this.enabled) {
1137 + if(this.debug) this.error('chart "' + this.id + '" is not enabled');
1138 + if(typeof callback != 'undefined') callback();
1139 + return;
1140 + }
1141
1078 - if(this.updates_counter && !NETDATA.options.page_is_visible) {
1079 - if(NETDATA.options.debug.focus || this.debug) this.log('canBeAutoRefreshed(): page does not have focus');
1080 - return false;
1081 - }
1142 + if(!this.isVisible()) {
1143 + if(NETDATA.options.debug.visibility || this.debug) this.log('is not visible');
1144 + if(typeof callback != 'undefined') callback();
1145 + return;
1146 + }
1147 + if(!this.chart) {
1148 + var this_state_object = this;
1149 + this.getChart(function() { this_state_object.updateChart(callback); });
1150 + return;
1151 + }
1152
1083 - if(!this.isVisible()) {
1084 - if(this.debug) this.log('canBeAutoRefreshed(): I am not visible.');
1085 - return false;
1086 - }
1153 + if(!this.library.initialized) {
1154 + var this_state_object = this;
1155 + this.library.initialize(function() { this_state_object.updateChart(callback); });
1156 + return;
1157 + }
1158
1088 - // options valid only for autoRefresh()
1089 - if(NETDATA.options.auto_refresher_stop_until == 0 || NETDATA.options.auto_refresher_stop_until < now) {
1090 - if(NETDATA.globalPanAndZoom.isActive()) {
1091 - if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
1092 - if(this.debug) this.log('canBeAutoRefreshed(): global panning: I need an update.');
1093 - return true;
1094 - }
1095 - else {
1096 - if(this.debug) this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
1097 - return false;
1098 - }
1099 - }
1159 + this.clearSelection();
1160 + this.chartURL();
1161 + if(this.debug) this.log('updating from ' + this.current.url);
1162
1101 - if(this.selected) {
1102 - if(this.debug) this.log('canBeAutoRefreshed(): I have a selection in place.');
1103 - return false;
1104 - }
1163 + var this_state_object = this;
1164 + $.ajax( {
1165 + url: this.current.url,
1166 + crossDomain: true,
1167 + cache: false
1168 + })
1169 + .then(function(data) {
1170 + this_state_object.updateChartWithData(data);
1171 + })
1172 + .fail(function() {
1173 + this_state_object.error('cannot download chart from ' + this_state_object.current.url);
1174 + })
1175 + .always(function() {
1176 + if(typeof callback == 'function') callback();
1177 + });
1178 + }
1179
1106 - if(this.paused) {
1107 - if(this.debug) this.log('canBeAutoRefreshed(): I am paused.');
1108 - return false;
1109 - }
1180 + chartState.prototype.isVisible = function() {
1181 + if(NETDATA.options.current.update_only_visible)
1182 + return $(this.element).visible(true);
1183 + else
1184 + return true;
1185 + }
1186
1111 - if(now - this.current.last_autorefreshed > this.current.view_update_every) {
1112 - if(this.debug) this.log('canBeAutoRefreshed(): It is time to update me.');
1113 - return true;
1114 - }
1115 - }
1116 - }
1187 + chartState.prototype.isAutoRefreshed = function() {
1188 + return this.current.autorefresh;
1189 + }
1190 +
1191 + chartState.prototype.canBeAutoRefreshed = function() {
1192 + if(this.isAutoRefreshed()) {
1193 + var now = new Date().getTime();
1194
1195 + if(this.updates_counter && !NETDATA.options.page_is_visible) {
1196 + if(NETDATA.options.debug.focus || this.debug) this.log('canBeAutoRefreshed(): page does not have focus');
1197 return false;
1119 - },
1198 + }
1199
1121 - autoRefresh: function(callback) {
1122 - if(this.canBeAutoRefreshed()) {
1123 - this.updateChart(callback);
1124 - }
1125 - else {
1126 - if(typeof callback != 'undefined')
1127 - callback();
1200 + if(!this.isVisible()) {
1201 + if(this.debug) this.log('canBeAutoRefreshed(): I am not visible.');
1202 + return false;
1203 + }
1204 +
1205 + // options valid only for autoRefresh()
1206 + if(NETDATA.options.auto_refresher_stop_until == 0 || NETDATA.options.auto_refresher_stop_until < now) {
1207 + if(NETDATA.globalPanAndZoom.isActive()) {
1208 + if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
1209 + if(this.debug) this.log('canBeAutoRefreshed(): global panning: I need an update.');
1210 + return true;
1211 + }
1212 + else {
1213 + if(this.debug) this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
1214 + return false;
1215 + }
1216 }
1129 - },
1217
1131 - _defaultsFromDownloadedChart: function(chart) {
1132 - this.chart = chart;
1133 - this.chart_url = chart.url;
1134 - this.current.view_update_every = chart.update_every * 1000;
1135 - this.current.points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
1136 - },
1218 + if(this.selected) {
1219 + if(this.debug) this.log('canBeAutoRefreshed(): I have a selection in place.');
1220 + return false;
1221 + }
1222
1138 - // fetch the chart description from the netdata server
1139 - getChart: function(callback) {
1140 - this.chart = NETDATA.chartRegistry.get(this.host, this.id);
1141 - if(this.chart) {
1142 - this._defaultsFromDownloadedChart(this.chart);
1143 - if(typeof callback == 'function') callback();
1223 + if(this.paused) {
1224 + if(this.debug) this.log('canBeAutoRefreshed(): I am paused.');
1225 + return false;
1226 }
1145 - else {
1146 - this.chart_url = this.host + "/api/v1/chart?chart=" + this.id;
1147 - if(this.debug) this.log('downloading ' + this.chart_url);
1148 - var this_state_object = this;
1149 -
1150 - $.ajax( {
1151 - url: this.chart_url,
1152 - crossDomain: true,
1153 - cache: false
1154 - })
1155 - .done(function(chart) {
1156 - chart.url = this_state_object.chart_url;
1157 - chart.data_url = (this_state_object.host + chart.data_url);
1158 - this_state_object._defaultsFromDownloadedChart(chart);
1159 - NETDATA.chartRegistry.add(this_state_object.host, this_state_object.id, chart);
1160 - })
1161 - .fail(function() {
1162 - NETDATA.error(404, this_state_object.chart_url);
1163 - this_state_object.error('chart "' + this_state_object.id + '" not found on url "' + this_state_object.chart_url + '"');
1164 - })
1165 - .always(function() {
1166 - if(typeof callback == 'function') callback();
1167 - });
1227 +
1228 + if(now - this.current.last_autorefreshed > this.current.view_update_every) {
1229 + if(this.debug) this.log('canBeAutoRefreshed(): It is time to update me.');
1230 + return true;
1231 }
1169 - },
1232 + }
1233 + }
1234 +
1235 + return false;
1236 + }
1237 +
1238 + chartState.prototype.autoRefresh = function(callback) {
1239 + if(this.canBeAutoRefreshed()) {
1240 + this.updateChart(callback);
1241 + }
1242 + else {
1243 + if(typeof callback != 'undefined')
1244 + callback();
1245 + }
1246 + }
1247
1171 - // resize the chart to its real dimensions
1172 - // as given by the caller
1173 - sizeChart: function() {
1174 - element.className += "netdata-container";
1248 + chartState.prototype._defaultsFromDownloadedChart = function(chart) {
1249 + this.chart = chart;
1250 + this.chart_url = chart.url;
1251 + this.current.view_update_every = chart.update_every * 1000;
1252 + this.current.points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
1253 + }
1254
1176 - if(this.debug) this.log('sizing element');
1255 + // fetch the chart description from the netdata server
1256 + chartState.prototype.getChart = function(callback) {
1257 + this.chart = NETDATA.chartRegistry.get(this.host, this.id);
1258 + if(this.chart) {
1259 + this._defaultsFromDownloadedChart(this.chart);
1260 + if(typeof callback == 'function') callback();
1261 + }
1262 + else {
1263 + this.chart_url = this.host + "/api/v1/chart?chart=" + this.id;
1264 + if(this.debug) this.log('downloading ' + this.chart_url);
1265 + var this_state_object = this;
1266 +
1267 + $.ajax( {
1268 + url: this.chart_url,
1269 + crossDomain: true,
1270 + cache: false
1271 + })
1272 + .done(function(chart) {
1273 + chart.url = this_state_object.chart_url;
1274 + chart.data_url = (this_state_object.host + chart.data_url);
1275 + this_state_object._defaultsFromDownloadedChart(chart);
1276 + NETDATA.chartRegistry.add(this_state_object.host, this_state_object.id, chart);
1277 + })
1278 + .fail(function() {
1279 + NETDATA.error(404, this_state_object.chart_url);
1280 + this_state_object.error('chart "' + this_state_object.id + '" not found on url "' + this_state_object.chart_url + '"');
1281 + })
1282 + .always(function() {
1283 + if(typeof callback == 'function') callback();
1284 + });
1285 + }
1286 + }
1287
1178 - if(this.width)
1179 - $(this.element).css('width', this.width);
1288 + // resize the chart to its real dimensions
1289 + // as given by the caller
1290 + chartState.prototype.sizeChart = function() {
1291 + this.element.className += "netdata-container";
1292
1181 - if(this.height)
1182 - $(this.element).css('height', this.height);
1293 + if(this.debug) this.log('sizing element');
1294
1184 - // these should be left to css
1185 - //$(this.element).css('display', 'inline-block')
1186 - //$(this.element).css('overflow', 'hidden');
1295 + if(this.width)
1296 + $(this.element).css('width', this.width);
1297
1188 - if(NETDATA.chartDefaults.min_width)
1189 - $(this.element).css('min-width', NETDATA.chartDefaults.min_width);
1190 - },
1298 + if(this.height)
1299 + $(this.element).css('height', this.height);
1300
1192 - // show a message in the chart
1193 - message: function(type, msg) {
1194 - this.element.innerHTML = '<div class="netdata-message netdata-' + type + '-message" style="font-size: x-small; overflow: hidden; width: 100%; height: 100%;"><small>'
1195 - + msg
1196 - + '</small></div>';
1301 + if(NETDATA.chartDefaults.min_width)
1302 + $(this.element).css('min-width', NETDATA.chartDefaults.min_width);
1303 + }
1304
1198 - // reset the creation datetime
1199 - // since we overwrote the whole element
1200 - this.created_ms = 0
1201 - if(this.debug) this.log(msg);
1202 - },
1305 + // show a message in the chart
1306 + chartState.prototype.message = function(type, msg) {
1307 + this.element.innerHTML = '<div class="netdata-message netdata-' + type + '-message" style="font-size: x-small; overflow: hidden; width: 100%; height: 100%;"><small>'
1308 + + msg
1309 + + '</small></div>';
1310
1204 - // show an error on the chart and stop it forever
1205 - error: function(msg) {
1206 - this.message('error', this.id + ': ' + msg);
1207 - this.enabled = false;
1208 - },
1311 + // reset the creation datetime
1312 + // since we overwrote the whole element
1313 + this.created_ms = 0
1314 + if(this.debug) this.log(msg);
1315 + }
1316
1210 - // show a message indicating the chart is loading
1211 - info: function(msg) {
1212 - this.message('info', this.id + ': ' + msg);
1213 - },
1317 + // show an error on the chart and stop it forever
1318 + chartState.prototype.error = function(msg) {
1319 + this.message('error', this.id + ': ' + msg);
1320 + this.enabled = false;
1321 + }
1322
1215 - init: function() {
1216 - if(this.debug) this.log('created');
1217 - this.sizeChart();
1218 - this.info("loading...");
1219 -
1220 - // make sure the host does not end with /
1221 - // all netdata API requests use absolute paths
1222 - while(this.host.slice(-1) == '/')
1223 - this.host = this.host.substring(0, this.host.length - 1);
1224 -
1225 - // check the requested library is available
1226 - // we don't initialize it here - it will be initialized when
1227 - // this chart will be first used
1228 - if(typeof NETDATA.chartLibraries[this.library_name] == 'undefined') {
1229 - NETDATA.error(402, this.library_name);
1230 - this.error('chart library "' + this.library_name + '" is not found');
1231 - }
1232 - else if(!NETDATA.chartLibraries[this.library_name].enabled) {
1233 - NETDATA.error(403, this.library_name);
1234 - this.error('chart library "' + this.library_name + '" is not enabled');
1235 - }
1236 - else
1237 - this.library = NETDATA.chartLibraries[this.library_name];
1323 + // show a message indicating the chart is loading
1324 + chartState.prototype.info = function(msg) {
1325 + this.message('info', this.id + ': ' + msg);
1326 + }
1327
1239 - // if we need to report the rendering speed
1240 - // find the element that needs to be updated
1241 - if(this.refresh_dt_element_name)
1242 - this.refresh_dt_element = document.getElementById(this.refresh_dt_element_name) || null;
1328 + chartState.prototype.init = function() {
1329 + if(this.debug) this.log('created');
1330 + this.sizeChart();
1331 + this.info("loading...");
1332 +
1333 + // make sure the host does not end with /
1334 + // all netdata API requests use absolute paths
1335 + while(this.host.slice(-1) == '/')
1336 + this.host = this.host.substring(0, this.host.length - 1);
1337 +
1338 + // check the requested library is available
1339 + // we don't initialize it here - it will be initialized when
1340 + // this chart will be first used
1341 + if(typeof NETDATA.chartLibraries[this.library_name] == 'undefined') {
1342 + NETDATA.error(402, this.library_name);
1343 + this.error('chart library "' + this.library_name + '" is not found');
1344 + }
1345 + else if(!NETDATA.chartLibraries[this.library_name].enabled) {
1346 + NETDATA.error(403, this.library_name);
1347 + this.error('chart library "' + this.library_name + '" is not enabled');
1348 + }
1349 + else
1350 + this.library = NETDATA.chartLibraries[this.library_name];
1351
1244 - // the default mode for all charts
1245 - this.setMode('auto');
1246 - }
1247 - };
1352 + // if we need to report the rendering speed
1353 + // find the element that needs to be updated
1354 + if(this.refresh_dt_element_name)
1355 + this.refresh_dt_element = document.getElementById(this.refresh_dt_element_name) || null;
1356
1249 - state.init();
1250 - return state;
1357 + // the default mode for all charts
1358 + this.setMode('auto');
1359 }
1360
1361 // get or create a chart state, given a DOM element
1362 NETDATA.chartState = function(element) {
1363 var state = $(element).data('netdata-state-object') || null;
1364 if(!state) {
1257 - state = NETDATA.chartInitialState(element);
1365 + state = new chartState(element);
1366 + state.init();
1367 $(element).data('netdata-state-object', state);
1368 }
1369 return state;
@@ -1624,110 +1733,8 @@
1733
1734 NETDATA.dygraph = {
1735 smooth: false,
1627 - state: null,
1628 - sync: false,
1629 - dont_sync_before: 0,
1630 - slaves: []
1736 };
1737
1633 - NETDATA.dygraph.syncStart = function(state, event, x, points, row, seriesName) {
1634 - if(!NETDATA.options.current.sync_selection) return;
1635 - if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraph.syncStart()');
1636 -
1637 - //if(NETDATA.dygraph.state && NETDATA.dygraph.state != state) {
1638 - // state.log('sync: I am not the sync master.');
1639 - // return;
1640 - //}
1641 - // state.debug = true;
1642 -
1643 - var t = state.current.after_ms + row * state.current.view_update_every;
1644 - // console.log('row = ' + row + ', x = ' + x + ', t = ' + t + ' ' + ((t == x)?'SAME':'DIFFERENT') + ', rows in db: ' + state.current.data.points + ' visible(x) = ' + state.timeIsVisible(x) + ' visible(t) = ' + state.timeIsVisible(t) + ' r(x) = ' + state.calculateRowForTime(x) + ' r(t) = ' + state.calculateRowForTime(t) + ' range: ' + state.current.after_ms + ' - ' + state.current.before_ms + ' real: ' + state.current.data.after + ' - ' + state.current.data.before + ' every: ' + state.current.view_update_every);
1645 -
1646 - now = new Date().getTime();
1647 - if(now < NETDATA.dygraph.dont_sync_before) {
1648 - if(state.debug) state.log('sync: cannot sync yet.');
1649 - return;
1650 - }
1651 -
1652 - // since we are the sync master, we should not call state.setSelection()
1653 - // dygraphs is taking care of visualizing our selection.
1654 - state.selected = true;
1655 -
1656 - var dygraph = state.dygraph_instance;
1657 -
1658 - if(!NETDATA.dygraph.sync) {
1659 - if(state.debug) state.log('sync: setting up...');
1660 - $.each(NETDATA.options.targets, function(i, target) {
1661 - var st = NETDATA.chartState(target);
1662 - if(st == state) {
1663 - if(state.debug) st.log('sync: not adding me to sync');
1664 - }
1665 - else {
1666 - if(typeof st.dygraph_instance == 'object' && st.library_name == state.library_name && st.isVisible()) {
1667 - NETDATA.dygraph.slaves.push(st);
1668 - if(state.debug) st.log('sync: added slave to sync');
1669 - }
1670 - }
1671 - });
1672 - NETDATA.dygraph.sync = true;
1673 - }
1674 -
1675 - $.each(NETDATA.dygraph.slaves, function(i, st) {
1676 - if(st == state) {
1677 - if(state.debug) st.log('sync: ignoring me from set selection');
1678 - }
1679 - else {
1680 - if(state.debug) st.log('sync: showing master selection');
1681 - st.setSelection(t);
1682 - }
1683 - });
1684 - }
1685 -
1686 - NETDATA.dygraph.syncStop = function(state) {
1687 - if(!NETDATA.options.current.sync_selection) return;
1688 -
1689 - if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraph.syncStop()');
1690 -
1691 - //if(NETDATA.dygraph.state && NETDATA.dygraph.state != state) {
1692 - // state.log('sync: I am not the sync master.');
1693 - // return;
1694 - //}
1695 -
1696 - if(NETDATA.dygraph.sync) {
1697 - if(state.debug) st.log('sync: cleaning up...');
1698 - $.each(NETDATA.dygraph.slaves, function(i, st) {
1699 - if(st == state) {
1700 - if(state.debug) st.log('sync: not adding me to sync stop');
1701 - }
1702 - else {
1703 - if(state.debug) st.log('sync: removed slave from sync');
1704 - st.clearSelection();
1705 - }
1706 - });
1707 -
1708 - NETDATA.dygraph.slaves = [];
1709 - NETDATA.dygraph.sync = false;
1710 - }
1711 -
1712 - // since we are the sync master, we should not call state.clearSelection()
1713 - // dygraphs is taking care of visualizing our selection.
1714 - state.selected = false;
1715 - }
1716 -
1717 - NETDATA.dygraph.resetChart = function(state, dygraph, context) {
1718 - if(NETDATA.options.debug.dygraph) state.log('dygraph.resetChart()');
1719 -
1720 - state.resetChart();
1721 - }
1722 -
1723 - NETDATA.dygraph.chartPanOrZoom = function(state, dygraph, after, before) {
1724 - if(NETDATA.options.debug.dygraph) console.log('>>>> dygraph.zoomOrPan(state, dygraph, after:' + after + ', before: ' + before + ')');
1725 -
1726 - //console.log(state.dygraph_instance);
1727 - state.updateChartPanOrZoom(after, before);
1728 - return;
1729 - }
1730 -
1738 NETDATA.dygraphSetSelection = function(state, t) {
1739 if(typeof state.dygraph_instance != 'undefined') {
1740 var r = state.calculateRowForTime(t);
@@ -1957,19 +1964,28 @@
1964 var after = Math.round(x_range[0]);
1965 var before = Math.round(x_range[1]);
1966
1960 - NETDATA.dygraph.chartPanOrZoom(state, this, after, before);
1967 + state.updateChartPanOrZoom(after, before);
1968 }
1969 },
1970 zoomCallback: function(minDate, maxDate, yRanges) {
1971 if(NETDATA.options.debug.dygraph) state.log('dygraphZoomCallback()');
1965 - NETDATA.dygraph.syncStop(state);
1966 - state.delaySelectionSync();
1967 - NETDATA.dygraph.chartPanOrZoom(state, this, minDate, maxDate);
1972 + state.globalSelectionSyncStop();
1973 + state.globalSelectionSyncDelay();
1974 + state.updateChartPanOrZoom(minDate, maxDate);
1975 },
1976 highlightCallback: function(event, x, points, row, seriesName) {
1977 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphHighlightCallback()');
1978 state.pauseChart();
1972 - NETDATA.dygraph.syncStart(state, event, x, points, row, seriesName);
1979 +
1980 + // there is a bug in dygraph when the chart is zoomed enough
1981 + // the time it thinks is selected is wrong
1982 + // here we calculate the time t based on the row number selected
1983 + // which is ok
1984 + var t = state.current.after_ms + row * state.current.view_update_every;
1985 + // console.log('row = ' + row + ', x = ' + x + ', t = ' + t + ' ' + ((t == x)?'SAME':'DIFFERENT') + ', rows in db: ' + state.current.data.points + ' visible(x) = ' + state.timeIsVisible(x) + ' visible(t) = ' + state.timeIsVisible(t) + ' r(x) = ' + state.calculateRowForTime(x) + ' r(t) = ' + state.calculateRowForTime(t) + ' range: ' + state.current.after_ms + ' - ' + state.current.before_ms + ' real: ' + state.current.data.after + ' - ' + state.current.data.before + ' every: ' + state.current.view_update_every);
1986 +
1987 + state.globalSelectionSync(t);
1988 +
1989 // fix legend zIndex using the internal structures of dygraph legend module
1990 // this works, but it is a hack!
1991 // state.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000;
@@ -1977,12 +1993,12 @@
1993 unhighlightCallback: function(event) {
1994 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphUnhighlightCallback()');
1995 state.unpauseChart();
1980 - NETDATA.dygraph.syncStop(state);
1996 + state.globalSelectionSyncStop();
1997 },
1998 interactionModel : {
1999 mousedown: function(event, dygraph, context) {
2000 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.mousedown()');
1985 - NETDATA.dygraph.syncStop(state);
2001 + state.globalSelectionSyncStop();
2002
2003 if(NETDATA.options.debug.dygraph) state.log('dygraphMouseDown()');
2004
@@ -1994,24 +2010,24 @@
2010 if(event.button && event.button == 1) {
2011 if (event.altKey || event.shiftKey) {
2012 state.setMode('pan');
1997 - state.delaySelectionSync();
2013 + state.globalSelectionSyncDelay();
2014 Dygraph.startPan(event, dygraph, context);
2015 }
2016 else {
2017 state.setMode('zoom');
2002 - state.delaySelectionSync();
2018 + state.globalSelectionSyncDelay();
2019 Dygraph.startZoom(event, dygraph, context);
2020 }
2021 }
2022 else {
2023 if (event.altKey || event.shiftKey) {
2024 state.setMode('zoom');
2009 - state.delaySelectionSync();
2025 + state.globalSelectionSyncDelay();
2026 Dygraph.startZoom(event, dygraph, context);
2027 }
2028 else {
2029 state.setMode('pan');
2014 - state.delaySelectionSync();
2030 + state.globalSelectionSyncDelay();
2031 Dygraph.startPan(event, dygraph, context);
2032 }
2033 }
@@ -2020,14 +2036,14 @@
2036 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.mousemove()');
2037
2038 if(context.isPanning) {
2023 - NETDATA.dygraph.syncStop(state);
2024 - state.delaySelectionSync();
2039 + state.globalSelectionSyncStop();
2040 + state.globalSelectionSyncDelay();
2041 state.setMode('pan');
2042 Dygraph.movePan(event, dygraph, context);
2043 }
2044 else if(context.isZooming) {
2029 - NETDATA.dygraph.syncStop(state);
2030 - state.delaySelectionSync();
2045 + state.globalSelectionSyncStop();
2046 + state.globalSelectionSyncDelay();
2047 state.setMode('zoom');
2048 Dygraph.moveZoom(event, dygraph, context);
2049 }
@@ -2036,11 +2052,11 @@
2052 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.mouseup()');
2053
2054 if (context.isPanning) {
2039 - state.delaySelectionSync();
2055 + state.globalSelectionSyncDelay();
2056 Dygraph.endPan(event, dygraph, context);
2057 }
2058 else if (context.isZooming) {
2043 - state.delaySelectionSync();
2059 + state.globalSelectionSyncDelay();
2060 Dygraph.endZoom(event, dygraph, context);
2061 }
2062 },
@@ -2050,14 +2066,14 @@
2066 },
2067 dblclick: function(event, dygraph, context) {
2068 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.dblclick()');
2053 - NETDATA.dygraph.syncStop(state);
2054 - NETDATA.dygraph.resetChart(state, dygraph, context);
2069 + state.globalSelectionSyncStop();
2070 + state.resetChart();
2071 },
2072 mousewheel: function(event, dygraph, context) {
2073 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.mousewheel()');
2074
2059 - NETDATA.dygraph.syncStop(state);
2060 - state.delaySelectionSync();
2075 + state.globalSelectionSyncStop();
2076 + state.globalSelectionSyncDelay();
2077
2078 if(event.altKey || event.shiftKey) {
2079 // http://dygraphs.com/gallery/interaction-api.js
@@ -2077,13 +2093,13 @@
2093 if(NETDATA.options.debug.dygraph) state.log('percent: ' + percentage + ' from ' + after_old + ' - ' + before_old + ' to ' + after + ' - ' + before + ', range from ' + (before_old - after_old).toString() + ' to ' + (before - after).toString());
2094
2095 state.setMode('zoom');
2080 - NETDATA.dygraph.chartPanOrZoom(state, dygraph, after, before);
2096 + state.updateChartPanOrZoom(after, before);
2097 }
2098 },
2099 touchstart: function(event, dygraph, context) {
2100 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.touchstart()');
2085 - NETDATA.dygraph.syncStop(state);
2086 - state.delaySelectionSync();
2101 + state.globalSelectionSyncStop();
2102 + state.globalSelectionSyncDelay();
2103 Dygraph.Interaction.startTouch(event, dygraph, context);
2104 context.touchDirections = { x: true, y: false };
2105 state.setMode('zoom');
@@ -2091,7 +2107,7 @@
2107 touchmove: function(event, dygraph, context) {
2108 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.touchmove()');
2109 //Dygraph.cancelEvent(event);
2094 - NETDATA.dygraph.syncStop(state);
2110 + state.globalSelectionSyncStop();
2111 Dygraph.Interaction.moveTouch(event, dygraph, context);
2112 },
2113 touchend: function(event, dygraph, context) {