969 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			969 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
///
 | 
						|
/// Copyright © 2016-2024 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 * as CanvasGauges from 'canvas-gauges';
 | 
						|
import { FontStyle, FontWeight } from '@home/components/widget/lib/settings.models';
 | 
						|
import tinycolor from 'tinycolor2';
 | 
						|
import { isDefined, isDefinedAndNotNull, isUndefined, padValue } from '@core/utils';
 | 
						|
import { ColorProcessor, constantColor } from '@shared/models/widget-settings.models';
 | 
						|
import GenericOptions = CanvasGauges.GenericOptions;
 | 
						|
import BaseGauge = CanvasGauges.BaseGauge;
 | 
						|
 | 
						|
export type GaugeType = 'arc' | 'donut' | 'horizontalBar' | 'verticalBar';
 | 
						|
 | 
						|
export interface CanvasDigitalGaugeOptions extends GenericOptions {
 | 
						|
  gaugeType?: GaugeType;
 | 
						|
  gaugeWithScale?: number;
 | 
						|
  dashThickness?: number;
 | 
						|
  roundedLineCap?: boolean;
 | 
						|
  gaugeColor?: string;
 | 
						|
  symbol?: string;
 | 
						|
  hideValue?: boolean;
 | 
						|
  hideMinMax?: boolean;
 | 
						|
  fontTitle?: string;
 | 
						|
  fontValue?: string;
 | 
						|
  fontMinMaxSize?: number;
 | 
						|
  fontMinMaxStyle?: FontStyle;
 | 
						|
  fontMinMaxWeight?: FontWeight;
 | 
						|
  colorMinMax?: string;
 | 
						|
  fontMinMax?: string;
 | 
						|
  fontLabelSize?: number;
 | 
						|
  fontLabelStyle?: FontStyle;
 | 
						|
  fontLabelWeight?: FontWeight;
 | 
						|
  colorLabel?: string;
 | 
						|
  colorValue?: string;
 | 
						|
  fontLabel?: string;
 | 
						|
  neonGlowBrightness?: number;
 | 
						|
  isMobile?: boolean;
 | 
						|
  donutStartAngle?: number;
 | 
						|
  donutEndAngle?: number;
 | 
						|
 | 
						|
  neonColorTitle?: string;
 | 
						|
  neonColorLabel?: string;
 | 
						|
  neonColorValue?: string;
 | 
						|
  neonColorMinMax?: string;
 | 
						|
  timestamp?: number;
 | 
						|
  gaugeWidthScale?: number;
 | 
						|
  fontTitleHeight?: FontHeightInfo;
 | 
						|
  fontLabelHeight?: FontHeightInfo;
 | 
						|
  fontValueHeight?: FontHeightInfo;
 | 
						|
  fontMinMaxHeight?: FontHeightInfo;
 | 
						|
 | 
						|
  ticksValue?: number[];
 | 
						|
  ticks?: number[];
 | 
						|
  colorTicks?: string;
 | 
						|
  tickWidth?: number;
 | 
						|
 | 
						|
  labelTimestamp?: string;
 | 
						|
  unitTitle?: string;
 | 
						|
  showUnitTitle?: boolean;
 | 
						|
  showTimestamp?: boolean;
 | 
						|
 | 
						|
  barColorProcessor: ColorProcessor;
 | 
						|
}
 | 
						|
 | 
						|
