firehol/netdata

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

Summary

Maintainability
F
3 wks
Test Coverage

// d3pie

NETDATA.d3pieInitialize = function (callback) {
    if (typeof netdataNoD3pie === 'undefined' || !netdataNoD3pie) {

        // d3pie requires D3
        if (!NETDATA.chartLibraries.d3.initialized) {
            if (NETDATA.chartLibraries.d3.enabled) {
                NETDATA.d3Initialize(function () {
                    NETDATA.d3pieInitialize(callback);
                });
            } else {
                NETDATA.chartLibraries.d3pie.enabled = false;
                if (typeof callback === "function") {
                    return callback();
                }
            }
        } else {
            $.ajax({
                url: NETDATA.d3pie_js,
                cache: true,
                dataType: "script",
                xhrFields: {withCredentials: true} // required for the cookie
            })
                .done(function () {
                    NETDATA.registerChartLibrary('d3pie', NETDATA.d3pie_js);
                })
                .fail(function () {
                    NETDATA.chartLibraries.d3pie.enabled = false;
                    NETDATA.error(100, NETDATA.d3pie_js);
                })
                .always(function () {
                    if (typeof callback === "function") {
                        return callback();
                    }
                });
        }
    } else {
        NETDATA.chartLibraries.d3pie.enabled = false;
        if (typeof callback === "function") {
            return callback();
        }
    }
};

NETDATA.d3pieSetContent = function (state, data, index) {
    state.legendFormatValueDecimalsFromMinMax(
        data.min,
        data.max
    );

    let content = [];
    let colors = state.chartColors();
    let len = data.result.labels.length;
    for (let i = 1; i < len; i++) {
        let label = data.result.labels[i];
        let value = data.result.data[index][label];
        let color = colors[i - 1];

        if (value !== null && value > 0) {
            content.push({
                label: label,
                value: value,
                color: color
            });
        }
    }

    if (content.length === 0) {
        content.push({
            label: 'no data',
            value: 100,
            color: '#666666'
        });
    }

    state.tmp.d3pie_last_slot = index;
    return content;
};

NETDATA.d3pieDateRange = function (state, data, index) {
    let dt = Math.round((data.before - data.after + 1) / data.points);
    let dt_str = NETDATA.seconds4human(dt);

    let before = data.result.data[index].time;
    let after = before - (dt * 1000);

    let d1 = NETDATA.dateTime.localeDateString(after);
    let t1 = NETDATA.dateTime.localeTimeString(after);
    let d2 = NETDATA.dateTime.localeDateString(before);
    let t2 = NETDATA.dateTime.localeTimeString(before);

    if (d1 === d2) {
        return d1 + ' ' + t1 + ' to ' + t2 + ', ' + dt_str;
    }

    return d1 + ' ' + t1 + ' to ' + d2 + ' ' + t2 + ', ' + dt_str;
};

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

    let slot = state.calculateRowForTime(t);
    slot = state.data.result.data.length - slot - 1;

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

    if (state.tmp.d3pie_last_slot === slot) {
        // we already show this slot, don't do anything
        return true;
    }

    if (state.tmp.d3pie_timer === undefined) {
        state.tmp.d3pie_timer = NETDATA.timeout.set(function () {
            state.tmp.d3pie_timer = undefined;
            NETDATA.d3pieChange(state, NETDATA.d3pieSetContent(state, state.data, slot), NETDATA.d3pieDateRange(state, state.data, slot));
        }, 0);
    }

    return true;
};

NETDATA.d3pieClearSelection = function (state, force) {
    if (typeof state.tmp.d3pie_timer !== 'undefined') {
        NETDATA.timeout.clear(state.tmp.d3pie_timer);
        state.tmp.d3pie_timer = undefined;
    }

    if (state.isAutoRefreshable() && state.data !== null && force !== true) {
        NETDATA.d3pieChartUpdate(state, state.data);
    } else {
        if (state.tmp.d3pie_last_slot !== -1) {
            state.tmp.d3pie_last_slot = -1;
            NETDATA.d3pieChange(state, [{label: 'no data', value: 1, color: '#666666'}], 'no data available');
        }
    }

    return true;
};

NETDATA.d3pieChange = function (state, content, footer) {
    if (state.d3pie_forced_subtitle === null) {
        //state.d3pie_instance.updateProp("header.subtitle.text", state.units_current);
        state.d3pie_instance.options.header.subtitle.text = state.units_current;
    }

    if (state.d3pie_forced_footer === null) {
        //state.d3pie_instance.updateProp("footer.text", footer);
        state.d3pie_instance.options.footer.text = footer;
    }

    //state.d3pie_instance.updateProp("data.content", content);
    state.d3pie_instance.options.data.content = content;
    state.d3pie_instance.destroy();
    state.d3pie_instance.recreate();
    return true;
};

