firehol/netdata

View on GitHub
src/web/gui/src/dashboard.js/charting/gauge.js

Summary

Maintainability
F
1 mo
Test Coverage
// gauge.js

NETDATA.gaugeInitialize = function (callback) {
    if (typeof netdataNoGauge === 'undefined' || !netdataNoGauge) {
        $.ajax({
            url: NETDATA.gauge_js,
            cache: true,
            dataType: "script",
            xhrFields: {withCredentials: true} // required for the cookie
        })
            .done(function () {
                NETDATA.registerChartLibrary('gauge', NETDATA.gauge_js);
            })
            .fail(function () {
                NETDATA.chartLibraries.gauge.enabled = false;
                NETDATA.error(100, NETDATA.gauge_js);
            })
            .always(function () {
                if (typeof callback === "function") {
                    return callback();
                }
            })
    }
    else {
        NETDATA.chartLibraries.gauge.enabled = false;
        if (typeof callback === "function") {
            return callback();
        }
    }
};

NETDATA.gaugeAnimation = function (state, status) {
    let speed = 32;

    if (typeof status === 'boolean' && status === false) {
        speed = 1000000000;
    } else if (typeof status === 'number') {
        speed = status;
    }

    // console.log('gauge speed ' + speed);
    state.tmp.gauge_instance.animationSpeed = speed;
    state.tmp.___gaugeOld__.speed = speed;
};

NETDATA.gaugeSet = function (state, value, min, max) {
    if (typeof value !== 'number') {
        value = 0;
    }
    if (typeof min !== 'number') {
        min = 0;
    }
    if (typeof max !== 'number') {
        max = 0;
    }
    if (value > max) {
        max = value;
    }
    if (value < min) {
        min = value;
    }
    if (min > max) {
        let t = min;
        min = max;
        max = t;
    }
    else if (min === max) {
        max = min + 1;
    }

    state.legendFormatValueDecimalsFromMinMax(min, max);

    // gauge.js has an issue if the needle
    // is smaller than min or larger than max
    // when we set the new values
    // the needle will go crazy

    // to prevent it, we always feed it
    // with a percentage, so that the needle
    // is always between min and max
    let pcent = (value - min) * 100 / (max - min);

    // bug fix for gauge.js 1.3.1
    // if the value is the absolute min or max, the chart is broken
    if (pcent < 0.001) {
        pcent = 0.001;
    }
    if (pcent > 99.999) {
        pcent = 99.999;
    }

    state.tmp.gauge_instance.set(pcent);
    // console.log('gauge set ' + pcent + ', value ' + value + ', min ' + min + ', max ' + max);

    state.tmp.___gaugeOld__.value = value;
    state.tmp.___gaugeOld__.min = min;
    state.tmp.___gaugeOld__.max = max;
};

NETDATA.gaugeSetLabels = function (state, value, min, max) {
    if (state.tmp.___gaugeOld__.valueLabel !== value) {
        state.tmp.___gaugeOld__.valueLabel = value;
        state.tmp.gaugeChartLabel.innerText = state.legendFormatValue(value);
    }
    if (state.tmp.___gaugeOld__.minLabel !== min) {
        state.tmp.___gaugeOld__.minLabel = min;
        state.tmp.gaugeChartMin.innerText = state.legendFormatValue(min);
    }
    if (state.tmp.___gaugeOld__.maxLabel !== max) {
        state.tmp.___gaugeOld__.maxLabel = max;
        state.tmp.gaugeChartMax.innerText = state.legendFormatValue(max);
    }
};

NETDATA.gaugeClearSelection = function (state, force) {
    if (typeof state.tmp.gaugeEvent !== 'undefined' && typeof state.tmp.gaugeEvent.timer !== 'undefined') {
        NETDATA.timeout.clear(state.tmp.gaugeEvent.timer);
        state.tmp.gaugeEvent.timer = undefined;
    }

    if (state.isAutoRefreshable() && state.data !== null && force !== true) {
        NETDATA.gaugeChartUpdate(state, state.data);
    } else {
        NETDATA.gaugeAnimation(state, false);
        NETDATA.gaugeSetLabels(state, null, null, null);
        NETDATA.gaugeSet(state, null, null, null);
    }

    NETDATA.gaugeAnimation(state, true);
    return true;
};