const defaultDigitalGaugeOptions: CanvasDigitalGaugeOptions = { ...GenericOptions,
 | 
						|
  ...{
 | 
						|
    gaugeType: 'arc',
 | 
						|
    gaugeWithScale: 0.75,
 | 
						|
    dashThickness: 0,
 | 
						|
    roundedLineCap: false,
 | 
						|
 | 
						|
    gaugeColor: '#777',
 | 
						|
    barColorProcessor: ColorProcessor.fromSettings(constantColor('blue')),
 | 
						|
 | 
						|
    symbol: '',
 | 
						|
    hideValue: false,
 | 
						|
    hideMinMax: false,
 | 
						|
 | 
						|
    fontTitle: 'Roboto',
 | 
						|
 | 
						|
    fontValue: 'Roboto',
 | 
						|
 | 
						|
    fontMinMaxSize: 10,
 | 
						|
    fontMinMaxStyle: 'normal',
 | 
						|
    fontMinMaxWeight: '500',
 | 
						|
    colorMinMax: '#eee',
 | 
						|
    fontMinMax: 'Roboto',
 | 
						|
 | 
						|
    fontLabelSize: 8,
 | 
						|
    fontLabelStyle: 'normal',
 | 
						|
    fontLabelWeight: '500',
 | 
						|
    colorLabel: '#eee',
 | 
						|
    fontLabel: 'Roboto',
 | 
						|
 | 
						|
    neonGlowBrightness: 0,
 | 
						|
 | 
						|
    colorTicks: 'gray',
 | 
						|
    tickWidth: 4,
 | 
						|
    ticks: [],
 | 
						|
 | 
						|
    isMobile: false
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
BaseGauge.initialize('CanvasDigitalGauge', defaultDigitalGaugeOptions);
 | 
						|
 | 
						|
interface HTMLCanvasElementClone extends HTMLCanvasElement {
 | 
						|
  initialized?: boolean;
 | 
						|
  renderedTimestamp?: number;
 | 
						|
  renderedValue?: number;
 | 
						|
  renderedProgress?: string;
 | 
						|
}
 | 
						|
 | 
						|
interface DigitalGaugeCanvasRenderingContext2D extends CanvasRenderingContext2D {
 | 
						|
  barDimensions?: BarDimensions;
 | 
						|
  currentColor?: string;
 | 
						|
}
 | 
						|
 | 
						|
interface BarDimensions {
 | 
						|
  timeseriesLabelY?: number;
 | 
						|
  baseX: number;
 | 
						|
  baseY: number;
 | 
						|
  width: number;
 | 
						|
  height: number;
 | 
						|
  origBaseX?: number;
 | 
						|
  origBaseY?: number;
 | 
						|
  fontSizeFactor?: number;
 | 
						|
  Ro?: number;
 | 
						|
  Cy?: number;
 | 
						|
  titleY?: number;
 | 
						|
  titleBottom?: number;
 | 
						|
  Ri?: number;
 | 
						|
  Cx?: number;
 | 
						|
  strokeWidth?: number;
 | 
						|
  Rm?: number;
 | 
						|
  fontValueBaseline?: CanvasTextBaseline;
 | 
						|
  fontMinMaxBaseline?: CanvasTextBaseline;
 | 
						|
  fontMinMaxAlign?: CanvasTextAlign;
 | 
						|
  labelY?: number;
 | 
						|
  valueY?: number;
 | 
						|
  minY?: number;
 | 
						|
  maxY?: number;
 | 
						|
  minX?: number;
 | 
						|
  maxX?: number;
 | 
						|
  barTop?: number;
 | 
						|
  barBottom?: number;
 | 
						|
  barLeft?: number;
 | 
						|
  barRight?: number;
 | 
						|
  dashLength?: number;
 | 
						|
}
 | 
						|
 | 
						|
interface FontHeightInfo {
 | 
						|
  ascent?: number;
 | 
						|
  height?: number;
 | 
						|
  descent?: number;
 | 
						|
}
 | 
						|
 | 
						|
export class Drawings {
 | 
						|
  static font(options: CanvasGauges.GenericOptions, target: string, baseSize: number): string {
 | 
						|
    return options['font' + target + 'Style'] + ' ' +
 | 
						|
      options['font' + target + 'Weight'] + ' ' +
 | 
						|
      options['font' + target + 'Size'] * baseSize + 'px ' +
 | 
						|
      options['font' + target];
 | 
						|
  }
 | 
						|
  static normalizedValue(options: CanvasGauges.GenericOptions): {normal: number; indented: number} {
 | 
						|
    const value = options.value;
 | 
						|
    const min = options.minValue;
 | 
						|
    const max = options.maxValue;
 | 
						|
    const dt = (max - min) * 0.01;
 | 
						|
    return {
 | 
						|
      normal: value < min ? min : value > max ? max : value,
 | 
						|
      indented: value < min ? min - dt : value > max ? max + dt : value
 | 
						|
    };
 | 
						|
  }
 | 
						|
  static verifyError(err: any) {
 | 
						|
    if (err instanceof DOMException && (err as any).result === 0x8053000b) {
 | 
						|
      return ; // ignore it
 | 
						|
    }
 | 
						|
    throw err;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
export class CanvasDigitalGauge extends BaseGauge {
 | 
						|
 | 
						|
  static heightCache: {[key: string]: FontHeightInfo} = {};
 | 
						|
 | 
						|
  private elementValueClone: HTMLCanvasElementClone;
 | 
						|
  private contextValueClone: DigitalGaugeCanvasRenderingContext2D;
 | 
						|
 | 
						|
  private elementProgressClone: HTMLCanvasElementClone;
 | 
						|
  private contextProgressClone: DigitalGaugeCanvasRenderingContext2D;
 | 
						|
 | 
						|
  public _value: number;
 | 
						|
 | 
						|
  constructor(options: CanvasDigitalGaugeOptions) {
 | 
						|
    options = {...defaultDigitalGaugeOptions, ...(options || {})};
 | 
						|
    super(CanvasDigitalGauge.configure(options));
 | 
						|
    this.initValueClone();
 | 
						|
  }
 | 
						|
 | 
						|
  static configure(options: CanvasDigitalGaugeOptions): CanvasDigitalGaugeOptions {
 | 
						|
 | 
						|
    if (options.value > options.maxValue) {
 | 
						|
      options.value = options.maxValue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (options.value < options.minValue) {
 | 
						|
      options.value = options.minValue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (options.gaugeType === 'donut') {
 | 
						|
      if (!isDefinedAndNotNull(options.donutStartAngle)) {
 | 
						|
        options.donutStartAngle = 1.5 * Math.PI;
 | 
						|
      }
 | 
						|
      if (!isDefinedAndNotNull(options.donutEndAngle)) {
 | 
						|
        options.donutEndAngle = options.donutStartAngle + 2 * Math.PI;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    options.ticksValue = [];
 | 
						|
    for (const tick of options.ticks) {
 | 
						|
      if (tick !== null) {
 | 
						|
        options.ticksValue.push(CanvasDigitalGauge.normalizeValue(tick, options.minValue, options.maxValue));
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (options.neonGlowBrightness) {
 | 
						|
      options.neonColorTitle = tinycolor(options.colorTitle).brighten(options.neonGlowBrightness).toRgbString();
 | 
						|
      options.neonColorLabel = tinycolor(options.colorLabel).brighten(options.neonGlowBrightness).toRgbString();
 | 
						|
      options.neonColorValue = tinycolor(options.colorValue).brighten(options.neonGlowBrightness).toRgbString();
 | 
						|
      options.neonColorMinMax = tinycolor(options.colorMinMax).brighten(options.neonGlowBrightness).toRgbString();
 | 
						|
    }
 | 
						|
 | 
						|
    return options;
 | 
						|
  }
 | 
						|
 | 
						|
  static normalizeValue(value: number, min: number, max: number): number {
 | 
						|
    const normalValue = (value - min) / (max - min);
 | 
						|
    if (normalValue <= 0) {
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
    if (normalValue >= 1) {
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
    return normalValue;
 | 
						|
  }
 | 
						|
 | 
						|
  private initValueClone() {
 | 
						|
    const canvas = this.canvas;
 | 
						|
    this.elementValueClone = canvas.element.cloneNode(true) as HTMLCanvasElementClone;
 | 
						|
    this.contextValueClone = this.elementValueClone.getContext('2d');
 | 
						|
    this.elementValueClone.initialized = false;
 | 
						|
 | 
						|
    this.contextValueClone.translate(canvas.drawX, canvas.drawY);
 | 
						|
    this.contextValueClone.save();
 | 
						|
 | 
						|
    this.elementProgressClone = canvas.element.cloneNode(true) as HTMLCanvasElementClone;
 | 
						|
    this.contextProgressClone = this.elementProgressClone.getContext('2d');
 | 
						|
    this.elementProgressClone.initialized = false;
 | 
						|
 | 
						|
    this.contextProgressClone.translate(canvas.drawX, canvas.drawY);
 | 
						|
    this.contextProgressClone.save();
 | 
						|
  }
 | 
						|
 | 
						|
  destroy() {
 | 
						|
    this.contextValueClone = null;
 | 
						|
    this.elementValueClone = null;
 | 
						|
    this.contextProgressClone = null;
 | 
						|
    this.elementProgressClone = null;
 | 
						|
    super.destroy();
 | 
						|
  }
 | 
						|
 | 
						|
  update(options: GenericOptions): BaseGauge {
 | 
						|
    this.canvas.onRedraw = null;
 | 
						|
    const result = super.update(options);
 | 
						|
    this.initValueClone();
 | 
						|
    this.canvas.onRedraw = this.draw.bind(this);
 | 
						|
    this.draw();
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
 | 
						|
  set timestamp(timestamp: number) {
 | 
						|
    (this.options as CanvasDigitalGaugeOptions).timestamp = timestamp;
 | 
						|
    this.draw();
 | 
						|
  }
 | 
						|
 | 
						|
  get timestamp(): number {
 | 
						|
    return (this.options as CanvasDigitalGaugeOptions).timestamp;
 | 
						|
  }
 | 
						|
 | 
						|
  draw(): CanvasDigitalGauge {
 | 
						|
    try {
 | 
						|
      const canvas = this.canvas;
 | 
						|
      if (!canvas.drawWidth || !canvas.drawHeight) {
 | 
						|
        return this;
 | 
						|
      }
 | 
						|
      const [x, y, w, h] = [
 | 
						|
        -canvas.drawX,
 | 
						|
        -canvas.drawY,
 | 
						|
        canvas.drawWidth,
 | 
						|
        canvas.drawHeight
 | 
						|
      ];
 | 
						|
      const options = this.options as CanvasDigitalGaugeOptions;
 | 
						|
      const elementClone = canvas.elementClone as HTMLCanvasElementClone;
 | 
						|
      if (!elementClone.initialized) {
 | 
						|
        const context: DigitalGaugeCanvasRenderingContext2D = canvas.contextClone;
 | 
						|
 | 
						|
        // clear the cache
 | 
						|
        context.clearRect(x, y, w, h);
 | 
						|
        context.save();
 | 
						|
 | 
						|
        const canvasContext = canvas.context as DigitalGaugeCanvasRenderingContext2D;
 | 
						|
 | 
						|
        canvasContext.barDimensions = barDimensions(context, options, x, y, w, h);
 | 
						|
        this.contextValueClone.barDimensions = canvasContext.barDimensions;
 | 
						|
        this.contextProgressClone.barDimensions = canvasContext.barDimensions;
 | 
						|
 | 
						|
        drawBackground(context, options);
 | 
						|
 | 
						|
        drawDigitalTitle(context, options);
 | 
						|
 | 
						|
        if (options.showUnitTitle) {
 | 
						|
          drawDigitalLabel(context, options, options.unitTitle, 'labelY');
 | 
						|
        }
 | 
						|
 | 
						|
        drawDigitalMinMax(context, options);
 | 
						|
 | 
						|
        elementClone.initialized = true;
 | 
						|
      }
 | 
						|
 | 
						|
      let valueChanged = false;
 | 
						|
      if (!this.elementValueClone.initialized ||
 | 
						|
           isDefined(this._value) && this.elementValueClone.renderedValue !== this._value ||
 | 
						|
           (options.showTimestamp && this.elementValueClone.renderedTimestamp !== this.timestamp)) {
 | 
						|
        if (isDefined(this._value)) {
 | 
						|
          this.elementValueClone.renderedValue = this._value;
 | 
						|
        }
 | 
						|
        if (isUndefined(this.elementValueClone.renderedValue)) {
 | 
						|
          this.elementValueClone.renderedValue = this.value;
 | 
						|
        }
 | 
						|
        const context = this.contextValueClone;
 | 
						|
        // clear the cache
 | 
						|
        context.clearRect(x, y, w, h);
 | 
						|
 | 
						|
        context.drawImage(canvas.elementClone, x, y, w, h);
 | 
						|
 | 
						|
        drawDigitalValue(context, options, this.elementValueClone.renderedValue);
 | 
						|
 | 
						|
        if (options.showTimestamp) {
 | 
						|
          drawDigitalLabel(context, options, options.labelTimestamp, 'timeseriesLabelY');
 | 
						|
          this.elementValueClone.renderedTimestamp = this.timestamp;
 | 
						|
        }
 | 
						|
 | 
						|
        this.elementValueClone.initialized = true;
 | 
						|
 | 
						|
        valueChanged = true;
 | 
						|
      }
 | 
						|
 | 
						|
      const progress = (Drawings.normalizedValue(options).normal - options.minValue) /
 | 
						|
        (options.maxValue - options.minValue);
 | 
						|
 | 
						|
      const fixedProgress = progress.toFixed(3);
 | 
						|
 | 
						|
      if (!this.elementProgressClone.initialized || this.elementProgressClone.renderedProgress !== fixedProgress || valueChanged) {
 | 
						|
        const context = this.contextProgressClone;
 | 
						|
        // clear the cache
 | 
						|
        context.clearRect(x, y, w, h);
 | 
						|
 | 
						|
        context.drawImage(this.elementValueClone, x, y, w, h);
 | 
						|
 | 
						|
        if (Number(fixedProgress) > 0) {
 | 
						|
          drawProgress(context, options, progress);
 | 
						|
        }
 | 
						|
 | 
						|
        this.elementProgressClone.initialized = true;
 | 
						|
        this.elementProgressClone.renderedProgress = fixedProgress;
 | 
						|
      }
 | 
						|
 | 
						|
      this.canvas.commit();
 | 
						|
 | 
						|
      // clear the canvas
 | 
						|
      canvas.context.clearRect(x, y, w, h);
 | 
						|
 | 
						|
      canvas.context.drawImage(this.elementProgressClone, x, y, w, h);
 | 
						|
 | 
						|
      // @ts-ignore
 | 
						|
      super.draw();
 | 
						|
 | 
						|
    } catch (err) {
 | 
						|
      Drawings.verifyError(err);
 | 
						|
    }
 | 
						|
    return this;
 | 
						|
  }
 | 
						|
 | 
						|
  getValueColor() {
 | 
						|
    if (this.contextProgressClone) {
 | 
						|
      let color = this.contextProgressClone.currentColor;
 | 
						|
      const options = this.options as CanvasDigitalGaugeOptions;
 | 
						|
      if (!color) {
 | 
						|
        options.barColorProcessor.update(options.value);
 | 
						|
        const calculateColor = tinycolor(options.barColorProcessor.color);
 | 
						|
        if (options.neonGlowBrightness) {
 | 
						|
          color = calculateColor.brighten(options.neonGlowBrightness).toRgbString();
 | 
						|
        } else {
 | 
						|
          color = calculateColor.toRgbString();
 | 
						|
        }
 | 
						|
      }
 | 
						|
      return color;
 | 
						|
    } else {
 | 
						|
      return '#000';
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
function barDimensions(context: DigitalGaugeCanvasRenderingContext2D,
 | 
						|
                       options: CanvasDigitalGaugeOptions,
 | 
						|
                       x: number, y: number, w: number, h: number): BarDimensions {
 | 
						|
  context.barDimensions = {
 | 
						|
    baseX: x,
 | 
						|
    baseY: y,
 | 
						|
    width: w,
 | 
						|
    height: h
 | 
						|
  };
 | 
						|
 | 
						|
  const bd = context.barDimensions;
 | 
						|
 | 
						|
  let aspect = 1;
 | 
						|
 | 
						|
  if (options.gaugeType === 'horizontalBar') {
 | 
						|
    aspect = options.title === '' ? 2.5 : 2;
 | 
						|
  } else if (options.gaugeType === 'verticalBar') {
 | 
						|
    aspect = options.hideMinMax ? 0.35 : 0.5;
 | 
						|
  } else if (options.gaugeType === 'arc') {
 | 
						|
    aspect = 1.5;
 | 
						|
  }
 | 
						|
 | 
						|
  const currentAspect = w / h;
 | 
						|
  if (currentAspect > aspect) {
 | 
						|
    bd.width = (h * aspect);
 | 
						|
    bd.height = h;
 | 
						|
  } else {
 | 
						|
    bd.width = w;
 | 
						|
    bd.height = w / aspect;
 | 
						|
  }
 | 
						|
 | 
						|
  bd.origBaseX = bd.baseX;
 | 
						|
  bd.origBaseY = bd.baseY;
 | 
						|
  bd.baseX += (w - bd.width) / 2;
 | 
						|
  bd.baseY += (h - bd.height) / 2;
 | 
						|
 | 
						|
  if (options.gaugeType === 'donut') {
 | 
						|
    bd.fontSizeFactor = Math.max(bd.width, bd.height) / 125;
 | 
						|
  } else if (options.gaugeType === 'verticalBar' || (options.gaugeType === 'arc' && options.title === '')) {
 | 
						|
    bd.fontSizeFactor = Math.max(bd.width, bd.height) / 150;
 | 
						|
  } else {
 | 
						|
    bd.fontSizeFactor = Math.max(bd.width, bd.height) / 200;
 | 
						|
  }
 | 
						|
 | 
						|
  const gws = options.gaugeWidthScale;
 | 
						|
 | 
						|
  if (options.neonGlowBrightness) {
 | 
						|
    options.fontTitleHeight = determineFontHeight(options, 'Title', bd.fontSizeFactor);
 | 
						|
    options.fontLabelHeight = determineFontHeight(options, 'Label', bd.fontSizeFactor);
 | 
						|
    options.fontValueHeight = determineFontHeight(options, 'Value', bd.fontSizeFactor);
 | 
						|
    options.fontMinMaxHeight = determineFontHeight(options, 'MinMax', bd.fontSizeFactor);
 | 
						|
  }
 | 
						|
 | 
						|
  if (options.gaugeType === 'donut') {
 | 
						|
    bd.Ro = bd.width / 2 - bd.width / 20;
 | 
						|
    bd.Cy = bd.baseY + bd.height / 2;
 | 
						|
    if (options.title && typeof options.title === 'string' && options.title.length > 0) {
 | 
						|
      let titleOffset = determineFontHeight(options, 'Title', bd.fontSizeFactor).height;
 | 
						|
      titleOffset += bd.fontSizeFactor * 2;
 | 
						|
      bd.titleY = bd.baseY + titleOffset;
 | 
						|
      titleOffset += bd.fontSizeFactor * 2;
 | 
						|
      bd.Cy += titleOffset / 2;
 | 
						|
      bd.Ro -= titleOffset / 2;
 | 
						|
    }
 | 
						|
    bd.Ri = bd.Ro - bd.width / 6.666666666666667 * gws * 1.2;
 | 
						|
    bd.Cx = bd.baseX + bd.width / 2;
 | 
						|
  } else if (options.gaugeType === 'arc') {
 | 
						|
    if (options.title && typeof options.title === 'string' && options.title.length > 0) {
 | 
						|
      bd.Ro = bd.width / 2 - bd.width / 7;
 | 
						|
      bd.Ri = bd.Ro - bd.width / 6.666666666666667 * gws;
 | 
						|
    } else {
 | 
						|
      bd.Ro = bd.width / 2 - bd.fontSizeFactor * 4;
 | 
						|
      bd.Ri = bd.Ro - bd.width / 6.666666666666667 * gws * 1.2;
 | 
						|
    }
 | 
						|
    bd.Cx = bd.baseX + bd.width / 2;
 | 
						|
    bd.Cy = bd.baseY + bd.height / 1.25;
 | 
						|
  } else if (options.gaugeType === 'verticalBar') {
 | 
						|
    bd.Ro = bd.width / 2 - bd.width / 10;
 | 
						|
    bd.Ri = bd.Ro - bd.width / 6.666666666666667 * gws * (options.hideMinMax ? 4 : 2.5);
 | 
						|
  } else { // horizontalBar
 | 
						|
    bd.Ro = bd.width / 2 - bd.width / 10;
 | 
						|
    bd.Ri = bd.Ro - bd.width / 6.666666666666667 * gws;
 | 
						|
  }
 | 
						|
 | 
						|
  bd.strokeWidth = bd.Ro - bd.Ri;
 | 
						|
  bd.Rm = bd.Ri + bd.strokeWidth * 0.5;
 | 
						|
 | 
						|
  bd.fontValueBaseline = 'alphabetic';
 | 
						|
  bd.fontMinMaxBaseline = 'alphabetic';
 | 
						|
  bd.fontMinMaxAlign = 'center';
 | 
						|
 | 
						|
  if (options.gaugeType === 'donut') {
 | 
						|
    bd.fontValueBaseline = 'middle';
 | 
						|
    if (options.showUnitTitle || options.showTimestamp) {
 | 
						|
      const valueHeight = determineFontHeight(options, 'Value', bd.fontSizeFactor).height;
 | 
						|
      const labelHeight = determineFontHeight(options, 'Label', bd.fontSizeFactor).height;
 | 
						|
      const total = valueHeight + labelHeight;
 | 
						|
      bd.labelY = bd.Cy + total / 2;
 | 
						|
      bd.timeseriesLabelY = determineTimeseriesLabelY(options, bd.labelY, bd.fontSizeFactor);
 | 
						|
      bd.valueY = bd.Cy - total / 2 + valueHeight / 2;
 | 
						|
    } else {
 | 
						|
      bd.valueY = bd.Cy;
 | 
						|
    }
 | 
						|
  } else if (options.gaugeType === 'arc') {
 | 
						|
    bd.titleY = bd.Cy - bd.Ro - 12 * bd.fontSizeFactor;
 | 
						|
    bd.valueY = bd.Cy;
 | 
						|
    bd.labelY = bd.Cy + (8 + options.fontLabelSize) * bd.fontSizeFactor;
 | 
						|
    bd.timeseriesLabelY = determineTimeseriesLabelY(options, bd.labelY, bd.fontSizeFactor);
 | 
						|
    bd.minY = bd.maxY = bd.labelY;
 | 
						|
    if (options.roundedLineCap) {
 | 
						|
      bd.minY += bd.strokeWidth / 2;
 | 
						|
      bd.maxY += bd.strokeWidth / 2;
 | 
						|
    }
 | 
						|
    bd.minX = bd.Cx - bd.Rm;
 | 
						|
    bd.maxX = bd.Cx + bd.Rm;
 | 
						|
  } else if (options.gaugeType === 'horizontalBar') {
 | 
						|
    bd.titleY = bd.baseY + 4 * bd.fontSizeFactor +
 | 
						|
      (options.title === '' ? 0 : options.fontTitleSize * bd.fontSizeFactor);
 | 
						|
    bd.titleBottom = bd.titleY + (options.title === '' ? 0 : 4) * bd.fontSizeFactor;
 | 
						|
 | 
						|
    bd.valueY = bd.titleBottom +
 | 
						|
      (options.hideValue ? 0 : options.fontValueSize * bd.fontSizeFactor);
 | 
						|
 | 
						|
    bd.barTop = bd.valueY + 8 * bd.fontSizeFactor;
 | 
						|
    bd.barBottom = bd.barTop + bd.strokeWidth;
 | 
						|
 | 
						|
    if (options.hideMinMax && !options.showUnitTitle && !options.showTimestamp) {
 | 
						|
      bd.labelY = bd.barBottom;
 | 
						|
      bd.timeseriesLabelY = determineTimeseriesLabelY(options, bd.labelY, bd.fontSizeFactor);
 | 
						|
      bd.barLeft = bd.origBaseX + options.fontMinMaxSize / 3 * bd.fontSizeFactor;
 | 
						|
      bd.barRight = bd.origBaseX + w + /*bd.width*/ -options.fontMinMaxSize / 3 * bd.fontSizeFactor;
 | 
						|
    } else {
 | 
						|
      context.font = Drawings.font(options, 'MinMax', bd.fontSizeFactor);
 | 
						|
      const minTextWidth = context.measureText(options.minValue + '').width;
 | 
						|
      const maxTextWidth = context.measureText(options.maxValue + '').width;
 | 
						|
      const maxW = Math.max(minTextWidth, maxTextWidth);
 | 
						|
      bd.minX = bd.origBaseX + maxW / 2 + options.fontMinMaxSize / 3 * bd.fontSizeFactor;
 | 
						|
      bd.maxX = bd.origBaseX + w + /*bd.width*/ -maxW / 2 - options.fontMinMaxSize / 3 * bd.fontSizeFactor;
 | 
						|
      bd.barLeft = bd.minX;
 | 
						|
      bd.barRight = bd.maxX;
 | 
						|
      bd.labelY = bd.barBottom + (8 + options.fontLabelSize) * bd.fontSizeFactor;
 | 
						|
      bd.timeseriesLabelY = determineTimeseriesLabelY(options, bd.labelY, bd.fontSizeFactor);
 | 
						|
      bd.minY = bd.maxY = bd.labelY;
 | 
						|
    }
 | 
						|
  } else if (options.gaugeType === 'verticalBar') {
 | 
						|
    bd.titleY = bd.baseY + ((options.title === '' ? 0 : options.fontTitleSize) + 8) * bd.fontSizeFactor;
 | 
						|
    bd.titleBottom = bd.titleY + (options.title === '' ? 0 : 4) * bd.fontSizeFactor;
 | 
						|
 | 
						|
    bd.valueY = bd.titleBottom + (options.hideValue ? 0 : options.fontValueSize * bd.fontSizeFactor);
 | 
						|
    bd.barTop = bd.valueY + 8 * bd.fontSizeFactor;
 | 
						|
 | 
						|
    bd.labelY = bd.baseY + bd.height;
 | 
						|
    bd.timeseriesLabelY = determineTimeseriesLabelY(options, bd.labelY, bd.fontSizeFactor);
 | 
						|
    if (options.showUnitTitle || options.showTimestamp) {
 | 
						|
      bd.barBottom = bd.labelY - options.fontLabelSize * bd.fontSizeFactor;
 | 
						|
    } else {
 | 
						|
      bd.barBottom = bd.labelY;
 | 
						|
    }
 | 
						|
    bd.minX = bd.maxX = bd.baseX + bd.width / 2 + bd.strokeWidth / 2 + options.fontMinMaxSize / 3 * bd.fontSizeFactor;
 | 
						|
    bd.minY = bd.barBottom;
 | 
						|
    bd.maxY = bd.barTop;
 | 
						|
    bd.fontMinMaxBaseline = 'middle';
 | 
						|
    bd.fontMinMaxAlign = 'left';
 | 
						|
  }
 | 
						|
 | 
						|
  if (options.dashThickness) {
 | 
						|
    let circumference: number;
 | 
						|
    if (options.gaugeType === 'donut') {
 | 
						|
      circumference = Math.PI * bd.Rm * 2;
 | 
						|
    } else if (options.gaugeType === 'arc') {
 | 
						|
      circumference = Math.PI * bd.Rm;
 | 
						|
    } else if (options.gaugeType === 'horizontalBar') {
 | 
						|
      circumference = bd.barRight - bd.barLeft;
 | 
						|
    } else if (options.gaugeType === 'verticalBar') {
 | 
						|
      circumference = bd.barBottom - bd.barTop;
 | 
						|
    }
 | 
						|
    let dashCount = Math.floor(circumference / (options.dashThickness * bd.fontSizeFactor));
 | 
						|
    if (options.gaugeType === 'donut') {
 | 
						|
      // eslint-disable-next-line no-bitwise
 | 
						|
      dashCount = (dashCount | 1) - 1;
 | 
						|
    } else {
 | 
						|
      // eslint-disable-next-line no-bitwise
 | 
						|
      dashCount = (dashCount - 1) | 1;
 | 
						|
    }
 | 
						|
    bd.dashLength = Math.ceil(circumference / dashCount);
 | 
						|
  }
 | 
						|
 | 
						|
  return bd;
 | 
						|
}
 | 
						|
 | 
						|
function determineTimeseriesLabelY(options: CanvasDigitalGaugeOptions, labelY: number, fontSizeFactor: number){
 | 
						|
  if (options.showUnitTitle) {
 | 
						|
    return  labelY + options.fontLabelSize * fontSizeFactor * 1.2;
 | 
						|
  } else {
 | 
						|
    return labelY;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
function determineFontHeight(options: CanvasDigitalGaugeOptions, target: string, baseSize: number): FontHeightInfo {
 | 
						|
  const fontStyleStr = 'font-style:' + options['font' + target + 'Style'] + ';font-weight:' +
 | 
						|
    options['font' + target + 'Weight'] + ';font-size:' +
 | 
						|
    options['font' + target + 'Size'] * baseSize + 'px;font-family:' +
 | 
						|
    options['font' + target];
 | 
						|
  let result = CanvasDigitalGauge.heightCache[fontStyleStr];
 | 
						|
  if (!result) {
 | 
						|
    const fontStyle = {
 | 
						|
      fontFamily: options['font' + target],
 | 
						|
      fontSize: options['font' + target + 'Size'] * baseSize + 'px',
 | 
						|
      fontWeight: options['font' + target + 'Weight'],
 | 
						|
      fontStyle: options['font' + target + 'Style']
 | 
						|
    };
 | 
						|
    const text = $('<span>Hg</span>').css(fontStyle);
 | 
						|
    const block = $('<div style="display: inline-block; width: 1px; height: 0;"></div>');
 | 
						|
 | 
						|
    const div = $('<div></div>');
 | 
						|
    div.append(text, block);
 | 
						|
 | 
						|
    const body = $('body');
 | 
						|
    body.append(div);
 | 
						|
 | 
						|
    try {
 | 
						|
      result = {};
 | 
						|
      block.css({verticalAlign: 'baseline'});
 | 
						|
      result.ascent = block.offset().top - text.offset().top;
 | 
						|
      block.css({verticalAlign: 'bottom'});
 | 
						|
      result.height = block.offset().top - text.offset().top;
 | 
						|
      result.descent = result.height - result.ascent;
 | 
						|
    } finally {
 | 
						|
      div.remove();
 | 
						|
    }
 | 
						|
 | 
						|
    CanvasDigitalGauge.heightCache[fontStyleStr] = result;
 | 
						|
  }
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
function drawBackground(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions) {
 | 
						|
  const {barLeft, barRight, barTop, barBottom, width, baseX, strokeWidth} =
 | 
						|
    context.barDimensions;
 | 
						|
  if (context.barDimensions.dashLength) {
 | 
						|
    context.setLineDash([context.barDimensions.dashLength]);
 | 
						|
  }
 | 
						|
  context.beginPath();
 | 
						|
  context.strokeStyle = options.gaugeColor;
 | 
						|
  context.lineWidth = strokeWidth;
 | 
						|
  if (options.roundedLineCap) {
 | 
						|
    context.lineCap = 'round';
 | 
						|
  }
 | 
						|
  if (options.gaugeType === 'donut') {
 | 
						|
    context.arc(context.barDimensions.Cx, context.barDimensions.Cy, context.barDimensions.Rm,
 | 
						|
      options.donutStartAngle, options.donutEndAngle);
 | 
						|
    context.stroke();
 | 
						|
  } else if (options.gaugeType === 'arc') {
 | 
						|
    context.arc(context.barDimensions.Cx, context.barDimensions.Cy,
 | 
						|
      context.barDimensions.Rm, Math.PI, 2 * Math.PI);
 | 
						|
    context.stroke();
 | 
						|
  } else if (options.gaugeType === 'horizontalBar') {
 | 
						|
    context.moveTo(barLeft, barTop + strokeWidth / 2);
 | 
						|
    context.lineTo(barRight, barTop + strokeWidth / 2);
 | 
						|
    context.stroke();
 | 
						|
  } else if (options.gaugeType === 'verticalBar') {
 | 
						|
    context.moveTo(baseX + width / 2, barBottom);
 | 
						|
    context.lineTo(baseX + width / 2, barTop);
 | 
						|
    context.stroke();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
function drawText(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions,
 | 
						|
                  target: string, text: string, textX: number, textY: number) {
 | 
						|
  context.fillStyle = options[(options.neonGlowBrightness ? 'neonColor' : 'color') + target];
 | 
						|
  context.fillText(text, textX, textY);
 | 
						|
}
 | 
						|
 | 
						|
function drawDigitalTitle(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions) {
 | 
						|
  if (!options.title || typeof options.title !== 'string') {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  const {titleY, width, baseX, fontSizeFactor} =
 | 
						|
    context.barDimensions;
 | 
						|
 | 
						|
  const textX = Math.round(baseX + width / 2);
 | 
						|
  const textY = titleY;
 | 
						|
 | 
						|
  context.save();
 | 
						|
  context.textAlign = 'center';
 | 
						|
  context.font = Drawings.font(options, 'Title', fontSizeFactor);
 | 
						|
  context.lineWidth = 0;
 | 
						|
  drawText(context, options, 'Title', options.title.toUpperCase(), textX, textY);
 | 
						|
  context.restore();
 | 
						|
}
 | 
						|
 | 
						|
function drawDigitalLabel(context: DigitalGaugeCanvasRenderingContext2D,
 | 
						|
                          options: CanvasDigitalGaugeOptions,
 | 
						|
                          text: string,
 | 
						|
                          nameTextY: string) {
 | 
						|
  if (!text || text === '') {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  const {baseX, width, fontSizeFactor} =
 | 
						|
    context.barDimensions;
 | 
						|
 | 
						|
  const textX = Math.round(baseX + width / 2);
 | 
						|
  const textY = context.barDimensions[nameTextY];
 | 
						|
 | 
						|
  context.save();
 | 
						|
  context.textAlign = 'center';
 | 
						|
  context.font = Drawings.font(options, 'Label', fontSizeFactor);
 | 
						|
  context.lineWidth = 0;
 | 
						|
  drawText(context, options, 'Label', text, textX, textY);
 | 
						|
  context.restore();
 | 
						|
}
 | 
						|
 | 
						|
function drawDigitalMinMax(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions) {
 | 
						|
  if (options.hideMinMax || options.gaugeType === 'donut') {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  const {minY, maxY, minX, maxX, fontSizeFactor, fontMinMaxAlign, fontMinMaxBaseline} =
 | 
						|
    context.barDimensions;
 | 
						|
 | 
						|
  context.save();
 | 
						|
  context.textAlign = fontMinMaxAlign;
 | 
						|
  context.textBaseline = fontMinMaxBaseline;
 | 
						|
  context.font = Drawings.font(options, 'MinMax', fontSizeFactor);
 | 
						|
  context.lineWidth = 0;
 | 
						|
  drawText(context, options, 'MinMax', options.minValue + '', minX, minY);
 | 
						|
  drawText(context, options, 'MinMax', options.maxValue + '', maxX, maxY);
 | 
						|
  context.restore();
 | 
						|
}
 | 
						|
 | 
						|
function drawDigitalValue(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions, value: any) {
 | 
						|
  if (options.hideValue) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  const {valueY, baseX, width, fontSizeFactor, fontValueBaseline} =
 | 
						|
    context.barDimensions;
 | 
						|
 | 
						|
  const textX = Math.round(baseX + width / 2);
 | 
						|
  const textY = valueY;
 | 
						|
 | 
						|
  let text = options.valueText || padValue(value, options.valueDec);
 | 
						|
  text += options.symbol;
 | 
						|
 | 
						|
  context.save();
 | 
						|
  context.textAlign = 'center';
 | 
						|
  context.textBaseline = fontValueBaseline;
 | 
						|
  context.font = Drawings.font(options, 'Value', fontSizeFactor);
 | 
						|
  context.lineWidth = 0;
 | 
						|
  drawText(context, options, 'Value', text, textX, textY);
 | 
						|
  context.restore();
 | 
						|
}
 | 
						|
 | 
						|
function drawArcGlow(context: DigitalGaugeCanvasRenderingContext2D,
 | 
						|
                     Cx: number, Cy: number, Ri: number, Rm: number, Ro: number,
 | 
						|
                     color: string, progress: number, isDonut: boolean,
 | 
						|
                     donutStartAngle?: number, donutEndAngle?: number) {
 | 
						|
  context.setLineDash([]);
 | 
						|
  const strokeWidth = Ro - Ri;
 | 
						|
  const blur = 0.55;
 | 
						|
  const edge = strokeWidth * blur;
 | 
						|
  context.lineWidth = strokeWidth + edge;
 | 
						|
  const stop = blur / (2 * blur + 2);
 | 
						|
  const glowGradient = context.createRadialGradient(Cx, Cy, Ri - edge / 2, Cx, Cy, Ro + edge / 2);
 | 
						|
  const color1 = tinycolor(color).setAlpha(0.5).toRgbString();
 | 
						|
  const color2 = tinycolor(color).setAlpha(0).toRgbString();
 | 
						|
  glowGradient.addColorStop(0, color2);
 | 
						|
  glowGradient.addColorStop(stop, color1);
 | 
						|
  glowGradient.addColorStop(1.0 - stop, color1);
 | 
						|
  glowGradient.addColorStop(1, color2);
 | 
						|
  context.strokeStyle = glowGradient;
 | 
						|
  context.beginPath();
 | 
						|
  const e = 0.01 * Math.PI;
 | 
						|
  if (isDonut) {
 | 
						|
    context.arc(Cx, Cy, Rm, donutStartAngle - e, donutStartAngle +
 | 
						|
      (donutEndAngle - donutStartAngle) * progress + e);
 | 
						|
  } else {
 | 
						|
    context.arc(Cx, Cy, Rm, Math.PI - e, Math.PI + Math.PI * progress + e);
 | 
						|
  }
 | 
						|
  context.stroke();
 | 
						|
}
 | 
						|
 | 
						|
function drawBarGlow(context: DigitalGaugeCanvasRenderingContext2D, startX: number, startY: number,
 | 
						|
                     endX: number, endY: number, color: string, strokeWidth: number, isVertical: boolean) {
 | 
						|
  context.setLineDash([]);
 | 
						|
  const blur = 0.55;
 | 
						|
  const edge = strokeWidth * blur;
 | 
						|
  context.lineWidth = strokeWidth + edge;
 | 
						|
  const stop = blur / (2 * blur + 2);
 | 
						|
  const gradientStartX = isVertical ? startX - context.lineWidth / 2 : 0;
 | 
						|
  const gradientStartY = isVertical ? 0 : startY - context.lineWidth / 2;
 | 
						|
  const gradientStopX = isVertical ? startX + context.lineWidth / 2 : 0;
 | 
						|
  const gradientStopY = isVertical ? 0 : startY + context.lineWidth / 2;
 | 
						|
 | 
						|
  const glowGradient = context.createLinearGradient(gradientStartX, gradientStartY, gradientStopX, gradientStopY);
 | 
						|
  const color1 = tinycolor(color).setAlpha(0.5).toRgbString();
 | 
						|
  const color2 = tinycolor(color).setAlpha(0).toRgbString();
 | 
						|
  glowGradient.addColorStop(0, color2);
 | 
						|
  glowGradient.addColorStop(stop, color1);
 | 
						|
  glowGradient.addColorStop(1.0 - stop, color1);
 | 
						|
  glowGradient.addColorStop(1, color2);
 | 
						|
  context.strokeStyle = glowGradient;
 | 
						|
  const dx = isVertical ? 0 : 0.05 * context.lineWidth;
 | 
						|
  const dy = isVertical ? 0.05 * context.lineWidth : 0;
 | 
						|
  context.beginPath();
 | 
						|
  context.moveTo(startX - dx, startY + dy);
 | 
						|
  context.lineTo(endX + dx, endY - dy);
 | 
						|
  context.stroke();
 | 
						|
}
 | 
						|
 | 
						|
function drawTickArc(context: DigitalGaugeCanvasRenderingContext2D, tickValues: number[], Cx: number, Cy: number,
 | 
						|
                     Ri: number, Rm: number, Ro: number, startAngle: number, endAngle: number,
 | 
						|
                     color: string, tickWidth: number) {
 | 
						|
  if (!tickValues.length) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  const strokeWidth = Ro - Ri;
 | 
						|
  context.beginPath();
 | 
						|
  context.lineWidth = tickWidth;
 | 
						|
  context.strokeStyle = color;
 | 
						|
  for (const tick of tickValues) {
 | 
						|
    const angle = startAngle + tick * endAngle;
 | 
						|
    const x1 = Cx + (Ri + strokeWidth) * Math.cos(angle);
 | 
						|
    const y1 = Cy + (Ri + strokeWidth) * Math.sin(angle);
 | 
						|
    const x2 = Cx + Ri * Math.cos(angle);
 | 
						|
    const y2 = Cy + Ri * Math.sin(angle);
 | 
						|
    context.moveTo(x1, y1);
 | 
						|
    context.lineTo(x2, y2);
 | 
						|
  }
 | 
						|
  context.stroke();
 | 
						|
}
 | 
						|
 | 
						|
function drawTickBar(context: DigitalGaugeCanvasRenderingContext2D, tickValues: number[], startX: number, startY: number,
 | 
						|
                     distanceBar: number, strokeWidth: number, isVertical: boolean, color: string, tickWidth: number) {
 | 
						|
  if (!tickValues.length) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  context.beginPath();
 | 
						|
  context.lineWidth = tickWidth;
 | 
						|
  context.strokeStyle = color;
 | 
						|
  for (const tick of tickValues) {
 | 
						|
    const tickValue = tick * distanceBar;
 | 
						|
    if (isVertical) {
 | 
						|
      context.moveTo(startX - strokeWidth / 2, startY + tickValue - distanceBar);
 | 
						|
      context.lineTo(startX + strokeWidth / 2, startY + tickValue - distanceBar);
 | 
						|
    } else {
 | 
						|
      context.moveTo(startX + tickValue, startY);
 | 
						|
      context.lineTo(startX + tickValue, startY + strokeWidth);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  context.stroke();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
function drawProgress(context: DigitalGaugeCanvasRenderingContext2D,
 | 
						|
                      options: CanvasDigitalGaugeOptions, progress: number) {
 | 
						|
  let neonColor: string;
 | 
						|
  context.save();
 | 
						|
  options.barColorProcessor.update(options.value);
 | 
						|
  const color = tinycolor(options.barColorProcessor.color);
 | 
						|
  if (options.neonGlowBrightness) {
 | 
						|
    context.currentColor = neonColor = color.brighten(options.neonGlowBrightness).toRgbString();
 | 
						|
  } else {
 | 
						|
    context.currentColor = context.strokeStyle = color.toRgbString();
 | 
						|
  }
 | 
						|
 | 
						|
  const {barLeft, barRight, barTop, baseX, width, barBottom, Cx, Cy, Rm, Ro, Ri, strokeWidth} =
 | 
						|
    context.barDimensions;
 | 
						|
 | 
						|
  if (context.barDimensions.dashLength) {
 | 
						|
    context.setLineDash([context.barDimensions.dashLength]);
 | 
						|
  }
 | 
						|
  context.lineWidth = strokeWidth;
 | 
						|
  if (options.roundedLineCap) {
 | 
						|
    context.lineCap = 'round';
 | 
						|
  } else {
 | 
						|
    context.lineCap = 'butt';
 | 
						|
  }
 | 
						|
  if (options.gaugeType === 'donut') {
 | 
						|
    if (options.neonGlowBrightness) {
 | 
						|
      context.strokeStyle = neonColor;
 | 
						|
    }
 | 
						|
    context.beginPath();
 | 
						|
    context.arc(Cx, Cy, Rm, options.donutStartAngle, options.donutStartAngle +
 | 
						|
      (options.donutEndAngle - options.donutStartAngle) * progress);
 | 
						|
    context.stroke();
 | 
						|
    if (options.neonGlowBrightness && !options.isMobile) {
 | 
						|
      drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true,
 | 
						|
        options.donutStartAngle, options.donutEndAngle);
 | 
						|
    }
 | 
						|
    drawTickArc(context, options.ticksValue, Cx, Cy, Ri, Rm, Ro, options.donutStartAngle,
 | 
						|
      options.donutEndAngle - options.donutStartAngle, options.colorTicks, options.tickWidth);
 | 
						|
  } else if (options.gaugeType === 'arc') {
 | 
						|
    if (options.neonGlowBrightness) {
 | 
						|
      context.strokeStyle = neonColor;
 | 
						|
    }
 | 
						|
    context.beginPath();
 | 
						|
    context.arc(Cx, Cy, Rm, Math.PI, Math.PI + Math.PI * progress);
 | 
						|
    context.stroke();
 | 
						|
    if (options.neonGlowBrightness && !options.isMobile) {
 | 
						|
      drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, false);
 | 
						|
    }
 | 
						|
    drawTickArc(context, options.ticksValue, Cx, Cy, Ri, Rm, Ro, Math.PI, Math.PI, options.colorTicks, options.tickWidth);
 | 
						|
  } else if (options.gaugeType === 'horizontalBar') {
 | 
						|
    if (options.neonGlowBrightness) {
 | 
						|
      context.strokeStyle = neonColor;
 | 
						|
    }
 | 
						|
    context.beginPath();
 | 
						|
    context.moveTo(barLeft, barTop + strokeWidth / 2);
 | 
						|
    context.lineTo(barLeft + (barRight - barLeft) * progress, barTop + strokeWidth / 2);
 | 
						|
    context.stroke();
 | 
						|
    if (options.neonGlowBrightness && !options.isMobile) {
 | 
						|
      drawBarGlow(context, barLeft, barTop + strokeWidth / 2,
 | 
						|
        barLeft + (barRight - barLeft) * progress, barTop + strokeWidth / 2,
 | 
						|
        neonColor, strokeWidth, false);
 | 
						|
    }
 | 
						|
    drawTickBar(context, options.ticksValue, barLeft, barTop, barRight - barLeft, strokeWidth,
 | 
						|
      false, options.colorTicks, options.tickWidth);
 | 
						|
  } else if (options.gaugeType === 'verticalBar') {
 | 
						|
    if (options.neonGlowBrightness) {
 | 
						|
      context.strokeStyle = neonColor;
 | 
						|
    }
 | 
						|
    context.beginPath();
 | 
						|
    context.moveTo(baseX + width / 2, barBottom);
 | 
						|
    context.lineTo(baseX + width / 2, barBottom - (barBottom - barTop) * progress);
 | 
						|
    context.stroke();
 | 
						|
    if (options.neonGlowBrightness && !options.isMobile) {
 | 
						|
      drawBarGlow(context, baseX + width / 2, barBottom,
 | 
						|
        baseX + width / 2, barBottom - (barBottom - barTop) * progress,
 | 
						|
        neonColor, strokeWidth, true);
 | 
						|
    }
 | 
						|
    drawTickBar(context, options.ticksValue, baseX + width / 2, barTop, barTop - barBottom, strokeWidth,
 | 
						|
      true, options.colorTicks, options.tickWidth);
 | 
						|
  }
 | 
						|
 | 
						|
  context.restore();
 | 
						|
}
 |