NETDATA.d3pieChartUpdate = function (state, data) {
    return NETDATA.d3pieChange(state, NETDATA.d3pieSetContent(state, data, 0), NETDATA.d3pieDateRange(state, data, 0));
};

NETDATA.d3pieChartCreate = function (state, data) {

    state.element_chart.id = 'd3pie-' + state.uuid;
    // console.log('id = ' + state.element_chart.id);

    let content = NETDATA.d3pieSetContent(state, data, 0);

    state.d3pie_forced_title = NETDATA.dataAttribute(state.element, 'd3pie-title', null);
    state.d3pie_forced_subtitle = NETDATA.dataAttribute(state.element, 'd3pie-subtitle', null);
    state.d3pie_forced_footer = NETDATA.dataAttribute(state.element, 'd3pie-footer', null);

    state.d3pie_options = {
        header: {
            title: {
                text: (state.d3pie_forced_title !== null) ? state.d3pie_forced_title : state.title,
                color: NETDATA.dataAttribute(state.element, 'd3pie-title-color', NETDATA.themes.current.d3pie.title),
                fontSize: NETDATA.dataAttribute(state.element, 'd3pie-title-fontsize', 12),
                fontWeight: NETDATA.dataAttribute(state.element, 'd3pie-title-fontweight', "bold"),
                font: NETDATA.dataAttribute(state.element, 'd3pie-title-font', "arial")
            },
            subtitle: {
                text: (state.d3pie_forced_subtitle !== null) ? state.d3pie_forced_subtitle : state.units_current,
                color: NETDATA.dataAttribute(state.element, 'd3pie-subtitle-color', NETDATA.themes.current.d3pie.subtitle),
                fontSize: NETDATA.dataAttribute(state.element, 'd3pie-subtitle-fontsize', 10),
                fontWeight: NETDATA.dataAttribute(state.element, 'd3pie-subtitle-fontweight', "normal"),
                font: NETDATA.dataAttribute(state.element, 'd3pie-subtitle-font', "arial")
            },
            titleSubtitlePadding: 1
        },
        footer: {
            text: (state.d3pie_forced_footer !== null) ? state.d3pie_forced_footer : NETDATA.d3pieDateRange(state, data, 0),
            color: NETDATA.dataAttribute(state.element, 'd3pie-footer-color', NETDATA.themes.current.d3pie.footer),
            fontSize: NETDATA.dataAttribute(state.element, 'd3pie-footer-fontsize', 9),
            fontWeight: NETDATA.dataAttribute(state.element, 'd3pie-footer-fontweight', "bold"),
            font: NETDATA.dataAttribute(state.element, 'd3pie-footer-font', "arial"),
            location: NETDATA.dataAttribute(state.element, 'd3pie-footer-location', "bottom-center") // bottom-left, bottom-center, bottom-right
        },
        size: {
            canvasHeight: state.chartHeight(),
            canvasWidth: state.chartWidth(),
            pieInnerRadius: NETDATA.dataAttribute(state.element, 'd3pie-pieinnerradius', "45%"),
            pieOuterRadius: NETDATA.dataAttribute(state.element, 'd3pie-pieouterradius', "80%")
        },
        data: {
            // none, random, value-asc, value-desc, label-asc, label-desc
            sortOrder: NETDATA.dataAttribute(state.element, 'd3pie-sortorder', "value-desc"),
            smallSegmentGrouping: {
                enabled: NETDATA.dataAttributeBoolean(state.element, "d3pie-smallsegmentgrouping-enabled", false),
                value: NETDATA.dataAttribute(state.element, 'd3pie-smallsegmentgrouping-value', 1),
                // percentage, value
                valueType: NETDATA.dataAttribute(state.element, 'd3pie-smallsegmentgrouping-valuetype', "percentage"),
                label: NETDATA.dataAttribute(state.element, 'd3pie-smallsegmentgrouping-label', "other"),
                color: NETDATA.dataAttribute(state.element, 'd3pie-smallsegmentgrouping-color', NETDATA.themes.current.d3pie.other)
            },

            // REQUIRED! This is where you enter your pie data; it needs to be an array of objects
            // of this form: { label: "label", value: 1.5, color: "#000000" } - color is optional
            content: content
        },
        labels: {
            outer: {
                // label, value, percentage, label-value1, label-value2, label-percentage1, label-percentage2
                format: NETDATA.dataAttribute(state.element, 'd3pie-labels-outer-format', "label-value1"),
                hideWhenLessThanPercentage: NETDATA.dataAttribute(state.element, 'd3pie-labels-outer-hidewhenlessthanpercentage', null),
                pieDistance: NETDATA.dataAttribute(state.element, 'd3pie-labels-outer-piedistance', 15)
            },
            inner: {
                // label, value, percentage, label-value1, label-value2, label-percentage1, label-percentage2
                format: NETDATA.dataAttribute(state.element, 'd3pie-labels-inner-format', "percentage"),
                hideWhenLessThanPercentage: NETDATA.dataAttribute(state.element, 'd3pie-labels-inner-hidewhenlessthanpercentage', 2)
            },
            mainLabel: {
                color: NETDATA.dataAttribute(state.element, 'd3pie-labels-mainLabel-color', NETDATA.themes.current.d3pie.mainlabel), // or 'segment' for dynamic color
                font: NETDATA.dataAttribute(state.element, 'd3pie-labels-mainLabel-font', "arial"),
                fontSize: NETDATA.dataAttribute(state.element, 'd3pie-labels-mainLabel-fontsize', 10),
                fontWeight: NETDATA.dataAttribute(state.element, 'd3pie-labels-mainLabel-fontweight', "normal")
            },
            percentage: {
                color: NETDATA.dataAttribute(state.element, 'd3pie-labels-percentage-color', NETDATA.themes.current.d3pie.percentage),
                font: NETDATA.dataAttribute(state.element, 'd3pie-labels-percentage-font', "arial"),
                fontSize: NETDATA.dataAttribute(state.element, 'd3pie-labels-percentage-fontsize', 10),
                fontWeight: NETDATA.dataAttribute(state.element, 'd3pie-labels-percentage-fontweight', "bold"),
                decimalPlaces: 0
            },
            value: {
                color: NETDATA.dataAttribute(state.element, 'd3pie-labels-value-color', NETDATA.themes.current.d3pie.value),
                font: NETDATA.dataAttribute(state.element, 'd3pie-labels-value-font', "arial"),
                fontSize: NETDATA.dataAttribute(state.element, 'd3pie-labels-value-fontsize', 10),
                fontWeight: NETDATA.dataAttribute(state.element, 'd3pie-labels-value-fontweight', "bold")
            },
            lines: {
                enabled: NETDATA.dataAttributeBoolean(state.element, 'd3pie-labels-lines-enabled', true),
                style: NETDATA.dataAttribute(state.element, 'd3pie-labels-lines-style', "curved"),
                color: NETDATA.dataAttribute(state.element, 'd3pie-labels-lines-color', "segment") // "segment" or a hex color
            },
            truncation: {
                enabled: NETDATA.dataAttributeBoolean(state.element, 'd3pie-labels-truncation-enabled', false),
                truncateLength: NETDATA.dataAttribute(state.element, 'd3pie-labels-truncation-truncatelength', 30)
            },
            formatter: function (context) {
                // console.log(context);
                if (context.part === 'value') {
                    return state.legendFormatValue(context.value);
                }
                if (context.part === 'percentage') {
                    return context.label + '%';
                }

                return context.label;
            }
        },
        effects: {
            load: {
                effect: "none", // none / default
                speed: 0 // commented in the d3pie code to speed it up
            },
            pullOutSegmentOnClick: {
                effect: "bounce", // none / linear / bounce / elastic / back
                speed: 400,
                size: 5
            },
            highlightSegmentOnMouseover: true,
            highlightLuminosity: -0.2
        },
        tooltips: {
            enabled: false,
            type: "placeholder", // caption|placeholder
            string: "",
            placeholderParser: null, // function
            styles: {
                fadeInSpeed: 250,
                backgroundColor: NETDATA.themes.current.d3pie.tooltip_bg,
                backgroundOpacity: 0.5,
                color: NETDATA.themes.current.d3pie.tooltip_fg,
                borderRadius: 2,
                font: "arial",
                fontSize: 12,
                padding: 4
            }
        },
        misc: {
            colors: {
                background: 'transparent', // transparent or color #
                // segments: state.chartColors(),
                segmentStroke: NETDATA.dataAttribute(state.element, 'd3pie-misc-colors-segmentstroke', NETDATA.themes.current.d3pie.segment_stroke)
            },
            gradient: {
                enabled: NETDATA.dataAttributeBoolean(state.element, 'd3pie-misc-gradient-enabled', false),
                percentage: NETDATA.dataAttribute(state.element, 'd3pie-misc-colors-percentage', 95),
                color: NETDATA.dataAttribute(state.element, 'd3pie-misc-gradient-color', NETDATA.themes.current.d3pie.gradient_color)
            },
            canvasPadding: {
                top: 5,
                right: 5,
                bottom: 5,
                left: 5
            },
            pieCenterOffset: {
                x: 0,
                y: 0
            },
            cssPrefix: NETDATA.dataAttribute(state.element, 'd3pie-cssprefix', null)
        },
        callbacks: {
            onload: null,
            onMouseoverSegment: null,
            onMouseoutSegment: null,
            onClickSegment: null
        }
    };

    state.d3pie_instance = new d3pie(state.element_chart, state.d3pie_options);
    return true;
};