NETDATA.gaugeSetSelection = function (state, t) {
    if (state.timeIsVisible(t) !== true) {
        return NETDATA.gaugeClearSelection(state, true);
    }

    let slot = state.calculateRowForTime(t);
    if (slot < 0 || slot >= state.data.result.length) {
        return NETDATA.gaugeClearSelection(state, true);
    }

    if (typeof state.tmp.gaugeEvent === 'undefined') {
        state.tmp.gaugeEvent = {
            timer: undefined,
            value: 0,
            min: 0,
            max: 0
        };
    }

    let value = state.data.result[state.data.result.length - 1 - slot];
    let min = (state.tmp.gaugeMin === null) ? NETDATA.commonMin.get(state) : state.tmp.gaugeMin;
    let max = (state.tmp.gaugeMax === null) ? NETDATA.commonMax.get(state) : state.tmp.gaugeMax;

    // make sure it is zero based
    // but only if it has not been set by the user
    if (state.tmp.gaugeMin === null && min > 0) {
        min = 0;
    }
    if (state.tmp.gaugeMax === null && max < 0) {
        max = 0;
    }

    state.tmp.gaugeEvent.value = value;
    state.tmp.gaugeEvent.min = min;
    state.tmp.gaugeEvent.max = max;
    NETDATA.gaugeSetLabels(state, value, min, max);

    if (state.tmp.gaugeEvent.timer === undefined) {
        NETDATA.gaugeAnimation(state, false);

        state.tmp.gaugeEvent.timer = NETDATA.timeout.set(function () {
            state.tmp.gaugeEvent.timer = undefined;
            NETDATA.gaugeSet(state, state.tmp.gaugeEvent.value, state.tmp.gaugeEvent.min, state.tmp.gaugeEvent.max);
        }, 0);
    }

    return true;
};

NETDATA.gaugeChartUpdate = function (state, data) {
    let value, min, max;

    if (NETDATA.globalPanAndZoom.isActive() || state.isAutoRefreshable() === false) {
        NETDATA.gaugeSetLabels(state, null, null, null);
        state.tmp.gauge_instance.set(0);
    } else {
        value = data.result[0];
        min = (state.tmp.gaugeMin === null) ? NETDATA.commonMin.get(state) : state.tmp.gaugeMin;
        max = (state.tmp.gaugeMax === null) ? NETDATA.commonMax.get(state) : state.tmp.gaugeMax;
        if (value < min) {
            min = value;
        }
        if (value > max) {
            max = value;
        }

        // make sure it is zero based
        // but only if it has not been set by the user
        if (state.tmp.gaugeMin === null && min > 0) {
            min = 0;
        }
        if (state.tmp.gaugeMax === null && max < 0) {
            max = 0;
        }

        NETDATA.gaugeSet(state, value, min, max);
        NETDATA.gaugeSetLabels(state, value, min, max);
    }

    return true;
};

