digital-gauge.js

587 lines | 29.099 kB Blame History Raw Download
/*
 * Copyright © 2016 The Thingsboard Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import $ from 'jquery';
import tinycolor from 'tinycolor2';
import 'justgage';
import Raphael from 'raphael';

/* eslint-disable angular/angularelement */

export default class TbDigitalGauge {
    constructor(containerElement, settings, data) {

        var tbGauge = this;

        window.Raphael = Raphael; // eslint-disable-line no-undef, angular/window-service

        var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; // eslint-disable-line no-undef

        var gaugeElement = $(containerElement);

        this.localSettings = {};

        this.localSettings.minValue = settings.minValue || 0;
        this.localSettings.maxValue = settings.maxValue || 100;
        this.localSettings.gaugeType = settings.gaugeType || 'arc';
        this.localSettings.donutStartAngle = (angular.isDefined(settings.donutStartAngle) && settings.donutStartAngle !== null)
            ? settings.donutStartAngle : 90;
        this.localSettings.neonGlowBrightness = settings.neonGlowBrightness || 0;
        this.localSettings.dashThickness = settings.dashThickness || 0;
        this.localSettings.roundedLineCap = settings.roundedLineCap === true;

        var dataKey = data[0].dataKey;
        var keyColor = settings.defaultColor || dataKey.color;

        this.localSettings.title = ((settings.showTitle === true) ?
            (settings.title && settings.title.length > 0 ?
                settings.title : dataKey.label) : '');

        this.localSettings.unitTitle = ((settings.showUnitTitle === true) ?
            (settings.unitTitle && settings.unitTitle.length > 0 ?
                settings.unitTitle : dataKey.label) : '');

        this.localSettings.gaugeWidthScale = settings.gaugeWidthScale || 0.75;
        this.localSettings.gaugeColor = settings.gaugeColor || tinycolor(keyColor).setAlpha(0.2).toRgbString();

        if (!settings.levelColors || settings.levelColors.length <= 0) {
            this.localSettings.levelColors = [keyColor, keyColor];
        } else {
            this.localSettings.levelColors = settings.levelColors.slice();
        }
        if (this.localSettings.neonGlowBrightness) {
            this.localSettings.origLevelColors = [];
            for (var i = 0; i < this.localSettings.levelColors.length; i++) {
                this.localSettings.origLevelColors.push(this.localSettings.levelColors[i]);
                this.localSettings.levelColors[i] = tinycolor(this.localSettings.levelColors[i]).brighten(this.localSettings.neonGlowBrightness).toHexString();
            }
            var colorsCount = this.localSettings.origLevelColors.length;
            var inc = colorsCount > 1 ? (1 / (colorsCount - 1)) : 1;
            this.localSettings.colorsRange = [];
            for (i = 0; i < this.localSettings.origLevelColors.length; i++) {
                var percentage = inc * i;
                var tColor = tinycolor(this.localSettings.origLevelColors[i]);
                this.localSettings.colorsRange[i] = {
                    pct: percentage,
                    color: tColor.toRgb(),
                    rgbString: tColor.toRgbString
                };
            }
        }


        this.localSettings.refreshAnimationType = settings.refreshAnimationType || '>';
        this.localSettings.refreshAnimationTime = settings.refreshAnimationTime || 700;
        this.localSettings.startAnimationType = settings.startAnimationType || '>';
        this.localSettings.startAnimationTime = settings.startAnimationTime || 700;
        this.localSettings.decimals = (angular.isDefined(settings.decimals) && settings.decimals !== null)
            ? settings.decimals : 0;
        this.localSettings.units = settings.units || '';
        this.localSettings.hideValue = settings.showValue !== true;
        this.localSettings.hideMinMax = settings.showMinMax !== true;

        this.localSettings.titleFont = {};
        var settingsTitleFont = settings.titleFont;
        if (!settingsTitleFont) {
            settingsTitleFont = {};
        }

        this.localSettings.titleFont.family = settingsTitleFont.family || 'RobotoDraft';
        this.localSettings.titleFont.size = settingsTitleFont.size ? settingsTitleFont.size : 12;
        this.localSettings.titleFont.style = settingsTitleFont.style ? settingsTitleFont.style : 'normal';
        this.localSettings.titleFont.weight = settingsTitleFont.weight ? settingsTitleFont.weight : '500';
        this.localSettings.titleFont.color = settingsTitleFont.color ? settingsTitleFont.color : keyColor;

        this.localSettings.labelFont = {};
        var settingsLabelFont = settings.labelFont;
        if (!settingsLabelFont) {
            settingsLabelFont = {};
        }

        this.localSettings.labelFont.family = settingsLabelFont.family || 'RobotoDraft';
        this.localSettings.labelFont.size = settingsLabelFont.size ? settingsLabelFont.size : 8;
        this.localSettings.labelFont.style = settingsLabelFont.style ? settingsLabelFont.style : 'normal';
        this.localSettings.labelFont.weight = settingsLabelFont.weight ? settingsLabelFont.weight : '500';
        this.localSettings.labelFont.color = settingsLabelFont.color ? settingsLabelFont.color : keyColor;

        this.localSettings.valueFont = {};
        var settingsValueFont = settings.valueFont;
        if (!settingsValueFont) {
            settingsValueFont = {};
        }

        this.localSettings.valueFont.family = settingsValueFont.family || 'RobotoDraft';
        this.localSettings.valueFont.size = settingsValueFont.size ? settingsValueFont.size : 18;
        this.localSettings.valueFont.style = settingsValueFont.style ? settingsValueFont.style : 'normal';
        this.localSettings.valueFont.weight = settingsValueFont.weight ? settingsValueFont.weight : '500';
        this.localSettings.valueFont.color = settingsValueFont.color ? settingsValueFont.color : keyColor;

        this.localSettings.minMaxFont = {};
        var settingsMinMaxFont = settings.minMaxFont;
        if (!settingsMinMaxFont) {
            settingsMinMaxFont = {};
        }

        this.localSettings.minMaxFont.family = settingsMinMaxFont.family || 'RobotoDraft';
        this.localSettings.minMaxFont.size = settingsMinMaxFont.size ? settingsMinMaxFont.size : 10;
        this.localSettings.minMaxFont.style = settingsMinMaxFont.style ? settingsMinMaxFont.style : 'normal';
        this.localSettings.minMaxFont.weight = settingsMinMaxFont.weight ? settingsMinMaxFont.weight : '500';
        this.localSettings.minMaxFont.color = settingsMinMaxFont.color ? settingsMinMaxFont.color : keyColor;

        if (this.localSettings.neonGlowBrightness) {
            this.localSettings.titleFont.origColor = this.localSettings.titleFont.color;
            this.localSettings.titleFont.color = tinycolor(this.localSettings.titleFont.color).brighten(this.localSettings.neonGlowBrightness).toHexString();
            this.localSettings.labelFont.origColor = this.localSettings.labelFont.color;
            this.localSettings.labelFont.color = tinycolor(this.localSettings.labelFont.color).brighten(this.localSettings.neonGlowBrightness).toHexString();
            this.localSettings.valueFont.origColor = this.localSettings.valueFont.color;
            this.localSettings.valueFont.color = tinycolor(this.localSettings.valueFont.color).brighten(this.localSettings.neonGlowBrightness).toHexString();
            this.localSettings.minMaxFont.origColor = this.localSettings.minMaxFont.color;
            this.localSettings.minMaxFont.color = tinycolor(this.localSettings.minMaxFont.color).brighten(this.localSettings.neonGlowBrightness).toHexString();
        }

        var gaugeOptions = {
            parentNode: gaugeElement[0],
            value: 0,
            min: this.localSettings.minValue,
            max: this.localSettings.maxValue,
            title: this.localSettings.title,
            label: this.localSettings.unitTitle,
            humanFriendlyDecimal: 0,
            gaugeWidthScale: this.localSettings.gaugeWidthScale,
            relativeGaugeSize: true,
            gaugeColor: this.localSettings.gaugeColor,
            levelColors: this.localSettings.levelColors,
            refreshAnimationType: this.localSettings.refreshAnimationType,
            refreshAnimationTime: this.localSettings.refreshAnimationTime,
            startAnimationType: this.localSettings.startAnimationType,
            startAnimationTime: this.localSettings.startAnimationTime,
            humanFriendly: false,
            donut: this.localSettings.gaugeType === 'donut',
            donutStartAngle: this.localSettings.donutStartAngle,
            decimals: this.localSettings.decimals,
            pointer: false,
            symbol: this.localSettings.units,
            hideValue: this.localSettings.hideValue,
            hideMinMax: this.localSettings.hideMinMax,
            titleFontColor: this.localSettings.titleFont.color,
            labelFontColor: this.localSettings.labelFont.color,
            valueFontColor: this.localSettings.valueFont.color,
            valueFontFamily: this.localSettings.valueFont.family
        };

        this.gauge = new JustGage(gaugeOptions); // eslint-disable-line no-undef

        var gParams = this.gauge.params;

        var titleTextElement = $(this.gauge.txtTitle.node);
        titleTextElement.css('fontFamily', this.localSettings.titleFont.family);
        titleTextElement.css('fontSize', this.localSettings.titleFont.size + 'px');
        titleTextElement.css('fontStyle', this.localSettings.titleFont.style);
        titleTextElement.css('fontWeight', this.localSettings.titleFont.weight);
        titleTextElement.css('textTransform', 'uppercase');

        var labelTextElement = $(this.gauge.txtLabel.node);
        labelTextElement.css('fontFamily', this.localSettings.labelFont.family);
        labelTextElement.css('fontSize', this.localSettings.labelFont.size + 'px');
        labelTextElement.css('fontStyle', this.localSettings.labelFont.style);
        labelTextElement.css('fontWeight', this.localSettings.labelFont.weight);
        labelTextElement.css('textTransform', 'uppercase');

        var valueTextElement = $(this.gauge.txtValue.node);
        valueTextElement.css('fontSize', this.localSettings.valueFont.size + 'px');
        valueTextElement.css('fontStyle', this.localSettings.valueFont.style);
        valueTextElement.css('fontWeight', this.localSettings.valueFont.weight);

        var minValTextElement = $(this.gauge.txtMin.node);
        var maxValTextElement = $(this.gauge.txtMax.node);
        minValTextElement.css('fontFamily', this.localSettings.minMaxFont.family);
        maxValTextElement.css('fontFamily', this.localSettings.minMaxFont.family);
        minValTextElement.css('fontSize', this.localSettings.minMaxFont.size+'px');
        maxValTextElement.css('fontSize', this.localSettings.minMaxFont.size+'px');
        minValTextElement.css('fontStyle', this.localSettings.minMaxFont.style);
        maxValTextElement.css('fontStyle', this.localSettings.minMaxFont.style);
        minValTextElement.css('fontWeight', this.localSettings.minMaxFont.weight);
        maxValTextElement.css('fontWeight', this.localSettings.minMaxFont.weight);
        minValTextElement.css('fill', this.localSettings.minMaxFont.color);
        maxValTextElement.css('fill', this.localSettings.minMaxFont.color);

        var gaugeLevelElement = $(this.gauge.level.node);
        var gaugeBackElement = $(this.gauge.gauge.node);

        var w = gParams.widgetW;
        var gws = this.localSettings.gaugeWidthScale;
        var Ro, Ri;
        if (this.localSettings.gaugeType === 'donut') {
            Ro = w / 2 - w / 7;
        } else {
            Ro = w / 2 - w / 10;
        }
        Ri = Ro - w / 6.666666666666667 * gws;
        gParams.strokeWidth = Ro - Ri;

        gParams.viewport = {
            x: 0,
            y: 0,
            width: gParams.canvasW,
            height: gParams.canvasH
        }
        var maxW;
        if (this.localSettings.gaugeType === 'donut') {
            if (gaugeOptions.title && gaugeOptions.title.length > 0) {
                gParams.viewport.height = 140;
            } else {
                gParams.viewport.y = 17;
                gParams.viewport.height = 120;
            }
            gParams.viewport.x = 40;
            gParams.viewport.width = 120;
            $('tspan', labelTextElement).attr('dy', '6');
            if (!this.localSettings.unitTitle || this.localSettings.unitTitle.length === 0) {
                var Cy = gParams.widgetH / 1.95 + gParams.dy;
                gParams.valueY = Cy + (this.localSettings.valueFont.size-4)/2;
                this.gauge.txtValue.attr({"y": gParams.valueY });
            }
        } else if (this.localSettings.gaugeType === 'arc') {
            if (gaugeOptions.title && gaugeOptions.title.length > 0) {
                gParams.viewport.y = 5;
                gParams.viewport.height = 140;
            } else {
                gParams.viewport.y = 40;
                gParams.viewport.height = 100;
            }
            if (this.localSettings.roundedLineCap) {
                $('tspan', minValTextElement).attr('dy', ''+(gParams.strokeWidth/2));
                $('tspan', maxValTextElement).attr('dy', ''+(gParams.strokeWidth/2));
            }
        } else if (this.localSettings.gaugeType === 'horizontalBar') {
            gParams.titleY = gParams.dy + gParams.widgetH / 3.5 + (this.localSettings.title === '' ? 0 : this.localSettings.titleFont.size);
            this.gauge.txtTitle.attr({"y": gParams.titleY });
            gParams.titleBottom = gParams.titleY + (this.localSettings.title === '' ? 0 : 8);

            gParams.valueY = gParams.titleBottom + (this.localSettings.hideValue ? 0 : this.localSettings.valueFont.size);
            gParams.barTop = gParams.valueY + 8;
            gParams.barBottom = gParams.barTop + gParams.strokeWidth;

            this.gauge.txtValue.attr({"y": gParams.valueY });

            if (this.localSettings.hideMinMax && this.localSettings.unitTitle === '') {
                gParams.labelY = gParams.barBottom;
                gParams.barLeft = this.localSettings.minMaxFont.size/3;
                gParams.barRight = gParams.viewport.width - this.localSettings.minMaxFont.size/3;
            } else {
                maxW = Math.max(this.gauge.txtMin.node.getComputedTextLength(), this.gauge.txtMax.node.getComputedTextLength());
                gParams.minX = maxW/2 + this.localSettings.minMaxFont.size/3;
                gParams.maxX = gParams.viewport.width - maxW/2 - this.localSettings.minMaxFont.size/3;
                gParams.barLeft = gParams.minX;
                gParams.barRight = gParams.maxX;
                gParams.labelY = gParams.barBottom + 4 + this.localSettings.labelFont.size;
                this.gauge.txtLabel.attr({"y": gParams.labelY });
                this.gauge.txtMin.attr({"x": gParams.minX, "y": gParams.labelY });
                this.gauge.txtMax.attr({"x": gParams.maxX, "y": gParams.labelY });
            }
            gParams.viewport.y = 40;
            gParams.viewport.height = gParams.labelY-25;
        } else if (this.localSettings.gaugeType === 'verticalBar') {
            gParams.titleY = (this.localSettings.title === '' ? 0 : this.localSettings.titleFont.size) + 8;
            this.gauge.txtTitle.attr({"y": gParams.titleY });
            gParams.titleBottom = gParams.titleY + (this.localSettings.title === '' ? 0 : 8);

            gParams.valueY = gParams.titleBottom + (this.localSettings.hideValue ? 0 : this.localSettings.valueFont.size);
            gParams.barTop = gParams.valueY + 8;
            this.gauge.txtValue.attr({"y": gParams.valueY });

            gParams.labelY = gParams.widgetH - 16;
            if (this.localSettings.unitTitle === '') {
                gParams.barBottom = gParams.labelY;
            } else {
                gParams.barBottom = gParams.labelY - 4 - this.localSettings.labelFont.size;
                this.gauge.txtLabel.attr({"y": gParams.labelY });
            }
            gParams.minX = gParams.maxX = (gParams.widgetW/2 + gParams.dx) + gParams.strokeWidth/2 + this.localSettings.minMaxFont.size/3;
            gParams.minY = gParams.barBottom;
            gParams.maxY = gParams.barTop;
            this.gauge.txtMin.attr({"text-anchor": "start", "x": gParams.minX, "y": gParams.minY });
            this.gauge.txtMax.attr({"text-anchor": "start", "x": gParams.maxX, "y": gParams.maxY });
            maxW = Math.max(this.gauge.txtMin.node.getComputedTextLength(), this.gauge.txtMax.node.getComputedTextLength());
            gParams.prefWidth = gParams.strokeWidth + (maxW + this.localSettings.minMaxFont.size ) * 2;
            gParams.viewport.x = (gParams.canvasW - gParams.prefWidth)/2;
            gParams.viewport.width = gParams.prefWidth;
        }
        this.gauge.canvas.setViewBox(gParams.viewport.x, gParams.viewport.y, gParams.viewport.width, gParams.viewport.height, true);

        if (this.localSettings.dashThickness) {
            var Rm = Ri + gParams.strokeWidth * 0.5;
            var circumference = Math.PI * Rm;
            if (this.localSettings.gaugeType === 'donut') {
                circumference *=2;
            }
            var dashCount = Math.floor(circumference / (this.localSettings.dashThickness));
            if (this.localSettings.gaugeType === 'donut') {
                dashCount = (dashCount | 1) - 1;
            } else {
                dashCount = (dashCount - 1) | 1;
            }
            var dashLength = circumference/dashCount;
            gaugeLevelElement.attr('stroke-dasharray', '' + dashLength + 'px');
            gaugeBackElement.attr('stroke-dasharray', '' + dashLength + 'px');
        }

        function getColor(val, pct) {

            var lower, upper, range, rangePct, pctLower, pctUpper, color;

            if (tbGauge.localSettings.colorsRange.length === 1) {
                return tbGauge.localSettings.colorsRange[0].rgbString;
            }
            if (pct === 0) {
                return tbGauge.localSettings.colorsRange[0].rgbString;
            }

            for (var j = 0; j < tbGauge.localSettings.colorsRange.length; j++) {
                if (pct <= tbGauge.localSettings.colorsRange[j].pct) {
                    lower = tbGauge.localSettings.colorsRange[j - 1];
                    upper = tbGauge.localSettings.colorsRange[j];
                    range = upper.pct - lower.pct;
                    rangePct = (pct - lower.pct) / range;
                    pctLower = 1 - rangePct;
                    pctUpper = rangePct;
                    color = tinycolor({
                        r: Math.floor(lower.color.r * pctLower + upper.color.r * pctUpper),
                        g: Math.floor(lower.color.g * pctLower + upper.color.g * pctUpper),
                        b: Math.floor(lower.color.b * pctLower + upper.color.b * pctUpper)
                    });
                    return color.toRgbString();
                }
            }

        }

        this.gauge.canvas.customAttributes.pki = function(value, min, max, w, h, dx, dy, gws, donut, reverse) { // eslint-disable-line no-unused-vars
            var alpha, Rm, Ro, Ri, Cx, Cy, Xm, Ym, Xo, Yo, path;

            if (tbGauge.localSettings.neonGlowBrightness && !isFirefox
                && tbGauge.floodColorElement1 && tbGauge.floodColorElement2) {
                var progress = (value - min) / (max - min);
                var resultColor = getColor(value, progress);
                var brightenColor1 = tinycolor(resultColor).brighten(tbGauge.localSettings.neonGlowBrightness).toRgbString();
                var brightenColor2 = resultColor;
                tbGauge.floodColorElement1.setAttribute('flood-color', brightenColor1);
                tbGauge.floodColorElement2.setAttribute('flood-color', brightenColor2);
            }

            var gaugeType = tbGauge.localSettings.gaugeType;

            if (gaugeType === 'donut') {
                alpha = (1 - 2 * (value - min) / (max - min)) * Math.PI;
                Ro = w / 2 - w / 7;
                Ri = Ro - w / 6.666666666666667 * gws;
                Rm = Ri + (Ro - Ri)/2;

                Cx = w / 2 + dx;
                Cy = h / 1.95 + dy;

                Xm = w / 2 + dx + Rm * Math.cos(alpha);
                Ym = h - (h - Cy) - Rm * Math.sin(alpha);

                path = "M" + (Cx - Rm) + "," + Cy + " ";
                if ((value - min) > ((max - min) / 2)) {
                    path += "A" + Rm + "," + Rm + " 0 0 1 " + (Cx + Rm) + "," + Cy + " ";
                    path += "A" + Rm + "," + Rm + " 0 0 1 " + Xm + "," + Ym + " ";
                } else {
                    path += "A" + Rm + "," + Rm + " 0 0 1 " + Xm + "," + Ym + " ";
                }
                return {
                    path: path
                };

            } else if (gaugeType === 'arc') {
                alpha = (1 - (value - min) / (max - min)) * Math.PI;
                Ro = w / 2 - w / 10;
                Ri = Ro - w / 6.666666666666667 * gws;
                Rm = Ri + (Ro - Ri)/2;

                Cx = w / 2 + dx;
                Cy = h / 1.25 + dy;

                Xm = w / 2 + dx + Rm * Math.cos(alpha);
                Ym = h - (h - Cy) - Rm * Math.sin(alpha);

                path = "M" + (Cx - Rm) + "," + Cy + " ";
                path += "A" + Rm + "," + Rm + " 0 0 1 " + Xm + "," + Ym + " ";

                return {
                    path: path
                };
            } else if (gaugeType === 'horizontalBar') {
                Cx = tbGauge.gauge.params.barLeft;
                Cy = tbGauge.gauge.params.barTop + tbGauge.gauge.params.strokeWidth/2;
                Ro = (tbGauge.gauge.params.barRight - tbGauge.gauge.params.barLeft)/2;
                alpha = (value - min) / (max - min);
                Xo = Cx + 2 * Ro * alpha;
                path = "M" + Cx + "," + Cy + " ";
                path += "H" + " " + Xo;
                return {
                    path: path
                };
            } else if (gaugeType === 'verticalBar') {
                Cx = w / 2 + dx;
                Cy = tbGauge.gauge.params.barBottom;
                Ro = (tbGauge.gauge.params.barBottom - tbGauge.gauge.params.barTop)/2;
                alpha = (value - min) / (max - min);
                Yo = Cy - 2 * Ro * alpha;
                path = "M" + Cx + "," + Cy + " ";
                path += "V" + " " + Yo;
                return {
                    path: path
                };
            }
        };

        var gaugeAttrs = {
            "stroke":  this.gauge.gauge.attrs.fill,
            "fill": 'rgba(0,0,0,0)',
            pki: [  this.gauge.config.max,
                this.gauge.config.min,
                this.gauge.config.max,
                gParams.widgetW,
                gParams.widgetH,
                gParams.dx,
                gParams.dy,
                this.gauge.config.gaugeWidthScale,
                this.gauge.config.donut,
                this.gauge.config.reverse
            ]
        };
        gaugeAttrs['stroke-width'] = gParams.strokeWidth;


        var gaugeLevelAttrs = {
            "stroke":  this.gauge.level.attrs.fill,
            "fill": 'rgba(0,0,0,0)'
        };
        gaugeLevelAttrs['stroke-width'] = gParams.strokeWidth;
        if (this.localSettings.roundedLineCap) {
            gaugeAttrs['stroke-linecap'] = 'round';
            gaugeLevelAttrs['stroke-linecap'] = 'round';
        }

        this.gauge.gauge.attr(gaugeAttrs);
        this.gauge.level.attr(gaugeLevelAttrs);

        this.gauge.level.animate = function(attrs, refreshAnimationTime, refreshAnimationType) {
            if (attrs.fill) {
                attrs.stroke = attrs.fill;
                attrs.fill = 'rgba(0,0,0,0)';
            }
            return Raphael.el.animate.call(tbGauge.gauge.level, attrs, refreshAnimationTime, refreshAnimationType);
        }

        function neonShadow(color) {
            var brightenColor = tinycolor(color).brighten(tbGauge.localSettings.neonGlowBrightness);
            return     '0 0 10px '+brightenColor+','+
                '0 0 20px  '+brightenColor+','+
                '0 0 30px  '+brightenColor+','+
                '0 0 40px  '+ color +','+
                '0 0 70px  '+ color +','+
                '0 0 80px  '+ color +','+
                '0 0 100px  '+ color +','+
                '0 0 150px  '+ color;
        }

        if (this.localSettings.neonGlowBrightness) {
            titleTextElement.css('textShadow', neonShadow(this.localSettings.titleFont.origColor));
            valueTextElement.css('textShadow', neonShadow(this.localSettings.valueFont.origColor));
            labelTextElement.css('textShadow', neonShadow(this.localSettings.labelFont.origColor));
            minValTextElement.css('textShadow', neonShadow(this.localSettings.minMaxFont.origColor));
            maxValTextElement.css('textShadow', neonShadow(this.localSettings.minMaxFont.origColor));
        }

        if (this.localSettings.neonGlowBrightness && !isFirefox) {
            var filterX = (gParams.viewport.x / gParams.viewport.width)*100 + '%';
            var filterY = (gParams.viewport.y / gParams.viewport.height)*100 + '%';
            var svgBackFilterId = 'backBlurFilter' + Math.random();
            var svgBackFilter = document.createElementNS("http://www.w3.org/2000/svg", "filter"); // eslint-disable-line no-undef, angular/document-service
            svgBackFilter.setAttribute('id', svgBackFilterId);
            svgBackFilter.setAttribute('filterUnits', 'userSpaceOnUse');
            svgBackFilter.setAttribute('x', filterX);
            svgBackFilter.setAttribute('y', filterY);
            svgBackFilter.setAttribute('width', '100%');
            svgBackFilter.setAttribute('height', '100%');
            svgBackFilter.innerHTML =
                '<feComponentTransfer>'+
                '<feFuncR type="linear" slope="1.5"/>'+
                '<feFuncG type="linear" slope="1.5"/>'+
                '<feFuncB type="linear" slope="1.5"/>'+
                '</feComponentTransfer>'+
                '<feGaussianBlur stdDeviation="3" result="coloredBlur"></feGaussianBlur>'+
                '<feMerge>'+
                '<feMergeNode in="coloredBlur"/>'+
                '<feMergeNode in="SourceGraphic"/>'+
                '</feMerge>';
            gaugeBackElement.attr('filter', 'url(#'+svgBackFilterId+')');

            var svgFillFilterId = 'fillBlurFilter' + Math.random();
            var svgFillFilter = document.createElementNS("http://www.w3.org/2000/svg", "filter"); // eslint-disable-line no-undef, angular/document-service
            svgFillFilter.setAttribute('id', svgFillFilterId);
            svgFillFilter.setAttribute('filterUnits', 'userSpaceOnUse');
            svgFillFilter.setAttribute('x', filterX);
            svgFillFilter.setAttribute('y', filterY);
            svgFillFilter.setAttribute('width', '100%');
            svgFillFilter.setAttribute('height', '100%');

            var brightenColor1 = tinycolor(this.localSettings.origLevelColors[0]).brighten(this.localSettings.neonGlowBrightness).toRgbString();
            var brightenColor2 = tinycolor(this.localSettings.origLevelColors[0]).toRgbString();
            svgFillFilter.innerHTML =
                '<feFlood flood-color="'+brightenColor1+'" result="flood1" />'+
                '<feComposite in="flood1" in2="SourceGraphic" operator="in" result="floodShape" />'+
                '<feGaussianBlur in="floodShape" stdDeviation="3" result="blur" />'+
                '<feFlood flood-color="'+brightenColor2+'" result="flood2" />'+
                '<feComposite in="flood2" in2="SourceGraphic" operator="in" result="floodShape2" />'+
                '<feGaussianBlur in="floodShape2" stdDeviation="12" result="blur2" />'+
                '<feMerge result="blurs">'+
                '  <feMergeNode in="blur2"/>'+
                '  <feMergeNode in="blur2"/>'+
                '  <feMergeNode in="blur"/>'+
                '  <feMergeNode in="blur"/>'+
                '  <feMergeNode in="SourceGraphic"/>'+
                '</feMerge>';
            this.floodColorElement1 = $('feFlood:nth-of-type(1)', svgFillFilter)[0];
            this.floodColorElement2 = $('feFlood:nth-of-type(2)', svgFillFilter)[0];
            gaugeLevelElement.attr('filter', 'url(#'+svgFillFilterId+')');

            var svgDefsElement = $('svg > defs', containerElement);
            svgDefsElement[0].appendChild(svgBackFilter);
            svgDefsElement[0].appendChild(svgFillFilter);
        } else {
            gaugeBackElement.attr('filter', '');
            gaugeLevelElement.attr('filter', '');
        }
    }

    redraw(data) {
        if (data.length > 0) {
            var cellData = data[0];
            if (cellData.data.length > 0) {
                var tvPair = cellData.data[cellData.data.length -
                1];
                var value = tvPair[1];
                this.gauge.refresh(value);
            }
        }
    }
}

/* eslint-enable angular/angularelement */