NETDATA.gaugeChartCreate = function (state, data) {
    // let chart = $(state.element_chart);

    let value = data.result[0];
    let min = NETDATA.dataAttribute(state.element, 'gauge-min-value', null);
    let max = NETDATA.dataAttribute(state.element, 'gauge-max-value', null);
    // let adjust = NETDATA.dataAttribute(state.element, 'gauge-adjust', null);
    let pointerColor = NETDATA.dataAttribute(state.element, 'gauge-pointer-color', NETDATA.themes.current.gauge_pointer);
    let strokeColor = NETDATA.dataAttribute(state.element, 'gauge-stroke-color', NETDATA.themes.current.gauge_stroke);
    let startColor = NETDATA.dataAttribute(state.element, 'gauge-start-color', state.chartCustomColors()[0]);
    let stopColor = NETDATA.dataAttribute(state.element, 'gauge-stop-color', void 0);
    let generateGradient = NETDATA.dataAttribute(state.element, 'gauge-generate-gradient', false);

    if (min === null) {
        min = NETDATA.commonMin.get(state);
        state.tmp.gaugeMin = null;
    } else {
        state.tmp.gaugeMin = min;
    }

    if (max === null) {
        max = NETDATA.commonMax.get(state);
        state.tmp.gaugeMax = null;
    } else {
        state.tmp.gaugeMax = max;
    }

    // make sure it is zero based
    // but only if it has not been set by the user
    if (state.tmp.gaugeMin === null && min > 0) {
        min = 0;
    }
    if (state.tmp.gaugeMax === null && max < 0) {
        max = 0;
    }

    let width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5;
    // console.log('gauge width: ' + width.toString() + ', height: ' + height.toString());
    //switch(adjust) {
    //  case 'width': width = height * ratio; break;
    //  case 'height':
    //  default: height = width / ratio; break;
    //}
    //state.element.style.width = width.toString() + 'px';
    //state.element.style.height = height.toString() + 'px';

    let lum_d = 0.05;

    let options = {
        lines: 12,                  // The number of lines to draw
        angle: 0.14,                // The span of the gauge arc
        lineWidth: 0.57,            // The line thickness
        radiusScale: 1.0,           // Relative radius
        pointer: {
            length: 0.85,           // 0.9 The radius of the inner circle
            strokeWidth: 0.045,     // The rotation offset
            color: pointerColor     // Fill color
        },
        limitMax: true,             // If false, the max value of the gauge will be updated if value surpass max
        limitMin: true,             // If true, the min value of the gauge will be fixed unless you set it manually
        colorStart: startColor,     // Colors
        colorStop: stopColor,       // just experiment with them
        strokeColor: strokeColor,   // to see which ones work best for you
        generateGradient: (generateGradient === true), // gmosx: 
        gradientType: 0,
        highDpiSupport: true        // High resolution support
    };

    if (generateGradient.constructor === Array) {
        // example options:
        // data-gauge-generate-gradient="[0, 50, 100]"
        // data-gauge-gradient-percent-color-0="#FFFFFF"
        // data-gauge-gradient-percent-color-50="#999900"
        // data-gauge-gradient-percent-color-100="#000000"

        options.percentColors = [];
        let len = generateGradient.length;
        while (len--) {
            let pcent = generateGradient[len];
            let color = NETDATA.dataAttribute(state.element, 'gauge-gradient-percent-color-' + pcent.toString(), false);
            if (color !== false) {
                let a = [];
                a[0] = pcent / 100;
                a[1] = color;
                options.percentColors.unshift(a);
            }
        }
        if (options.percentColors.length === 0) {
            delete options.percentColors;
        }
    } else if (generateGradient === false && NETDATA.themes.current.gauge_gradient) {
        //noinspection PointlessArithmeticExpressionJS
        options.percentColors = [
            [0.0, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 0))],
            [0.1, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 1))],
            [0.2, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 2))],
            [0.3, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 3))],
            [0.4, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 4))],
            [0.5, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 5))],
            [0.6, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 6))],
            [0.7, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 7))],
            [0.8, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 8))],
            [0.9, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 9))],
            [1.0, NETDATA.colorLuminance(startColor, 0.0)]];
    }

    state.tmp.gauge_canvas = document.createElement('canvas');
    state.tmp.gauge_canvas.id = 'gauge-' + state.uuid + '-canvas';
    state.tmp.gauge_canvas.className = 'gaugeChart';
    state.tmp.gauge_canvas.width = width;
    state.tmp.gauge_canvas.height = height;
    state.element_chart.appendChild(state.tmp.gauge_canvas);

    let valuefontsize = Math.floor(height / 5);
    let valuetop = Math.round((height - valuefontsize) / 3.2);
    state.tmp.gaugeChartLabel = document.createElement('span');
    state.tmp.gaugeChartLabel.className = 'gaugeChartLabel';
    state.tmp.gaugeChartLabel.style.fontSize = valuefontsize + 'px';
    state.tmp.gaugeChartLabel.style.top = valuetop.toString() + 'px';
    state.element_chart.appendChild(state.tmp.gaugeChartLabel);

    let titlefontsize = Math.round(valuefontsize / 2.1);
    let titletop = 0;
    state.tmp.gaugeChartTitle = document.createElement('span');
    state.tmp.gaugeChartTitle.className = 'gaugeChartTitle';
    state.tmp.gaugeChartTitle.innerText = state.title;
    state.tmp.gaugeChartTitle.style.fontSize = titlefontsize + 'px';
    state.tmp.gaugeChartTitle.style.lineHeight = titlefontsize + 'px';
    state.tmp.gaugeChartTitle.style.top = titletop.toString() + 'px';
    state.element_chart.appendChild(state.tmp.gaugeChartTitle);

    let unitfontsize = Math.round(titlefontsize * 0.9);
    state.tmp.gaugeChartUnits = document.createElement('span');
    state.tmp.gaugeChartUnits.className = 'gaugeChartUnits';
    state.tmp.gaugeChartUnits.innerText = state.units_current;
    state.tmp.gaugeChartUnits.style.fontSize = unitfontsize + 'px';
    state.element_chart.appendChild(state.tmp.gaugeChartUnits);

    state.tmp.gaugeChartMin = document.createElement('span');
    state.tmp.gaugeChartMin.className = 'gaugeChartMin';
    state.tmp.gaugeChartMin.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
    state.element_chart.appendChild(state.tmp.gaugeChartMin);

    state.tmp.gaugeChartMax = document.createElement('span');
    state.tmp.gaugeChartMax.className = 'gaugeChartMax';
    state.tmp.gaugeChartMax.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px';
    state.element_chart.appendChild(state.tmp.gaugeChartMax);

    // when we just re-create the chart
    // do not animate the first update
    let animate = true;
    if (typeof state.tmp.gauge_instance !== 'undefined') {
        animate = false;
    }

    state.tmp.gauge_instance = new Gauge(state.tmp.gauge_canvas).setOptions(options); // create sexy gauge!

    state.tmp.___gaugeOld__ = {
        value: value,
        min: min,
        max: max,
        valueLabel: null,
        minLabel: null,
        maxLabel: null
    };

    // we will always feed a percentage
    state.tmp.gauge_instance.minValue = 0;
    state.tmp.gauge_instance.maxValue = 100;

    NETDATA.gaugeAnimation(state, animate);
    NETDATA.gaugeSet(state, value, min, max);
    NETDATA.gaugeSetLabels(state, value, min, max);
    NETDATA.gaugeAnimation(state, true);

    state.legendSetUnitsString = function (units) {
        if (typeof state.tmp.gaugeChartUnits !== 'undefined' && state.tmp.units !== units) {
            state.tmp.gaugeChartUnits.innerText = units;
            state.tmp.___gaugeOld__.valueLabel = null;
            state.tmp.___gaugeOld__.minLabel = null;
            state.tmp.___gaugeOld__.maxLabel = null;
            state.tmp.units = units;
        }
    };
    state.legendShowUndefined = function () {
        if (typeof state.tmp.gauge_instance !== 'undefined') {
            NETDATA.gaugeClearSelection(state);
        }
    };

    return true;
};