/* Yocto-Visualization-4web (ES2017 full 2.1.11996) - www.yoctopuce.com */

// obj/full/Renderer/YDataRendererCommon.js
var Vector3 = class _Vector3 {
  constructor(a, b, c) {
    this.a = a;
    this.b = b;
    this.c = c;
  }
  static FromXYCoord(x, y) {
    return new _Vector3(x, y, 1);
  }
  multiplyByM(m) {
    return new _Vector3(m.a * this.a + m.b * this.b + m.c * this.c, m.d * this.a + m.e * this.b + m.f * this.c, m.g * this.a + m.h * this.b + m.i * this.c);
  }
  multiplyByN(n) {
    return new _Vector3(this.a * n, this.b * n, this.c * n);
  }
  AddV(v) {
    return new _Vector3(this.a + v.a, this.b + v.b, this.c + v.c);
  }
  SubstractV(v) {
    return new _Vector3(this.a - v.a, this.b - v.b, this.c - v.c);
  }
  toPointF() {
    return new PointF(this.a, this.b);
  }
  toPoint() {
    return new Point(this.a, this.b);
  }
};
var Matrix3x3 = class _Matrix3x3 {
  // don't use the constructor directly but newMatrix, newTranslateMatrix, newRotateMatrix etc...
  constructor(a, b, c, d, e, f, g, h, i, flags) {
    this.a = a;
    this.b = b;
    this.c = c;
    this.d = d;
    this.e = e;
    this.f = f;
    this.g = g;
    this.h = h;
    this.i = i;
    if (b == 0 && d == 0 && a == 1 && e == 1 && i == 1 && g == 0 && h == 0) {
      flags |= _Matrix3x3.Flag_TRANSLATION;
      if (c == 0 && f == 0)
        flags |= _Matrix3x3.Flag_IDENTITY;
    }
    this.isTranslation = (flags & _Matrix3x3.Flag_TRANSLATION) != 0;
    this.isIdentity = (flags & _Matrix3x3.Flag_IDENTITY) != 0;
  }
  clone() {
    let flag = 0;
    if (this.isTranslation)
      flag |= _Matrix3x3.Flag_TRANSLATION;
    if (this.isIdentity)
      flag |= _Matrix3x3.Flag_IDENTITY;
    return new _Matrix3x3(this.a, this.b, this.c, this.d, this.e, this.f, this.g, this.h, this.i, flag);
  }
  get determinant() {
    let detA = this.e * this.i - this.h * this.f;
    let detB = this.d * this.i - this.g * this.f;
    let detC = this.d * this.h - this.g * this.e;
    return this.a * detA + -this.b * detB + this.c * detC;
  }
  get transpose() {
    if (this.isIdentity)
      return _Matrix3x3.newIdentityMatrix();
    return new _Matrix3x3(this.a, this.d, this.g, this.b, this.e, this.h, this.c, this.f, this.i, _Matrix3x3.Flag_NONE);
  }
  multiplyByV(v) {
    if (this.isTranslation)
      return new Vector3(this.c + v.a, this.f + v.b, 1);
    return new Vector3(this.a * v.a + this.b * v.b + this.c * v.c, this.d * v.a + this.e * v.b + this.f * v.c, this.g * v.a + this.h * v.b + this.i * v.c);
  }
  static newMatrix(a, b, c, d, e, f, g, h, i) {
    return new _Matrix3x3(a, b, c, d, e, f, g, h, i, _Matrix3x3.Flag_NONE);
  }
  static newTranslateMatrix(offsetX, offsetY) {
    let flag = _Matrix3x3.Flag_TRANSLATION;
    if (offsetX == 0 && offsetY == 0)
      flag |= _Matrix3x3.Flag_IDENTITY;
    return new _Matrix3x3(1, 0, offsetX, 0, 1, offsetY, 0, 0, 1, flag);
  }
  static newRotateMatrix(AngleDeg) {
    AngleDeg = Math.PI * AngleDeg / 180;
    return new _Matrix3x3(Math.cos(AngleDeg), -Math.sin(AngleDeg), 0, Math.sin(AngleDeg), -Math.cos(AngleDeg), 0, 0, 0, 1, _Matrix3x3.Flag_NONE);
  }
  static newScaleMatrix(Coef) {
    return new _Matrix3x3(Coef, 0, 0, 0, Coef, 0, 0, 0, 1, _Matrix3x3.Flag_NONE);
  }
  static newIdentityMatrix() {
    return new _Matrix3x3(1, 0, 0, 0, 1, 0, 0, 0, 1, _Matrix3x3.Flag_IDENTITY | _Matrix3x3.Flag_TRANSLATION);
  }
  toCSS() {
    return "matrix(" + this.a.toString() + "," + this.d.toString() + "," + this.b.toString() + "," + this.e.toString() + "," + this.c.toString() + "," + this.f.toString() + ")";
  }
  toString() {
    return "| " + this.a.toFixed(2) + " " + this.b.toFixed(2) + " " + this.c.toFixed(2) + " |" + (this.isIdentity ? " I" : "") + "\n| " + this.d.toFixed(2) + " " + this.e.toFixed(2) + " " + this.f.toFixed(2) + " |" + (this.isTranslation ? " T" : "") + "\n| " + this.g.toFixed(2) + " " + this.g.toFixed(2) + " " + this.i.toFixed(2) + " |\n";
  }
  multiplyByM(m) {
    if (this.isIdentity)
      return m.clone();
    if (m.isIdentity)
      return this.clone();
    if (this.isTranslation && m.isTranslation)
      return _Matrix3x3.newTranslateMatrix(this.c + m.c, this.f + m.f);
    return new _Matrix3x3(this.a * m.a + this.b * m.d + this.c * m.g, this.a * m.b + this.b * m.e + this.c * m.h, this.a * m.c + this.b * m.f + this.c * m.i, this.d * m.a + this.e * m.d + this.f * m.g, this.d * m.b + this.e * m.e + this.f * m.h, this.d * m.c + this.e * m.f + this.f * m.i, this.g * m.a + this.h * m.d + this.i * m.g, this.g * m.b + this.h * m.e + this.i * m.h, this.g * m.c + this.h * m.f + this.i * m.i, _Matrix3x3.Flag_NONE);
  }
  get inverse() {
    if (this.isIdentity)
      return _Matrix3x3.newIdentityMatrix();
    if (this.isTranslation)
      return _Matrix3x3.newTranslateMatrix(-this.c, -this.f);
    let det = this.determinant;
    if (det == 0)
      throw new Error("matrix cannot be inverted");
    let detA = this.e * this.i - this.f * this.h;
    let detB = this.b * this.i - this.c * this.h;
    let detC = this.b * this.f - this.c * this.e;
    let detD = this.d * this.i - this.f * this.g;
    let detE = this.a * this.i - this.c * this.g;
    let detF = this.a * this.f - this.c * this.d;
    let detG = this.d * this.h - this.e * this.g;
    let detH = this.a * this.h - this.b * this.g;
    let detI = this.a * this.e - this.b * this.d;
    return new _Matrix3x3(detA / det, -detB / det, detC / det, -detD / det, detE / det, -detF / det, detG / det, -detH / det, detI / det, _Matrix3x3.Flag_NONE);
  }
  log() {
    console.log(this.toString());
  }
};
Matrix3x3.Flag_NONE = 0;
Matrix3x3.Flag_IDENTITY = 1;
Matrix3x3.Flag_TRANSLATION = 2;
var YEnum = class {
  static fromString(container, value) {
    let p = Object.getOwnPropertyNames(container);
    for (let i = 0; i < p.length; i++) {
      if (p[i] == value)
        return container[value];
    }
    throw new Error("YEnum" + value + " is not a " + container + " value");
  }
  static siblings(container) {
    let res = [];
    let p = Object.getOwnPropertyNames(container);
    for (let i = 0; i < p.length; i++) {
      if (container[p[i]] instanceof YEnumItem) {
        res.push(container[p[i]]);
      }
    }
    return res;
  }
};
var YEnumItem = class {
  constructor(value, humanreadable, container) {
    this._value = value;
    this._container = container;
    this._humanreadable = humanreadable;
  }
  fromString(value) {
    return YEnum.fromString(this._container, value);
  }
  get toString() {
    return this._value.toString();
  }
  get description() {
    return this._humanreadable;
  }
  get sibblings() {
    return YEnum.siblings(this._container);
  }
};
var ViewPortSettings = class {
  constructor() {
    this.IRLx = 0;
    this.IRLy = 0;
    this.zoomx = 0;
    this.zoomy = 0;
    this.Lmargin = 0;
    this.Rmargin = 0;
    this.Tmargin = 0;
    this.Bmargin = 0;
    this.Width = 0;
    this.Height = 0;
    this.Capture = false;
    this.IRLCaptureStartX = 0;
    this.CaptureStartY = 0;
    this.OriginalXAxisMin = 0;
    this.OriginalXAxisMax = 0;
    this.OriginalIRLx = 0;
    this.OriginalLmargin = 0;
    this.OriginalZoomx = 0;
  }
};
var YFont = class {
  get userData() {
    return this._userData;
  }
  set userData(value) {
    this._userData = value;
  }
  get directParent() {
    return this._directParent;
  }
  constructor(parentRenderer, directParent, size, fontChangeCallback) {
    this._userData = null;
    this._fontChangeCallback = null;
    this._name = "Arial";
    this._italic = false;
    this._bold = false;
    this._color = YColor.Black;
    this._alternateColor = null;
    this._font = null;
    this._brush = new YSolidBrush(YColor.Black);
    this._parentRenderer = parentRenderer;
    this._directParent = directParent;
    this._fontChangeCallback = fontChangeCallback ? fontChangeCallback : null;
    this._size = new Proportional(size ? size : 10, Proportional.ResizeRule.FIXED, parentRenderer, this, this.ResetFont);
  }
  ResetFont(source) {
    this._font = null;
    if (source != null)
      this._parentRenderer.ProportionnalValueChanged(source);
  }
  get name() {
    return this._name;
  }
  set name(value) {
    this._name = value;
    this.ResetFont(null);
    this._parentRenderer.redraw();
  }
  get hasChanged() {
    return this._font == null;
  }
  get size() {
    return this._size.value;
  }
  set size(value) {
    if (value <= 0)
      throw new RangeError("Size must be a positive value");
    value = Math.round(100 * value) / 100;
    this._size.value = value;
    this.ResetFont(null);
    if (this._fontChangeCallback != null)
      this._fontChangeCallback(this);
    this._parentRenderer.redraw();
  }
  get italic() {
    return this._italic;
  }
  set italic(value) {
    if (this._italic != value) {
      this._italic = value;
      this.ResetFont(null);
      this._parentRenderer.redraw();
    }
  }
  get bold() {
    return this._bold;
  }
  set bold(value) {
    if (this._bold != value) {
      this._bold = value;
      this.ResetFont(null);
      this._parentRenderer.redraw();
    }
  }
  get color() {
    return this._color;
  }
  set color(value) {
    if (this._color != value) {
      this._color = value;
      this._brush = null;
      this._parentRenderer.redraw();
    }
  }
  get alternateColor() {
    return this._alternateColor;
  }
  set alternateColor(value) {
    if (this._alternateColor != value) {
      this._alternateColor = value;
      this._brush = null;
      this._parentRenderer.redraw();
    }
  }
  get fontObject() {
    return this._name ? this._name : "Arial";
  }
  get brush() {
    if (this._brush == null)
      this._brush = new YSolidBrush(this._alternateColor != null ? this._alternateColor : this._color);
    return this._brush;
  }
  get sizeInPoints() {
    return this._size.value * 0.75;
  }
  get sizeForCanvas() {
    return this._size.value * 1.15;
  }
  get htmlCode() {
    return (this._italic ? "italic " : "") + (this._bold ? "bold " : "") + this.sizeForCanvas.toString() + "px " + this._name;
  }
};
var YSizeF = class {
  constructor(font, st) {
    this._w = 0;
    this._h = 0;
    this._lines = [];
    this._linesCount = 0;
    this._lineHeight = 0;
    this._firstlineHeight = 0;
    if (font != null) {
      this._lineHeight = font.size * 1.25;
      this._firstlineHeight = this._lineHeight * 0.75;
      if (st.indexOf("\n") < 0) {
        this._lines = [st];
        this._linesCount = 1;
      } else {
        this._lines = st.split("\n");
        this._linesCount = this._lines.length;
      }
    }
  }
  get lines() {
    return this._lines;
  }
  get linesCount() {
    return this._linesCount;
  }
  get firstLineHeight() {
    return this._firstlineHeight;
  }
  get lineHeight() {
    return this._lineHeight;
  }
  get height() {
    return this._h;
  }
  get width() {
    return this._w;
  }
  set width(value) {
    this._w = value;
  }
  set height(value) {
    this._h = value;
  }
};
var YTextRenderingHint = class {
  constructor(value) {
    this._value = 0;
    this._value = value;
  }
};
YTextRenderingHint.SystemDefault = new YTextRenderingHint(0);
YTextRenderingHint.SingleBitPerPixelGridFit = new YTextRenderingHint(1);
YTextRenderingHint.SingleBitPerPixel = new YTextRenderingHint(2);
YTextRenderingHint.AntiAliasGridFit = new YTextRenderingHint(3);
YTextRenderingHint.AntiAlias = new YTextRenderingHint(4);
YTextRenderingHint.ClearTypeGridFit = new YTextRenderingHint(5);
var YSmoothingMode = class {
  constructor(value) {
    this._value = 0;
    this._value = value;
  }
};
YSmoothingMode.Invalid = new YSmoothingMode(-1);
YSmoothingMode.Default = new YSmoothingMode(0);
YSmoothingMode.HighSpeed = new YSmoothingMode(1);
YSmoothingMode.HighQuality = new YSmoothingMode(2);
YSmoothingMode.None = new YSmoothingMode(3);
YSmoothingMode.AntiAlias = new YSmoothingMode(4);
var YStringBuilder = class {
  constructor() {
    this._str = "";
  }
  AppendLine(s) {
    this._str += s + "\n";
  }
  Append(s) {
    this._str += s;
  }
  get contents() {
    return this._str;
  }
};
var YStringFormat = class {
  get Alignment() {
    return this._Alignment;
  }
  set Alignment(value) {
    this._Alignment = value;
  }
  get LineAlignment() {
    return this._LineAlignment;
  }
  set LineAlignment(value) {
    this._LineAlignment = value;
  }
  get FormatFlags() {
    return this._formatFlags;
  }
  set FormatFlags(value) {
    this._formatFlags = value;
  }
  get Trimming() {
    return this._Trimming;
  }
  set Trimming(value) {
    this._Trimming = value;
  }
  constructor(clip) {
    this._Alignment = 0;
    this._LineAlignment = 0;
    this._formatFlags = 0;
    this._Trimming = 0;
    this._clip = 16384;
    this._clip = clip;
  }
};
var YColor = class _YColor {
  get name() {
    return this._name;
  }
  set predefname(value) {
    this._name = value;
  }
  static get predefinedColors() {
    if (_YColor._predefinedColors == null) {
      _YColor._predefinedColors = {};
      let names = Object.getOwnPropertyNames(_YColor);
      for (let i = 0; i < names.length; i++) {
        if (_YColor[names[i]] instanceof _YColor) {
          _YColor._predefinedColors[names[i]] = _YColor[names[i]];
          _YColor._predefinedColors[names[i]].predefname = names[i];
        }
      }
    }
    return _YColor._predefinedColors;
  }
  static FromString(value) {
    let valueUpper = value.toUpperCase();
    let propNames = Object.getOwnPropertyNames(_YColor);
    for (let i = 0; i < propNames.length; i++) {
      if (propNames[i].toUpperCase() == valueUpper) {
        if (_YColor[propNames[i]] instanceof _YColor) {
          return _YColor[propNames[i]];
        }
      }
    }
    if (value.length == 7 && value.substr(0, 1).toUpperCase() == "#") {
      let r = parseInt(value.substr(1, 2), 16);
      let g = parseInt(value.substr(3, 2), 16);
      let b = parseInt(value.substr(5, 2), 16);
      return new _YColor(false, 255, r, g, b);
    }
    if (value.length == 12) {
      if (value.substr(0, 4).toUpperCase() == "RGB:") {
        let alpha = parseInt(value.substr(4, 2), 16);
        let r = parseInt(value.substr(6, 2), 16);
        let g = parseInt(value.substr(8, 2), 16);
        let b = parseInt(value.substr(10, 2), 16);
        return new _YColor(false, alpha, r, g, b);
      } else if (value.substr(0, 4).toUpperCase() == "HSL:") {
        let alpha = parseInt(value.substr(4, 2), 16);
        let h = parseInt(value.substr(6, 2), 16);
        let s = parseInt(value.substr(8, 2), 16);
        let l = parseInt(value.substr(10, 2), 16);
        return new _YColor(true, alpha, h, s, l);
      }
    }
    return null;
  }
  static hex(v) {
    let s = v.toString(16);
    if (s.length <= 1)
      return "0" + s;
    return s;
  }
  toString() {
    if (this.isHSLColor) {
      return "HSL:" + (_YColor.hex(this.transparency) + _YColor.hex(this.h) + _YColor.hex(this.s) + _YColor.hex(this.l)).toUpperCase();
    } else {
      let propNames = Object.getOwnPropertyNames(_YColor);
      for (let i = 0; i < propNames.length; i++) {
        let o = Reflect.get(_YColor, propNames[i]);
        let c = o;
        if (c.alpha == this.alpha && c.red == this.red && c.green == this.green && c.blue == this.blue)
          return propNames[i];
      }
    }
    return "RGB:" + (_YColor.hex(this.transparency) + _YColor.hex(this.r) + _YColor.hex(this.g) + _YColor.hex(this.b)).toUpperCase();
  }
  get svgCode() {
    return "rgb(" + this.r.toString() + ", " + this.g.toString() + ", " + this.b.toString() + ")";
  }
  get alphaCode() {
    return (this.transparency / 255).toFixed(3);
  }
  static hsl2rgbInt(temp1, temp2, temp3) {
    if (temp3 >= 170)
      return (temp1 + 127) / 255 >> 0;
    if (temp3 > 42) {
      if (temp3 <= 127)
        return (temp2 + 127) / 255 >> 0;
      temp3 = 170 - temp3;
    }
    return (temp1 * 255 + (temp2 - temp1) * (6 * temp3) + 32512) / 65025 >> 0;
  }
  hsl2rgb() {
    let temp1;
    let temp2;
    let temp3;
    this.rgbConvertionDone = true;
    if (this.s == 0) {
      this.r = this.l;
      this.g = this.l;
      this.b = this.l;
      return;
    }
    if (this.l <= 127) {
      temp2 = this.l * (255 + this.s);
    } else {
      temp2 = (this.l + this.s) * 255 - this.l * this.s;
    }
    temp1 = 510 * this.l - temp2;
    temp3 = this.h + 85;
    if (temp3 > 255)
      temp3 = temp3 - 255;
    this.r = _YColor.hsl2rgbInt(temp1, temp2, temp3);
    temp3 = this.h;
    if (temp3 > 255)
      temp3 = temp3 - 255;
    this.g = _YColor.hsl2rgbInt(temp1, temp2, temp3);
    if (this.h >= 85) {
      temp3 = this.h - 85;
    } else {
      temp3 = this.h + 170;
    }
    this.b = _YColor.hsl2rgbInt(temp1, temp2, temp3);
    if (this.r > 255)
      this.r = 255;
    if (this.g > 255)
      this.g = 255;
    if (this.b > 255)
      this.b = 255;
  }
  computeHSL() {
    let R = this.r;
    let G = this.g;
    let B = this.b;
    let H;
    let S;
    let L;
    let max = R > G ? R : G;
    let min = R < G ? R : G;
    let correction = 0;
    let divisor = 0;
    this.hslConvertionDone = true;
    if (B > max)
      max = B;
    if (B < min)
      min = B;
    L = (max + min + 1) / 2 >> 0;
    if (max == min) {
      this.h = 0;
      this.s = 0;
      this.l = L;
      return;
    }
    correction = (max + min) / 2 >> 0;
    if (L <= 127) {
      S = (255 * (max - min) + correction) / (max + min) >> 0;
    } else {
      S = (255 * (max - min) + 255 - correction) / (510 - (max + min)) >> 0;
    }
    correction = 3 * (max - min);
    divisor = 2 * correction;
    if (R == max) {
      H = 0;
      R = G;
      G = B;
    } else if (G == max) {
      H = 85;
      G = R;
      R = B;
    } else {
      H = 170;
    }
    if (R >= G) {
      H += (255 * (R - G) + correction) / divisor >> 0;
    } else {
      H += 255 - (255 * (G - R) - correction) / divisor >> 0;
    }
    if (H > 255)
      H -= 255;
    if (S > 255)
      S = 255;
    if (L > 255)
      L = 255;
    this.h = H;
    this.s = S;
    this.l = L;
  }
  get hue() {
    if (!this.hslConvertionDone)
      this.computeHSL();
    return this.h;
  }
  get saturation() {
    if (!this.hslConvertionDone)
      this.computeHSL();
    return this.s;
  }
  get luminosity() {
    if (!this.hslConvertionDone)
      this.computeHSL();
    return this.l;
  }
  get red() {
    if (!this.rgbConvertionDone)
      this.hsl2rgb();
    return this.r;
  }
  get green() {
    if (!this.rgbConvertionDone)
      this.hsl2rgb();
    return this.g;
  }
  get blue() {
    if (!this.rgbConvertionDone)
      this.hsl2rgb();
    return this.b;
  }
  get alpha() {
    return this.transparency;
  }
  static FromArgb(a, r, g, b) {
    return new _YColor(false, a, r, g, b);
  }
  static FromAhsl(a, h, s, l) {
    return new _YColor(true, a, h, s, l);
  }
  get isHSL() {
    return this.isHSLColor;
  }
  get isRGB() {
    return !this.isHSLColor;
  }
  equal(c) {
    if (this.isHSLColor) {
      if (!c.isHSLColor)
        return false;
      if (c.hue != this.hue)
        return false;
      if (c.saturation != this.saturation)
        return false;
      if (c.luminosity != this.luminosity)
        return false;
      if (c.alpha != this.alpha)
        return false;
    } else {
      if (c.isHSLColor)
        return false;
      if (c.red != this.red)
        return false;
      if (c.green != this.green)
        return false;
      if (c.blue != this.blue)
        return false;
      if (c.alpha != this.alpha)
        return false;
    }
    return true;
  }
  clone() {
    if (this.isHSLColor)
      return new _YColor(true, this.transparency, this.h, this.s, this.l, this.isPredefined);
    return new _YColor(false, this.transparency, this.r, this.g, this.b, this.isPredefined);
  }
  get isPredefined() {
    return this._isPredefined;
  }
  constructor(isHsl, transparency, r_h, g_s, b_l, isPredefined) {
    this.hslConvertionDone = false;
    this.rgbConvertionDone = false;
    this.transparency = 0;
    this.r = 0;
    this.g = 0;
    this.b = 0;
    this.h = 0;
    this.s = 0;
    this.l = 0;
    this._name = "";
    this._htmlcode = this.computeHTMLCode();
    this.hslConvertionDone = isHsl;
    this.isHSLColor = isHsl;
    this.transparency = transparency;
    this._isPredefined = isPredefined === true;
    if (isHsl) {
      this.h = r_h;
      this.s = g_s;
      this.l = b_l;
      this.hsl2rgb();
    } else {
      this.r = r_h;
      this.g = g_s;
      this.b = b_l;
      this.rgbConvertionDone = true;
    }
    this._htmlcode = this.computeHTMLCode();
  }
  computeHTMLCode() {
    let a = this.transparency / 255;
    let r = this.r;
    let g = this.g;
    let b = this.b;
    return "rgba(" + r + "," + g + "," + b + "," + a.toFixed(3) + ")";
  }
  get htmlCode() {
    return this._htmlcode;
  }
};
YColor.AliceBlue = new YColor(false, 255, 240, 248, 255, true);
YColor.AntiqueWhite = new YColor(false, 255, 250, 235, 215, true);
YColor.Aqua = new YColor(false, 255, 0, 255, 255, true);
YColor.Aquamarine = new YColor(false, 255, 127, 255, 212, true);
YColor.Azure = new YColor(false, 255, 240, 255, 255, true);
YColor.Beige = new YColor(false, 255, 245, 245, 220, true);
YColor.Bisque = new YColor(false, 255, 255, 228, 196, true);
YColor.Black = new YColor(false, 255, 0, 0, 0, true);
YColor.BlanchedAlmond = new YColor(false, 255, 255, 235, 205, true);
YColor.Blue = new YColor(false, 255, 0, 0, 255, true);
YColor.BlueViolet = new YColor(false, 255, 138, 43, 226, true);
YColor.Brown = new YColor(false, 255, 165, 42, 42, true);
YColor.BurlyWood = new YColor(false, 255, 222, 184, 135, true);
YColor.CadetBlue = new YColor(false, 255, 95, 158, 160, true);
YColor.Chartreuse = new YColor(false, 255, 127, 255, 0, true);
YColor.Chocolate = new YColor(false, 255, 210, 105, 30, true);
YColor.Coral = new YColor(false, 255, 255, 127, 80, true);
YColor.CornflowerBlue = new YColor(false, 255, 100, 149, 237, true);
YColor.Cornsilk = new YColor(false, 255, 255, 248, 220, true);
YColor.Crimson = new YColor(false, 255, 220, 20, 60, true);
YColor.Cyan = new YColor(false, 255, 0, 255, 255, true);
YColor.DarkBlue = new YColor(false, 255, 0, 0, 139, true);
YColor.DarkCyan = new YColor(false, 255, 0, 139, 139, true);
YColor.DarkGoldenrod = new YColor(false, 255, 184, 134, 11, true);
YColor.DarkGray = new YColor(false, 255, 169, 169, 169, true);
YColor.DarkGreen = new YColor(false, 255, 0, 100, 0, true);
YColor.DarkKhaki = new YColor(false, 255, 189, 183, 107, true);
YColor.DarkMagenta = new YColor(false, 255, 139, 0, 139, true);
YColor.DarkOliveGreen = new YColor(false, 255, 85, 107, 47, true);
YColor.DarkOrange = new YColor(false, 255, 255, 140, 0, true);
YColor.DarkOrchid = new YColor(false, 255, 153, 50, 204, true);
YColor.DarkRed = new YColor(false, 255, 139, 0, 0, true);
YColor.DarkSalmon = new YColor(false, 255, 233, 150, 122, true);
YColor.DarkSeaGreen = new YColor(false, 255, 143, 188, 143, true);
YColor.DarkSlateBlue = new YColor(false, 255, 72, 61, 139, true);
YColor.DarkSlateGray = new YColor(false, 255, 47, 79, 79, true);
YColor.DarkTurquoise = new YColor(false, 255, 0, 206, 209, true);
YColor.DarkViolet = new YColor(false, 255, 148, 0, 211, true);
YColor.DeepPink = new YColor(false, 255, 255, 20, 147, true);
YColor.DeepSkyBlue = new YColor(false, 255, 0, 191, 255, true);
YColor.DimGray = new YColor(false, 255, 105, 105, 105, true);
YColor.DodgerBlue = new YColor(false, 255, 30, 144, 255, true);
YColor.Firebrick = new YColor(false, 255, 178, 34, 34, true);
YColor.FloralWhite = new YColor(false, 255, 255, 250, 240, true);
YColor.ForestGreen = new YColor(false, 255, 34, 139, 34, true);
YColor.Fuchsia = new YColor(false, 255, 255, 0, 255, true);
YColor.Gainsboro = new YColor(false, 255, 220, 220, 220, true);
YColor.GhostWhite = new YColor(false, 255, 248, 248, 255, true);
YColor.Gold = new YColor(false, 255, 255, 215, 0, true);
YColor.Goldenrod = new YColor(false, 255, 218, 165, 32, true);
YColor.Gray = new YColor(false, 255, 128, 128, 128, true);
YColor.Green = new YColor(false, 255, 0, 128, 0, true);
YColor.GreenYellow = new YColor(false, 255, 173, 255, 47, true);
YColor.Honeydew = new YColor(false, 255, 240, 255, 240, true);
YColor.HotPink = new YColor(false, 255, 255, 105, 180, true);
YColor.IndianRed = new YColor(false, 255, 205, 92, 92, true);
YColor.Indigo = new YColor(false, 255, 75, 0, 130, true);
YColor.Ivory = new YColor(false, 255, 255, 255, 240, true);
YColor.Khaki = new YColor(false, 255, 240, 230, 140, true);
YColor.Lavender = new YColor(false, 255, 230, 230, 250, true);
YColor.LavenderBlush = new YColor(false, 255, 255, 240, 245, true);
YColor.LawnGreen = new YColor(false, 255, 124, 252, 0, true);
YColor.LemonChiffon = new YColor(false, 255, 255, 250, 205, true);
YColor.LightBlue = new YColor(false, 255, 173, 216, 230, true);
YColor.LightCoral = new YColor(false, 255, 240, 128, 128, true);
YColor.LightCyan = new YColor(false, 255, 224, 255, 255, true);
YColor.LightGoldenrodYellow = new YColor(false, 255, 250, 250, 210, true);
YColor.LightGray = new YColor(false, 255, 211, 211, 211, true);
YColor.LightGreen = new YColor(false, 255, 144, 238, 144, true);
YColor.LightPink = new YColor(false, 255, 255, 182, 193, true);
YColor.LightSalmon = new YColor(false, 255, 255, 160, 122, true);
YColor.LightSeaGreen = new YColor(false, 255, 32, 178, 170, true);
YColor.LightSkyBlue = new YColor(false, 255, 135, 206, 250, true);
YColor.LightSlateGray = new YColor(false, 255, 119, 136, 153, true);
YColor.LightSteelBlue = new YColor(false, 255, 176, 196, 222, true);
YColor.LightYellow = new YColor(false, 255, 255, 255, 224, true);
YColor.Lime = new YColor(false, 255, 0, 255, 0, true);
YColor.LimeGreen = new YColor(false, 255, 50, 205, 50, true);
YColor.Linen = new YColor(false, 255, 250, 240, 230, true);
YColor.Magenta = new YColor(false, 255, 255, 0, 255, true);
YColor.Maroon = new YColor(false, 255, 128, 0, 0, true);
YColor.MediumAquamarine = new YColor(false, 255, 102, 205, 170, true);
YColor.MediumBlue = new YColor(false, 255, 0, 0, 205, true);
YColor.MediumOrchid = new YColor(false, 255, 186, 85, 211, true);
YColor.MediumPurple = new YColor(false, 255, 147, 112, 219, true);
YColor.MediumSeaGreen = new YColor(false, 255, 60, 179, 113, true);
YColor.MediumSlateBlue = new YColor(false, 255, 123, 104, 238, true);
YColor.MediumSpringGreen = new YColor(false, 255, 0, 250, 154, true);
YColor.MediumTurquoise = new YColor(false, 255, 72, 209, 204, true);
YColor.MediumVioletRed = new YColor(false, 255, 199, 21, 133, true);
YColor.MidnightBlue = new YColor(false, 255, 25, 25, 112, true);
YColor.MintCream = new YColor(false, 255, 245, 255, 250, true);
YColor.MistyRose = new YColor(false, 255, 255, 228, 225, true);
YColor.Moccasin = new YColor(false, 255, 255, 228, 181, true);
YColor.NavajoWhite = new YColor(false, 255, 255, 222, 173, true);
YColor.Navy = new YColor(false, 255, 0, 0, 128, true);
YColor.OldLace = new YColor(false, 255, 253, 245, 230, true);
YColor.Olive = new YColor(false, 255, 128, 128, 0, true);
YColor.OliveDrab = new YColor(false, 255, 107, 142, 35, true);
YColor.Orange = new YColor(false, 255, 255, 165, 0, true);
YColor.OrangeRed = new YColor(false, 255, 255, 69, 0, true);
YColor.Orchid = new YColor(false, 255, 218, 112, 214, true);
YColor.PaleGoldenrod = new YColor(false, 255, 238, 232, 170, true);
YColor.PaleGreen = new YColor(false, 255, 152, 251, 152, true);
YColor.PaleTurquoise = new YColor(false, 255, 175, 238, 238, true);
YColor.PaleVioletRed = new YColor(false, 255, 219, 112, 147, true);
YColor.PapayaWhip = new YColor(false, 255, 255, 239, 213, true);
YColor.PeachPuff = new YColor(false, 255, 255, 218, 185, true);
YColor.Peru = new YColor(false, 255, 205, 133, 63, true);
YColor.Pink = new YColor(false, 255, 255, 192, 203, true);
YColor.Plum = new YColor(false, 255, 221, 160, 221, true);
YColor.PowderBlue = new YColor(false, 255, 176, 224, 230, true);
YColor.Purple = new YColor(false, 255, 128, 0, 128, true);
YColor.Red = new YColor(false, 255, 255, 0, 0, true);
YColor.RosyBrown = new YColor(false, 255, 188, 143, 143, true);
YColor.RoyalBlue = new YColor(false, 255, 65, 105, 225, true);
YColor.SaddleBrown = new YColor(false, 255, 139, 69, 19, true);
YColor.Salmon = new YColor(false, 255, 250, 128, 114, true);
YColor.SandyBrown = new YColor(false, 255, 244, 164, 96, true);
YColor.SeaGreen = new YColor(false, 255, 46, 139, 87, true);
YColor.SeaShell = new YColor(false, 255, 255, 245, 238, true);
YColor.Sienna = new YColor(false, 255, 160, 82, 45, true);
YColor.Silver = new YColor(false, 255, 192, 192, 192, true);
YColor.SkyBlue = new YColor(false, 255, 135, 206, 235, true);
YColor.SlateBlue = new YColor(false, 255, 106, 90, 205, true);
YColor.SlateGray = new YColor(false, 255, 112, 128, 144, true);
YColor.Snow = new YColor(false, 255, 255, 250, 250, true);
YColor.SpringGreen = new YColor(false, 255, 0, 255, 127, true);
YColor.SteelBlue = new YColor(false, 255, 70, 130, 180, true);
YColor.Tan = new YColor(false, 255, 210, 180, 140, true);
YColor.Teal = new YColor(false, 255, 0, 128, 128, true);
YColor.Thistle = new YColor(false, 255, 216, 191, 216, true);
YColor.Tomato = new YColor(false, 255, 255, 99, 71, true);
YColor.Transparent = new YColor(false, 0, 255, 255, 255, true);
YColor.Turquoise = new YColor(false, 255, 64, 224, 208, true);
YColor.Violet = new YColor(false, 255, 238, 130, 238, true);
YColor.Wheat = new YColor(false, 255, 245, 222, 179, true);
YColor.White = new YColor(false, 255, 255, 255, 255, true);
YColor.WhiteSmoke = new YColor(false, 255, 245, 245, 245, true);
YColor.Yellow = new YColor(false, 255, 255, 255, 0, true);
YColor.YellowGreen = new YColor(false, 255, 154, 205, 50, true);
YColor._predefinedColors = null;
var YBrush = class {
  constructor(c, disableAntialias) {
    this._noAntiAlias = false;
    this._color = c;
    if (typeof disableAntialias != "undefined") {
      this._noAntiAlias = disableAntialias;
    }
  }
  get noAntiAlias() {
    return this._noAntiAlias;
  }
  get color() {
    return this._color;
  }
};
var YSolidBrush = class extends YBrush {
};
var YLinearGradientBrush = class extends YBrush {
  constructor(c1, c2) {
    super(c1);
    this._color1 = c1;
    this._color2 = c2;
  }
  get color1() {
    return this._color1;
  }
  get color2() {
    return this._color2;
  }
};
var YPen = class _YPen {
  constructor(color, thickness, disableAntialias) {
    this._thickness = 1;
    this._color = YColor.Black;
    this._noAntiAlias = false;
    this._startCap = 1;
    this._endCap = 1;
    this._linejoin = _YPen.LineJoin.Miter;
    this._thickness = thickness;
    this._color = thickness > 0 ? color : YColor.Transparent;
    if (typeof disableAntialias != "undefined") {
      this._noAntiAlias = disableAntialias;
    }
  }
  get noAntiAlias() {
    return this._noAntiAlias;
  }
  get lineWidth() {
    return this._thickness;
  }
  get strokeStyle() {
    return this._color.htmlCode;
  }
  get color() {
    return this._color;
  }
  set startCap(value) {
    this._startCap = value;
  }
  set endCap(value) {
    this._endCap = value;
  }
  set linejoin(value) {
    this._linejoin = value;
  }
};
(function(YPen2) {
  let LineJoin;
  (function(LineJoin2) {
    LineJoin2[LineJoin2["Miter"] = 0] = "Miter";
    LineJoin2[LineJoin2["Bevel"] = 1] = "Bevel";
    LineJoin2[LineJoin2["Round"] = 2] = "Round";
    LineJoin2[LineJoin2["MiterClipped"] = 3] = "MiterClipped";
  })(LineJoin = YPen2.LineJoin || (YPen2.LineJoin = {}));
})(YPen || (YPen = {}));
var YRectangle = class {
  constructor(x, y, w, h) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
  }
};
var Point = class {
  constructor(valueX, valueY) {
    this.X = valueX >> 0;
    this.Y = valueY >> 0;
  }
};
var minMaxPoint = class {
  constructor(valueX1, valueYMIN, valueX2, valueYMAX) {
    this.X1 = valueX1 >> 0;
    this.YMIN = valueYMIN >> 0;
    this.X2 = valueX2 >> 0;
    this.YMAX = valueYMAX >> 0;
  }
};
var PointF = class {
  constructor(valueX, valueY) {
    this.X = valueX;
    this.Y = valueY;
  }
};
var captureParametersSet = class {
  constructor() {
    this.captureType = YDataRenderer.CaptureType.SVG;
    this.captureFolder = "/";
    this.captureWidth = 1024;
    this.captureHeight = 1024;
    this.captureDPI = 96;
  }
};
var GenericPanel = class _GenericPanel {
  get userData() {
    return this._userData;
  }
  set userData(value) {
    this._userData = value;
  }
  get directParent() {
    return this._directParent;
  }
  constructor(parent, directParent) {
    this._userData = null;
    this._enabled = false;
    this._panelTextAlign = _GenericPanel.TextAlign.LEFT;
    this._text = "";
    this._bgColor = new YColor(false, 255, 255, 255, 192);
    this._borderColor = YColor.Black;
    this._borderthickness = 1;
    this._padding = 10;
    this._verticalMargin = 10;
    this._horizontalMargin = 10;
    this._bgBrush = null;
    this._pen = null;
    this._font = null;
    this._directParent = directParent;
    this._parentRenderer = parent;
    this._font = new YFont(parent, this, 8, null);
  }
  get enabled() {
    return this._enabled;
  }
  set enabled(value) {
    if (this._enabled != value) {
      this._enabled = value;
      this._parentRenderer.clearCachedObjects();
      this._parentRenderer.redraw();
    }
  }
  get panelTextAlign() {
    return this._panelTextAlign;
  }
  set panelTextAlign(value) {
    this._panelTextAlign = value;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get text() {
    return this._text;
  }
  set text(value) {
    this._text = value;
    this._parentRenderer.clearCachedObjects();
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get bgColor() {
    return this._bgColor;
  }
  set bgColor(value) {
    this._bgColor = value;
    this._bgBrush = null;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get borderColor() {
    return this._borderColor;
  }
  set borderColor(value) {
    this._borderColor = value;
    this._pen = null;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get borderthickness() {
    return this._borderthickness;
  }
  set borderthickness(value) {
    if (value < 0)
      throw new Error("Border thickness must be a positive value");
    this._borderthickness = value;
    this._parentRenderer.clearCachedObjects();
    this._pen = null;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get padding() {
    return this._padding;
  }
  set padding(value) {
    if (value < 0)
      throw new RangeError("Padding must be a positive value");
    this._padding = value;
    this._parentRenderer.clearCachedObjects();
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get verticalMargin() {
    return this._verticalMargin;
  }
  set verticalMargin(value) {
    this._verticalMargin = value;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get horizontalMargin() {
    return this._horizontalMargin;
  }
  set horizontalMargin(value) {
    this._horizontalMargin = value;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get bgBrush() {
    if (this._bgBrush == null)
      this._bgBrush = new YSolidBrush(this._bgColor);
    return this._bgBrush;
  }
  get pen() {
    if (this._pen == null) {
      this._pen = new YPen(this._borderColor, this._borderthickness, true);
    }
    return this._pen;
  }
  get font() {
    return this._font;
  }
};
(function(GenericPanel2) {
  class HorizontalAlignPosEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, HorizontalAlignPos);
    }
  }
  GenericPanel2.HorizontalAlignPosEnumItem = HorizontalAlignPosEnumItem;
  class HorizontalAlignPos extends YEnum {
  }
  HorizontalAlignPos.LEFT = new HorizontalAlignPosEnumItem("LEFT", "Left");
  HorizontalAlignPos.CENTER = new HorizontalAlignPosEnumItem("CENTER", "Center");
  HorizontalAlignPos.RIGHT = new HorizontalAlignPosEnumItem("RIGHT", "Right");
  GenericPanel2.HorizontalAlignPos = HorizontalAlignPos;
  class VerticalAlignPosEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, VerticalAlignPos);
    }
  }
  GenericPanel2.VerticalAlignPosEnumItem = VerticalAlignPosEnumItem;
  class VerticalAlignPos extends YEnum {
  }
  VerticalAlignPos.TOP = new VerticalAlignPosEnumItem("TOP", "Top");
  VerticalAlignPos.CENTER = new VerticalAlignPosEnumItem("CENTER", "Center");
  VerticalAlignPos.BOTTOM = new VerticalAlignPosEnumItem("BOTTOM", "Bottom");
  GenericPanel2.VerticalAlignPos = VerticalAlignPos;
  class TextAlignEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, TextAlign);
    }
  }
  GenericPanel2.TextAlignEnumItem = TextAlignEnumItem;
  class TextAlign extends YEnum {
  }
  TextAlign.LEFT = new TextAlignEnumItem("LEFT", "Left");
  TextAlign.CENTER = new TextAlignEnumItem("CENTER", "Center");
  TextAlign.RIGHT = new TextAlignEnumItem("RIGHT", "Right");
  GenericPanel2.TextAlign = TextAlign;
})(GenericPanel || (GenericPanel = {}));
var MessagePanel = class extends GenericPanel {
  constructor(parent, directParent) {
    super(parent, directParent);
    this._panelHrzAlign = GenericPanel.HorizontalAlignPos.CENTER;
    this._panelVrtAlign = GenericPanel.VerticalAlignPos.CENTER;
  }
  get panelHrzAlign() {
    return this._panelHrzAlign;
  }
  set panelHrzAlign(value) {
    this._panelHrzAlign = value;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get panelVrtAlign() {
    return this._panelVrtAlign;
  }
  set panelVrtAlign(value) {
    this._panelVrtAlign = value;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
};
var AnnotationPanel = class extends GenericPanel {
  constructor(parent, directParent) {
    super(parent, directParent);
    this._overlap = false;
    this._positionOffsetX = 50;
    this._positionOffsetY = 50;
    this._panelHrzAlign = GenericPanel.HorizontalAlignPos.CENTER;
    this._panelVrtAlign = GenericPanel.VerticalAlignPos.TOP;
  }
  get overlap() {
    return this._overlap;
  }
  set overlap(value) {
    if (!value && this._panelHrzAlign == GenericPanel.HorizontalAlignPos.CENTER && this._panelVrtAlign == GenericPanel.VerticalAlignPos.CENTER) {
      this._panelVrtAlign = GenericPanel.VerticalAlignPos.TOP;
    }
    this._overlap = value;
    this._parentRenderer.clearCachedObjects();
    this._parentRenderer.redraw();
  }
  get positionOffsetX() {
    return this._positionOffsetX;
  }
  set positionOffsetX(value) {
    this._positionOffsetX = value;
    this._parentRenderer.clearCachedObjects();
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get positionOffsetY() {
    return this._positionOffsetY;
  }
  set positionOffsetY(value) {
    this._positionOffsetY = value;
    this._parentRenderer.clearCachedObjects();
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get panelHrzAlign() {
    return this._panelHrzAlign;
  }
  set panelHrzAlign(value) {
    if (!this._overlap && value == GenericPanel.HorizontalAlignPos.CENTER && this._panelVrtAlign == GenericPanel.VerticalAlignPos.CENTER) {
      this._panelVrtAlign = GenericPanel.VerticalAlignPos.TOP;
    }
    this._panelHrzAlign = value;
    this._parentRenderer.clearCachedObjects();
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get panelVrtAlign() {
    return this._panelVrtAlign;
  }
  set panelVrtAlign(value) {
    if (!this._overlap && value == GenericPanel.VerticalAlignPos.CENTER && this._panelHrzAlign == GenericPanel.HorizontalAlignPos.CENTER) {
      this._panelHrzAlign = GenericPanel.HorizontalAlignPos.RIGHT;
    }
    this._panelVrtAlign = value;
    this._parentRenderer.clearCachedObjects();
    if (this._enabled)
      this._parentRenderer.redraw();
  }
};
var Zone = class {
  get directParent() {
    return this._directParent;
  }
  get userData() {
    return this._userData;
  }
  set userData(value) {
    this._userData = value;
  }
  resetCache() {
  }
  constructor(parentRenderer, directParent) {
    this._userData = null;
    this._zoneBrush = null;
    this._color = YColor.Red;
    this._visible = false;
    this._min = 0;
    this._max = 100;
    this._directParent = directParent;
    this._parentRenderer = parentRenderer;
  }
  get zoneBrush() {
    if (this._zoneBrush == null)
      this._zoneBrush = new YSolidBrush(this._color);
    return this._zoneBrush;
  }
  get color() {
    return this._color;
  }
  set color(value) {
    this._color = value;
    this._zoneBrush = null;
    if (this.visible)
      this._parentRenderer.redraw();
  }
  get visible() {
    return this._visible;
  }
  set visible(value) {
    this._visible = value;
    this._parentRenderer.redraw();
  }
  set_minMax(min, max) {
    if (min > max)
      throw new RangeError("Min cannot be greater than max ");
    this._min = min;
    this._max = max;
    this.resetCache();
    if (this.visible)
      this._parentRenderer.redraw();
  }
  get min() {
    return this._min;
  }
  set min(value) {
    if (value >= this._max && !YDataRenderer.minMaxCheckDisabled) {
      throw new RangeError("Min cannot be greater than max (" + this._max.toString() + ")");
    }
    this._min = value;
    this.resetCache();
    if (this.visible)
      this._parentRenderer.redraw();
  }
  get max() {
    return this._max;
  }
  set max(value) {
    if (value <= this._min && !YDataRenderer.minMaxCheckDisabled) {
      throw new RangeError("Max cannot be greater than min (" + this._min.toString() + ")");
    }
    this._max = value;
    this.resetCache();
    if (this.visible)
      this._parentRenderer.redraw();
  }
};
var Proportional = class _Proportional {
  get userData() {
    return this._userData;
  }
  set userData(value) {
    this._userData = value;
  }
  get directParent() {
    return this._directParent;
  }
  get value() {
    return this._value;
  }
  set value(v) {
    this._value = v;
    this.set_refPoint();
    if (this._reset != null)
      this._reset(this);
  }
  get resizeRule() {
    return this._resizeRule;
  }
  set resizeRule(value) {
    this.set_refPoint();
    this._resizeRule = value;
  }
  set_refPoint() {
    this._refWidth = Math.max(1, this._parentRenderer.usableUiWidth());
    this._refHeight = Math.max(1, this._parentRenderer.usableUiHeight());
    this._refValue = this._value;
  }
  constructor(value, resizeRule, parentRenderer, directParent, resetCallBack) {
    this._reset = null;
    this._refWidth = 1;
    this._refHeight = 1;
    this._refValue = 1;
    this.valueStack = [];
    this._resizeRule = _Proportional.ResizeRule.FIXED;
    this._userData = null;
    this._reset = resetCallBack;
    this._parentRenderer = parentRenderer;
    this._value = value;
    this._resizeRule = resizeRule;
    this._directParent = directParent;
    this.set_refPoint();
    this._parentRenderer.AddNewProportionalToSizeValue(this);
  }
  containerResizedPushNewCoef(coef) {
    this.valueStack.push(this._value);
    this._value = Math.round(100 * this._refValue * coef) / 100;
    if (this._reset != null)
      this._reset(this);
  }
  containerResizedPop() {
    if (this.valueStack.length <= 0)
      throw new RangeError("Can't pop, empty stack.");
    this._value = this.valueStack.pop();
    if (this._reset != null)
      this._reset(this);
  }
  static resizeCoef(rule, refWidth, refHeight, newWidth, newHeight) {
    switch (rule) {
      case _Proportional.ResizeRule.RELATIVETOWIDTH:
        return newWidth / refWidth;
      case _Proportional.ResizeRule.RELATIVETOHEIGHT:
        return newHeight / refHeight;
      case _Proportional.ResizeRule.RELATIVETOBOTH:
        return Math.min(newHeight / refHeight, newWidth / refWidth);
    }
    return 1;
  }
  containerResized(newWidth, newHeight) {
    this._value = Math.round(100 * this._refValue * _Proportional.resizeCoef(this._resizeRule, this._refWidth, this._refHeight, newWidth, newHeight)) / 100;
    if (this._reset != null)
      this._reset(this);
  }
  forceChangeCallback() {
    if (this._reset != null)
      this._reset(this);
  }
};
(function(Proportional2) {
  class ResizeRuleEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, ResizeRule);
    }
  }
  Proportional2.ResizeRuleEnumItem = ResizeRuleEnumItem;
  class ResizeRule extends YEnum {
  }
  ResizeRule.FIXED = new ResizeRuleEnumItem("FIXED", "Fixed");
  ResizeRule.RELATIVETOWIDTH = new ResizeRuleEnumItem("RELATIVETOWIDTH", "Relative to Width");
  ResizeRule.RELATIVETOHEIGHT = new ResizeRuleEnumItem("RELATIVETOHEIGHT", "Relative to height");
  ResizeRule.RELATIVETOBOTH = new ResizeRuleEnumItem("RELATIVETOBOTH", "Relative to Width and Height");
  Proportional2.ResizeRule = ResizeRule;
})(Proportional || (Proportional = {}));
var YDataRenderer = class _YDataRenderer {
  get annotationPanels() {
    return this._annotationPanels;
  }
  get userData() {
    return this._userData;
  }
  static get minMaxCheckDisabled() {
    return _YDataRenderer._disableMinMaxCheck;
  }
  static set minMaxCheckDisabled(value) {
    _YDataRenderer._disableMinMaxCheck = value;
  }
  resetlegendPens() {
  }
  get getCaptureParameters() {
    return this._getCaptureParameters;
  }
  set getCaptureParameters(value) {
    this._getCaptureParameters = value;
  }
  get messagePanels() {
    return this._messagePanels;
  }
  static globalMouseMove(e) {
    _YDataRenderer.globalMouseX = e.pageX;
    _YDataRenderer.globalMouseY = e.pageY;
  }
  clearTransformationMatrix() {
    this._Scr2ElmMatrix = null;
    this._Elm2ScrMatrix = null;
  }
  // This is the one of the "magic" parts, instead on relying
  // on each HTML element offsetLeft and offsetTop coordinates,we
  // compute the whole transformation matrix for the canvas Element,
  // this way, CSS transformations, including scale and rotation are
  // handled property.  This is important since Yocto-Visualization
  // allows in to inject a widget inside an arbitrary DIV. This
  // also importnat to be able to make these convertion from
  // any position on the whole page because of drag operations
  findElementAbsolutePosition(el) {
    let staticFound = false;
    let relativeFound = false;
    let dx = 0;
    let dy = 0;
    while (el != null) {
      let style = window.getComputedStyle(el);
      if (!relativeFound && style.position == "static" && !staticFound) {
        dx = el.offsetLeft + parseFloat(style.borderLeftWidth);
        dy = el.offsetTop + parseFloat(style.borderTopWidth);
        staticFound = true;
      }
      if (style.position == "relative" || style.position == "absolute") {
        dx += el.offsetLeft + parseFloat(style.borderLeftWidth);
        dy += el.offsetTop + parseFloat(style.borderTopWidth);
        relativeFound = true;
      }
      el = el.parentElement;
    }
    return new PointF(dx, dy);
  }
  get Elm2ScrMatrix() {
    if (this._Elm2ScrMatrix == null) {
      let el = this.UIContainer;
      let AsolutePositionsStack = [];
      while (el != null) {
        AsolutePositionsStack.push(this.findElementAbsolutePosition(el));
        el = el.parentElement;
      }
      el = this.UIContainer;
      let MatrixStack = [];
      let staticFound = false;
      let relativeFound = false;
      let index = 0;
      while (el != null) {
        let parent = el.parentElement;
        let style = window.getComputedStyle(el);
        let dx = AsolutePositionsStack[index].X;
        let dy = AsolutePositionsStack[index].Y;
        if (index < AsolutePositionsStack.length - 1) {
          dx -= AsolutePositionsStack[index + 1].X;
          dy -= AsolutePositionsStack[index + 1].Y;
        }
        let matrix = style["transform"];
        if (matrix != "none") {
          let matrixStr = matrix.match(/matrix.*\((.+)\)/)[1].split(", ");
          let matrixValues = [];
          for (let i = 0; i < 6; i++) {
            matrixValues.push(parseFloat(matrixStr[i]));
          }
          let OriginMatrixMatrixBefore = null;
          let TransformMatrix = Matrix3x3.newMatrix(matrixValues[0], matrixValues[2], matrixValues[4], matrixValues[1], matrixValues[3], matrixValues[5], 0, 0, 1);
          let OriginMatrixMatrixAfter = null;
          if (style.transformOrigin) {
            let parts = style.transformOrigin.split(" ");
            let Ox = Number.parseFloat(parts[0]);
            let Oy = Number.parseFloat(parts[1]);
            OriginMatrixMatrixBefore = Matrix3x3.newTranslateMatrix(-Ox, -Oy);
            OriginMatrixMatrixAfter = Matrix3x3.newTranslateMatrix(Ox, Oy);
          }
          if (OriginMatrixMatrixBefore != null)
            MatrixStack.push(OriginMatrixMatrixBefore);
          if (TransformMatrix != null)
            MatrixStack.push(TransformMatrix);
          if (OriginMatrixMatrixAfter != null)
            MatrixStack.push(OriginMatrixMatrixAfter);
        }
        if (dx != 0 || dy != 0)
          MatrixStack.push(Matrix3x3.newTranslateMatrix(dx, dy));
        el = parent;
        index++;
      }
      this._Elm2ScrMatrix = Matrix3x3.newIdentityMatrix();
      for (let i = MatrixStack.length - 1; i >= 0; i--) {
        this._Elm2ScrMatrix = this._Elm2ScrMatrix.multiplyByM(MatrixStack[i]);
      }
    }
    return this._Elm2ScrMatrix;
  }
  get Scr2ElmMatrix() {
    if (this._Scr2ElmMatrix == null) {
      this._Scr2ElmMatrix = this.Elm2ScrMatrix.inverse;
    }
    return this._Scr2ElmMatrix;
  }
  addAnnotationPanel() {
    let p = new AnnotationPanel(this, this);
    this._annotationPanels.push(p);
    this.redraw();
    return p;
  }
  AllowRedraw() {
    this._redrawAllowed--;
    if (this._redrawAllowed < 0)
      throw new RangeError("Too many AllowRedraw calls");
    if (this._redrawAllowed == 0)
      this.redraw();
  }
  AllowRedrawNoRefresh() {
    this._redrawAllowed--;
    if (this._redrawAllowed < 0)
      throw new RangeError("Too many AllowRedraw calls");
  }
  DisableRedraw() {
    this._redrawAllowed++;
  }
  AddNewProportionalToSizeValue(v) {
    if (this.ProportionalToSizeValues.indexOf(v) < 0)
      this.ProportionalToSizeValues.push(v);
  }
  canRedraw() {
    return this._redrawAllowed == 0;
  }
  setPatchAnnotationCallback(callback) {
    this._PatchAnnotationCallback = callback;
  }
  patchAnnotation(text) {
    text = text.replace("\\n", "\n");
    if (text.indexOf("$") < 0)
      return text;
    let now = /* @__PURE__ */ new Date();
    text = text.replace("$DAY$", now.getDay().toString());
    text = text.replace("$MONTH$", now.getMonth().toString());
    text = text.replace("$YEAR$", now.getFullYear().toString());
    text = text.replace("$HOUR$", now.getHours().toString());
    text = text.replace("$MINUTE$", now.getMinutes().toString());
    text = text.replace("$SECOND$", now.getSeconds().toString());
    if (this._PatchAnnotationCallback != null)
      text = this._PatchAnnotationCallback(text);
    return text;
  }
  mouseLocalPosition() {
    let v = Vector3.FromXYCoord(_YDataRenderer.globalMouseX, _YDataRenderer.globalMouseY);
    let m = this.Scr2ElmMatrix;
    let p = m.multiplyByV(v).toPoint();
    if (p.X < 0 || p.Y < 0 || p.X > this.UIContainer.offsetWidth || p.Y > this.UIContainer.offsetHeight)
      return null;
    return p;
  }
  set proportionnalValueChangeCallback(value) {
    this._proportionnalValueChangeCallback = value;
  }
  ProportionnalValueChanged(source) {
    if (this._proportionnalValueChangeCallback != null)
      this._proportionnalValueChangeCallback(source);
  }
  getContainerInnerWidth() {
    return this.UIContainer.offsetWidth;
  }
  getContainerInnerHeight() {
    return this.UIContainer.offsetHeight;
  }
  Draw(timestamp) {
    if (!this.canRedraw())
      return 0;
    let w = this.getContainerInnerWidth();
    let h = this.getContainerInnerHeight();
    if (w <= 5 || h <= 5)
      return 0;
    this.DisableRedraw();
    let offscreenCanvas = document.createElement("canvas");
    offscreenCanvas.width = w;
    offscreenCanvas.height = h;
    let g = new YGraphics(offscreenCanvas, w, h, 90);
    let start = performance.now();
    try {
      this.Render(g, w, h);
    } catch (e) {
      debugger;
      this.log("Rendering error: " + e.message);
    }
    let elapsed = performance.now() - start;
    let drawArea = this.UIContainer.getContext("2d");
    drawArea.clearRect(0, 0, w, h);
    drawArea.drawImage(offscreenCanvas, 0, 0);
    this.rendererTimingTotal += elapsed;
    this.rendererTimingCount++;
    let avg = this.rendererTimingTotal / this.rendererTimingCount;
    g.Dispose();
    this.AllowRedrawNoRefresh();
    this.renderingPostProcessing();
    return 0;
  }
  renderingPostProcessing() {
  }
  get resizeRule() {
    return this._resizeRule;
  }
  set resizeRule(value) {
    if (value != this._resizeRule) {
      this.DisableRedraw();
      this._resizeRule = value;
      for (let i = 0; i < this.ProportionalToSizeValues.length; i++) {
        this.ProportionalToSizeValues[i].resizeRule = this._resizeRule;
      }
      this.AllowRedraw();
      this.redraw();
    }
  }
  redraw() {
    if (!(document.visibilityState === "visible"))
      return;
    if (this._redrawAllowed > 0)
      return;
    if (this.getContainerInnerWidth() < 2)
      return;
    if (this.getContainerInnerHeight() < 2)
      return;
    if (this.requestAnimationFrameID != null) {
      window.cancelAnimationFrame(this.requestAnimationFrameID);
    }
    this.requestAnimationFrameID = window.requestAnimationFrame((timestamp) => {
      try {
        this.Draw(timestamp);
      } catch (e) {
        console.log("caught");
        this.requestAnimationFrameID = null;
        throw e;
      }
      this.requestAnimationFrameID = null;
    });
  }
  usableUiWidth() {
    return this.getContainerInnerWidth();
  }
  usableUiHeight() {
    return this.getContainerInnerHeight();
  }
  resetProportionalSizeObjectsCachePush(newcoef) {
    this.clearCachedObjects();
    if (this._resizeRule != Proportional.ResizeRule.FIXED) {
      for (let i = 0; i < this.ProportionalToSizeValues.length; i++) {
        this.ProportionalToSizeValues[i].containerResizedPushNewCoef(newcoef);
      }
    }
  }
  resetProportionalSizeObjectsCachePop() {
    this.clearCachedObjects();
    if (this._resizeRule != Proportional.ResizeRule.FIXED) {
      for (let i = 0; i < this.ProportionalToSizeValues.length; i++) {
        this.ProportionalToSizeValues[i].containerResizedPop();
      }
    }
  }
  resetProportionalSizeObjectsCache(w, h) {
    this.clearCachedObjects();
    if (this._resizeRule != Proportional.ResizeRule.FIXED) {
      for (let i = 0; i < this.ProportionalToSizeValues.length; i++) {
        this.ProportionalToSizeValues[i].containerResized(w, h);
      }
    }
  }
  containerResized() {
    this.containerResize(null, null);
  }
  containerResize(sender, e) {
    this.clearTransformationMatrix();
    this.DisableRedraw();
    this.UIContainer.width = this.UIContainer.offsetWidth;
    this.UIContainer.height = this.UIContainer.offsetHeight;
    this.resetProportionalSizeObjectsCache(this.usableUiWidth(), this.usableUiHeight());
    this.AllowRedraw();
    this.redraw();
  }
  proportionnalsizeReset() {
    this.resetProportionalSizeObjectsCache(this.usableUiWidth(), this.usableUiHeight());
  }
  captureAndDownloadImage(captureType, defaultFilename, captureWidth, captureHeight, captureDPI) {
    let error = "";
    let w;
    let h;
    let ratio = this.getContainerInnerWidth() / this.getContainerInnerHeight();
    if (captureDPI == null)
      captureDPI = 90;
    if (defaultFilename == null || typeof defaultFilename == "undefined") {
      defaultFilename = "capture.";
      if (captureType == _YDataRenderer.CaptureType.PNG) {
        defaultFilename = defaultFilename + "png";
      }
      if (captureType == _YDataRenderer.CaptureType.SVG) {
        defaultFilename = defaultFilename + "svg";
      }
    }
    if (captureWidth == null || typeof captureWidth == "undefined") {
      w = this.getContainerInnerWidth();
      if (captureHeight == null || typeof captureHeight == "undefined") {
        h = this.getContainerInnerHeight();
      } else {
        h = captureHeight >> 0;
        w = h * ratio >> 0;
      }
    } else {
      w = captureWidth >> 0;
      if (captureHeight == null || typeof captureHeight == "undefined") {
        h = w * ratio >> 0;
      } else {
        h = captureHeight >> 0;
      }
    }
    if (w <= 5 || h <= 5)
      return;
    this.DisableRedraw();
    if (captureType == _YDataRenderer.CaptureType.SVG) {
      w = Math.round(w / 1.371);
      h = Math.round(h / 1.371);
    }
    let DrawArea = document.createElement("canvas");
    DrawArea.width = w;
    DrawArea.height = h;
    let g;
    switch (captureType) {
      case _YDataRenderer.CaptureType.PNG:
        g = new YGraphics(DrawArea, w, h, captureDPI);
        break;
      case _YDataRenderer.CaptureType.SVG:
        g = new YGraphicsSVG(DrawArea, w, h, captureDPI);
        break;
      default:
        throw new RangeError("capture :unknown type");
    }
    let newCoef = Proportional.resizeCoef(Proportional.ResizeRule.RELATIVETOBOTH, this.refWidth, this.refHeight, w, h);
    this.log("start capture");
    this.resetProportionalSizeObjectsCachePush(newCoef);
    let renderok = false;
    this._snapshotPanel.enabled = false;
    try {
      let t = this.Render(g, w, h);
      renderok = true;
    } catch (e) {
      error = e.message;
      this.log("Render error: " + error);
    }
    this.log("capture completed");
    this.resetProportionalSizeObjectsCachePop();
    if (renderok) {
      let element = document.createElement("a");
      let data = g.get_downloadableData();
      element.setAttribute("href", data);
      element.setAttribute("download", defaultFilename);
      element.style.display = "none";
      document.body.appendChild(element);
      element.click();
      document.body.removeChild(element);
    }
    g.Dispose();
    this.AllowRedraw();
  }
  getFocus(sender, e) {
    this.log("got focus");
  }
  gainFocus() {
  }
  lostFocus(sender, e) {
  }
  get AllowPrintScreenCapture() {
    return this._AllowPrintScreenCapture;
  }
  set AllowPrintScreenCapture(value) {
    this._AllowPrintScreenCapture = value;
  }
  constructor(UIContainer, logFunction) {
    this._redrawAllowed = 1;
    this._refWidth = 1;
    this._refHeight = 1;
    this.rendererTimingTotal = 0;
    this.rendererTimingCount = 0;
    this._PatchAnnotationCallback = null;
    this._logFunction = null;
    this._annotationPanels = [];
    this._userData = null;
    this.documentVisibiltyChangeFct = null;
    this.containerResizedFct = null;
    this._getCaptureParameters = null;
    this.OnDblClick = null;
    this.OnRightClick = null;
    this._messagePanels = [];
    this._Scr2ElmMatrix = null;
    this._Elm2ScrMatrix = null;
    this.ProportionalToSizeValues = [];
    this._proportionnalValueChangeCallback = null;
    this._resizeRule = Proportional.ResizeRule.FIXED;
    this.requestAnimationFrameID = null;
    this._snapshotPanel = null;
    this._snapshotTimer = null;
    this._AllowPrintScreenCapture = false;
    if (!_YDataRenderer.globalMouseMoveSet) {
      document.addEventListener("mousemove", (e) => {
        _YDataRenderer.globalMouseMove(e);
      });
      _YDataRenderer.globalMouseMoveSet = true;
    }
    this.UIContainer = UIContainer;
    this.UIContainer.width = this.getContainerInnerWidth();
    this.UIContainer.height = this.getContainerInnerHeight();
    this._logFunction = logFunction;
    this.parentForm = UIContainer.ownerDocument;
    this._annotationPanels = [];
    this._messagePanels = [];
    this.DisableRedraw();
    this._snapshotPanel = this.addMessagePanel();
    this._snapshotPanel.panelTextAlign = MessagePanel.TextAlign.CENTER;
    this._snapshotPanel.text = "Captured to clipboard";
    this._snapshotPanel.panelHrzAlign = MessagePanel.HorizontalAlignPos.CENTER;
    this._snapshotPanel.panelVrtAlign = MessagePanel.VerticalAlignPos.CENTER;
    this._snapshotPanel.bgColor = new YColor(false, 200, 204, 247, 161);
    this._snapshotPanel.font.size = 16;
    this.AllowRedrawNoRefresh();
    this.containerResizedFct = () => {
      this.containerResize(null, null);
    };
    document.addEventListener("resize", this.containerResizedFct);
    this.resetRefrenceSize();
    this.documentVisibiltyChangeFct = () => {
      if (document.visibilityState === "visible") {
        this.redraw();
      }
    };
    document.addEventListener("visibilitychange", this.documentVisibiltyChangeFct);
  }
  destroy() {
    document.removeEventListener("visibilitychange", this.documentVisibiltyChangeFct);
    document.removeEventListener("resize", this.containerResized);
    this.UIContainer.parentNode.removeChild(this.UIContainer);
    this.UIContainer = null;
    this.parentForm = null;
    this._annotationPanels = null;
    this._messagePanels = null;
    this._Scr2ElmMatrix = null;
    this._Elm2ScrMatrix = null;
  }
  resetRefrenceSize() {
    this._refWidth = this.getContainerInnerWidth();
    this._refHeight = this.getContainerInnerHeight();
  }
  get refWidth() {
    return this._refWidth;
  }
  get refHeight() {
    return this._refHeight;
  }
  RendererCanvas_Click(sender, e) {
  }
  RendererCanvas_DoubleClick(sender, e) {
  }
  addMessagePanel() {
    let p = new MessagePanel(this, this);
    this._messagePanels.push(p);
    return p;
  }
  DrawMessagePanels(g, viewPortWidth, viewPortHeight) {
    for (let i = 0; i < this._messagePanels.length; i++) {
      if (this._messagePanels[i].enabled) {
        let p = this._messagePanels[i];
        let AvailableWidth = viewPortWidth - 2 * p.padding - p.borderthickness;
        if (AvailableWidth < 100)
          AvailableWidth = 100;
        let ssize = null;
        let sizeok = false;
        while (!sizeok) {
          ssize = g.MeasureString(p.text, p.font, AvailableWidth);
          if ((ssize.width >= this.UIContainer.width || ssize.height >= this.UIContainer.height) && p.font.size > 5) {
            p.font.size = Math.round(p.font.size * 9) / 10;
          } else
            sizeok = true;
        }
        if (ssize == null)
          return;
        let panelWidth = ssize.width + 2 * p.padding + p.borderthickness;
        let panelHeight = ssize.height + 2 * p.padding + p.borderthickness;
        let x = 0;
        switch (p.panelHrzAlign) {
          case MessagePanel.HorizontalAlignPos.LEFT:
            x = p.horizontalMargin;
            break;
          case MessagePanel.HorizontalAlignPos.RIGHT:
            x = viewPortWidth - panelWidth - p.horizontalMargin;
            break;
          default:
            x = (viewPortWidth - panelWidth) / 2;
            break;
        }
        let y = 0;
        switch (p.panelVrtAlign) {
          case MessagePanel.VerticalAlignPos.TOP:
            y = p.verticalMargin;
            break;
          case MessagePanel.VerticalAlignPos.BOTTOM:
            y = viewPortHeight - panelHeight - p.verticalMargin;
            break;
          default:
            y = (viewPortHeight - panelHeight) / 2;
            break;
        }
        g.FillRectangleXYHW(p.bgBrush, x, y, panelWidth, panelHeight);
        if (p.borderthickness > 0)
          g.DrawRectangleXYHW(p.pen, x, y, panelWidth, panelHeight);
        let sf = new YStringFormat(
          16384
          /* YStringFormat.StringFormatFlags.NoClip */
        );
        switch (p.panelTextAlign) {
          case MessagePanel.TextAlign.LEFT:
            sf.LineAlignment = 0;
            sf.Alignment = 0;
            break;
          case MessagePanel.TextAlign.RIGHT:
            sf.LineAlignment = 2;
            sf.Alignment = 2;
            break;
          default:
            sf.LineAlignment = 1;
            sf.Alignment = 1;
            break;
        }
        let r = new YRectangle(x + p.padding + p.borderthickness / 2 >> 0, y + p.padding + p.borderthickness / 2 >> 0, ssize.width + 1, ssize.height + 1);
        g.DrawStringRect(p.text, p.font, p.font.brush, r, sf);
      }
    }
  }
  drawAnnotationPanels(g, annotationPanels, viewPortWidth, viewPortHeight, overlap, mainViewPort) {
    let active = false;
    for (let i = 0; i < this.annotationPanels.length; i++) {
      if (this.annotationPanels[i].enabled)
        active = true;
    }
    if (!active)
      return;
    for (let i = 0; i < this.annotationPanels.length; i++) {
      if (annotationPanels[i].enabled && annotationPanels[i].overlap == overlap) {
        let p = annotationPanels[i];
        let AvailableWidth = viewPortWidth - 2 * p.padding - p.borderthickness;
        if (AvailableWidth < 100)
          AvailableWidth = 100;
        let textToDisplay = p.text.replace("\\n", "\n");
        if (textToDisplay.indexOf("$") >= 0) {
          textToDisplay = textToDisplay.replace("\\n", "\n");
          textToDisplay = this.patchAnnotation(textToDisplay);
        }
        let ssize = g.MeasureString(textToDisplay, p.font, AvailableWidth);
        let panelWidth = ssize.width + 2 * p.padding + p.borderthickness;
        let panelHeight = ssize.height + 2 * p.padding + p.borderthickness;
        let x = 0;
        switch (p.panelHrzAlign) {
          case MessagePanel.HorizontalAlignPos.LEFT:
            x = p.horizontalMargin;
            if (!annotationPanels[i].overlap && mainViewPort.Lmargin < panelWidth + 10) {
              mainViewPort.Lmargin = panelWidth + 10;
            }
            break;
          case MessagePanel.HorizontalAlignPos.RIGHT:
            x = viewPortWidth - panelWidth - p.horizontalMargin;
            if (!annotationPanels[i].overlap && mainViewPort.Rmargin < panelWidth + 20) {
              mainViewPort.Rmargin = panelWidth + 20;
            }
            break;
          default:
            x = (viewPortWidth - panelWidth) / 2;
            break;
        }
        let y = 0;
        switch (p.panelVrtAlign) {
          case MessagePanel.VerticalAlignPos.TOP:
            y = p.verticalMargin;
            if (!annotationPanels[i].overlap && mainViewPort.Tmargin < panelHeight + 20) {
              mainViewPort.Tmargin = panelHeight + 20;
            }
            break;
          case MessagePanel.VerticalAlignPos.BOTTOM:
            y = viewPortHeight - panelHeight - p.verticalMargin;
            if (!annotationPanels[i].overlap && mainViewPort.Bmargin < panelHeight + 20) {
              mainViewPort.Bmargin = panelHeight + 20;
            }
            break;
          default:
            y = (viewPortHeight - panelHeight) / 2;
            break;
        }
        if (annotationPanels[i].overlap) {
          x += annotationPanels[i].positionOffsetX / 100 * (viewPortWidth - panelWidth);
          y += annotationPanels[i].positionOffsetY / 100 * (viewPortHeight - panelHeight);
          if (x < 0)
            x = 0;
          if (y < 0)
            y = 0;
          if (x > viewPortWidth - panelWidth)
            x = viewPortWidth - panelWidth;
          if (y > viewPortHeight - panelHeight)
            y = viewPortHeight - panelHeight;
        }
        g.FillRectangleXYHW(p.bgBrush, x, y, panelWidth, panelHeight);
        if (p.borderthickness > 0)
          g.DrawRectangleXYHW(p.pen, x, y, panelWidth, panelHeight);
        let sf = new YStringFormat(
          16384
          /* YStringFormat.StringFormatFlags.NoClip */
        );
        switch (p.panelTextAlign) {
          case MessagePanel.TextAlign.LEFT:
            sf.LineAlignment = 0;
            sf.Alignment = 0;
            break;
          case MessagePanel.TextAlign.RIGHT:
            sf.LineAlignment = 2;
            sf.Alignment = 2;
            break;
          default:
            sf.LineAlignment = 1;
            sf.Alignment = 1;
            break;
        }
        let r = new YRectangle(x + p.padding + p.borderthickness / 2, y + p.padding + p.borderthickness / 2, ssize.width + 1, ssize.height + 1);
        g.DrawStringRect(textToDisplay, p.font, p.font.brush, r, sf);
      }
    }
  }
  log(s) {
    if (this._logFunction == null)
      return;
    this._logFunction(s);
  }
};
YDataRenderer.RendererDebug = false;
YDataRenderer.FloatToStrformats = ["0", "0", "0", "0.0", "0.00", "0.000", "0.0000"];
YDataRenderer._disableMinMaxCheck = false;
YDataRenderer.globalMouseMoveSet = false;
YDataRenderer.globalMouseX = -1;
YDataRenderer.globalMouseY = -1;
(function(YDataRenderer2) {
  class CaptureTypeEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, CaptureType);
    }
  }
  YDataRenderer2.CaptureTypeEnumItem = CaptureTypeEnumItem;
  class CaptureType extends YEnum {
  }
  CaptureType.PNG = new CaptureTypeEnumItem("PNG", "Bitmap (PNG)");
  CaptureType.SVG = new CaptureTypeEnumItem("SVG", "Vector (SVG)");
  YDataRenderer2.CaptureType = CaptureType;
  class CaptureTargetEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, CaptureTarget);
    }
  }
  YDataRenderer2.CaptureTargetEnumItem = CaptureTargetEnumItem;
  class CaptureTarget extends YEnum {
  }
  CaptureTarget.ToClipBoard = new CaptureTargetEnumItem("ToClipBoard", "ClipBoard");
  CaptureTarget.ToFile = new CaptureTargetEnumItem("ToFile", "File");
  YDataRenderer2.CaptureTarget = CaptureTarget;
  class CaptureFormatsEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, CaptureFormats);
    }
  }
  YDataRenderer2.CaptureFormatsEnumItem = CaptureFormatsEnumItem;
  class CaptureFormats extends YEnum {
  }
  CaptureFormats.Keep = new CaptureFormatsEnumItem("Keep", "Keep original size");
  CaptureFormats.Fixed = new CaptureFormatsEnumItem("Fixed", "Fixed size");
  CaptureFormats.FixedWidth = new CaptureFormatsEnumItem("FixedWidth", "Fixed width, keep ration aspect");
  CaptureFormats.FixedHeight = new CaptureFormatsEnumItem("FixedHeight", "Fixed height, keep ration aspect");
  YDataRenderer2.CaptureFormats = CaptureFormats;
})(YDataRenderer || (YDataRenderer = {}));
var YGraphics = class _YGraphics {
  constructor(canvas, width, height, dpi) {
    this._c = null;
    this._g = null;
    this._width = 0;
    this._height = 0;
    this._dpi = 0;
    this._image = null;
    this._lastPen = null;
    this._lastBrush = null;
    this._lastFont = null;
    this._clipCounter = 0;
    this._textRenderingHint = null;
    this._smoothingMode = YSmoothingMode.Default;
    this._c = canvas;
    this._g = this._c.getContext("2d");
    this._g.textBaseline = "top";
    this._width = width;
    this._height = height;
    this._dpi = dpi;
  }
  get_downloadableData() {
    return this._c.toDataURL("image/png");
  }
  get graphics() {
    return this._g;
  }
  setPen(p) {
    if (p == this._lastPen)
      return;
    if (p != null) {
      this._g.lineWidth = p.lineWidth;
      let st = p.strokeStyle;
      this._g.strokeStyle = p.strokeStyle;
    } else {
      this._g.lineWidth = 0;
    }
    this._lastPen = p;
  }
  setBrush(b) {
    if (b == this._lastBrush)
      return;
    if (b instanceof YSolidBrush) {
      this._g.fillStyle = b.color.htmlCode;
    } else if (b instanceof YLinearGradientBrush) {
      let lingrad = this._g.createLinearGradient(0, 0, 0, this._height);
      lingrad.addColorStop(0, b.color1.htmlCode);
      lingrad.addColorStop(1, b.color2.htmlCode);
      this._g.fillStyle = lingrad;
    } else {
      throw new Error("invalid / insupported brush type");
    }
    this._lastBrush = b;
  }
  setFont(f) {
    if (f == this._lastFont && !f.hasChanged)
      return;
    this._g.font = f.htmlCode;
    this._lastFont = f;
  }
  DrawLineXY(p, x1, y1, x2, y2) {
    if (p.noAntiAlias) {
      let offset = p.lineWidth == p.lineWidth >> 0 && (p.lineWidth & 1) == 1 ? 0.5 : 0;
      x1 = Math.round(x1) + offset;
      y1 = Math.round(y1) + offset;
      x2 = Math.round(x2) + offset;
      y2 = Math.round(y2) + offset;
    }
    this.setPen(p);
    this._g.beginPath();
    this._g.moveTo(x1, y1);
    this._g.lineTo(x2, y2);
    this._g.stroke();
  }
  DrawLine(p, p1, p2) {
    this.setPen(p);
    this._g.beginPath();
    this._g.moveTo(p1.X, p1.Y);
    this._g.lineTo(p2.X, p2.Y);
    this._g.stroke();
  }
  SetClip(rect) {
    this._g.save();
    this._g.beginPath();
    this._g.rect(rect.x, rect.y, rect.w, rect.h);
    this._g.clip();
    this._clipCounter++;
  }
  ResetClip() {
    if (this._clipCounter <= 0)
      throw new Error("clipping stack error");
    this._g.restore();
    this._clipCounter--;
  }
  MeasureString(text, font, width) {
    this.setFont(font);
    let res = new YSizeF(font, text);
    let count = res.linesCount;
    if (count == 0)
      return res;
    res.height = res.firstLineHeight * 1.2 + (count - 1) * res.lineHeight;
    let max = 0;
    let dim;
    for (let i = 0; i < count; i++) {
      dim = this._g.measureText(res.lines[i]);
      max = Math.max(max, dim.width);
    }
    res.width = max;
    return res;
  }
  MeasureStringSF(text, font, width, stringFormat) {
    return this.MeasureString(text, font, width);
  }
  FillRectangle(brush, rect) {
    this.setBrush(brush);
    this._g.beginPath();
    this._g.fillRect(rect.x, rect.y, rect.w, rect.h);
    this._g.fill();
  }
  FillRectangleXYHW(brush, x, y, width, height) {
    this.setBrush(brush);
    this._g.beginPath();
    this._g.fillRect(x, y, width, height);
    this._g.fill();
  }
  DrawRectangle(p, rect) {
    this.setPen(p);
    this._g.beginPath();
    if (p.noAntiAlias) {
      let offset = p.lineWidth == p.lineWidth >> 0 && (p.lineWidth & 1) == 1 ? 0.5 : 0;
      this._g.rect(Math.round(rect.x) + offset, Math.round(rect.y) + offset, Math.round(rect.w), Math.round(rect.h));
    } else {
      this._g.rect(rect.x, rect.y, rect.w, rect.h);
    }
    this._g.stroke();
  }
  DrawRectangleXYHW(p, x, y, width, height) {
    if (p.noAntiAlias) {
      let offset = p.lineWidth == p.lineWidth >> 0 && (p.lineWidth & 1) == 1 ? 0.5 : 0;
      x = Math.round(x) + offset;
      y = Math.round(y) + offset;
      width = Math.round(width);
      height = Math.round(height);
    }
    this.setPen(p);
    this._g.beginPath();
    this._g.rect(x, y, width, height);
    this._g.stroke();
  }
  DrawStringXY(s, font, brush, x, y) {
    this.setPen(null);
    this.setBrush(brush);
    this.setFont(font);
    let totalsz = this.MeasureString(s, font, 0);
    let dy = totalsz.lineHeight;
    for (let i = 0; i < totalsz.linesCount; i++) {
      this._g.fillText(totalsz.lines[i], x, y);
      y += dy;
    }
  }
  DrawStringXYF(s, font, brush, x, y, format) {
    if (_YGraphics._debugDrawString) {
      let pen = new YPen(YColor.Red, 1);
      this.DrawLineXY(pen, x - 5, y, x + 5, y);
      this.DrawLineXY(pen, x, y - 5, x, y + 5);
    }
    let sz = this.MeasureString(s, font, 1e4);
    if (format.Alignment == 1) {
      x -= sz.width >> 1;
    } else if (format.Alignment == 2)
      x -= sz.width;
    if (format.LineAlignment == 1) {
      y -= sz.height / 2 >> 0;
    } else if (format.LineAlignment == 2)
      y -= sz.height;
    this.DrawStringXY(s, font, brush, x, y);
  }
  DrawStringPF(s, font, brush, p, format) {
    this.DrawStringXYF(s, font, brush, p.X, p.Y, format);
  }
  DrawString(s, font, brush, p) {
    if (_YGraphics._debugDrawString) {
      let pen = new YPen(YColor.Red, 1);
      this.DrawLineXY(pen, p.X - 5, p.Y, p.X + 5, p.Y);
      this.DrawLineXY(pen, p.X, p.Y - 5, p.X, p.Y + 5);
    }
    this.setPen(null);
    this.setBrush(brush);
    this.setFont(font);
    this._g.fillText(s, p.X, p.Y);
  }
  DrawStringRect(s, font, brush, layoutRectangle, format) {
    if (_YGraphics._debugDrawString) {
      let pen = new YPen(YColor.Red, 1);
      this.DrawRectangle(pen, layoutRectangle);
    }
    this.setPen(null);
    this.setBrush(brush);
    this.setFont(font);
    let totalsz = this.MeasureString(s, font, 0);
    let dy = totalsz.lineHeight;
    let y = layoutRectangle.y;
    switch (format.LineAlignment) {
      case 0:
        break;
      case 1:
        y += (layoutRectangle.h - totalsz.height) / 2;
        break;
      case 2:
        y += layoutRectangle.h - totalsz.height;
        break;
    }
    let xOrigin = layoutRectangle.x;
    if (format.FormatFlags & 2) {
      this.Transform(layoutRectangle.x, layoutRectangle.y, Math.PI / 2);
      y -= layoutRectangle.y + layoutRectangle.w;
      xOrigin -= layoutRectangle.x;
    }
    for (let i = 0; i < totalsz.linesCount; i++) {
      let s2 = totalsz.lines[i];
      let sz = this.MeasureString(s2, font, 0);
      let x = xOrigin;
      switch (format.Alignment) {
        case 0:
          break;
        case 1:
          x += (layoutRectangle.w - sz.width) / 2;
          break;
        case 2:
          x += layoutRectangle.w - sz.width;
          break;
      }
      this._g.fillText(s2, x, y);
      y += dy;
    }
    if (format.FormatFlags & 2)
      this.ResetTransform();
  }
  Transform(dx, dy, angle) {
    let sin = Math.sin(angle);
    let cos = Math.cos(angle);
    this._g.save();
    this._g.transform(cos, sin, -sin, cos, dx, dy);
  }
  ResetTransform() {
    this._g.restore();
  }
  FillEllipse(brush, x, y, width, height) {
    this.setBrush(brush);
    this._g.beginPath();
    this._g.ellipse(x + width / 2, y + height / 2, width / 2, height / 2, 0, 0, 2 * Math.PI);
    this._g.fill();
  }
  DrawEllipse(pen, x, y, width, height) {
    this.setPen(pen);
    this._g.beginPath();
    this._g.ellipse(x + width / 2, y + height / 2, width / 2, height / 2, 0, 0, 2 * Math.PI);
    this._g.stroke();
  }
  FillPolygon(brush, points) {
    this.setBrush(brush);
    if (points.length <= 2)
      return;
    this._g.beginPath();
    if (brush.noAntiAlias) {
      this._g.moveTo(Math.round(points[0].X) + 0.5, Math.round(points[0].Y) + 0.5);
      for (let i = 1; i < points.length; i++) {
        this._g.lineTo(Math.round(points[i].X) + 0.5, Math.round(points[i].Y) + 0.5);
      }
    } else {
      this._g.moveTo(points[0].X, points[0].Y);
      for (let i = 1; i < points.length; i++) {
        this._g.lineTo(points[i].X, points[i].Y);
      }
    }
    this._g.closePath();
    this._g.fill();
  }
  DrawPolygon(pen, points) {
    this.setPen(pen);
    if (points.length <= 1)
      return;
    this._g.beginPath();
    if (pen.noAntiAlias) {
      let offset = pen.lineWidth == pen.lineWidth >> 0 && (pen.lineWidth & 1) == 1 ? 0.5 : 0;
      this._g.moveTo(Math.round(points[0].X) + offset, Math.round(points[0].Y) + offset);
      for (let i = 1; i < points.length; i++) {
        this._g.lineTo(Math.round(points[i].X) + offset, Math.round(points[i].Y) + offset);
      }
    } else {
      this._g.moveTo(points[0].X, points[0].Y);
      for (let i = 1; i < points.length; i++) {
        this._g.lineTo(points[i].X, points[i].Y);
      }
    }
    this._g.closePath();
    this._g.stroke();
  }
  DrawLines(pen, points) {
    this.setPen(pen);
    if (points.length <= 1)
      return;
    this._g.beginPath();
    this._g.lineCap = "round";
    this._g.lineJoin = "round";
    this._g.moveTo(points[0].X, points[0].Y);
    for (let i = 1; i < points.length; i++) {
      this._g.lineTo(points[i].X, points[i].Y);
    }
    this._g.stroke();
  }
  Dispose() {
    this._c = null;
    this._g = null;
    this._width = 0;
    this._height = 0;
    this._dpi = 0;
  }
  get TextRenderingHint() {
    return this._textRenderingHint;
  }
  set TextRenderingHint(value) {
    this._textRenderingHint = value;
  }
  get SmoothingMode() {
    return this._smoothingMode;
  }
  set SmoothingMode(value) {
    this._smoothingMode = value;
  }
  DrawImage(srcimage, destRect, srcRect, srcUnit) {
    this._g.drawImage(srcimage, srcRect.x, srcRect.y, srcRect.w, srcRect.h, destRect.x, destRect.y, destRect.w, destRect.h);
  }
  comment(s) {
  }
};
YGraphics._debugDrawString = false;
var YGraphicsSVG = class _YGraphicsSVG extends YGraphics {
  constructor(canvas, width, height, dpi) {
    super(canvas, width, height, dpi);
    this._clipcount = 0;
    this._clipSectionsToClose = 0;
    this._transformSectionsToClose = 0;
    this._gradientCount = 0;
    _YGraphicsSVG.SVGID++;
    this._SVGdefs = new YStringBuilder();
    this._SVGcontents = new YStringBuilder();
    this._SVGdefs.AppendLine('<clipPath id="pageClip_' + _YGraphicsSVG.SVGID.toString() + '"><rect x="0" y="0"  width="' + width.toString() + '" height="' + height.toString() + '"/></clipPath>');
  }
  static escapeXml(unsafe) {
    return unsafe.replace(/[^ !#$%(-;=?-z]/g, (c) => "&#" + c.charCodeAt(0) + ";");
  }
  get_downloadableData() {
    return "data:image/svg+xml;base64," + btoa(this.get_svgContents());
  }
  DrawLineXY(p, x1, y1, x2, y2) {
    this._SVGcontents.AppendLine('<line x1="' + x1.toString() + '"  y1 ="' + y1.toString() + '"  x2 ="' + x2.toString() + '"  y2 ="' + y2.toString() + '" style = "stroke:' + p.color.svgCode + ";stroke-opacity:" + p.color.alphaCode + "; stroke-width:" + p.lineWidth.toString() + '"/>');
  }
  DrawLine(p, p1, p2) {
    this.DrawLineXY(p, p1.X, p1.Y, p2.X, p2.Y);
  }
  SetClip(rect) {
    this.ResetClip();
    this._SVGdefs.AppendLine('<clipPath id="clip_' + _YGraphicsSVG.SVGID.toString() + "_" + this._clipcount.toString() + '"><rect x="' + rect.x.toString() + '" y="' + rect.y.toString() + '"  width="' + rect.w.toString() + '" height="' + rect.h.toString() + '"/></clipPath>');
    this._SVGcontents.AppendLine('<g clip-path="url(#clip_' + _YGraphicsSVG.SVGID.toString() + "_" + this._clipcount.toString() + ')">');
    this._clipcount++;
    this._clipSectionsToClose++;
  }
  ResetClip() {
    if (this._clipSectionsToClose > 0) {
      this._SVGcontents.AppendLine("</g>");
      this._clipSectionsToClose--;
    }
  }
  BrushToSVG(brush, revert) {
    let fillParam = "";
    if (brush instanceof YSolidBrush) {
      fillParam = 'fill = "' + brush.color.svgCode + '" fill-opacity="' + brush.color.alphaCode + '" ';
    } else if (brush instanceof YLinearGradientBrush) {
      this._SVGdefs.AppendLine('<linearGradient id="grad_' + _YGraphicsSVG.SVGID.toString() + "_" + this._gradientCount + '" x1="0%" ' + (revert ? 'y1="100%" ' : 'y1="0%" ') + 'x2="0%" ' + (revert ? 'y2="0%" ' : 'y2="100%" ') + '>\r\n<stop offset="0%" style ="stop-color:' + brush.color1.svgCode + ";stop-opacity:" + brush.color1.alphaCode + '"/>\r\n<stop offset="100%" style ="stop-color:' + brush.color2.svgCode + ";stop-opacity:" + brush.color2.alphaCode + '"/>\r\n</linearGradient>');
      fillParam = 'fill="url(#grad_' + _YGraphicsSVG.SVGID.toString() + "_" + this._gradientCount + ')" ';
      this._gradientCount++;
    } else {
      throw new ReferenceError("unsupported brush type.");
    }
    return fillParam;
  }
  FillRectangle(brush, rect) {
    this._SVGcontents.AppendLine('<rect x="' + rect.x.toString() + '"  y ="' + rect.y.toString() + '"  width ="' + rect.w.toString() + '"  height ="' + rect.h.toString() + '" ' + this.BrushToSVG(brush, true) + 'style="stroke-width:0"/>');
  }
  FillRectangleXYHW(brush, x, y, width, height) {
    this.FillRectangle(brush, new YRectangle(x, y, width, height));
  }
  DrawRectangle(p, rect) {
    this._SVGcontents.AppendLine('<rect x="' + rect.x.toString() + '"  y ="' + rect.y.toString() + '"  width ="' + rect.w.toString() + '"  height ="' + rect.h.toString() + '"  fill="none" style = "stroke:' + p.color.svgCode + ";stroke-opacity:" + p.color.alphaCode + "; stroke-width:" + p.lineWidth.toString() + '"/>');
  }
  DrawRectangleXYHW(p, x, y, width, height) {
    this.DrawRectangle(p, new YRectangle(x, y, width, height));
  }
  DrawEllipse(pen, x, y, width, height) {
    this._SVGcontents.AppendLine('<ellipse  cx="' + (x + width / 2).toString() + '"  cy ="' + (y + height / 2).toString() + '"  rx ="' + (width / 2).toString() + '"  ry ="' + (height / 2).toString() + '"  fill="none" style = "stroke:' + pen.color.svgCode + ";stroke-opacity:" + pen.color.alphaCode + "; stroke-width:" + pen.lineWidth.toString() + '"/>');
  }
  FillEllipse(brush, x, y, width, height) {
    this._SVGcontents.AppendLine('<ellipse  cx="' + (x + width / 2).toString() + '"  cy ="' + (y + height / 2).toString() + '"  rx ="' + (width / 2).toString() + '"  ry ="' + (height / 2).toString() + '" ' + this.BrushToSVG(brush, false) + 'style="stroke-width:0"/>');
  }
  DrawStringXY(s, font, brush, x, y) {
    let tokens = s.split("\n");
    for (let i = 0; i < tokens.length; i++) {
      s = tokens[i];
      this._SVGcontents.AppendLine('<text x="' + x.toString() + '" y="' + (y + font.sizeInPoints).toString() + '" text-anchor="start" font-family="' + font.name.toString() + '" font-size="' + font.sizeInPoints.toString() + 'pt" font-weight="' + (font.bold ? "bold" : "normal") + '" font-style="' + (font.italic ? "italic" : "normal") + '" ' + this.BrushToSVG(brush, false) + 'style="stroke-width:0">\r\n' + _YGraphicsSVG.escapeXml(s) + "\r\n</text>");
      y += font.sizeInPoints * 1.75;
    }
  }
  DrawString(s, font, brush, p) {
    if (YGraphics._debugDrawString) {
      let pen = new YPen(YColor.Red, 1);
      this.DrawLineXY(pen, p.X - 5, p.Y, p.X + 5, p.Y);
      this.DrawLineXY(pen, p.X, p.Y - 5, p.X, p.Y + 5);
    }
    this.DrawStringXY(s, font, brush, p.X, p.Y);
  }
  DrawStringF(s, font, brush, point, format) {
    let totalsz = this.MeasureString(s, font, 0);
    let y = point.Y + font.size * 1.25;
    switch (format.LineAlignment) {
      case 0:
        break;
      case 1:
        y += -totalsz.height / 2;
        break;
      case 2:
        y += -totalsz.height;
        break;
    }
    let tokens = s.split("\n");
    for (let i = 0; i < tokens.length; i++) {
      let s2 = tokens[i];
      let sz = this.MeasureString(s2, font, 0);
      let x = point.X;
      switch (format.Alignment) {
        case 0:
          break;
        case 1:
          x += -sz.width / 2;
          break;
        case 2:
          x += -sz.width;
          break;
      }
      this._SVGcontents.AppendLine('<text x="' + x.toString() + '" y="' + y.toString() + '" text-anchor="start" font-family="' + font.name.toString() + '" font-size="' + font.sizeInPoints.toString() + 'pt" font-weight="' + (font.bold ? "bold" : "normal") + '" font-style="' + (font.italic ? "italic" : "normal") + '" ' + this.BrushToSVG(brush, false) + 'style="stroke-width:0">\r\n' + _YGraphicsSVG.escapeXml(s2) + "\r\n</text>");
      y += font.sizeInPoints * 1.75;
    }
  }
  DrawStringRect(s, font, brush, layoutRectangle, format) {
    if (YGraphics._debugDrawString) {
      let pen = new YPen(YColor.Red, 1);
      this.DrawRectangle(pen, layoutRectangle);
    }
    let totalsz = this.MeasureString(s, font, 0);
    let y = layoutRectangle.y + font.sizeInPoints * 1.1;
    switch (format.LineAlignment) {
      case 0:
        break;
      case 1:
        y += (layoutRectangle.h - totalsz.height) / 2;
        break;
      case 2:
        y += layoutRectangle.h - totalsz.height;
        break;
    }
    let tokens = s.split("\n");
    for (let i = 0; i < tokens.length; i++) {
      let s2 = tokens[i];
      let sz = this.MeasureString(s2, font, 0);
      let x = layoutRectangle.x;
      switch (format.Alignment) {
        case 0:
          break;
        case 1:
          x += (layoutRectangle.w - sz.width) / 2;
          break;
        case 2:
          x += layoutRectangle.w - sz.width;
          break;
      }
      this._SVGcontents.AppendLine('<text x="' + x.toString() + '" y="' + y.toString() + '" text-anchor="start" font-family="' + font.name.toString() + '" font-size="' + (font.sizeInPoints * 1.1).toString() + 'pt" font-weight="' + (font.bold ? "bold" : "normal") + '" font-style="' + (font.italic ? "italic" : "normal") + '" ' + this.BrushToSVG(brush, false) + 'style="stroke-width:0">\r\n' + _YGraphicsSVG.escapeXml(s2) + "\r\n</text>");
      y += font.sizeInPoints * 1.75;
    }
  }
  Transform(dx, dy, angle) {
    this._SVGcontents.AppendLine('<g transform="translate(' + dx.toString() + " " + dy.toString() + ") rotate(" + (180 * angle / Math.PI).toString() + ')">');
    this._transformSectionsToClose++;
  }
  ResetTransform() {
    if (this._transformSectionsToClose > 0) {
      this._SVGcontents.AppendLine("</g>");
      this._transformSectionsToClose--;
    }
  }
  DrawPolygon(pen, points) {
    if (points.length < 2)
      return;
    this._SVGcontents.Append('<path  d="M ' + points[0].X.toString() + " " + points[0].Y.toString());
    for (let i = 1; i < points.length; i += 1) {
      this._SVGcontents.Append(" L " + points[i].X.toString() + " " + points[i].Y.toString());
    }
    this._SVGcontents.AppendLine(' z" fill="none" style="stroke:' + pen.color.svgCode + ";stroke-opacity:" + pen.color.alphaCode + "; stroke-width:" + pen.lineWidth.toString() + '"/>');
  }
  DrawLines(pen, points) {
    if (points.length < 2)
      return;
    this._SVGcontents.Append('<path  d="M ' + points[0].X.toString() + " " + points[0].Y.toString());
    for (let i = 1; i < points.length; i++) {
      this._SVGcontents.Append(" L " + points[i].X.toString() + " " + points[i].Y.toString());
    }
    this._SVGcontents.AppendLine('" fill="none" style="stroke:' + pen.color.svgCode + ";stroke-opacity:" + pen.color.alphaCode + "; stroke-linecap:round; stroke-linejoin:round;stroke-width:" + pen.lineWidth.toString() + '"/>');
  }
  FillPolygon(brush, points) {
    if (points.length < 2)
      return;
    this._SVGcontents.Append('<path  d="M ' + points[0].X.toString() + " " + points[0].Y.toString());
    for (let i = 1; i < points.length; i++) {
      this._SVGcontents.Append(" L " + points[i].X.toString() + " " + points[i].Y.toString());
    }
    this._SVGcontents.AppendLine(' z" ' + this.BrushToSVG(brush, false) + 'style="stroke-width:0"/>\r\n');
  }
  DrawImage(image, destRect, srcRect, srcUnit) {
    throw new Error("DrawImage not supported, find an other way.");
  }
  save(filename) {
    throw new Error("Direct save to file not supported.");
  }
  comment(s) {
    this._SVGcontents.AppendLine("<!--" + s + "-->");
  }
  get_svgContents() {
    let physicalWidth = (2.54 * (this._width / this._dpi)).toFixed(3);
    let physicalheight = (2.54 * (this._height / this._dpi)).toFixed(3);
    while (this._clipSectionsToClose > 0) {
      this._SVGcontents.AppendLine("</g>");
      this._clipSectionsToClose--;
    }
    while (this._transformSectionsToClose > 0) {
      this._SVGcontents.AppendLine("</g>");
      this._transformSectionsToClose--;
    }
    return '<?xml version = "1.0" standalone = "no" ?>\r\n<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\r\n<svg width = "' + physicalWidth + 'cm" height = "' + physicalheight + 'cm" viewBox = "0 0 ' + this._width.toString() + " " + this._height.toString() + '" xmlns = "http://www.w3.org/2000/svg" version = "1.1" >\r\n<defs>\r\n' + this._SVGdefs.contents + '</defs>\r\n<g clip-path="url(#pageClip_' + _YGraphicsSVG.SVGID.toString() + ')">\r\n' + this._SVGcontents.contents + "</g>\r\n</svg>\n";
  }
};
YGraphicsSVG.SVGID = 0;

// obj/full/Renderer/YAngularGauge.js
var YAngularZone = class extends Zone {
  get path() {
    return this._path;
  }
  setPathSize(count) {
    this._path = new Array(count).fill(null);
  }
  setPathPoint(index, p) {
    this._path[index] = p;
  }
  resetPath() {
    this._path = null;
  }
  resetCache() {
    this.resetPath();
  }
  get width() {
    return this._width;
  }
  set width(value) {
    if (value <= 0)
      throw new RangeError("Width must be a positive value");
    this._width = value;
    this._path = null;
    this._parentRenderer.redraw();
  }
  get outerRadius() {
    return this._outerRadius;
  }
  set outerRadius(value) {
    this._outerRadius = Math.max(0, Math.min(100, value));
    this._path = null;
    this._parentRenderer.redraw();
  }
  constructor(parentRenderer, directParent) {
    super(parentRenderer, directParent);
    this._path = null;
    this._width = 10;
    this._outerRadius = 98;
  }
};
var YAngularGauge = class extends YDataRenderer {
  get min() {
    return this._min;
  }
  set min(value) {
    if (value >= this._max && !YDataRenderer.minMaxCheckDisabled) {
      throw new RangeError("Min cannot be greater than max (" + this._max.toString() + ")");
    }
    this._min = value;
    for (let i = 0; i < this._zones.length; i++) {
      this._zones[i].resetPath();
    }
    if (this._needleValue < this._min) {
      this._needleValue = this._min;
    }
    this.redraw();
  }
  get max() {
    return this._max;
  }
  set max(value) {
    if (value <= this._min && !YDataRenderer.minMaxCheckDisabled) {
      throw new RangeError("Max cannot be less than min (" + this._min.toString() + ")");
    }
    this._max = value;
    for (let i = 0; i < this._zones.length; i++) {
      this._zones[i].resetPath();
    }
    if (this._needleValue > this._max) {
      this._needleValue = this._max;
    }
    this.redraw();
  }
  get borderpen() {
    if (this._borderpen == null) {
      this._borderpen = new YPen(this._borderColor, this._borderThickness);
      this._borderpen.startCap = 1;
      this._borderpen.endCap = 1;
    }
    return this._borderpen;
  }
  get borderColor() {
    return this._borderColor;
  }
  set borderColor(value) {
    this._borderColor = value;
    this._borderpen = null;
    this.redraw();
  }
  get backgroundColor1() {
    return this._backgroundColor1;
  }
  set backgroundColor1(value) {
    this._backgroundColor1 = value;
    this._bgBrush = null;
    this.redraw();
  }
  get backgroundColor2() {
    return this._backgroundColor2;
  }
  set backgroundColor2(value) {
    this._backgroundColor2 = value;
    this._bgBrush = null;
    this.redraw();
  }
  get borderThickness() {
    return this._borderThickness;
  }
  set borderThickness(value) {
    if (value < 0)
      throw new RangeError("thickness must be a positive value");
    this._borderThickness = value;
    this._borderpen = null;
    this.redraw();
  }
  get valueFormater() {
    return this._valueFormater;
  }
  set valueFormater(value) {
    this._valueFormater = value;
    this.redraw();
  }
  get minmaxFormater() {
    return this._minmaxFormater;
  }
  set minmaxFormater(value) {
    this._minmaxFormater = value;
    this.redraw();
  }
  get thickness() {
    return this._thickness;
  }
  set thickness(value) {
    if (value < 0)
      throw new RangeError("Thickness must be a positive value");
    this._thickness = Math.max(Math.min(value, 80), 1);
    this.redraw();
  }
  get value() {
    return this._value;
  }
  set value(value) {
    this._value = value;
    this.redraw();
  }
  get color1() {
    return this._color1;
  }
  set color1(value) {
    this._color1 = value;
    this.redraw();
  }
  get color2() {
    return this._color2;
  }
  set color2(value) {
    this._color2 = value;
    this.redraw();
  }
  get graduationColor() {
    return this._graduationColor;
  }
  set graduationColor(value) {
    this._graduationColor = value;
    this._graduationPen = null;
    this.redraw();
  }
  get graduationThickness() {
    return this._graduationThickness;
  }
  set graduationThickness(value) {
    if (value < 0)
      throw new RangeError("Thickness must be a positive value");
    this._graduationThickness = value;
    this._graduationPen = null;
    this.redraw();
  }
  get graduationSize() {
    return this._graduationSize;
  }
  set graduationSize(value) {
    if (value <= 0)
      throw new RangeError("Graduation size must be a positive value");
    this._graduationSize = value;
    this.redraw();
  }
  get graduation() {
    return this._graduation;
  }
  set graduation(value) {
    this._graduation = value;
    this.redraw();
  }
  get unitFactor() {
    return this._unitFactor;
  }
  set unitFactor(value) {
    if (value == 0)
      throw new RangeError("Factor cannot be zero.");
    this._unitFactor = value;
    this.redraw();
  }
  get unit() {
    return this._unit;
  }
  set unit(value) {
    this._unit = value;
    this.redraw();
  }
  get unitFont() {
    return this._unitFont;
  }
  get subgraduationColor() {
    return this._subgraduationColor;
  }
  set subgraduationColor(value) {
    this._subgraduationColor = value;
    this._subgraduationPen = null;
    this.redraw();
  }
  get subgraduationThickness() {
    return this._subgraduationThickness;
  }
  set subgraduationThickness(value) {
    if (value < 0)
      throw new RangeError("Thickness must be a positive value");
    this._subgraduationThickness = value;
    this._subgraduationPen = null;
    this.redraw();
  }
  get subgraduationSize() {
    return this._subgraduationSize;
  }
  set subgraduationSize(value) {
    if (value <= 0)
      throw new RangeError("Size must be a positive value");
    this._subgraduationSize = value;
    this.redraw();
  }
  get graduationOuterRadiusSize() {
    return this._graduationOuterRadiusSize;
  }
  set graduationOuterRadiusSize(value) {
    this._graduationOuterRadiusSize = Math.max(0, Math.min(100, value));
    this.redraw();
  }
  get subgraduationCount() {
    return this._subgraduationCount;
  }
  set subgraduationCount(value) {
    if (value < 0)
      throw new RangeError("Count must be a positive value");
    this._subgraduationCount = value;
    this.redraw();
  }
  get statusColor() {
    return this._statusColor;
  }
  set statusColor(value) {
    this._statusColor = value;
    this.redraw();
  }
  get statusFont() {
    return this._unitFont;
  }
  get statusLine() {
    return this._statusLine;
  }
  set statusLine(value) {
    this._statusLine = value;
    this.redraw();
  }
  get showNeedle() {
    return this._showNeedle;
  }
  set showNeedle(value) {
    this._showNeedle = value;
    this.redraw();
  }
  get needleColor() {
    return this._needleColor;
  }
  set needleColor(value) {
    this._needleColor = value;
    this._needleBrush = null;
    this.redraw();
  }
  get needleMaxSpeed() {
    return this._needleMaxSpeed;
  }
  set needleMaxSpeed(value) {
    if (value <= 0)
      throw new RangeError("Speed must be a positive value");
    this._needleMaxSpeed = value;
    this.redraw();
  }
  get needleLength1() {
    return this._needleLength1;
  }
  set needleLength1(value) {
    this._needleLength1 = value;
    this.redraw();
  }
  get needleLength2() {
    return this._needleLength2;
  }
  set needleLength2(value) {
    this._needleLength2 = value;
    this.redraw();
  }
  get needleWidth() {
    return this._needleWidth;
  }
  set needleWidth(value) {
    if (value <= 0)
      throw new RangeError("Width must be a positive value");
    this._needleWidth = value;
    this.redraw();
  }
  get needleContourColor() {
    return this._needleContourColor;
  }
  set needleContourColor(value) {
    this._needleContourColor = value;
    this._needleContourPen = null;
    this.redraw();
  }
  get needleContourThickness() {
    return this._needleContourThickness;
  }
  set needleContourThickness(value) {
    if (value < 0)
      throw new RangeError("Thickness must be a positive value");
    this._needleContourThickness = value;
    this._needleContourPen = null;
    this.redraw();
  }
  get graduationFont() {
    return this._graduationFont;
  }
  get showMinMax() {
    return this._showMinMax;
  }
  set showMinMax(value) {
    this._showMinMax = value;
    this.redraw();
  }
  constructor(UIContainer, logFunction) {
    super(UIContainer, logFunction);
    this._min = 0;
    this._max = 100;
    this.SegmentMaxLength = 8;
    this._borderpen = null;
    this._borderColor = YColor.Black;
    this._bgBrush = null;
    this._backgroundColor1 = YColor.FromArgb(255, 240, 240, 240);
    this._backgroundColor2 = YColor.FromArgb(255, 200, 200, 200);
    this._borderThickness = 5;
    this._valueFormater = null;
    this._minmaxFormater = null;
    this._thickness = 20;
    this._value = 0;
    this._needleValue = 0;
    this._color1 = YColor.Green;
    this._color2 = YColor.Red;
    this._graduationPen = null;
    this._graduationColor = YColor.Black;
    this._graduationThickness = 2;
    this._graduationSize = 10;
    this._graduation = 10;
    this._unitFactor = 1;
    this._unit = "";
    this._subgraduationPen = null;
    this._subgraduationColor = YColor.Black;
    this._subgraduationThickness = 1;
    this._subgraduationSize = 5;
    this._graduationOuterRadiusSize = 98;
    this._subgraduationCount = 5;
    this._statusColor = YColor.Gray;
    this._statusLine = "";
    this._showNeedle = true;
    this._needleBrush = null;
    this._needleColor = YColor.Red;
    this._needleMaxSpeed = 5;
    this._needleLength1 = 90;
    this._needleLength2 = 5;
    this._needleWidth = 5;
    this._needleContourPen = null;
    this._needleContourColor = YColor.DarkRed;
    this._needleContourThickness = 1;
    this._showMinMax = true;
    this._path = null;
    this._graduationFont = new YFont(this, this, Math.min(this.getContainerInnerWidth(), this.getContainerInnerHeight()) / 15, null);
    this._unitFont = new YFont(this, this, Math.min(this.getContainerInnerWidth(), this.getContainerInnerHeight()) / 20, null);
    this._statusFont = new YFont(this, this, Math.min(this.getContainerInnerWidth(), this.getContainerInnerHeight()) / 15, null);
    this.unitFont.color = YColor.DarkGray;
    this._statusFont.color = YColor.DarkGray;
    this.resizeRule = Proportional.ResizeRule.RELATIVETOBOTH;
    this._zones = [];
  }
  clearCachedObjects() {
    if (this._zones != null) {
      for (let i = 0; i < this._zones.length; i++) {
        this._zones[i].resetPath();
      }
    }
    this._path = null;
    this._bgBrush = null;
  }
  get zones() {
    return this._zones;
  }
  AddZone() {
    let z = new YAngularZone(this, this);
    this._zones.push(z);
    return z;
  }
  Render(g, w, h) {
    let mainViewPort = new ViewPortSettings();
    mainViewPort.Lmargin = 0;
    mainViewPort.Rmargin = 0;
    mainViewPort.Tmargin = 0;
    mainViewPort.Bmargin = 0;
    g.SmoothingMode = YSmoothingMode.HighQuality;
    g.TextRenderingHint = YTextRenderingHint.AntiAlias;
    let stringFormat4Sizing = new YStringFormat(
      16384
      /* YDataRendering.YStringFormat.StringFormatFlags.NoClip */
    );
    let stringFormat = new YStringFormat(
      16384
      /* YDataRendering.YStringFormat.StringFormatFlags.NoClip */
    );
    stringFormat.Alignment = 1;
    stringFormat.LineAlignment = 1;
    this.drawAnnotationPanels(g, this._annotationPanels, w, h, false, mainViewPort);
    g.TextRenderingHint = YTextRenderingHint.AntiAlias;
    let xcenter = mainViewPort.Lmargin + (w - mainViewPort.Lmargin - mainViewPort.Rmargin) / 2;
    let ycenter = mainViewPort.Tmargin + (h - mainViewPort.Tmargin - mainViewPort.Bmargin) / 2;
    let radius = Math.min((w - mainViewPort.Lmargin - mainViewPort.Rmargin) / 2, (h - mainViewPort.Tmargin - mainViewPort.Bmargin) / 2) - this.borderThickness;
    let circonference = 2 * radius * 3.14 >> 0;
    let AngleAperture = 4 * 2 * Math.PI / 5;
    if (this._path == null) {
      let outterlength = 2 * radius * Math.PI;
      let stepCount = outterlength / this.SegmentMaxLength >> 0;
      let stepsize = 2 * Math.PI / stepCount;
      this._path = new Array(stepCount).fill(null);
      let n = 0;
      for (let i = 0; i < stepCount; i++) {
        let a = 2 * i * Math.PI / stepCount;
        this._path[n++] = new PointF(xcenter + radius * Math.cos(a), ycenter - radius * Math.sin(a));
      }
    }
    if (this._bgBrush == null)
      this._bgBrush = new YLinearGradientBrush(this._backgroundColor1, this._backgroundColor2);
    if (this._borderpen == null) {
      this._borderpen = new YPen(this._borderColor, this._borderThickness);
      this._borderpen.linejoin = YPen.LineJoin.Round;
    }
    if (this._path.length > 3)
      g.FillPolygon(this._bgBrush, this._path);
    if (this._graduationPen == null)
      this._graduationPen = new YPen(this._graduationColor, this._graduationThickness);
    if (this._subgraduationPen == null)
      this._subgraduationPen = new YPen(this._subgraduationColor, this._subgraduationThickness);
    let unitDesc = (this._unitFactor != 1 ? "x" + this._unitFactor.toString() + " " : "") + this._unit;
    let size = g.MeasureStringSF(unitDesc.toString(), this._unitFont, 1e4, stringFormat4Sizing);
    let unitPos = new YRectangle(xcenter - size.width / 2 >> 0, ycenter + radius / 2 - size.height / 2 >> 0, size.width + 1 >> 0, size.height + 1 >> 0);
    g.DrawStringRect(unitDesc, this._unitFont, this._unitFont.brush, unitPos, stringFormat);
    if (this._statusLine != "") {
      size = g.MeasureStringSF(this._statusLine, this._statusFont, 1e4, stringFormat4Sizing);
      let statusPos = new YRectangle(xcenter - size.width / 2 >> 0, ycenter - radius / 3 - size.height / 2 >> 0, size.width + 1, size.height + 1);
      g.DrawStringRect(this._statusLine, this._statusFont, this._statusFont.brush, statusPos, stringFormat);
    }
    let firstGraduation;
    let gratuationCount;
    let Angle, C, S, R1, R2;
    let outerCoef = this._graduationOuterRadiusSize / 100;
    for (let i = 0; i < this._zones.length; i++) {
      if (this._zones[i].visible) {
        if (this._zones[i].path == null) {
          let zmin = Math.max(this._min, Math.min(this._max, this._zones[i].min));
          let zmax = Math.max(this._min, Math.min(this._max, this._zones[i].max));
          if (zmax > zmin) {
            let zOuterCoef = this._zones[i].outerRadius / 100;
            let Angle1 = (Math.PI - AngleAperture) / 2 + AngleAperture * (zmin - this._min) / (this._max - this._min);
            let Angle2 = (Math.PI - AngleAperture) / 2 + AngleAperture * (zmax - this._min) / (this._max - this._min);
            let outterlength = (Angle2 - Angle1) * radius;
            let stepCount = outterlength / this.SegmentMaxLength >> 0;
            if (stepCount < 2)
              stepCount = 2;
            this._zones[i].setPathSize(2 * stepCount + 2);
            for (let j = 0; j <= stepCount; j++) {
              let A = Angle1 + (Angle2 - Angle1) * j / stepCount;
              this._zones[i].setPathPoint(j, new PointF(xcenter - radius * zOuterCoef * Math.cos(A), ycenter - radius * zOuterCoef * Math.sin(A)));
            }
            let innerRadiusCoef = zOuterCoef - this._zones[i].width / 100;
            for (let j = stepCount; j >= 0; j--) {
              let A = Angle1 + (Angle2 - Angle1) * j / stepCount;
              this._zones[i].setPathPoint(2 * stepCount + 1 - j, new PointF(xcenter - radius * innerRadiusCoef * Math.cos(A), ycenter - radius * innerRadiusCoef * Math.sin(A)));
            }
          }
        }
        if (this._zones[i].path != null)
          g.FillPolygon(this._zones[i].zoneBrush, this._zones[i].path);
      }
    }
    firstGraduation = this._graduation * (this._min / this._graduation) >> 0;
    if (this._min < 0)
      firstGraduation -= this._graduation;
    while (firstGraduation < this._min) {
      firstGraduation += this._graduation;
    }
    gratuationCount = 1 + (this._max - this._min) / this._graduation >> 0;
    if (this._subgraduationCount > 0 && this._subgraduationCount * gratuationCount < circonference) {
      let subgraduation = this._graduation / this._subgraduationCount;
      firstGraduation = subgraduation * (this._min / subgraduation >> 0);
      if (this._min < 0)
        firstGraduation -= subgraduation;
      while (firstGraduation < this._min) {
        firstGraduation += subgraduation;
      }
      gratuationCount = 1 + (this._max - this._min) / subgraduation >> 0;
      for (let i = 0; i < gratuationCount; i++) {
        let value = firstGraduation + i * subgraduation;
        if (value <= this._max) {
          Angle = (Math.PI - AngleAperture) / 2 + AngleAperture * (value - this._min) / (this._max - this._min);
          C = Math.cos(Angle);
          S = Math.sin(Angle);
          R1 = outerCoef * (radius - this._borderThickness / 2);
          R2 = (100 - this._subgraduationSize) * (outerCoef * (radius - this._borderThickness / 2)) / 100;
          g.DrawLineXY(this._subgraduationPen, xcenter - R1 * C, ycenter - R1 * S, xcenter - R2 * C, ycenter - R2 * S);
        }
      }
    }
    if (gratuationCount < circonference) {
      for (let i = 0; i < gratuationCount; i++) {
        let gvalue = firstGraduation + i * this._graduation;
        if (gvalue <= this._max) {
          Angle = (Math.PI - AngleAperture) / 2 + AngleAperture * (gvalue - this._min) / (this._max - this._min);
          C = Math.cos(Angle);
          S = Math.sin(Angle);
          R1 = outerCoef * (radius - this._borderThickness / 2);
          R2 = (100 - this._graduationSize) * (outerCoef * (radius - this._borderThickness / 2)) / 100;
          g.DrawLineXY(this._graduationPen, xcenter - R1 * C, ycenter - R1 * S, xcenter - R2 * C, ycenter - R2 * S);
          size = g.MeasureStringSF(gvalue.toString().trim(), this._graduationFont, 1e3, stringFormat4Sizing);
          let HalfDiagonal = 0.4 * Math.sqrt(size.width * size.width + size.height * size.height);
          let position = new YRectangle(xcenter - (R2 - HalfDiagonal) * C - size.width / 2 >> 0, ycenter - (R2 - HalfDiagonal) * S - size.height / 2 >> 0, (size.width >> 0) + 1, size.height >> 0);
          g.DrawStringRect(gvalue.toString(), this._graduationFont, this._graduationFont.brush, position, stringFormat);
        }
      }
    }
    if (this._borderThickness > 0 && this._path.length > 3)
      g.DrawPolygon(this._borderpen, this._path);
    this.drawAnnotationPanels(g, this._annotationPanels, w, h, true, mainViewPort);
    if (this._showNeedle) {
      if (this._needleValue != this._value) {
        let step = this._unitFactor * this._needleMaxSpeed * (this._max - this._min) / 100;
        if (Math.abs(this._value - this._needleValue) < step) {
          this._needleValue = this._value;
        } else if (this._needleValue < this.value) {
          this._needleValue += step;
        } else {
          this._needleValue -= step;
        }
      }
      let needlevalue = this._needleValue / this._unitFactor;
      let allowedOverflow = (this._max - this.min) * 0.05;
      if (needlevalue < this._min - allowedOverflow)
        needlevalue = this._min - allowedOverflow;
      if (needlevalue > this._max + allowedOverflow)
        needlevalue = this._max + allowedOverflow;
      Angle = (Math.PI - AngleAperture) / 2 + AngleAperture * (needlevalue - this._min) / (this._max - this._min);
      C = Math.cos(Angle);
      S = Math.sin(Angle);
      R1 = radius * this._needleLength1 / 100;
      R2 = radius * this._needleLength2 / 100;
      let R3 = radius * this._needleWidth / 200;
      let needlepath = new Array(4).fill(null);
      needlepath[0] = new PointF(xcenter - R1 * C, ycenter - R1 * S);
      needlepath[1] = new PointF(xcenter + R3 * S, ycenter - R3 * C);
      needlepath[2] = new PointF(xcenter + R2 * C, ycenter + R2 * S);
      needlepath[3] = new PointF(xcenter - R3 * S, ycenter + R3 * C);
      if (this._needleBrush == null)
        this._needleBrush = new YSolidBrush(this._needleColor);
      g.FillPolygon(this._needleBrush, needlepath);
      if (this._needleContourThickness > 0) {
        if (this._needleContourPen == null) {
          this._needleContourPen = new YPen(this._needleContourColor, this._needleContourThickness);
          this._needleContourPen.startCap = 2;
          this._needleContourPen.endCap = 2;
          this._needleContourPen.linejoin = YPen.LineJoin.Round;
        }
        let needlepath2 = new Array(5).fill(null);
        needlepath2[0] = needlepath[0];
        needlepath2[1] = needlepath[1];
        needlepath2[2] = needlepath[2];
        needlepath2[3] = needlepath[3];
        needlepath2[4] = needlepath[0];
        g.DrawLines(this._needleContourPen, needlepath2);
      }
    }
    this.DrawMessagePanels(g, w, h);
    return 0;
  }
  renderingPostProcessing() {
    if (this._needleValue != this._value)
      this.redraw();
  }
};

// obj/full/Renderer/YDigitalDisplay.js
var YDigitalDisplay = class _YDigitalDisplay extends YDataRenderer {
  get backgroundColor1() {
    return this._backgroundColor1;
  }
  set backgroundColor1(value) {
    this._backgroundColor1 = value;
    this._bgBrush = null;
    this.redraw();
  }
  get backgroundColor2() {
    return this._backgroundColor2;
  }
  set backgroundColor2(value) {
    this._backgroundColor2 = value;
    this._bgBrush = null;
    this.redraw();
  }
  get alternateValue() {
    return this._alternateValue;
  }
  set alternateValue(value) {
    this._alternateValue = value;
    this.redraw();
  }
  get valueFormater() {
    return this._valueFormater;
  }
  set valueFormater(value) {
    this._valueFormater = value;
    this.redraw();
  }
  get hrzAlignmentOfset() {
    return this._hrzAlignmentOfset;
  }
  set hrzAlignmentOfset(value) {
    this._hrzAlignmentOfset = value;
    this.redraw();
  }
  get hrzAlignment() {
    return this._hrzAlignment;
  }
  set hrzAlignment(value) {
    this._hrzAlignment = value;
    this.redraw();
  }
  get outOfRangeMin() {
    return this._outOfRangeMin;
  }
  set outOfRangeMin(value) {
    if (!Number.isNaN(value) && !Number.isNaN(this._outOfRangeMax) && !YDataRenderer.minMaxCheckDisabled) {
      if (value >= this._outOfRangeMax)
        throw new RangeError("Min cannot be greater than max (" + this._outOfRangeMax.toString() + ")");
    }
    this._outOfRangeMin = value;
    this.redraw();
  }
  get outOfRangeMax() {
    return this._outOfRangeMax;
  }
  set outOfRangeMax(value) {
    if (!Number.isNaN(value) && !Number.isNaN(this._outOfRangeMin) && !YDataRenderer.minMaxCheckDisabled) {
      if (value <= this._outOfRangeMin)
        throw new RangeError("Min cannot be less than max (" + this._outOfRangeMin.toString() + ")");
    }
    this._outOfRangeMax = value;
    this.redraw();
  }
  get outOfRangeColor() {
    return this._outOfRangeColor;
  }
  set outOfRangeColor(value) {
    this._outOfRangeColor = value;
    this.redraw();
  }
  get value() {
    return this._value;
  }
  set value(value) {
    this._value = value;
    this.redraw();
  }
  get font() {
    return this._font;
  }
  constructor(UIContainer, logFunction) {
    super(UIContainer, logFunction);
    this._bgBrush = null;
    this._backgroundColor1 = YColor.Black;
    this._backgroundColor2 = YColor.FromArgb(255, 48, 48, 48);
    this._alternateValue = null;
    this._valueFormater = null;
    this._hrzAlignmentOfset = 5;
    this._hrzAlignment = _YDigitalDisplay.HrzAlignment.DECIMAL;
    this._outOfRangeMin = Number.NaN;
    this._outOfRangeMax = Number.NaN;
    this._outOfRangeColor = YColor.Red;
    this._value = 0;
    this._font = new YFont(this, this, Math.min(UIContainer.width / 5, UIContainer.height / 2), null);
    this._font.color = YColor.LightGreen;
    this.resizeRule = Proportional.ResizeRule.RELATIVETOBOTH;
  }
  clearCachedObjects() {
    if (this.font != null)
      this.font.ResetFont(null);
    this._bgBrush = null;
  }
  Render(g, w, h) {
    let mainViewPort = new ViewPortSettings();
    mainViewPort.Lmargin = 0;
    mainViewPort.Rmargin = 0;
    mainViewPort.Tmargin = 0;
    mainViewPort.Bmargin = 0;
    g.SmoothingMode = YSmoothingMode.HighQuality;
    g.TextRenderingHint = YTextRenderingHint.AntiAlias;
    let stringFormat = new YStringFormat(
      16384
      /* YDataRendering.YStringFormat.StringFormatFlags.NoClip */
    );
    stringFormat.Alignment = 1;
    stringFormat.LineAlignment = 1;
    if (this._bgBrush == null) {
      this._bgBrush = new YLinearGradientBrush(this._backgroundColor1, this._backgroundColor2);
    }
    g.FillRectangleXYHW(this._bgBrush, 0, 0, w, h);
    this.drawAnnotationPanels(g, this._annotationPanels, w, h, false, mainViewPort);
    if (mainViewPort.Tmargin >= 20)
      mainViewPort.Tmargin -= 10;
    if (mainViewPort.Bmargin >= 20)
      mainViewPort.Bmargin -= 10;
    g.TextRenderingHint = YTextRenderingHint.AntiAlias;
    let availWidth = w - (mainViewPort.Lmargin + mainViewPort.Rmargin);
    let availHeight = h - (mainViewPort.Tmargin + mainViewPort.Bmargin);
    if (availWidth > 10 && availHeight > 10) {
      let svalue;
      if (this._alternateValue == null) {
        svalue = this._valueFormater == null ? this.value.toFixed(3) : this._valueFormater(this, this.value);
        if (!Number.isNaN(this._outOfRangeMin) && this.value < this._outOfRangeMin) {
          this.font.alternateColor = this._outOfRangeColor;
        } else if (!Number.isNaN(this._outOfRangeMax) && this.value > this._outOfRangeMax) {
          this.font.alternateColor = this._outOfRangeColor;
        } else {
          this.font.alternateColor = null;
        }
      } else {
        this._font.alternateColor = null;
        svalue = this._alternateValue;
      }
      let size = g.MeasureStringSF(svalue, this.font, 1e4, stringFormat);
      let pos;
      let align = this._hrzAlignment;
      if (this._alternateValue != null && align == _YDigitalDisplay.HrzAlignment.DECIMAL)
        align = _YDigitalDisplay.HrzAlignment.RIGHT;
      if (align == _YDigitalDisplay.HrzAlignment.DECIMAL && svalue.indexOf(".") < 0)
        align = _YDigitalDisplay.HrzAlignment.RIGHT;
      switch (align) {
        case _YDigitalDisplay.HrzAlignment.LEFT:
          pos = new YRectangle(mainViewPort.Lmargin + (availWidth * this.hrzAlignmentOfset / 100 >> 0), mainViewPort.Tmargin + (availHeight - size.height) / 2 >> 0, size.width + 1 >> 0, size.height + 1 >> 0);
          g.DrawStringRect(svalue, this.font, this.font.brush, pos, stringFormat);
          break;
        case _YDigitalDisplay.HrzAlignment.CENTER:
          pos = new YRectangle(mainViewPort.Lmargin + (availWidth - size.width) / 2 >> 0, mainViewPort.Tmargin + (availHeight - size.height) / 2 >> 0, size.width + 1 >> 0, size.height + 1 >> 0);
          g.DrawStringRect(svalue, this.font, this.font.brush, pos, stringFormat);
          break;
        case _YDigitalDisplay.HrzAlignment.DECIMAL:
          let left = "";
          let p = svalue.lastIndexOf(",");
          if (p < 0)
            p = svalue.lastIndexOf(".");
          if (p >= 0) {
            left = svalue.substring(0, p + 1);
          } else {
            p = 0;
            while (p < svalue.length && (svalue[p] >= "0" && svalue[p] <= "9" || svalue[p] == "-" || svalue[p] == "'" || svalue[p] == " ")) {
              p++;
            }
            left = svalue.substring(0, p);
          }
          let lsize = g.MeasureStringSF(left, this.font, 1e4, stringFormat);
          pos = new YRectangle(mainViewPort.Lmargin + (availWidth - lsize.width - availWidth * this.hrzAlignmentOfset / 100) >> 0, mainViewPort.Tmargin + (availHeight - size.height) / 2 >> 0, size.width + 1 >> 0, size.height + 1 >> 0);
          g.DrawStringRect(svalue, this.font, this.font.brush, pos, stringFormat);
          break;
        case _YDigitalDisplay.HrzAlignment.RIGHT:
          pos = new YRectangle(mainViewPort.Lmargin + (availWidth - size.width - availWidth * this.hrzAlignmentOfset / 100) >> 0, mainViewPort.Tmargin + (availHeight - size.height) / 2 >> 0, size.width + 1 >> 0, size.height + 1 >> 0);
          g.DrawStringRect(svalue, this.font, this.font.brush, pos, stringFormat);
          break;
      }
    }
    this.drawAnnotationPanels(g, this._annotationPanels, w, h, true, mainViewPort);
    this.DrawMessagePanels(g, w, h);
    return 0;
  }
};
(function(YDigitalDisplay2) {
  class HrzAlignmentEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, HrzAlignment);
    }
  }
  YDigitalDisplay2.HrzAlignmentEnumItem = HrzAlignmentEnumItem;
  class HrzAlignment extends YEnum {
  }
  HrzAlignment.LEFT = new HrzAlignmentEnumItem("LEFT", "Left");
  HrzAlignment.CENTER = new HrzAlignmentEnumItem("CENTER", "Center");
  HrzAlignment.DECIMAL = new HrzAlignmentEnumItem("DECIMAL", "Decimal");
  HrzAlignment.RIGHT = new HrzAlignmentEnumItem("RIGHT", "Right");
  YDigitalDisplay2.HrzAlignment = HrzAlignment;
})(YDigitalDisplay || (YDigitalDisplay = {}));

// obj/full/Renderer/YGraph.js
var SUMMARY_GRANULARITY = 100;
var pointXY = class _pointXY {
  constructor(X, Y) {
    this.x = X === void 0 ? 0 : X;
    this.y = Y === void 0 ? 0 : Y;
  }
  clone() {
    return new _pointXY(this.x, this.y);
  }
};
var pointsSummary = class _pointsSummary {
  constructor(X1, X2, YMIN, YMAX) {
    this.x1 = X1;
    this.x2 = X2;
    this.ymin = YMIN;
    this.ymax = YMAX;
  }
  clone() {
    return new _pointsSummary(this.x1, this.x2, this.ymin, this.ymin);
  }
};
var TimeConverterParseResult = class {
  constructor() {
    this.success = false;
    this.result = 0;
  }
};
var TimeResolution = class {
  constructor() {
    this.step = 0;
    this.format = 0;
  }
};
var YDate = class _YDate extends Date {
  ToString(format) {
    let res = "";
    let ampm = "";
    if (format & _YDate.D)
      res = res + this.getDate() + " ";
    if (format & _YDate.M)
      res = res + _YDate.months[this.getMonth()] + " ";
    if (format & _YDate.YY) {
      let y = this.getFullYear().toString();
      res = res + y.substr(y.length - 2) + " ";
    } else if (format & _YDate.YYYY) {
      let y = this.getFullYear().toString();
      res = res + y + " ";
    }
    if (format & _YDate.CR)
      res = res + "\n";
    if (format & _YDate.h) {
      if (_YDate.use24Hformat) {
        let h = "0" + this.getHours().toString();
        res = res + h.substring(h.length - 2);
        if (!(format & _YDate.m))
          res = res + "H";
      } else {
        let hour = this.getHours();
        ampm = "AM";
        if (hour > 11)
          ampm = "PM";
        if (hour > 12)
          hour = hour - 12;
        if (hour == 0)
          hour = 12;
        res = res + hour.toString();
      }
    }
    if (format & _YDate.m) {
      let m = "0" + this.getMinutes().toString();
      res = res + ":" + m.substring(m.length - 2);
    }
    if (format & _YDate.s) {
      let s = "0" + this.getSeconds().toString();
      res = res + ":" + s.substring(s.length - 2);
    }
    if (format & _YDate.ms1) {
      let ms = "00" + this.getMilliseconds().toString();
      res = res + "." + ms.substring(ms.length - 3).substring(1);
    } else if (format & _YDate.ms01) {
      let ms = "00" + this.getMilliseconds().toString();
      res = res + "." + ms.substring(ms.length - 3).substring(2);
    } else if (format & _YDate.ms001) {
      let ms = "00" + this.getMilliseconds().toString();
      res = res + "." + ms.substring(ms.length - 3).substring(3);
    }
    if (format & _YDate.h && !_YDate.use24Hformat) {
      res = res + ampm;
    }
    return res;
  }
};
YDate.D = 1;
YDate.DD = 2;
YDate.M = 4;
YDate.YY = 8;
YDate.h = 16;
YDate.m = 32;
YDate.s = 64;
YDate.ms1 = 128;
YDate.ms01 = 256;
YDate.ms001 = 512;
YDate.CR = 1024;
YDate.isRelative = 2048;
YDate.YYYY = 4096;
YDate.d = (/* @__PURE__ */ new Date()).toLocaleTimeString().toUpperCase();
YDate.use24Hformat = YDate.d.indexOf("AM") < 0 && YDate.d.indexOf("PM") < 0;
YDate.months = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"];
var TimeConverter = class _TimeConverter {
  static UTCNow() {
    return new YDate();
  }
  static ToUnixTime(datetime) {
    return datetime.getTime() / 1e3;
  }
  static FromUnixTime(unixtime) {
    let t = new YDate();
    t.setTime(unixtime * 1e3);
    return t;
  }
  static tryParseStringToAbsDateTime(str) {
    let res = new TimeConverterParseResult();
    res.success = false;
    let date = /* @__PURE__ */ new Date();
    let year = date.getFullYear();
    let month = date.getMonth();
    let day = date.getDate();
    let hours = 0;
    let minutes = 0;
    let seconds = 0;
    str = str.trim();
    while (str.indexOf("  ") > 0) {
      str = str.replace("  ", " ");
    }
    let dateFound = false;
    let timeFound = false;
    let it = str.split(" ");
    if (it.length <= 0)
      return res;
    for (let i = 0; i < it.length && i < 2; i++) {
      if (it[i].indexOf("-") > 0) {
        let tokens = it[i].split("-");
        if (tokens.length == 1) {
          day = parseInt(tokens[0]);
        } else if (tokens.length == 2) {
          day = parseInt(tokens[1]);
          month = parseInt(tokens[0]) - 1;
        } else {
          day = parseInt(tokens[2]);
          month = parseInt(tokens[1]) - 1;
          year = parseInt(tokens[0]);
        }
        dateFound = true;
      } else if (it[i].indexOf(":") > 0) {
        let tokens = it[i].split(":");
        if (tokens.length == 1) {
          hours = parseInt(tokens[0]);
        } else if (tokens.length == 2) {
          hours = parseInt(tokens[0]);
          minutes = parseInt(tokens[1]);
        } else {
          hours = parseInt(tokens[0]);
          minutes = parseInt(tokens[1]);
          seconds = parseFloat(tokens[2]);
        }
        timeFound = true;
      }
    }
    if (!timeFound && !dateFound)
      return res;
    if (isNaN(year) || isNaN(month) || isNaN(day) || isNaN(hours) || isNaN(minutes) || isNaN(seconds))
      return res;
    date.setFullYear(year, month, day);
    date.setHours(hours, minutes, seconds >> 0, 1e3 * (seconds % 1) >> 0);
    res.result = date.getTime() / 1e3;
    res.success = true;
    return res;
  }
  static tryParseStringToSecTimeSpan(st) {
    let res = new TimeConverterParseResult();
    let d = 0;
    st = st.toUpperCase();
    let n = st.indexOf("D");
    if (n > 0) {
      d = parseFloat(st.substring(0, n));
      if (isNaN(d)) {
        return res;
      }
      res.result += +d * 86400;
      if (n == st.length - 1) {
        res.success = true;
        return res;
      }
      st = st.substring(n + 1);
    }
    n = st.indexOf("H");
    if (n > 0) {
      d = parseFloat(st.substring(0, n));
      if (isNaN(d)) {
        return res;
      }
      res.result += d * 3600;
      if (n == st.length - 1) {
        res.success = true;
        return res;
      }
      st = st.substring(n + 1);
    }
    n = st.indexOf("M");
    if (n > 0) {
      d = parseFloat(st.substring(0, n));
      if (isNaN(d)) {
        return res;
      }
      res.result += d * 60;
      if (n == st.length - 1) {
        res.success = true;
        return res;
      }
      st = st.substring(n + 1);
    }
    n = st.indexOf("S");
    if (n < 0)
      n = st.length;
    d = parseFloat(st.substring(0, n));
    if (isNaN(d)) {
      return res;
    }
    res.result += d;
    res.success = true;
    return res;
  }
  //public static  secTimeSpanToString( timespan:number):string
  //  { return TimeConverter.secTimeSpanToString( timespan, 0); }
  static secTimeSpanToString(timespan, resolution) {
    let started = false;
    let res = "";
    if (timespan < 0) {
      res = "-";
      timespan = -timespan;
    }
    if (timespan >= 86400) {
      let d = timespan / 86400 >> 0;
      res = res + d.toString() + "d";
      timespan -= 86400 * d;
      started = true;
    }
    if (resolution >= 86400)
      return res != "" ? res : "0d";
    if (timespan >= 3600) {
      let d = timespan / 3600 >> 0;
      let ds = d.toString();
      if (started && ds.length == 1)
        ds = "0" + ds;
      res = res + ds + "h";
      timespan -= 3600 * d;
      started = true;
    }
    if (resolution >= 3600)
      return res != "" ? res : "0h";
    if (timespan < resolution)
      return res != "" ? res : "0h";
    if (timespan >= 60) {
      let d = timespan / 60 >> 0;
      let ds = d.toString();
      if (started && ds.length == 1)
        ds = "0" + ds;
      res = res + ds + "m";
      timespan -= 60 * d;
      started = true;
    }
    if (resolution >= 60)
      return res != "" ? res : "0m";
    if (timespan < resolution)
      return res != "" ? res : "0m";
    timespan = Math.round(timespan * 100) / 100;
    let s;
    if (resolution > 0.1) {
      s = timespan.toFixed(0);
    } else if (resolution > 0.01) {
      s = timespan.toFixed(1);
    } else if (resolution > 1e-3) {
      s = timespan.toFixed(2);
    } else {
      s = timespan.toString();
    }
    if (started && timespan < 10)
      s = "0" + s;
    res = res + s + "s";
    return res;
  }
  static RelativeFormat(dataDeltaTime, viewportDeltaTime, resolution) {
    let ShowSecondsTenth = true;
    let ShowSecondsHundredth = true;
    let ShowSeconds = true;
    let ShowMinutes = false;
    let ShowHours = false;
    let ShowDays = false;
    if (dataDeltaTime <= 0.1) {
      ShowSecondsHundredth = true;
    }
    if (dataDeltaTime <= 1) {
      ShowSecondsTenth = true;
    }
    if (dataDeltaTime >= 60 || viewportDeltaTime >= 60) {
      ShowMinutes = true;
    }
    if (dataDeltaTime >= 3600 || viewportDeltaTime >= 3600) {
      ShowHours = true;
    }
    if (dataDeltaTime >= 86400 || viewportDeltaTime >= 86400) {
      ShowDays = true;
    }
    if (resolution >= 0.1)
      ShowSecondsHundredth = false;
    if (resolution >= 1)
      ShowSecondsTenth = false;
    if (resolution >= 60)
      ShowSeconds = false;
    if (resolution >= 3600)
      ShowMinutes = false;
    if (resolution >= 86400)
      ShowHours = false;
    let format = 0;
    format |= YDate.isRelative;
    if (ShowSecondsTenth)
      format |= YDate.ms1;
    if (ShowSecondsHundredth)
      format |= YDate.ms01;
    if (ShowSeconds)
      format |= YDate.s;
    if (ShowMinutes)
      format |= YDate.m;
    if (ShowHours)
      format |= YDate.h;
    if (ShowDays)
      format |= YDate.D;
    return format;
  }
  static BestTimeformat(dataDeltaTime, viewportDeltaTime, tref) {
    let res = new TimeResolution();
    let ShowSecondsTenth = true;
    let ShowSecondsHundredth = true;
    let ShowSeconds = true;
    let ShowMinutes = false;
    let ShowHours = false;
    let ShowDays = false;
    let ShowMonths = false;
    let ShowYears = false;
    if (viewportDeltaTime <= 0.1) {
      res.step = 0.01;
    } else if (viewportDeltaTime <= 1) {
      res.step = 0.1;
    } else if (viewportDeltaTime <= 2) {
      res.step = 0.2;
    } else if (viewportDeltaTime <= 5) {
      res.step = 0.5;
    } else if (viewportDeltaTime <= 10) {
      res.step = 1;
    } else if (viewportDeltaTime <= 20) {
      res.step = 2;
    } else if (viewportDeltaTime <= 30) {
      res.step = 3;
    } else if (viewportDeltaTime <= 40) {
      res.step = 4;
    } else if (viewportDeltaTime <= 60) {
      res.step = 5;
    } else if (viewportDeltaTime <= 120) {
      res.step = 10;
    } else if (viewportDeltaTime <= 300) {
      res.step = 30;
    } else if (viewportDeltaTime <= 900) {
      res.step = 60;
    } else if (viewportDeltaTime <= 1800) {
      res.step = 180;
    } else if (viewportDeltaTime <= 3600) {
      res.step = 300;
    } else if (viewportDeltaTime <= 7200) {
      res.step = 600;
    } else if (viewportDeltaTime <= 14e3) {
      res.step = 900;
    } else if (viewportDeltaTime <= 21600) {
      res.step = 1800;
    } else if (viewportDeltaTime <= 43200) {
      res.step = 3600;
    } else if (viewportDeltaTime <= 86400) {
      res.step = 7200;
    } else if (viewportDeltaTime <= 2 * 86400) {
      res.step = 2 * 7200;
    } else if (viewportDeltaTime <= 4 * 86400) {
      res.step = 4 * 7200;
    } else if (viewportDeltaTime <= 7 * 86400) {
      res.step = 86400;
    } else if (viewportDeltaTime <= 14 * 86400) {
      res.step = 2 * 86400;
    } else if (viewportDeltaTime <= 28 * 86400) {
      res.step = 4 * 86400;
    } else if (viewportDeltaTime <= 56 * 86400) {
      res.step = 7 * 86400;
    } else if (viewportDeltaTime <= 112 * 86400) {
      res.step = 14 * 86400;
    } else if (viewportDeltaTime <= 224 * 86400) {
      res.step = 31 * 86400;
    } else if (viewportDeltaTime <= 448 * 86400) {
      res.step = 62 * 86400;
    } else if (viewportDeltaTime <= 896 * 86400) {
      res.step = 93 * 86400;
    } else {
      res.step = 365 * 86400;
    }
    if (tref == _TimeConverter.TimeReference.ABSOLUTE) {
      ShowSecondsHundredth = true;
      ShowSecondsTenth = true;
      ShowMinutes = true;
      ShowHours = true;
      ShowDays = dataDeltaTime > 86400;
      ShowMonths = dataDeltaTime > 86400;
      ShowYears = dataDeltaTime > 28 * 6 * 86400;
      if (res.step >= 0.1)
        ShowSecondsHundredth = false;
      if (res.step >= 1)
        ShowSecondsTenth = false;
      if (res.step >= 60)
        ShowSeconds = false;
      if (res.step >= 3600)
        ShowMinutes = false;
      if (res.step >= 86400)
        ShowHours = false;
      if (res.step >= 31 * 86400)
        ShowDays = false;
      if (res.step >= 365 * 86400)
        ShowMonths = false;
      res.format = 0;
      if (ShowSecondsHundredth)
        res.format |= YDate.ms01;
      if (ShowSecondsTenth)
        res.format |= YDate.ms1;
      if (ShowSeconds)
        res.format |= YDate.s;
      if (ShowMinutes)
        res.format = res.format |= YDate.m;
      if (ShowHours)
        res.format = res.format |= YDate.h;
      if (res.format != 0 && (ShowDays || ShowMonths))
        res.format |= YDate.CR;
      if (ShowDays)
        res.format |= YDate.D;
      if (ShowMonths)
        res.format |= YDate.M;
      if (ShowYears)
        res.format |= YDate.YY;
      if (res.format == YDate.YY)
        res.format = YDate.YYYY;
    } else {
      res.format = _TimeConverter.RelativeFormat(dataDeltaTime, viewportDeltaTime, res.step);
    }
    return res;
  }
};
(function(TimeConverter2) {
  class TimeReferenceEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, TimeReference);
    }
  }
  TimeConverter2.TimeReferenceEnumItem = TimeReferenceEnumItem;
  class TimeReference extends YEnum {
  }
  TimeReference.ABSOLUTE = new TimeReferenceEnumItem("ABSOLUTE", "Absolute");
  TimeReference.RELATIVE = new TimeReferenceEnumItem("RELATIVE", "Relative to first data");
  TimeConverter2.TimeReference = TimeReference;
})(TimeConverter || (TimeConverter = {}));
var MinMax = class {
  constructor(minimum, maximum) {
    this.Min = 0;
    this.Max = 0;
    this.Min = minimum;
    this.Max = maximum;
  }
};
var MinMaxHandler = class {
  static extend(M, factor) {
    if (isNaN(M.Min))
      return M;
    let delta = M.Max - M.Min;
    return new MinMax(M.Min - delta * (factor - 1) / 2, M.Max + delta * (factor - 1) / 2);
  }
  static DefaultValue(value1, value2) {
    if (typeof value2 === "undefined") {
      if (typeof value1 === "undefined")
        return new MinMax(Number.NaN, Number.NaN);
      return new MinMax(value1, value1);
    }
    if (typeof value1 === "undefined")
      return new MinMax(value2, value2);
    if (value2 < value1)
      throw new RangeError("MinMax invalid parameters (" + value1.toString() + ">" + value2.toString());
    return new MinMax(value1, value2);
  }
  static isDefined(v) {
    return !isNaN(v.Min);
  }
  static Combine(M1, M2) {
    if (isNaN(M1.Min))
      return new MinMax(M2.Min, M2.Max);
    if (isNaN(M2.Min))
      return new MinMax(M1.Min, M1.Max);
    let res = new MinMax(M2.Min, M2.Max);
    if (M1.Min < res.Min)
      res.Min = M1.Min;
    if (M1.Max < res.Min)
      res.Min = M1.Max;
    if (M1.Min > res.Max)
      res.Max = M1.Min;
    if (M1.Max > res.Max)
      res.Max = M1.Max;
    return res;
  }
  static CombineWithNumber(M1, value) {
    if (isNaN(M1.Min))
      return new MinMax(value, value);
    if (value < M1.Min)
      return new MinMax(value, M1.Max);
    if (value > M1.Max)
      return new MinMax(M1.Min, value);
    return new MinMax(M1.Min, M1.Max);
  }
};
var DataSegment = class _DataSegment {
  static ArrayCopy(sourceArray, sourceIndex, destinationArray, destinationIndex, length) {
    for (let i = 0; i < length; i++) {
      destinationArray[destinationIndex + i] = sourceArray[sourceIndex + i].clone();
    }
  }
  constructor(p) {
    this.data = [];
    this.count = 0;
    if (p instanceof Array) {
      this.data = new Array(p.length);
      _DataSegment.ArrayCopy(p, 0, this.data, 0, p.length);
      this.count = p.length;
    } else if (p instanceof pointXY) {
      this.data = new Array(_DataSegment.SegmentGranularity);
      this.data[0] = p;
      this.count = 1;
    } else {
      throw new Error("invalid constructor paramter type");
    }
  }
  grow() {
    let targetCount = this.data.length + _DataSegment.SegmentGranularity;
    while (this.data.length < targetCount) {
      this.data.push(null);
    }
  }
};
DataSegment.SegmentGranularity = 1e3;
var Summary = class _Summary {
  getBufferpoint() {
    if (this.vtlPtCount <= 0)
      return null;
    return new pointsSummary(this.Xmin, this.Xmax, this.Ymin, this.Ymax);
  }
  Dump() {
    var _a, _b, _c, _d;
    console.log("Summary level" + this.selfIndex + ", " + this.segments.length + " segments, " + this.totalpoints + " points");
    let lastx = 0;
    for (let s = 0; s < this.segments.length; s++) {
      console.log("  segment " + s);
      for (let i = 0; i < this.segments[s].ptCount; i++) {
        console.log(((_a = this.segments[s].data[i]) === null || _a === void 0 ? void 0 : _a.x1) + "," + ((_b = this.segments[s].data[i]) === null || _b === void 0 ? void 0 : _b.ymin) + " delataX=" + Math.round(this.segments[s].data[i].x1 - lastx));
        console.log(((_c = this.segments[s].data[i]) === null || _c === void 0 ? void 0 : _c.x2) + "," + ((_d = this.segments[s].data[i]) === null || _d === void 0 ? void 0 : _d.ymax) + " delataX=" + Math.round(this.segments[s].data[i].x2 - this.segments[s].data[i].x1));
        lastx = this.segments[s].data[i].x2;
      }
    }
  }
  constructor(index, array, pointSize) {
    this.segments = [];
    this.totalpoints = 0;
    this.Xmin = 0;
    this.Xmax = 0;
    this.Ymin = 0;
    this.Ymax = 0;
    this.vtlPtCount = 0;
    this.pointSize = 0;
    this.isLast = false;
    this.newSummaryLevelTrigger = 0;
    this.selfIndex = index;
    this.selfArray = array;
    this.pointSize = pointSize;
    this.isLast = true;
  }
  processSegments(segments) {
    if (segments.length <= 0)
      return;
    let last = segments.length - 1;
    this.Xmin = segments[last].data[0].x;
    this.Xmax = segments[last].data[0].x;
    this.Ymin = segments[last].data[0].y;
    this.Ymax = segments[last].data[0].y;
    this.totalpoints = 0;
    this.vtlPtCount = 0;
    for (let s = last; s >= 0; s--) {
      for (let i = 0; i < segments[s].data.length; i++) {
        let p = segments[s].data[i];
        if (p.x < this.Ymin)
          this.Ymin = p.y;
        if (p.x > this.Ymax)
          this.Ymax = p.y;
        this.Xmax = p.x;
        this.vtlPtCount++;
        if (this.vtlPtCount >= this.pointSize) {
          this.flush(false);
          this.Xmin = p.x;
          this.Ymin = p.y;
          this.Ymax = p.y;
        }
      }
      if (s > 0) {
        let len = segments[s].data.length;
        let p2 = segments[s - 1].data[0];
        let p1 = segments[s].data[len - 1];
        let lastDelta = 1;
        if (len > 1) {
          let p0 = segments[s].data[len - 2];
          lastDelta = p1.x - p0.x;
        }
        if (p2.x - p1.x > this.pointSize * lastDelta) {
          this.flush(false);
          this.Xmin = p2.x;
          this.Ymin = p2.y;
          this.Ymax = p2.y;
          this.addNewEmptySegment();
        }
      }
    }
    if (this.totalpoints > 1) {
      let it = new _Summary(this.selfIndex + 1, this.selfArray, this.pointSize * SUMMARY_GRANULARITY);
      this.selfArray.push(it);
      this.isLast = false;
      it.processSegments(segments);
    }
  }
  addNewLevel() {
    let newIndex = this.selfArray.length;
    if (_Summary.DBG)
      console.log("--- ADDING NEW LEVEL " + newIndex);
    let it = new _Summary(newIndex, this.selfArray, this.pointSize * SUMMARY_GRANULARITY);
    this.selfArray.push(it);
    this.isLast = false;
    let _xmin = this.segments[0].data[0].x1;
    let _xmax = this.segments[0].data[0].x2;
    let _ymin = this.segments[0].data[0].ymin;
    let _ymax = this.segments[0].data[0].ymax;
    let _count = 0;
    for (let s = 0; s < this.segments.length; s++) {
      for (let i = 0; i < this.segments[s].ptCount; i++) {
        _xmax = this.segments[s].data[i].x2;
        if (this.segments[s].data[i].ymin < _ymin)
          _ymin = this.segments[s].data[i].ymin;
        if (this.segments[s].data[i].ymin > _ymax)
          _ymax = this.segments[s].data[i].ymin;
      }
      _count += this.segments[s].ptCount;
      if (s < this.segments.length - 1) {
        if (this.segments[s].ptCount > 2) {
          let lastIndex = this.segments[s].ptCount - 1;
          let lastDelta = this.segments[s].data[lastIndex].x1 - this.segments[s].data[lastIndex - 1].x2;
          if (this.segments[s + 1].data[0].x1 - this.segments[s].data[this.segments[s].ptCount - 1].x2 > lastDelta * SUMMARY_GRANULARITY) {
            it.addNewEmptySegment();
            _xmin = this.segments[s + 1].data[0].x1;
            _xmax = this.segments[s + 1].data[0].x2;
            _ymin = this.segments[s + 1].data[0].ymin;
            _ymax = this.segments[s + 1].data[0].ymax;
            _count = 0;
          }
        }
      }
    }
    if (_count != 0) {
      it.addNewSinglePointSegment(_xmin, _xmax, _ymin, _ymax, _count);
    }
  }
  addNewSinglePointSegment(xmin, xmax, ymin, ymax, _count) {
    if (_Summary.DBG)
      console.log(" ===> level " + this.selfIndex + " new single point segment");
    this.segments.push(new DataSummarySegment(new pointsSummary(xmin, xmax, ymin, ymax), _count));
  }
  addNewEmptySegment() {
    if (_Summary.DBG)
      console.log(" ===> level " + this.selfIndex + " new empty segment");
    this.segments.push(new DataSummarySegment(null, null));
  }
  addSequentialPoint(p, maxHoleSize) {
    if (_Summary.DBG)
      console.log(" ADD Point, LEVEL " + this.selfIndex + " Vtl point = " + this.vtlPtCount + "/" + this.pointSize + " TOTAL Points= " + this.totalpoints);
    if (this.isLast && this.totalpoints >= SUMMARY_GRANULARITY)
      this.addNewLevel();
    if (this.vtlPtCount == 0) {
      this.Xmin = p.x;
      this.Xmax = p.x;
      this.Ymin = p.y;
      this.Ymax = p.y;
      this.vtlPtCount = 1;
      if (_Summary.DBG) {
        if (this.selfIndex == 1)
          console.log(" --> added 1srt virtual pt vtlPtCount=" + this.vtlPtCount + "/" + this.pointSize);
      }
      if (!this.isLast)
        this.selfArray[this.selfIndex + 1].addSequentialPoint(p, maxHoleSize * SUMMARY_GRANULARITY);
      return;
    } else {
      if (_Summary.DBG)
        console.log(" --> level " + this.selfIndex + " delta = " + (p.x - this.Xmax) + "/" + maxHoleSize);
      if (maxHoleSize > 0 && p.x - this.Xmax > maxHoleSize) {
        if (_Summary.DBG)
          console.log(" --> level " + this.selfIndex + " hole detected");
        this.flush(false);
        this.addNewEmptySegment();
        this.Xmin = p.x;
        this.Xmax = p.x;
        this.Ymin = p.y;
        this.Ymax = p.y;
        this.vtlPtCount = 1;
        if (!this.isLast)
          this.selfArray[this.selfIndex + 1].addSequentialPoint(p, maxHoleSize * SUMMARY_GRANULARITY);
        return;
      }
      if (p.y < this.Ymin)
        this.Ymin = p.y;
      if (p.y > this.Ymax)
        this.Ymax = p.y;
      this.Xmax = p.x;
      this.vtlPtCount++;
      if (_Summary.DBG) {
        if (this.selfIndex == 1)
          console.log(" --> level " + this.selfIndex + " added subsequent virtual pt vtlPtCount=" + this.vtlPtCount + "/" + this.pointSize);
      }
      if (this.vtlPtCount >= this.pointSize) {
        if (_Summary.DBG) {
          if (this.selfIndex == 1)
            console.log(" --> level " + this.selfIndex + " flushing");
        }
        this.flush(false);
      }
    }
    if (!this.isLast)
      this.selfArray[this.selfIndex + 1].addSequentialPoint(p, maxHoleSize * SUMMARY_GRANULARITY);
  }
  flush(forceNewSegment) {
    if (this.segments.length == 0 || forceNewSegment)
      this.segments.push(new DataSummarySegment(null, null));
    let lastsegment = this.segments[this.segments.length - 1];
    lastsegment.data[lastsegment.ptCount] = new pointsSummary(this.Xmin, this.Xmax, this.Ymin, this.Ymax);
    this.totalpoints++;
    lastsegment.ptCount++;
    this.vtlPtCount = 0;
  }
};
Summary.DBG = false;
var DataSummarySegment = class _DataSummarySegment {
  static ArrayCopy(sourceArray, sourceIndex, destinationArray, destinationIndex, length) {
    for (let i = 0; i < length; i++) {
      destinationArray[destinationIndex + i] = sourceArray[sourceIndex + i].clone();
    }
  }
  constructor(p, virtualPointCount) {
    this.data = [];
    this.ptCount = 0;
    if (p instanceof Array) {
      this.data = new Array(p.length);
      _DataSummarySegment.ArrayCopy(p, 0, this.data, 0, p.length);
      this.ptCount = p.length;
    } else if (p instanceof pointsSummary) {
      this.data = new Array(_DataSummarySegment.SegmentGranularity);
      this.data[0] = p;
      this.ptCount = 1;
    } else if (p == null) {
      this.data = new Array(_DataSummarySegment.SegmentGranularity);
      this.ptCount = 0;
    }
  }
  grow() {
    let targetCount = this.data.length + _DataSummarySegment.SegmentGranularity;
    while (this.data.length < targetCount) {
      this.data.push(null);
    }
  }
};
DataSummarySegment.SegmentGranularity = 1e3;
var DataSerie = class _DataSerie {
  get userData() {
    return this._userData;
  }
  set userData(value) {
    this._userData = value;
  }
  static get MaxPointsPerSeries() {
    return _DataSerie._MaxPointsPerSeries;
  }
  static set MaxPointsPerSeries(value) {
    _DataSerie._MaxPointsPerSeries = value;
  }
  get timeRange() {
    return this._timeRange;
  }
  get valueRange() {
    return this._valueRange;
  }
  constructor(parent) {
    this.totalPointCount = 0;
    this._userData = null;
    this._yAxisIndex = 0;
    this._pen = null;
    this._legendPen = null;
    this._brush = null;
    this._navigatorpen = null;
    this._visible = true;
    this._disabled = false;
    this._color = YColor.Black;
    this._thickness = 1;
    this._legend = "";
    this._unit = "";
    this.segments = [];
    this.summaries = null;
    if (parent.yAxes.length <= 0)
      throw new Error("Define at least one yAxis");
    this._timeRange = MinMaxHandler.DefaultValue();
    this._valueRange = MinMaxHandler.DefaultValue();
    this.parent = parent;
  }
  get yAxisIndex() {
    return this._yAxisIndex;
  }
  set yAxisIndex(value) {
    if (value >= this.parent.yAxes.length)
      throw new RangeError("No such yAxis (" + value.toString() + ")");
    this._yAxisIndex = value;
    this.parent.yAxes[this._yAxisIndex].AutoShow();
  }
  get pen() {
    if (this._pen == null) {
      this._pen = new YPen(this._color, this._thickness);
      this._pen.endCap = 2;
      this._pen.linejoin = YPen.LineJoin.Round;
    }
    return this._pen;
  }
  get legendPen() {
    if (this._legendPen == null) {
      this._legendPen = new YPen(this._color, this._thickness * this.parent.legendPanel.traceWidthFactor);
    }
    return this._legendPen;
  }
  resetlegendPen() {
    this._legendPen = null;
  }
  get brush() {
    if (this._brush == null)
      this._brush = new YSolidBrush(this._color);
    return this._brush;
  }
  get navigatorpen() {
    if (this._navigatorpen == null) {
      this._navigatorpen = new YPen(YColor.FromArgb(100, this._color.red, this._color.green, this._color.blue), 1);
    }
    return this._navigatorpen;
  }
  get visible() {
    return this._visible;
  }
  set visible(value) {
    this._visible = value;
    this.parent.redraw();
  }
  get disabled() {
    return this._disabled;
  }
  set disabled(value) {
    this._disabled = value;
    this.parent.redraw();
  }
  get color() {
    return this._color;
  }
  set color(value) {
    this._color = value;
    this._pen = null;
    this._legendPen = null;
    this._brush = null;
    this._navigatorpen = null;
    this.parent.redraw();
  }
  get thickness() {
    return this._thickness;
  }
  set thickness(value) {
    if (value < 0)
      throw new RangeError("Thickness must be a positive value");
    this._thickness = value;
    this._pen = null;
    this._legendPen = null;
    this.parent.redraw();
  }
  get legend() {
    return this._legend;
  }
  set legend(value) {
    this._legend = value;
    this.parent.redraw();
  }
  get unit() {
    return this._unit;
  }
  set unit(value) {
    this._unit = value;
    this.parent.redraw();
  }
  AddNewSegment(p) {
    this.segments.splice(0, 0, new DataSegment(p));
  }
  getlastPoint() {
    if (this.segments.length <= 0) {
      return new pointXY(NaN, NaN);
    }
    return this.segments[this.segments.length - 1].data[this.segments[this.segments.length - 1].count - 1];
  }
  handleSummary(p, delta) {
    if (this.summaries == null) {
      this.summaries = [];
      this.summaries.push(new Summary(0, this.summaries, SUMMARY_GRANULARITY));
    }
    if (this.summaries != null)
      this.summaries[0].addSequentialPoint(p, delta);
  }
  AddPoint(p) {
    this._timeRange = MinMaxHandler.CombineWithNumber(this._timeRange, p.x);
    this._valueRange = MinMaxHandler.CombineWithNumber(this._valueRange, p.y);
    let delta1 = -1;
    let delta2 = -1;
    if (this.segments.length <= 0) {
      this.AddNewSegment(p);
      this.totalPointCount++;
      if (SUMMARY_GRANULARITY > 0)
        this.handleSummary(p, delta1);
      return;
    } else if (this.segments[0].count > 1) {
      delta1 = this.segments[0].data[this.segments[0].count - 1].x - this.segments[0].data[this.segments[0].count - 2].x;
      delta2 = p.x - this.segments[0].data[this.segments[0].count - 1].x;
      if (delta2 > 0.1 && (delta2 < 0 || delta2 > 2 * delta1)) {
        this.AddNewSegment(p);
        if (SUMMARY_GRANULARITY > 0)
          this.handleSummary(p, delta1);
        return;
      }
    }
    if (SUMMARY_GRANULARITY > 0)
      this.handleSummary(p, delta1);
    this.segments[0].data[this.segments[0].count] = p;
    this.segments[0].count++;
    this.totalPointCount++;
    if (_DataSerie._MaxPointsPerSeries > 0 && this.totalPointCount > _DataSerie._MaxPointsPerSeries) {
      this.dataCleanUp();
      this.rebuildSummaries();
    }
    this.parent.adjustGlobalTimeRange(p.x);
    this.parent.redraw();
  }
  rebuildSummaries() {
    if (SUMMARY_GRANULARITY <= 0)
      return;
    this.summaries = [];
    this.summaries.push(new Summary(0, this.summaries, SUMMARY_GRANULARITY));
    this.summaries[0].processSegments(this.segments);
  }
  dumpSummaries() {
    if (this.summaries != null)
      for (let i = 0; i < this.summaries.length; i++)
        this.summaries[i].Dump();
    else
      console.log("****No summaries ****");
  }
  dataCleanUp() {
    if (this.segments.length <= 0)
      return;
    let newLimit = _DataSerie._MaxPointsPerSeries * 90 / 100;
    while (this.segments[this.segments.length - 1].count <= this.totalPointCount - newLimit) {
      this.totalPointCount -= this.segments[this.segments.length - 1].count;
      this.segments.splice(this.segments.length - 1, 1);
    }
    if (this.totalPointCount > newLimit) {
      let delta = this.totalPointCount - newLimit;
      let newsize = this.segments[this.segments.length - 1].count - delta;
      let newdata = new Array(newsize);
      DataSegment.ArrayCopy(this.segments[this.segments.length - 1].data, delta, newdata, 0, this.segments[this.segments.length - 1].count - delta);
      this.segments[this.segments.length - 1].data = newdata;
      this.segments[this.segments.length - 1].count -= delta;
      this.totalPointCount -= delta;
    }
    let tmin = this.segments[0].data[0].x;
    let tmax = this.segments[0].data[0].x;
    let ymin = this.segments[0].data[0].y;
    let ymax = this.segments[0].data[0].y;
    for (let i = 0; i < this.segments.length; i++) {
      let count = this.segments[i].count;
      if (tmin > this.segments[i].data[0].x)
        tmin = this.segments[i].data[0].x;
      if (tmax < this.segments[i].data[count - 1].x)
        tmax = this.segments[i].data[count - 1].x;
      for (let j = 0; j < count; j++) {
        if (ymin > this.segments[i].data[j].y)
          ymin = this.segments[i].data[j].y;
        if (ymax < this.segments[i].data[j].y)
          ymax = this.segments[i].data[j].y;
      }
    }
    this._timeRange.Min = tmin;
    this._timeRange.Max = tmax;
    this._valueRange.Min = ymin;
    this._valueRange.Max = ymax;
  }
  InsertPoints(points) {
    if (points.length == 0)
      return;
    if (points.length == 1) {
      this._timeRange = MinMaxHandler.CombineWithNumber(this._timeRange, points[0].x);
      this._valueRange = MinMaxHandler.CombineWithNumber(this._valueRange, points[0].y);
      return;
    }
    let FirstStep = points[1].x - points[0].x;
    let LastStep = points[points.length - 1].x - points[points.length - 2].x;
    let InsertAtBegining = -1;
    let InsertAtEnd = -1;
    for (let i = 0; i < this.segments.length; i++) {
      if (this.segments[i].count > 1) {
        let DeltaInsertAtBegining = this.segments[i].data[0].x - points[points.length - 1].x;
        let DeltaInsertAtEnd = points[0].x - this.segments[i].data[this.segments[i].count - 1].x;
        if (DeltaInsertAtBegining > 0 && DeltaInsertAtBegining < 2 * FirstStep)
          InsertAtBegining = i;
        if (DeltaInsertAtEnd > 0 && DeltaInsertAtEnd < 2 * LastStep)
          InsertAtEnd = i;
      }
    }
    if (InsertAtBegining >= 0) {
      DataSegment.ArrayCopy(this.segments[InsertAtBegining].data, 0, this.segments[InsertAtBegining].data, points.length, this.segments[InsertAtBegining].count);
      DataSegment.ArrayCopy(points, 0, this.segments[InsertAtBegining].data, 0, points.length);
      this.segments[InsertAtBegining].count += points.length;
      this.totalPointCount += points.length;
    } else if (InsertAtEnd >= 0) {
      DataSegment.ArrayCopy(points, 0, this.segments[InsertAtEnd].data, this.segments[InsertAtEnd].count, points.length);
      this.segments[InsertAtEnd].count += points.length;
      this.totalPointCount += points.length;
    } else {
      let inserted = false;
      for (let i = 0; i < this.segments.length; i++) {
        if (this.segments[i].data[this.segments[i].data.length - 1].x < points[0].x && !inserted) {
          this.segments.splice(i, 0, new DataSegment(points));
          inserted = true;
        }
      }
      if (!inserted)
        this.segments.push(new DataSegment(points));
      this.totalPointCount += points.length;
    }
    this._timeRange = MinMaxHandler.CombineWithNumber(this._timeRange, points[0].x);
    this._timeRange = MinMaxHandler.CombineWithNumber(this._timeRange, points[points.length - 1].x);
    for (let i = 0; i < points.length; i++) {
      this._valueRange = MinMaxHandler.CombineWithNumber(this._valueRange, points[i].y);
    }
    if (_DataSerie._MaxPointsPerSeries > 0 && this.totalPointCount > _DataSerie._MaxPointsPerSeries)
      this.dataCleanUp();
    this.parent.redraw();
  }
  static CompareSegments(a, b) {
    if (a.data[0].x > b.data[0].x)
      return -1;
    if (a.data[0].x < b.data[0].x)
      return 1;
    return 0;
  }
  getData() {
    let res = [];
    this.segments.sort(_DataSerie.CompareSegments);
    for (let i = this.segments.length - 1; i >= 0; i--) {
      for (let j = 0; j < this.segments[i].count; j++) {
        res.push(this.segments[i].data[j]);
      }
    }
    return res;
  }
  findClosestValue(x, AllowInterpolation) {
    let N1 = 0;
    let N2 = 0;
    let Pos = 0;
    if (this.segments.length <= 0)
      return null;
    for (let i = 0; i < this.segments.length; i++) {
      if (x >= this.segments[i].data[0].x && x <= this.segments[i].data[this.segments[i].count - 1].x) {
        let data = this.segments[i].data;
        N1 = 0;
        N2 = this.segments[i].count - 1;
        while (N2 - N1 > 1) {
          let N = N1 + N2 >> 1;
          if (data[N].x > x)
            N2 = N;
          else
            N1 = N;
        }
        Pos = N1 - 1;
        if (Pos < 0)
          Pos = 0;
        if (!AllowInterpolation) {
          if (x - data[Pos].x < data[Pos + 1].x - x)
            return data[Pos];
          else
            return data[Pos + 1];
        } else {
          Pos++;
          if (x == data[Pos].x)
            return data[Pos];
          if (x == data[Pos + 1].x)
            return data[Pos + 1];
          let p = new pointXY();
          p.x = x;
          p.y = data[Pos].y + (data[Pos + 1].y - data[Pos].y) * (x - data[Pos].x) / (data[Pos + 1].x - data[Pos].x);
          return p;
        }
      }
    }
    if (AllowInterpolation)
      return null;
    try {
      this.segments[0].data[0].clone();
    } catch (e) {
      debugger;
    }
    let match = this.segments[0].data[0];
    let delta = Math.abs(this.segments[0].data[0].x - x);
    for (let i = 0; i < this.segments.length; i++) {
      let d1 = Math.abs(this.segments[i].data[0].x - x);
      let d2 = Math.abs(this.segments[i].data[this.segments[i].count - 1].x - x);
      if (d1 < delta) {
        match = this.segments[i].data[0];
        delta = d1;
      }
      if (d2 < delta) {
        match = this.segments[i].data[this.segments[i].count - 1];
        delta = d2;
      }
    }
    return match.clone();
  }
  clear() {
    this.segments = [];
    this._timeRange = MinMaxHandler.DefaultValue();
    this._valueRange = MinMaxHandler.DefaultValue();
    this.parent.clearCachedObjects();
    this.totalPointCount = 0;
  }
};
DataSerie._MaxPointsPerSeries = 0;
var DataTracker = class _DataTracker {
  get directParent() {
    return this._directParent;
  }
  get userData() {
    return this._userData;
  }
  set userData(value) {
    this._userData = value;
  }
  constructor(parent, directParent) {
    this._userData = null;
    this._enabled = false;
    this._showSerieName = false;
    this._showTimeStamp = false;
    this._dataPrecisionString = "";
    this._dataPrecision = _DataTracker.DataPrecision.PRECISION_NOLIMIT;
    this._diameter = 5;
    this._handleLength = 25;
    this._detectionDistance = 50;
    this._bgColor = YColor.FromArgb(200, 255, 255, 255);
    this._borderColor = YColor.Black;
    this._borderthickness = 1;
    this._padding = 10;
    this._verticalMargin = 10;
    this._horizontalMargin = 10;
    this._bgBrush = null;
    this._pen = null;
    this._font = null;
    this._directParent = directParent;
    this._parentRenderer = parent;
    this._font = new YFont(parent, this, 8, null);
  }
  get enabled() {
    return this._enabled;
  }
  set enabled(value) {
    this._enabled = value;
    this._parentRenderer.redraw();
  }
  get showSerieName() {
    return this._showSerieName;
  }
  set showSerieName(value) {
    this._showSerieName = value;
    this._parentRenderer.redraw();
  }
  get showTimeStamp() {
    return this._showTimeStamp;
  }
  set showTimeStamp(value) {
    this._showTimeStamp = value;
    this._parentRenderer.redraw();
  }
  get dataPrecisionString() {
    return this._dataPrecisionString;
  }
  get dataPrecision() {
    return this._dataPrecision;
  }
  set dataPrecision(value) {
    this._dataPrecision = value;
    this.compute_dataPrecisionString();
    this._parentRenderer.redraw();
  }
  compute_dataPrecisionString() {
    if (this._dataPrecision == _DataTracker.DataPrecision.PRECISION_NOLIMIT) {
      this._dataPrecisionString = "";
      return;
    }
    this._dataPrecisionString = "0.";
  }
  get diameter() {
    return this._diameter;
  }
  set diameter(value) {
    if (value < 0)
      throw new RangeError("Diameter must be a positive value");
    this._diameter = value;
    this._parentRenderer.redraw();
  }
  get handleLength() {
    return this._handleLength;
  }
  set handleLength(value) {
    if (value < 0)
      throw new RangeError("Hanle length must be a positive value");
    this._handleLength = value;
    this._parentRenderer.redraw();
  }
  get detectionDistance() {
    return this._detectionDistance;
  }
  set detectionDistance(value) {
    if (value <= 0)
      throw new RangeError("Distance must be a positive value");
    this._detectionDistance = value;
  }
  get bgColor() {
    return this._bgColor;
  }
  set bgColor(value) {
    this._bgColor = value;
    this._bgBrush = null;
    this._parentRenderer.redraw();
  }
  get borderColor() {
    return this._borderColor;
  }
  set borderColor(value) {
    this._borderColor = value;
    this._pen = null;
    this._parentRenderer.redraw();
  }
  get borderthickness() {
    return this._borderthickness;
  }
  set borderthickness(value) {
    if (value < 0)
      throw new RangeError("Thickness must be a positive value");
    this._borderthickness = value;
    this._pen = null;
    this._parentRenderer.redraw();
  }
  get padding() {
    return this._padding;
  }
  set padding(value) {
    if (value < 0)
      throw new RangeError("Padding must be a positive value");
    this._padding = value;
    this._parentRenderer.redraw();
  }
  get verticalMargin() {
    return this._verticalMargin;
  }
  set verticalMargin(value) {
    if (value < 0)
      throw new RangeError("Margin must be a positive value");
    this._verticalMargin = value;
    this._parentRenderer.redraw();
  }
  get horizontalMargin() {
    return this._horizontalMargin;
  }
  set horizontalMargin(value) {
    if (value < 0)
      throw new RangeError("Margin must be a positive value");
    this._horizontalMargin = value;
    this._parentRenderer.redraw();
  }
  get bgBrush() {
    if (this._bgBrush == null) {
      this._bgBrush = new YSolidBrush(this._bgColor);
    }
    return this._bgBrush;
  }
  get pen() {
    if (this._pen == null)
      this._pen = new YPen(this._borderColor, this._borderthickness, true);
    return this._pen;
  }
  get font() {
    return this._font;
  }
};
(function(DataTracker2) {
  class DataPrecisionEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, DataPrecision);
    }
  }
  DataTracker2.DataPrecisionEnumItem = DataPrecisionEnumItem;
  class DataPrecision extends YEnum {
  }
  DataPrecision.PRECISION_NOLIMIT = new DataPrecisionEnumItem("PRECISION_NOLIMIT", "As is");
  DataPrecision.PRECISION_1 = new DataPrecisionEnumItem("PRECISION_1", "1");
  DataPrecision.PRECISION_01 = new DataPrecisionEnumItem("PRECISION_01", "0.1");
  DataPrecision.PRECISION_001 = new DataPrecisionEnumItem("PRECISION_001", "0.01");
  DataPrecision.PRECISION_0001 = new DataPrecisionEnumItem("PRECISION_0001", "0.001");
  DataPrecision.PRECISION_00001 = new DataPrecisionEnumItem("PRECISION_00001", "0.0001");
  DataPrecision.PRECISION_000001 = new DataPrecisionEnumItem("PRECISION_000001", "0.00001");
  DataPrecision.PRECISION_0000001 = new DataPrecisionEnumItem("PRECISION_0000001", "0.000001");
  DataPrecision.PRECISION_00000001 = new DataPrecisionEnumItem("PRECISION_00000001", "0.0000001");
  DataPrecision.PRECISION_000000001 = new DataPrecisionEnumItem("PRECISION_000000001", "0.00000001");
  DataPrecision.PRECISION_0000000001 = new DataPrecisionEnumItem("PRECISION_0000000001", "0.000000001");
  DataTracker2.DataPrecision = DataPrecision;
})(DataTracker || (DataTracker = {}));
var LegendPanel = class _LegendPanel {
  get directParent() {
    return this._directParent;
  }
  get userData() {
    return this._userData;
  }
  set userData(value) {
    this._userData = value;
  }
  get traceWidthFactor() {
    return this._traceWidth;
  }
  set traceWidthFactor(value) {
    if (value <= 0)
      throw new RangeError("This has to be a strictly positive value");
    this._traceWidth = value;
    this._parentRenderer.resetlegendPens();
    this._parentRenderer.redraw();
  }
  constructor(parent, directParent) {
    this._userData = null;
    this._traceWidth = 1;
    this._enabled = false;
    this._position = _LegendPanel.Position.BOTTOM;
    this._overlap = false;
    this._bgColor = YColor.FromArgb(200, 255, 255, 255);
    this._borderColor = YColor.Black;
    this._borderthickness = 1;
    this._padding = 10;
    this._verticalMargin = 10;
    this._horizontalMargin = 10;
    this._bgBrush = null;
    this._pen = null;
    this._font = null;
    this._directParent = directParent;
    this._parentRenderer = parent;
    this._font = new YFont(parent, this, 8, null);
  }
  get enabled() {
    return this._enabled;
  }
  set enabled(value) {
    this._enabled = value;
    this._parentRenderer.redraw();
  }
  get position() {
    return this._position;
  }
  set position(value) {
    this._position = value;
    this._parentRenderer.redraw();
  }
  get overlap() {
    return this._overlap;
  }
  set overlap(value) {
    this._overlap = value;
    this._parentRenderer.redraw();
  }
  get bgColor() {
    return this._bgColor;
  }
  set bgColor(value) {
    this._bgColor = value;
    this._bgBrush = null;
    this._parentRenderer.redraw();
  }
  get borderColor() {
    return this._borderColor;
  }
  set borderColor(value) {
    this._borderColor = value;
    this._pen = null;
    this._parentRenderer.redraw();
  }
  get borderthickness() {
    return this._borderthickness;
  }
  set borderthickness(value) {
    if (value < 0)
      throw new RangeError("Thickness must be a positive value");
    this._borderthickness = value;
    this._pen = null;
    this._parentRenderer.redraw();
  }
  get padding() {
    return this._padding;
  }
  set padding(value) {
    if (value < 0)
      throw new RangeError("Padding must be a positive value");
    this._padding = value;
    this._parentRenderer.redraw();
  }
  get verticalMargin() {
    return this._verticalMargin;
  }
  set verticalMargin(value) {
    if (value < 0)
      throw new RangeError("Margin must be a positive value");
    this._verticalMargin = value;
    this._parentRenderer.redraw();
  }
  get horizontalMargin() {
    return this._horizontalMargin;
  }
  set horizontalMargin(value) {
    if (value < 0)
      throw new RangeError("Margin must be a positive value");
    this._horizontalMargin = value;
    this._parentRenderer.redraw();
  }
  get bgBrush() {
    if (this._bgBrush == null) {
      this._bgBrush = new YSolidBrush(this._bgColor);
    }
    return this._bgBrush;
  }
  get pen() {
    if (this._pen == null)
      this._pen = new YPen(this._borderColor, this._borderthickness, true);
    return this._pen;
  }
  get font() {
    return this._font;
  }
};
(function(LegendPanel2) {
  class PositionEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, Position);
    }
  }
  LegendPanel2.PositionEnumItem = PositionEnumItem;
  class Position extends YEnum {
  }
  Position.LEFT = new PositionEnumItem("LEFT", "Left");
  Position.TOPLEFT = new PositionEnumItem("TOPLEFT", "Top-Left");
  Position.TOP = new PositionEnumItem("TOP", "Top");
  Position.TOPRIGHT = new PositionEnumItem("TOPRIGHT", "Top-Right");
  Position.RIGHT = new PositionEnumItem("RIGHT", "Right");
  Position.BOTTOMRIGHT = new PositionEnumItem("BOTTOMRIGHT", "Bottom-Right");
  Position.BOTTOM = new PositionEnumItem("BOTTOM", "Bottom");
  Position.BOTTOMLEFT = new PositionEnumItem("BOTTOMLEFT", "Bottom-Left");
  LegendPanel2.Position = Position;
})(LegendPanel || (LegendPanel = {}));
var Navigator = class _Navigator {
  get directParent() {
    return this._directParent;
  }
  get userData() {
    return this._userData;
  }
  set userData(value) {
    this._userData = value;
  }
  get showXAxisZones() {
    return this._showXAxisZones;
  }
  set showXAxisZones(value) {
    this._showXAxisZones = value;
  }
  get relativeheight() {
    return this._relativeheight;
  }
  set relativeheight(value) {
    if (value < 10)
      value = 10;
    if (value > 50)
      value = 50;
    this._relativeheight = value;
    this._parentRenderer.redraw();
  }
  constructor(parent, directParent) {
    this._userData = null;
    this._viewport = new ViewPortSettings();
    this.Xrange = null;
    this._showXAxisZones = true;
    this._relativeheight = 10;
    this._enabled = false;
    this._bgColor1 = YColor.FromArgb(255, 225, 225, 225);
    this._cursorBorderColor = YColor.FromArgb(255, 40, 40, 40);
    this._yAxisHandling = _Navigator.YAxisHandling.AUTO;
    this._bgColor2 = YColor.FromArgb(255, 225, 225, 225);
    this._cursorColor = YColor.FromArgb(100, 0, 255, 0);
    this._cursorBrush = null;
    this._pen = null;
    this._cursorBorderPen = null;
    this._xAxisColor = YColor.Black;
    this._xAxisThickness = 1;
    this._borderPen = null;
    this._borderColor = YColor.DimGray;
    this._borderThickness = 1;
    this._bgBrush = null;
    this._font = null;
    this._directParent = directParent;
    this._parentRenderer = parent;
    this._font = new YFont(parent, this, 8, null);
  }
  get enabled() {
    return this._enabled;
  }
  set enabled(value) {
    this._enabled = value;
    this._parentRenderer.redraw();
  }
  get bgColor1() {
    return this._bgColor1;
  }
  set bgColor1(value) {
    this._bgColor1 = value;
    this._bgBrush = null;
    this._parentRenderer.redraw();
  }
  get cursorBorderColor() {
    return this._cursorBorderColor;
  }
  set cursorBorderColor(value) {
    this._cursorBorderColor = value;
    this._cursorBorderPen = null;
    this._parentRenderer.redraw();
  }
  get yAxisHandling() {
    return this._yAxisHandling;
  }
  set yAxisHandling(value) {
    this._yAxisHandling = value;
    this._parentRenderer.redraw();
  }
  get bgColor2() {
    return this._bgColor2;
  }
  set bgColor2(value) {
    this._bgColor2 = value;
    this._bgBrush = null;
    this._parentRenderer.redraw();
  }
  get cursorColor() {
    return this._cursorColor;
  }
  set cursorColor(value) {
    this._cursorColor = value;
    this._cursorBrush = null;
    this._parentRenderer.redraw();
  }
  get cursorBrush() {
    if (this._cursorBrush == null) {
      this._cursorBrush = new YSolidBrush(this._cursorColor);
    }
    return this._cursorBrush;
  }
  get pen() {
    if (this._pen == null)
      this._pen = new YPen(this._xAxisColor, this._xAxisThickness, true);
    return this._pen;
  }
  get cursorBorderPen() {
    if (this._cursorBorderPen == null)
      this._cursorBorderPen = new YPen(this._cursorBorderColor, 1, true);
    return this._cursorBorderPen;
  }
  get xAxisColor() {
    return this._xAxisColor;
  }
  set xAxisColor(value) {
    this._xAxisColor = value;
    this._pen = null;
    this._parentRenderer.redraw();
  }
  get xAxisThickness() {
    return this._xAxisThickness;
  }
  set xAxisThickness(value) {
    if (value < 0)
      throw new RangeError("Thickness must be a positive value");
    this._xAxisThickness = value;
    this._pen = null;
    this._parentRenderer.redraw();
  }
  get borderPen() {
    if (this._borderPen == null)
      this._borderPen = new YPen(this._borderColor, this._borderThickness, true);
    return this._borderPen;
  }
  get borderColor() {
    return this._borderColor;
  }
  set borderColor(value) {
    this._borderColor = value;
    this._borderPen = null;
    this._parentRenderer.redraw();
  }
  get borderThickness() {
    return this._borderThickness;
  }
  set borderThickness(value) {
    if (value < 0)
      throw new RangeError("Thickness must be a positive value");
    this._borderThickness = value;
    this._borderPen = null;
    this._parentRenderer.redraw();
  }
  setPosition(ParentWidth, ParentHeight, Lmargin, Rmargin, Tmargin, Bmargin) {
    if (this._viewport.Lmargin != Lmargin || this._viewport.Rmargin != Rmargin || this._viewport.Tmargin != Tmargin || this._viewport.Bmargin != Bmargin) {
      this._bgBrush = null;
    }
    this._viewport.Lmargin = Lmargin;
    this._viewport.Rmargin = Rmargin;
    this._viewport.Bmargin = Bmargin;
    this._viewport.Tmargin = Tmargin;
    this._viewport.Width = ParentWidth;
    this._viewport.Height = ParentHeight;
  }
  setIRLPosition(IRLx, IRLy, xZoom, yZoom) {
    this._viewport.IRLx = IRLx;
    this._viewport.IRLy = IRLy;
    this._viewport.zoomx = xZoom;
    this._viewport.zoomy = yZoom;
  }
  startCapture(IRLStartPoint, xAxisMin, xAxisMax) {
    this._viewport.OriginalXAxisMin = xAxisMin;
    this._viewport.OriginalXAxisMax = xAxisMax;
    this._viewport.OriginalIRLx = this._viewport.IRLx;
    this._viewport.OriginalLmargin = this._viewport.Lmargin;
    this._viewport.OriginalZoomx = this._viewport.zoomx;
    this._viewport.IRLCaptureStartX = IRLStartPoint.x;
    this._viewport.Capture = true;
  }
  get Capture() {
    return this._viewport.Capture;
  }
  stopCapture() {
    this._viewport.Capture = false;
  }
  get viewport() {
    return this._viewport;
  }
  get bgBrush() {
    if (this._bgBrush == null) {
      this._bgBrush = new YLinearGradientBrush(this._bgColor1, this._bgColor2);
    }
    return this._bgBrush;
  }
  get font() {
    return this._font;
  }
};
(function(Navigator2) {
  class YAxisHandlingEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, YAxisHandling);
    }
  }
  Navigator2.YAxisHandlingEnumItem = YAxisHandlingEnumItem;
  class YAxisHandling extends YEnum {
  }
  YAxisHandling.AUTO = new YAxisHandlingEnumItem("AUTO", "Automatic");
  YAxisHandling.INHERIT = new YAxisHandlingEnumItem("INHERIT", "Inherit from main view");
  Navigator2.YAxisHandling = YAxisHandling;
})(Navigator || (Navigator = {}));
var Marker = class _Marker {
  get userData() {
    return this._userData;
  }
  set userData(value) {
    this._userData = value;
  }
  static _round100(v) {
    return Math.round(100 * v) / 100;
  }
  get stringFormat() {
    if (this._stringFormat != null)
      return this._stringFormat;
    this._stringFormat = new YStringFormat(
      16384
      /* YDataRendering.YStringFormat.StringFormatFlags.NoClip */
    );
    this._stringFormat.LineAlignment = 1;
    switch (this._textAlign) {
      case _Marker.TextAlign.LEFT:
        this._stringFormat.Alignment = 0;
        break;
      case _Marker.TextAlign.CENTER:
        this._stringFormat.Alignment = 1;
        break;
      case _Marker.TextAlign.RIGHT:
        this._stringFormat.Alignment = 2;
        break;
    }
    return this._stringFormat;
  }
  get PatchTextCallback() {
    return this._MarkerTextCallback;
  }
  set PatchTextCallback(callback) {
    this._MarkerTextCallback = callback;
  }
  get directParent() {
    return this._directParent;
  }
  constructor(parent, directParent) {
    this._userData = null;
    this._stringFormat = null;
    this._MarkerTextCallback = null;
    this._enabled = false;
    this._xposition = 0;
    this._xpositionIsRelative = false;
    this._yposition = 92;
    this._text = "Marker";
    this._textAlign = _Marker.TextAlign.CENTER;
    this._bgColor = YColor.FromArgb(255, 255, 255, 192);
    this._borderColor = YColor.DarkRed;
    this._borderthickness = 1;
    this._arrowSize = 5;
    this._padding = 5;
    this._verticalMargin = 5;
    this._horizontalMargin = 5;
    this._bgBrush = null;
    this._arrowBrush = null;
    this._pen = null;
    this._navigatorpen = null;
    this._font = null;
    this._directParent = directParent;
    this._parentRenderer = parent;
    this._font = new YFont(parent, this, 8, null);
  }
  startCapture() {
    this._parentRenderer.startMarkerCapture(this);
  }
  setCapturedPosition(position, axis) {
    this.enabled = true;
    this._xpositionIsRelative = axis.timeReference == TimeConverter.TimeReference.RELATIVE && axis.zeroTime > 0;
    this._xposition = _Marker._round100(this._xpositionIsRelative ? position - axis.zeroTime : position);
    this._parentRenderer.clearCachedObjects();
    this._parentRenderer.redraw();
  }
  get enabled() {
    return this._enabled;
  }
  set enabled(value) {
    if (this._enabled != value) {
      this._enabled = value;
      this._parentRenderer.clearCachedObjects();
      this._parentRenderer.redraw();
    }
  }
  get xposition() {
    return this._xposition;
  }
  set xposition(value) {
    this._xposition = _Marker._round100(value);
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get timereference() {
    return this._xpositionIsRelative ? TimeConverter.TimeReference.RELATIVE : TimeConverter.TimeReference.ABSOLUTE;
  }
  set timereference(value) {
    let v = value == TimeConverter.TimeReference.RELATIVE;
    if (this._xpositionIsRelative != v) {
      let ZeroPosition = this._directParent.zeroTime;
      if (isNaN(ZeroPosition))
        ZeroPosition = 0;
      if (v) {
        this._xpositionIsRelative = true;
        this._xposition -= ZeroPosition;
      } else {
        this._xpositionIsRelative = false;
        this._xposition += ZeroPosition;
      }
      this._xposition = _Marker._round100(this._xposition);
      this._parentRenderer.redraw();
    }
  }
  // a special variant which also to get/set both xposition and xpositionIsRelative at the same time
  // and allow to start position capture as well.
  get positionOnXAxis() {
    return new xAxisPosition(this._xposition, this._xpositionIsRelative);
  }
  set positionOnXAxis(value) {
    if (value.capture) {
      this.startCapture();
    } else {
      let v = _Marker._round100(value.value);
      if (this._xpositionIsRelative != value.relative || this._xposition != v) {
        this._xposition = v;
        this._xpositionIsRelative = value.relative;
        if (this._enabled)
          this._parentRenderer.redraw();
      }
    }
  }
  get yposition() {
    return this._yposition;
  }
  set yposition(value) {
    this._yposition = Math.min(100, Math.max(0, value));
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get text() {
    return this._text;
  }
  set text(value) {
    this._text = value;
    this._parentRenderer.clearCachedObjects();
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get textAlign() {
    return this._textAlign;
  }
  set textAlign(value) {
    this._textAlign = value;
    this._stringFormat = null;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get shortText() {
    if (this._text.length <= 20)
      return this._text;
    return this._text.substring(0, 18) + "..";
  }
  get bgColor() {
    return this._bgColor;
  }
  set bgColor(value) {
    this._bgColor = value;
    this._bgBrush = null;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get borderColor() {
    return this._borderColor;
  }
  set borderColor(value) {
    this._borderColor = value;
    this._arrowBrush = null;
    this._pen = null;
    this._navigatorpen = null;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get borderthickness() {
    return this._borderthickness;
  }
  set borderthickness(value) {
    if (value < 0)
      throw new RangeError("thickness must be a positive value");
    this._borderthickness = value;
    this._parentRenderer.clearCachedObjects();
    this._pen = null;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get arrowSize() {
    return this._arrowSize;
  }
  set arrowSize(value) {
    this._arrowSize = value;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get padding() {
    return this._padding;
  }
  set padding(value) {
    if (value < 0)
      throw new RangeError("Padding must be a positive value");
    this._padding = value;
    this._parentRenderer.clearCachedObjects();
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get verticalMargin() {
    return this._verticalMargin;
  }
  set verticalMargin(value) {
    this._verticalMargin = value;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get horizontalMargin() {
    return this._horizontalMargin;
  }
  set horizontalMargin(value) {
    this._horizontalMargin = value;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get bgBrush() {
    if (this._bgBrush == null) {
      this._bgBrush = new YSolidBrush(this._bgColor);
    }
    return this._bgBrush;
  }
  get arrowBrush() {
    if (this._arrowBrush == null) {
      this._arrowBrush = new YSolidBrush(this._borderColor, true);
    }
    return this._arrowBrush;
  }
  get pen() {
    if (this._pen == null)
      this._pen = new YPen(this._borderColor, this._borderthickness, true);
    return this._pen;
  }
  get navigatorpen() {
    if (this._navigatorpen == null)
      this._navigatorpen = new YPen(this._borderColor, 1);
    return this._navigatorpen;
  }
  get font() {
    return this._font;
  }
};
(function(Marker2) {
  class TextAlignEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, TextAlign);
    }
  }
  Marker2.TextAlignEnumItem = TextAlignEnumItem;
  class TextAlign extends YEnum {
  }
  TextAlign.LEFT = new TextAlignEnumItem("LEFT", "Left");
  TextAlign.CENTER = new TextAlignEnumItem("CENTER", "Center");
  TextAlign.RIGHT = new TextAlignEnumItem("RIGHT", "Right");
  Marker2.TextAlign = TextAlign;
})(Marker || (Marker = {}));
var Legend = class {
  get directParent() {
    return this._directParent;
  }
  get userData() {
    return this._userData;
  }
  set userData(value) {
    this._userData = value;
  }
  constructor(parent, directParent) {
    this._userData = null;
    this._title = "";
    this._font = null;
    this._directParent = directParent;
    this._parentRenderer = parent;
    this._font = new YFont(parent, this, 12, null);
  }
  get title() {
    return this._title;
  }
  set title(value) {
    this._title = value;
    this._parentRenderer.redraw();
  }
  get font() {
    return this._font;
  }
};
var GenericAxis = class {
  get directParent() {
    return this._directParent;
  }
  get zones() {
    return this._zones;
  }
  AddZone() {
    let z = new Zone(this._parentRenderer, this);
    this._zones.push(z);
    return z;
  }
  get userData() {
    return this._userData;
  }
  set userData(value) {
    this._userData = value;
  }
  get AxisChanged() {
    return this._AxisChanged;
  }
  set AxisChanged(value) {
    this._AxisChanged = value;
  }
  constructor(parent, directParent) {
    this._userData = null;
    this._AxisChanged = null;
    this._pen = null;
    this._gridPen = null;
    this._visible = true;
    this._AllowAutoShow = false;
    this._min = Number.NaN;
    this._max = Number.NaN;
    this._step = Number.NaN;
    this._thickness = 1;
    this._color = YColor.Black;
    this._showGrid = false;
    this._gridColor = YColor.FromArgb(50, 0, 0, 0);
    this._gridThickness = 1;
    this._font = null;
    this._zones = [];
    this._directParent = directParent;
    this._parentRenderer = parent;
    this._legend = new Legend(parent, this);
    this._font = new YFont(parent, this);
  }
  get pen() {
    if (this._pen == null)
      this._pen = new YPen(this._color, this._thickness, true);
    return this._pen;
  }
  get gridPen() {
    if (this._gridPen == null)
      this._gridPen = new YPen(this._gridColor, this._gridThickness, true);
    return this._gridPen;
  }
  get visible() {
    return this._visible;
  }
  set visible(value) {
    this._visible = value;
    if (!value) {
      this._AllowAutoShow = false;
    }
    this._parentRenderer.redraw();
  }
  get AllowAutoShow() {
    return this._AllowAutoShow;
  }
  set AllowAutoShow(value) {
    this._AllowAutoShow = value;
  }
  AutoShow() {
    if (this._AllowAutoShow) {
      this.visible = true;
      if (this._AxisChanged != null)
        this._AxisChanged(this);
    }
  }
  set_minMax(value_min, value_max) {
    if (!isNaN(value_min) && !isNaN(value_max) && value_min >= value_max) {
      throw new RangeError("Min (" + value_min.toString() + ") cannot be greater than max (" + value_max.toString() + ")");
    }
    this._min = value_min;
    this._max = value_max;
    this._parentRenderer.redraw();
  }
  get min() {
    return this._min;
  }
  set min(value) {
    if (!isNaN(value) && !isNaN(this._max) && !YDataRenderer.minMaxCheckDisabled) {
      if (value >= this._max) {
        throw new RangeError("Min cannot be greater than max (" + this._max.toString() + ")");
      }
    }
    this._min = value;
    this._parentRenderer.redraw();
  }
  setMinMax(min, max) {
    if (min < max) {
      this._min = min;
      this._max = max;
      this._parentRenderer.redraw();
    }
  }
  get max() {
    return this._max;
  }
  set max(value) {
    if (!isNaN(value) && !isNaN(this._min) && !YDataRenderer.minMaxCheckDisabled) {
      if (value <= this._min)
        throw new RangeError("Max cannot be less than min (" + this._min.toString() + ")");
    }
    this._max = value;
    this._parentRenderer.redraw();
  }
  get step() {
    return this._step;
  }
  set step(value) {
    if (!isNaN(value) && value < 0)
      throw new RangeError("Steps must be a strictely positive value");
    this._step = value;
    this._parentRenderer.redraw();
  }
  get thickness() {
    return this._thickness;
  }
  set thickness(value) {
    if (value < 0)
      throw new RangeError("Thickness must be a positive value");
    this._thickness = value;
    this._pen = null;
    this._parentRenderer.redraw();
  }
  get color() {
    return this._color;
  }
  set color(value) {
    this._color = value;
    this._pen = null;
    this._parentRenderer.redraw();
  }
  get showGrid() {
    return this._showGrid;
  }
  set showGrid(value) {
    this._showGrid = value;
    this._parentRenderer.redraw();
  }
  get gridColor() {
    return this._gridColor;
  }
  set gridColor(value) {
    this._gridColor = value;
    this._gridPen = null;
    this._parentRenderer.redraw();
  }
  get gridThickness() {
    return this._gridThickness;
  }
  set gridThickness(value) {
    if (value < 0)
      throw new RangeError("Thickness must be a positive value");
    this._gridThickness = value;
    this._gridPen = null;
    this._parentRenderer.redraw();
  }
  get font() {
    return this._font;
  }
  get legend() {
    return this._legend;
  }
};
var StartStopStep = class {
  constructor() {
    this.dataMin = 0;
    this.dataMax = 0;
    this.absMin = 0;
    this.absMax = 0;
    this.step = 0;
    this.start = 0;
    this.stop = 0;
    this.precision = 0;
  }
};
var xAxisPosition = class _xAxisPosition {
  get relative() {
    return this._isRelative;
  }
  set relative(value) {
    this._isRelative = value;
  }
  get value() {
    return this._value;
  }
  set value(value) {
    this._value = value;
  }
  constructor(v, rel, capture) {
    this._isRelative = false;
    this._value = 0;
    this._capture = false;
    this._isRelative = rel;
    this._value = v;
    this._capture = typeof capture == "undefined" ? false : capture;
  }
  clone() {
    return new _xAxisPosition(this._value, this._isRelative, this._capture);
  }
  toString() {
    if (this._isRelative) {
      return TimeConverter.secTimeSpanToString(this._value, 0);
    } else {
      let date = TimeConverter.FromUnixTime(this._value);
      let res = date.getFullYear().toString() + "-" + (date.getMonth() + 1).toString() + "-" + date.getDate().toString() + " " + date.getHours().toString() + ":";
      let st = date.getMinutes().toString();
      if (st.length <= 1)
        st = "0" + st;
      res = res + st + ":";
      let s = date.getSeconds();
      let ms = date.getMilliseconds();
      s = s + ms / 1e3;
      if (s < 10)
        res = res + "0";
      if (ms == 0) {
        res = res + s.toString();
      } else {
        res = res + s.toFixed(3);
      }
      return res;
    }
  }
  TryParse(str) {
    if (this._isRelative)
      return TimeConverter.tryParseStringToSecTimeSpan(str);
    return TimeConverter.tryParseStringToAbsDateTime(str);
  }
  get capture() {
    return this._capture;
  }
  set capture(value) {
    this._capture = value;
  }
};
xAxisPosition.DTdisplayformat = "DD/MM/YY hh:mm:ss.ff";
xAxisPosition.TSdisplayformat = "dd.hh:mm:ss.ff";
var YNumberFormatInfo = class {
  constructor() {
    this.NumberDecimalSeparator = ".";
  }
};
var YAxis = class _YAxis extends GenericAxis {
  constructor(parent, directParent, index) {
    super(parent, directParent);
    this._index = 0;
    this._highlightZero = false;
    this._position = _YAxis.HrzPosition.LEFT;
    this.innerWidth = 0;
    this.zoom = 0;
    this.IRLy = 0;
    this._index = index;
    this.nfi = new YNumberFormatInfo();
    this.nfi.NumberDecimalSeparator = ".";
    this.startStopStep = new StartStopStep();
    this.startStopStep.start = 0;
    this.startStopStep.stop = 1;
    this.startStopStep.step = 0.1;
  }
  get index() {
    return this._index;
  }
  lockMinMax() {
    this._min = this.startStopStep.absMin;
    this._max = this.startStopStep.absMax;
    this._parentRenderer.redraw();
  }
  unlockMinMax() {
    this._min = Number.NaN;
    this._max = Number.NaN;
    this._parentRenderer.redraw();
  }
  get highlightZero() {
    return this._highlightZero;
  }
  set highlightZero(value) {
    this._highlightZero = value;
    this._parentRenderer.redraw();
  }
  get position() {
    return this._position;
  }
  set position(value) {
    this._position = value;
    this._parentRenderer.redraw();
  }
  computeStartAndStep(M) {
    let res = new StartStopStep();
    let min = this.min;
    let max = this.max;
    res.step = this.step;
    res.precision = 0;
    if (!MinMaxHandler.isDefined(M)) {
      M = MinMaxHandler.DefaultValue();
      M.Min = 0;
      M.Max = 100;
    }
    if (isNaN(min))
      min = M.Min;
    if (isNaN(max))
      max = M.Max;
    res.absMax = max;
    res.absMin = min;
    if (min == max) {
      min -= 0.5;
      max += 0.5;
    }
    if (min != 0)
      min -= (max - min) * 0.025;
    if (max != 0)
      max += (max - min) * 0.025;
    res.start = min;
    res.stop = max;
    res.dataMin = min;
    res.dataMax = max;
    let Delta = max - min;
    if (isNaN(res.step)) {
      let MagnitudePwr = Math.log10(Delta);
      if (MagnitudePwr - Math.floor(MagnitudePwr) != 0)
        MagnitudePwr = Math.floor(MagnitudePwr) + 1;
      res.precision = MagnitudePwr - 1 >> 0;
      let Magnitude = Math.pow(10, res.precision);
      let C = Delta / Magnitude;
      if (C <= 2) {
        res.step = Magnitude / 5;
        res.precision--;
      } else if (C <= 5) {
        res.step = Magnitude / 2;
        res.precision--;
      } else {
        res.step = Magnitude;
      }
      if (isNaN(this.min)) {
        let c = min / res.step;
        if (c - Math.floor(c) != 0)
          c = c > 0 ? Math.floor(c) + 1 : Math.floor(c) - 1;
        res.start = res.step * c;
      }
    } else {
      let v = res.step.toString();
      let p = v.indexOf(".");
      if (p >= 0) {
        res.precision = -(v.length - p - 1);
      } else {
        res.precision = 0;
      }
    }
    this.startStopStep = res;
    return res;
  }
};
(function(YAxis2) {
  class HrzPositionEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, HrzPosition);
    }
  }
  YAxis2.HrzPositionEnumItem = HrzPositionEnumItem;
  class HrzPosition extends YEnum {
  }
  HrzPosition.LEFT = new HrzPositionEnumItem("LEFT", "Left");
  HrzPosition.RIGHT = new HrzPositionEnumItem("RIGHT", "Right");
  YAxis2.HrzPosition = HrzPosition;
})(YAxis || (YAxis = {}));
var XAxis = class _XAxis extends GenericAxis {
  get position() {
    return this._position;
  }
  set position(value) {
    this._position = value;
    this._parentRenderer.redraw();
  }
  get markers() {
    return this._markers;
  }
  AddMarker() {
    let m = new Marker(this._parentGraph, this);
    this._markers.push(m);
    this._parentGraph.clearCachedObjects();
    this._parentGraph.redraw();
    return m;
  }
  get initialZoom() {
    return this._initialZoom;
  }
  set initialZoom(value) {
    if (value <= 0)
      throw new RangeError("Zoom must be a positive value");
    this._initialZoom = value;
    this.min = this.min - this._initialZoom * this._initialOffset / 100;
    this.max = this.min + this.initialZoom;
    this._parentRenderer.redraw();
  }
  get initialOffset() {
    return this._initialOffset;
  }
  set initialOffset(value) {
    this._initialOffset = value;
    let p = this._parentGraph.getMostRecentPoint();
    if (isNaN(p.x)) {
      this._min = this._min - this._initialZoom * this._initialOffset / 100;
      this._max = this._min + this._initialZoom;
      this._parentRenderer.redraw();
    } else {
      let zoom = this._max - this._min;
      this._min = p.x - zoom * this._initialOffset / 100;
      this._max = this._min + zoom;
    }
    this._parentRenderer.redraw();
  }
  get labelFormat() {
    return this._format;
  }
  set labelFormat(value) {
    this._format = value;
    this._parentRenderer.redraw();
  }
  constructor(parent, directParent) {
    super(parent, directParent);
    this._position = _XAxis.VrtPosition.BOTTOM;
    this._markers = [];
    this._initialZoom = 300;
    this._initialOffset = 0;
    this._format = _XAxis.FORMATAUTO;
    this._timeReference = TimeConverter.TimeReference.ABSOLUTE;
    this._zeroTime = 0;
    this._fullSize = 0;
    this.innerHeight = 0;
    this._overflowHandling = _XAxis.OverflowHandling.DONOTHING;
    this._parentGraph = parent;
    this._markers = [];
    this.min = TimeConverter.ToUnixTime(TimeConverter.UTCNow());
    this.max = this.min + this.initialZoom;
    this.step = 30;
  }
  get timeReference() {
    return this._timeReference;
  }
  set timeReference(value) {
    this._timeReference = value;
    this._parentRenderer.redraw();
  }
  get zeroTime() {
    return this._zeroTime;
  }
  set zeroTime(value) {
    this._zeroTime = value;
  }
  get fullSize() {
    return this._fullSize;
  }
  set fullSize(value) {
    this._fullSize = value;
  }
  bestFormat(dataTimedelta, viewportTimedelta) {
    return TimeConverter.BestTimeformat(dataTimedelta, viewportTimedelta, this._timeReference);
  }
  get overflowHandling() {
    return this._overflowHandling;
  }
  set overflowHandling(value) {
    this._overflowHandling = value;
  }
};
XAxis.FORMATAUTO = 0;
(function(XAxis2) {
  class VrtPositionEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, VrtPosition);
    }
  }
  XAxis2.VrtPositionEnumItem = VrtPositionEnumItem;
  class VrtPosition extends YEnum {
  }
  VrtPosition.TOP = new VrtPositionEnumItem("TOP", "Top");
  VrtPosition.BOTTOM = new VrtPositionEnumItem("BOTTOM", "Bottom");
  XAxis2.VrtPosition = VrtPosition;
  class OverflowHandlingEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, OverflowHandling);
    }
  }
  XAxis2.OverflowHandlingEnumItem = OverflowHandlingEnumItem;
  class OverflowHandling extends YEnum {
  }
  OverflowHandling.DONOTHING = new OverflowHandlingEnumItem("DONOTHING", "Do nothing");
  OverflowHandling.SCROLL = new OverflowHandlingEnumItem("SCROLL", "Scroll contents");
  OverflowHandling.CONTRACT = new OverflowHandlingEnumItem("CONTRACT", "Squeeze contents");
  XAxis2.OverflowHandling = OverflowHandling;
})(XAxis || (XAxis = {}));
var DataPanel = class _DataPanel extends GenericPanel {
  constructor(parent, directParent) {
    super(parent, directParent);
    this._panelHrzAlign = _DataPanel.HorizontalAlign.CENTERED;
    this._panelVrtAlign = _DataPanel.VerticalAlign.CENTERED;
    this._horizontalPosition = _DataPanel.HorizontalPosition.ABSOLUTEX;
    this._verticalPosition = _DataPanel.VerticalPosition.ABSOLUTEY;
    this._AbsoluteXposition = 0;
    this._AbsoluteYposition = 0;
    this._YScaleIndex = 0;
  }
  get panelHrzAlign() {
    return this._panelHrzAlign;
  }
  set panelHrzAlign(value) {
    this._panelHrzAlign = value;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get panelVrtAlign() {
    return this._panelVrtAlign;
  }
  set panelVrtAlign(value) {
    this._panelVrtAlign = value;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get horizontalPosition() {
    return this._horizontalPosition;
  }
  set horizontalPosition(value) {
    this._horizontalPosition = value;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get verticalPosition() {
    return this._verticalPosition;
  }
  set verticalPosition(value) {
    this._verticalPosition = value;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get AbsoluteXposition() {
    return this._AbsoluteXposition;
  }
  set AbsoluteXposition(value) {
    this._AbsoluteXposition = value;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get AbsoluteYposition() {
    return this._AbsoluteYposition;
  }
  set AbsoluteYposition(value) {
    this._AbsoluteYposition = value;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
  get yScaleIndex() {
    return this._YScaleIndex;
  }
  set yScaleIndex(value) {
    this._YScaleIndex = value;
    if (this._enabled)
      this._parentRenderer.redraw();
  }
};
(function(DataPanel2) {
  class HorizontalAlignEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, HorizontalAlign);
    }
  }
  DataPanel2.HorizontalAlignEnumItem = HorizontalAlignEnumItem;
  class HorizontalAlign extends YEnum {
  }
  HorizontalAlign.LEFTOF = new HorizontalAlignEnumItem("LEFTOF", "Left");
  HorizontalAlign.CENTERED = new HorizontalAlignEnumItem("CENTERED", "Center");
  HorizontalAlign.RIGHTOF = new HorizontalAlignEnumItem("RIGHTOF", "Right");
  DataPanel2.HorizontalAlign = HorizontalAlign;
  class VerticalAlignEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, VerticalAlign);
    }
  }
  DataPanel2.VerticalAlignEnumItem = VerticalAlignEnumItem;
  class VerticalAlign extends YEnum {
  }
  VerticalAlign.ABOVE = new VerticalAlignEnumItem("ABOVE", "Top");
  VerticalAlign.CENTERED = new VerticalAlignEnumItem("CENTERED", "Center");
  VerticalAlign.BELOW = new VerticalAlignEnumItem("BELOW", "Bottom");
  DataPanel2.VerticalAlign = VerticalAlign;
  class HorizontalPositionEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, HorizontalPosition);
    }
  }
  DataPanel2.HorizontalPositionEnumItem = HorizontalPositionEnumItem;
  class HorizontalPosition extends YEnum {
  }
  HorizontalPosition.LEFTBORDER = new HorizontalPositionEnumItem("LEFTBORDER", "Left border");
  HorizontalPosition.ABSOLUTEX = new HorizontalPositionEnumItem("ABSOLUTEX", "Absolute X position");
  HorizontalPosition.RIGHTBORDER = new HorizontalPositionEnumItem("RIGHTBORDER", "Right borde");
  DataPanel2.HorizontalPosition = HorizontalPosition;
  class VerticalPositionEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, VerticalPosition);
    }
  }
  DataPanel2.VerticalPositionEnumItem = VerticalPositionEnumItem;
  class VerticalPosition extends YEnum {
  }
  VerticalPosition.TOPBORDER = new VerticalPositionEnumItem("TOPBORDER", "Top border");
  VerticalPosition.ABSOLUTEY = new VerticalPositionEnumItem("ABSOLUTEY", "Absolute Y position");
  VerticalPosition.BOTTOMBORDER = new VerticalPositionEnumItem("BOTTOMBORDER", "Bottom border");
  DataPanel2.VerticalPosition = VerticalPosition;
})(DataPanel || (DataPanel = {}));
var YCursor = class {
  constructor(pngImageData) {
  }
  get handle() {
    return "crosshair";
  }
};
var YTimeSpan = class {
  constructor(value) {
    this._value = 0;
    this._value = value;
  }
  toString(format) {
    let res = "";
    let v = Math.abs(this._value / 1e3);
    let dec = Math.floor(v);
    let frac = Math.round(1e3 * (v - Math.floor(v)));
    if (format & YDate.ms1) {
      let ms = "00" + frac.toString();
      res = "." + ms.substring(ms.length - 3).substring(0, 1);
    } else if (format & YDate.ms01) {
      let ms = "00" + frac.toString();
      res = "." + ms.substring(ms.length - 3).substring(0, 2);
    } else if (format & YDate.ms001) {
      let ms = "00" + frac.toString();
      res = "." + ms.substring(ms.length - 3).substring(0, 3);
    }
    if (format & YDate.s) {
      let sec = dec % 60;
      let s = "0" + sec.toString();
      res = s.substring(s.length - 2) + res + "s";
    }
    dec = Math.floor(dec / 60);
    if (format & YDate.m) {
      let min = dec % 60;
      let s = "0" + min.toString();
      res = s.substring(s.length - 2) + "m" + res;
    }
    dec = Math.floor(dec / 60);
    if (format & YDate.h) {
      let hrs = dec % 24;
      let s = "0" + hrs.toString();
      res = s.substring(s.length - 2) + "h" + res;
    }
    dec = Math.floor(dec / 24);
    if (format & YDate.D) {
      let s = dec.toString();
      res = s.substring(s.length - 2) + "d" + res;
    }
    return res;
  }
};
YTimeSpan.TicksPerSecond = 1e3;
var YGraph = class _YGraph extends YDataRenderer {
  get legendPanel() {
    return this._legendPanel;
  }
  get dataTracker() {
    return this._dataTracker;
  }
  get borderColor() {
    return this._borderColor;
  }
  set borderColor(value) {
    this._borderColor = value;
    this._borderPen = null;
    this.redraw();
  }
  get borderThickness() {
    return this._borderThickness;
  }
  set borderThickness(value) {
    if (value < 0)
      throw new RangeError("thickness must be a positive value");
    this._borderThickness = value;
    this._borderPen = null;
    this.redraw();
  }
  static get verticalDragZoomEnabled() {
    return _YGraph._defaultVerticalDragZoomEnabled;
  }
  static set verticalDragZoomEnabled(value) {
    _YGraph._defaultVerticalDragZoomEnabled = value;
  }
  static createCaptureCursor() {
    if (_YGraph.captureCursor != null)
      return;
    let base64png = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAALHRFWHRDcmVhdGlvbiBUaW1lAFRodSAyMCBBdWcgMjAyMCAxMjoxNTo1MCArMDEwMP38NhoAAAAHdElNRQfkCBQOJAvCrm0ZAAAACXBIWXMAAFxGAABcRgEUlENBAAAABGdBTUEAALGPC / xhBQAAAmVJREFUeNrtVrFuFDEQHd9eBImAIBFoAgVC0NJQUFDwL / QgRXzAfUz + Iy0pQwsIJAhCQMQBUS5c2DXv7T5fzN5qs / Fxdw0jPY3ttT3jt54ZmyXIYDDoQXnodWBV / SRJXZhJbwCXov65pZ + 4zklfBsZRf2EMxOuTjc / igIu0s0UygAvnonWZ0NP4fB2QkQz6yHvP / i76V4E1jZ / bic4LIuMnND7ZwDl + u4 / me + AnkKPvu + 7rFMNZBzZ64eRTm5w6sQ8cAcUZexVytOCpOnv7j + QdDD + CHgKjMg80nWpeQrYgd4A3wHjWPJAqN4BVoLcsB1ZMd25ZDkzkvwNLd6AMQ4XGIuXElKz6SAo3rYpLhsZKyyJmwu2WTLiF5g / g2NozIY1 / BkacRwaYkZgUPlr7L2EdeAhjLxpqwTOr0vAHOZG37FPIOO3mfXXG1qEWAGvM + TC6Rydk / KmMv7bmWlD / v95qtaCThGpo1TPsFvp7wBO0v4nBqWoYvR0yOeLFThHmdH4TcgGQy8i + hkn5V / WbSjGr7e9oD96372LJB1o7izbPtYHpPw7V / 8u4yvzFkvPTO3MXuMbx8JRPij8svgAcAw / EwCe0f8XGxZYFB6JQZzl + rHWj1Gf5WZLVTj5py5HbVoXjeF4OuMhYnQHKulW / x6U64CPtbfpVxQu7CX0PeicMov3cKup5EZmw / KwMFNb8pMtl5G3MhIyHkOX3PPUSMmXzOX7Fqsw35Gu5NidEwXWr / jnnD + XUFzLARJRaDXnCQ53o0BpSLzcXzQfAK + Cl9EEwXrKTyr1OWGa3sFnLvPDsn6Tg8P0PrBcSMR2NtfsAAAAASUVORK5CYII =";
    try {
      _YGraph.captureCursor = new YCursor(atob(base64png));
    } catch (Exception) {
      console.log("Cannot create custom cursor");
    }
  }
  get dataPanels() {
    return this._dataPanels;
  }
  addDataPanel() {
    let p = new DataPanel(this, this);
    this._dataPanels.push(p);
    return p;
  }
  setMarkerCaptureCallbacks(start, stop) {
    this._markerCaptureStartedCallback = start;
    this._markerCaptureStoppedCallback = stop;
  }
  startMarkerCapture(m) {
    if (this._markerCaptureStartedCallback != null)
      this._markerCaptureStartedCallback(m);
    this.markerCapture = m;
    this.UIContainer.focus();
  }
  constructor(ChartContainer, logFunction) {
    super(ChartContainer, logFunction);
    this._markerCaptureStartedCallback = null;
    this._markerCaptureStoppedCallback = null;
    this.lastPointCount = -1;
    this.lastTopMargin = -1;
    this.lastBottomMargin = -1;
    this.navigatorCache = null;
    this.markerCapture = null;
    this._borderPen = null;
    this._borderColor = YColor.LightGray;
    this._borderThickness = 1;
    this._touchStartfct = null;
    this._touchMovefct = null;
    this._touchEndfct = null;
    this._mouseDownfct = null;
    this._mouseMovefct = null;
    this._mouseWheelfct = null;
    this._mouseKeyDownfct = null;
    this.mainViewPort = new ViewPortSettings();
    this._timeRange = null;
    this._bgBrush = null;
    this._bgColor1 = YColor.FromArgb(255, 200, 200, 200);
    this._bgColor2 = YColor.FromArgb(255, 255, 255, 255);
    this._touchStartPinchDistance = -1;
    this._touchStartPinchCenter = new Point(0, 0);
    this._touchStartPinchZoom = 1;
    this._touchStartPinchIRLx = 0;
    this._touchStartPinchRange = 0;
    this.dataTrackerRefreshtimeout = null;
    _YGraph.createCaptureCursor();
    this._xAxis = new XAxis(this, this);
    this._yAxes = [];
    this._series = [];
    this._dataPanels = [];
    this._navigator = new Navigator(this, this);
    this._legendPanel = new LegendPanel(this, this);
    this._dataTracker = new DataTracker(this, this);
    this._touchStartfct = (e) => {
      this.TouchStart(this.UIContainer, e);
    };
    this._touchMovefct = (e) => {
      this.TouchMove(this.UIContainer, e);
    };
    this._touchEndfct = (e) => {
      this.TouchEnd(this.UIContainer, e);
    };
    this._mouseDownfct = (e) => {
      this.MouseDown(this.UIContainer, e);
    };
    this._mouseMovefct = (e) => {
      this.MouseMove(this.UIContainer, e);
    };
    this._mouseWheelfct = (e) => {
      this.mouseWheelEvent(this.UIContainer, e);
    };
    this._mouseKeyDownfct = (e) => {
      this.KeyDown(this.UIContainer, e);
    };
    this.UIContainer.addEventListener("touchstart", this._touchStartfct);
    this.UIContainer.addEventListener("touchmove", this._touchMovefct);
    this.UIContainer.addEventListener("touchend", this._touchEndfct);
    this.UIContainer.addEventListener("mousedown", this._mouseDownfct);
    this.UIContainer.addEventListener("mousemove", this._mouseMovefct);
    this.UIContainer.addEventListener("wheel", this._mouseWheelfct);
    this.UIContainer.addEventListener("keydown", this._mouseKeyDownfct);
    this._timeRange = MinMaxHandler.DefaultValue();
    let originalContainerWidth = ChartContainer.width;
    let originalContainerHeight = ChartContainer.height;
    let originalFormWidth = ChartContainer.width;
    let originalFormHeight = ChartContainer.height;
  }
  destroy() {
    if (this._touchStartfct != null)
      this.UIContainer.removeEventListener("touchstart", this._touchStartfct);
    if (this._touchMovefct != null)
      this.UIContainer.removeEventListener("touchstart", this._touchMovefct);
    if (this._touchEndfct != null)
      this.UIContainer.removeEventListener("touchstart", this._touchEndfct);
    if (this._mouseDownfct != null)
      this.UIContainer.removeEventListener("mousedown", this._mouseDownfct);
    if (this._mouseMovefct != null)
      this.UIContainer.removeEventListener("mousemove", this._mouseMovefct);
    if (this._mouseWheelfct != null)
      this.UIContainer.removeEventListener("wheel", this._mouseWheelfct);
    if (this._mouseKeyDownfct != null)
      this.UIContainer.removeEventListener("keydown", this._mouseKeyDownfct);
    super.destroy();
    Object.entries(this).forEach((pair) => {
      Reflect.set(this, pair[0], null);
    });
  }
  getMostRecentPoint() {
    let res = new pointXY(NaN, NaN);
    for (let i = 0; i < this._series.length; i++)
      if (!this.series[i].disabled) {
        let p = this.series[i].getlastPoint();
        if (!isNaN(p.x)) {
          if (isNaN(res.x))
            res = p;
          else if (p.x > res.x)
            res = p;
        }
      }
    return res;
  }
  adjustGlobalTimeRange(x) {
    let max = this._timeRange.Max;
    this._timeRange = MinMaxHandler.CombineWithNumber(this._timeRange, x);
    if (isNaN(max))
      return;
    let ofset = x - max;
    if (ofset > 0) {
      switch (this._xAxis.overflowHandling) {
        case XAxis.OverflowHandling.SCROLL:
          if (max > this._xAxis.min + (this._xAxis.max - this._xAxis.min) * 0.85 && max <= this._xAxis.max) {
            this.DisableRedraw();
            this._xAxis.set_minMax(this._xAxis.min + ofset, this._xAxis.max + ofset);
            this.AllowRedraw();
          }
          break;
        case XAxis.OverflowHandling.CONTRACT:
          if (max > this._xAxis.min + (this._xAxis.max - this._xAxis.min) * 0.95 && max <= this._xAxis.max) {
            this.DisableRedraw();
            this._xAxis.max += ofset;
            this.AllowRedraw();
          }
          break;
      }
    }
  }
  get bgColor1() {
    return this._bgColor1;
  }
  set bgColor1(value) {
    this._bgColor1 = value;
    this._bgBrush = null;
    this.redraw();
  }
  get bgColor2() {
    return this._bgColor2;
  }
  set bgColor2(value) {
    this._bgColor2 = value;
    this._bgBrush = null;
    this.redraw();
  }
  get xAxis() {
    return this._xAxis;
  }
  get navigator() {
    return this._navigator;
  }
  get yAxes() {
    return this._yAxes;
  }
  get series() {
    return this._series;
  }
  addYAxis() {
    let s = new YAxis(this, this, this._yAxes.length);
    this._yAxes.push(s);
    this.redraw();
    return s;
  }
  addSerie() {
    let s = new DataSerie(this);
    this._series.push(s);
    this.redraw();
    return s;
  }
  clearCachedObjects() {
    this._bgBrush = null;
    this.navigatorCache = null;
  }
  TouchStart(sender, e) {
    if (e.touches.length == 1) {
      this.HandleMouseDown(sender, e.touches[0].pageX, e.touches[0].pageY);
    } else if (e.touches.length == 2) {
      e.preventDefault();
      this.HandleEndOfMouseCapture();
      this._touchStartPinchDistance = Math.sqrt(Math.pow(e.touches[1].pageX - e.touches[0].pageX, 2) + Math.pow(e.touches[1].pageY - e.touches[0].pageY, 2));
      this._touchStartPinchCenter = new Point(e.touches[1].pageX + e.touches[0].pageX >> 2, e.touches[1].pageY + e.touches[0].pageY >> 2);
      this._touchStartPinchZoom = this.mainViewPort.zoomx;
      this._touchStartPinchIRLx = this.mainViewPort.IRLx;
      this._touchStartPinchRange = this._xAxis.max - this._xAxis.min;
    }
  }
  MouseDown(sender, e) {
    if (e.buttons == 2 && this.markerCapture != null) {
      this.markerCapture = null;
      if (this._markerCaptureStoppedCallback != null)
        this._markerCaptureStoppedCallback(null);
    }
    if (e.buttons != 1)
      return;
    this.HandleMouseDown(sender, e.pageX, e.pageY);
  }
  HandleMouseDown(sender, pageX, pageY) {
    let p = this.Scr2ElmMatrix.multiplyByV(Vector3.FromXYCoord(pageX, pageY)).toPoint();
    let eX = p.X;
    let eY = p.Y;
    if (eX >= this.mainViewPort.Lmargin && eX <= this.mainViewPort.Width - this.mainViewPort.Rmargin && eY >= this.mainViewPort.Tmargin && eY <= this.mainViewPort.Height - this.mainViewPort.Bmargin) {
      if (this.markerCapture != null) {
        let p22 = _YGraph.ViewPortPointToIRL(this.mainViewPort, new Point(eX, eY));
        this.markerCapture.setCapturedPosition(p22.x, this.xAxis);
        if (this._markerCaptureStoppedCallback != null)
          this._markerCaptureStoppedCallback(this.markerCapture);
        this.markerCapture = null;
        return;
      }
      let p2 = _YGraph.ViewPortPointToIRL(this.mainViewPort, new Point(eX, eY));
      this.mainViewPort.OriginalXAxisMin = this.xAxis.min;
      this.mainViewPort.OriginalXAxisMax = this.xAxis.max;
      this.mainViewPort.OriginalIRLx = this.mainViewPort.IRLx;
      this.mainViewPort.OriginalLmargin = this.mainViewPort.Lmargin;
      this.mainViewPort.OriginalZoomx = this.mainViewPort.zoomx;
      this.mainViewPort.CaptureStartY = eY;
      this.mainViewPort.IRLCaptureStartX = p2.x;
      this.mainViewPort.Capture = true;
    } else if (eX >= this._navigator.viewport.Lmargin && eX <= this._navigator.viewport.Width - this._navigator.viewport.Rmargin && eY >= this._navigator.viewport.Lmargin && eY <= this._navigator.viewport.Height - this._navigator.viewport.Bmargin) {
      let p2 = _YGraph.ViewPortPointToIRL(this._navigator.viewport, new Point(eX, eY));
      let p22 = _YGraph.ViewPortPointToIRL(this.mainViewPort, new Point(this.mainViewPort.Lmargin, 0));
      let p3 = _YGraph.ViewPortPointToIRL(this.mainViewPort, new Point(this.mainViewPort.Width - this.mainViewPort.Rmargin, 0));
      if (p2.x >= p22.x && p2.x <= p3.x) {
        this._navigator.startCapture(p2, this._xAxis.min, this._xAxis.max);
      } else {
        this.DisableRedraw();
        let min = p2.x - (p3.x - p22.x) / 2;
        let max = min + (p3.x - p22.x);
        this._xAxis.set_minMax(min, max);
        this.AllowRedraw();
        this.Draw(0);
      }
    }
  }
  TouchMove(sender, e) {
    if (e.touches.length == 1) {
      e.preventDefault();
      this.HandleMouseMove(sender, e.touches[0].pageX, e.touches[0].pageY);
    } else if (e.touches.length == 2) {
      e.preventDefault();
      let newdistance = Math.sqrt(Math.pow(e.touches[1].pageX - e.touches[0].pageX, 2) + Math.pow(e.touches[1].pageY - e.touches[0].pageY, 2));
      let ZoomFactor = newdistance / this._touchStartPinchDistance;
      let NextZoomX = this._touchStartPinchZoom * ZoomFactor;
      if (NextZoomX > this.mainViewPort.zoomx && NextZoomX > 1e3)
        return;
      let currentRange = this._xAxis.max - this._xAxis.min;
      this.mainViewPort.IRLx = this._touchStartPinchIRLx + (this._touchStartPinchCenter.X - this.mainViewPort.Lmargin) / this._touchStartPinchZoom - (this._touchStartPinchCenter.X - this.mainViewPort.Lmargin) / NextZoomX;
      this._xAxis.set_minMax(this.mainViewPort.IRLx, this.mainViewPort.IRLx + this._touchStartPinchRange / ZoomFactor);
      this.mainViewPort.zoomx = NextZoomX;
      this.redraw();
    }
  }
  TouchEnd(sender, e) {
    this.HandleEndOfMouseCapture();
  }
  MouseMove(sender, e) {
    if (e.buttons != 1 && (this.mainViewPort.Capture || this._navigator.Capture))
      this.HandleEndOfMouseCapture();
    this.HandleMouseMove(sender, e.pageX, e.pageY);
    if (this.dataTracker.enabled) {
      if (this.dataTrackerRefreshtimeout != null)
        clearTimeout(this.dataTrackerRefreshtimeout);
      this.dataTrackerRefreshtimeout = setTimeout(() => {
        this.redraw();
      }, 100);
    }
  }
  HandleEndOfMouseCapture() {
    this.mainViewPort.Capture = false;
    this._navigator.stopCapture();
    if (this._dataTracker.enabled)
      this.redraw();
  }
  HandleMouseMove(sender, pageX, pageY) {
    let p = this.Scr2ElmMatrix.multiplyByV(Vector3.FromXYCoord(pageX, pageY)).toPoint();
    let eX = p.X;
    let eY = p.Y;
    if (this.markerCapture != null) {
      if (eX > this.mainViewPort.Lmargin && eX < this.mainViewPort.Width - this.mainViewPort.Rmargin && eY > this.mainViewPort.Tmargin && eY < this.mainViewPort.Height - this.mainViewPort.Bmargin) {
        if (this.UIContainer.style.cursor != _YGraph.captureCursor.handle && this.UIContainer.style.cursor != "crosshair") {
          this.UIContainer.style.cursor = _YGraph.captureCursor != null ? _YGraph.captureCursor.handle : "crosshair";
        }
      } else if (this.UIContainer.style.cursor != "default")
        this.UIContainer.style.cursor = "default";
    } else if (this.UIContainer.style.cursor != "default")
      this.UIContainer.style.cursor = "default";
    if (this.mainViewPort.Capture) {
      let x1 = this.mainViewPort.OriginalIRLx + (eX - this.mainViewPort.OriginalLmargin) / this.mainViewPort.OriginalZoomx;
      let deltaX = x1 - this.mainViewPort.IRLCaptureStartX;
      let deltaY = eY - this.mainViewPort.CaptureStartY;
      this.DisableRedraw();
      let halfAxisDelta = (this.mainViewPort.OriginalXAxisMax - this.mainViewPort.OriginalXAxisMin) / 2;
      let Axismiddle = (this.mainViewPort.OriginalXAxisMax + this.mainViewPort.OriginalXAxisMin) / 2;
      let deltaCoef = _YGraph._defaultVerticalDragZoomEnabled && Math.abs(deltaY) > 10 ? Math.pow(1.01, deltaY) : 1;
      this._xAxis.set_minMax(Axismiddle - halfAxisDelta * deltaCoef - deltaX, Axismiddle + halfAxisDelta * deltaCoef - deltaX);
      this.AllowRedraw();
      this.redraw();
      return;
    }
    if (this._navigator.viewport.Capture) {
      let x1 = this._navigator.viewport.OriginalIRLx + (eX - this._navigator.viewport.OriginalLmargin) / this._navigator.viewport.OriginalZoomx;
      let delta = x1 - this._navigator.viewport.IRLCaptureStartX;
      this.DisableRedraw();
      this._xAxis.set_minMax(this._navigator.viewport.OriginalXAxisMin + delta, this._navigator.viewport.OriginalXAxisMax + delta);
      this.AllowRedraw();
      this.redraw();
      return;
    }
  }
  cross(p) {
  }
  static IRLPointToViewPort(viewport, p, IRLy, zoomy) {
    if (IRLy === void 0) {
      let xx2 = viewport.Lmargin + Math.round((p.x - viewport.IRLx) * viewport.zoomx);
      let yy2 = viewport.Height - viewport.Bmargin - Math.round((p.y - viewport.IRLy) * viewport.zoomy);
      return new Point(xx2 >> 0, yy2 >> 0);
    }
    let xx = viewport.Lmargin + Math.round((p.x - viewport.IRLx) * viewport.zoomx);
    let yy = viewport.Height - viewport.Bmargin - Math.round((p.y - IRLy) * zoomy);
    return new Point(xx >> 0, yy >> 0);
  }
  static IRLPointSummaryToViewPort(viewport, p, IRLy, zoomy) {
    if (IRLy === void 0) {
      let x12 = viewport.Lmargin + Math.round((p.x1 - viewport.IRLx) * viewport.zoomx);
      let ymin2 = viewport.Height - viewport.Bmargin - Math.round((p.ymin - viewport.IRLy) * viewport.zoomy);
      let x22 = viewport.Lmargin + Math.round((p.x2 - viewport.IRLx) * viewport.zoomx);
      let ymax2 = viewport.Height - viewport.Bmargin - Math.round((p.ymax - viewport.IRLy) * viewport.zoomy);
      return new minMaxPoint(x12, ymin2, x22, ymax2);
    }
    let x1 = viewport.Lmargin + Math.round((p.x1 - viewport.IRLx) * viewport.zoomx);
    let ymin = viewport.Height - viewport.Bmargin - Math.round((p.ymin - IRLy) * zoomy);
    let x2 = viewport.Lmargin + Math.round((p.x2 - viewport.IRLx) * viewport.zoomx);
    let ymax = viewport.Height - viewport.Bmargin - Math.round((p.ymax - IRLy) * zoomy);
    return new minMaxPoint(x1, ymin, x2, ymax);
  }
  static ViewPortPointToIRL(viewport, p, IRLy, zoomy) {
    if (IRLy === void 0) {
      return new pointXY(viewport.IRLx + (p.X - viewport.Lmargin) / viewport.zoomx, viewport.IRLy + (+viewport.Height - p.Y - viewport.Bmargin) / viewport.zoomy);
    }
    return new pointXY(viewport.IRLx + (p.X - viewport.Lmargin) / viewport.zoomx, IRLy + (+viewport.Height - p.Y - viewport.Bmargin) / zoomy);
  }
  static FindMinMax(start, end, data, count) {
    let res = MinMaxHandler.DefaultValue();
    if (data[0].x > end)
      return res;
    if (data[count - 1].x < start)
      return res;
    let N1 = 0;
    let N2 = 0;
    let First = 0;
    if (data[0].x < start) {
      N1 = 0;
      N2 = count - 1;
      while (N2 - N1 > 1) {
        let N = N1 + N2 >> 1;
        if (data[N].x > start)
          N2 = N;
        else
          N1 = N;
      }
      First = N1 - 1;
      if (First < 0)
        First = 0;
    }
    let Last = count - 1;
    if (data[Last] === void 0) {
      debugger;
    }
    if (data[Last].x > end) {
      N1 = 0;
      N2 = count - 1;
      while (N2 - N1 > 1) {
        let N = N1 + N2 >> 1;
        if (data[N].x < end)
          N1 = N;
        else
          N2 = N;
      }
      Last = N2 + 1;
      if (Last > count - 1)
        Last = count - 1;
    }
    res.Min = data[First].y;
    res.Max = data[First].y;
    for (let i = First + 1; i <= Last; i++) {
      if (data[i].y < res.Min)
        res.Min = data[i].y;
      if (data[i].y > res.Max)
        res.Max = data[i].y;
    }
    return res;
  }
  resetlegendPens() {
    for (let i = 0; i < this._series.length; i++) {
      this._series[i].resetlegendPen();
    }
  }
  drawLegendPanel(g, viewPortWidth, viewPortHeight, mainViewPort) {
    let verticalRatio = 1.25;
    if (!this._legendPanel.enabled)
      return;
    let legendWidths = new Array(this._series.length);
    let legendHeight = new Array(this._series.length);
    let ofsetx = new Array(this._series.length);
    let ofsety = new Array(this._series.length);
    let legends = new Array(this._series.length);
    let totalHeight = 0;
    let totalWidth = 0;
    let maxWidth = 0;
    let maxHeight = 0;
    g.TextRenderingHint = YTextRenderingHint.SingleBitPerPixelGridFit;
    g.SetClip(new YRectangle(0, 0, viewPortWidth, viewPortHeight));
    for (let i = 0; i < this._series.length; i++) {
      if (this._series[i].legend != "")
        legends[i] = this._series[i].legend;
      else
        legends[i] = "Series " + (i + 1).toString();
    }
    if (this._legendPanel.position == LegendPanel.Position.TOP || this._legendPanel.position == LegendPanel.Position.BOTTOM) {
      let availableWidth = viewPortWidth - 2 * this._legendPanel.padding + this._legendPanel.borderthickness;
      if (this._legendPanel.overlap)
        availableWidth = availableWidth - mainViewPort.Lmargin - mainViewPort.Rmargin;
      totalHeight = 0;
      let xx = 0;
      let yy = 0;
      for (let i = 0; i < this._series.length; i++) {
        if (this._series[i].segments.length > 0 && this._series[i].visible && !this._series[i].disabled) {
          let ssize = g.MeasureString(legends[i], this._legendPanel.font, 1e5);
          legendHeight[i] = ssize.height + 1;
          let ww = ssize.width + 20;
          if (xx == 0)
            totalHeight += ssize.height;
          if (availableWidth - xx < ww) {
            if (xx == 0) {
              ofsetx[i] = xx;
              ofsety[i] = yy;
              yy += ssize.height;
              if (maxWidth < ww)
                maxWidth = ww;
            } else {
              yy += ssize.height;
              ofsetx[i] = 0;
              ofsety[i] = yy;
              xx = ww;
              totalHeight += ssize.height;
              if (maxWidth < xx)
                maxWidth = xx;
            }
          } else {
            ofsetx[i] = xx;
            ofsety[i] = yy;
            xx += ww;
            if (maxWidth < xx)
              maxWidth = xx;
          }
        }
      }
      if (totalWidth > availableWidth) {
        totalWidth = availableWidth;
      }
    } else {
      let ty = 0;
      for (let i = 0; i < this._series.length; i++) {
        if (this._series[i].segments.length > 0 && this._series[i].visible && !this._series[i].disabled) {
          let ssize = g.MeasureString(legends[i], this._legendPanel.font, 1e5);
          legendWidths[i] = ssize.width + 1;
          if (maxWidth < legendWidths[i] + 20)
            maxWidth = legendWidths[i] + 20;
          legendHeight[i] = ssize.height + 1;
          if (maxHeight < legendHeight[i])
            maxHeight = legendHeight[i];
          ofsetx[i] = 0;
          ofsety[i] = ty;
          ty += ssize.height * verticalRatio;
          totalHeight += i == 0 ? ssize.height : ssize.height * verticalRatio;
        }
      }
    }
    let w = maxWidth + 2 * this._legendPanel.padding + this._legendPanel.borderthickness;
    let h = totalHeight + 2 * this._legendPanel.padding + this._legendPanel.borderthickness;
    let x = 0;
    let y = 0;
    switch (this._legendPanel.position) {
      case LegendPanel.Position.LEFT:
        x = this._legendPanel.horizontalMargin;
        if (!this._legendPanel.overlap) {
          mainViewPort.Lmargin += w + 2 * this.legendPanel.horizontalMargin + this.legendPanel.borderthickness >> 0;
          y = (viewPortHeight - h) / 2;
        } else {
          x += mainViewPort.Lmargin;
          y = mainViewPort.Tmargin + (viewPortHeight - mainViewPort.Tmargin - mainViewPort.Bmargin - h) / 2;
        }
        break;
      case LegendPanel.Position.TOPLEFT:
        x = this._legendPanel.horizontalMargin;
        y = this._legendPanel.verticalMargin;
        if (!this._legendPanel.overlap) {
          mainViewPort.Lmargin += w + 2 * this.legendPanel.horizontalMargin + this.legendPanel.borderthickness >> 0;
        } else {
          x += mainViewPort.Lmargin;
          y += mainViewPort.Tmargin;
        }
        break;
      case LegendPanel.Position.TOP:
        if (!this._legendPanel.overlap) {
          x = (viewPortWidth - w) / 2 - this._legendPanel.horizontalMargin - this._legendPanel.borderthickness;
          y = this._legendPanel.verticalMargin + this._legendPanel.borderthickness;
          mainViewPort.Tmargin += totalHeight + this._legendPanel.verticalMargin + 2 * this._legendPanel.verticalMargin + this._legendPanel.borderthickness >> 0;
        } else {
          x = mainViewPort.Lmargin + (viewPortWidth - mainViewPort.Lmargin - mainViewPort.Rmargin - w) / 2 - this._legendPanel.horizontalMargin - this._legendPanel.borderthickness;
          y = mainViewPort.Tmargin + this._legendPanel.verticalMargin - this._legendPanel.borderthickness;
        }
        break;
      case LegendPanel.Position.TOPRIGHT:
        x = viewPortWidth - this._legendPanel.horizontalMargin - w;
        y = this._legendPanel.verticalMargin;
        if (!this._legendPanel.overlap) {
          mainViewPort.Rmargin += w + 2 * this._legendPanel.horizontalMargin + this._legendPanel.borderthickness >> 0;
        } else {
          x -= mainViewPort.Rmargin;
          y += mainViewPort.Tmargin;
        }
        break;
      case LegendPanel.Position.RIGHT:
        x = viewPortWidth - this._legendPanel.horizontalMargin - w;
        if (!this._legendPanel.overlap) {
          mainViewPort.Rmargin += w + 2 * this._legendPanel.horizontalMargin + this._legendPanel.borderthickness >> 0;
          y = (viewPortHeight - h) / 2;
        } else {
          x -= mainViewPort.Rmargin;
          y = mainViewPort.Tmargin + (viewPortHeight - mainViewPort.Tmargin - mainViewPort.Bmargin - h) / 2;
        }
        break;
      case LegendPanel.Position.BOTTOMRIGHT:
        x = viewPortWidth - this._legendPanel.horizontalMargin - w;
        if (!this._legendPanel.overlap) {
          mainViewPort.Rmargin += w + 2 * this._legendPanel.horizontalMargin + this._legendPanel.borderthickness >> 0;
          y = viewPortHeight - this._legendPanel.verticalMargin - h;
        } else {
          x -= mainViewPort.Rmargin;
          y = viewPortHeight - mainViewPort.Bmargin - h - this._legendPanel.verticalMargin;
        }
        break;
      case LegendPanel.Position.BOTTOM:
        if (!this._legendPanel.overlap) {
          x = (viewPortWidth - w) / 2 - this._legendPanel.horizontalMargin - this._legendPanel.borderthickness;
          y = viewPortHeight - this._legendPanel.verticalMargin - 2 * this._legendPanel.padding - this._legendPanel.borderthickness - totalHeight;
          mainViewPort.Bmargin += totalHeight + 2 * this._legendPanel.padding + 2 * this._legendPanel.verticalMargin + this._legendPanel.borderthickness;
        } else {
          x = mainViewPort.Lmargin + (viewPortWidth - mainViewPort.Lmargin - mainViewPort.Rmargin - w) / 2 - this._legendPanel.horizontalMargin - this._legendPanel.borderthickness;
          y = viewPortHeight - mainViewPort.Bmargin - totalHeight - 2 * this._legendPanel.padding - 2 * this._legendPanel.verticalMargin - this._legendPanel.borderthickness;
        }
        break;
      case LegendPanel.Position.BOTTOMLEFT:
        x = this._legendPanel.horizontalMargin;
        y = this._legendPanel.verticalMargin;
        if (!this._legendPanel.overlap) {
          mainViewPort.Lmargin += w + 2 * this._legendPanel.horizontalMargin + this._legendPanel.borderthickness;
          y = viewPortHeight - this._legendPanel.verticalMargin - h;
        } else {
          x += mainViewPort.Lmargin;
          y = viewPortHeight - mainViewPort.Bmargin - h - this._legendPanel.verticalMargin;
        }
        break;
    }
    let rect = new YRectangle(x >> 0, y >> 0, w >> 0, h >> 0);
    g.FillRectangle(this._legendPanel.bgBrush, rect);
    g.DrawRectangle(this._legendPanel.pen, rect);
    g.TextRenderingHint = YTextRenderingHint.AntiAlias;
    for (let i = 0; i < this._series.length; i++) {
      if (this._series[i].segments.length > 0 && this._series[i].visible && !this._series[i].disabled) {
        g.DrawStringXY(legends[i], this._legendPanel.font, this._legendPanel.font.brush, x + ofsetx[i] + 20 + this._legendPanel.padding >> 0, y + ofsety[i] + this._legendPanel.padding >> 0);
        let px = x + ofsetx[i] + this._legendPanel.borderthickness / 2 + this._legendPanel.padding + 6 >> 0;
        let py = y + ofsety[i] + this._legendPanel.padding + legendHeight[i] / 2 >> 0;
        g.DrawLine(this._series[i].legendPen, new PointF(px, py), new PointF(px + 12, py));
      }
    }
  }
  static DoSegmentRendering(w, g, p, data, count, xTimeStart, xTimeEnd) {
    if (data[0].x > xTimeEnd || data[count - 1].x < xTimeStart)
      return 0;
    let isSVG = g instanceof YGraphicsSVG;
    let N1 = 0;
    let N2 = 0;
    let First = 0;
    if (data[0].x < xTimeStart) {
      N1 = 0;
      N2 = count - 1;
      while (N2 - N1 > 1) {
        let N = N1 + N2 >> 1;
        if (data[N].x > xTimeStart)
          N2 = N;
        else
          N1 = N;
      }
      First = N1 - 1;
      if (First < 0)
        First = 0;
    }
    let Last = count - 1;
    if (data[Last].x > xTimeEnd) {
      N1 = 0;
      N2 = count - 1;
      while (N2 - N1 > 1) {
        let N = N1 + N2 >> 1;
        if (data[N].x < xTimeEnd)
          N1 = N;
        else
          N2 = N;
      }
      Last = N2 + 1;
      if (Last > count - 1)
        Last = count - 1;
    }
    if (Last - First > 2 * w.Width - w.Lmargin - w.Rmargin) {
      let ToDraw = new Array(3 * (Last - First + 1));
      let Current = _YGraph.IRLPointToViewPort(w, data[First]);
      let New;
      let i = First + 1;
      let n = 0;
      let max;
      let min;
      let limit;
      while (i < Last) {
        ToDraw[n++] = new PointF(Current.X, Current.Y);
        min = data[i].y;
        max = min;
        limit = _YGraph.ViewPortPointToIRL(w, new Point(Current.X + 1, Current.Y + 1)).x;
        do {
          if (data[i].y > max)
            max = data[i].y;
          if (data[i].y < min)
            min = data[i].y;
          i++;
        } while (i < Last && data[i].x < limit);
        let p1 = _YGraph.IRLPointToViewPort(w, new pointXY(data[i].x, min));
        let p2 = _YGraph.IRLPointToViewPort(w, new pointXY(data[i].x, max));
        if (Math.abs(p1.Y - p2.Y) > 2) {
          ToDraw[n++] = new PointF(p1.X, p1.Y);
          ToDraw[n++] = new PointF(p2.X, p2.Y);
        }
        Current = _YGraph.IRLPointToViewPort(w, data[i]);
      }
      ToDraw[n++] = Current;
      ToDraw = ToDraw.slice(0, n);
      if (n > 1)
        g.DrawLines(p, ToDraw);
      return n;
    } else {
      if (isSVG) {
        let ToDraw = new Array(Last - First + 1);
        for (let i = First; i <= Last; i++) {
          ToDraw[i - First] = _YGraph.IRLPointToViewPort(w, data[i]);
        }
        g.DrawLines(p, ToDraw);
      } else {
        for (let i = First; i < Last; i++) {
          g.DrawLine(p, _YGraph.IRLPointToViewPort(w, data[i]), _YGraph.IRLPointToViewPort(w, data[i + 1]));
        }
      }
    }
    return Last - First;
  }
  static DoSummarySegmentRendering(w, g, p, data, count, finalPoint, xTimeStart, xTimeEnd) {
    let ToDraw = new Array(2 * count + 1);
    let n = 0;
    if (count > 0) {
      if (data[0].x1 > xTimeEnd || data[count - 1].x2 < xTimeStart)
        return 0;
      let isSVG = g instanceof YGraphicsSVG;
      let N1 = 0;
      let N2 = 0;
      let First = 0;
      if (data[0].x1 < xTimeStart) {
        N1 = 0;
        N2 = count - 1;
        while (N2 - N1 > 1) {
          let N = N1 + N2 >> 1;
          if (data[N].x1 > xTimeStart)
            N2 = N;
          else
            N1 = N;
        }
        First = N1 - 1;
        if (First < 0)
          First = 0;
      }
      let Last = count - 1;
      if (data[Last].x2 > xTimeEnd) {
        N1 = 0;
        N2 = count - 1;
        while (N2 - N1 > 1) {
          let N = N1 + N2 >> 1;
          if (data[N].x2 < xTimeEnd)
            N1 = N;
          else
            N2 = N;
        }
        Last = N2 + 1;
        if (Last > count - 1)
          Last = count - 1;
      }
      let Current;
      let New;
      let i = First;
      let max;
      let min;
      let limit;
      Current = _YGraph.IRLPointSummaryToViewPort(w, data[i]);
      ToDraw[n++] = new PointF(Current.X1, Current.YMIN);
      let ymin = Current.YMAX;
      let ymax = Current.YMIN;
      let x = Current.X1;
      let buffered = 0;
      while (i <= Last) {
        Current = _YGraph.IRLPointSummaryToViewPort(w, data[i]);
        if (ymin > Current.YMAX)
          ymin = Current.YMAX;
        if (ymax < Current.YMIN)
          ymax = Current.YMIN;
        buffered++;
        if (Current.X2 > x) {
          ToDraw[n++] = new PointF(x, ymin);
          if (ymax - ymin > 2)
            ToDraw[n++] = new PointF(x + Current.X2 >> 1, ymax);
          x = Current.X2;
          ymin = Current.YMAX;
          ymax = Current.YMIN;
          buffered = 0;
        }
        i++;
      }
      if (buffered > 0) {
        ToDraw[n++] = new PointF(x, Current.YMIN);
        ToDraw[n++] = new PointF(x + Current.X2 >> 1, Current.YMAX);
      }
    }
    if (finalPoint) {
      let Current = _YGraph.IRLPointSummaryToViewPort(w, finalPoint);
      ToDraw[n++] = new PointF(Current.X1, Current.YMIN);
      ToDraw[n++] = new PointF(Current.X2, Current.YMAX);
    }
    if (n > 1) {
      ToDraw = ToDraw.slice(0, n);
      g.DrawLines(p, ToDraw);
    }
    return n;
  }
  DrawYAxisZones(w, g, scale) {
    if (!scale.visible)
      return;
    let Delta = scale.startStopStep.dataMax - scale.startStopStep.dataMin;
    let YZoom = Delta / (w.Height - w.Bmargin - w.Tmargin);
    for (let i = 0; i < scale.zones.length; i++) {
      if (scale.zones[i].visible) {
        let max = scale.zones[i].max;
        let min = scale.zones[i].min;
        if (Number.isNaN(max)) {
          max = scale.startStopStep.dataMax;
        }
        if (Number.isNaN(min))
          min = scale.startStopStep.dataMin;
        if (max < min) {
          let t = max;
          max = min;
          min = t;
        }
        let y0 = w.Height - w.Bmargin - Math.round((max - scale.startStopStep.dataMin) / YZoom) >> 0;
        let h = Math.round((max - min) / YZoom) >> 0;
        g.FillRectangleXYHW(scale.zones[i].zoneBrush, this.mainViewPort.Lmargin, y0, this.mainViewPort.Width - this.mainViewPort.Rmargin - this.mainViewPort.Lmargin + 1, h);
      }
    }
  }
  DrawXAxisZones(w, g, scale) {
    if (!scale.visible)
      return;
    let delta = scale.max - scale.min;
    let XZoom = delta / (w.Width - w.Lmargin - w.Rmargin);
    for (let i = 0; i < scale.zones.length; i++) {
      if (scale.zones[i].visible) {
        let max = scale.zones[i].max;
        let min = scale.zones[i].min;
        if (Number.isNaN(max))
          max = scale.min;
        if (Number.isNaN(min))
          min = scale.max;
        if (max < min) {
          let t = max;
          max = min;
          min = t;
        }
        let x0 = w.Lmargin + Math.round((min - scale.min) / XZoom) >> 0;
        g.FillRectangleXYHW(scale.zones[i].zoneBrush, x0, this.mainViewPort.Tmargin, (max - min) / XZoom >> 0, this.mainViewPort.Height - this.mainViewPort.Tmargin - this.mainViewPort.Bmargin);
      }
    }
  }
  // noinspection JSSuspiciousNameCombination
  static DrawYAxis(w, g, axis, ofset, simulation) {
    if (!axis.visible) {
      axis.innerWidth = 0;
      return axis.innerWidth;
    }
    let Delta = axis.startStopStep.dataMax - axis.startStopStep.dataMin;
    let YZoom = Delta / (w.Height - w.Bmargin - w.Tmargin);
    let leftSide = axis.position == YAxis.HrzPosition.LEFT;
    let x = leftSide ? w.Lmargin - ofset : w.Width - w.Rmargin + ofset;
    if (!simulation)
      g.DrawLineXY(axis.pen, x, w.Tmargin, x, w.Height - w.Bmargin);
    let format = new YStringFormat(
      16384
      /* YDataRendering.YStringFormat.StringFormatFlags.NoClip */
    );
    format.LineAlignment = 1;
    format.Alignment = leftSide ? 2 : 0;
    let FirstStep = axis.startStopStep.step * Math.floor(axis.startStopStep.start / axis.startStopStep.step);
    if (FirstStep < 0) {
      FirstStep -= axis.startStopStep.step;
    }
    let stepCount = ((Delta - (FirstStep - axis.startStopStep.dataMin)) / axis.startStopStep.step >> 0) + 1;
    if (!simulation)
      g.TextRenderingHint = YTextRenderingHint.AntiAlias;
    let UnitWidth = 0;
    let labelPrecision = 0;
    if (axis.startStopStep.precision < 0)
      labelPrecision = -axis.startStopStep.precision;
    if (stepCount < w.Height) {
      for (let i = 0; i < stepCount; i++) {
        let y = Math.round((FirstStep + i * axis.startStopStep.step - axis.startStopStep.dataMin) / YZoom) >> 0;
        if (y >= 0) {
          y = w.Height - w.Bmargin - y;
          let v = FirstStep + i * axis.startStopStep.step;
          if (!simulation) {
            if (axis.showGrid && (i > 0 || axis.startStopStep.dataMin != 0))
              g.DrawLineXY(axis.gridPen, w.Lmargin, y, w.Width - w.Rmargin, y);
            if (Math.abs(v) < 1e-6 && axis.highlightZero) {
              g.DrawLineXY(axis.pen, w.Lmargin, y, w.Width - w.Rmargin, y);
            }
            g.DrawLineXY(axis.pen, x + (leftSide ? -2 : 2), y, x + (leftSide ? 5 : -5), y);
          }
          let label = v.toFixed(labelPrecision);
          let ssize = g.MeasureString(label, axis.font, 1e5);
          if (ssize.width > UnitWidth)
            UnitWidth = ssize.width;
          if (!simulation) {
            let p = new Point(x + (leftSide ? -3 : 3), y);
            g.DrawStringPF(label, axis.font, axis.font.brush, p, format);
          }
        }
      }
    }
    if (axis.legend.title != "") {
      let size = g.MeasureString(axis.legend.title, axis.legend.font, 1e5);
      if (!simulation) {
        let format2 = new YStringFormat(
          16384
          /* YDataRendering.YStringFormat.StringFormatFlags.NoClip */
        );
        format2.Alignment = 1;
        format2.LineAlignment = 1;
        format2.Trimming = 0;
        let legendX = x + (leftSide ? -UnitWidth - size.height : UnitWidth + size.height + 2) >> 0;
        let legendY = w.Tmargin + (w.Height - w.Tmargin - w.Bmargin) / 2 >> 0;
        g.Transform(legendX, legendY, leftSide ? -Math.PI / 2 : Math.PI / 2);
        g.DrawStringPF(axis.legend.title, axis.legend.font, axis.legend.font.brush, new Point(0, 0), format2);
        g.ResetTransform();
      }
      UnitWidth += size.height;
    }
    axis.innerWidth = (UnitWidth >> 0) + 10;
    return axis.innerWidth;
  }
  DrawMonitorXAxis(w, g, xRange, format) {
    let delta = xRange.Max - xRange.Min;
    let scale = TimeConverter.BestTimeformat(delta, delta, this.xAxis.timeReference);
    let XZoom = delta / (w.Width - w.Lmargin - w.Rmargin);
    let stepCount = (delta / scale.step >> 0) + 2;
    let FirstStep = scale.step * Math.floor(xRange.Min / scale.step);
    if (FirstStep < xRange.Min)
      FirstStep += scale.step;
    g.TextRenderingHint = YTextRenderingHint.AntiAlias;
    let y = w.Height - w.Bmargin;
    g.DrawLineXY(this._navigator.pen, w.Lmargin, w.Height - w.Bmargin - 1, w.Width - w.Rmargin, w.Height - w.Bmargin - 1);
    let label;
    let t = FirstStep;
    do {
      let d = TimeConverter.FromUnixTime(t);
      if (scale.step > 31.1 * 86400) {
        t = TimeConverter.ToUnixTime(new Date(d.getFullYear(), d.getMonth(), 1));
      }
      if (t >= xRange.Min) {
        let x = w.Lmargin + Math.round((t - xRange.Min) / XZoom);
        g.DrawLineXY(this._navigator.pen, x, y, x, y - 4);
        if (format == XAxis.FORMATAUTO) {
          label = TimeConverter.FromUnixTime(t).ToString(scale.format);
        } else {
          label = TimeConverter.FromUnixTime(t).ToString(format);
        }
        let ssize = g.MeasureString(label, this._navigator.font, 1e5);
        g.DrawString(label, this._navigator.font, this._navigator.font.brush, new Point(x - ssize.width / 2, y - ssize.height - 1));
      }
      t += scale.step;
    } while (t < xRange.Max);
  }
  static XLabel(t, scale, scaleFormat, timeRange) {
    let label;
    if (scale.timeReference == TimeConverter.TimeReference.ABSOLUTE) {
      if (scale.labelFormat == XAxis.FORMATAUTO) {
        label = TimeConverter.FromUnixTime(t).ToString(scaleFormat.format);
      } else {
        label = t.toString();
      }
    } else {
      let ticks = YTimeSpan.TicksPerSecond * (Math.round(1e3 * (t - scale.zeroTime)) / 1e3);
      label = ticks < 0 ? "-" + new YTimeSpan(-ticks).toString(scaleFormat.format) : new YTimeSpan(ticks).toString(scaleFormat.format);
    }
    return label;
  }
  DrawXAxis(w, g, scale, simulation) {
    if (w.Width - w.Rmargin - w.Lmargin < 10)
      return 1;
    let stringFormat = new YStringFormat(
      16384
      /* YDataRendering.YStringFormat.StringFormatFlags.NoClip */
    );
    stringFormat.Alignment = 1;
    let bottomSide = scale.position == XAxis.VrtPosition.BOTTOM;
    let y = bottomSide ? w.Height - w.Bmargin : w.Tmargin;
    if (!simulation)
      g.DrawLineXY(scale.pen, w.Lmargin, y, w.Width - w.Rmargin, y);
    let delta = scale.max - scale.min;
    let XZoom = delta / (w.Width - w.Lmargin - w.Rmargin);
    let stepCount = (delta / scale.step >> 0) + 1;
    let FirstStep = 0;
    let timeRange = MinMaxHandler.DefaultValue();
    for (let i = 0; i < this._series.length; i++) {
      if (!this._series[i].disabled) {
        timeRange = MinMaxHandler.Combine(timeRange, this._series[i].timeRange);
      }
    }
    scale.zeroTime = timeRange.Min;
    if (scale.timeReference == TimeConverter.TimeReference.ABSOLUTE) {
      FirstStep = scale.step * Math.floor(scale.min / scale.step);
      timeRange.Min = scale.min;
      timeRange.Max = scale.max;
    } else {
      if (Number.isNaN(timeRange.Min))
        return 0;
      FirstStep = timeRange.Min + scale.step * Math.floor((scale.min - scale.zeroTime) / scale.step);
    }
    if (FirstStep < scale.min)
      FirstStep += scale.step;
    let timeOffset = 0;
    if (scale.timeReference != TimeConverter.TimeReference.ABSOLUTE) {
      timeOffset = FirstStep;
    }
    scale.fullSize = timeRange.Max - timeRange.Min;
    let scaleFormat = scale.bestFormat(timeRange.Max - timeRange.Min, scale.max - scale.min);
    if (!simulation)
      g.TextRenderingHint = YTextRenderingHint.AntiAlias;
    let UnitHeight = 0;
    let label;
    scale.step = scaleFormat.step;
    let t = parseFloat(FirstStep.toString());
    label = _YGraph.XLabel(t, scale, scaleFormat, timeRange);
    let ssize = g.MeasureString(label, scale.font, 1e5);
    let mod = 1;
    while (mod * (w.Width - w.Rmargin - w.Lmargin) / stepCount < ssize.width) {
      mod++;
    }
    let steps = Math.round((t - timeOffset) / scale.step) >> 0;
    let previousT = 0;
    do {
      let d = TimeConverter.FromUnixTime(t);
      if (scale.step > 32 * 86400 && scale.timeReference == TimeConverter.TimeReference.ABSOLUTE) {
        if (scale.step >= 365 * 86400) {
          let m = d.getMonth();
          t = TimeConverter.ToUnixTime(new Date(d.getFullYear() + (m > 5 ? 1 : 0), 0, 1));
        } else
          t = TimeConverter.ToUnixTime(new Date(d.getFullYear(), d.getMonth(), 1));
        if (t < previousT) {
          debugger;
        }
      }
      if (t >= scale.min) {
        let x = w.Lmargin + Math.round((t - scale.min) / XZoom) >> 0;
        if (x <= w.Width - w.Rmargin) {
          if (!simulation) {
            if (scale.showGrid)
              g.DrawLineXY(scale.gridPen, x, w.Tmargin, x, w.Height - w.Bmargin);
            g.DrawLineXY(scale.pen, x, y + (bottomSide ? 2 : -2), x, y + (bottomSide ? -5 : 5));
          }
          label = _YGraph.XLabel(t, scale, scaleFormat, timeRange);
          ssize = g.MeasureString(label, scale.font, 1e5);
          if (ssize.height > UnitHeight)
            UnitHeight = ssize.height;
          if (!simulation) {
            if (steps % mod == 0) {
              g.DrawStringPF(label, scale.font, scale.font.brush, new PointF(x, y + (bottomSide ? 5 : -ssize.height >> 0) - 2), stringFormat);
            }
          }
        }
      }
      previousT = t;
      t += scale.step;
      if (t < previousT)
        debugger;
      steps++;
    } while (t <= scale.max);
    if (scale.legend.title != "") {
      let size = g.MeasureString(scale.legend.title, scale.legend.font, 1e5);
      if (!simulation) {
        let legendX = w.Lmargin + (w.Width - w.Lmargin - w.Rmargin - size.width) / 2 >> 0;
        let legendY = bottomSide ? w.Height - w.Bmargin + UnitHeight + 5 : w.Tmargin - UnitHeight - size.height * 1.5;
        g.DrawString(scale.legend.title, scale.legend.font, scale.legend.font.brush, new PointF(legendX, legendY));
      }
      UnitHeight += size.height >> 0;
    }
    scale.innerHeight = (UnitHeight >> 0) + 10;
    return scale.innerHeight;
  }
  pixelxSize(mainViewPort, scaleX) {
    let dtime = scaleX.max - scaleX.min;
    let dview = mainViewPort.Width - mainViewPort.Lmargin - mainViewPort.Rmargin;
    if (dview > 0)
      return dtime / dview;
    return 0;
  }
  TimeToAutoSting(t, mainViewPort, scaleX) {
    let strValue = "";
    let dtime = scaleX.max - scaleX.min;
    let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
    if (dtime > 0) {
      let pixelSize = this.pixelxSize(mainViewPort, scaleX);
      if (pixelSize > 0) {
        if (scaleX.timeReference == TimeConverter.TimeReference.ABSOLUTE) {
          let date = TimeConverter.FromUnixTime(t);
          let MMMM = months[date.getMonth()];
          let dd = date.getDate().toString();
          let HH = date.getHours().toString();
          if (HH.length < 2)
            HH = "0" + HH;
          let mm = date.getMinutes().toString();
          if (mm.length < 2)
            mm = "0" + mm;
          let ss = date.getSeconds().toString();
          if (ss.length < 2)
            ss = "0" + ss;
          let ff = Math.round(date.getMilliseconds() / 10).toString();
          if (ff.length < 2)
            ff = "0" + ss;
          let f = Math.round(date.getMilliseconds() / 100).toString();
          if (dtime >= 86400)
            strValue += MMMM + " " + dd;
          if (pixelSize < 0.1) {
            strValue += (strValue != "" ? " " : "") + HH + ":" + mm + ":" + ss + "." + ff;
          } else if (pixelSize < 1) {
            strValue += (strValue != "" ? " " : "") + HH + ":" + mm + ":" + ss + "." + f;
          } else if (pixelSize < 60) {
            strValue += (strValue != "" ? " " : "") + HH + ":" + mm + ":" + ss;
          } else if (pixelSize < 3600) {
            strValue += (strValue != "" ? " " : "") + HH + ":" + mm;
          } else if (pixelSize < 86400)
            strValue += (strValue != "" ? " " : "") + HH + "H";
        } else {
          let format = TimeConverter.RelativeFormat(scaleX.fullSize, dtime, pixelSize);
          let ticks = YTimeSpan.TicksPerSecond * (Math.round(100 * (t - scaleX.zeroTime)) / 100);
          strValue += ticks < 0 ? "-" + new YTimeSpan(-ticks).toString(format) : new YTimeSpan(ticks).toString(format);
        }
      }
    }
    return strValue;
  }
  DrawDataTracker(g, viewPortWidth, viewPortHeight, scaleX) {
    if (!this._dataTracker.enabled) {
      return;
    }
    let p = this.mouseLocalPosition();
    if (p == null) {
      return;
    }
    if (p.X <= this.mainViewPort.Lmargin) {
      return;
    }
    if (p.Y <= this.mainViewPort.Tmargin) {
      return;
    }
    if (p.X >= this.UIContainer.width - this.mainViewPort.Rmargin) {
      return;
    }
    if (p.Y >= this.UIContainer.height - this.mainViewPort.Bmargin) {
      return;
    }
    g.SetClip(new YRectangle(0, 0, viewPortWidth, viewPortHeight));
    let DataPoint = _YGraph.ViewPortPointToIRL(this.mainViewPort, p);
    let delta = -1;
    let bestindex = -1;
    let bestmatch = new Array(this._series.length);
    let IRLmatch = new Array(this._series.length);
    for (let i = 0; i < this._series.length; i++) {
      if (this._series[i].visible && !this._series[i].disabled) {
        let p2 = this._series[i].findClosestValue(DataPoint.x, false);
        if (p2 != null) {
          IRLmatch[i] = p2;
          bestmatch[i] = _YGraph.IRLPointToViewPort(this.mainViewPort, IRLmatch[i], this.yAxes[this._series[i].yAxisIndex].IRLy, this.yAxes[this._series[i].yAxisIndex].zoom);
          if (bestindex < 0 || delta > Math.abs(bestmatch[i].Y - p.Y)) {
            delta = Math.abs(bestmatch[i].Y - p.Y);
            if (this._dataTracker.detectionDistance == 0 || delta <= this._dataTracker.detectionDistance && Math.abs(bestmatch[i].X - p.X) < this._dataTracker.detectionDistance) {
              bestindex = i;
            }
          }
        }
      }
    }
    if (bestindex >= 0) {
      let xx = bestmatch[bestindex].X - this._dataTracker.diameter / 2 >> 0;
      let yy = bestmatch[bestindex].Y - this._dataTracker.diameter / 2 >> 0;
      let dd = this._dataTracker.diameter;
      g.FillEllipse(this._series[bestindex].brush, xx, yy, dd, dd);
      g.DrawEllipse(this._dataTracker.pen, xx, yy, dd, dd);
      let dx;
      let dy;
      if (p.X > this.mainViewPort.Lmargin + (viewPortWidth - this.mainViewPort.Lmargin - this.mainViewPort.Rmargin) / 2)
        dx = -1;
      else
        dx = 1;
      if (p.Y > this.mainViewPort.Tmargin + (viewPortHeight - this.mainViewPort.Tmargin - this.mainViewPort.Bmargin) / 2)
        dy = -1;
      else
        dy = 1;
      let xx2 = bestmatch[bestindex].X + dx * (this._dataTracker.handleLength * 1.5) >> 0;
      let yy2 = bestmatch[bestindex].Y + dy * this._dataTracker.handleLength >> 0;
      g.DrawLineXY(this._dataTracker.pen, bestmatch[bestindex].X + dx * 0.707 * this._dataTracker.diameter / 2 >> 0, bestmatch[bestindex].Y + dy * 0.707 * this._dataTracker.diameter / 2 >> 0, bestmatch[bestindex].X + dx * this._dataTracker.handleLength >> 0, bestmatch[bestindex].Y + dy * this._dataTracker.handleLength >> 0);
      g.DrawLineXY(this._dataTracker.pen, bestmatch[bestindex].X + dx * this._dataTracker.handleLength >> 0, bestmatch[bestindex].Y + dy * this._dataTracker.handleLength >> 0, xx2, yy2);
      let strValue = "";
      if (this._dataTracker.showSerieName)
        strValue += this._series[bestindex].legend + "\r\n";
      if (this._dataTracker.showTimeStamp) {
        let t = IRLmatch[bestindex].x;
        strValue += this.TimeToAutoSting(t, this.mainViewPort, scaleX) + "\r\n";
      }
      if (this._dataTracker.dataPrecision.toString == DataTracker.DataPrecision.PRECISION_NOLIMIT.toString) {
        strValue += IRLmatch[bestindex].y.toString() + this._series[bestindex].unit;
      } else {
        let strvalue = this._dataTracker.dataPrecision.description;
        let precision = -Math.log10(Number(strvalue));
        strValue += IRLmatch[bestindex].y.toFixed(precision) + this._series[bestindex].unit;
      }
      let ssize = g.MeasureString(strValue, this._dataTracker.font, 1e4);
      let labelwidth = ssize.width + 2 * this._dataTracker.padding + this._dataTracker.borderthickness;
      let labelHeight = ssize.height + 2 * this._dataTracker.padding + this._dataTracker.borderthickness;
      if (dx > 0) {
        g.FillRectangleXYHW(this._dataTracker.bgBrush, xx2, yy2 - (labelHeight >> 1), labelwidth, labelHeight);
        g.DrawRectangleXYHW(this._dataTracker.pen, xx2, yy2 - (labelHeight >> 1), labelwidth, labelHeight);
        g.DrawStringXY(strValue, this._dataTracker.font, this._dataTracker.font.brush, xx2 + this._dataTracker.padding >> 0, yy2 - (labelHeight >> 1) + this._dataTracker.padding >> 0);
      } else {
        g.FillRectangleXYHW(this._dataTracker.bgBrush, xx2 - labelwidth, yy2 - (labelHeight >> 1), labelwidth, labelHeight);
        g.DrawRectangleXYHW(this._dataTracker.pen, xx2 - labelwidth, yy2 - (labelHeight >> 1), labelwidth, labelHeight);
        g.DrawStringXY(strValue, this._dataTracker.font, this.dataTracker.font.brush, xx2 + this._dataTracker.padding - labelwidth >> 0, yy2 - (labelHeight >> 1) + this._dataTracker.padding >> 0);
      }
    }
  }
  /*
       *  XAxis scale)
      {
          if (!scale.visible) return;
          double delta = scale.max - scale.min;
          Double XZoom = (delta) / (w.Width - w.Lmargin - w.Rmargin);
  
          for (int i = 0; i < scale.zones.Count; i++)
              if (scale.zones[i].visible)
              {
                  double max = scale.zones[i].max;
                  double min = scale.zones[i].min;
                  if (double.IsNaN(max)) max = scale.min;
                  if (double.IsNaN(min)) min = scale.max;
                  if (max < min) { double t = max; max = min; min = t; }
                  int x0 =  w.Lmargin + (int)Math.Round((min - scale.min) / XZoom);
       *
       * */
  DrawMarkers(w, g, scaleX, viewPortWidth, viewPortHeight) {
    if (this._xAxis.markers.length == 0)
      return;
    g.SetClip(new YRectangle(w.Lmargin, w.Tmargin, w.Width - w.Rmargin - w.Lmargin, w.Height - w.Bmargin - w.Tmargin));
    let Bottomleft = _YGraph.ViewPortPointToIRL(w, new Point(w.Lmargin, w.Height - w.Bmargin));
    let TopRight = _YGraph.ViewPortPointToIRL(w, new Point(w.Width - w.Rmargin, w.Tmargin));
    let dy = (w.Height - w.Bmargin - w.Tmargin) / 100;
    let pixelSize = -1;
    g.TextRenderingHint = YTextRenderingHint.AntiAlias;
    for (let i = 0; i < this._xAxis.markers.length; i++) {
      if (this._xAxis.markers[i].enabled) {
        if (pixelSize < 0)
          pixelSize = this.pixelxSize(this.mainViewPort, scaleX);
        let mustdraw = true;
        let xpos = 0;
        if (this._xAxis.markers[i].timereference == TimeConverter.TimeReference.RELATIVE) {
          if (this._xAxis.zeroTime > 0) {
            xpos = this._xAxis.markers[i].xposition + this._xAxis.zeroTime;
          } else {
            mustdraw = false;
          }
        } else {
          xpos = this._xAxis.markers[i].xposition;
        }
        if (xpos > Bottomleft.x - 100 * pixelSize && xpos < TopRight.x + 100 * pixelSize && mustdraw) {
          let p = _YGraph.IRLPointToViewPort(w, new pointXY(xpos, 0));
          let xxCenter = p.X >> 0;
          let yyCenter = w.Height - w.Bmargin - this._xAxis.markers[i].yposition * dy >> 0;
          let strValue = this._xAxis.markers[i].text.replace("\\n", "\n");
          let now = /* @__PURE__ */ new Date();
          if (strValue.indexOf("$") >= 0) {
            if (strValue.indexOf("$MARKERTIME$") >= 0) {
              let s = this._xAxis.markers[i].timereference == TimeConverter.TimeReference.RELATIVE ? TimeConverter.secTimeSpanToString(this._xAxis.markers[i].xposition, pixelSize) : this.TimeToAutoSting(this._xAxis.markers[i].xposition, this.mainViewPort, scaleX);
              strValue = strValue.replace("$MARKERTIME$", s);
            }
            if (strValue.indexOf("$VALUE") >= 0) {
              for (let j = 0; j < this._series.length; j++) {
                if (!this._series[j].disabled) {
                  let pt = this._series[j].findClosestValue(xpos, true);
                  let st = pt != null ? pt.y.toFixed(0) : "--";
                  strValue = strValue.replace("$VALUE" + (j + 1).toString() + "$", st);
                } else {
                  strValue = strValue.replace("$VALUE" + (j + 1).toString() + "$", "");
                }
              }
            }
            if (strValue.indexOf("$UNIT") >= 0) {
              for (let j = 0; j < this._series.length; j++) {
                if (!this._series[j].disabled) {
                  strValue = strValue.replace("$UNIT" + (j + 1).toString() + "$", this._series[j].unit);
                } else {
                  strValue = strValue.replace("$UNIT" + (j + 1).toString() + "$", "");
                }
              }
            }
            if (strValue.indexOf("$LEGEND") >= 0) {
              for (let j = 0; j < this._series.length; j++) {
                if (!this._series[j].disabled) {
                  strValue = strValue.replace("$LEGEND" + (j + 1).toString() + "$", this._series[j].legend);
                } else {
                  strValue = strValue.replace("$LEGEND" + (j + 1).toString() + "$", "");
                }
              }
            }
            if (this._xAxis.markers[i].PatchTextCallback != null) {
              strValue = this._xAxis.markers[i].PatchTextCallback(strValue);
            }
          }
          let ssize = g.MeasureString(strValue, this._xAxis.markers[i].font, 1e4);
          let labelWidth = ssize.width + 2 * this._xAxis.markers[i].padding + this._xAxis.markers[i].borderthickness;
          let labelHeight = ssize.height + 2 * this._xAxis.markers[i].padding + this._xAxis.markers[i].borderthickness;
          g.FillRectangleXYHW(this._xAxis.markers[i].bgBrush, xxCenter - (labelWidth >> 1), yyCenter - (labelHeight >> 1), labelWidth >> 0, labelHeight >> 0);
          g.DrawRectangleXYHW(this._xAxis.markers[i].pen, xxCenter - (labelWidth >> 1), yyCenter - (labelHeight >> 1), labelWidth >> 0, labelHeight >> 0);
          let xText;
          switch (this._xAxis.markers[i].textAlign) {
            case Marker.TextAlign.LEFT:
              xText = xxCenter - (labelWidth >> 1) + this._xAxis.markers[i].padding;
              break;
            case Marker.TextAlign.RIGHT:
              xText = xxCenter + (labelWidth >> 1) - this._xAxis.markers[i].padding;
              break;
            default:
              xText = xxCenter;
              break;
          }
          g.DrawStringPF(strValue, this._xAxis.markers[i].font, this._xAxis.markers[i].font.brush, new PointF(xText, yyCenter), this._xAxis.markers[i].stringFormat);
          g.DrawLineXY(this._xAxis.markers[i].pen, xxCenter, w.Tmargin >> 0, xxCenter, yyCenter - (labelHeight >> 1));
          g.DrawLineXY(this._xAxis.markers[i].pen, xxCenter, yyCenter + (labelHeight >> 1), xxCenter, w.Height - w.Bmargin >> 0);
          if (this._xAxis.markers[i].arrowSize > 0) {
            if (this._xAxis.markers[i].yposition > 25) {
              let triangle = [
                new PointF(xxCenter - this._xAxis.markers[i].arrowSize, yyCenter + (labelHeight >> 1)),
                new PointF(xxCenter + this._xAxis.markers[i].arrowSize, yyCenter + (labelHeight >> 1)),
                new PointF(xxCenter, yyCenter + (labelHeight >> 1) + this._xAxis.markers[i].arrowSize)
              ];
              g.FillPolygon(this._xAxis.markers[i].arrowBrush, triangle);
            }
            if (this._xAxis.markers[i].yposition < 75) {
              let triangle = [
                new PointF(xxCenter - this._xAxis.markers[i].arrowSize, yyCenter + (labelHeight >> 1)),
                new PointF(xxCenter + this._xAxis.markers[i].arrowSize, yyCenter + (labelHeight >> 1)),
                new PointF(xxCenter, yyCenter - (labelHeight >> 1) - this._xAxis.markers[i].arrowSize)
              ];
              g.FillPolygon(this._xAxis.markers[i].arrowBrush, triangle);
            }
          }
        }
      }
    }
    g.ResetClip();
  }
  DrawDataPanels(w, g, scaleX, scalesY, viewPortWidth, viewPortHeight) {
    if (this._dataPanels.length == 0)
      return;
    g.SetClip(new YRectangle(w.Lmargin, w.Tmargin, w.Width - w.Rmargin - w.Lmargin, w.Height - w.Bmargin - w.Tmargin));
    for (let i = 0; i < this._dataPanels.length; i++) {
      if (this._dataPanels[i].enabled) {
        let p = this._dataPanels[i];
        if (p.yScaleIndex < scalesY.length) {
          let AvailableWidth = w.Width - 2 * p.padding - p.borderthickness;
          if (AvailableWidth < 100)
            AvailableWidth = 100;
          let ssize = g.MeasureString(p.text, p.font, AvailableWidth >> 0);
          let panelWidth = ssize.width + 2 * p.padding + p.borderthickness;
          let panelHeight = ssize.height + 2 * p.padding + p.borderthickness;
          let x = 0;
          switch (p.horizontalPosition) {
            case DataPanel.HorizontalPosition.LEFTBORDER:
              x = w.Lmargin;
              break;
            case DataPanel.HorizontalPosition.RIGHTBORDER:
              x = w.Width - w.Rmargin;
              break;
            case DataPanel.HorizontalPosition.ABSOLUTEX:
              let delta = scaleX.max - scaleX.min;
              let XZoom = delta / (w.Width - w.Lmargin - w.Rmargin);
              x = w.Lmargin + Math.round((p.AbsoluteXposition - scaleX.min) / XZoom) >> 0;
              break;
          }
          let y = 0;
          switch (p.verticalPosition) {
            case DataPanel.VerticalPosition.TOPBORDER:
              y = w.Tmargin;
              break;
            case DataPanel.VerticalPosition.BOTTOMBORDER:
              y = w.Height - w.Bmargin;
              break;
            case DataPanel.VerticalPosition.ABSOLUTEY:
              y = w.Height - w.Bmargin - Math.round((p.AbsoluteYposition - scalesY[p.yScaleIndex].IRLy) * scalesY[p.yScaleIndex].zoom) >> 0;
              break;
          }
          switch (p.panelHrzAlign) {
            case DataPanel.HorizontalAlign.LEFTOF:
              x -= panelWidth + p.horizontalMargin;
              break;
            case DataPanel.HorizontalAlign.RIGHTOF:
              x += p.horizontalMargin;
              break;
            default:
              x -= panelWidth / 2;
              break;
          }
          switch (p.panelVrtAlign) {
            case DataPanel.VerticalAlign.ABOVE:
              y -= panelHeight + p.verticalMargin;
              break;
            case DataPanel.VerticalAlign.BELOW:
              y += p.verticalMargin;
              break;
            default:
              y -= panelHeight / 2;
              break;
          }
          g.FillRectangleXYHW(p.bgBrush, x >> 0, y >> 0, panelWidth >> 0, panelHeight >> 0);
          if (p.borderthickness > 0)
            g.DrawRectangleXYHW(p.pen, x >> 0, y >> 0, panelWidth >> 0, panelHeight >> 0);
          let sf = new YStringFormat(
            16384
            /* YDataRendering.YStringFormat.StringFormatFlags.NoClip */
          );
          switch (p.panelTextAlign) {
            case MessagePanel.TextAlign.LEFT:
              sf.LineAlignment = 0;
              sf.Alignment = 0;
              break;
            case MessagePanel.TextAlign.RIGHT:
              sf.LineAlignment = 2;
              sf.Alignment = 2;
              break;
            default:
              sf.LineAlignment = 1;
              sf.Alignment = 1;
              break;
          }
          let rect = new YRectangle(x + p.padding + p.borderthickness / 2 >> 0, y + p.padding + p.borderthickness / 2 >> 0, ssize.width >>= 1, (ssize.height >> 0) + 1);
          g.DrawStringRect(p.text, p.font, p.font.brush, rect, sf);
        } else {
          throw new RangeError("Cannot renderer data panel #" + i.toString() + ", no such Y axis");
        }
      }
    }
  }
  // find the index of a series segment containing a specific timestamp
  // non integer indexes (XX.5) means the timestamp is located between
  // segements XX and XX+1
  findSegmentIndex(serieIndex, timeStamp, debug) {
    let s = this._series[serieIndex];
    if (s.segments.length <= 0)
      return -1;
    let start = 0;
    let end = s.segments.length - 1;
    if (debug) {
      console.log("looking for " + timeStamp);
      for (let i = 0; i < s.segments.length; i++)
        console.log("seg " + i + " [" + s.segments[i].data[0].x + ".." + s.segments[i].data[s.segments[i].count - 1].x + "]");
      debugger;
    }
    while (true) {
      let startSeg = s.segments[start];
      let endSeg = s.segments[end];
      if (timeStamp > startSeg.data[startSeg.count - 1].x)
        return start - 0.5;
      if (timeStamp < endSeg.data[0].x)
        return end + 0.5;
      if (timeStamp >= startSeg.data[0].x)
        return start;
      if (timeStamp <= endSeg.data[endSeg.count - 1].x)
        return end;
      if (end == start) {
        debugger;
        return -99;
      }
      if (end - start == 1)
        return start + 0.5;
      let middle = start + end >> 1;
      let middleSeg = s.segments[middle];
      if (timeStamp <= middleSeg.data[middleSeg.count - 1].x) {
        end = end - 1;
        start = middle;
      } else {
        end = middle - 1;
        start = start + 1;
      }
    }
  }
  findTimestampIndexInSegment(serieIndex, segmentIndex, timeStamp) {
    let seg = this._series[serieIndex].segments[segmentIndex];
    if (!seg)
      debugger;
    if (timeStamp <= seg.data[0].x)
      return 0;
    let count = this._series[serieIndex].segments[segmentIndex].count;
    if (timeStamp >= seg.data[count - 1].x)
      return count - 1;
    let start = 0;
    let end = count - 1;
    let middle = 0;
    while (true) {
      if (end - start <= 1) {
        if (timeStamp <= seg.data[start].x)
          return start;
        return end;
      }
      middle = start + end >> 1;
      if (timeStamp <= seg.data[middle].x)
        end = middle;
      else
        start = middle;
    }
  }
  Render(g, UIw, UIh) {
    if (UIw < 50 || UIh < 50)
      return 0;
    let lastLmargin = this.mainViewPort.Lmargin;
    let lastRmargin = this.mainViewPort.Rmargin;
    this.mainViewPort.Width = UIw;
    this.mainViewPort.Height = UIh;
    this.mainViewPort.Lmargin = 0;
    this.mainViewPort.Rmargin = 0;
    g.SmoothingMode = YSmoothingMode.HighQuality;
    let yMarginOffset = 5;
    for (let i = 0; i < this._yAxes.length; i++) {
      if (this._yAxes[i].visible) {
        let s = g.MeasureString("8", this._yAxes[i].font, 1e5);
        let o = (s.height + 1) / 2 >> 0;
        if (yMarginOffset < o)
          yMarginOffset = o;
      }
    }
    this.mainViewPort.Tmargin = this._xAxis.position == XAxis.VrtPosition.TOP ? 0 : yMarginOffset;
    this.mainViewPort.Bmargin = this._xAxis.position == XAxis.VrtPosition.BOTTOM ? 0 : yMarginOffset;
    if (!this._legendPanel.overlap)
      this.drawLegendPanel(g, UIw, UIh, this.mainViewPort);
    this.drawAnnotationPanels(g, this._annotationPanels, UIw, UIh, false, this.mainViewPort);
    if (this.mainViewPort.Bmargin == 0)
      this.mainViewPort.Bmargin = 5;
    if (this.mainViewPort.Tmargin == 0)
      this.mainViewPort.Tmargin = 5;
    let h = this.DrawXAxis(this.mainViewPort, g, this._xAxis, true);
    if (this._xAxis.position == XAxis.VrtPosition.TOP)
      this.mainViewPort.Tmargin += h;
    else
      this.mainViewPort.Bmargin += h;
    this.mainViewPort.IRLx = this._xAxis.min;
    let M;
    for (let i = 0; i < this._yAxes.length; i++) {
      M = MinMaxHandler.DefaultValue();
      for (let k = 0; k < this._series.length; k++) {
        if (this._series[k].yAxisIndex == i && !this._series[k].disabled) {
          for (let j = 0; j < this._series[k].segments.length; j++) {
            M = MinMaxHandler.Combine(M, _YGraph.FindMinMax(this._xAxis.min, this._xAxis.max, this._series[k].segments[j].data, this._series[k].segments[j].count));
          }
        }
      }
      this._yAxes[i].computeStartAndStep(M);
    }
    if (this.mainViewPort.Lmargin == 0)
      this.mainViewPort.Lmargin = 5;
    if (this.mainViewPort.Rmargin == 0)
      this.mainViewPort.Rmargin = 5;
    for (let i = 0; i < this._yAxes.length; i++) {
      let sw = _YGraph.DrawYAxis(this.mainViewPort, g, this._yAxes[i], 0, true);
      this.mainViewPort.Lmargin += this._yAxes[i].position == YAxis.HrzPosition.LEFT ? sw : 0;
      this.mainViewPort.Rmargin += this._yAxes[i].position == YAxis.HrzPosition.RIGHT ? sw : 0;
    }
    if (this._navigator.enabled) {
      if (lastLmargin != this.mainViewPort.Lmargin || lastRmargin != this.mainViewPort.Rmargin) {
        this.navigatorCache = null;
      }
      let nh = this._navigator.relativeheight * this.UIContainer.height / 100 >> 0;
      let ofset = this.xAxis.position == XAxis.VrtPosition.BOTTOM ? h : 0;
      this._navigator.setPosition(UIw, UIh, this.mainViewPort.Lmargin, this.mainViewPort.Rmargin, this.mainViewPort.Height - nh - this.mainViewPort.Bmargin + ofset, this.mainViewPort.Bmargin - ofset);
      this.mainViewPort.Bmargin += nh;
    }
    if (this.lastTopMargin != this.mainViewPort.Tmargin || this.lastBottomMargin != this.mainViewPort.Bmargin) {
      this._bgBrush = null;
      this.lastTopMargin = this.mainViewPort.Tmargin;
      this.lastBottomMargin = this.mainViewPort.Bmargin;
    }
    if (this._bgBrush == null) {
      this._bgBrush = new YLinearGradientBrush(this._bgColor2, this._bgColor1);
    }
    g.FillRectangleXYHW(this._bgBrush, this.mainViewPort.Lmargin, this.mainViewPort.Tmargin, this.mainViewPort.Width - this.mainViewPort.Rmargin - this.mainViewPort.Lmargin, this.mainViewPort.Height - this.mainViewPort.Bmargin - this.mainViewPort.Tmargin);
    if (this._borderThickness > 0) {
      if (this._borderPen == null)
        this._borderPen = new YPen(this._borderColor, this._borderThickness);
      g.DrawRectangleXYHW(this._borderPen, this.mainViewPort.Lmargin, this.mainViewPort.Tmargin, this.mainViewPort.Width - this.mainViewPort.Rmargin - this.mainViewPort.Lmargin, this.mainViewPort.Height - this.mainViewPort.Bmargin - this.mainViewPort.Tmargin);
    }
    g.SetClip(new YRectangle(this.mainViewPort.Lmargin, this.mainViewPort.Tmargin, this.mainViewPort.Width - this.mainViewPort.Rmargin - this.mainViewPort.Lmargin, this.mainViewPort.Height - this.mainViewPort.Bmargin - this.mainViewPort.Tmargin));
    for (let i = 0; i < this._yAxes.length; i++) {
      this.DrawYAxisZones(this.mainViewPort, g, this._yAxes[i]);
    }
    this.DrawXAxisZones(this.mainViewPort, g, this.xAxis);
    g.ResetClip();
    this.DrawXAxis(this.mainViewPort, g, this._xAxis, false);
    let leftOffset = 0;
    let rightOffset = 0;
    for (let i = 0; i < this._yAxes.length; i++) {
      let ww = _YGraph.DrawYAxis(this.mainViewPort, g, this._yAxes[i], this._yAxes[i].position == YAxis.HrzPosition.LEFT ? leftOffset : rightOffset, false);
      if (this._yAxes[i].position == YAxis.HrzPosition.LEFT)
        leftOffset += ww;
      if (this._yAxes[i].position == YAxis.HrzPosition.RIGHT)
        rightOffset += ww;
    }
    g.SetClip(new YRectangle(this.mainViewPort.Lmargin, this.mainViewPort.Tmargin, this.mainViewPort.Width - this.mainViewPort.Rmargin - this.mainViewPort.Lmargin, this.mainViewPort.Height - this.mainViewPort.Bmargin - this.mainViewPort.Tmargin));
    this.mainViewPort.zoomx = (this.mainViewPort.Width - this.mainViewPort.Lmargin - this.mainViewPort.Rmargin) / (this._xAxis.max - this._xAxis.min);
    let mypenb = null;
    let lineCount = 0;
    let pointCount = 0;
    let Bottomleft = _YGraph.ViewPortPointToIRL(this.mainViewPort, new Point(this.mainViewPort.Lmargin, this.mainViewPort.Height - this.mainViewPort.Bmargin));
    let TopRight = _YGraph.ViewPortPointToIRL(this.mainViewPort, new Point(this.mainViewPort.Width - this.mainViewPort.Rmargin, this.mainViewPort.Tmargin));
    let xTimeStart = Bottomleft.x;
    let xTimeEnd = TopRight.x;
    let availabelPixelWidth = this.mainViewPort.Width - this.mainViewPort.Rmargin - this.mainViewPort.Lmargin;
    for (let k = 0; k < this._series.length; k++) {
      if (this._series[k].visible && !this._series[k].disabled && this._series[k].segments.length > 0) {
        let scaleIndex = this._series[k].yAxisIndex;
        mypenb = this._series[k].pen;
        this.mainViewPort.IRLy = this._yAxes[scaleIndex].startStopStep.dataMin;
        this._yAxes[this._series[k].yAxisIndex].IRLy = this.mainViewPort.IRLy;
        let delta = this._yAxes[scaleIndex].startStopStep.dataMax - this._yAxes[scaleIndex].startStopStep.dataMin;
        if (delta == 0) {
          delta = 1;
          this.mainViewPort.IRLy -= delta / 2;
        }
        this.mainViewPort.zoomy = (this.mainViewPort.Height - this.mainViewPort.Tmargin - this.mainViewPort.Bmargin) / delta;
        this._yAxes[this._series[k].yAxisIndex].zoom = this.mainViewPort.zoomy;
        g.comment("** main view-port series " + k.toString());
        let FirstSegmentIndexTmp = this.findSegmentIndex(k, xTimeEnd, false);
        let LastSegmentIndexTmp = this.findSegmentIndex(k, xTimeStart, false);
        let inside = true;
        let maxIndex = this._series[k].segments.length - 1;
        if (FirstSegmentIndexTmp < 0 && LastSegmentIndexTmp < 0) {
          inside = false;
        }
        if (FirstSegmentIndexTmp > maxIndex && LastSegmentIndexTmp > maxIndex) {
          inside = false;
        }
        if (inside) {
          let FirstSegmentIndex = Math.floor(FirstSegmentIndexTmp + 0.5);
          let LastSegmentIndex = Math.floor(LastSegmentIndexTmp);
          if (LastSegmentIndex < 0) {
            throw new Error("findSegmentIndex return an invalid index (" + LastSegmentIndex + ")");
          }
          if (FirstSegmentIndex < 0) {
            throw new Error("findSegmentIndex return an invalid index (" + FirstSegmentIndex + ")!");
          }
          if (FirstSegmentIndex > this._series[k].segments.length - 1) {
            throw new Error("findSegmentIndex return an invalid index (" + FirstSegmentIndex + ")!!");
          }
          if (LastSegmentIndex > this._series[k].segments.length - 1) {
            throw new Error("findSegmentIndex return an invalid index (" + LastSegmentIndex + ")!!!");
          }
          let firstDataIndex = this.findTimestampIndexInSegment(k, FirstSegmentIndex, xTimeEnd);
          let lastDataIndex = this.findTimestampIndexInSegment(k, LastSegmentIndex, xTimeStart);
          let totalPointsToDraw = 0;
          if (FirstSegmentIndex == LastSegmentIndex)
            totalPointsToDraw = firstDataIndex - lastDataIndex;
          else if (FirstSegmentIndex < LastSegmentIndex) {
            totalPointsToDraw = this._series[k].segments[LastSegmentIndex].count - lastDataIndex + firstDataIndex;
            for (let i = FirstSegmentIndex + 1; i < LastSegmentIndex; i++)
              totalPointsToDraw += this._series[k].segments[i].count;
          }
          let density = Math.round(totalPointsToDraw / availabelPixelWidth);
          if (density < SUMMARY_GRANULARITY || SUMMARY_GRANULARITY <= 0) {
            for (let i = 0; i < this._series[k].segments.length; i++) {
              lineCount += _YGraph.DoSegmentRendering(this.mainViewPort, g, mypenb, this._series[k].segments[i].data, this._series[k].segments[i].count, xTimeStart, xTimeEnd);
              pointCount += this._series[k].segments[i].count;
            }
          } else {
            let level = Math.floor(Math.log(density) / Math.log(SUMMARY_GRANULARITY)) - 1;
            let s = this._series[k].summaries[level];
            let finalpoint = null;
            for (let i = 0; i < s.segments.length; i++) {
              if (i == s.segments.length - 1) {
                finalpoint = s.getBufferpoint();
              }
              lineCount += _YGraph.DoSummarySegmentRendering(this.mainViewPort, g, mypenb, s.segments[i].data, s.segments[i].ptCount, finalpoint, xTimeStart, xTimeEnd);
              pointCount += s.segments[i].ptCount;
            }
          }
        } else
          console.log("Data are ouside dataview");
      }
    }
    g.ResetClip();
    if (this._navigator.enabled) {
      g.comment("** navigator **");
      let v = this._navigator.viewport;
      let range = MinMaxHandler.DefaultValue();
      for (let i = 0; i < this._series.length; i++) {
        if (!this._series[i].disabled)
          range = MinMaxHandler.Combine(range, this._series[i].timeRange);
      }
      this._navigator.Xrange = MinMaxHandler.extend(range, 1.05);
      v.zoomx = (v.Width - v.Lmargin - v.Rmargin) / (this._navigator.Xrange.Max - this._navigator.Xrange.Min);
      if (this.lastPointCount != pointCount && !this.mainViewPort.Capture && !this._navigator.Capture || this.navigatorCache == null || g instanceof YGraphicsSVG) {
        g.comment("Redraw navigator");
        if (this.navigatorCache != null)
          this.navigatorCache = null;
        this.navigatorCache = document.createElement("canvas");
        this.navigatorCache.width = v.Width;
        this.navigatorCache.height = v.Height;
        this.lastPointCount = pointCount;
        let ng;
        if (g instanceof YGraphicsSVG) {
          ng = g;
        } else {
          ng = new YGraphics(this.navigatorCache, v.Width, v.Height, 90);
        }
        ng.SetClip(new YRectangle(v.Lmargin, v.Tmargin, v.Width - v.Rmargin - v.Lmargin, v.Height - v.Bmargin - v.Tmargin));
        ng.FillRectangleXYHW(this._navigator.bgBrush, v.Lmargin, v.Tmargin, v.Width - v.Rmargin - v.Lmargin, v.Height - v.Bmargin - v.Tmargin);
        if (this.xAxis.zones.length > 0 && this._navigator.showXAxisZones) {
          let delta = this._navigator.Xrange.Max - this._navigator.Xrange.Min;
          let XZoom = delta / (v.Width - v.Lmargin - v.Rmargin);
          for (let i = 0; i < this.xAxis.zones.length; i++) {
            if (this.xAxis.zones[i].visible) {
              let min = this.xAxis.zones[i].min;
              let max = this.xAxis.zones[i].max;
              if (isNaN(min))
                min = this._navigator.Xrange.Min;
              if (isNaN(max))
                max = this._navigator.Xrange.Max;
              ng.FillRectangleXYHW(this.xAxis.zones[i].zoneBrush, v.Lmargin + (min - this._navigator.Xrange.Min) / XZoom >> 0, v.Tmargin >> 0, (max - min) / XZoom >> 0, v.Height - v.Bmargin - v.Tmargin);
            }
          }
        }
        if (MinMaxHandler.isDefined(this._navigator.Xrange) && this._navigator.Xrange.Max - this._navigator.Xrange.Min > 0) {
          let Min;
          let Max;
          v.IRLx = this._navigator.Xrange.Min;
          let dontSticktoBorderZoom = 4 / (v.Height - v.Bmargin - v.Tmargin);
          let Bottomleft2 = _YGraph.ViewPortPointToIRL(v, new Point(v.Lmargin, v.Height - v.Bmargin));
          let TopRight2 = _YGraph.ViewPortPointToIRL(v, new Point(v.Width - v.Rmargin, v.Tmargin));
          let xTimeStart2 = Bottomleft2.x;
          let xTimeEnd2 = TopRight2.x;
          if (this._navigator.yAxisHandling == Navigator.YAxisHandling.AUTO) {
            for (let k = 0; k < this._series.length; k++) {
              if (!this._series[k].disabled) {
                ng.comment("** navigator series " + k.toString());
                v.IRLy = this._series[k].valueRange.Min;
                let yAxisIndex = this._series[k].yAxisIndex;
                mypenb = this._series[k].navigatorpen;
                Min = this._series[k].valueRange.Min;
                Max = this._series[k].valueRange.Max;
                if (Max - Min <= 0) {
                  v.IRLy = Min - 0.5;
                  Max = Min + 0.5;
                } else {
                  let delta = Max - Min;
                  Min -= delta * dontSticktoBorderZoom;
                  Max += delta * dontSticktoBorderZoom;
                }
                v.IRLy = Min;
                v.zoomy = (v.Height - v.Tmargin - v.Bmargin) / (Max - Min);
                let availableWidth = v.Width - v.Lmargin - v.Rmargin;
                let totalPoint = 0;
                for (let i = 0; i < this._series[k].segments.length; i++)
                  totalPoint += this._series[k].segments[i].count;
                let density = Math.round(totalPoint / availableWidth);
                if (density < SUMMARY_GRANULARITY || SUMMARY_GRANULARITY <= 0) {
                  for (let i = 0; i < this._series[k].segments.length; i++) {
                    lineCount += _YGraph.DoSegmentRendering(v, ng, mypenb, this._series[k].segments[i].data, this._series[k].segments[i].count, xTimeStart2, xTimeEnd2);
                  }
                } else {
                  let level = Math.floor(Math.log(density) / Math.log(SUMMARY_GRANULARITY)) - 1;
                  let s = this._series[k].summaries[level];
                  let finalpoint = null;
                  for (let i = 0; i < s.segments.length; i++) {
                    if (i == s.segments.length - 1)
                      finalpoint = s.getBufferpoint();
                    lineCount += _YGraph.DoSummarySegmentRendering(v, ng, mypenb, s.segments[i].data, s.segments[i].ptCount, finalpoint, xTimeStart2, xTimeEnd2);
                  }
                }
              }
            }
          } else {
            for (let i = 0; i < this._yAxes.length; i++) {
              let Yrange = MinMaxHandler.DefaultValue();
              for (let j = 0; j < this._series.length; j++) {
                if (this._series[j].yAxisIndex == i && !this._series[j].disabled) {
                  Yrange = MinMaxHandler.Combine(Yrange, this._series[j].valueRange);
                }
              }
              Yrange = MinMaxHandler.extend(Yrange, 1 + 2 * dontSticktoBorderZoom);
              Min = this._yAxes[i].min;
              if (isNaN(Min))
                Min = Yrange.Min;
              Max = this._yAxes[i].max;
              if (isNaN(Max))
                Max = Yrange.Max;
              if (Number.isNaN(Min)) {
                Min = 0;
                Max = 1;
              }
              if (Max - Min <= 0) {
                Min = Min - 0.5;
                Max = Min + 0.5;
              }
              v.IRLy = Min;
              v.zoomy = (v.Height - v.Tmargin - v.Bmargin) / (Max - Min);
              for (let j = 0; j < this._series.length; j++) {
                if (this._series[j].yAxisIndex == i && !this._series[j].disabled && this._series[j].visible) {
                  ng.comment("** navigator series " + j.toString());
                  mypenb = this._series[j].navigatorpen;
                  let availableWidth = v.Width - v.Lmargin - v.Rmargin;
                  let totalPoint = 0;
                  for (let i2 = 0; i2 < this._series[j].segments.length; i2++)
                    totalPoint += this._series[j].segments[i2].count;
                  let density = Math.round(totalPoint / availableWidth);
                  if (density < SUMMARY_GRANULARITY || SUMMARY_GRANULARITY <= 0) {
                    for (let i2 = 0; i2 < this._series[j].segments.length; i2++) {
                      lineCount += _YGraph.DoSegmentRendering(v, ng, mypenb, this._series[j].segments[i2].data, this._series[j].segments[i2].count, xTimeStart2, xTimeEnd2);
                    }
                  } else {
                    let level = Math.floor(Math.log(density) / Math.log(SUMMARY_GRANULARITY)) - 1;
                    console.log("Navigator (YAXIS=INHERIT), Serie " + j + ", density is ~" + density + "pts/pixel drawing summarized data level " + level);
                    let s = this._series[j].summaries[level];
                    let finalpoint = null;
                    for (let i2 = 0; i2 < s.segments.length; i2++) {
                      if (i2 == s.segments.length - 1)
                        finalpoint = s.getBufferpoint();
                      lineCount += _YGraph.DoSummarySegmentRendering(v, ng, mypenb, s.segments[i2].data, s.segments[i2].ptCount, finalpoint, xTimeStart2, xTimeEnd2);
                    }
                  }
                }
              }
            }
          }
          for (let i = 0; i < this._xAxis.markers.length; i++) {
            if (this._xAxis.markers[i].enabled) {
              let p = _YGraph.IRLPointToViewPort(v, new pointXY(this._xAxis.markers[i].xposition + (this._xAxis.markers[i].timereference == TimeConverter.TimeReference.RELATIVE ? this._xAxis.zeroTime : 0), 0));
              ng.DrawLineXY(this._xAxis.markers[i].navigatorpen, p.X, v.Tmargin, p.X, v.Height - v.Bmargin);
            }
          }
          if (this._navigator.borderThickness > 0) {
            ng.DrawLineXY(this._navigator.borderPen, v.Lmargin, v.Tmargin, v.Width - v.Rmargin, v.Tmargin);
          }
          this.DrawMonitorXAxis(v, ng, this._navigator.Xrange, this.xAxis.labelFormat);
          this._navigator.setIRLPosition(v.IRLx, v.IRLy, v.zoomx, v.zoomy);
        }
        ng.ResetClip();
        if (!(g instanceof YGraphicsSVG))
          ng.Dispose();
      }
      let cacheW = v.Width - v.Rmargin - v.Lmargin + 1;
      let cacheH = v.Width - v.Rmargin - v.Lmargin + 1;
      let rectsrc = new YRectangle(v.Lmargin, v.Tmargin - 1, cacheW, cacheH);
      let rectdst = new YRectangle(v.Lmargin, v.Tmargin, cacheW, cacheH);
      g.SetClip(rectdst);
      if (!(g instanceof YGraphicsSVG)) {
        g.DrawImage(
          this.navigatorCache,
          rectsrc,
          rectdst,
          2
          /* YDataRendering.YGraphicsUnit.Pixel */
        );
      }
      if (this._navigator.borderThickness > 0) {
        g.DrawLineXY(this._navigator.borderPen, v.Lmargin + 1, v.Tmargin, v.Lmargin + 1, v.Height - v.Bmargin - 1);
        g.DrawLineXY(this._navigator.borderPen, v.Width - v.Rmargin, v.Tmargin, v.Width - v.Rmargin, v.Height - v.Bmargin - 1);
        g.DrawLineXY(this._navigator.borderPen, v.Lmargin + 1, v.Tmargin, v.Width - v.Rmargin, v.Tmargin);
      }
      let IRLCursorStart = _YGraph.ViewPortPointToIRL(this.mainViewPort, new Point(this.mainViewPort.Lmargin, 0));
      let IRLCursorEnd = _YGraph.ViewPortPointToIRL(this.mainViewPort, new Point(this.mainViewPort.Width - this.mainViewPort.Rmargin, 0));
      let CursorStart = _YGraph.IRLPointToViewPort(this._navigator.viewport, new pointXY(IRLCursorStart.x, 0));
      let CursorEnd = _YGraph.IRLPointToViewPort(this._navigator.viewport, new pointXY(IRLCursorEnd.x, 0));
      g.FillRectangle(this._navigator.cursorBrush, new YRectangle(CursorStart.X - 1, v.Tmargin, CursorEnd.X - CursorStart.X + 2, v.Height - v.Bmargin - v.Tmargin - 1));
      g.DrawLineXY(this._navigator.cursorBorderPen, CursorStart.X - 1 >> 0, v.Tmargin >> 0, CursorStart.X - 1 >> 0, v.Height - v.Bmargin - 1);
      g.DrawLineXY(this._navigator.cursorBorderPen, CursorEnd.X + 1 >> 0, v.Tmargin >> 0, CursorEnd.X + 1 >> 0, v.Height - v.Bmargin - 1);
      g.ResetClip();
    }
    if (this._legendPanel.overlap)
      this.drawLegendPanel(g, UIw, UIh, this.mainViewPort);
    g.TextRenderingHint = YTextRenderingHint.SingleBitPerPixelGridFit;
    this.DrawMarkers(this.mainViewPort, g, this.xAxis, UIw, UIh);
    this.drawAnnotationPanels(g, this._annotationPanels, UIw, UIh, true, this.mainViewPort);
    this.DrawDataPanels(this.mainViewPort, g, this.xAxis, this._yAxes, UIw, UIh);
    this.DrawDataTracker(g, UIw, UIh, this.xAxis);
    this.DrawMessagePanels(g, UIw, UIh);
    return 0;
  }
  KeyDown(sender, e) {
    if (e.code == "ArrowLeft") {
      let delta = 0.2 * (this._xAxis.max - this._xAxis.min);
      this._xAxis.set_minMax(this._xAxis.min - delta, this._xAxis.max - delta);
      this.redraw();
    }
    if (e.code == "ArrowRight") {
      let delta = 0.2 * (this._xAxis.max - this._xAxis.min);
      this._xAxis.set_minMax(this._xAxis.min + delta, this._xAxis.max + delta);
      this.redraw();
    }
    if (e.code == "ArrowUp") {
      this.mouseWheel(new Point(this.UIContainer.width >> 1, this.UIContainer.height >> 1), 10);
    }
    if (e.code == "ArrowDown") {
      this.mouseWheel(new Point(this.UIContainer.width >> 1, this.UIContainer.height >> 1), -10);
    }
  }
  mouseWheel(pos, delta) {
    let ZoomFactor = Math.pow(1.25, delta / 120);
    let NextZoomX = this.mainViewPort.zoomx * ZoomFactor;
    if (NextZoomX > this.mainViewPort.zoomx && NextZoomX > 1e3)
      return;
    let currentRange = this._xAxis.max - this._xAxis.min;
    if (currentRange / ZoomFactor > 25 * 365 * 86400)
      return;
    this.mainViewPort.IRLx += (pos.X - this.mainViewPort.Lmargin) / this.mainViewPort.zoomx - (pos.X - this.mainViewPort.Lmargin) / NextZoomX;
    let range = this._xAxis.max - this._xAxis.min;
    this._xAxis.set_minMax(this.mainViewPort.IRLx, this.mainViewPort.IRLx + range / ZoomFactor);
    this.mainViewPort.zoomx = NextZoomX;
    this.redraw();
  }
  mouseWheelEvent(sender, e) {
    let p = this.Scr2ElmMatrix.multiplyByV(Vector3.FromXYCoord(e.pageX, e.pageY)).toPoint();
    let eX = p.X;
    let eY = p.Y;
    this.mouseWheel(new Point(eX, eY), e.deltaY > 0 ? -150 : 150);
    e.preventDefault();
  }
};
YGraph._defaultVerticalDragZoomEnabled = false;
YGraph.captureCursor = null;

// obj/full/Renderer/YSolidGauge.js
var DrawPrameters = class {
  constructor() {
    this.outerRadius = 0;
    this.innerRadius = 0;
    this.angleStart = 0;
    this.angleEnd = 0;
    this.ycenter = 0;
    this.xcenter = 0;
    this.heightTop = 0;
    this.heightBottom = 0;
    this.valueRectangle = new YRectangle(0, 0, 0, 0);
    this.valueFormat = new YStringFormat(
      16384
      /* YDataRendering.YStringFormat.StringFormatFlags.NoClip */
    );
    this.minValueRectangle = new YRectangle(0, 0, 0, 0);
    this.minValueFormat = new YStringFormat(
      16384
      /* YDataRendering.YStringFormat.StringFormatFlags.NoClip */
    );
    this.maxValueRectangle = new YRectangle(0, 0, 0, 0);
    this.maxValueFormat = new YStringFormat(
      16384
      /* YDataRendering.YStringFormat.StringFormatFlags.NoClip */
    );
    this.minValue = "";
    this.maxValue = "";
    this.value = "";
  }
};
var YSolidGauge = class _YSolidGauge extends YDataRenderer {
  get min() {
    return this._min;
  }
  set min(value) {
    if (value >= this._max && !YDataRenderer.minMaxCheckDisabled) {
      throw new RangeError("Min cannot be greater than max (" + this._max.toString() + ")");
    }
    this._min = value;
    if (this._shownValue < this._min)
      this._shownValue = this._min;
    this.redraw();
  }
  get max() {
    return this._max;
  }
  set max(value) {
    if (value <= this._min && !YDataRenderer.minMaxCheckDisabled) {
      throw new RangeError("Max cannot be less than min (" + this._min.toString() + ")");
    }
    this._max = value;
    if (this._shownValue > this._max)
      this._shownValue = this._max;
    this.redraw();
  }
  get borderpen() {
    if (this._borderpen == null) {
      this._borderpen = new YPen(this._borderColor, this._borderThickness);
      this._borderpen.startCap = 1;
      this._borderpen.endCap = 1;
    }
    return this._borderpen;
  }
  get borderColor() {
    return this._borderColor;
  }
  set borderColor(value) {
    this._borderColor = value;
    this._borderpen = null;
    this.redraw();
  }
  get backgroundColor1() {
    return this._backgroundColor1;
  }
  set backgroundColor1(value) {
    this._backgroundColor1 = value;
    this._bgBrush = null;
    this.redraw();
  }
  get backgroundColor2() {
    return this._backgroundColor2;
  }
  set backgroundColor2(value) {
    this._backgroundColor2 = value;
    this._bgBrush = null;
    this.redraw();
  }
  get borderThickness() {
    return this._borderThickness;
  }
  set borderThickness(value) {
    if (value < 0)
      throw new RangeError("Thickness must be a positive value");
    this._borderThickness = value;
    this._borderpen = null;
    this._path = null;
    this.redraw();
  }
  get valueFormater() {
    return this._valueFormater;
  }
  set valueFormater(value) {
    this._valueFormater = value;
    this.redraw();
  }
  get minmaxFormater() {
    return this._minmaxFormater;
  }
  set minmaxFormater(value) {
    this._minmaxFormater = value;
    this.redraw();
  }
  get thickness() {
    return this._thickness;
  }
  set thickness(value) {
    if (value < 0)
      throw new RangeError("Thickness must be a positive value");
    this._thickness = Math.max(Math.min(value, 80), 1);
    this._path = null;
    this.redraw();
  }
  get maxSpeed() {
    return this._maxSpeed;
  }
  set maxSpeed(value) {
    if (value <= 0)
      throw new RangeError("Speed must be a positive value");
    this._maxSpeed = value;
  }
  get value() {
    return this._value;
  }
  set value(value) {
    this._value = value;
    this.redraw();
  }
  get color1() {
    return this._color1;
  }
  set color1(value) {
    this._color1 = value;
    this.redraw();
  }
  get color2() {
    return this._color2;
  }
  set color2(value) {
    this._color2 = value;
    this.redraw();
  }
  get font() {
    return this._font;
  }
  get minMaxFont() {
    return this._minMaxFont;
  }
  get showMinMax() {
    return this._showMinMax;
  }
  set showMinMax(value) {
    this._showMinMax = value;
    this._path = null;
    this.redraw();
  }
  get displayMode() {
    return this._displayMode;
  }
  set displayMode(value) {
    this._displayMode = value;
    this._path = null;
    this._bgBrush = null;
    this.redraw();
  }
  FontsizeChange(source) {
    this._path = null;
  }
  constructor(UIContainer, mode, logFunction) {
    super(UIContainer, logFunction);
    this._shownValue = 0;
    this._min = 0;
    this._max = 100;
    this.SegmentMaxLength = 8;
    this.mainViewPort = new ViewPortSettings();
    this._borderpen = null;
    this._borderColor = YColor.Black;
    this._bgBrush = null;
    this._backgroundColor1 = YColor.FromArgb(255, 240, 240, 240);
    this._backgroundColor2 = YColor.FromArgb(255, 200, 200, 200);
    this._borderThickness = 5;
    this._valueFormater = null;
    this._minmaxFormater = null;
    this._thickness = 25;
    this._maxSpeed = 0.1;
    this._value = 0;
    this._color1 = YColor.Green;
    this._color2 = YColor.Red;
    this._font = null;
    this._minMaxFont = null;
    this._showMinMax = true;
    this._path = null;
    this.lastDrawParameters = new DrawPrameters();
    this._displayMode = _YSolidGauge.DisplayMode.DISPLAY90;
    this._minMaxFont = new YFont(this, this, Math.min(UIContainer.width, UIContainer.height) / 15, () => {
      this.FontsizeChange(this._minMaxFont);
    });
    this._displayMode = mode;
    this._font = new YFont(this, this, Math.min(UIContainer.width, UIContainer.height) / 5, null);
    this.resizeRule = Proportional.ResizeRule.RELATIVETOBOTH;
    let g = new YGraphics(UIContainer, UIContainer.width, UIContainer.height, 90);
    let p = this.ComputeDrawParameters(g, UIContainer.width, UIContainer.height, this.mainViewPort);
    g.Dispose();
  }
  clearCachedObjects() {
    this._bgBrush = null;
    this._path = null;
  }
  ComputeDrawParameters(g, UIw, UIh, mainViewPort) {
    UIw -= mainViewPort.Lmargin + mainViewPort.Rmargin;
    UIh -= mainViewPort.Tmargin + mainViewPort.Bmargin;
    let w = UIw - 5 - this._borderThickness;
    let h = UIh - 5 - this._borderThickness;
    let xcenter = UIw / 2;
    let outerRadius = 0;
    let angleStart = 0;
    let angleEnd = 0;
    let ycenter = 0;
    let ValueRectangle = new YRectangle(0, 0, 0, 0);
    let valueFormat = new YStringFormat(
      16384
      /* YDataRendering.YStringFormat.StringFormatFlags.NoClip */
    );
    let innerRadius = 0;
    let minMaxHeight = 0;
    let s1 = new YSizeF(null, "");
    let s2 = new YSizeF(null, "");
    this.lastDrawParameters.value = this._valueFormater == null ? this._value.toFixed(0) : this._valueFormater(this, this._value);
    if (this._showMinMax) {
      this.lastDrawParameters.minValue = this._minmaxFormater == null ? this._min.toFixed(0) : this._minmaxFormater(this, this._min);
      this.lastDrawParameters.maxValue = this._minmaxFormater == null ? this._max.toFixed(0) : this._minmaxFormater(this, this._max);
      s1 = g.MeasureString(this.lastDrawParameters.minValue, this._minMaxFont, 1e5);
      s2 = g.MeasureString(this.lastDrawParameters.maxValue, this._minMaxFont, 1e5);
      this.lastDrawParameters.minValueFormat = new YStringFormat(
        16384
        /* YDataRendering.YStringFormat.StringFormatFlags.NoClip */
      );
      this.lastDrawParameters.maxValueFormat = new YStringFormat(
        16384
        /* YDataRendering.YStringFormat.StringFormatFlags.NoClip */
      );
      minMaxHeight = s1.height;
      if (s2.height > minMaxHeight)
        minMaxHeight = s2.height;
    }
    switch (this._displayMode) {
      case _YSolidGauge.DisplayMode.DISPLAY90:
        h = h - minMaxHeight;
        w = w - minMaxHeight;
        outerRadius = w;
        if (outerRadius > h - this._borderThickness)
          outerRadius = h - this.borderThickness;
        if (outerRadius > w - this.borderThickness)
          outerRadius = w - this.borderThickness;
        angleStart = Math.PI / 2;
        angleEnd = Math.PI;
        this.lastDrawParameters.heightTop = outerRadius;
        this.lastDrawParameters.heightBottom = 0;
        ycenter = mainViewPort.Tmargin + h;
        xcenter = mainViewPort.Lmargin + UIw / 2 + outerRadius / 2 - minMaxHeight + this._borderThickness;
        innerRadius = outerRadius * (100 - this._thickness) / 100;
        ValueRectangle = new YRectangle(xcenter - innerRadius >> 0, ycenter - innerRadius >> 0, innerRadius >> 0, innerRadius >> 0);
        valueFormat.Alignment = 2;
        valueFormat.LineAlignment = 2;
        if (this._showMinMax) {
          this.lastDrawParameters.minValueRectangle = new YRectangle(xcenter - (outerRadius + innerRadius + s1.width) / 2 >> 0, ycenter + this._borderThickness >> 0, s1.width + 1 >> 0, minMaxHeight + 1 >> 0);
          this.lastDrawParameters.minValueFormat.Alignment = 0;
          this.lastDrawParameters.minValueFormat.LineAlignment = 0;
          this.lastDrawParameters.maxValueRectangle = new YRectangle(xcenter + this._borderThickness >> 0, ycenter - outerRadius + (outerRadius - innerRadius - s2.width) / 2 >> 0, minMaxHeight + 1 >> 0, s2.width + 1 >> 0);
          this.lastDrawParameters.maxValueFormat.Alignment = 0;
          this.lastDrawParameters.maxValueFormat.LineAlignment = 0;
          this.lastDrawParameters.maxValueFormat.FormatFlags = 2;
        }
        break;
      case _YSolidGauge.DisplayMode.DISPLAY180:
        h = h - minMaxHeight;
        let s0 = new YSizeF(null, "");
        s0 = g.MeasureString(this.lastDrawParameters.value, this._font, 1e5);
        outerRadius = w / 2 - this.borderThickness;
        if (outerRadius > h - this._borderThickness)
          outerRadius = h - this._borderThickness;
        if (outerRadius > w - this.borderThickness)
          outerRadius = w - this.borderThickness;
        angleStart = 0;
        angleEnd = Math.PI;
        ycenter = outerRadius + this._borderThickness / 2;
        innerRadius = outerRadius * (100 - this._thickness) / 100;
        this.lastDrawParameters.heightTop = outerRadius;
        this.lastDrawParameters.heightBottom = 0;
        ValueRectangle = new YRectangle(xcenter - innerRadius >> 0, ycenter + this._borderThickness + minMaxHeight - s0.height >> 0, 2 * innerRadius >> 0, s0.height + 1 >> 0);
        valueFormat.Alignment = 1;
        valueFormat.LineAlignment = 2;
        if (this._showMinMax) {
          this.lastDrawParameters.minValueRectangle = new YRectangle(xcenter - (outerRadius + innerRadius + s1.width) / 2 >> 0, ycenter + this._borderThickness >> 0, s1.width + 1 >> 0, minMaxHeight + 1 >> 0);
          this.lastDrawParameters.minValueFormat.Alignment = 0;
          this.lastDrawParameters.minValueFormat.LineAlignment = 0;
          this.lastDrawParameters.maxValueRectangle = new YRectangle(xcenter + (outerRadius + innerRadius - s2.width) / 2 >> 0, ycenter + this._borderThickness >> 0, s2.width + 1 >> 0, minMaxHeight + 1 >> 0);
          this.lastDrawParameters.maxValueFormat.Alignment = 0;
          this.lastDrawParameters.maxValueFormat.LineAlignment = 0;
        }
        break;
      case _YSolidGauge.DisplayMode.DISPLAY270:
        outerRadius = w;
        if (outerRadius > h / 2)
          outerRadius = h / 2;
        if (outerRadius > w / 2)
          outerRadius = w / 2;
        this.lastDrawParameters.heightTop = outerRadius;
        this.lastDrawParameters.heightBottom = outerRadius;
        angleStart = 0;
        angleEnd = 3 * Math.PI / 2;
        ycenter = mainViewPort.Tmargin + UIh / 2;
        innerRadius = outerRadius * (100 - this._thickness) / 100;
        ValueRectangle = new YRectangle(xcenter - innerRadius >> 0, ycenter - innerRadius >> 0, 2 * innerRadius >> 0, 2 * innerRadius >> 0);
        valueFormat.Alignment = 1;
        valueFormat.LineAlignment = 1;
        if (this._showMinMax) {
          this.lastDrawParameters.minValueRectangle = new YRectangle(xcenter + this._borderThickness >> 0, ycenter + (innerRadius + innerRadius + s1.height) / 2 >> 0, s1.width + 1 >> 0, s1.height + 1 >> 0);
          this.lastDrawParameters.minValueFormat.Alignment = 0;
          this.lastDrawParameters.minValueFormat.LineAlignment = 0;
          this.lastDrawParameters.maxValueRectangle = new YRectangle(xcenter + (innerRadius + innerRadius + s1.height) / 2 >> 0, ycenter + this._borderThickness >> 0, s2.height + 1 >> 0, s2.width + 1 >> 0);
          this.lastDrawParameters.maxValueFormat.Alignment = 0;
          this.lastDrawParameters.maxValueFormat.LineAlignment = 0;
          this.lastDrawParameters.maxValueFormat.FormatFlags = 2;
        }
        break;
      case _YSolidGauge.DisplayMode.DISPLAY360:
        outerRadius = w;
        if (outerRadius > h / 0.85 / 2)
          outerRadius = h / 0.85 / 2;
        if (outerRadius > w / 2)
          outerRadius = w / 2;
        this.lastDrawParameters.heightTop = outerRadius;
        this.lastDrawParameters.heightBottom = outerRadius * 0.7;
        ycenter = mainViewPort.Tmargin + outerRadius + this._borderThickness / 2;
        angleStart = -Math.PI / 4;
        angleEnd = 5 * Math.PI / 4;
        innerRadius = outerRadius * (100 - this._thickness) / 100;
        ValueRectangle = new YRectangle(xcenter - innerRadius >> 0, ycenter - innerRadius >> 0, 2 * innerRadius >> 0, 2 * innerRadius >> 0);
        valueFormat.Alignment = 1;
        valueFormat.LineAlignment = 1;
        if (this._showMinMax) {
          let dx = Math.abs(innerRadius * Math.cos(angleStart));
          let dy = innerRadius * Math.abs(Math.sin(angleStart)) + 2 * Math.abs((outerRadius - innerRadius) * Math.sin(angleStart) / 3);
          this.lastDrawParameters.minValueRectangle = new YRectangle(xcenter - dx >> 0, ycenter + dy - minMaxHeight / 2 >> 0, s1.width + 1 >> 0, minMaxHeight + 1 >> 0);
          this.lastDrawParameters.minValueFormat.Alignment = 0;
          this.lastDrawParameters.minValueFormat.LineAlignment = 1;
          this.lastDrawParameters.maxValueRectangle = new YRectangle(xcenter + dx - s2.width >> 0, ycenter + dy - minMaxHeight / 2 >> 0, s2.width + 1 >> 0, minMaxHeight + 1 >> 0);
          this.lastDrawParameters.maxValueFormat.Alignment = 0;
          this.lastDrawParameters.maxValueFormat.LineAlignment = 1;
        }
        break;
    }
    this.lastDrawParameters.outerRadius = outerRadius;
    this.lastDrawParameters.innerRadius = innerRadius;
    this.lastDrawParameters.angleStart = angleStart;
    this.lastDrawParameters.angleEnd = angleEnd;
    this.lastDrawParameters.ycenter = ycenter;
    this.lastDrawParameters.xcenter = xcenter;
    this.lastDrawParameters.valueRectangle = ValueRectangle;
    this.lastDrawParameters.valueFormat = valueFormat;
    return this.lastDrawParameters;
  }
  Render(g, w, h) {
    this.mainViewPort = new ViewPortSettings();
    g.SmoothingMode = YSmoothingMode.HighQuality;
    g.TextRenderingHint = YTextRenderingHint.AntiAlias;
    this.drawAnnotationPanels(g, this._annotationPanels, w, h, false, this.mainViewPort);
    let p = this.ComputeDrawParameters(g, w, h, this.mainViewPort);
    if (this._path == null) {
      let outterlength = 2 * p.outerRadius * Math.PI * (p.angleEnd - p.angleStart) / (2 * Math.PI);
      let stepCount = outterlength / this.SegmentMaxLength >> 0;
      let stepsize = (p.angleEnd - p.angleStart) / stepCount;
      this._path = new Array(2 * (stepCount + 1));
      let n = 0;
      for (let i = 0; i <= stepCount; i++) {
        let a = p.angleStart + i * stepsize;
        this._path[n++] = new PointF(p.xcenter + p.outerRadius * Math.cos(a), p.ycenter - p.outerRadius * Math.sin(a));
      }
      for (let i = stepCount; i >= 0; i--) {
        let a = p.angleStart + i * stepsize;
        this._path[n++] = new PointF(p.xcenter + p.innerRadius * Math.cos(a), p.ycenter - p.innerRadius * Math.sin(a));
      }
    }
    if (this._bgBrush == null) {
      this._bgBrush = new YLinearGradientBrush(this._backgroundColor1, this._backgroundColor2);
    }
    if (this._borderpen == null) {
      this._borderpen = new YPen(this._borderColor, this._borderThickness);
      this._borderpen.linejoin = YPen.LineJoin.Round;
    }
    g.FillPolygon(this._bgBrush, this._path);
    if (this._shownValue != this._value) {
      let step = this._maxSpeed * (this._max - this._min) / 100;
      if (Math.abs(this._value - this._shownValue) < step) {
        this._shownValue = this._value;
      } else if (this._shownValue < this._value) {
        this._shownValue += step;
      } else {
        this._shownValue -= step;
      }
    }
    let v = this._shownValue;
    if (v >= this._min) {
      if (v > this._max)
        v = this._max;
      let valueFactor = (v - this._min) / (this._max - this.min);
      let angleValue = p.angleStart + (p.angleEnd - p.angleStart) * valueFactor;
      let outterlength = 2 * p.outerRadius * Math.PI * (angleValue - p.angleStart) / (2 * Math.PI);
      let stepCount = outterlength / this.SegmentMaxLength >> 0;
      let stepsize = (angleValue - p.angleStart) / stepCount;
      let pt = new Array(2 * (stepCount + 1));
      let n = 0;
      for (let i = 0; i <= stepCount; i++) {
        let a = p.angleEnd - i * stepsize;
        pt[n++] = new PointF(p.xcenter + p.outerRadius * Math.cos(a), p.ycenter - p.outerRadius * Math.sin(a));
      }
      for (let i = stepCount; i >= 0; i--) {
        let a = p.angleEnd - i * stepsize;
        pt[n++] = new PointF(p.xcenter + p.innerRadius * Math.cos(a), p.ycenter - p.innerRadius * Math.sin(a));
      }
      let b;
      if (this._color1 == this._color2) {
        b = new YSolidBrush(this._color1);
      } else {
        let A1 = this._color1.alpha;
        let H1 = this._color1.hue;
        let S1 = this._color1.saturation;
        let L1 = this._color1.luminosity;
        let A2 = this._color2.alpha;
        let H2 = this._color2.hue;
        let S2 = this._color2.saturation;
        let L2 = this._color2.luminosity;
        let A = Math.round(A1 + (A2 - A1) * valueFactor) >> 0 & 255;
        let H;
        if (Math.abs(H2 - H1) <= 127) {
          H = H1 + (H2 - H1) * valueFactor >> 0;
        } else {
          H = H1 + 256 + (H2 - H1 + 256) * valueFactor >> 0;
          if (H > 256)
            H -= 256;
        }
        let S = S1 + (S2 - S1) * valueFactor >> 0;
        let L = L1 + (L2 - L1) * valueFactor >> 0;
        b = new YSolidBrush(new YColor(true, A, H, S, L));
      }
      g.FillPolygon(b, pt);
    }
    if (this._borderThickness > 0)
      g.DrawPolygon(this._borderpen, this._path);
    g.DrawStringRect(this.lastDrawParameters.value, this._font, this._font.brush, p.valueRectangle, p.valueFormat);
    if (this._showMinMax) {
      g.DrawStringRect(this.lastDrawParameters.minValue, this._minMaxFont, this._minMaxFont.brush, this.lastDrawParameters.minValueRectangle, this.lastDrawParameters.minValueFormat);
      g.DrawStringRect(this.lastDrawParameters.maxValue, this._minMaxFont, this._minMaxFont.brush, this.lastDrawParameters.maxValueRectangle, this.lastDrawParameters.maxValueFormat);
    }
    this.drawAnnotationPanels(g, this._annotationPanels, w, h, true, this.mainViewPort);
    this.DrawMessagePanels(g, w, h);
    return 0;
  }
  renderingPostProcessing() {
    if (this._shownValue != this._value)
      this.redraw();
  }
};
(function(YSolidGauge2) {
  class DisplayModeEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, DisplayMode);
    }
  }
  YSolidGauge2.DisplayModeEnumItem = DisplayModeEnumItem;
  class DisplayMode extends YEnum {
  }
  DisplayMode.DISPLAY90 = new DisplayModeEnumItem("DISPLAY90", "90\xB0");
  DisplayMode.DISPLAY180 = new DisplayModeEnumItem("DISPLAY180", "180\xB0");
  DisplayMode.DISPLAY270 = new DisplayModeEnumItem("DISPLAY270", "270\xB0");
  DisplayMode.DISPLAY360 = new DisplayModeEnumItem("DISPLAY360", "360\xB0");
  YSolidGauge2.DisplayMode = DisplayMode;
})(YSolidGauge || (YSolidGauge = {}));

// obj/full/Api/yocto_api.js
var YAPI_SUCCESS = 0;
var YAPI_NOT_INITIALIZED = -1;
var YAPI_INVALID_ARGUMENT = -2;
var YAPI_NOT_SUPPORTED = -3;
var YAPI_DEVICE_NOT_FOUND = -4;
var YAPI_VERSION_MISMATCH = -5;
var YAPI_DEVICE_BUSY = -6;
var YAPI_TIMEOUT = -7;
var YAPI_IO_ERROR = -8;
var YAPI_NO_MORE_DATA = -9;
var YAPI_EXHAUSTED = -10;
var YAPI_DOUBLE_ACCES = -11;
var YAPI_UNAUTHORIZED = -12;
var YAPI_RTC_NOT_READY = -13;
var YAPI_FILE_NOT_FOUND = -14;
var YAPI_SSL_ERROR = -15;
var YAPI_RFID_SOFT_ERROR = -16;
var YAPI_RFID_HARD_ERROR = -17;
var YAPI_BUFFER_TOO_SMALL = -18;
var YAPI_DNS_ERROR = -19;
var YAPI_SSL_UNK_CERT = -20;
var YAPI_UNCONFIGURED = -21;
var YAPI_INVALID_INT = 2147483647;
var YAPI_INVALID_UINT = -1;
var YAPI_INVALID_LONG = 9223372036854776e3;
var YAPI_INVALID_DOUBLE = -Number.MAX_VALUE;
var YAPI_INVALID_STRING = "!INVALID!";
var YAPI_NO_TRUSTED_CA_CHECK = 1;
var YAPI_NO_EXPIRATION_CHECK = 2;
var YAPI_NO_HOSTNAME_CHECK = 4;
var YAPI_LEGACY = 8;
var YAPI_MIN_DOUBLE = -Number.MAX_VALUE;
var YAPI_MAX_DOUBLE = Number.MAX_VALUE;
var Y_FUNCTIONDESCRIPTOR_INVALID = YAPI_INVALID_STRING;
var Y_DETECT_NONE = 0;
var Y_DETECT_USB = 1;
var Y_DETECT_NET = 2;
var Y_DETECT_ALL = Y_DETECT_USB | Y_DETECT_NET;
var DEFAULT_DEVICE_LIST_VALIDITY_MS = 1e4;
var DEFAULT_NETWORK_TIMEOUT_MS = 2e4;
var YOCTO_CALIB_TYPE_OFS = 30;
var NOTIFY_NETPKT_NAME = "0";
var NOTIFY_NETPKT_CHILD = "2";
var NOTIFY_NETPKT_FUNCNAME = "4";
var NOTIFY_NETPKT_FUNCVAL = "5";
var NOTIFY_NETPKT_FUNCNAMEYDX = "8";
var NOTIFY_NETPKT_CONFCHGYDX = "s";
var NOTIFY_NETPKT_FLUSHV2YDX = "t";
var NOTIFY_NETPKT_FUNCV2YDX = "u";
var NOTIFY_NETPKT_TIMEV2YDX = "v";
var NOTIFY_NETPKT_DEVLOGYDX = "w";
var NOTIFY_NETPKT_TIMEVALYDX = "x";
var NOTIFY_NETPKT_FUNCVALYDX = "y";
var NOTIFY_NETPKT_TIMEAVGYDX = "z";
var NOTIFY_NETPKT_STOP = 10;
var NOTIFY_V2_6RAWBYTES = 1;
var NOTIFY_V2_TYPEDDATA = 2;
var PUBVAL_LEGACY = 0;
var PUBVAL_1RAWBYTE = 1;
var PUBVAL_2RAWBYTES = 2;
var PUBVAL_3RAWBYTES = 3;
var PUBVAL_4RAWBYTES = 4;
var PUBVAL_5RAWBYTES = 5;
var PUBVAL_6RAWBYTES = 6;
var PUBVAL_C_LONG = 7;
var PUBVAL_C_FLOAT = 8;
var PUBVAL_YOCTO_FLOAT_E3 = 9;
var YOCTO_PUBVAL_SIZE = 6;
var YOCTO_HASH_BUF_SIZE = 28;
var YOCTO_BASETYPE_FUNCTION = 0;
var YOCTO_BASETYPE_SENSOR = 1;
var Y_BASETYPES = {
  Function: YOCTO_BASETYPE_FUNCTION,
  Sensor: YOCTO_BASETYPE_SENSOR
};
var YErrorMsg = class {
  constructor(msg = "") {
    this.msg = msg;
  }
};
var YoctoError = class _YoctoError extends Error {
  constructor(...params) {
    super(...params);
    this.errorMsg = this.name;
    this.name = "YoctoError";
    if ("captureStackTrace" in Error) {
      Error.captureStackTrace(this, _YoctoError);
    }
  }
};
var _YY_UrlInfo = class {
  constructor(str_url) {
    this.orgUrl = str_url;
    let proto = "auto";
    let user = "";
    let pass = "";
    let port = 4444;
    let host;
    let dom = "";
    if (str_url.slice(0, 7) == "http://") {
      proto = "http";
      str_url = str_url.slice(7);
    } else if (str_url.slice(0, 5) == "ws://") {
      proto = "ws";
      str_url = str_url.slice(5);
    } else if (str_url.slice(0, 8) == "https://") {
      proto = "https";
      port = 4443;
      str_url = str_url.slice(8);
    } else if (str_url.slice(0, 6) == "wss://") {
      proto = "wss";
      port = 4443;
      str_url = str_url.slice(6);
    } else if (str_url.slice(0, 7) == "auto://") {
      str_url = str_url.slice(7);
    } else if (str_url.slice(0, 9) == "secure://") {
      str_url = str_url.slice(9);
      port = 4443;
      proto = "secure";
    }
    str_url = str_url.replace("/not.byn", "");
    if (str_url[str_url.length - 1] == "/") {
      str_url = str_url.slice(0, str_url.length - 1);
    }
    let pos = str_url.indexOf("/");
    if (pos > 0) {
      dom = str_url.slice(pos);
      str_url = str_url.slice(0, pos);
    }
    let authpos = str_url.indexOf("@");
    if (authpos >= 0) {
      let auth = str_url.slice(0, authpos);
      let passpos = auth.indexOf(":");
      if (passpos >= 0) {
        user = auth.slice(0, passpos);
        pass = auth.slice(passpos + 1);
      } else {
        user = auth;
      }
      str_url = str_url.slice(authpos + 1);
    }
    let endv6 = str_url.indexOf("]");
    pos = str_url.indexOf(":");
    if (pos > 0 && endv6 > 0 && pos < endv6) {
      pos = str_url.indexOf(":", endv6);
    }
    if (pos < 0) {
      host = str_url;
      if (dom != "") {
        if (proto == "http") {
          port = 80;
        } else if (proto == "https") {
          port = 443;
        }
      }
    } else {
      host = str_url.slice(0, pos);
      port = YAPIContext.imm_atoi(str_url.slice(pos + 1));
    }
    if (host == "callback") {
      port = 4444;
    }
    this.proto = proto;
    this.user = user;
    this.pass = pass;
    this.host = host;
    this.port = port;
    this.domain = dom;
  }
  imm_getHost() {
    return this.host;
  }
  imm_getPass() {
    return this.pass;
  }
  imm_getPort() {
    return this.port;
  }
  imm_getUser() {
    return this.user;
  }
  imm_getUrl(withProto = false, withUserPass = true, withEndSlash = false) {
    if (this.proto == "usb") {
      return "usb";
    }
    let url = "";
    if (withProto) {
      url += this.proto + "://";
    }
    if (withUserPass && this.user != "") {
      url += this.user;
      if (this.pass != "") {
        url += ":";
        url += this.pass;
      }
      url += "@";
    }
    url += this.host;
    url += ":";
    url += this.port;
    url += this.domain;
    if (withEndSlash && url[url.length - 1] != "/") {
      url += "/";
    }
    return url;
  }
  imm_getRootUrl() {
    return this.imm_getUrl(true, false, true);
  }
  imm_getProto() {
    return this.proto;
  }
  imm_useWebSocket() {
    return this.proto.startsWith("ws") || this.proto == "auto" || this.proto == "secure";
  }
  /**
   * @return subdomain (starting with a /)
   */
  imm_getSubDomain() {
    let dom = this.domain;
    return dom;
  }
  imm_hasAuthParam() {
    return this.user != "";
  }
  imm_useSecureSocket() {
    return "wss" == this.proto || "https" == this.proto || "secure" == this.proto;
  }
  imm_testInfoJson() {
    return this.proto == "auto" || this.proto == "secure" || this.proto == "http" || this.proto == "https";
  }
  imm_updateBestProto(proto, port) {
    this.port = port;
    if (this.proto != "http" && this.proto != "https") {
      this.proto = proto;
    }
  }
  imm_updateForRedirect(host, port, is_secure) {
    this.host = host;
    this.port = port;
    if (this.imm_useWebSocket()) {
      this.proto = is_secure ? "wss" : "ws";
    } else {
      this.proto = is_secure ? "https" : "http";
    }
  }
  imm_updatePortInfo(proto, port) {
    this.proto = proto;
    this.port = port;
  }
  imm_getOriginalURL() {
    return this.orgUrl;
  }
  imm_updateFrom(urlInfo) {
    this.proto = urlInfo.proto;
    this.user = urlInfo.user;
    this.pass = urlInfo.pass;
    this.host = urlInfo.host;
    this.port = urlInfo.port;
    this.domain = urlInfo.domain;
    this.orgUrl = urlInfo.orgUrl;
  }
};
var Y_MD5Ctx = class {
  constructor() {
    this.buf = new Uint32Array(4);
    this.bits = new Uint32Array(2);
    this.inBuf = new ArrayBuffer(64);
    this.in8 = new Uint8Array(this.inBuf);
    this.in32 = new Uint32Array(this.inBuf);
    this.in32[0] = 1;
    this.bigEndian = this.in8[0] != 1;
    this.buf[0] = 1732584193 >>> 0;
    this.buf[1] = 4023233417 >>> 0;
    this.buf[2] = 2562383102 >>> 0;
    this.buf[3] = 271733878 >>> 0;
    this.bits[0] = 0;
    this.bits[1] = 0;
  }
  _byteReverseIn() {
    for (let i = 0; i < 16; i++) {
      let a = this.in32[i];
      this.in32[i] = (a >>> 24 | (a & 255) << 24 | (a & 16711680) >>> 8 | (a & 65280) << 8) >>> 0;
    }
  }
  _transform() {
    let F1 = ((x, y, z) => (z ^ x & (y ^ z)) >>> 0);
    let F2 = ((x, y, z) => F1(z, x, y));
    let F3 = ((x, y, z) => (x ^ y ^ z) >>> 0);
    let F4 = ((x, y, z) => (y ^ (x | ~z)) >>> 0);
    let MD5STEP = ((f, w, x, y, z, data, s) => {
      w = w + f(x, y, z) + (data >>> 0) >>> 0;
      w = (w << s >>> 0 | w >>> 32 - s) >>> 0;
      return w + x >>> 0;
    });
    let a = this.buf[0];
    let b = this.buf[1];
    let c = this.buf[2];
    let d = this.buf[3];
    let dataIn = this.in32;
    a = MD5STEP(F1, a, b, c, d, dataIn[0] + 3614090360, 7);
    d = MD5STEP(F1, d, a, b, c, dataIn[1] + 3905402710, 12);
    c = MD5STEP(F1, c, d, a, b, dataIn[2] + 606105819, 17);
    b = MD5STEP(F1, b, c, d, a, dataIn[3] + 3250441966, 22);
    a = MD5STEP(F1, a, b, c, d, dataIn[4] + 4118548399, 7);
    d = MD5STEP(F1, d, a, b, c, dataIn[5] + 1200080426, 12);
    c = MD5STEP(F1, c, d, a, b, dataIn[6] + 2821735955, 17);
    b = MD5STEP(F1, b, c, d, a, dataIn[7] + 4249261313, 22);
    a = MD5STEP(F1, a, b, c, d, dataIn[8] + 1770035416, 7);
    d = MD5STEP(F1, d, a, b, c, dataIn[9] + 2336552879, 12);
    c = MD5STEP(F1, c, d, a, b, dataIn[10] + 4294925233, 17);
    b = MD5STEP(F1, b, c, d, a, dataIn[11] + 2304563134, 22);
    a = MD5STEP(F1, a, b, c, d, dataIn[12] + 1804603682, 7);
    d = MD5STEP(F1, d, a, b, c, dataIn[13] + 4254626195, 12);
    c = MD5STEP(F1, c, d, a, b, dataIn[14] + 2792965006, 17);
    b = MD5STEP(F1, b, c, d, a, dataIn[15] + 1236535329, 22);
    a = MD5STEP(F2, a, b, c, d, dataIn[1] + 4129170786, 5);
    d = MD5STEP(F2, d, a, b, c, dataIn[6] + 3225465664, 9);
    c = MD5STEP(F2, c, d, a, b, dataIn[11] + 643717713, 14);
    b = MD5STEP(F2, b, c, d, a, dataIn[0] + 3921069994, 20);
    a = MD5STEP(F2, a, b, c, d, dataIn[5] + 3593408605, 5);
    d = MD5STEP(F2, d, a, b, c, dataIn[10] + 38016083, 9);
    c = MD5STEP(F2, c, d, a, b, dataIn[15] + 3634488961, 14);
    b = MD5STEP(F2, b, c, d, a, dataIn[4] + 3889429448, 20);
    a = MD5STEP(F2, a, b, c, d, dataIn[9] + 568446438, 5);
    d = MD5STEP(F2, d, a, b, c, dataIn[14] + 3275163606, 9);
    c = MD5STEP(F2, c, d, a, b, dataIn[3] + 4107603335, 14);
    b = MD5STEP(F2, b, c, d, a, dataIn[8] + 1163531501, 20);
    a = MD5STEP(F2, a, b, c, d, dataIn[13] + 2850285829, 5);
    d = MD5STEP(F2, d, a, b, c, dataIn[2] + 4243563512, 9);
    c = MD5STEP(F2, c, d, a, b, dataIn[7] + 1735328473, 14);
    b = MD5STEP(F2, b, c, d, a, dataIn[12] + 2368359562, 20);
    a = MD5STEP(F3, a, b, c, d, dataIn[5] + 4294588738, 4);
    d = MD5STEP(F3, d, a, b, c, dataIn[8] + 2272392833, 11);
    c = MD5STEP(F3, c, d, a, b, dataIn[11] + 1839030562, 16);
    b = MD5STEP(F3, b, c, d, a, dataIn[14] + 4259657740, 23);
    a = MD5STEP(F3, a, b, c, d, dataIn[1] + 2763975236, 4);
    d = MD5STEP(F3, d, a, b, c, dataIn[4] + 1272893353, 11);
    c = MD5STEP(F3, c, d, a, b, dataIn[7] + 4139469664, 16);
    b = MD5STEP(F3, b, c, d, a, dataIn[10] + 3200236656, 23);
    a = MD5STEP(F3, a, b, c, d, dataIn[13] + 681279174, 4);
    d = MD5STEP(F3, d, a, b, c, dataIn[0] + 3936430074, 11);
    c = MD5STEP(F3, c, d, a, b, dataIn[3] + 3572445317, 16);
    b = MD5STEP(F3, b, c, d, a, dataIn[6] + 76029189, 23);
    a = MD5STEP(F3, a, b, c, d, dataIn[9] + 3654602809, 4);
    d = MD5STEP(F3, d, a, b, c, dataIn[12] + 3873151461, 11);
    c = MD5STEP(F3, c, d, a, b, dataIn[15] + 530742520, 16);
    b = MD5STEP(F3, b, c, d, a, dataIn[2] + 3299628645, 23);
    a = MD5STEP(F4, a, b, c, d, dataIn[0] + 4096336452, 6);
    d = MD5STEP(F4, d, a, b, c, dataIn[7] + 1126891415, 10);
    c = MD5STEP(F4, c, d, a, b, dataIn[14] + 2878612391, 15);
    b = MD5STEP(F4, b, c, d, a, dataIn[5] + 4237533241, 21);
    a = MD5STEP(F4, a, b, c, d, dataIn[12] + 1700485571, 6);
    d = MD5STEP(F4, d, a, b, c, dataIn[3] + 2399980690, 10);
    c = MD5STEP(F4, c, d, a, b, dataIn[10] + 4293915773, 15);
    b = MD5STEP(F4, b, c, d, a, dataIn[1] + 2240044497, 21);
    a = MD5STEP(F4, a, b, c, d, dataIn[8] + 1873313359, 6);
    d = MD5STEP(F4, d, a, b, c, dataIn[15] + 4264355552, 10);
    c = MD5STEP(F4, c, d, a, b, dataIn[6] + 2734768916, 15);
    b = MD5STEP(F4, b, c, d, a, dataIn[13] + 1309151649, 21);
    a = MD5STEP(F4, a, b, c, d, dataIn[4] + 4149444226, 6);
    d = MD5STEP(F4, d, a, b, c, dataIn[11] + 3174756917, 10);
    c = MD5STEP(F4, c, d, a, b, dataIn[2] + 718787259, 15);
    b = MD5STEP(F4, b, c, d, a, dataIn[9] + 3951481745, 21);
    this.buf[0] = (this.buf[0] + a & 4294967295) >>> 0;
    this.buf[1] = (this.buf[1] + b & 4294967295) >>> 0;
    this.buf[2] = (this.buf[2] + c & 4294967295) >>> 0;
    this.buf[3] = (this.buf[3] + d & 4294967295) >>> 0;
  }
  addData(buf) {
    let len = buf.length;
    let pos = 0;
    let t = this.bits[0];
    this.bits[0] = t + (len << 3) >>> 0;
    if (this.bits[0] < t) {
      this.bits[1]++;
    }
    this.bits[1] += len >>> 29;
    t = t >>> 3 & 63;
    while (pos < len) {
      while (pos < len && t < 64) {
        this.in8[t++] = buf[pos++];
      }
      if (t < 64)
        return;
      if (this.bigEndian)
        this._byteReverseIn();
      this._transform();
      t = 0;
    }
  }
  calculate() {
    let t = this.bits[0] >>> 3 & 63;
    this.in8[t++] = 128;
    if (t > 56) {
      while (t < 64) {
        this.in8[t++] = 0;
      }
      if (this.bigEndian)
        this._byteReverseIn();
      this._transform();
      for (t = 0; t < 14; t++) {
        this.in32[t] = 0;
      }
    } else {
      while (t < 56) {
        this.in8[t++] = 0;
      }
      if (this.bigEndian)
        this._byteReverseIn();
    }
    this.in32[14] = this.bits[0];
    this.in32[15] = this.bits[1];
    this._transform();
    let res = new Uint8Array(16);
    for (t = 0; t < 16; t++) {
      res[t] = this.buf[t >>> 2] >>> 8 * (t & 3) & 255;
    }
    return res;
  }
};
var YFunctionType = class {
  constructor(yapi, classname) {
    this._yapi = yapi;
    this._className = classname;
    this._connectedFns = {};
    this._requestedFns = {};
    this._hwIdByName = {};
    this._nameByHwId = {};
    this._valueByHwId = {};
    this._baseType = 0;
  }
  /** Index a single function given by HardwareId and logical name; store any advertised value
   *
   * @returns true iff there was a logical name discrepancy
   */
  imm_reindexFunction(str_hwid, str_name, str_val, int_basetype) {
    let currname = this._nameByHwId[str_hwid];
    let res = false;
    if (currname == void 0 || currname == "") {
      if (str_name != "") {
        this._nameByHwId[str_hwid] = str_name;
        res = true;
      }
    } else if (currname != str_name) {
      if (this._hwIdByName[currname] == str_hwid) {
        delete this._hwIdByName[currname];
      }
      if (str_name != "") {
        this._nameByHwId[str_hwid] = str_name;
      } else {
        delete this._nameByHwId[str_hwid];
      }
      res = true;
    }
    if (str_name != "") {
      this._hwIdByName[str_name] = str_hwid;
    }
    if (str_val) {
      this._valueByHwId[str_hwid] = str_val;
    } else {
      if (this._valueByHwId[str_hwid] == void 0) {
        this._valueByHwId[str_hwid] = "";
      }
    }
    if (int_basetype) {
      if (this._baseType == 0) {
        this._baseType = int_basetype;
      }
    }
    return res;
  }
  /** Forget a disconnected function given by HardwareId
   */
  imm_forgetFunction(str_hwid) {
    let currname = this._nameByHwId[str_hwid];
    if (currname != void 0) {
      if (currname != "" && this._hwIdByName[currname] == str_hwid) {
        delete this._hwIdByName[currname];
      }
      delete this._nameByHwId[str_hwid];
    }
    if (this._valueByHwId[str_hwid] != void 0) {
      delete this._valueByHwId[str_hwid];
    }
    let con_fn = this._connectedFns[str_hwid];
    if (con_fn) {
      this._requestedFns[str_hwid] = con_fn;
      delete this._connectedFns[str_hwid];
    }
  }
  /** Find the exact Hardware Id of the specified function, if currently connected
   * If device is not known as connected, return a clean error
   * This function will not cause any network access
   */
  imm_resolve(str_func) {
    let res;
    let dotpos = str_func.indexOf(".");
    if (dotpos < 0) {
      res = this._hwIdByName[str_func];
      if (res != void 0) {
        return {
          errorType: YAPI_SUCCESS,
          errorMsg: "no error",
          result: String(res)
        };
      }
      dotpos = str_func.length;
      str_func += "." + this._className.substr(0, 1).toLowerCase() + this._className.substr(1);
    }
    if (this._valueByHwId[str_func] != void 0) {
      return {
        errorType: YAPI_SUCCESS,
        errorMsg: "no error",
        result: String(str_func)
      };
    }
    let serial = "";
    let funcid;
    if (dotpos > 0) {
      let devid = str_func.substr(0, dotpos);
      funcid = str_func.substr(dotpos + 1);
      let dev = this._yapi.imm_getDevice(devid);
      if (!dev) {
        return {
          errorType: YAPI_DEVICE_NOT_FOUND,
          errorMsg: "Device [" + devid + "] not online",
          result: ""
        };
      }
      serial = dev.imm_getSerialNumber();
      res = serial + "." + funcid;
      if (this._valueByHwId[res] != void 0) {
        return {
          errorType: YAPI_SUCCESS,
          errorMsg: "no error",
          result: String(res)
        };
      }
      let i, nfun = dev.imm_functionCount();
      for (i = 0; i < nfun; i++) {
        res = serial + "." + dev.imm_functionId(i);
        let name = this._nameByHwId[res];
        if (name != void 0 && name == funcid) {
          return {
            errorType: YAPI_SUCCESS,
            errorMsg: "no error",
            result: String(res)
          };
        }
      }
    } else {
      funcid = str_func.substr(1);
      for (let hwid_str in this._connectedFns) {
        let pos = hwid_str.indexOf(".");
        let str_function = hwid_str.substr(pos + 1);
        if (str_function == funcid) {
          return {
            errorType: YAPI_SUCCESS,
            errorMsg: "no error",
            result: String(hwid_str)
          };
        }
      }
    }
    return {
      errorType: YAPI_DEVICE_NOT_FOUND,
      errorMsg: "No function [" + funcid + "] found on device [" + serial + "]",
      result: ""
    };
  }
  /** Find the friendly name (use logical name if available) of the specified function, if currently connected
   * If device is not known as connected, return a clean error
   * This function will not cause any network access
   */
  imm_getFriendlyName(str_func) {
    let resolved = this.imm_resolve(str_func);
    let name;
    if (resolved.errorType != YAPI_SUCCESS) {
      return resolved;
    }
    let hwId = resolved.result;
    if (this._className == "Module") {
      let friend = hwId;
      name = this._nameByHwId[friend];
      if (name != void 0 && name != "") {
        friend = this._nameByHwId[friend];
      }
      return {
        errorType: YAPI_SUCCESS,
        errorMsg: "no error",
        result: String(friend)
      };
    } else {
      let pos = hwId.indexOf(".");
      let str_serialMod = hwId.substr(0, pos);
      let str_friendModFull = this._yapi.imm_getFriendlyNameFunction("Module", str_serialMod).result;
      let int_friendModDot = str_friendModFull.indexOf(".");
      let str_friendMod = int_friendModDot > 0 ? str_friendModFull.substr(0, int_friendModDot) : str_friendModFull;
      let str_friendFunc = hwId.substr(pos + 1);
      name = this._nameByHwId[hwId];
      if (name != void 0 && name != "") {
        str_friendFunc = name;
      }
      return {
        errorType: YAPI_SUCCESS,
        errorMsg: "no error",
        result: String(str_friendMod + "." + str_friendFunc)
      };
    }
  }
  /** Associates a given function object to a function id
   */
  imm_setFunction(str_func, obj_func) {
    let funres = this.imm_resolve(str_func);
    if (funres.result) {
      this._connectedFns[funres.result] = obj_func;
    } else {
      this._requestedFns[str_func] = obj_func;
    }
  }
  /** Retrieve a function object by hardware id, updating the indexes on the fly if needed
   */
  imm_getFunction(str_func) {
    let funres = this.imm_resolve(str_func);
    if (funres.errorType == YAPI_SUCCESS) {
      let conn_fn = this._connectedFns[funres.result];
      if (conn_fn != void 0) {
        return conn_fn;
      }
      let req_fn = this._requestedFns[str_func];
      if (req_fn != void 0) {
        this._connectedFns[funres.result] = req_fn;
        delete this._requestedFns[str_func];
      }
      return req_fn;
    } else {
      return this._requestedFns[str_func];
    }
  }
  /** Stores a function advertised value by hardware id, and tell if an event should be queued for it
   */
  imm_setFunctionValue(str_hwid, str_pubval) {
    let currval = this._valueByHwId[str_hwid];
    if (!(currval == void 0) && currval == str_pubval) {
      return false;
    }
    this._valueByHwId[str_hwid] = str_pubval;
    return true;
  }
  /** Retrieve a function advertised value by hardware id
   */
  imm_getFunctionValue(str_hwid) {
    return this._valueByHwId[str_hwid];
  }
  /** Return the basetype of this function class
   */
  imm_getBaseType() {
    return this._baseType;
  }
  /** Test if function type is compatible with basetype
   */
  imm_matchBaseType(baseclass) {
    return baseclass == YOCTO_BASETYPE_FUNCTION || baseclass == this._baseType;
  }
  /** Find the hardwareId of the first instance of a given function class
   */
  imm_getFirstHardwareId() {
    let res = null;
    for (res in this._valueByHwId) {
      break;
    }
    return res;
  }
  /** Find the hardwareId for the next instance of a given function class
   */
  imm_getNextHardwareId(str_hwid) {
    for (let iter_hwid in this._valueByHwId) {
      if (str_hwid == "!") {
        return iter_hwid;
      }
      if (str_hwid == iter_hwid) {
        str_hwid = "!";
      }
    }
    return null;
  }
};
var YHTTPBody = class {
  /** Object storing a file to upload
   */
  constructor(str_fname, bin_data, fun_progressCb) {
    this.fname = str_fname;
    this.data = bin_data;
    this.progressCb = fun_progressCb;
  }
};
var YHTTPRequest = class {
  /** Object storing the result of any HTTP Query, with status code and error message
   */
  constructor(bin_res, int_errType = YAPI_SUCCESS, str_errMsg = "no error") {
    this.devUrl = null;
    this.obj_result = null;
    this.asyncId = 0;
    this.acceptor = null;
    this.toBeSent = null;
    this.sendPos = 0;
    this.progressCb = null;
    this.timeoutId = null;
    this.sendTimeoutId = null;
    this.next = null;
    this._creat = "";
    this._sent = "";
    this.bin_result = bin_res;
    this.errorType = int_errType;
    this.errorMsg = str_errMsg;
  }
};
var YFuncRequest = class {
  /* Object storing the result of a function request, with status code and error message */
  constructor(obj_res, int_errType = YAPI_SUCCESS, str_errMsg = "no error") {
    this.errorType = int_errType;
    this.errorMsg = str_errMsg;
    this.obj_result = obj_res;
  }
};
var YDataStream = class _YDataStream {
  // API symbols as static members
  //--- (end of generated code: YDataStream attributes declaration)
  constructor(obj_parent, obj_dataset, encoded) {
    this._cal = null;
    this._runNo = 0;
    this._utcStamp = 0;
    this._nCols = 0;
    this._nRows = 0;
    this._startTime = 0;
    this._duration = 0;
    this._dataSamplesInterval = 0;
    this._firstMeasureDuration = 0;
    this._columnNames = [];
    this._functionId = "";
    this._isClosed = false;
    this._isAvg = false;
    this._minVal = 0;
    this._avgVal = 0;
    this._maxVal = 0;
    this._values = [];
    this._isLoaded = false;
    this.DATA_INVALID = YAPI_INVALID_DOUBLE;
    this.DURATION_INVALID = YAPI_INVALID_DOUBLE;
    this._parent = obj_parent;
    this._yapi = this._parent._yapi;
    if (typeof obj_dataset != "undefined") {
      this.imm_initFromDataSet(obj_dataset, encoded);
    }
  }
  //--- (generated code: YDataStream implementation)
  _parseCalibArr(iCalib) {
    let caltyp;
    let calhdl;
    let maxpos;
    let position;
    let calpar = [];
    let calraw = [];
    let calref = [];
    let fRaw;
    let fRef;
    caltyp = iCalib[0] / 1e3 >> 0;
    if (caltyp < YOCTO_CALIB_TYPE_OFS) {
      this._cal = null;
      return YAPI_SUCCESS;
    }
    calhdl = this._yapi.imm_getCalibrationHandler(caltyp);
    if (!(calhdl != null)) {
      this._cal = null;
      return YAPI_SUCCESS;
    }
    maxpos = iCalib.length;
    calpar.length = 0;
    position = 1;
    while (position < maxpos) {
      calpar.push(iCalib[position]);
      position = position + 1;
    }
    calraw.length = 0;
    calref.length = 0;
    position = 1;
    while (position + 1 < maxpos) {
      fRaw = iCalib[position];
      fRaw = fRaw / 1e3;
      fRef = iCalib[position + 1];
      fRef = fRef / 1e3;
      calraw.push(fRaw);
      calref.push(fRef);
      position = position + 2;
    }
    this._cal = { src: "", hdl: calhdl, typ: caltyp, par: calpar, raw: calraw, cal: calref };
    return YAPI_SUCCESS;
  }
  imm_initFromDataSet(dataset, encoded) {
    let val;
    let ms_offset;
    let samplesPerHour;
    let caltyp;
    let iCalib = [];
    this._runNo = encoded[0] + (encoded[1] << 16);
    this._utcStamp = encoded[2] + (encoded[3] << 16);
    val = encoded[4];
    this._isAvg = (val & 256) == 0;
    samplesPerHour = val & 255;
    if ((val & 256) != 0) {
      samplesPerHour = samplesPerHour * 3600;
    } else {
      if ((val & 512) != 0) {
        samplesPerHour = samplesPerHour * 60;
      }
    }
    this._dataSamplesInterval = 3600 / samplesPerHour;
    ms_offset = encoded[6];
    if (ms_offset < 1e3) {
      this._startTime = this._utcStamp + ms_offset / 1e3;
    } else {
      this._startTime = this._utcStamp - this._dataSamplesInterval;
    }
    this._firstMeasureDuration = encoded[5];
    if (!this._isAvg) {
      this._firstMeasureDuration = this._firstMeasureDuration / 1e3;
    }
    val = encoded[7];
    this._isClosed = val != 65535;
    if (val == 65535) {
      val = 0;
    }
    this._nRows = val;
    if (this._nRows > 0) {
      if (this._firstMeasureDuration > 0) {
        this._duration = this._firstMeasureDuration + (this._nRows - 1) * this._dataSamplesInterval;
      } else {
        this._duration = this._nRows * this._dataSamplesInterval;
      }
    } else {
      this._duration = 0;
    }
    iCalib = dataset.imm_get_calibration();
    caltyp = iCalib[0];
    if (caltyp == 0) {
      this._cal = null;
    } else {
      this._parseCalibArr(iCalib);
    }
    this._functionId = dataset.imm_get_functionId();
    if (this._isAvg) {
      this._columnNames.length = 0;
      this._columnNames.push(this._functionId + "_min");
      this._columnNames.push(this._functionId + "_avg");
      this._columnNames.push(this._functionId + "_max");
      this._nCols = 3;
    } else {
      this._columnNames.length = 0;
      this._columnNames.push(this._functionId);
      this._nCols = 1;
    }
    if (this._nRows > 0) {
      this._avgVal = this.imm_decodeAvg(encoded[8] + ((encoded[9] ^ 32768) << 16), 1);
      this._minVal = this.imm_decodeVal(encoded[10] + (encoded[11] << 16));
      this._maxVal = this.imm_decodeVal(encoded[12] + (encoded[13] << 16));
    }
    return 0;
  }
  imm_parseStream(sdata) {
    let idx;
    let udat = [];
    let dat = [];
    if (this._isLoaded && !this._isClosed) {
      return YAPI_SUCCESS;
    }
    if (sdata.length == 0) {
      this._nRows = 0;
      return YAPI_SUCCESS;
    }
    udat = this._yapi.imm_decodeWords(this._parent.imm_json_get_string(sdata));
    this._values.length = 0;
    idx = 0;
    if (this._isAvg) {
      while (idx + 3 < udat.length) {
        dat.length = 0;
        if (udat[idx] == 65535 && udat[idx + 1] == 65535) {
          dat.push(NaN);
          dat.push(NaN);
          dat.push(NaN);
        } else {
          dat.push(this.imm_decodeVal(udat[idx + 2] + (udat[idx + 3] << 16)));
          dat.push(this.imm_decodeAvg(udat[idx] + ((udat[idx + 1] ^ 32768) << 16), 1));
          dat.push(this.imm_decodeVal(udat[idx + 4] + (udat[idx + 5] << 16)));
        }
        idx = idx + 6;
        this._values.push(dat.slice());
      }
    } else {
      while (idx + 1 < udat.length) {
        dat.length = 0;
        if (udat[idx] == 65535 && udat[idx + 1] == 65535) {
          dat.push(NaN);
        } else {
          dat.push(this.imm_decodeAvg(udat[idx] + ((udat[idx + 1] ^ 32768) << 16), 1));
        }
        this._values.push(dat.slice());
        idx = idx + 2;
      }
    }
    this._nRows = this._values.length;
    this._isLoaded = true;
    return YAPI_SUCCESS;
  }
  imm_wasLoaded() {
    return this._isLoaded;
  }
  imm_get_url() {
    let url;
    url = "logger.json?id=" + this._functionId + "&run=" + String(Math.round(this._runNo)) + "&utc=" + String(Math.round(this._utcStamp));
    return url;
  }
  imm_get_baseurl() {
    let url;
    url = "logger.json?id=" + this._functionId + "&run=" + String(Math.round(this._runNo)) + "&utc=";
    return url;
  }
  imm_get_urlsuffix() {
    let url;
    url = String(Math.round(this._utcStamp));
    return url;
  }
  async loadStream() {
    return this.imm_parseStream(await this._parent._download(this.imm_get_url()));
  }
  imm_decodeVal(w) {
    let val;
    val = w / 1e3;
    if (!(this._cal == null)) {
      val = this._cal.hdl(val, this._cal.typ, this._cal.par, this._cal.raw, this._cal.cal);
    }
    return val;
  }
  imm_decodeAvg(dw, count) {
    let val;
    val = dw / 1e3;
    if (!(this._cal == null)) {
      val = this._cal.hdl(val, this._cal.typ, this._cal.par, this._cal.raw, this._cal.cal);
    }
    return val;
  }
  async isClosed() {
    return this._isClosed;
  }
  /**
   * Returns the run index of the data stream. A run can be made of
   * multiple datastreams, for different time intervals.
   *
   * @return an unsigned number corresponding to the run index.
   */
  async get_runIndex() {
    return this._runNo;
  }
  /**
   * Returns the relative start time of the data stream, measured in seconds.
   * For recent firmwares, the value is relative to the present time,
   * which means the value is always negative.
   * If the device uses a firmware older than version 13000, value is
   * relative to the start of the time the device was powered on, and
   * is always positive.
   * If you need an absolute UTC timestamp, use get_realStartTimeUTC().
   *
   * <b>DEPRECATED</b>: This method has been replaced by get_realStartTimeUTC().
   *
   * @return an unsigned number corresponding to the number of seconds
   *         between the start of the run and the beginning of this data
   *         stream.
   */
  async get_startTime() {
    return this._utcStamp - (Date.now() / 1e3 >> 0);
  }
  /**
   * Returns the start time of the data stream, relative to the Jan 1, 1970.
   * If the UTC time was not set in the datalogger at the time of the recording
   * of this data stream, this method returns 0.
   *
   * <b>DEPRECATED</b>: This method has been replaced by get_realStartTimeUTC().
   *
   * @return an unsigned number corresponding to the number of seconds
   *         between the Jan 1, 1970 and the beginning of this data
   *         stream (i.e. Unix time representation of the absolute time).
   */
  async get_startTimeUTC() {
    return Math.round(this._startTime);
  }
  /**
   * Returns the start time of the data stream, relative to the Jan 1, 1970.
   * If the UTC time was not set in the datalogger at the time of the recording
   * of this data stream, this method returns 0.
   *
   * @return a floating-point number  corresponding to the number of seconds
   *         between the Jan 1, 1970 and the beginning of this data
   *         stream (i.e. Unix time representation of the absolute time).
   */
  async get_realStartTimeUTC() {
    return this._startTime;
  }
  /**
   * Returns the number of milliseconds between two consecutive
   * rows of this data stream. By default, the data logger records one row
   * per second, but the recording frequency can be changed for
   * each device function
   *
   * @return an unsigned number corresponding to a number of milliseconds.
   */
  async get_dataSamplesIntervalMs() {
    return Math.round(this._dataSamplesInterval * 1e3);
  }
  async get_dataSamplesInterval() {
    return this._dataSamplesInterval;
  }
  async get_firstDataSamplesInterval() {
    return this._firstMeasureDuration;
  }
  /**
   * Returns the number of data rows present in this stream.
   *
   * If the device uses a firmware older than version 13000,
   * this method fetches the whole data stream from the device
   * if not yet done, which can cause a little delay.
   *
   * @return an unsigned number corresponding to the number of rows.
   *
   * On failure, throws an exception or returns zero.
   */
  async get_rowCount() {
    if (this._nRows != 0 && this._isClosed) {
      return this._nRows;
    }
    await this.loadStream();
    return this._nRows;
  }
  /**
   * Returns the number of data columns present in this stream.
   * The meaning of the values present in each column can be obtained
   * using the method get_columnNames().
   *
   * If the device uses a firmware older than version 13000,
   * this method fetches the whole data stream from the device
   * if not yet done, which can cause a little delay.
   *
   * @return an unsigned number corresponding to the number of columns.
   *
   * On failure, throws an exception or returns zero.
   */
  async get_columnCount() {
    if (this._nCols != 0) {
      return this._nCols;
    }
    await this.loadStream();
    return this._nCols;
  }
  /**
   * Returns the title (or meaning) of each data column present in this stream.
   * In most case, the title of the data column is the hardware identifier
   * of the sensor that produced the data. For streams recorded at a lower
   * recording rate, the dataLogger stores the min, average and max value
   * during each measure interval into three columns with suffixes _min,
   * _avg and _max respectively.
   *
   * If the device uses a firmware older than version 13000,
   * this method fetches the whole data stream from the device
   * if not yet done, which can cause a little delay.
   *
   * @return a list containing as many strings as there are columns in the
   *         data stream.
   *
   * On failure, throws an exception or returns an empty array.
   */
  async get_columnNames() {
    if (this._columnNames.length != 0) {
      return this._columnNames;
    }
    await this.loadStream();
    return this._columnNames;
  }
  /**
   * Returns the smallest measure observed within this stream.
   * If the device uses a firmware older than version 13000,
   * this method will always return YDataStream.DATA_INVALID.
   *
   * @return a floating-point number corresponding to the smallest value,
   *         or YDataStream.DATA_INVALID if the stream is not yet complete (still recording).
   *
   * On failure, throws an exception or returns YDataStream.DATA_INVALID.
   */
  async get_minValue() {
    return this._minVal;
  }
  /**
   * Returns the average of all measures observed within this stream.
   * If the device uses a firmware older than version 13000,
   * this method will always return YDataStream.DATA_INVALID.
   *
   * @return a floating-point number corresponding to the average value,
   *         or YDataStream.DATA_INVALID if the stream is not yet complete (still recording).
   *
   * On failure, throws an exception or returns YDataStream.DATA_INVALID.
   */
  async get_averageValue() {
    return this._avgVal;
  }
  /**
   * Returns the largest measure observed within this stream.
   * If the device uses a firmware older than version 13000,
   * this method will always return YDataStream.DATA_INVALID.
   *
   * @return a floating-point number corresponding to the largest value,
   *         or YDataStream.DATA_INVALID if the stream is not yet complete (still recording).
   *
   * On failure, throws an exception or returns YDataStream.DATA_INVALID.
   */
  async get_maxValue() {
    return this._maxVal;
  }
  async get_realDuration() {
    if (this._isClosed) {
      return this._duration;
    }
    return (Date.now() / 1e3 >> 0) - this._utcStamp;
  }
  /**
   * Returns the whole data set contained in the stream, as a bidimensional
   * table of numbers.
   * The meaning of the values present in each column can be obtained
   * using the method get_columnNames().
   *
   * This method fetches the whole data stream from the device,
   * if not yet done.
   *
   * @return a list containing as many elements as there are rows in the
   *         data stream. Each row itself is a list of floating-point
   *         numbers.
   *
   * On failure, throws an exception or returns an empty array.
   */
  async get_dataRows() {
    if (this._values.length == 0 || !this._isClosed) {
      await this.loadStream();
    }
    return this._values;
  }
  /**
   * Returns a single measure from the data stream, specified by its
   * row and column index.
   * The meaning of the values present in each column can be obtained
   * using the method get_columnNames().
   *
   * This method fetches the whole data stream from the device,
   * if not yet done.
   *
   * @param row : row index
   * @param col : column index
   *
   * @return a floating-point number
   *
   * On failure, throws an exception or returns YDataStream.DATA_INVALID.
   */
  async get_data(row, col) {
    if (this._values.length == 0 || !this._isClosed) {
      await this.loadStream();
    }
    if (row >= this._values.length) {
      return _YDataStream.DATA_INVALID;
    }
    if (col >= this._values[row].length) {
      return _YDataStream.DATA_INVALID;
    }
    return this._values[row][col];
  }
};
YDataStream.DATA_INVALID = YAPI_INVALID_DOUBLE;
YDataStream.DURATION_INVALID = YAPI_INVALID_DOUBLE;
var YDataSet = class {
  // API symbols as static members
  //--- (end of generated code: YDataSet attributes declaration)
  constructor(obj_parent, str_functionId = "", str_unit = "", double_startTime = 0, double_endTime = 0) {
    this._hardwareId = "";
    this._functionId = "";
    this._unit = "";
    this._bulkLoad = 0;
    this._startTimeMs = 0;
    this._endTimeMs = 0;
    this._progress = 0;
    this._calib = [];
    this._streams = [];
    this._preview = [];
    this._measures = [];
    this._summaryMinVal = 0;
    this._summaryMaxVal = 0;
    this._summaryTotalAvg = 0;
    this._summaryTotalTime = 0;
    this.DATA_INVALID = YAPI_INVALID_DOUBLE;
    this.DURATION_INVALID = YAPI_INVALID_DOUBLE;
    this.HARDWAREID_INVALID = YAPI_INVALID_STRING;
    this.FUNCTIONID_INVALID = YAPI_INVALID_STRING;
    this.UNIT_INVALID = YAPI_INVALID_STRING;
    this._summary = new YMeasure(0, 0, 0, 0, 0);
    if (str_functionId == "") {
      this._parent = obj_parent;
      this._yapi = obj_parent._yapi;
      this._startTimeMs = 0;
      this._endTimeMs = 0;
    } else {
      this._parent = obj_parent;
      this._yapi = obj_parent._yapi;
      this._functionId = str_functionId;
      this._unit = str_unit;
      this._startTimeMs = double_startTime * 1e3;
      this._endTimeMs = double_endTime * 1e3;
      this._progress = -1;
    }
  }
  imm_get_functionId() {
    return this._functionId;
  }
  //--- (generated code: YDataSet implementation)
  imm_get_calibration() {
    return this._calib;
  }
  async loadSummary(data) {
    let dataRows = [];
    let tim;
    let mitv;
    let itv;
    let fitv;
    let end_;
    let nCols;
    let minCol;
    let avgCol;
    let maxCol;
    let res;
    let m_pos;
    let previewTotalTime;
    let previewTotalAvg;
    let previewMinVal;
    let previewMaxVal;
    let previewAvgVal;
    let previewStartMs;
    let previewStopMs;
    let previewDuration;
    let streamStartTimeMs;
    let streamDuration;
    let streamEndTimeMs;
    let minVal;
    let avgVal;
    let maxVal;
    let summaryStartMs;
    let summaryStopMs;
    let summaryTotalTime;
    let summaryTotalAvg;
    let summaryMinVal;
    let summaryMaxVal;
    let url;
    let strdata;
    let measure_data = [];
    if (this._progress < 0) {
      strdata = this._yapi.imm_bin2str(data);
      if (strdata == "{}") {
        this._parent._throw(YAPI_VERSION_MISMATCH, "device firmware is too old");
        return YAPI_VERSION_MISMATCH;
      }
      res = await this._parse(strdata);
      if (res < 0) {
        return res;
      }
    }
    summaryTotalTime = 0;
    summaryTotalAvg = 0;
    summaryMinVal = YAPI_MAX_DOUBLE;
    summaryMaxVal = YAPI_MIN_DOUBLE;
    summaryStartMs = YAPI_MAX_DOUBLE;
    summaryStopMs = YAPI_MIN_DOUBLE;
    for (let ii_0 of this._streams) {
      streamStartTimeMs = Math.round(await ii_0.get_realStartTimeUTC() * 1e3);
      streamDuration = await ii_0.get_realDuration();
      streamEndTimeMs = streamStartTimeMs + Math.round(streamDuration * 1e3);
      if (streamStartTimeMs >= this._startTimeMs && (this._endTimeMs == 0 || streamEndTimeMs <= this._endTimeMs)) {
        previewMinVal = await ii_0.get_minValue();
        previewAvgVal = await ii_0.get_averageValue();
        previewMaxVal = await ii_0.get_maxValue();
        previewStartMs = streamStartTimeMs;
        previewStopMs = streamEndTimeMs;
        previewDuration = streamDuration;
      } else {
        if (!ii_0.imm_wasLoaded()) {
          url = ii_0.imm_get_url();
          data = await this._parent._download(url);
          ii_0.imm_parseStream(data);
        }
        dataRows = await ii_0.get_dataRows();
        if (dataRows.length == 0) {
          return this.get_progress();
        }
        tim = streamStartTimeMs;
        fitv = Math.round(await ii_0.get_firstDataSamplesInterval() * 1e3);
        itv = Math.round(await ii_0.get_dataSamplesInterval() * 1e3);
        nCols = dataRows[0].length;
        minCol = 0;
        if (nCols > 2) {
          avgCol = 1;
        } else {
          avgCol = 0;
        }
        if (nCols > 2) {
          maxCol = 2;
        } else {
          maxCol = 0;
        }
        previewTotalTime = 0;
        previewTotalAvg = 0;
        previewStartMs = streamEndTimeMs;
        previewStopMs = streamStartTimeMs;
        previewMinVal = YAPI_MAX_DOUBLE;
        previewMaxVal = YAPI_MIN_DOUBLE;
        m_pos = 0;
        while (m_pos < dataRows.length) {
          measure_data = dataRows[m_pos];
          if (m_pos == 0) {
            mitv = fitv;
          } else {
            mitv = itv;
          }
          end_ = tim + mitv;
          if (end_ > this._startTimeMs && (this._endTimeMs == 0 || tim < this._endTimeMs)) {
            minVal = measure_data[minCol];
            avgVal = measure_data[avgCol];
            maxVal = measure_data[maxCol];
            if (previewStartMs > tim) {
              previewStartMs = tim;
            }
            if (previewStopMs < end_) {
              previewStopMs = end_;
            }
            if (previewMinVal > minVal) {
              previewMinVal = minVal;
            }
            if (previewMaxVal < maxVal) {
              previewMaxVal = maxVal;
            }
            if (!isNaN(avgVal)) {
              previewTotalAvg = previewTotalAvg + avgVal * mitv;
              previewTotalTime = previewTotalTime + mitv;
            }
          }
          tim = end_;
          m_pos = m_pos + 1;
        }
        if (previewTotalTime > 0) {
          previewAvgVal = previewTotalAvg / previewTotalTime;
          previewDuration = (previewStopMs - previewStartMs) / 1e3;
        } else {
          previewAvgVal = 0;
          previewDuration = 0;
        }
      }
      this._preview.push(new YMeasure(previewStartMs / 1e3, previewStopMs / 1e3, previewMinVal, previewAvgVal, previewMaxVal));
      if (summaryMinVal > previewMinVal) {
        summaryMinVal = previewMinVal;
      }
      if (summaryMaxVal < previewMaxVal) {
        summaryMaxVal = previewMaxVal;
      }
      if (summaryStartMs > previewStartMs) {
        summaryStartMs = previewStartMs;
      }
      if (summaryStopMs < previewStopMs) {
        summaryStopMs = previewStopMs;
      }
      summaryTotalAvg = summaryTotalAvg + previewAvgVal * previewDuration;
      summaryTotalTime = summaryTotalTime + previewDuration;
    }
    if (this._startTimeMs == 0 || this._startTimeMs > summaryStartMs) {
      this._startTimeMs = summaryStartMs;
    }
    if (this._endTimeMs == 0 || this._endTimeMs < summaryStopMs) {
      this._endTimeMs = summaryStopMs;
    }
    if (summaryTotalTime > 0) {
      this._summary = new YMeasure(summaryStartMs / 1e3, summaryStopMs / 1e3, summaryMinVal, summaryTotalAvg / summaryTotalTime, summaryMaxVal);
    } else {
      this._summary = new YMeasure(0, 0, YAPI_INVALID_DOUBLE, YAPI_INVALID_DOUBLE, YAPI_INVALID_DOUBLE);
    }
    return this.get_progress();
  }
  async processMore(progress, data) {
    let stream;
    let dataRows = [];
    let tim;
    let itv;
    let fitv;
    let avgv;
    let end_;
    let nCols;
    let minCol;
    let avgCol;
    let maxCol;
    let firstMeasure;
    let baseurl;
    let url;
    let suffix;
    let suffixes = [];
    let idx;
    let bulkFile;
    let urlIdx;
    let streamBin = [];
    if (progress != this._progress) {
      return this._progress;
    }
    if (this._progress < 0) {
      return await this.loadSummary(data);
    }
    stream = this._streams[this._progress];
    if (!stream.imm_wasLoaded()) {
      stream.imm_parseStream(data);
    }
    dataRows = await stream.get_dataRows();
    this._progress = this._progress + 1;
    if (dataRows.length == 0) {
      return this.get_progress();
    }
    tim = Math.round(await stream.get_realStartTimeUTC() * 1e3);
    fitv = Math.round(await stream.get_firstDataSamplesInterval() * 1e3);
    itv = Math.round(await stream.get_dataSamplesInterval() * 1e3);
    if (fitv == 0) {
      fitv = itv;
    }
    if (tim < itv) {
      tim = itv;
    }
    nCols = dataRows[0].length;
    minCol = 0;
    if (nCols > 2) {
      avgCol = 1;
    } else {
      avgCol = 0;
    }
    if (nCols > 2) {
      maxCol = 2;
    } else {
      maxCol = 0;
    }
    firstMeasure = true;
    for (let ii_0 of dataRows) {
      if (firstMeasure) {
        end_ = tim + fitv;
        firstMeasure = false;
      } else {
        end_ = tim + itv;
      }
      avgv = ii_0[avgCol];
      if (end_ > this._startTimeMs && (this._endTimeMs == 0 || tim < this._endTimeMs) && !isNaN(avgv)) {
        this._measures.push(new YMeasure(tim / 1e3, end_ / 1e3, ii_0[minCol], avgv, ii_0[maxCol]));
      }
      tim = end_;
    }
    if (this._bulkLoad > 0 && this._progress < this._streams.length) {
      stream = this._streams[this._progress];
      if (stream.imm_wasLoaded()) {
        return this.get_progress();
      }
      baseurl = stream.imm_get_baseurl();
      url = stream.imm_get_url();
      suffix = stream.imm_get_urlsuffix();
      suffixes.push(suffix);
      idx = this._progress + 1;
      while (idx < this._streams.length && suffixes.length < this._bulkLoad) {
        stream = this._streams[idx];
        if (!stream.imm_wasLoaded() && stream.imm_get_baseurl() == baseurl) {
          suffix = stream.imm_get_urlsuffix();
          suffixes.push(suffix);
          url = url + "," + suffix;
        }
        idx = idx + 1;
      }
      bulkFile = await this._parent._download(url);
      streamBin = this._parent.imm_json_get_array(bulkFile);
      urlIdx = 0;
      idx = this._progress;
      while (idx < this._streams.length && urlIdx < suffixes.length && urlIdx < streamBin.length) {
        stream = this._streams[idx];
        if (stream.imm_get_baseurl() == baseurl && stream.imm_get_urlsuffix() == suffixes[urlIdx]) {
          stream.imm_parseStream(streamBin[urlIdx]);
          urlIdx = urlIdx + 1;
        }
        idx = idx + 1;
      }
    }
    return this.get_progress();
  }
  async get_privateDataStreams() {
    return this._streams;
  }
  /**
   * Returns the unique hardware identifier of the function who performed the measures,
   * in the form SERIAL.FUNCTIONID. The unique hardware identifier is composed of the
   * device serial number and of the hardware identifier of the function
   * (for example THRMCPL1-123456.temperature1)
   *
   * @return a string that uniquely identifies the function (ex: THRMCPL1-123456.temperature1)
   *
   * On failure, throws an exception or returns  YDataSet.HARDWAREID_INVALID.
   */
  async get_hardwareId() {
    let mo;
    if (!(this._hardwareId == "")) {
      return this._hardwareId;
    }
    mo = await this._parent.get_module();
    this._hardwareId = await mo.get_serialNumber() + "." + await this.get_functionId();
    return this._hardwareId;
  }
  /**
   * Returns the hardware identifier of the function that performed the measure,
   * without reference to the module. For example temperature1.
   *
   * @return a string that identifies the function (ex: temperature1)
   */
  async get_functionId() {
    return this._functionId;
  }
  /**
   * Returns the measuring unit for the measured value.
   *
   * @return a string that represents a physical unit.
   *
   * On failure, throws an exception or returns  YDataSet.UNIT_INVALID.
   */
  async get_unit() {
    return this._unit;
  }
  /**
   * Returns the start time of the dataset, relative to the Jan 1, 1970.
   * When the YDataSet object is created, the start time is the value passed
   * in parameter to the get_dataSet() function. After the
   * very first call to loadMore(), the start time is updated
   * to reflect the timestamp of the first measure actually found in the
   * dataLogger within the specified range.
   *
   * <b>DEPRECATED</b>: This method has been replaced by get_summary()
   * which contain more precise informations.
   *
   * @return an unsigned number corresponding to the number of seconds
   *         between the Jan 1, 1970 and the beginning of this data
   *         set (i.e. Unix time representation of the absolute time).
   */
  async get_startTimeUTC() {
    return this.imm_get_startTimeUTC();
  }
  imm_get_startTimeUTC() {
    return this._startTimeMs / 1e3;
  }
  /**
   * Returns the end time of the dataset, relative to the Jan 1, 1970.
   * When the YDataSet object is created, the end time is the value passed
   * in parameter to the get_dataSet() function. After the
   * very first call to loadMore(), the end time is updated
   * to reflect the timestamp of the last measure actually found in the
   * dataLogger within the specified range.
   *
   * <b>DEPRECATED</b>: This method has been replaced by get_summary()
   * which contain more precise informations.
   *
   * @return an unsigned number corresponding to the number of seconds
   *         between the Jan 1, 1970 and the end of this data
   *         set (i.e. Unix time representation of the absolute time).
   */
  async get_endTimeUTC() {
    return this.imm_get_endTimeUTC();
  }
  imm_get_endTimeUTC() {
    return Math.round(this._endTimeMs / 1e3);
  }
  /**
   * Returns the progress of the downloads of the measures from the data logger,
   * on a scale from 0 to 100. When the object is instantiated by get_dataSet,
   * the progress is zero. Each time loadMore() is invoked, the progress
   * is updated, to reach the value 100 only once all measures have been loaded.
   *
   * @return an integer in the range 0 to 100 (percentage of completion).
   */
  get_progress() {
    if (this._progress < 0) {
      return 0;
    }
    if (this._progress >= this._streams.length) {
      return 100;
    }
    return (1 + (1 + this._progress) * 98) / (1 + this._streams.length) >> 0;
  }
  /**
   * Loads the next block of measures from the dataLogger, and updates
   * the progress indicator.
   *
   * @return an integer in the range 0 to 100 (percentage of completion),
   *         or a negative error code in case of failure.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async loadMore() {
    let url;
    let stream;
    if (this._progress < 0) {
      url = "logger.json?id=" + this._functionId;
      if (this._startTimeMs != 0) {
        url = url + "&from=" + String(Math.round(this.imm_get_startTimeUTC()));
      }
      if (this._endTimeMs != 0) {
        url = url + "&to=" + String(Math.round(this.imm_get_endTimeUTC() + 1));
      }
    } else {
      if (this._progress >= this._streams.length) {
        return 100;
      } else {
        stream = this._streams[this._progress];
        if (stream.imm_wasLoaded()) {
          return await this.processMore(this._progress, this._yapi.imm_str2bin(""));
        }
        url = stream.imm_get_url();
      }
    }
    try {
      return await this.processMore(this._progress, await this._parent._download(url));
    } catch (e) {
      return await this.processMore(this._progress, await this._parent._download(url));
    }
  }
  /**
   * Returns an YMeasure object which summarizes the whole
   * YDataSet. In includes the following information:
   * - the start of a time interval
   * - the end of a time interval
   * - the minimal value observed during the time interval
   * - the average value observed during the time interval
   * - the maximal value observed during the time interval
   *
   * This summary is available as soon as loadMore() has
   * been called for the first time.
   *
   * @return an YMeasure object
   */
  async get_summary() {
    return this._summary;
  }
  /**
   * Returns a condensed version of the measures that can
   * retrieved in this YDataSet, as a list of YMeasure
   * objects. Each item includes:
   * - the start of a time interval
   * - the end of a time interval
   * - the minimal value observed during the time interval
   * - the average value observed during the time interval
   * - the maximal value observed during the time interval
   *
   * This preview is available as soon as loadMore() has
   * been called for the first time.
   *
   * @return a table of records, where each record depicts the
   *         measured values during a time interval
   *
   * On failure, throws an exception or returns an empty array.
   */
  async get_preview() {
    return this._preview;
  }
  /**
   * Returns the detailed set of measures for the time interval corresponding
   * to a given condensed measures previously returned by get_preview().
   * The result is provided as a list of YMeasure objects.
   *
   * @param measure : condensed measure from the list previously returned by
   *         get_preview().
   *
   * @return a table of records, where each record depicts the
   *         measured values during a time interval
   *
   * On failure, throws an exception or returns an empty array.
   */
  async get_measuresAt(measure) {
    let startUtcMs;
    let stream;
    let dataRows = [];
    let measures = [];
    let tim;
    let itv;
    let end_;
    let nCols;
    let minCol;
    let avgCol;
    let maxCol;
    startUtcMs = measure.get_startTimeUTC() * 1e3;
    stream = null;
    for (let ii_0 of this._streams) {
      if (Math.round(await ii_0.get_realStartTimeUTC() * 1e3) == startUtcMs) {
        stream = ii_0;
      }
    }
    if (stream == null) {
      return measures;
    }
    dataRows = await stream.get_dataRows();
    if (dataRows.length == 0) {
      return measures;
    }
    tim = Math.round(await stream.get_realStartTimeUTC() * 1e3);
    itv = Math.round(await stream.get_dataSamplesInterval() * 1e3);
    if (tim < itv) {
      tim = itv;
    }
    nCols = dataRows[0].length;
    minCol = 0;
    if (nCols > 2) {
      avgCol = 1;
    } else {
      avgCol = 0;
    }
    if (nCols > 2) {
      maxCol = 2;
    } else {
      maxCol = 0;
    }
    for (let ii_1 of dataRows) {
      end_ = tim + itv;
      if (end_ > this._startTimeMs && (this._endTimeMs == 0 || tim < this._endTimeMs)) {
        measures.push(new YMeasure(tim / 1e3, end_ / 1e3, ii_1[minCol], ii_1[avgCol], ii_1[maxCol]));
      }
      tim = end_;
    }
    return measures;
  }
  /**
   * Returns all measured values currently available for this DataSet,
   * as a list of YMeasure objects. Each item includes:
   * - the start of the measure time interval
   * - the end of the measure time interval
   * - the minimal value observed during the time interval
   * - the average value observed during the time interval
   * - the maximal value observed during the time interval
   *
   * Before calling this method, you should call loadMore()
   * to load data from the device. You may have to call loadMore()
   * several time until all rows are loaded, but you can start
   * looking at available data rows before the load is complete.
   *
   * The oldest measures are always loaded first, and the most
   * recent measures will be loaded last. As a result, timestamps
   * are normally sorted in ascending order within the measure table,
   * unless there was an unexpected adjustment of the datalogger UTC
   * clock.
   *
   * @return a table of records, where each record depicts the
   *         measured value for a given time interval
   *
   * On failure, throws an exception or returns an empty array.
   */
  async get_measures() {
    return this._measures;
  }
  //--- (end of generated code: YDataSet implementation)
  // YDataSet parser for stream list
  async _parse(str_json) {
    let loadval = null;
    try {
      loadval = JSON.parse(str_json);
    } catch (err) {
    }
    if (!loadval) {
      this._progress = 0;
      return await this.get_progress();
    }
    this._functionId = loadval.id;
    this._unit = loadval.unit;
    this._bulkLoad = loadval.bulk ? parseInt(loadval.bulk) : 0;
    if (loadval.calib) {
      this._calib = this._yapi.imm_decodeFloats(loadval.calib);
      this._calib[0] = this._calib[0] / 1e3 >> 0;
    } else {
      this._calib = this._yapi.imm_decodeWords(loadval.cal);
    }
    this._summary = new YMeasure(0, 0, 0, 0, 0);
    this._streams = [];
    this._preview = [];
    this._measures = [];
    for (let i = 0; i < loadval.streams.length; i++) {
      let stream = this._parent.imm_findDataStream(this, loadval.streams[i]);
      if (!stream)
        continue;
      let streamStartTime = await stream.get_realStartTimeUTC() * 1e3;
      let streamEndTime = streamStartTime + await stream.get_realDuration() * 1e3;
      if (this._startTimeMs > 0 && streamEndTime <= this._startTimeMs) {
      } else if (this._endTimeMs > 0 && streamStartTime >= this._endTimeMs) {
      } else {
        this._streams.push(stream);
      }
    }
    this._progress = 0;
    return this.get_progress();
  }
};
YDataSet.DATA_INVALID = YAPI_INVALID_DOUBLE;
YDataSet.DURATION_INVALID = YAPI_INVALID_DOUBLE;
YDataSet.HARDWAREID_INVALID = YAPI_INVALID_STRING;
YDataSet.FUNCTIONID_INVALID = YAPI_INVALID_STRING;
YDataSet.UNIT_INVALID = YAPI_INVALID_STRING;
YDataSet.DATA_INVALID = YAPI_INVALID_DOUBLE;
YDataSet.DURATION_INVALID = YAPI_INVALID_DOUBLE;
YDataSet.HARDWAREID_INVALID = YAPI_INVALID_STRING;
YDataSet.FUNCTIONID_INVALID = YAPI_INVALID_STRING;
YDataSet.UNIT_INVALID = YAPI_INVALID_STRING;
var YConsolidatedDataSet = class _YConsolidatedDataSet {
  // API symbols as static members
  //--- (end of generated code: YConsolidatedDataSet attributes declaration)
  constructor(double_startTime, double_endTime, obj_sensorList) {
    this._start = 0;
    this._end = 0;
    this._nsensors = 0;
    this._sensors = [];
    this._datasets = [];
    this._progresss = [];
    this._nextidx = [];
    this._nexttim = [];
    this.imm_init(double_startTime, double_endTime, obj_sensorList);
  }
  //--- (generated code: YConsolidatedDataSet implementation)
  imm_init(startt, endt, sensorList) {
    this._start = startt;
    this._end = endt;
    this._sensors = sensorList;
    this._nsensors = -1;
    return YAPI_SUCCESS;
  }
  /**
   * Returns an object holding historical data for multiple
   * sensors, for a specified time interval.
   * The measures will be retrieved from the data logger, which must have been turned
   * on at the desired time. The resulting object makes it possible to load progressively
   * a large set of measures from multiple sensors, consolidating data on the fly
   * to align records based on measurement timestamps.
   *
   * @param sensorNames : array of logical names or hardware identifiers of the sensors
   *         for which data must be loaded from their data logger.
   * @param startTime : the start of the desired measure time interval,
   *         as a Unix timestamp, i.e. the number of seconds since
   *         January 1, 1970 UTC. The special value 0 can be used
   *         to include any measure, without initial limit.
   * @param endTime : the end of the desired measure time interval,
   *         as a Unix timestamp, i.e. the number of seconds since
   *         January 1, 1970 UTC. The special value 0 can be used
   *         to include any measure, without ending limit.
   *
   * @return an instance of YConsolidatedDataSet, providing access to
   *         consolidated historical data. Records can be loaded progressively
   *         using the YConsolidatedDataSet.nextRecord() method.
   */
  static Init(sensorNames, startTime, endTime) {
    let nSensors;
    let sensorList = [];
    let idx;
    let sensorName;
    let s;
    let obj;
    nSensors = sensorNames.length;
    sensorList.length = 0;
    idx = 0;
    while (idx < nSensors) {
      sensorName = sensorNames[idx];
      s = YSensor.FindSensor(sensorName);
      sensorList.push(s);
      idx = idx + 1;
    }
    obj = new _YConsolidatedDataSet(startTime, endTime, sensorList);
    return obj;
  }
  /**
   * Extracts the next data record from the data logger of all sensors linked to this
   * object.
   *
   * @param datarec : array of floating point numbers, that will be filled by the
   *         function with the timestamp of the measure in first position,
   *         followed by the measured value in next positions.
   *
   * @return an integer in the range 0 to 100 (percentage of completion),
   *         or a negative error code in case of failure.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async nextRecord(datarec) {
    let s;
    let idx;
    let sensor;
    let newdataset;
    let globprogress;
    let currprogress;
    let currnexttim;
    let newvalue;
    let measures = [];
    let nexttime;
    if (this._nsensors == -1) {
      this._nsensors = this._sensors.length;
      this._datasets.length = 0;
      this._progresss.length = 0;
      this._nextidx.length = 0;
      this._nexttim.length = 0;
      s = 0;
      while (s < this._nsensors) {
        sensor = this._sensors[s];
        newdataset = await sensor.get_recordedData(this._start, this._end);
        this._datasets.push(newdataset);
        this._progresss.push(0);
        this._nextidx.push(0);
        this._nexttim.push(0);
        s = s + 1;
      }
    }
    datarec.length = 0;
    nexttime = 0;
    s = 0;
    while (s < this._nsensors) {
      currnexttim = this._nexttim[s];
      if (currnexttim == 0) {
        idx = this._nextidx[s];
        measures = await this._datasets[s].get_measures();
        currprogress = this._progresss[s];
        while (idx >= measures.length && currprogress < 100) {
          currprogress = await this._datasets[s].loadMore();
          if (currprogress < 0) {
            currprogress = 100;
          }
          this._progresss[s] = currprogress;
          measures = await this._datasets[s].get_measures();
        }
        if (idx < measures.length) {
          currnexttim = measures[idx].get_endTimeUTC();
          this._nexttim[s] = currnexttim;
        }
      }
      if (currnexttim > 0) {
        if (nexttime == 0 || nexttime > currnexttim) {
          nexttime = currnexttim;
        }
      }
      s = s + 1;
    }
    if (nexttime == 0) {
      return 100;
    }
    datarec.length = 0;
    datarec.push(nexttime);
    globprogress = 0;
    s = 0;
    while (s < this._nsensors) {
      if (this._nexttim[s] == nexttime) {
        idx = this._nextidx[s];
        measures = await this._datasets[s].get_measures();
        newvalue = measures[idx].get_averageValue();
        datarec.push(newvalue);
        this._nexttim[s] = 0;
        this._nextidx[s] = idx + 1;
      } else {
        datarec.push(NaN);
      }
      currprogress = this._progresss[s];
      globprogress = globprogress + currprogress;
      s = s + 1;
    }
    if (globprogress > 0) {
      globprogress = globprogress / this._nsensors >> 0;
      if (globprogress > 99) {
        globprogress = 99;
      }
    }
    return globprogress;
  }
};
var YDevice = class {
  // Device constructor. Automatically call the YAPI functin to reindex device
  constructor(obj_yapi, str_rooturl, obj_wpRec, obj_ypRecs) {
    this._yapi = obj_yapi;
    this._rootUrl = str_rooturl;
    this._serialNumber = "";
    this._logicalName = "";
    this._productName = "";
    this._productId = 0;
    this._beacon = 0;
    this._devYdx = -1;
    this._lastErrorType = YAPI_SUCCESS;
    this._lastErrorMsg = "no error";
    this._cache = { _expiration: 0, _json: new Uint8Array(0), _precooked: {} };
    this._functions = [];
    this._busy = 0;
    this._pendingQueries = Promise.resolve();
    this._lastTimeRef = 0;
    this._lastDuration = 0;
    this._logCallback = null;
    this._logIsPulling = false;
    this._logpos = 0;
    if (obj_wpRec && obj_ypRecs) {
      this._serialNumber = obj_wpRec.serialNumber;
      this._logicalName = obj_wpRec.logicalName;
      this._productName = obj_wpRec.productName;
      this._productId = obj_wpRec.productId;
      this._beacon = obj_wpRec.beacon;
      this._devYdx = obj_wpRec.index == void 0 ? -1 : obj_wpRec.index;
      this.imm_updateFromYP(obj_ypRecs);
      this._yapi.imm_reindexDevice(this);
    }
  }
  _throw(int_errType, str_errMsg, obj_retVal) {
    this._lastErrorType = int_errType;
    this._lastErrorMsg = str_errMsg;
    return this._yapi._throw(int_errType, str_errMsg, obj_retVal);
  }
  /** Return the root URL used to access a device (including the trailing slash)
   *
   * @returns {string}
   */
  imm_getRootUrl() {
    return this._rootUrl;
  }
  /** Return the serial number of the device, as found during discovery
   *
   * @returns {string}
   */
  imm_getSerialNumber() {
    return this._serialNumber;
  }
  /** Return the logical name of the device, as found during discovery
   *
   * @returns {string}
   */
  imm_getLogicalName() {
    return this._logicalName;
  }
  async getLogicalName() {
    if (this._cache._expiration == 0) {
      await this.refresh();
    }
    return this._logicalName;
  }
  /** Return the product name of the device, as found during discovery
   *
   * @returns {string}
   */
  imm_getProductName() {
    return this._productName;
  }
  /** Return the product Id of the device, as found during discovery
   *
   * @returns {number}
   */
  imm_getProductId() {
    return this._productId;
  }
  /** Return the beacon state of the device, as found during discovery
   *
   * @returns {number}
   */
  imm_getBeacon() {
    return this._beacon;
  }
  /** Return the beacon state of the device, as found during discovery
   *
   * @returns {number}
   */
  async getBeacon() {
    if (this._cache._expiration == 0) {
      await this.refresh();
    }
    return this._beacon;
  }
  // Return the value of the last timestamp sent by the device, if any
  imm_getLastTimeRef() {
    return this._lastTimeRef;
  }
  // Return the value of the last duration sent by the device, if any
  imm_getLastDuration() {
    return this._lastDuration;
  }
  imm_triggerLogPull() {
    if (this._logCallback == null || this._logIsPulling) {
      return;
    }
    this._logIsPulling = true;
    let request = "GET logs.txt?pos=" + this._logpos;
    let prom = this._yapi.devRequest(this._rootUrl, request, null, 0);
    prom.then(async (yreq) => {
      if (yreq.errorType != YAPI_SUCCESS) {
        this._logIsPulling = false;
        return;
      }
      if (this._logCallback == null) {
        this._logIsPulling = false;
        return;
      }
      let resultStr = YAPI.imm_bin2str(yreq.bin_result);
      let pos = resultStr.lastIndexOf("\n@");
      if (pos < 0) {
        this._logIsPulling = false;
        return;
      }
      let logs = resultStr.substring(0, pos);
      let posStr = resultStr.substring(pos + 2);
      this._logpos = parseInt(posStr);
      let module = YModule.FindModuleInContext(this._yapi, this._serialNumber);
      let lines = logs.trim().split("\n");
      try {
        for (let i = 0; i < lines.length; i++) {
          await this._logCallback(module, lines[i]);
        }
      } catch (e) {
        this._yapi.imm_log("Exception in device log callback:", e);
      }
      this._logIsPulling = false;
    });
  }
  imm_registerLogCallback(callback) {
    this._logCallback = callback;
    if (callback != null) {
      this.imm_triggerLogPull();
    } else {
      this._logpos = 0;
      this._logIsPulling = false;
    }
  }
  /** Return the value of the last timestamp sent by the device, if any
   */
  imm_setTimeRef(float_timestamp, float_duration) {
    this._lastTimeRef = float_timestamp;
    this._lastDuration = float_duration;
  }
  /** Return the hub-specific devYdx of the device, as found during discovery
   *
   * @returns {number}
   */
  imm_getDevYdx() {
    return this._devYdx;
  }
  /** Return a string that describes the device (serial number, logical name or root URL)
   *
   * @returns {string}
   */
  imm_describe() {
    let res = this._rootUrl;
    if (this._serialNumber != "") {
      res = this._serialNumber;
      if (this._logicalName != "") {
        res = res + " (" + this._logicalName + ")";
      }
    }
    return this._productName + " " + res;
  }
  /** Update device cache and YAPI function lists from yp records
   */
  imm_updateFromYP(obj_ypRecs) {
    let funidx = 0;
    for (let categ in obj_ypRecs) {
      for (let key in obj_ypRecs[categ]) {
        let rec = obj_ypRecs[categ][key];
        let hwid = rec["hardwareId"];
        let dotpos = hwid.indexOf(".");
        if (hwid.substr(0, dotpos) == this._serialNumber) {
          let funydx = rec["index"];
          if (funydx == void 0)
            funydx = funidx;
          this._functions[funydx] = [hwid.substr(dotpos + 1), rec["logicalName"]];
          funidx++;
        }
      }
    }
  }
  /** Update device cache and YAPI function lists accordingly
   */
  async updateFromReq(yreq, loadval) {
    this._cache._expiration = this._yapi.GetTickCount() + this._yapi.defaultCacheValidity;
    this._cache._json = yreq.bin_result;
    let func;
    let reindex = false;
    if (this._productName == "") {
      for (func in loadval) {
        if (func == "module") {
          this._serialNumber = loadval.module.serialNumber;
          this._logicalName = loadval.module.logicalName;
          this._productName = loadval.module.productName;
          this._productId = loadval.module.productId;
          this._beacon = loadval.module.beacon;
        } else if (func == "services") {
          this.imm_updateFromYP(loadval.services.yellowPages);
        }
      }
      reindex = true;
    } else {
      let renamed = false;
      for (func in loadval) {
        if (func == "module") {
          if (this._logicalName != loadval.module.logicalName) {
            this._logicalName = loadval.module.logicalName;
            reindex = true;
          }
          this._beacon = loadval.module.beacon;
        } else if (func != "services") {
          let name = loadval[func]["logicalName"];
          if (name == void 0)
            name = loadval.module.logicalName;
          let pubval = loadval[func]["advertisedValue"];
          if (pubval != void 0) {
            await this._yapi.setFunctionValue(loadval.module.serialNumber + "." + func, pubval);
          }
          let funydx;
          for (funydx in this._functions) {
            if (this._functions[funydx][0] == func) {
              if (this._functions[funydx][1] != name) {
                this._functions[funydx][1] = name;
                reindex = true;
              }
              break;
            }
          }
        }
      }
    }
    if (reindex) {
      this._yapi.imm_reindexDevice(this);
    }
  }
  // Force the REST API string in cache to expire immediately
  imm_dropCache() {
    this._cache._expiration = 0;
    this._cache._precooked = {};
  }
  /** Retrieve the number of functions (beside "module") in the device
   */
  imm_functionCount() {
    let funcPos = 0;
    for (let key in this._functions) {
      funcPos++;
    }
    return funcPos;
  }
  /** Retrieve the Id of the nth function (beside "module") in the device
   */
  imm_functionId(int_idx) {
    let funcPos = 0;
    for (let key in this._functions) {
      if (int_idx === funcPos) {
        return this._functions[key][0];
      }
      funcPos++;
    }
    return "";
  }
  /** Retrieve the base type of the nth function (beside "module") in the device
   */
  imm_functionBaseType(int_idx) {
    let funid = this.imm_functionId(int_idx);
    if (funid !== "") {
      let ftype = this._yapi.imm_getFunctionBaseType(this._serialNumber + "." + funid);
      for (let baseType in Y_BASETYPES) {
        if (Y_BASETYPES[baseType] === ftype) {
          return baseType;
        }
      }
    }
    return "Function";
  }
  /** Retrieve the type of the nth function (beside 'module') in the device
   */
  imm_functionType(int_idx) {
    let funid = this.imm_functionId(int_idx);
    if (funid !== "") {
      let i;
      for (i = funid.length; i > 0; i--) {
        if (funid[i - 1] > "9") {
          break;
        }
      }
      let functionType = funid[0].toUpperCase() + funid.substr(1, i - 1);
      return functionType;
    }
    return "";
  }
  /** Retrieve the logical name of the nth function (beside "module") in the device
   */
  imm_functionName(int_idx) {
    let funcPos = 0;
    for (let key in this._functions) {
      if (int_idx === funcPos) {
        return this._functions[key][1];
      }
      funcPos++;
    }
    return "";
  }
  /** Retrieve the advertised value of the nth function (beside "module") in the device
   */
  imm_functionValue(int_idx) {
    let funid = this.imm_functionId(int_idx);
    if (funid !== "") {
      return this._yapi.imm_getFunctionValue(this._serialNumber + "." + funid);
    }
    return "";
  }
  /** Retrieve the Id of a function given its funydx (internal function identifier index)
   */
  imm_functionIdByFunYdx(int_funydx) {
    if (this._functions[int_funydx]) {
      return this._functions[int_funydx][0];
    }
    return "";
  }
  /** Map an optimized JZON reply to a previously known JSON structure
   */
  imm_jzon2json(jzon, json) {
    if (Array.isArray(jzon)) {
      let sz = jzon.length;
      if (Array.isArray(json)) {
        let defval = json.length > 0 ? json[0] : null;
        let res = [];
        for (let idx = 0; idx < sz; idx++) {
          res[idx] = this.imm_jzon2json(jzon[idx], defval);
        }
        return res;
      } else if (typeof json === "object") {
        let idx = 0;
        let res = {};
        for (let key in json) {
          if (json.hasOwnProperty(key)) {
            res[key] = this.imm_jzon2json(jzon[idx], json[key]);
            idx++;
          }
        }
        return res;
      } else {
        return jzon;
      }
    } else if (typeof jzon === "object") {
      if (Array.isArray(json)) {
        return jzon;
      } else if (typeof json === "object") {
        let defval = null;
        for (let key in json) {
          if (json.hasOwnProperty(key)) {
            defval = json[key];
            break;
          }
        }
        let res = {};
        for (let key in jzon) {
          if (jzon.hasOwnProperty(key)) {
            if (json.hasOwnProperty(key) && (!Array.isArray(json[key]) || json[key].length > 0)) {
              res[key] = this.imm_jzon2json(jzon[key], json[key]);
            } else {
              res[key] = this.imm_jzon2json(jzon[key], defval);
            }
          }
        }
        return res;
      } else {
        return jzon;
      }
    }
    return jzon;
  }
  /** Get the whole REST API string for a device, from cache if possible
   */
  async requestAPI(int_msValidity) {
    if (this._cache._expiration > this._yapi.GetTickCount()) {
      let res = new YHTTPRequest(this._cache._json);
      res.obj_result = JSON.parse(JSON.stringify(this._cache._precooked));
      return res;
    }
    let req = "GET /api.json";
    let precooked = this._cache._precooked;
    if (precooked.module && precooked.module.firmwareRelease) {
      req += "?fw=" + encodeURIComponent(precooked.module.firmwareRelease);
    }
    let yreq = await this._yapi.devRequest(this._rootUrl, req, null, 0);
    if (yreq.errorType != YAPI_SUCCESS)
      return yreq;
    if (!int_msValidity) {
      int_msValidity = this._yapi.defaultCacheValidity;
    }
    this._cache._json = yreq.bin_result;
    try {
      yreq.obj_result = JSON.parse(this._yapi.imm_bin2str(yreq.bin_result));
      if (Array.isArray(yreq.obj_result)) {
        if (precooked.module) {
          let objres = this.imm_jzon2json(yreq.obj_result, precooked);
          yreq.obj_result = objres;
          if (objres.module && objres.module.serialNumber === objres.module.serialNumber && objres.module.firmwareRelease === objres.module.firmwareRelease) {
            let jsonstr = JSON.stringify(yreq.obj_result);
            this._cache._json = yreq.bin_result = this._yapi.imm_str2bin(jsonstr);
            this._cache._precooked = JSON.parse(jsonstr);
          } else {
            this.imm_dropCache();
            yreq.errorType = YAPI_IO_ERROR;
            yreq.errorMsg = "Request failed, could not parse API JZON result for " + this._rootUrl;
          }
        } else {
          yreq.errorType = YAPI_IO_ERROR;
          yreq.errorMsg = "Request failed, could not parse API array result for " + this._rootUrl;
        }
      } else if (yreq.obj_result.module && yreq.obj_result.module.firmwareRelease) {
        this._cache._precooked = JSON.parse(JSON.stringify(yreq.obj_result));
      }
      this._cache._expiration = this._yapi.GetTickCount() + int_msValidity;
      this._logicalName = yreq.obj_result.module.logicalName;
      this._beacon = yreq.obj_result.module.beacon;
    } catch (err) {
      yreq.errorType = YAPI_IO_ERROR;
      yreq.errorMsg = "Request failed, could not parse API JSON result for " + this._rootUrl;
    }
    return yreq;
  }
  /** Reload a device API (store in cache), and update YAPI function lists accordingly
   *
   * @returns {number}
   */
  async refresh() {
    let yreq = await this.requestAPI(this._yapi.defaultCacheValidity);
    if (yreq.errorType != YAPI_SUCCESS) {
      return this._throw(yreq.errorType, yreq.errorMsg, yreq.errorType);
    }
    await this.updateFromReq(yreq, yreq.obj_result);
    return YAPI_SUCCESS;
  }
  async waitPendingQueries() {
    let newPromise = this._pendingQueries;
    if (newPromise != null) {
      try {
        await newPromise;
      } catch (e) {
        console.log(e);
      }
    }
  }
};
var YFirmwareFile = class _YFirmwareFile {
  constructor(path, serial, pictype, product, firmware, prog_version, ROM_nb_zone, FLA_nb_zone, ROM_total_size, FLA_total_size, data, zone_ofs) {
    this._path = path;
    this._serial = serial;
    this._pictype = pictype;
    this._product = product;
    this._firmware = firmware;
    this._prog_version = prog_version;
    this._ROM_nb_zone = ROM_nb_zone;
    this._FLA_nb_zone = FLA_nb_zone;
    this._ROM_total_size = ROM_total_size;
    this._FLA_total_size = FLA_total_size;
    this._data = data;
    this._zone_ofs = zone_ofs;
  }
  /**
   * Parse the binary buffer provided as input and initialize a new object
   * returns null if the file is not a valid firmware
   */
  static imm_Parse(path, data, force) {
    const BYN_REV_V4 = 4;
    const BYN_REV_V5 = 5;
    const BYN_REV_V6 = 6;
    const MAX_ROM_ZONES_PER_FILES = 16;
    const MAX_FLASH_ZONES_PER_FILES = 4;
    const BYN_HEAD_SIZE_V4 = 96 + 8;
    const BYN_HEAD_SIZE_V5 = 96 + 32;
    const BYN_HEAD_SIZE_V6 = 96 + 48;
    const BYN_MD5_OFS_V6 = 96 + 16;
    let pos = 0;
    let getShort = (() => {
      let res = data[pos] + (data[pos + 1] << 8);
      pos += 2;
      return res;
    });
    let getInt = (() => {
      let res = data[pos] + (data[pos + 1] << 8) + (data[pos + 2] << 16) + (data[pos + 3] << 24);
      pos += 4;
      return res;
    });
    let getString = ((maxlen) => {
      let end = pos + maxlen;
      while (end > pos && data[end - 1] == 0) {
        end--;
      }
      let res = YAPI.imm_bin2str(data.subarray(pos, end));
      pos += maxlen;
      return res;
    });
    let sign = getString(4);
    if (sign != "BYN")
      return null;
    let rev = getShort();
    let serial = getString(20);
    let pictype = getString(20);
    let product = getString(28);
    let firmware = getString(22);
    if (serial.length >= 20)
      return null;
    if (product.length >= 28)
      return null;
    if (firmware.length >= 22)
      return null;
    let ROM_nb_zone = 0;
    let FLA_nb_zone = 0;
    let ROM_total_size = 0;
    let FLA_total_size = 0;
    let prog_buf;
    let prog_version = "";
    let zone_ofs;
    let datasize;
    switch (rev) {
      case BYN_REV_V4:
        zone_ofs = BYN_HEAD_SIZE_V4;
        ROM_nb_zone = getInt();
        datasize = getInt();
        if (ROM_nb_zone > MAX_ROM_ZONES_PER_FILES)
          return null;
        if (datasize != data.length - BYN_HEAD_SIZE_V4)
          return null;
        break;
      case BYN_REV_V5:
        zone_ofs = BYN_HEAD_SIZE_V5;
        prog_version = getString(22);
        if (!force && !_YFirmwareFile.imm_progCompatible(prog_version))
          return null;
        getShort();
        ROM_nb_zone = getInt();
        datasize = getInt();
        if (ROM_nb_zone > MAX_ROM_ZONES_PER_FILES)
          return null;
        if (datasize != data.length - BYN_HEAD_SIZE_V5)
          return null;
        break;
      case BYN_REV_V6:
        zone_ofs = BYN_HEAD_SIZE_V6;
        let md5hdr = data.subarray(pos, pos + 16);
        pos += 16;
        let md5hdrstr = YAPI.imm_bin2hexstr(md5hdr);
        let md5ctx = new Y_MD5Ctx();
        md5ctx.addData(data.subarray(BYN_MD5_OFS_V6));
        let md5bynstr = YAPI.imm_bin2hexstr(md5ctx.calculate());
        if (md5hdrstr != md5bynstr) {
          YAPI.imm_log("Invalid firmware image signature, file is corrupt");
          if (YAPI._logLevel >= 2) {
            YAPI.imm_log("hdr MD5: " + md5hdrstr);
            YAPI.imm_log("byn MD5: " + md5bynstr);
            YAPI.imm_log("byn size: " + data.length);
          }
          return null;
        }
        prog_version = getString(22);
        if (!force && !_YFirmwareFile.imm_progCompatible(prog_version))
          return null;
        ROM_nb_zone = data[pos++];
        FLA_nb_zone = data[pos++];
        ROM_total_size = getInt();
        FLA_total_size = getInt();
        if (ROM_nb_zone > MAX_ROM_ZONES_PER_FILES)
          return null;
        if (FLA_nb_zone > MAX_FLASH_ZONES_PER_FILES)
          return null;
        break;
      default:
        return null;
    }
    return new _YFirmwareFile(path, serial, pictype, product, firmware, prog_version, ROM_nb_zone, FLA_nb_zone, ROM_total_size, FLA_total_size, data, zone_ofs);
  }
  static imm_progCompatible(prog_version) {
    if (prog_version == "")
      return true;
    let apiVer = YAPI.imm_GetAPIVersion();
    let dashpos = apiVer.indexOf("-");
    if (dashpos > 0) {
      apiVer = apiVer.slice(0, dashpos);
    }
    apiVer = apiVer.slice(apiVer.lastIndexOf(".") + 1);
    if (parseInt(prog_version) > parseInt(apiVer)) {
      YAPI.imm_log("checkProgField: byn=" + prog_version + " api=" + apiVer);
      return false;
    }
    return true;
  }
  imm_getSerial() {
    return this._serial;
  }
  imm_getPictype() {
    return this._pictype;
  }
  imm_getProduct() {
    return this._product;
  }
  imm_getFirmwareRelease() {
    return this._firmware;
  }
  imm_getFirmwareReleaseAsInt() {
    return parseInt(this._firmware);
  }
  imm_getProg_version() {
    return this._prog_version;
  }
  imm_getROM_nb_zone() {
    return this._ROM_nb_zone;
  }
  imm_getFLA_nb_zone() {
    return this._FLA_nb_zone;
  }
  imm_getROM_total_size() {
    return this._ROM_total_size;
  }
  imm_getFLA_total_size() {
    return this._FLA_total_size;
  }
  imm_getData() {
    return this._data;
  }
  imm_getPath() {
    return this._path;
  }
};
var YFirmwareUpdate = class _YFirmwareUpdate {
  // API symbols as static members
  //--- (end of generated code: YFirmwareUpdate attributes declaration)
  constructor(obj_yapi, str_serial, str_path, bin_settings, bool_force) {
    this._serial = "";
    this._settings = new Uint8Array(0);
    this._firmwarepath = "";
    this._progress_msg = "";
    this._progress_c = 0;
    this._progress = 0;
    this._restore_step = 0;
    this._force = false;
    this._yapi = obj_yapi;
    this._serial = str_serial;
    this._firmwarepath = str_path;
    this._settings = bin_settings;
    this._force = bool_force;
  }
  imm_progress(progress, msg) {
    this._progress = progress;
    this._progress_msg = msg;
  }
  async _processMore_internal(newupdate) {
    if (!newupdate)
      return YAPI_SUCCESS;
    let bytes;
    this.imm_progress(0, "Firmware update started");
    if (typeof this._firmwarepath == "string" && this._firmwarepath.indexOf("yoctopuce.com") >= 0) {
      this.imm_progress(1, "Downloading firmware");
      bytes = await this._yapi.system_env.downloadfile(this._firmwarepath, this._yapi);
    } else {
      this.imm_progress(1, "Loading firmware");
      bytes = await this._yapi.system_env.loadfile(this._firmwarepath);
    }
    let firmware = YFirmwareFile.imm_Parse(this._firmwarepath, bytes, this._force);
    if (!firmware) {
      return this._yapi._throw(YAPI_DEVICE_NOT_FOUND, "Device " + this._serial + " is not detected", YAPI_DEVICE_NOT_FOUND);
    }
    this.imm_progress(5, "Check if module is already in bootloader");
    let hub = null;
    let module = YModule.FindModuleInContext(this._yapi, this._serial + ".module");
    if (await module.isOnline()) {
      let dev = this._yapi.imm_getDevice(this._serial);
      let baseUrl = dev.imm_getRootUrl();
      let byPos = baseUrl.indexOf("/bySerial/");
      if (byPos >= 0) {
        baseUrl = baseUrl.slice(0, byPos + 1);
      } else if (baseUrl.slice(-1) != "/")
        baseUrl = baseUrl + "/";
      let urlInfo = new _YY_UrlInfo(baseUrl);
      hub = this._yapi.imm_getHub(urlInfo);
    } else {
      let hubs = this._yapi._connectedHubs;
      for (let i = 0; i < hubs.length; i++) {
        let ldrs = await hubs[i].getBootloaders();
        if (ldrs.indexOf(this._serial) >= 0) {
          hub = hubs[i];
          break;
        }
      }
    }
    if (hub == null) {
      this.imm_progress(-1, "Device " + this._serial + " is not detected");
      return this._yapi._throw(YAPI_DEVICE_NOT_FOUND, "Device " + this._serial + " is not detected", YAPI_DEVICE_NOT_FOUND);
    }
    try {
      await hub.firmwareUpdate(this._serial, firmware, this._settings, (percent, msg) => {
        this.imm_progress(5 + (percent * 80 + 50) / 100 >> 0, msg);
      });
    } catch (e) {
      this.imm_progress(-1, e.message);
      return this._yapi._throw(YAPI_IO_ERROR, e.message, YAPI_IO_ERROR);
    }
    if (module._cache.parentHub) {
      this.imm_progress(100, "Firmware update scheduled successfully");
    } else {
      this.imm_progress(80, "Wait for the device to restart");
      let timeout = this._yapi.GetTickCount() + 6e4;
      await module.clearCache();
      while (!await module.isOnline() && timeout > this._yapi.GetTickCount()) {
        await this._yapi.Sleep(500);
        await this._yapi.UpdateDeviceList();
      }
      if (await module.isOnline()) {
        if (this._settings != null) {
          this.imm_progress(95, "Restoring device settings");
          await module.set_allSettingsAndFiles(this._settings);
          await module.saveToFlash();
        }
        let real_fw = await module.get_firmwareRelease();
        if (real_fw == firmware.imm_getFirmwareRelease()) {
          this.imm_progress(100, "Success");
        } else {
          this.imm_progress(-1, "Unable to update firmware");
        }
      } else {
        this.imm_progress(-1, "Device did not reboot correctly");
      }
    }
    return YAPI_SUCCESS;
  }
  static async checkFirmware_r(file, serial_base, force) {
    if (file.substr(-4).toLowerCase() != ".byn")
      return null;
    let bynfile = await YAPI.system_env.loadfile(file);
    let firmware = YFirmwareFile.imm_Parse(file, bynfile, force);
    if (!firmware)
      return null;
    if (firmware.imm_getSerial().slice(0, serial_base.length) != serial_base)
      return null;
    return firmware;
  }
  /**
   * Test if the byn file is valid for this module. It is possible to pass a directory instead of a file.
   * In that case, this method returns the path of the most recent appropriate byn file. This method will
   * ignore any firmware older than minrelease.
   *
   * @param serial {string} : the serial number of the module to update
   * @param path {string} : the path of a byn file or a directory that contains byn files
   * @param minrelease {number} : a positive integer
   * @param force {boolean} : true to force an update even if the API is below expected revision
   *
   * @return {string} : the path of the byn file to use, or an empty string if no byn files matches the requirement
   *
   * On failure, returns a string that starts with "error:".
   */
  static async CheckFirmwareEx(serial, path, minrelease, force) {
    let link = "";
    let best_rev = 0;
    let current_rev;
    if (typeof path == "string" && path.indexOf("yoctopuce.com") >= 0) {
      try {
        let data = await YAPI.system_env.downloadfile("http://www.yoctopuce.com/FR/common/getLastFirmwareLink.php?serial=" + serial, YAPI);
        let obj = JSON.parse(YAPI.imm_bin2str(data));
        link = obj["link"];
        best_rev = obj["version"];
      } catch (e) {
        YAPI.imm_log("failed to retrieve firmware information from www.yoctopuce.com", e);
        YAPI._throw(YAPI_IO_ERROR, "failed to retrieve firmware information from www.yoctopuce.com", "");
        return "";
      }
    } else {
      let firmware = await _YFirmwareUpdate.checkFirmware_r(path, serial.substring(0, 8), force);
      if (firmware != null) {
        best_rev = firmware.imm_getFirmwareReleaseAsInt();
        link = firmware.imm_getPath();
      }
    }
    if (minrelease != 0) {
      if (minrelease < best_rev) {
        return link;
      } else {
        return "";
      }
    }
    return link;
  }
  static async CheckFirmware_internal(serial, path, minrelease) {
    return _YFirmwareUpdate.CheckFirmwareEx(serial, path, minrelease, false);
  }
  static async GetAllBootLoadersInContext_internal(yctx) {
    let hubs = yctx._connectedHubs;
    let res = [];
    for (let i = 0; i < hubs.length; i++) {
      let ldrs = await hubs[i].getBootloaders();
      for (let j = 0; j < ldrs.length; j++) {
        res.push(ldrs[j]);
      }
    }
    return res;
  }
  static async GetAllBootLoaders_internal() {
    return _YFirmwareUpdate.GetAllBootLoadersInContext(YAPI);
  }
  //--- (generated code: YFirmwareUpdate implementation)
  async _processMore(newupdate) {
    return await this._processMore_internal(newupdate);
  }
  /**
   * Returns a list of all the modules in "firmware update" mode.
   *
   * @return an array of strings containing the serial numbers of devices in "firmware update" mode.
   */
  static async GetAllBootLoaders() {
    return await this.GetAllBootLoaders_internal();
  }
  /**
   * Returns a list of all the modules in "firmware update" mode.
   *
   * @param yctx : a YAPI context.
   *
   * @return an array of strings containing the serial numbers of devices in "firmware update" mode.
   */
  static async GetAllBootLoadersInContext(yctx) {
    return await this.GetAllBootLoadersInContext_internal(yctx);
  }
  /**
   * Test if the byn file is valid for this module. It is possible to pass a directory instead of a file.
   * In that case, this method returns the path of the most recent appropriate byn file. This method will
   * ignore any firmware older than minrelease.
   *
   * @param serial : the serial number of the module to update
   * @param path : the path of a byn file or a directory that contains byn files
   * @param minrelease : a positive integer
   *
   * @return : the path of the byn file to use, or an empty string if no byn files matches the requirement
   *
   * On failure, returns a string that starts with "error:".
   */
  static async CheckFirmware(serial, path, minrelease) {
    return await this.CheckFirmware_internal(serial, path, minrelease);
  }
  /**
   * Returns the progress of the firmware update, on a scale from 0 to 100. When the object is
   * instantiated, the progress is zero. The value is updated during the firmware update process until
   * the value of 100 is reached. The 100 value means that the firmware update was completed
   * successfully. If an error occurs during the firmware update, a negative value is returned, and the
   * error message can be retrieved with get_progressMessage.
   *
   * @return an integer in the range 0 to 100 (percentage of completion)
   *         or a negative error code in case of failure.
   */
  async get_progress() {
    if (this._progress >= 0) {
      await this._processMore(0);
    }
    return this._progress;
  }
  /**
   * Returns the last progress message of the firmware update process. If an error occurs during the
   * firmware update process, the error message is returned
   *
   * @return a string  with the latest progress message, or the error message.
   */
  async get_progressMessage() {
    return this._progress_msg;
  }
  /**
   * Starts the firmware update process. This method starts the firmware update process in background. This method
   * returns immediately. You can monitor the progress of the firmware update with the get_progress()
   * and get_progressMessage() methods.
   *
   * @return an integer in the range 0 to 100 (percentage of completion),
   *         or a negative error code in case of failure.
   *
   * On failure returns a negative error code.
   */
  async startUpdate() {
    let err;
    let leng;
    err = this._yapi.imm_bin2str(this._settings);
    leng = err.length;
    if (leng >= 6 && "error:" == err.substr(0, 6)) {
      this._progress = -1;
      this._progress_msg = err.substr(6, leng - 6);
    } else {
      this._progress = 0;
      this._progress_c = 0;
      await this._processMore(1);
    }
    return this._progress;
  }
};
var YFunction = class _YFunction {
  //--- (end of generated code: YFunction attributes declaration)
  constructor(obj_yapi, str_func) {
    this._logicalName = _YFunction.LOGICALNAME_INVALID;
    this._advertisedValue = _YFunction.ADVERTISEDVALUE_INVALID;
    this._valueCallbackFunction = null;
    this._cacheExpiration = 0;
    this._serial = "";
    this._funId = "";
    this._hwId = "";
    this.LOGICALNAME_INVALID = YAPI_INVALID_STRING;
    this.ADVERTISEDVALUE_INVALID = YAPI_INVALID_STRING;
    this._yapi = obj_yapi;
    this._className = "Function";
    this._func = str_func;
    this._lastErrorType = YAPI_SUCCESS;
    this._lastErrorMsg = "no error";
    this._dataStreams = {};
    this._userData = null;
    this._cache = { _expiration: -1, functionid: "", hwid: "" };
    this._valueCallbackFunction = null;
  }
  _throw(int_errType, str_errMsg, obj_retVal) {
    this._lastErrorType = int_errType;
    this._lastErrorMsg = str_errMsg;
    return this._yapi._throw(int_errType, str_errMsg, obj_retVal);
  }
  async isReadOnly_internal() {
    try {
      let serial = await this.get_serialNumber();
      return this._yapi.isReadOnly(serial);
    } catch (e) {
      return true;
    }
  }
  //--- (generated code: YFunction implementation)
  imm_parseAttr(name, val) {
    switch (name) {
      case "_expiration":
        this._cacheExpiration = val;
        return 1;
      case "logicalName":
        this._logicalName = val;
        return 1;
      case "advertisedValue":
        this._advertisedValue = val;
        return 1;
    }
    return 0;
  }
  /**
   * Returns the logical name of the function.
   *
   * @return a string corresponding to the logical name of the function
   *
   * On failure, throws an exception or returns YFunction.LOGICALNAME_INVALID.
   */
  async get_logicalName() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YFunction.LOGICALNAME_INVALID;
      }
    }
    res = this._logicalName;
    return res;
  }
  /**
   * Changes the logical name of the function. You can use yCheckLogicalName()
   * prior to this call to make sure that your parameter is valid.
   * Remember to call the saveToFlash() method of the module if the
   * modification must be kept.
   *
   * @param newval : a string corresponding to the logical name of the function
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_logicalName(newval) {
    let rest_val;
    if (!await YAPI.CheckLogicalName(newval)) {
      return this._throw(YAPI.INVALID_ARGUMENT, "Invalid name :" + newval, YAPI.INVALID_ARGUMENT);
    }
    rest_val = String(newval);
    return await this._setAttr("logicalName", rest_val);
  }
  /**
   * Returns a short string representing the current state of the function.
   *
   * @return a string corresponding to a short string representing the current state of the function
   *
   * On failure, throws an exception or returns YFunction.ADVERTISEDVALUE_INVALID.
   */
  async get_advertisedValue() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YFunction.ADVERTISEDVALUE_INVALID;
      }
    }
    res = this._advertisedValue;
    return res;
  }
  async set_advertisedValue(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("advertisedValue", rest_val);
  }
  /**
   * Retrieves a function for a given identifier.
   * The identifier can be specified using several formats:
   *
   * - FunctionLogicalName
   * - ModuleSerialNumber.FunctionIdentifier
   * - ModuleSerialNumber.FunctionLogicalName
   * - ModuleLogicalName.FunctionIdentifier
   * - ModuleLogicalName.FunctionLogicalName
   *
   *
   * This function does not require that the function is online at the time
   * it is invoked. The returned object is nevertheless valid.
   * Use the method YFunction.isOnline() to test if the function is
   * indeed online at a given time. In case of ambiguity when looking for
   * a function by logical name, no error is notified: the first instance
   * found is returned. The search is performed first by hardware name,
   * then by logical name.
   *
   * If a call to this object's is_online() method returns FALSE although
   * you are certain that the matching device is plugged, make sure that you did
   * call registerHub() at application initialization time.
   *
   * @param func : a string that uniquely characterizes the function, for instance
   *         MyDevice..
   *
   * @return a YFunction object allowing you to drive the function.
   */
  static FindFunction(func) {
    let obj;
    obj = _YFunction._FindFromCache("Function", func);
    if (obj == null) {
      obj = new _YFunction(YAPI, func);
      _YFunction._AddToCache("Function", func, obj);
    }
    return obj;
  }
  /**
   * Retrieves a function for a given identifier in a YAPI context.
   * The identifier can be specified using several formats:
   *
   * - FunctionLogicalName
   * - ModuleSerialNumber.FunctionIdentifier
   * - ModuleSerialNumber.FunctionLogicalName
   * - ModuleLogicalName.FunctionIdentifier
   * - ModuleLogicalName.FunctionLogicalName
   *
   *
   * This function does not require that the function is online at the time
   * it is invoked. The returned object is nevertheless valid.
   * Use the method YFunction.isOnline() to test if the function is
   * indeed online at a given time. In case of ambiguity when looking for
   * a function by logical name, no error is notified: the first instance
   * found is returned. The search is performed first by hardware name,
   * then by logical name.
   *
   * @param yctx : a YAPI context
   * @param func : a string that uniquely characterizes the function, for instance
   *         MyDevice..
   *
   * @return a YFunction object allowing you to drive the function.
   */
  static FindFunctionInContext(yctx, func) {
    let obj;
    obj = _YFunction._FindFromCacheInContext(yctx, "Function", func);
    if (obj == null) {
      obj = new _YFunction(yctx, func);
      _YFunction._AddToCache("Function", func, obj);
    }
    return obj;
  }
  /**
   * Registers the callback function that is invoked on every change of advertised value.
   * The callback is called once when it is registered, passing the current advertised value
   * of the function, provided that it is not an empty string.
   * The callback is then invoked only during the execution of ySleep or yHandleEvents.
   * This provides control over the time when the callback is triggered. For good responsiveness, remember to call
   * one of these two functions periodically. To unregister a callback, pass a null pointer as argument.
   *
   * @param callback : the callback function to call, or a null pointer. The callback function should take two
   *         arguments: the function object of which the value has changed, and the character string describing
   *         the new advertised value.
   * @noreturn
   */
  async registerValueCallback(callback) {
    let val;
    if (callback != null) {
      await _YFunction._UpdateValueCallbackList(this, true);
    } else {
      await _YFunction._UpdateValueCallbackList(this, false);
    }
    this._valueCallbackFunction = callback;
    if (callback != null && await this.isOnline()) {
      val = this._advertisedValue;
      if (!(val == "")) {
        await this._invokeValueCallback(val);
      }
    }
    return 0;
  }
  async _invokeValueCallback(value) {
    if (this._valueCallbackFunction != null) {
      try {
        await this._valueCallbackFunction(this, value);
      } catch (e) {
        this._yapi.imm_log("Exception in valueCallback:", e);
      }
    } else {
    }
    return 0;
  }
  /**
   * Disables the propagation of every new advertised value to the parent hub.
   * You can use this function to save bandwidth and CPU on computers with limited
   * resources, or to prevent unwanted invocations of the HTTP callback.
   * Remember to call the saveToFlash() method of the module if the
   * modification must be kept.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async muteValueCallbacks() {
    return await this.set_advertisedValue("SILENT");
  }
  /**
   * Re-enables the propagation of every new advertised value to the parent hub.
   * This function reverts the effect of a previous call to muteValueCallbacks().
   * Remember to call the saveToFlash() method of the module if the
   * modification must be kept.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async unmuteValueCallbacks() {
    return await this.set_advertisedValue("");
  }
  /**
   * Returns the current value of a single function attribute, as a text string, as quickly as
   * possible but without using the cached value.
   *
   * @param attrName : the name of the requested attribute
   *
   * @return a string with the value of the the attribute
   *
   * On failure, throws an exception or returns an empty string.
   */
  async loadAttribute(attrName) {
    let url;
    let attrVal;
    url = "api/" + await this.get_functionId() + "/" + attrName;
    attrVal = await this._download(url);
    return this._yapi.imm_bin2str(attrVal);
  }
  /**
   * Indicates whether changes to the function are prohibited or allowed.
   * Returns true if the function is blocked by an admin password
   * or if the function is not available.
   *
   * @return true if the function is write-protected or not online.
   */
  async isReadOnly() {
    return await this.isReadOnly_internal();
  }
  /**
   * Returns the serial number of the module, as set by the factory.
   *
   * @return a string corresponding to the serial number of the module, as set by the factory.
   *
   * On failure, throws an exception or returns YFunction.SERIALNUMBER_INVALID.
   */
  async get_serialNumber() {
    let m;
    m = await this.get_module();
    return await m.get_serialNumber();
  }
  _parserHelper() {
    return 0;
  }
  _is_valid_pass(passwd) {
    let tmp;
    if (passwd.length > YAPI.HASH_BUF_SIZE) {
      tmp = "Password too long (max " + String(Math.round(YAPI.HASH_BUF_SIZE)) + " chars) :" + passwd;
      this._throw(YAPI_INVALID_ARGUMENT, tmp);
      return false;
    }
    if (passwd.indexOf("@") >= 0) {
      this._throw(YAPI_INVALID_ARGUMENT, "Character @ is not allowed in password");
      return false;
    }
    if (passwd.indexOf("/") >= 0) {
      this._throw(YAPI_INVALID_ARGUMENT, "Character / is not allowed in password");
      return false;
    }
    return true;
  }
  /**
   * Returns the next Function
   *
   * @returns {YFunction}
   */
  nextFunction() {
    let resolve = this._yapi.imm_resolveFunction(this._className, this._func);
    if (resolve.errorType != YAPI.SUCCESS)
      return null;
    let next_hwid = this._yapi.imm_getNextHardwareId(this._className, resolve.result);
    if (next_hwid == null)
      return null;
    return _YFunction.FindFunctionInContext(this._yapi, next_hwid);
  }
  /**
   * Retrieves the first Function in a YAPI context
   *
   * @returns {YFunction}
   */
  static FirstFunction() {
    let next_hwid = YAPI.imm_getFirstHardwareId("Function");
    if (next_hwid == null)
      return null;
    return _YFunction.FindFunction(next_hwid);
  }
  /**
   * Retrieves the first Function in a given context
   *
   * @param yctx {YAPIContext}
   *
   * @returns {YFunction}
   */
  static FirstFunctionInContext(yctx) {
    let next_hwid = yctx.imm_getFirstHardwareId("Function");
    if (next_hwid == null)
      return null;
    return _YFunction.FindFunctionInContext(yctx, next_hwid);
  }
  //--- (end of generated code: YFunction implementation)
  /** Retrieve a function instance from cache
   */
  static _FindFromCacheInContext(yctx, className, func) {
    return yctx.imm_getFunction(className, func);
  }
  /** Retrieve a function instance from cache
   */
  static _FindFromCache(className, func) {
    return YAPI.imm_getFunction(className, func);
  }
  /** Add a function instance to cache
   */
  static _AddToCache(className, func, obj) {
    obj._yapi.imm_setFunction(className, func, obj);
  }
  /** Clear the function instance cache
   */
  static _ClearCache(obj_yapi = null) {
    if (!obj_yapi)
      obj_yapi = YAPI;
    obj_yapi.imm_ResetToDefaults();
  }
  /** Add or remove a value change callback
   */
  static async _UpdateValueCallbackList(obj_func, bool_add) {
    await obj_func._yapi._UpdateValueCallbackList(obj_func, bool_add);
  }
  /** Add or remove a timed report callback
   */
  static async _UpdateTimedReportCallbackList(obj_func, bool_add) {
    await obj_func._yapi._UpdateTimedReportCallbackList(obj_func, bool_add);
  }
  /**
   * Returns a short text that describes unambiguously the instance of the function in the form
   * TYPE(NAME)=SERIAL&#46;FUNCTIONID.
   * More precisely,
   * TYPE       is the type of the function,
   * NAME       it the name used for the first access to the function,
   * SERIAL     is the serial number of the module if the module is connected or "unresolved", and
   * FUNCTIONID is  the hardware identifier of the function if the module is connected.
   * For example, this method returns Relay(MyCustomName.relay1)=RELAYLO1-123456.relay1 if the
   * module is already connected or Relay(BadCustomeName.relay1)=unresolved if the module has
   * not yet been connected. This method does not trigger any USB or TCP transaction and can therefore be used in
   * a debugger.
   *
   * @return a string that describes the function
   *         (ex: Relay(MyCustomName.relay1)=RELAYLO1-123456.relay1)
   */
  async describe() {
    if (this._hwId != "") {
      return this._className + "(" + this._func + ")=" + this._hwId;
    }
    let resolve = this._yapi.imm_resolveFunction(this._className, this._func);
    if (resolve.errorType != YAPI_SUCCESS && resolve.result != this._func) {
      return this._className + "(" + this._func + ")=unresolved";
    }
    return this._className + "(" + this._func + ")=" + resolve.result;
  }
  /**
   * Returns the unique hardware identifier of the function in the form SERIAL.FUNCTIONID.
   * The unique hardware identifier is composed of the device serial
   * number and of the hardware identifier of the function (for example RELAYLO1-123456.relay1).
   *
   * @return a string that uniquely identifies the function (ex: RELAYLO1-123456.relay1)
   *
   * On failure, throws an exception or returns  YFunction.HARDWAREID_INVALID.
   */
  async get_hardwareId() {
    if (this._hwId != "") {
      return this._hwId;
    }
    let resolve = this._yapi.imm_resolveFunction(this._className, this._func);
    if (resolve.errorType != YAPI_SUCCESS) {
      await this.isOnline();
      resolve = this._yapi.imm_resolveFunction(this._className, this._func);
      if (resolve.errorType != YAPI_SUCCESS) {
        return this._throw(resolve.errorType, resolve.errorMsg, _YFunction.HARDWAREID_INVALID);
      }
    }
    return resolve.result;
  }
  /**
   * Returns the hardware identifier of the function, without reference to the module. For example
   * relay1
   *
   * @return a string that identifies the function (ex: relay1)
   *
   * On failure, throws an exception or returns  YFunction.FUNCTIONID_INVALID.
   */
  async get_functionId() {
    if (this._funId != "") {
      return this._funId;
    }
    let resolve = this._yapi.imm_resolveFunction(this._className, this._func);
    if (resolve.errorType != YAPI_SUCCESS) {
      await this.isOnline();
      resolve = this._yapi.imm_resolveFunction(this._className, this._func);
      if (resolve.errorType != YAPI_SUCCESS) {
        return this._throw(resolve.errorType, resolve.errorMsg, _YFunction.FUNCTIONID_INVALID);
      }
    }
    let hardwareId = resolve.result;
    let pos = hardwareId.indexOf(".");
    return hardwareId.substr(pos + 1);
  }
  imm_get_functionId() {
    if (this._funId != "") {
      return this._funId;
    }
    let resolve = this._yapi.imm_resolveFunction(this._className, this._func);
    if (resolve.errorType != YAPI_SUCCESS) {
      return this._throw(resolve.errorType, resolve.errorMsg, _YFunction.FUNCTIONID_INVALID);
    }
    let hardwareId = resolve.result;
    let pos = hardwareId.indexOf(".");
    return hardwareId.substr(pos + 1);
  }
  /**
   * Returns a global identifier of the function in the format MODULE_NAME&#46;FUNCTION_NAME.
   * The returned string uses the logical names of the module and of the function if they are defined,
   * otherwise the serial number of the module and the hardware identifier of the function
   * (for example: MyCustomName.relay1)
   *
   * @return a string that uniquely identifies the function using logical names
   *         (ex: MyCustomName.relay1)
   *
   * On failure, throws an exception or returns  YFunction.FRIENDLYNAME_INVALID.
   */
  async get_friendlyName() {
    let resolve = this._yapi.imm_getFriendlyNameFunction(this._className, this._func);
    if (resolve.errorType != YAPI_SUCCESS) {
      await this.isOnline();
      resolve = this._yapi.imm_getFriendlyNameFunction(this._className, this._func);
      if (resolve.errorType != YAPI_SUCCESS) {
        return this._throw(resolve.errorType, resolve.errorMsg, _YFunction.FRIENDLYNAME_INVALID);
      }
    }
    return resolve.result;
  }
  /** Store and parse an API request for current function
   */
  async _parse(yreq, msValidity) {
    if (!yreq.obj_result)
      return;
    yreq.obj_result._expiration = this._yapi.GetTickCount() + msValidity;
    this._cache = yreq.obj_result;
    this._serial = yreq.obj_result.deviceid;
    this._funId = yreq.obj_result.functionid;
    this._hwId = yreq.obj_result.hwid;
    for (let key in yreq.obj_result) {
      this.imm_parseAttr(key, yreq.obj_result[key]);
    }
    await this._parserHelper();
  }
  /**
       ** Helpers for built-in classes
       **
  
       // Helper for initializing standard attributes (used in particular by built-in classes)
       async _i(): Promise<void>
       {
       let arr_attrNames: string[] = this.constructor._attrList;
       this._className = this.constructor.name.slice(1);
       for(let i = 0; i < arr_attrNames.length; i++) {
       this['_'+arr_attrNames[i]] = this.constructor[arr_attrNames[i].toUpperCase()+'_INVALID'];
       }
       }
  
       // Helper for simple accessors (used in particular by built-in classes)
       async _g(str_attr): Promise<object>
       {
       if (this._cacheExpiration <= this._yapi.GetTickCount()) {
       if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
       return this.constructor[str_attr.toLocaleUpperCase()+'_INVALID'];
       }
       }
       return this['_'+str_attr];
       }
  
       // Helper for simple accessors (used in particular by built-in classes)
       async _s(str_attr, obj_val): Promise<number>
       {
       return this._setAttr(str_attr, String(obj_val));
       }
  
       // Helper for completing and exporting the class; used by built-in classes
       static _E(arr_attrlist)
       {
       let className = this.name.slice(1);
       this._attrList = arr_attrlist;
       for(let i = 0; i < arr_attrlist.length; i++) {
       let attrname = arr_attrlist[i];
       let getMethod = 'get_'+attrname;
       this.prototype[getMethod] = async function(): Promise<object> { return this._g(attrname); };
       }
       this['Find'+className] = function(func) {
       let str_classname = this.name.slice(1);
       let obj: YFunction;
       obj = YFunction._FindFromCache(str_classname, func);
       if (obj == null) {
       obj = new this(YAPI, func);
       YFunction._AddToCache(str_classname, func, obj);
       }
       return obj;
       };
       this['First'+className] = function() {
       let str_classname = this.name.slice(1);
       let next_hwid = YAPI.imm_getFirstHardwareId(str_classname);
       if(next_hwid == null) return null;
       return this['Find'+className](next_hwid);
       };
       this.prototype['next'+className] = function() {
       let resolve = this._yapi.imm_resolveFunction(this._className, this._func);
       if(resolve.errorType != YAPI.SUCCESS) return null;
       let next_hwid = this._yapi.imm_getNextHardwareId(this._className, resolve.result);
       if(next_hwid == null) return null;
       return this.constructor['Find'+className](next_hwid);
       };
       this.imm_Init();
       }
  
       ********/
  // Backward-compatibility helper
  isOnline_async(func, ctx) {
    this.isOnline().then((res) => {
      func(ctx, this, res);
    }).catch((e) => {
      func(ctx, this, false);
    });
  }
  // Backward-compatibility helper
  load_async(ms_validiy, func, ctx) {
    this.load(ms_validiy).then((res) => {
      func(ctx, this, YAPI_SUCCESS);
    }).catch((e) => {
      func(ctx, this, this.get_errorType());
    });
  }
  /** Return the value of an attribute from function cache, after reloading it from device if needed
   * Note: the function cache is a typed (parsed) cache, contrarily to the agnostic device cache
   */
  async _getAttr(str_attr) {
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS)
        return null;
    }
    if (typeof this._cache[str_attr] == "undefined") {
      this._throw(YAPI_VERSION_MISMATCH, "No such attribute " + str_attr + " in function", null);
    }
    return this._cache[str_attr];
  }
  /** Return the value of an attribute from function cache, after reloading it from device if needed
   * Note: the function cache is a typed (parsed) cache, contrarily to the agnostic device cache
   */
  async _getFixedAttr(str_attr) {
    if (this._cacheExpiration == 0) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS)
        return null;
    }
    if (typeof this._cache[str_attr] == "undefined") {
      this._throw(YAPI_VERSION_MISMATCH, "No such attribute " + str_attr + " in function", null);
    }
    return this._cache[str_attr];
  }
  /** Escape a string for posting it as an URL
   */
  imm_escapeAttr(str_newval) {
    return escape(str_newval).replace(/[+]/g, "%2B").replace(/%20/g, "+").replace(/%21/g, "!").replace(/%24/g, "$").replace(/%27/g, "'").replace(/%28/g, "(").replace(/%29/g, ")").replace(/%2[cC]/g, ",").replace(/%2[fF]/g, "/").replace(/%3[aA]/g, ":").replace(/%3[bB]/g, ";").replace(/%3[fF]/g, "?").replace(/%5[bB]/g, "[").replace(/%5[dD]/g, "]");
  }
  /** Change the value of an attribute on a device, and invalidate the cache
   */
  async _setAttr(str_attr, str_newval) {
    if (str_newval == void 0) {
      return this._throw(YAPI_INVALID_ARGUMENT, "Undefined value to set for attribute " + str_attr, null);
    }
    let attrname = encodeURIComponent(str_attr);
    let attrval = this.imm_escapeAttr(str_newval);
    let extra = "/" + attrname + "?" + attrname + "=" + attrval + "&.";
    if (this._cacheExpiration != 0) {
      this._cacheExpiration = this._yapi.GetTickCount();
      this._cache._expiration = this._cacheExpiration;
    }
    let yreq = await this._yapi.funcRequest(this._className, this._func, extra);
    if (yreq.errorType != YAPI_SUCCESS) {
      return this._throw(yreq.errorType, yreq.errorMsg, yreq.errorType);
    }
    return YAPI_SUCCESS;
  }
  /** Execute an arbitrary HTTP GET request on the device and return the binary content
   */
  async _download(str_path) {
    let devid = this._serial;
    if (devid == "") {
      devid = await (await this.module()).get_serialNumber();
    }
    if (devid == YAPI_INVALID_STRING) {
      return new Uint8Array(0);
    }
    let yreq = await this._yapi.devRequest(devid, "GET /" + str_path, null, 0);
    if (yreq.errorType != YAPI_SUCCESS) {
      return this._throw(yreq.errorType, yreq.errorMsg, "");
    }
    return yreq.bin_result;
  }
  /** Execute an out-of-band HTTP GET request on the device and return the binary content.
   * The request may execute in parallel to regular requests currently in progress.
   */
  async _downloadOutOfBand(str_path) {
    let devid = this._serial;
    if (devid == "") {
      devid = await (await this.module()).get_serialNumber();
    }
    if (devid == YAPI_INVALID_STRING) {
      return new Uint8Array(0);
    }
    let yreq = await this._yapi.devRequest(devid, "GET /" + str_path, null, 1);
    if (yreq.errorType != YAPI_SUCCESS) {
      return this._throw(yreq.errorType, yreq.errorMsg, "");
    }
    return yreq.bin_result;
  }
  /** Upload a file to the filesystem, to the specified full path name.
   * If a file already exists with the same path name, its content is overwritten.
   * The progress callback function is called with two parameters: the number of
   * bytes uploaded so far and the total size to be uploaded.
   */
  async _uploadWithProgress(str_path, bin_content, fun_progressCb) {
    let devid = this._serial;
    if (devid == "") {
      devid = await (await this.module()).get_serialNumber();
    }
    if (devid == YAPI_INVALID_STRING) {
      let res = new YHTTPRequest(null);
      res.errorType = this.get_errorType();
      res.errorMsg = this.get_errorMessage();
      return res;
    }
    let httpreq = "POST /upload.html";
    let len = bin_content.length;
    if (typeof bin_content == "string" || bin_content instanceof String) {
      bin_content = this._yapi.imm_str2bin(bin_content);
    } else if (bin_content instanceof Array) {
      bin_content = new Uint8Array(bin_content);
    }
    return this._yapi.devRequest(devid, httpreq, new YHTTPBody(str_path, bin_content, fun_progressCb), 0);
  }
  /** Upload a file to the filesystem, to the specified full path name.
   * If a file already exists with the same path name, its content is overwritten.
   * The progress callback function is called with two parameters: the number of
   * bytes uploaded so far and the total size to be uploaded.
   */
  async _uploadEx(str_path, bin_content) {
    let yreq = await this._uploadWithProgress(str_path, bin_content, null);
    if (yreq.errorType != YAPI_SUCCESS) {
      return this._throw(yreq.errorType, yreq.errorMsg, null);
    }
    return yreq.bin_result;
  }
  /** Upload a file to the filesystem, to the specified full path name.
   * If a file already exists with the same path name, its content is overwritten.
   */
  async _upload(str_path, bin_content) {
    let yreq = await this._uploadWithProgress(str_path, bin_content, null);
    if (yreq.errorType != YAPI_SUCCESS) {
      return this._throw(yreq.errorType, yreq.errorMsg, null);
    }
    return yreq.errorType;
  }
  /**
   * Waits for all pending asynchronous commands on the module to complete, and invoke
   * the user-provided callback function. The callback function can therefore freely
   * issue synchronous or asynchronous commands, without risking to block the
   * JavaScript VM.
   *
   * @param callback : callback function that is invoked when all pending commands on
   *         the module are completed.
   *         The callback function receives two arguments: the caller-specific
   *         context object and the receiving function object.
   * @param context : caller-specific object that is passed as-is to the callback function
   *
   * @return nothing.
   */
  wait_async(callback, context) {
    let devid = this._serial;
    if (devid == "") {
      this.module().then((module) => module.get_serialNumber().then(() => this.wait_async(callback, context)));
      return YAPI_SUCCESS;
    }
    if (devid == YAPI_INVALID_STRING) {
      callback(context, this);
      return YAPI_SUCCESS;
    }
    let lockdev = this._yapi.imm_getDevice(devid);
    let delayedCode = (() => {
      callback(context, this);
    });
    lockdev._pendingQueries = lockdev._pendingQueries.then(delayedCode, delayedCode);
    return YAPI_SUCCESS;
  }
  /** Get a value from a JSON buffer
   **/
  imm_json_get_key(bin_jsonbuff, str_key) {
    let loadval = JSON.parse(this._yapi.imm_bin2str(bin_jsonbuff));
    if (typeof loadval[str_key] != "undefined") {
      return loadval[str_key];
    }
    return "";
  }
  /** Get a string from a JSON buffer
   **/
  imm_json_get_string(bin_jsonbuff) {
    return JSON.parse(this._yapi.imm_bin2str(bin_jsonbuff));
  }
  /** Get an array of strings from a JSON buffer
   **/
  imm_json_get_array(bin_jsonbuff) {
    let loadval = JSON.parse(this._yapi.imm_bin2str(bin_jsonbuff));
    let res = [];
    for (let idx in loadval) {
      res.push(this._yapi.imm_str2bin(JSON.stringify(loadval[idx])));
    }
    return res;
  }
  /** Get an array of strings from a JSON buffer
   **/
  imm_get_json_path(bin_json, str_path) {
    let json = JSON.parse(this._yapi.imm_bin2str(bin_json));
    let paths = str_path.split("|");
    for (let i = 0; i < paths.length; i++) {
      let tmp = paths[i];
      json = json[tmp];
      if (json == void 0) {
        return new Uint8Array();
      }
    }
    return this._yapi.imm_str2bin(JSON.stringify(json));
  }
  /** Get a string from a JSON string
   **/
  imm_decode_json_string(bin_json) {
    if (bin_json.length == 0) {
      return "";
    }
    return JSON.parse(this._yapi.imm_bin2str(bin_json));
  }
  /** Get a integer from a JSON string
   **/
  imm_decode_json_int(bin_json) {
    if (bin_json.length == 0) {
      return 0;
    }
    return JSON.parse(this._yapi.imm_bin2str(bin_json));
  }
  // Method used to cache DataStream objects (new DataLogger)
  //
  /** Method used to cache DataStream objects (new DataLogger)
   **/
  imm_findDataStream(obj_dataset, str_def) {
    let key = obj_dataset.imm_get_functionId() + ":" + str_def;
    if (this._dataStreams[key]) {
      return this._dataStreams[key];
    }
    let words = this._yapi.imm_decodeWords(str_def);
    if (words.length < 14) {
      this._throw(YAPI.VERSION_MISMATCH, "device firwmare is too old", null);
      return null;
    }
    let newDataStream = new YDataStream(this, obj_dataset, words);
    this._dataStreams[key] = newDataStream;
    return newDataStream;
  }
  // Method used to clear cache of DataStream object (undocumented)
  async clearDataStreamCache() {
    this._dataStreams = {};
  }
  /**
   * Checks if the function is currently reachable, without raising any error.
   * If there is a cached value for the function in cache, that has not yet
   * expired, the device is considered reachable.
   * No exception is raised if there is an error while trying to contact the
   * device hosting the function.
   *
   * @return true if the function can be reached, and false otherwise
   */
  async isOnline() {
    if (this._cacheExpiration > this._yapi.GetTickCount())
      return true;
    let yreq = await this._yapi.funcRequest(this._className, this._func, "", this._yapi.defaultCacheValidity);
    if (yreq.errorType != YAPI_SUCCESS) {
      return yreq.errorType == YAPI_DEVICE_BUSY;
    }
    await this._parse(yreq, this._yapi.defaultCacheValidity);
    return true;
  }
  /**
   * Returns the numerical error code of the latest error with the function.
   * This method is mostly useful when using the Yoctopuce library with
   * exceptions disabled.
   *
   * @return a number corresponding to the code of the latest error that occurred while
   *         using the function object
   */
  get_errorType() {
    return this._lastErrorType;
  }
  /**
   * Returns the error message of the latest error with the function.
   * This method is mostly useful when using the Yoctopuce library with
   * exceptions disabled.
   *
   * @return a string corresponding to the latest error message that occured while
   *         using the function object
   */
  get_errorMessage() {
    return this._lastErrorMsg;
  }
  /**
   * Preloads the function cache with a specified validity duration.
   * By default, whenever accessing a device, all function attributes
   * are kept in cache for the standard duration (5 ms). This method can be
   * used to temporarily mark the cache as valid for a longer period, in order
   * to reduce network traffic for instance.
   *
   * @param msValidity : an integer corresponding to the validity attributed to the
   *         loaded function parameters, in milliseconds
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async load(msValidity) {
    let yreq = await this._yapi.funcRequest(this._className, this._func, "", msValidity);
    if (yreq.errorType != YAPI_SUCCESS) {
      return this._throw(yreq.errorType, yreq.errorMsg, yreq.errorType);
    }
    await this._parse(yreq, msValidity);
    return YAPI_SUCCESS;
  }
  /**
   * Invalidates the cache. Invalidates the cache of the function attributes. Forces the
   * next call to get_xxx() or loadxxx() to use values that come from the device.
   *
   * @noreturn
   */
  async clearCache() {
    let devreq = await this._yapi._funcDev(this._className, this._func);
    if (devreq.errorType != YAPI_SUCCESS) {
      return;
    }
    devreq.obj_result.device.imm_dropCache();
    if (this._cacheExpiration > 0) {
      this._cacheExpiration = this._yapi.GetTickCount();
    }
  }
  /**
   * Gets the YModule object for the device on which the function is located.
   * If the function cannot be located on any module, the returned instance of
   * YModule is not shown as on-line.
   *
   * @return {YModule} an instance of YModule
   */
  async module() {
    if (this._serial != "") {
      return YModule.FindModuleInContext(this._yapi, this._serial + ".module");
    }
    let hwid = this._func;
    let resolve;
    if (hwid.indexOf(".") < 0) {
      resolve = this._yapi.imm_resolveFunction(this._className, this._func);
      if (resolve.errorType == YAPI_SUCCESS)
        hwid = resolve.result;
    }
    let dotidx = hwid.indexOf(".");
    if (dotidx >= 0) {
      return YModule.FindModuleInContext(this._yapi, hwid.substr(0, dotidx) + ".module");
    }
    if (await this.load(this._yapi.defaultCacheValidity) == YAPI_SUCCESS) {
      resolve = this._yapi.imm_resolveFunction(this._className, this._func);
      if (resolve.result)
        hwid = resolve.result;
    }
    dotidx = hwid.indexOf(".");
    if (dotidx >= 0) {
      return YModule.FindModuleInContext(this._yapi, hwid.substr(0, dotidx) + ".module");
    }
    return YModule.FindModuleInContext(this._yapi, "module_of_" + this._className + "_" + this._func);
  }
  /**
   * Gets the YModule object for the device on which the function is located.
   * If the function cannot be located on any module, the returned instance of
   * YModule is not shown as on-line.
   *
   * @return an instance of YModule
   */
  async get_module() {
    return this.module();
  }
  /**
   * Returns a unique identifier of type YFUN_DESCR corresponding to the function.
   * This identifier can be used to test if two instances of YFunction reference the same
   * physical function on the same physical device.
   *
   * @return an identifier of type YFUN_DESCR.
   *
   * If the function has never been contacted, the returned value is Y$CLASSNAME$.FUNCTIONDESCRIPTOR_INVALID.
   */
  async get_functionDescriptor() {
    if (this._hwId != "") {
      return this._hwId;
    }
    let hwid = this._func;
    if (hwid.indexOf(".") < 0) {
      let resolve = this._yapi.imm_resolveFunction(this._className, this._func);
      if (resolve.errorType != YAPI_SUCCESS)
        hwid = resolve.result;
    }
    let dotidx = hwid.indexOf(".");
    if (dotidx >= 0) {
      return hwid;
    }
    return Y_FUNCTIONDESCRIPTOR_INVALID;
  }
  /**
   * Returns the value of the userData attribute, as previously stored using method
   * set_userData.
   * This attribute is never touched directly by the API, and is at disposal of the caller to
   * store a context.
   *
   * @return the object stored previously by the caller.
   */
  async get_userData() {
    return this._userData;
  }
  /**
   * Stores a user context provided as argument in the userData attribute of the function.
   * This attribute is never touched by the API, and is at disposal of the caller to store a context.
   *
   * @param data : any kind of object to be stored
   * @noreturn
   */
  async set_userData(data) {
    this._userData = data;
  }
};
YFunction.LOGICALNAME_INVALID = YAPI_INVALID_STRING;
YFunction.ADVERTISEDVALUE_INVALID = YAPI_INVALID_STRING;
(function(YFunction2) {
  YFunction2.FUNCTIONDESCRIPTOR_INVALID = YAPI_INVALID_STRING;
  YFunction2.HARDWAREID_INVALID = YAPI_INVALID_STRING;
  YFunction2.FUNCTIONID_INVALID = YAPI_INVALID_STRING;
  YFunction2.FRIENDLYNAME_INVALID = YAPI_INVALID_STRING;
})(YFunction || (YFunction = {}));
var YModule = class _YModule extends YFunction {
  //--- (end of generated code: YModule attributes declaration)
  constructor(yapi, func) {
    super(yapi, func);
    this._productName = _YModule.PRODUCTNAME_INVALID;
    this._serialNumber = _YModule.SERIALNUMBER_INVALID;
    this._productId = _YModule.PRODUCTID_INVALID;
    this._productRelease = _YModule.PRODUCTRELEASE_INVALID;
    this._firmwareRelease = _YModule.FIRMWARERELEASE_INVALID;
    this._persistentSettings = _YModule.PERSISTENTSETTINGS_INVALID;
    this._luminosity = _YModule.LUMINOSITY_INVALID;
    this._beacon = _YModule.BEACON_INVALID;
    this._upTime = _YModule.UPTIME_INVALID;
    this._usbCurrent = _YModule.USBCURRENT_INVALID;
    this._rebootCountdown = _YModule.REBOOTCOUNTDOWN_INVALID;
    this._userVar = _YModule.USERVAR_INVALID;
    this._valueCallbackModule = null;
    this._logCallback = null;
    this._confChangeCallback = null;
    this._beaconCallback = null;
    this.PRODUCTNAME_INVALID = YAPI_INVALID_STRING;
    this.SERIALNUMBER_INVALID = YAPI_INVALID_STRING;
    this.PRODUCTID_INVALID = YAPI_INVALID_UINT;
    this.PRODUCTRELEASE_INVALID = YAPI_INVALID_UINT;
    this.FIRMWARERELEASE_INVALID = YAPI_INVALID_STRING;
    this.PERSISTENTSETTINGS_LOADED = 0;
    this.PERSISTENTSETTINGS_SAVED = 1;
    this.PERSISTENTSETTINGS_MODIFIED = 2;
    this.PERSISTENTSETTINGS_INVALID = -1;
    this.LUMINOSITY_INVALID = YAPI_INVALID_UINT;
    this.BEACON_OFF = 0;
    this.BEACON_ON = 1;
    this.BEACON_INVALID = -1;
    this.UPTIME_INVALID = YAPI_INVALID_LONG;
    this.USBCURRENT_INVALID = YAPI_INVALID_UINT;
    this.REBOOTCOUNTDOWN_INVALID = YAPI_INVALID_INT;
    this.USERVAR_INVALID = YAPI_INVALID_INT;
    this._className = "Module";
    let devid = this._func;
    let dotidx = devid.indexOf(".");
    if (dotidx > 0)
      devid = devid.substr(0, dotidx);
    let dev = this._yapi.imm_getDevice(devid);
    if (dev) {
      this._serial = dev.imm_getSerialNumber();
      this._funId = "module";
      this._hwId = this._serial + ".module";
    }
  }
  _throw(int_errType, str_errMsg, obj_retVal) {
    this._lastErrorType = int_errType;
    this._lastErrorMsg = str_errMsg;
    return this._yapi._throw(int_errType, str_errMsg, obj_retVal);
  }
  static async _updateModuleCallbackList(YModule_module, bool_add) {
  }
  /** Return the internal device object hosting the function
   *
   * @return {YDevice}
   *
   * Raise an error if not found
   */
  imm_getDev() {
    let devid = this._func;
    let dotidx = devid.indexOf(".");
    if (dotidx > 0)
      devid = devid.substr(0, dotidx);
    let dev = this._yapi.imm_getDevice(devid);
    if (!dev) {
      this._throw(YAPI_DEVICE_NOT_FOUND, "Device [" + devid + "] is not online", null);
    }
    return dev;
  }
  /**
   * Forces a full redetection of the device, in case the functions changed
   *
   * @noreturn
   */
  async forceDeviceRefresh() {
    let dev = this.imm_getDev();
    if (!dev || !this._serial)
      return;
    await this._yapi.ForceDeviceRefresh(this._serial);
    if (this._cacheExpiration > 0) {
      this._cacheExpiration = this._yapi.GetTickCount();
    }
  }
  /**
   * Returns the number of functions (beside the "module" interface) available on the module.
   *
   * @return the number of functions on the module
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async functionCount() {
    let dev = this.imm_getDev();
    if (!dev)
      return YAPI_DEVICE_NOT_FOUND;
    return dev.imm_functionCount();
  }
  /**
   * Retrieves the hardware identifier of the <i>n</i>th function on the module.
   *
   * @param functionIndex : the index of the function for which the information is desired, starting at
   * 0 for the first function.
   *
   * @return a string corresponding to the unambiguous hardware identifier of the requested module function
   *
   * On failure, throws an exception or returns an empty string.
   */
  async functionId(functionIndex) {
    let dev = this.imm_getDev();
    if (!dev)
      return "";
    return dev.imm_functionId(functionIndex);
  }
  /**
   * Retrieves the type of the <i>n</i>th function on the module. Yoctopuce functions type names match
   * their class names without the <i>Y</i> prefix, for instance <i>Relay</i>, <i>Temperature</i> etc..
   *
   * @param functionIndex : the index of the function for which the information is desired, starting at
   * 0 for the first function.
   *
   * @return a string corresponding to the type of the function.
   *
   * On failure, throws an exception or returns an empty string.
   */
  async functionType(functionIndex) {
    let dev = this.imm_getDev();
    if (!dev)
      return "";
    return dev.imm_functionType(functionIndex);
  }
  /**
   * Retrieves the base type of the <i>n</i>th function on the module.
   * For instance, the base type of all measuring functions is "Sensor".
   *
   * @param functionIndex : the index of the function for which the information is desired, starting at
   * 0 for the first function.
   *
   * @return a string corresponding to the base type of the function
   *
   * On failure, throws an exception or returns an empty string.
   */
  async functionBaseType(functionIndex) {
    let dev = this.imm_getDev();
    if (!dev)
      return "";
    return dev.imm_functionBaseType(functionIndex);
  }
  /**
   * Retrieves the logical name of the <i>n</i>th function on the module.
   *
   * @param functionIndex : the index of the function for which the information is desired, starting at
   * 0 for the first function.
   *
   * @return a string corresponding to the logical name of the requested module function
   *
   * On failure, throws an exception or returns an empty string.
   */
  async functionName(functionIndex) {
    let dev = this.imm_getDev();
    if (!dev)
      return "";
    return dev.imm_functionName(functionIndex);
  }
  /**
   * Retrieves the advertised value of the <i>n</i>th function on the module.
   *
   * @param functionIndex : the index of the function for which the information is desired, starting at
   * 0 for the first function.
   *
   * @return a short string (up to 6 characters) corresponding to the advertised value of the requested
   * module function
   *
   * On failure, throws an exception or returns an empty string.
   */
  async functionValue(functionIndex) {
    let dev = this.imm_getDev();
    if (!dev)
      return "";
    return dev.imm_functionValue(functionIndex);
  }
  /**
   * Returns the logical name of the module.
   *
   * @return a string corresponding to the logical name of the module
   *
   * On failure, throws an exception or returns YModule.LOGICALNAME_INVALID.
   */
  async get_logicalName() {
    let dev = this.imm_getDev();
    if (dev != null && this._cache._expiration <= this._yapi.GetTickCount()) {
      return dev.getLogicalName();
    }
    let json_val = await this._getAttr("logicalName");
    return json_val == null ? _YModule.LOGICALNAME_INVALID : json_val;
  }
  async set_logicalName(newval) {
    let res = await super.set_logicalName(newval);
    let dev = this.imm_getDev();
    if (dev != null) {
      dev.imm_dropCache();
    }
    return res;
  }
  imm_flattenJsonStruct_internal(jsoncomplex) {
    let decoded = JSON.parse(this._yapi.imm_bin2str(jsoncomplex));
    let attrs = [];
    for (let function_name in decoded) {
      if (function_name == "services") {
        continue;
      }
      let function_attrs = decoded[function_name];
      for (let attr_name in function_attrs) {
        let attr_value = function_attrs[attr_name];
        if (attr_value === null || typeof attr_value === "object") {
          continue;
        }
        let flat = function_name + "/" + attr_name + "=" + attr_value;
        attrs.push(flat);
      }
    }
    return this._yapi.imm_str2bin(JSON.stringify(attrs));
  }
  async get_subDevices_internal() {
    let baseUrl = await this.get_url_internal();
    if (!baseUrl) {
      return [];
    }
    let hub = null;
    let hubUrl = "";
    for (let i = 0; i < this._yapi._connectedHubs.length; i++) {
      hubUrl = this._yapi._connectedHubs[i].imm_getRootUrl();
      if (baseUrl.slice(0, hubUrl.length) == hubUrl) {
        hub = this._yapi._connectedHubs[i];
        break;
      }
    }
    if (!hub || !hubUrl) {
      return [];
    }
    let hubSerial = hub.serialByYdx[0];
    if (hubSerial != this._serial) {
      return [];
    }
    let res = [];
    for (let serial in this._yapi._devs) {
      let rooturl = this._yapi._devs[serial].imm_getRootUrl();
      if (rooturl.substr(0, hubUrl.length) == hubUrl && serial != hubSerial) {
        res.push(serial);
      }
    }
    return res;
  }
  async get_parentHub_internal() {
    let baseUrl = await this.get_url_internal();
    if (!baseUrl) {
      return "";
    }
    let hub = null;
    for (let i = 0; i < this._yapi._connectedHubs.length; i++) {
      let hubUrl = this._yapi._connectedHubs[i].imm_getRootUrl();
      if (baseUrl.slice(0, hubUrl.length) == hubUrl) {
        hub = this._yapi._connectedHubs[i];
        break;
      }
    }
    if (!hub) {
      return "";
    }
    let hubSerial = hub.serialByYdx[0];
    if (hubSerial == this._serial) {
      return "";
    }
    return hubSerial;
  }
  async get_url_internal() {
    let devid = this._serial;
    if (devid == "") {
      devid = await this.get_serialNumber();
    }
    if (devid == YAPI_INVALID_STRING) {
      return "";
    }
    let lockdev = this._yapi.imm_getDevice(devid);
    if (!lockdev) {
      return "";
    }
    return lockdev.imm_getRootUrl();
  }
  async _startStopDevLog_internal(str_serial, bool_start) {
    let dev = this.imm_getDev();
    if (dev != null) {
      return dev.imm_registerLogCallback(this._logCallback);
    }
  }
  //--- (generated code: YModule implementation)
  imm_parseAttr(name, val) {
    switch (name) {
      case "productName":
        this._productName = val;
        return 1;
      case "serialNumber":
        this._serialNumber = val;
        return 1;
      case "productId":
        this._productId = val;
        return 1;
      case "productRelease":
        this._productRelease = val;
        return 1;
      case "firmwareRelease":
        this._firmwareRelease = val;
        return 1;
      case "persistentSettings":
        this._persistentSettings = val;
        return 1;
      case "luminosity":
        this._luminosity = val;
        return 1;
      case "beacon":
        this._beacon = val;
        return 1;
      case "upTime":
        this._upTime = val;
        return 1;
      case "usbCurrent":
        this._usbCurrent = val;
        return 1;
      case "rebootCountdown":
        this._rebootCountdown = val;
        return 1;
      case "userVar":
        this._userVar = val;
        return 1;
    }
    return super.imm_parseAttr(name, val);
  }
  /**
   * Returns the commercial name of the module, as set by the factory.
   *
   * @return a string corresponding to the commercial name of the module, as set by the factory
   *
   * On failure, throws an exception or returns YModule.PRODUCTNAME_INVALID.
   */
  async get_productName() {
    let res;
    let dev;
    if (this._cacheExpiration == 0) {
      dev = this.imm_getDev();
      if (!(dev == null)) {
        return dev.imm_getProductName();
      }
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YModule.PRODUCTNAME_INVALID;
      }
    }
    res = this._productName;
    return res;
  }
  /**
   * Returns the serial number of the module, as set by the factory.
   *
   * @return a string corresponding to the serial number of the module, as set by the factory
   *
   * On failure, throws an exception or returns YModule.SERIALNUMBER_INVALID.
   */
  async get_serialNumber() {
    let res;
    let dev;
    if (this._cacheExpiration == 0) {
      dev = this.imm_getDev();
      if (!(dev == null)) {
        return dev.imm_getSerialNumber();
      }
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YModule.SERIALNUMBER_INVALID;
      }
    }
    res = this._serialNumber;
    return res;
  }
  /**
   * Returns the USB device identifier of the module.
   *
   * @return an integer corresponding to the USB device identifier of the module
   *
   * On failure, throws an exception or returns YModule.PRODUCTID_INVALID.
   */
  async get_productId() {
    let res;
    let dev;
    if (this._cacheExpiration == 0) {
      dev = this.imm_getDev();
      if (!(dev == null)) {
        return dev.imm_getProductId();
      }
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YModule.PRODUCTID_INVALID;
      }
    }
    res = this._productId;
    return res;
  }
  /**
   * Returns the release number of the module hardware, preprogrammed at the factory.
   * The original hardware release returns value 1, revision B returns value 2, etc.
   *
   * @return an integer corresponding to the release number of the module hardware, preprogrammed at the factory
   *
   * On failure, throws an exception or returns YModule.PRODUCTRELEASE_INVALID.
   */
  async get_productRelease() {
    let res;
    if (this._cacheExpiration == 0) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YModule.PRODUCTRELEASE_INVALID;
      }
    }
    res = this._productRelease;
    return res;
  }
  /**
   * Returns the version of the firmware embedded in the module.
   *
   * @return a string corresponding to the version of the firmware embedded in the module
   *
   * On failure, throws an exception or returns YModule.FIRMWARERELEASE_INVALID.
   */
  async get_firmwareRelease() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YModule.FIRMWARERELEASE_INVALID;
      }
    }
    res = this._firmwareRelease;
    return res;
  }
  /**
   * Returns the current state of persistent module settings.
   *
   * @return a value among YModule.PERSISTENTSETTINGS_LOADED, YModule.PERSISTENTSETTINGS_SAVED and
   * YModule.PERSISTENTSETTINGS_MODIFIED corresponding to the current state of persistent module settings
   *
   * On failure, throws an exception or returns YModule.PERSISTENTSETTINGS_INVALID.
   */
  async get_persistentSettings() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YModule.PERSISTENTSETTINGS_INVALID;
      }
    }
    res = this._persistentSettings;
    return res;
  }
  async set_persistentSettings(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("persistentSettings", rest_val);
  }
  /**
   * Returns the luminosity of the  module informative LEDs (from 0 to 100).
   *
   * @return an integer corresponding to the luminosity of the  module informative LEDs (from 0 to 100)
   *
   * On failure, throws an exception or returns YModule.LUMINOSITY_INVALID.
   */
  async get_luminosity() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YModule.LUMINOSITY_INVALID;
      }
    }
    res = this._luminosity;
    return res;
  }
  /**
   * Changes the luminosity of the module informative leds. The parameter is a
   * value between 0 and 100.
   * Remember to call the saveToFlash() method of the module if the
   * modification must be kept.
   *
   * @param newval : an integer corresponding to the luminosity of the module informative leds
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_luminosity(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("luminosity", rest_val);
  }
  /**
   * Returns the state of the localization beacon.
   *
   * @return either YModule.BEACON_OFF or YModule.BEACON_ON, according to the state of the localization beacon
   *
   * On failure, throws an exception or returns YModule.BEACON_INVALID.
   */
  async get_beacon() {
    let res;
    let dev;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      dev = this.imm_getDev();
      if (!(dev == null)) {
        return dev.imm_getBeacon();
      }
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YModule.BEACON_INVALID;
      }
    }
    res = this._beacon;
    return res;
  }
  /**
   * Turns on or off the module localization beacon.
   *
   * @param newval : either YModule.BEACON_OFF or YModule.BEACON_ON
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_beacon(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("beacon", rest_val);
  }
  /**
   * Returns the number of milliseconds spent since the module was powered on.
   *
   * @return an integer corresponding to the number of milliseconds spent since the module was powered on
   *
   * On failure, throws an exception or returns YModule.UPTIME_INVALID.
   */
  async get_upTime() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YModule.UPTIME_INVALID;
      }
    }
    res = this._upTime;
    return res;
  }
  /**
   * Returns the current consumed by the module on the USB bus, in milli-amps.
   *
   * @return an integer corresponding to the current consumed by the module on the USB bus, in milli-amps
   *
   * On failure, throws an exception or returns YModule.USBCURRENT_INVALID.
   */
  async get_usbCurrent() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YModule.USBCURRENT_INVALID;
      }
    }
    res = this._usbCurrent;
    return res;
  }
  /**
   * Returns the remaining number of seconds before the module restarts, or zero when no
   * reboot has been scheduled.
   *
   * @return an integer corresponding to the remaining number of seconds before the module restarts, or zero when no
   *         reboot has been scheduled
   *
   * On failure, throws an exception or returns YModule.REBOOTCOUNTDOWN_INVALID.
   */
  async get_rebootCountdown() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YModule.REBOOTCOUNTDOWN_INVALID;
      }
    }
    res = this._rebootCountdown;
    return res;
  }
  async set_rebootCountdown(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("rebootCountdown", rest_val);
  }
  /**
   * Returns the value previously stored in this attribute.
   * On startup and after a device reboot, the value is always reset to zero.
   *
   * @return an integer corresponding to the value previously stored in this attribute
   *
   * On failure, throws an exception or returns YModule.USERVAR_INVALID.
   */
  async get_userVar() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YModule.USERVAR_INVALID;
      }
    }
    res = this._userVar;
    return res;
  }
  /**
   * Stores a 32 bit value in the device RAM. This attribute is at programmer disposal,
   * should he need to store a state variable.
   * On startup and after a device reboot, the value is always reset to zero.
   *
   * @param newval : an integer
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_userVar(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("userVar", rest_val);
  }
  /**
   * Allows you to find a module from its serial number or from its logical name.
   *
   * This function does not require that the module is online at the time
   * it is invoked. The returned object is nevertheless valid.
   * Use the method YModule.isOnline() to test if the module is
   * indeed online at a given time. In case of ambiguity when looking for
   * a module by logical name, no error is notified: the first instance
   * found is returned. The search is performed first by hardware name,
   * then by logical name.
   *
   *
   * If a call to this object's is_online() method returns FALSE although
   * you are certain that the device is plugged, make sure that you did
   * call registerHub() at application initialization time.
   *
   * @param func : a string containing either the serial number or
   *         the logical name of the desired module
   *
   * @return a YModule object allowing you to drive the module
   *         or get additional information on the module.
   */
  static FindModule(func) {
    let obj;
    let cleanHwId;
    let modpos;
    cleanHwId = func;
    modpos = func.indexOf(".module");
    if (modpos != func.length - 7) {
      cleanHwId = func + ".module";
    }
    obj = YFunction._FindFromCache("Module", cleanHwId);
    if (obj == null) {
      obj = new _YModule(YAPI, cleanHwId);
      YFunction._AddToCache("Module", cleanHwId, obj);
    }
    return obj;
  }
  /**
   * Retrieves a module for a given identifier in a YAPI context.
   * The identifier can be specified using several formats:
   *
   * - FunctionLogicalName
   * - ModuleSerialNumber.FunctionIdentifier
   * - ModuleSerialNumber.FunctionLogicalName
   * - ModuleLogicalName.FunctionIdentifier
   * - ModuleLogicalName.FunctionLogicalName
   *
   *
   * This function does not require that the module is online at the time
   * it is invoked. The returned object is nevertheless valid.
   * Use the method YModule.isOnline() to test if the module is
   * indeed online at a given time. In case of ambiguity when looking for
   * a module by logical name, no error is notified: the first instance
   * found is returned. The search is performed first by hardware name,
   * then by logical name.
   *
   * @param yctx : a YAPI context
   * @param func : a string that uniquely characterizes the module, for instance
   *         MyDevice.module.
   *
   * @return a YModule object allowing you to drive the module.
   */
  static FindModuleInContext(yctx, func) {
    let obj;
    let cleanHwId;
    let modpos;
    cleanHwId = func;
    modpos = func.indexOf(".module");
    if (modpos != func.length - 7) {
      cleanHwId = func + ".module";
    }
    obj = YFunction._FindFromCacheInContext(yctx, "Module", cleanHwId);
    if (obj == null) {
      obj = new _YModule(yctx, cleanHwId);
      YFunction._AddToCache("Module", cleanHwId, obj);
    }
    return obj;
  }
  /**
   * Registers the callback function that is invoked on every change of advertised value.
   * The callback is called once when it is registered, passing the current advertised value
   * of the function, provided that it is not an empty string.
   * The callback is then invoked only during the execution of ySleep or yHandleEvents.
   * This provides control over the time when the callback is triggered. For good responsiveness, remember to call
   * one of these two functions periodically. To unregister a callback, pass a null pointer as argument.
   *
   * @param callback : the callback function to call, or a null pointer. The callback function should take two
   *         arguments: the function object of which the value has changed, and the character string describing
   *         the new advertised value.
   * @noreturn
   */
  async registerValueCallback(callback) {
    let val;
    if (callback != null) {
      await YFunction._UpdateValueCallbackList(this, true);
    } else {
      await YFunction._UpdateValueCallbackList(this, false);
    }
    this._valueCallbackModule = callback;
    if (callback != null && await this.isOnline()) {
      val = this._advertisedValue;
      if (!(val == "")) {
        await this._invokeValueCallback(val);
      }
    }
    return 0;
  }
  async _invokeValueCallback(value) {
    if (this._valueCallbackModule != null) {
      try {
        await this._valueCallbackModule(this, value);
      } catch (e) {
        this._yapi.imm_log("Exception in valueCallback:", e);
      }
    } else {
      await super._invokeValueCallback(value);
    }
    return 0;
  }
  async get_productNameAndRevision() {
    let prodname;
    let prodrel;
    let fullname;
    prodname = await this.get_productName();
    prodrel = await this.get_productRelease();
    if (prodrel > 1) {
      fullname = prodname + " rev. " + String.fromCharCode(64 + prodrel);
    } else {
      fullname = prodname;
    }
    return fullname;
  }
  /**
   * Saves current settings in the nonvolatile memory of the module.
   * Warning: the number of allowed save operations during a module life is
   * limited (about 100000 cycles). Do not call this function within a loop.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async saveToFlash() {
    return await this.set_persistentSettings(
      1
      /* YModule.PERSISTENTSETTINGS.SAVED */
    );
  }
  /**
   * Reloads the settings stored in the nonvolatile memory, as
   * when the module is powered on.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async revertFromFlash() {
    return await this.set_persistentSettings(
      0
      /* YModule.PERSISTENTSETTINGS.LOADED */
    );
  }
  /**
   * Schedules a simple module reboot after the given number of seconds.
   *
   * @param secBeforeReboot : number of seconds before rebooting
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async reboot(secBeforeReboot) {
    return await this.set_rebootCountdown(secBeforeReboot);
  }
  /**
   * Schedules a module reboot into special firmware update mode.
   *
   * @param secBeforeReboot : number of seconds before rebooting
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async triggerFirmwareUpdate(secBeforeReboot) {
    return await this.set_rebootCountdown(-secBeforeReboot);
  }
  async _startStopDevLog(serial, start) {
    return await this._startStopDevLog_internal(serial, start);
  }
  /**
   * Registers a device log callback function. This callback will be called each time
   * that a module sends a new log message. Mostly useful to debug a Yoctopuce module.
   *
   * @param callback : the callback function to call, or a null pointer.
   *         The callback function should take two
   *         arguments: the module object that emitted the log message,
   *         and the character string containing the log.
   *         On failure, throws an exception or returns a negative error code.
   */
  async registerLogCallback(callback) {
    let serial;
    serial = await this.get_serialNumber();
    if (serial == YAPI_INVALID_STRING) {
      return YAPI_DEVICE_NOT_FOUND;
    }
    this._logCallback = callback;
    await this._startStopDevLog(serial, callback != null);
    return 0;
  }
  async get_logCallback() {
    return this._logCallback;
  }
  /**
   * Register a callback function, to be called when a persistent settings in
   * a device configuration has been changed (e.g. change of unit, etc).
   *
   * @param callback : a procedure taking a YModule parameter, or null
   *         to unregister a previously registered  callback.
   */
  async registerConfigChangeCallback(callback) {
    if (callback != null) {
      await _YModule._updateModuleCallbackList(this, true);
    } else {
      await _YModule._updateModuleCallbackList(this, false);
    }
    this._confChangeCallback = callback;
    return 0;
  }
  async _invokeConfigChangeCallback() {
    if (this._confChangeCallback != null) {
      try {
        await this._confChangeCallback(this);
      } catch (e) {
        this._yapi.imm_log("Exception in configChangeCallback:", e);
      }
    }
    return 0;
  }
  /**
   * Register a callback function, to be called when the localization beacon of the module
   * has been changed. The callback function should take two arguments: the YModule object of
   * which the beacon has changed, and an integer describing the new beacon state.
   *
   * @param callback : The callback function to call, or null to unregister a
   *         previously registered callback.
   */
  async registerBeaconCallback(callback) {
    if (callback != null) {
      await _YModule._updateModuleCallbackList(this, true);
    } else {
      await _YModule._updateModuleCallbackList(this, false);
    }
    this._beaconCallback = callback;
    return 0;
  }
  async _invokeBeaconCallback(beaconState) {
    if (this._beaconCallback != null) {
      try {
        await this._beaconCallback(this, beaconState);
      } catch (e) {
        this._yapi.imm_log("Exception in beaconCallback:", e);
      }
    }
    return 0;
  }
  /**
   * Triggers a configuration change callback, to check if they are supported or not.
   */
  async triggerConfigChangeCallback() {
    await this._setAttr("persistentSettings", "2");
    return 0;
  }
  /**
   * Tests whether the byn file is valid for this module. This method is useful to test if the module
   * needs to be updated.
   * It is possible to pass a directory as argument instead of a file. In this case, this method returns
   * the path of the most recent
   * appropriate .byn file. If the parameter onlynew is true, the function discards firmwares that are older or
   * equal to the installed firmware.
   *
   * @param path : the path of a byn file or a directory that contains byn files
   * @param onlynew : returns only files that are strictly newer
   *
   * @return the path of the byn file to use or a empty string if no byn files matches the requirement
   *
   * On failure, throws an exception or returns a string that start with "error:".
   */
  async checkFirmware(path, onlynew) {
    let serial;
    let release;
    let tmp_res;
    if (onlynew) {
      release = YAPIContext.imm_atoi(await this.get_firmwareRelease());
    } else {
      release = 0;
    }
    serial = await this.get_serialNumber();
    tmp_res = await YFirmwareUpdate.CheckFirmware(serial, path, release);
    if (tmp_res.indexOf("error:") == 0) {
      this._throw(YAPI_INVALID_ARGUMENT, tmp_res);
    }
    return tmp_res;
  }
  /**
   * Prepares a firmware update of the module. This method returns a YFirmwareUpdate object which
   * handles the firmware update process.
   *
   * @param path : the path of the .byn file to use.
   * @param force : true to force the firmware update even if some prerequisites appear not to be met
   *
   * @return a YFirmwareUpdate object or NULL on error.
   */
  async updateFirmwareEx(path, force) {
    let serial;
    let settings;
    serial = await this.get_serialNumber();
    settings = await this.get_allSettings();
    if (settings.length == 0) {
      this._throw(YAPI_IO_ERROR, "Unable to get device settings");
      settings = this._yapi.imm_str2bin("error:Unable to get device settings");
    }
    return new YFirmwareUpdate(this._yapi, serial, path, settings, force);
  }
  /**
   * Prepares a firmware update of the module. This method returns a YFirmwareUpdate object which
   * handles the firmware update process.
   *
   * @param path : the path of the .byn file to use.
   *
   * @return a YFirmwareUpdate object or NULL on error.
   */
  async updateFirmware(path) {
    return await this.updateFirmwareEx(path, false);
  }
  /**
   * Returns all the settings and uploaded files of the module. Useful to backup all the
   * logical names, calibrations parameters, and uploaded files of a device.
   *
   * @return a binary buffer with all the settings.
   *
   * On failure, throws an exception or returns an binary object of size 0.
   */
  async get_allSettings() {
    let settings;
    let json;
    let res;
    let sep;
    let name;
    let item;
    let t_type;
    let pageid;
    let url;
    let file_data;
    let file_data_bin;
    let temp_data_bin;
    let ext_settings;
    let filelist = [];
    let templist = [];
    settings = await this._download("api.json");
    if (settings.length == 0) {
      return settings;
    }
    ext_settings = ', "extras":[';
    templist = await this.get_functionIds("Temperature");
    sep = "";
    for (let ii_0 of templist) {
      if (YAPIContext.imm_atoi(await this.get_firmwareRelease()) > 9e3) {
        url = "api/" + ii_0 + "/sensorType";
        t_type = this._yapi.imm_bin2str(await this._download(url));
        if (t_type == "RES_NTC" || t_type == "RES_LINEAR") {
          pageid = ii_0.substr(11, ii_0.length - 11);
          if (pageid == "") {
            pageid = "1";
          }
          temp_data_bin = await this._download("extra.json?page=" + pageid);
          if (temp_data_bin.length > 0) {
            item = sep + '{"fid":"' + ii_0 + '", "json":' + this._yapi.imm_bin2str(temp_data_bin) + "}\n";
            ext_settings = ext_settings + item;
            sep = ",";
          }
        }
      }
    }
    ext_settings = ext_settings + '],\n"files":[';
    if (await this.hasFunction("files")) {
      json = await this._download("files.json?a=dir&d=1&f=");
      if (json.length == 0) {
        return json;
      }
      filelist = this.imm_json_get_array(json);
      sep = "";
      for (let ii_1 of filelist) {
        name = this.imm_json_get_key(ii_1, "name");
        if (name.length > 0 && !(name == "startupConf.json")) {
          if (name.substr(name.length - 1, 1) == "/") {
            file_data = "";
          } else {
            file_data_bin = await this._download(this.imm_escapeAttr(name));
            file_data = this._yapi.imm_bin2hexstr(file_data_bin);
          }
          item = sep + '{"name":"' + name + '", "data":"' + file_data + '"}\n';
          ext_settings = ext_settings + item;
          sep = ",";
        }
      }
    }
    res = this._yapi.imm_str2bin('{ "api":' + this._yapi.imm_bin2str(settings) + ext_settings + "]}");
    return res;
  }
  async loadThermistorExtra(funcId, jsonExtra) {
    let values = [];
    let url;
    let curr;
    let binCurr;
    let currTemp;
    let binCurrTemp;
    let ofs;
    let size;
    url = "api/" + funcId + ".json?command=Z";
    await this._download(url);
    values = this.imm_json_get_array(this._yapi.imm_str2bin(jsonExtra));
    ofs = 0;
    size = values.length;
    while (ofs + 1 < size) {
      binCurr = values[ofs];
      binCurrTemp = values[ofs + 1];
      curr = this.imm_json_get_string(binCurr);
      currTemp = this.imm_json_get_string(binCurrTemp);
      url = "api/" + funcId + ".json?command=m" + curr + ":" + currTemp;
      await this._download(url);
      ofs = ofs + 2;
    }
    return YAPI_SUCCESS;
  }
  async set_extraSettings(jsonExtra) {
    let extras = [];
    let tmp;
    let functionId;
    let data;
    extras = this.imm_json_get_array(this._yapi.imm_str2bin(jsonExtra));
    for (let ii_0 of extras) {
      tmp = this.imm_get_json_path(ii_0, "fid");
      functionId = this.imm_json_get_string(tmp);
      data = this.imm_get_json_path(ii_0, "json");
      if (await this.hasFunction(functionId)) {
        await this.loadThermistorExtra(functionId, this._yapi.imm_bin2str(data));
      }
    }
    return YAPI_SUCCESS;
  }
  /**
   * Restores all the settings and uploaded files to the module.
   * This method is useful to restore all the logical names and calibrations parameters,
   * uploaded files etc. of a device from a backup.
   * Remember to call the saveToFlash() method of the module if the
   * modifications must be kept.
   *
   * @param settings : a binary buffer with all the settings.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_allSettingsAndFiles(settings) {
    let down;
    let json_api;
    let json_files;
    let json_extra;
    let fuperror;
    let globalres;
    fuperror = 0;
    json_api = this.imm_get_json_path(settings, "api");
    if (json_api.length == 0) {
      return await this.set_allSettings(settings);
    }
    json_extra = this.imm_get_json_path(settings, "extras");
    if (json_extra.length > 0) {
      await this.set_extraSettings(this._yapi.imm_bin2str(json_extra));
    }
    await this.set_allSettings(json_api);
    if (await this.hasFunction("files")) {
      let files = [];
      let res;
      let tmp;
      let name;
      let data;
      down = await this._download("files.json?a=format");
      down = this.imm_get_json_path(down, "res");
      res = this.imm_json_get_string(down);
      if (!(res == "ok")) {
        return this._throw(YAPI_IO_ERROR, "format failed", YAPI_IO_ERROR);
      }
      json_files = this.imm_get_json_path(settings, "files");
      files = this.imm_json_get_array(json_files);
      for (let ii_0 of files) {
        tmp = this.imm_get_json_path(ii_0, "name");
        name = this.imm_json_get_string(tmp);
        tmp = this.imm_get_json_path(ii_0, "data");
        data = this.imm_json_get_string(tmp);
        if (name == "") {
          fuperror = fuperror + 1;
        } else {
          await this._upload(name, this._yapi.imm_hexstr2bin(data));
        }
      }
    }
    globalres = await this.set_allSettings(json_api);
    if (!(fuperror == 0)) {
      return this._throw(YAPI_IO_ERROR, "Error during file upload", YAPI_IO_ERROR);
    }
    return globalres;
  }
  /**
   * Tests if the device includes a specific function. This method takes a function identifier
   * and returns a boolean.
   *
   * @param funcId : the requested function identifier
   *
   * @return true if the device has the function identifier
   */
  async hasFunction(funcId) {
    let count;
    let i;
    let fid;
    count = await this.functionCount();
    i = 0;
    while (i < count) {
      fid = await this.functionId(i);
      if (fid == funcId) {
        return true;
      }
      i = i + 1;
    }
    return false;
  }
  /**
   * Retrieve all hardware identifier that match the type passed in argument.
   *
   * @param funType : The type of function (Relay, LightSensor, Voltage,...)
   *
   * @return an array of strings.
   */
  async get_functionIds(funType) {
    let count;
    let i;
    let ftype;
    let res = [];
    count = await this.functionCount();
    i = 0;
    while (i < count) {
      ftype = await this.functionType(i);
      if (ftype == funType) {
        res.push(await this.functionId(i));
      } else {
        ftype = await this.functionBaseType(i);
        if (ftype == funType) {
          res.push(await this.functionId(i));
        }
      }
      i = i + 1;
    }
    return res;
  }
  imm_flattenJsonStruct(jsoncomplex) {
    return this.imm_flattenJsonStruct_internal(jsoncomplex);
  }
  async calibVersion(cparams) {
    if (cparams == "0,") {
      return 3;
    }
    if (cparams.indexOf(",") >= 0) {
      if (cparams.indexOf(" ") > 0) {
        return 3;
      } else {
        return 1;
      }
    }
    if (cparams == "" || cparams == "0") {
      return 1;
    }
    if (cparams.length < 2 || cparams.indexOf(".") >= 0) {
      return 0;
    } else {
      return 2;
    }
  }
  async calibScale(unit_name, sensorType) {
    if (unit_name == "g" || unit_name == "gauss" || unit_name == "W") {
      return 1e3;
    }
    if (unit_name == "C") {
      if (sensorType == "") {
        return 16;
      }
      if (YAPIContext.imm_atoi(sensorType) < 8) {
        return 16;
      } else {
        return 100;
      }
    }
    if (unit_name == "m" || unit_name == "deg") {
      return 10;
    }
    return 1;
  }
  async calibOffset(unit_name) {
    if (unit_name == "% RH" || unit_name == "mbar" || unit_name == "lx") {
      return 0;
    }
    return 32767;
  }
  async calibConvert(param, currentFuncValue, unit_name, sensorType) {
    let paramVer;
    let funVer;
    let funScale;
    let funOffset;
    let paramScale;
    let paramOffset;
    let words = [];
    let words_str = [];
    let calibData = [];
    let iCalib = [];
    let calibType;
    let i;
    let maxSize;
    let ratio;
    let nPoints;
    let wordVal;
    paramVer = await this.calibVersion(param);
    funVer = await this.calibVersion(currentFuncValue);
    funScale = await this.calibScale(unit_name, sensorType);
    funOffset = await this.calibOffset(unit_name);
    paramScale = funScale;
    paramOffset = funOffset;
    if (funVer < 3) {
      if (funVer == 2) {
        words = this._yapi.imm_decodeWords(currentFuncValue);
        if (words[0] == 1366 && words[1] == 12500) {
          funScale = 1;
          funOffset = 0;
        } else {
          funScale = words[1];
          funOffset = words[0];
        }
      } else {
        if (funVer == 1) {
          if (currentFuncValue == "" || YAPIContext.imm_atoi(currentFuncValue) > 10) {
            funScale = 0;
          }
        }
      }
    }
    calibData.length = 0;
    calibType = 0;
    if (paramVer < 3) {
      if (paramVer == 2) {
        words = this._yapi.imm_decodeWords(param);
        if (words[0] == 1366 && words[1] == 12500) {
          paramScale = 1;
          paramOffset = 0;
        } else {
          paramScale = words[1];
          paramOffset = words[0];
        }
        if (words.length >= 3 && words[2] > 0) {
          maxSize = 3 + 2 * (words[2] % 10);
          if (maxSize > words.length) {
            maxSize = words.length;
          }
          i = 3;
          while (i < maxSize) {
            calibData.push(words[i]);
            i = i + 1;
          }
        }
      } else {
        if (paramVer == 1) {
          words_str = param.split(",");
          for (let ii_0 of words_str) {
            words.push(YAPIContext.imm_atoi(ii_0));
          }
          if (param == "" || words[0] > 10) {
            paramScale = 0;
          }
          if (words.length > 0 && words[0] > 0) {
            maxSize = 1 + 2 * (words[0] % 10);
            if (maxSize > words.length) {
              maxSize = words.length;
            }
            i = 1;
            while (i < maxSize) {
              calibData.push(words[i]);
              i = i + 1;
            }
          }
        } else {
          if (paramVer == 0) {
            ratio = YAPIContext.imm_atof(param);
            if (ratio > 0) {
              calibData.push(0);
              calibData.push(0);
              calibData.push(Math.round(65535 / ratio));
              calibData.push(65535);
            }
          }
        }
      }
      i = 0;
      while (i < calibData.length) {
        if (paramScale > 0) {
          calibData[i] = (calibData[i] - paramOffset) / paramScale;
        } else {
          calibData[i] = this._yapi.imm_decimalToDouble(Math.round(calibData[i]));
        }
        i = i + 1;
      }
    } else {
      iCalib = this._yapi.imm_decodeFloats(param);
      calibType = Math.round(iCalib[0] / 1e3);
      if (calibType >= 30) {
        calibType = calibType - 30;
      }
      i = 1;
      while (i < iCalib.length) {
        calibData.push(iCalib[i] / 1e3);
        i = i + 1;
      }
    }
    if (funVer >= 3) {
      if (calibData.length == 0) {
        param = "0,";
      } else {
        param = (30 + calibType).toString();
        i = 0;
        while (i < calibData.length) {
          if ((i & 1) > 0) {
            param = param + ":";
          } else {
            param = param + " ";
          }
          param = param + Math.round(calibData[i] * 1e3 / 1e3).toString();
          i = i + 1;
        }
        param = param + ",";
      }
    } else {
      if (funVer >= 1) {
        nPoints = calibData.length / 2 >> 0;
        param = nPoints.toString();
        i = 0;
        while (i < 2 * nPoints) {
          if (funScale == 0) {
            wordVal = this._yapi.imm_doubleToDecimal(Math.round(calibData[i]));
          } else {
            wordVal = calibData[i] * funScale + funOffset;
          }
          param = param + "," + Math.round(wordVal).toString();
          i = i + 1;
        }
      } else {
        if (calibData.length == 4) {
          param = Math.round(1e3 * (calibData[3] - calibData[1]) / calibData[2] - calibData[0]).toString();
        }
      }
    }
    return param;
  }
  async _tryExec(url) {
    let res;
    let done;
    res = YAPI_SUCCESS;
    done = 1;
    try {
      await this._download(url);
    } catch (e) {
      done = 0;
    }
    if (done == 0) {
      try {
        await YAPI.Sleep(500);
        await this._download(url);
      } catch (e) {
        res = await this.get_errorType();
      }
    }
    return res;
  }
  /**
   * Restores all the settings of the device. Useful to restore all the logical names and calibrations parameters
   * of a module from a backup.Remember to call the saveToFlash() method of the module if the
   * modifications must be kept.
   *
   * @param settings : a binary buffer with all the settings.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_allSettings(settings) {
    let restoreLast = [];
    let old_json_flat;
    let old_dslist = [];
    let old_jpath = [];
    let old_jpath_len = [];
    let old_val_arr = [];
    let actualSettings;
    let new_dslist = [];
    let new_jpath = [];
    let new_jpath_len = [];
    let new_val_arr = [];
    let cpos;
    let eqpos;
    let leng;
    let i;
    let j;
    let subres;
    let res;
    let njpath;
    let jpath;
    let fun;
    let attr;
    let value;
    let old_serial;
    let new_serial;
    let url;
    let tmp;
    let binTmp;
    let sensorType;
    let unit_name;
    let newval;
    let oldval;
    let old_calib;
    let each_str;
    let do_update;
    let found;
    res = YAPI_SUCCESS;
    binTmp = this.imm_get_json_path(settings, "api");
    if (binTmp.length > 0) {
      settings = binTmp;
    }
    old_serial = "";
    oldval = "";
    newval = "";
    old_json_flat = this.imm_flattenJsonStruct(settings);
    old_dslist = this.imm_json_get_array(old_json_flat);
    for (let ii_0 of old_dslist) {
      each_str = this.imm_json_get_string(ii_0);
      leng = each_str.length;
      eqpos = each_str.indexOf("=");
      if (eqpos < 0 || leng == 0) {
        this._throw(YAPI_INVALID_ARGUMENT, "Invalid settings");
        return YAPI_INVALID_ARGUMENT;
      }
      jpath = each_str.substr(0, eqpos);
      eqpos = eqpos + 1;
      value = each_str.substr(eqpos, leng - eqpos);
      old_jpath.push(jpath);
      old_jpath_len.push(jpath.length);
      old_val_arr.push(value);
      if (jpath == "module/serialNumber") {
        old_serial = value;
      }
    }
    try {
      actualSettings = await this._download("api.json");
    } catch (e) {
      await YAPI.Sleep(500);
      actualSettings = await this._download("api.json");
    }
    new_serial = await this.get_serialNumber();
    if (old_serial == new_serial || old_serial == "") {
      old_serial = "_NO_SERIAL_FILTER_";
    }
    actualSettings = this.imm_flattenJsonStruct(actualSettings);
    new_dslist = this.imm_json_get_array(actualSettings);
    for (let ii_1 of new_dslist) {
      each_str = this.imm_json_get_string(ii_1);
      leng = each_str.length;
      eqpos = each_str.indexOf("=");
      if (eqpos < 0 || leng == 0) {
        this._throw(YAPI_INVALID_ARGUMENT, "Invalid settings");
        return YAPI_INVALID_ARGUMENT;
      }
      jpath = each_str.substr(0, eqpos);
      eqpos = eqpos + 1;
      value = each_str.substr(eqpos, leng - eqpos);
      new_jpath.push(jpath);
      new_jpath_len.push(jpath.length);
      new_val_arr.push(value);
    }
    i = 0;
    while (i < new_jpath.length) {
      njpath = new_jpath[i];
      leng = njpath.length;
      cpos = njpath.indexOf("/");
      if (cpos < 0 || leng == 0) {
        continue;
      }
      fun = njpath.substr(0, cpos);
      cpos = cpos + 1;
      attr = njpath.substr(cpos, leng - cpos);
      do_update = true;
      if (fun == "services") {
        do_update = false;
      }
      if (do_update && attr == "firmwareRelease") {
        do_update = false;
      }
      if (do_update && attr == "usbCurrent") {
        do_update = false;
      }
      if (do_update && attr == "upTime") {
        do_update = false;
      }
      if (do_update && attr == "persistentSettings") {
        do_update = false;
      }
      if (do_update && attr == "adminPassword") {
        do_update = false;
      }
      if (do_update && attr == "userPassword") {
        do_update = false;
      }
      if (do_update && attr == "rebootCountdown") {
        do_update = false;
      }
      if (do_update && attr == "advertisedValue") {
        do_update = false;
      }
      if (do_update && attr == "poeCurrent") {
        do_update = false;
      }
      if (do_update && attr == "readiness") {
        do_update = false;
      }
      if (do_update && attr == "ipAddress") {
        do_update = false;
      }
      if (do_update && attr == "subnetMask") {
        do_update = false;
      }
      if (do_update && attr == "router") {
        do_update = false;
      }
      if (do_update && attr == "linkQuality") {
        do_update = false;
      }
      if (do_update && attr == "ssid") {
        do_update = false;
      }
      if (do_update && attr == "channel") {
        do_update = false;
      }
      if (do_update && attr == "security") {
        do_update = false;
      }
      if (do_update && attr == "message") {
        do_update = false;
      }
      if (do_update && attr == "signalValue") {
        do_update = false;
      }
      if (do_update && attr == "currentValue") {
        do_update = false;
      }
      if (do_update && attr == "currentRawValue") {
        do_update = false;
      }
      if (do_update && attr == "currentRunIndex") {
        do_update = false;
      }
      if (do_update && attr == "pulseTimer") {
        do_update = false;
      }
      if (do_update && attr == "lastTimePressed") {
        do_update = false;
      }
      if (do_update && attr == "lastTimeReleased") {
        do_update = false;
      }
      if (do_update && attr == "filesCount") {
        do_update = false;
      }
      if (do_update && attr == "freeSpace") {
        do_update = false;
      }
      if (do_update && attr == "timeUTC") {
        do_update = false;
      }
      if (do_update && attr == "rtcTime") {
        do_update = false;
      }
      if (do_update && attr == "unixTime") {
        do_update = false;
      }
      if (do_update && attr == "dateTime") {
        do_update = false;
      }
      if (do_update && attr == "rawValue") {
        do_update = false;
      }
      if (do_update && attr == "lastMsg") {
        do_update = false;
      }
      if (do_update && attr == "delayedPulseTimer") {
        do_update = false;
      }
      if (do_update && attr == "rxCount") {
        do_update = false;
      }
      if (do_update && attr == "txCount") {
        do_update = false;
      }
      if (do_update && attr == "msgCount") {
        do_update = false;
      }
      if (do_update && attr == "rxMsgCount") {
        do_update = false;
      }
      if (do_update && attr == "txMsgCount") {
        do_update = false;
      }
      if (do_update) {
        do_update = false;
        j = 0;
        found = false;
        newval = new_val_arr[i];
        while (j < old_jpath.length && !found) {
          if (new_jpath_len[i] == old_jpath_len[j] && new_jpath[i] == old_jpath[j]) {
            found = true;
            oldval = old_val_arr[j];
            if (!(newval == oldval) && !(oldval == old_serial)) {
              do_update = true;
            }
          }
          j = j + 1;
        }
      }
      if (do_update) {
        if (attr == "calibrationParam") {
          old_calib = "";
          unit_name = "";
          sensorType = "";
          j = 0;
          found = false;
          while (j < old_jpath.length && !found) {
            if (new_jpath_len[i] == old_jpath_len[j] && new_jpath[i] == old_jpath[j]) {
              found = true;
              old_calib = old_val_arr[j];
            }
            j = j + 1;
          }
          tmp = fun + "/unit";
          j = 0;
          found = false;
          while (j < new_jpath.length && !found) {
            if (tmp == new_jpath[j]) {
              found = true;
              unit_name = new_val_arr[j];
            }
            j = j + 1;
          }
          tmp = fun + "/sensorType";
          j = 0;
          found = false;
          while (j < new_jpath.length && !found) {
            if (tmp == new_jpath[j]) {
              found = true;
              sensorType = new_val_arr[j];
            }
            j = j + 1;
          }
          newval = await this.calibConvert(old_calib, new_val_arr[i], unit_name, sensorType);
          url = "api/" + fun + ".json?" + attr + "=" + this.imm_escapeAttr(newval);
          subres = await this._tryExec(url);
          if (res == YAPI_SUCCESS && subres != YAPI_SUCCESS) {
            res = subres;
          }
        } else {
          url = "api/" + fun + ".json?" + attr + "=" + this.imm_escapeAttr(oldval);
          if (attr == "resolution") {
            restoreLast.push(url);
          } else {
            subres = await this._tryExec(url);
            if (res == YAPI_SUCCESS && subres != YAPI_SUCCESS) {
              res = subres;
            }
          }
        }
      }
      i = i + 1;
    }
    for (let ii_2 of restoreLast) {
      subres = await this._tryExec(ii_2);
      if (res == YAPI_SUCCESS && subres != YAPI_SUCCESS) {
        res = subres;
      }
    }
    await this.clearCache();
    return res;
  }
  /**
   * Adds a file to the uploaded data at the next HTTP callback.
   * This function only affects the next HTTP callback and only works in
   * HTTP callback mode.
   *
   * @param filename : the name of the file to upload at the next HTTP callback
   *
   * @return nothing.
   */
  async addFileToHTTPCallback(filename) {
    let content;
    content = await this._download("@YCB+" + filename);
    if (content.length == 0) {
      return YAPI_NOT_SUPPORTED;
    }
    return YAPI_SUCCESS;
  }
  /**
   * Returns the unique hardware identifier of the module.
   * The unique hardware identifier is made of the device serial
   * number followed by string ".module".
   *
   * @return a string that uniquely identifies the module
   */
  async get_hardwareId() {
    let serial;
    serial = await this.get_serialNumber();
    return serial + ".module";
  }
  /**
   * Downloads the specified built-in file and returns a binary buffer with its content.
   *
   * @param pathname : name of the new file to load
   *
   * @return a binary buffer with the file content
   *
   * On failure, throws an exception or returns an empty content.
   */
  async download(pathname) {
    return await this._download(pathname);
  }
  /**
   * Returns the icon of the module. The icon is a PNG image and does not
   * exceeds 1536 bytes.
   *
   * @return a binary buffer with module icon, in png format.
   *         On failure, throws an exception or returns an empty content.
   */
  async get_icon2d() {
    return await this._download("icon2d.png");
  }
  /**
   * Returns a string with last logs of the module. This method return only
   * logs that are still in the module.
   *
   * @return a string with last logs of the module.
   *         On failure, throws an exception or returns  YAPI.INVALID_STRING.
   */
  async get_lastLogs() {
    let content;
    content = await this._download("logs.txt");
    if (content.length == 0) {
      return YAPI_INVALID_STRING;
    }
    return this._yapi.imm_bin2str(content);
  }
  /**
   * Adds a text message to the device logs. This function is useful in
   * particular to trace the execution of HTTP callbacks. If a newline
   * is desired after the message, it must be included in the string.
   *
   * @param text : the string to append to the logs.
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async log(text) {
    return await this._upload("logs.txt", this._yapi.imm_str2bin(text));
  }
  /**
   * Returns a list of all the modules that are plugged into the current module.
   * This method only makes sense when called for a YoctoHub/VirtualHub.
   * Otherwise, an empty array will be returned.
   *
   * @return an array of strings containing the sub modules.
   */
  async get_subDevices() {
    return await this.get_subDevices_internal();
  }
  /**
   * Returns the serial number of the YoctoHub on which this module is connected.
   * If the module is connected by USB, or if the module is the root YoctoHub, an
   * empty string is returned.
   *
   * @return a string with the serial number of the YoctoHub or an empty string
   */
  async get_parentHub() {
    return await this.get_parentHub_internal();
  }
  /**
   * Returns the URL used to access the module. If the module is connected by USB, the
   * string 'usb' is returned.
   *
   * @return a string with the URL of the module.
   */
  async get_url() {
    return await this.get_url_internal();
  }
  /**
   * Continues the module enumeration started using yFirstModule().
   * Caution: You can't make any assumption about the returned modules order.
   * If you want to find a specific module, use Module.findModule()
   * and a hardwareID or a logical name.
   *
   * @return a pointer to a YModule object, corresponding to
   *         the next module found, or a null pointer
   *         if there are no more modules to enumerate.
   */
  nextModule() {
    let resolve = this._yapi.imm_resolveFunction(this._className, this._func);
    if (resolve.errorType != YAPI.SUCCESS)
      return null;
    let next_hwid = this._yapi.imm_getNextHardwareId(this._className, resolve.result);
    if (next_hwid == null)
      return null;
    return _YModule.FindModuleInContext(this._yapi, next_hwid);
  }
  /**
   * Starts the enumeration of modules currently accessible.
   * Use the method YModule.nextModule() to iterate on the
   * next modules.
   *
   * @return a pointer to a YModule object, corresponding to
   *         the first module currently online, or a null pointer
   *         if there are none.
   */
  static FirstModule() {
    let next_hwid = YAPI.imm_getFirstHardwareId("Module");
    if (next_hwid == null)
      return null;
    return _YModule.FindModule(next_hwid);
  }
  /**
   * Retrieves the first Module in a given context
   *
   * @param yctx {YAPIContext}
   *
   * @returns {YModule}
   */
  static FirstModuleInContext(yctx) {
    let next_hwid = yctx.imm_getFirstHardwareId("Module");
    if (next_hwid == null)
      return null;
    return _YModule.FindModuleInContext(yctx, next_hwid);
  }
};
YModule.PRODUCTNAME_INVALID = YAPI_INVALID_STRING;
YModule.SERIALNUMBER_INVALID = YAPI_INVALID_STRING;
YModule.PRODUCTID_INVALID = YAPI_INVALID_UINT;
YModule.PRODUCTRELEASE_INVALID = YAPI_INVALID_UINT;
YModule.FIRMWARERELEASE_INVALID = YAPI_INVALID_STRING;
YModule.PERSISTENTSETTINGS_LOADED = 0;
YModule.PERSISTENTSETTINGS_SAVED = 1;
YModule.PERSISTENTSETTINGS_MODIFIED = 2;
YModule.PERSISTENTSETTINGS_INVALID = -1;
YModule.LUMINOSITY_INVALID = YAPI_INVALID_UINT;
YModule.BEACON_OFF = 0;
YModule.BEACON_ON = 1;
YModule.BEACON_INVALID = -1;
YModule.UPTIME_INVALID = YAPI_INVALID_LONG;
YModule.USBCURRENT_INVALID = YAPI_INVALID_UINT;
YModule.REBOOTCOUNTDOWN_INVALID = YAPI_INVALID_INT;
YModule.USERVAR_INVALID = YAPI_INVALID_INT;
var YSensor = class _YSensor extends YFunction {
  //--- (end of generated code: YSensor attributes declaration)
  constructor(yapi, func) {
    super(yapi, func);
    this._cal = null;
    this._unit = _YSensor.UNIT_INVALID;
    this._currentValue = _YSensor.CURRENTVALUE_INVALID;
    this._lowestValue = _YSensor.LOWESTVALUE_INVALID;
    this._highestValue = _YSensor.HIGHESTVALUE_INVALID;
    this._currentRawValue = _YSensor.CURRENTRAWVALUE_INVALID;
    this._logFrequency = _YSensor.LOGFREQUENCY_INVALID;
    this._reportFrequency = _YSensor.REPORTFREQUENCY_INVALID;
    this._advMode = _YSensor.ADVMODE_INVALID;
    this._calibrationParam = _YSensor.CALIBRATIONPARAM_INVALID;
    this._resolution = _YSensor.RESOLUTION_INVALID;
    this._sensorState = _YSensor.SENSORSTATE_INVALID;
    this._valueCallbackSensor = null;
    this._timedReportCallbackSensor = null;
    this._prevTR = 0;
    this._iresol = 0;
    this.UNIT_INVALID = YAPI_INVALID_STRING;
    this.CURRENTVALUE_INVALID = YAPI_INVALID_DOUBLE;
    this.LOWESTVALUE_INVALID = YAPI_INVALID_DOUBLE;
    this.HIGHESTVALUE_INVALID = YAPI_INVALID_DOUBLE;
    this.CURRENTRAWVALUE_INVALID = YAPI_INVALID_DOUBLE;
    this.LOGFREQUENCY_INVALID = YAPI_INVALID_STRING;
    this.REPORTFREQUENCY_INVALID = YAPI_INVALID_STRING;
    this.ADVMODE_IMMEDIATE = 0;
    this.ADVMODE_PERIOD_AVG = 1;
    this.ADVMODE_PERIOD_MIN = 2;
    this.ADVMODE_PERIOD_MAX = 3;
    this.ADVMODE_INVALID = -1;
    this.CALIBRATIONPARAM_INVALID = YAPI_INVALID_STRING;
    this.RESOLUTION_INVALID = YAPI_INVALID_DOUBLE;
    this.SENSORSTATE_INVALID = YAPI_INVALID_INT;
    this._className = "Sensor";
  }
  //--- (generated code: YSensor implementation)
  imm_parseAttr(name, val) {
    switch (name) {
      case "unit":
        this._unit = val;
        return 1;
      case "currentValue":
        this._currentValue = Math.round(val / 65.536) / 1e3;
        return 1;
      case "lowestValue":
        this._lowestValue = Math.round(val / 65.536) / 1e3;
        return 1;
      case "highestValue":
        this._highestValue = Math.round(val / 65.536) / 1e3;
        return 1;
      case "currentRawValue":
        this._currentRawValue = Math.round(val / 65.536) / 1e3;
        return 1;
      case "logFrequency":
        this._logFrequency = val;
        return 1;
      case "reportFrequency":
        this._reportFrequency = val;
        return 1;
      case "advMode":
        this._advMode = val;
        return 1;
      case "calibrationParam":
        this._calibrationParam = val;
        return 1;
      case "resolution":
        this._resolution = Math.round(val / 65.536) / 1e3;
        return 1;
      case "sensorState":
        this._sensorState = val;
        return 1;
    }
    return super.imm_parseAttr(name, val);
  }
  /**
   * Returns the measuring unit for the measure.
   *
   * @return a string corresponding to the measuring unit for the measure
   *
   * On failure, throws an exception or returns YSensor.UNIT_INVALID.
   */
  async get_unit() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YSensor.UNIT_INVALID;
      }
    }
    res = this._unit;
    return res;
  }
  /**
   * Returns the current value of the measure, in the specified unit, as a floating point number.
   * Note that a get_currentValue() call will *not* start a measure in the device, it
   * will just return the last measure that occurred in the device. Indeed, internally, each Yoctopuce
   * devices is continuously making measurements at a hardware specific frequency.
   *
   * If continuously calling  get_currentValue() leads you to performances issues, then
   * you might consider to switch to callback programming model. Check the "advanced
   * programming" chapter in in your device user manual for more information.
   *
   * @return a floating point number corresponding to the current value of the measure, in the specified
   * unit, as a floating point number
   *
   * On failure, throws an exception or returns YSensor.CURRENTVALUE_INVALID.
   */
  async get_currentValue() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YSensor.CURRENTVALUE_INVALID;
      }
    }
    if (this._cal == null) {
      res = this._currentValue;
    } else {
      res = await this._applyCalibration(this._currentRawValue);
    }
    if (res == _YSensor.CURRENTVALUE_INVALID) {
      return res;
    }
    res = Math.round(res * this._iresol) / this._iresol;
    return res;
  }
  /**
   * Changes the recorded minimal value observed. Can be used to reset the value returned
   * by get_lowestValue().
   *
   * @param newval : a floating point number corresponding to the recorded minimal value observed
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_lowestValue(newval) {
    let rest_val;
    rest_val = String(Math.round(newval * 65536));
    return await this._setAttr("lowestValue", rest_val);
  }
  /**
   * Returns the minimal value observed for the measure since the device was started.
   * Can be reset to an arbitrary value thanks to set_lowestValue().
   *
   * @return a floating point number corresponding to the minimal value observed for the measure since
   * the device was started
   *
   * On failure, throws an exception or returns YSensor.LOWESTVALUE_INVALID.
   */
  async get_lowestValue() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YSensor.LOWESTVALUE_INVALID;
      }
    }
    res = Math.round(this._lowestValue * this._iresol) / this._iresol;
    return res;
  }
  /**
   * Changes the recorded maximal value observed. Can be used to reset the value returned
   * by get_lowestValue().
   *
   * @param newval : a floating point number corresponding to the recorded maximal value observed
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_highestValue(newval) {
    let rest_val;
    rest_val = String(Math.round(newval * 65536));
    return await this._setAttr("highestValue", rest_val);
  }
  /**
   * Returns the maximal value observed for the measure since the device was started.
   * Can be reset to an arbitrary value thanks to set_highestValue().
   *
   * @return a floating point number corresponding to the maximal value observed for the measure since
   * the device was started
   *
   * On failure, throws an exception or returns YSensor.HIGHESTVALUE_INVALID.
   */
  async get_highestValue() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YSensor.HIGHESTVALUE_INVALID;
      }
    }
    res = Math.round(this._highestValue * this._iresol) / this._iresol;
    return res;
  }
  /**
   * Returns the uncalibrated, unrounded raw value returned by the
   * sensor, in the specified unit, as a floating point number.
   *
   * @return a floating point number corresponding to the uncalibrated, unrounded raw value returned by the
   *         sensor, in the specified unit, as a floating point number
   *
   * On failure, throws an exception or returns YSensor.CURRENTRAWVALUE_INVALID.
   */
  async get_currentRawValue() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YSensor.CURRENTRAWVALUE_INVALID;
      }
    }
    res = this._currentRawValue;
    return res;
  }
  /**
   * Returns the datalogger recording frequency for this function, or "OFF"
   * when measures are not stored in the data logger flash memory.
   *
   * @return a string corresponding to the datalogger recording frequency for this function, or "OFF"
   *         when measures are not stored in the data logger flash memory
   *
   * On failure, throws an exception or returns YSensor.LOGFREQUENCY_INVALID.
   */
  async get_logFrequency() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YSensor.LOGFREQUENCY_INVALID;
      }
    }
    res = this._logFrequency;
    return res;
  }
  /**
   * Changes the datalogger recording frequency for this function.
   * The frequency can be specified as samples per second,
   * as sample per minute (for instance "15/m") or in samples per
   * hour (eg. "4/h"). To disable recording for this function, use
   * the value "OFF". Note that setting the  datalogger recording frequency
   * to a greater value than the sensor native sampling frequency is useless,
   * and even counterproductive: those two frequencies are not related.
   * Remember to call the saveToFlash() method of the module if the modification must be kept.
   *
   * @param newval : a string corresponding to the datalogger recording frequency for this function
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_logFrequency(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("logFrequency", rest_val);
  }
  /**
   * Returns the timed value notification frequency, or "OFF" if timed
   * value notifications are disabled for this function.
   *
   * @return a string corresponding to the timed value notification frequency, or "OFF" if timed
   *         value notifications are disabled for this function
   *
   * On failure, throws an exception or returns YSensor.REPORTFREQUENCY_INVALID.
   */
  async get_reportFrequency() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YSensor.REPORTFREQUENCY_INVALID;
      }
    }
    res = this._reportFrequency;
    return res;
  }
  /**
   * Changes the timed value notification frequency for this function.
   * The frequency can be specified as samples per second,
   * as sample per minute (for instance "15/m") or in samples per
   * hour (e.g. "4/h"). To disable timed value notifications for this
   * function, use the value "OFF". Note that setting the  timed value
   * notification frequency to a greater value than the sensor native
   * sampling frequency is unless, and even counterproductive: those two
   * frequencies are not related.
   * Remember to call the saveToFlash() method of the module if the modification must be kept.
   *
   * @param newval : a string corresponding to the timed value notification frequency for this function
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_reportFrequency(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("reportFrequency", rest_val);
  }
  /**
   * Returns the measuring mode used for the advertised value pushed to the parent hub.
   *
   * @return a value among YSensor.ADVMODE_IMMEDIATE, YSensor.ADVMODE_PERIOD_AVG,
   * YSensor.ADVMODE_PERIOD_MIN and YSensor.ADVMODE_PERIOD_MAX corresponding to the measuring mode used
   * for the advertised value pushed to the parent hub
   *
   * On failure, throws an exception or returns YSensor.ADVMODE_INVALID.
   */
  async get_advMode() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YSensor.ADVMODE_INVALID;
      }
    }
    res = this._advMode;
    return res;
  }
  /**
   * Changes the measuring mode used for the advertised value pushed to the parent hub.
   * Remember to call the saveToFlash() method of the module if the modification must be kept.
   *
   * @param newval : a value among YSensor.ADVMODE_IMMEDIATE, YSensor.ADVMODE_PERIOD_AVG,
   * YSensor.ADVMODE_PERIOD_MIN and YSensor.ADVMODE_PERIOD_MAX corresponding to the measuring mode used
   * for the advertised value pushed to the parent hub
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_advMode(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("advMode", rest_val);
  }
  async get_calibrationParam() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YSensor.CALIBRATIONPARAM_INVALID;
      }
    }
    res = this._calibrationParam;
    return res;
  }
  async set_calibrationParam(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("calibrationParam", rest_val);
  }
  /**
   * Changes the resolution of the measured physical values. The resolution corresponds to the numerical precision
   * when displaying value. It does not change the precision of the measure itself.
   * Remember to call the saveToFlash() method of the module if the modification must be kept.
   *
   * @param newval : a floating point number corresponding to the resolution of the measured physical values
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_resolution(newval) {
    let rest_val;
    rest_val = String(Math.round(newval * 65536));
    return await this._setAttr("resolution", rest_val);
  }
  /**
   * Returns the resolution of the measured values. The resolution corresponds to the numerical precision
   * of the measures, which is not always the same as the actual precision of the sensor.
   * Remember to call the saveToFlash() method of the module if the modification must be kept.
   *
   * @return a floating point number corresponding to the resolution of the measured values
   *
   * On failure, throws an exception or returns YSensor.RESOLUTION_INVALID.
   */
  async get_resolution() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YSensor.RESOLUTION_INVALID;
      }
    }
    res = this._resolution;
    return res;
  }
  /**
   * Returns the sensor state code, which is zero when there is an up-to-date measure
   * available or a positive code if the sensor is not able to provide a measure right now.
   *
   * @return an integer corresponding to the sensor state code, which is zero when there is an up-to-date measure
   *         available or a positive code if the sensor is not able to provide a measure right now
   *
   * On failure, throws an exception or returns YSensor.SENSORSTATE_INVALID.
   */
  async get_sensorState() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YSensor.SENSORSTATE_INVALID;
      }
    }
    res = this._sensorState;
    return res;
  }
  /**
   * Retrieves a sensor for a given identifier.
   * The identifier can be specified using several formats:
   *
   * - FunctionLogicalName
   * - ModuleSerialNumber.FunctionIdentifier
   * - ModuleSerialNumber.FunctionLogicalName
   * - ModuleLogicalName.FunctionIdentifier
   * - ModuleLogicalName.FunctionLogicalName
   *
   *
   * This function does not require that the sensor is online at the time
   * it is invoked. The returned object is nevertheless valid.
   * Use the method YSensor.isOnline() to test if the sensor is
   * indeed online at a given time. In case of ambiguity when looking for
   * a sensor by logical name, no error is notified: the first instance
   * found is returned. The search is performed first by hardware name,
   * then by logical name.
   *
   * If a call to this object's is_online() method returns FALSE although
   * you are certain that the matching device is plugged, make sure that you did
   * call registerHub() at application initialization time.
   *
   * @param func : a string that uniquely characterizes the sensor, for instance
   *         MyDevice..
   *
   * @return a YSensor object allowing you to drive the sensor.
   */
  static FindSensor(func) {
    let obj;
    obj = YFunction._FindFromCache("Sensor", func);
    if (obj == null) {
      obj = new _YSensor(YAPI, func);
      YFunction._AddToCache("Sensor", func, obj);
    }
    return obj;
  }
  /**
   * Retrieves a sensor for a given identifier in a YAPI context.
   * The identifier can be specified using several formats:
   *
   * - FunctionLogicalName
   * - ModuleSerialNumber.FunctionIdentifier
   * - ModuleSerialNumber.FunctionLogicalName
   * - ModuleLogicalName.FunctionIdentifier
   * - ModuleLogicalName.FunctionLogicalName
   *
   *
   * This function does not require that the sensor is online at the time
   * it is invoked. The returned object is nevertheless valid.
   * Use the method YSensor.isOnline() to test if the sensor is
   * indeed online at a given time. In case of ambiguity when looking for
   * a sensor by logical name, no error is notified: the first instance
   * found is returned. The search is performed first by hardware name,
   * then by logical name.
   *
   * @param yctx : a YAPI context
   * @param func : a string that uniquely characterizes the sensor, for instance
   *         MyDevice..
   *
   * @return a YSensor object allowing you to drive the sensor.
   */
  static FindSensorInContext(yctx, func) {
    let obj;
    obj = YFunction._FindFromCacheInContext(yctx, "Sensor", func);
    if (obj == null) {
      obj = new _YSensor(yctx, func);
      YFunction._AddToCache("Sensor", func, obj);
    }
    return obj;
  }
  /**
   * Registers the callback function that is invoked on every change of advertised value.
   * The callback is called once when it is registered, passing the current advertised value
   * of the function, provided that it is not an empty string.
   * The callback is then invoked only during the execution of ySleep or yHandleEvents.
   * This provides control over the time when the callback is triggered. For good responsiveness, remember to call
   * one of these two functions periodically. To unregister a callback, pass a null pointer as argument.
   *
   * @param callback : the callback function to call, or a null pointer. The callback function should take two
   *         arguments: the function object of which the value has changed, and the character string describing
   *         the new advertised value.
   * @noreturn
   */
  async registerValueCallback(callback) {
    let val;
    if (callback != null) {
      await YFunction._UpdateValueCallbackList(this, true);
    } else {
      await YFunction._UpdateValueCallbackList(this, false);
    }
    this._valueCallbackSensor = callback;
    if (callback != null && await this.isOnline()) {
      val = this._advertisedValue;
      if (!(val == "")) {
        await this._invokeValueCallback(val);
      }
    }
    return 0;
  }
  async _invokeValueCallback(value) {
    if (this._valueCallbackSensor != null) {
      try {
        await this._valueCallbackSensor(this, value);
      } catch (e) {
        this._yapi.imm_log("Exception in valueCallback:", e);
      }
    } else {
      await super._invokeValueCallback(value);
    }
    return 0;
  }
  _parserHelper() {
    let calibStr;
    if (this._resolution > 0) {
      this._iresol = Math.round(1 / this._resolution);
    } else {
      this._iresol = 1e4;
    }
    calibStr = this._calibrationParam;
    if (calibStr == "0," || calibStr == "" || calibStr == "0") {
      this._cal = null;
      return 0;
    }
    if (this._cal == null || !(this._cal.src == calibStr)) {
      this._parseCalibStr(calibStr);
    }
    return 0;
  }
  /**
   * Checks if the sensor is currently able to provide an up-to-date measure.
   * Returns false if the device is unreachable, or if the sensor does not have
   * a current measure to transmit. No exception is raised if there is an error
   * while trying to contact the device hosting $THEFUNCTION$.
   *
   * @return true if the sensor can provide an up-to-date measure, and false otherwise
   */
  async isSensorReady() {
    try {
      if (await this.get_sensorState() != 0) {
        return false;
      }
    } catch (e) {
      return false;
    }
    return true;
  }
  /**
   * Returns the YDatalogger object of the device hosting the sensor. This method returns an object
   * that can control global parameters of the data logger. The returned object
   * should not be freed.
   *
   * @return an YDatalogger object, or null on error.
   */
  async get_dataLogger() {
    let logger;
    let modu;
    let serial;
    let hwid;
    modu = await this.get_module();
    serial = await modu.get_serialNumber();
    if (serial == YAPI_INVALID_STRING) {
      return null;
    }
    hwid = serial + ".dataLogger";
    logger = YDataLogger.FindDataLogger(hwid);
    return logger;
  }
  _parseCalibStr(calibStr) {
    let iCalib = [];
    let caltyp;
    let calhdl;
    let maxpos;
    let position;
    let calpar = [];
    let calraw = [];
    let calref = [];
    let fRaw;
    let fRef;
    let iRaw;
    let iRef;
    if (calibStr.indexOf(",") >= 0) {
      iCalib = this._yapi.imm_decodeFloats(calibStr);
      caltyp = iCalib[0] / 1e3 >> 0;
      if (caltyp < YOCTO_CALIB_TYPE_OFS) {
        this._cal = null;
        return YAPI_SUCCESS;
      }
      calhdl = this._yapi.imm_getCalibrationHandler(caltyp);
      if (!(calhdl != null)) {
        this._cal = null;
        return YAPI_SUCCESS;
      }
      maxpos = iCalib.length;
      calpar.length = 0;
      position = 1;
      while (position < maxpos) {
        calpar.push(iCalib[position]);
        position = position + 1;
      }
      calraw.length = 0;
      calref.length = 0;
      position = 1;
      while (position + 1 < maxpos) {
        fRaw = iCalib[position];
        fRaw = fRaw / 1e3;
        fRef = iCalib[position + 1];
        fRef = fRef / 1e3;
        calraw.push(fRaw);
        calref.push(fRef);
        position = position + 2;
      }
    } else {
      iCalib = this._yapi.imm_decodeWords(calibStr);
      if (iCalib.length <= 2) {
        this._cal = null;
        return YAPI_SUCCESS;
      }
      caltyp = iCalib[2];
      calhdl = this._yapi.imm_getCalibrationHandler(caltyp);
      if (!(calhdl != null)) {
        this._cal = null;
        return YAPI_SUCCESS;
      }
      if (caltyp <= 10) {
        maxpos = caltyp;
      } else {
        if (caltyp <= 20) {
          maxpos = caltyp - 10;
        } else {
          maxpos = 5;
        }
      }
      maxpos = 3 + 2 * maxpos;
      if (maxpos > iCalib.length) {
        maxpos = iCalib.length;
      }
      calpar.length = 0;
      calraw.length = 0;
      calref.length = 0;
      position = 3;
      while (position + 1 < maxpos) {
        iRaw = iCalib[position];
        iRef = iCalib[position + 1];
        calpar.push(iRaw);
        calpar.push(iRef);
        calraw.push(this._yapi.imm_decimalToDouble(iRaw));
        calref.push(this._yapi.imm_decimalToDouble(iRef));
        position = position + 2;
      }
    }
    this._cal = { src: calibStr, hdl: calhdl, typ: caltyp, par: calpar, raw: calraw, cal: calref };
    return YAPI_SUCCESS;
  }
  /**
   * Starts the data logger on the device. Note that the data logger
   * will only save the measures on this sensor if the logFrequency
   * is not set to "OFF".
   *
   * @return YAPI.SUCCESS if the call succeeds.
   */
  async startDataLogger() {
    let res;
    res = await this._download("api/dataLogger/recording?recording=1");
    if (!(res.length > 0)) {
      return this._throw(YAPI_IO_ERROR, "unable to start datalogger", YAPI_IO_ERROR);
    }
    return YAPI_SUCCESS;
  }
  /**
   * Stops the datalogger on the device.
   *
   * @return YAPI.SUCCESS if the call succeeds.
   */
  async stopDataLogger() {
    let res;
    res = await this._download("api/dataLogger/recording?recording=0");
    if (!(res.length > 0)) {
      return this._throw(YAPI_IO_ERROR, "unable to stop datalogger", YAPI_IO_ERROR);
    }
    return YAPI_SUCCESS;
  }
  /**
   * Retrieves a YDataSet object holding historical data for this
   * sensor, for a specified time interval. The measures will be
   * retrieved from the data logger, which must have been turned
   * on at the desired time. See the documentation of the YDataSet
   * class for information on how to get an overview of the
   * recorded data, and how to load progressively a large set
   * of measures from the data logger.
   *
   * This function only works if the device uses a recent firmware,
   * as YDataSet objects are not supported by firmwares older than
   * version 13000.
   *
   * @param startTime : the start of the desired measure time interval,
   *         as a Unix timestamp, i.e. the number of seconds since
   *         January 1, 1970 UTC. The special value 0 can be used
   *         to include any measure, without initial limit.
   * @param endTime : the end of the desired measure time interval,
   *         as a Unix timestamp, i.e. the number of seconds since
   *         January 1, 1970 UTC. The special value 0 can be used
   *         to include any measure, without ending limit.
   *
   * @return an instance of YDataSet, providing access to historical
   *         data. Past measures can be loaded progressively
   *         using methods from the YDataSet object.
   */
  async get_recordedData(startTime, endTime) {
    let funcid;
    let funit;
    funcid = await this.get_functionId();
    funit = await this.get_unit();
    return new YDataSet(this, funcid, funit, startTime, endTime);
  }
  /**
   * Registers the callback function that is invoked on every periodic timed notification.
   * The callback is invoked only during the execution of ySleep or yHandleEvents.
   * This provides control over the time when the callback is triggered. For good responsiveness, remember to call
   * one of these two functions periodically. To unregister a callback, pass a null pointer as argument.
   *
   * @param callback : the callback function to call, or a null pointer. The callback function should take two
   *         arguments: the function object of which the value has changed, and an YMeasure object describing
   *         the new advertised value.
   * @noreturn
   */
  async registerTimedReportCallback(callback) {
    let sensor;
    sensor = this;
    if (callback != null) {
      await YFunction._UpdateTimedReportCallbackList(sensor, true);
    } else {
      await YFunction._UpdateTimedReportCallbackList(sensor, false);
    }
    this._timedReportCallbackSensor = callback;
    return 0;
  }
  async _invokeTimedReportCallback(value) {
    if (this._timedReportCallbackSensor != null) {
      try {
        await this._timedReportCallbackSensor(this, value);
      } catch (e) {
        this._yapi.imm_log("Exception in timedReportCallback:", e);
      }
    } else {
    }
    return 0;
  }
  /**
   * Configures error correction data points, in particular to compensate for
   * a possible perturbation of the measure caused by an enclosure. It is possible
   * to configure up to five correction points. Correction points must be provided
   * in ascending order, and be in the range of the sensor. The device will automatically
   * perform a linear interpolation of the error correction between specified
   * points. Remember to call the saveToFlash() method of the module if the
   * modification must be kept.
   *
   * For more information on advanced capabilities to refine the calibration of
   * sensors, please contact support@yoctopuce.com.
   *
   * @param rawValues : array of floating point numbers, corresponding to the raw
   *         values returned by the sensor for the correction points.
   * @param refValues : array of floating point numbers, corresponding to the corrected
   *         values for the correction points.
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async calibrateFromPoints(rawValues, refValues) {
    let rest_val;
    let res;
    rest_val = await this._encodeCalibrationPoints(rawValues, refValues);
    res = await this._setAttr("calibrationParam", rest_val);
    return res;
  }
  /**
   * Retrieves error correction data points previously entered using the method
   * calibrateFromPoints.
   *
   * @param rawValues : array of floating point numbers, that will be filled by the
   *         function with the raw sensor values for the correction points.
   * @param refValues : array of floating point numbers, that will be filled by the
   *         function with the desired values for the correction points.
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async loadCalibrationPoints(rawValues, refValues) {
    rawValues.length = 0;
    refValues.length = 0;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return YAPI_DEVICE_NOT_FOUND;
      }
    }
    if (this._cal == null) {
      return YAPI_SUCCESS;
    }
    rawValues.length = 0;
    refValues.length = 0;
    for (let ii_0 of this._cal.raw) {
      rawValues.push(ii_0);
    }
    for (let ii_1 of this._cal.cal) {
      refValues.push(ii_1);
    }
    return YAPI_SUCCESS;
  }
  async _encodeCalibrationPoints(rawValues, refValues) {
    let res;
    let npt;
    let idx;
    npt = rawValues.length;
    if (npt != refValues.length) {
      this._throw(YAPI_INVALID_ARGUMENT, "Invalid calibration parameters (size mismatch)");
      return YAPI_INVALID_STRING;
    }
    if (npt == 0) {
      return "0";
    }
    res = String(Math.round(YOCTO_CALIB_TYPE_OFS));
    idx = 0;
    while (idx < npt) {
      res = res + "," + String(Math.round(rawValues[idx] * 1e3) / 1e3) + "," + String(Math.round(refValues[idx] * 1e3) / 1e3);
      idx = idx + 1;
    }
    return res;
  }
  async _applyCalibration(rawValue) {
    if (this._cal == null) {
      return rawValue;
    }
    if (rawValue == _YSensor.CURRENTVALUE_INVALID) {
      return _YSensor.CURRENTVALUE_INVALID;
    }
    return this._cal.hdl(rawValue, this._cal.typ, this._cal.par, this._cal.raw, this._cal.cal);
  }
  async _decodeTimedReport(timestamp, duration, report) {
    let i;
    let byteVal;
    let poww;
    let minRaw;
    let avgRaw;
    let maxRaw;
    let sublen;
    let difRaw;
    let startTime;
    let endTime;
    let minVal;
    let avgVal;
    let maxVal;
    if (duration > 0) {
      startTime = timestamp - duration;
    } else {
      startTime = this._prevTR;
    }
    endTime = timestamp;
    this._prevTR = endTime;
    if (startTime == 0) {
      startTime = endTime;
    }
    if (report.length <= 5) {
      poww = 1;
      avgRaw = 0;
      byteVal = 0;
      i = 1;
      while (i < report.length) {
        byteVal = report[i];
        avgRaw = avgRaw + poww * byteVal;
        poww = poww * 256;
        i = i + 1;
      }
      if ((byteVal & 128) != 0) {
        avgRaw = avgRaw - poww;
      }
      avgVal = avgRaw / 1e3;
      if (!(this._cal == null)) {
        avgVal = this._cal.hdl(avgVal, this._cal.typ, this._cal.par, this._cal.raw, this._cal.cal);
      }
      minVal = avgVal;
      maxVal = avgVal;
    } else {
      sublen = 1 + (report[1] & 3);
      poww = 1;
      avgRaw = 0;
      byteVal = 0;
      i = 2;
      while (sublen > 0 && i < report.length) {
        byteVal = report[i];
        avgRaw = avgRaw + poww * byteVal;
        poww = poww * 256;
        i = i + 1;
        sublen = sublen - 1;
      }
      if ((byteVal & 128) != 0) {
        avgRaw = avgRaw - poww;
      }
      sublen = 1 + (report[1] >> 2 & 3);
      poww = 1;
      difRaw = 0;
      while (sublen > 0 && i < report.length) {
        byteVal = report[i];
        difRaw = difRaw + poww * byteVal;
        poww = poww * 256;
        i = i + 1;
        sublen = sublen - 1;
      }
      minRaw = avgRaw - difRaw;
      sublen = 1 + (report[1] >> 4 & 3);
      poww = 1;
      difRaw = 0;
      while (sublen > 0 && i < report.length) {
        byteVal = report[i];
        difRaw = difRaw + poww * byteVal;
        poww = poww * 256;
        i = i + 1;
        sublen = sublen - 1;
      }
      maxRaw = avgRaw + difRaw;
      avgVal = avgRaw / 1e3;
      minVal = minRaw / 1e3;
      maxVal = maxRaw / 1e3;
      if (!(this._cal == null)) {
        avgVal = this._cal.hdl(avgVal, this._cal.typ, this._cal.par, this._cal.raw, this._cal.cal);
        minVal = this._cal.hdl(minVal, this._cal.typ, this._cal.par, this._cal.raw, this._cal.cal);
        maxVal = this._cal.hdl(maxVal, this._cal.typ, this._cal.par, this._cal.raw, this._cal.cal);
      }
    }
    return new YMeasure(startTime, endTime, minVal, avgVal, maxVal);
  }
  imm_decodeVal(w) {
    let val;
    val = w;
    if (!(this._cal == null)) {
      val = this._cal.hdl(val, this._cal.typ, this._cal.par, this._cal.raw, this._cal.cal);
    }
    return val;
  }
  imm_decodeAvg(dw) {
    let val;
    val = dw;
    if (!(this._cal == null)) {
      val = this._cal.hdl(val, this._cal.typ, this._cal.par, this._cal.raw, this._cal.cal);
    }
    return val;
  }
  /**
   * Continues the enumeration of sensors started using yFirstSensor().
   * Caution: You can't make any assumption about the returned sensors order.
   * If you want to find a specific a sensor, use Sensor.findSensor()
   * and a hardwareID or a logical name.
   *
   * @return a pointer to a YSensor object, corresponding to
   *         a sensor currently online, or a null pointer
   *         if there are no more sensors to enumerate.
   */
  nextSensor() {
    let resolve = this._yapi.imm_resolveFunction(this._className, this._func);
    if (resolve.errorType != YAPI.SUCCESS)
      return null;
    let next_hwid = this._yapi.imm_getNextHardwareId(this._className, resolve.result);
    if (next_hwid == null)
      return null;
    return _YSensor.FindSensorInContext(this._yapi, next_hwid);
  }
  /**
   * Starts the enumeration of sensors currently accessible.
   * Use the method YSensor.nextSensor() to iterate on
   * next sensors.
   *
   * @return a pointer to a YSensor object, corresponding to
   *         the first sensor currently online, or a null pointer
   *         if there are none.
   */
  static FirstSensor() {
    let next_hwid = YAPI.imm_getFirstHardwareId("Sensor");
    if (next_hwid == null)
      return null;
    return _YSensor.FindSensor(next_hwid);
  }
  /**
   * Starts the enumeration of sensors currently accessible.
   * Use the method YSensor.nextSensor() to iterate on
   * next sensors.
   *
   * @param yctx : a YAPI context.
   *
   * @return a pointer to a YSensor object, corresponding to
   *         the first sensor currently online, or a null pointer
   *         if there are none.
   */
  static FirstSensorInContext(yctx) {
    let next_hwid = yctx.imm_getFirstHardwareId("Sensor");
    if (next_hwid == null)
      return null;
    return _YSensor.FindSensorInContext(yctx, next_hwid);
  }
};
YSensor.UNIT_INVALID = YAPI_INVALID_STRING;
YSensor.CURRENTVALUE_INVALID = YAPI_INVALID_DOUBLE;
YSensor.LOWESTVALUE_INVALID = YAPI_INVALID_DOUBLE;
YSensor.HIGHESTVALUE_INVALID = YAPI_INVALID_DOUBLE;
YSensor.CURRENTRAWVALUE_INVALID = YAPI_INVALID_DOUBLE;
YSensor.LOGFREQUENCY_INVALID = YAPI_INVALID_STRING;
YSensor.REPORTFREQUENCY_INVALID = YAPI_INVALID_STRING;
YSensor.ADVMODE_IMMEDIATE = 0;
YSensor.ADVMODE_PERIOD_AVG = 1;
YSensor.ADVMODE_PERIOD_MIN = 2;
YSensor.ADVMODE_PERIOD_MAX = 3;
YSensor.ADVMODE_INVALID = -1;
YSensor.CALIBRATIONPARAM_INVALID = YAPI_INVALID_STRING;
YSensor.RESOLUTION_INVALID = YAPI_INVALID_DOUBLE;
YSensor.SENSORSTATE_INVALID = YAPI_INVALID_INT;
var YMeasure = class {
  // API symbols as static members
  //--- (end of generated code: YMeasure attributes declaration)
  constructor(float_start, float_end, float_minVal, float_avgVal, float_maxVal) {
    this._start = 0;
    this._end = 0;
    this._minVal = 0;
    this._avgVal = 0;
    this._maxVal = 0;
    this._start = float_start;
    this._end = float_end;
    this._minVal = float_minVal;
    this._avgVal = float_avgVal;
    this._maxVal = float_maxVal;
  }
  //--- (generated code: YMeasure implementation)
  /**
   * Returns the start time of the measure, relative to the Jan 1, 1970 UTC
   * (Unix timestamp). When the recording rate is higher then 1 sample
   * per second, the timestamp may have a fractional part.
   *
   * @return a floating point number corresponding to the number of seconds
   *         between the Jan 1, 1970 UTC and the beginning of this measure.
   */
  get_startTimeUTC() {
    return this._start;
  }
  /**
   * Returns the end time of the measure, relative to the Jan 1, 1970 UTC
   * (Unix timestamp). When the recording rate is higher than 1 sample
   * per second, the timestamp may have a fractional part.
   *
   * @return a floating point number corresponding to the number of seconds
   *         between the Jan 1, 1970 UTC and the end of this measure.
   */
  get_endTimeUTC() {
    return this._end;
  }
  /**
   * Returns the smallest value observed during the time interval
   * covered by this measure.
   *
   * @return a floating-point number corresponding to the smallest value observed.
   */
  get_minValue() {
    return this._minVal;
  }
  /**
   * Returns the average value observed during the time interval
   * covered by this measure.
   *
   * @return a floating-point number corresponding to the average value observed.
   */
  get_averageValue() {
    return this._avgVal;
  }
  /**
   * Returns the largest value observed during the time interval
   * covered by this measure.
   *
   * @return a floating-point number corresponding to the largest value observed.
   */
  get_maxValue() {
    return this._maxVal;
  }
  //--- (end of generated code: YMeasure implementation)
  /**
   * Returns the start date of the measure.
   *
   * @return {Date} a Date object corresponding to the beginning of this measure
   */
  get_startTimeUTC_asDate() {
    return new Date(Math.round(this._start * 1e3));
  }
  /**
   * Returns the start date of the measure.
   *
   * @return {Date} a Date object corresponding to the end of this measure
   */
  get_endTimeUTC_asDate() {
    return new Date(Math.round(this._end * 1e3));
  }
};
var YDataLogger = class _YDataLogger extends YFunction {
  //--- (end of generated code: YDataLogger attributes declaration)
  constructor(yapi, func) {
    super(yapi, func);
    this._currentRunIndex = _YDataLogger.CURRENTRUNINDEX_INVALID;
    this._timeUTC = _YDataLogger.TIMEUTC_INVALID;
    this._recording = _YDataLogger.RECORDING_INVALID;
    this._autoStart = _YDataLogger.AUTOSTART_INVALID;
    this._beaconDriven = _YDataLogger.BEACONDRIVEN_INVALID;
    this._usage = _YDataLogger.USAGE_INVALID;
    this._clearHistory = _YDataLogger.CLEARHISTORY_INVALID;
    this._valueCallbackDataLogger = null;
    this.CURRENTRUNINDEX_INVALID = YAPI_INVALID_UINT;
    this.TIMEUTC_INVALID = YAPI_INVALID_LONG;
    this.RECORDING_OFF = 0;
    this.RECORDING_ON = 1;
    this.RECORDING_PENDING = 2;
    this.RECORDING_INVALID = -1;
    this.AUTOSTART_OFF = 0;
    this.AUTOSTART_ON = 1;
    this.AUTOSTART_INVALID = -1;
    this.BEACONDRIVEN_OFF = 0;
    this.BEACONDRIVEN_ON = 1;
    this.BEACONDRIVEN_INVALID = -1;
    this.USAGE_INVALID = YAPI_INVALID_UINT;
    this.CLEARHISTORY_FALSE = 0;
    this.CLEARHISTORY_TRUE = 1;
    this.CLEARHISTORY_INVALID = -1;
    this._className = "DataLogger";
  }
  //--- (generated code: YDataLogger implementation)
  imm_parseAttr(name, val) {
    switch (name) {
      case "currentRunIndex":
        this._currentRunIndex = val;
        return 1;
      case "timeUTC":
        this._timeUTC = val;
        return 1;
      case "recording":
        this._recording = val;
        return 1;
      case "autoStart":
        this._autoStart = val;
        return 1;
      case "beaconDriven":
        this._beaconDriven = val;
        return 1;
      case "usage":
        this._usage = val;
        return 1;
      case "clearHistory":
        this._clearHistory = val;
        return 1;
    }
    return super.imm_parseAttr(name, val);
  }
  /**
   * Returns the current run number, corresponding to the number of times the module was
   * powered on with the dataLogger enabled at some point.
   *
   * @return an integer corresponding to the current run number, corresponding to the number of times the module was
   *         powered on with the dataLogger enabled at some point
   *
   * On failure, throws an exception or returns YDataLogger.CURRENTRUNINDEX_INVALID.
   */
  async get_currentRunIndex() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YDataLogger.CURRENTRUNINDEX_INVALID;
      }
    }
    res = this._currentRunIndex;
    return res;
  }
  /**
   * Returns the Unix timestamp for current UTC time, if known.
   *
   * @return an integer corresponding to the Unix timestamp for current UTC time, if known
   *
   * On failure, throws an exception or returns YDataLogger.TIMEUTC_INVALID.
   */
  async get_timeUTC() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YDataLogger.TIMEUTC_INVALID;
      }
    }
    res = this._timeUTC;
    return res;
  }
  /**
   * Changes the current UTC time reference used for recorded data.
   *
   * @param newval : an integer corresponding to the current UTC time reference used for recorded data
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_timeUTC(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("timeUTC", rest_val);
  }
  /**
   * Returns the current activation state of the data logger.
   *
   * @return a value among YDataLogger.RECORDING_OFF, YDataLogger.RECORDING_ON and
   * YDataLogger.RECORDING_PENDING corresponding to the current activation state of the data logger
   *
   * On failure, throws an exception or returns YDataLogger.RECORDING_INVALID.
   */
  async get_recording() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YDataLogger.RECORDING_INVALID;
      }
    }
    res = this._recording;
    return res;
  }
  /**
   * Changes the activation state of the data logger to start/stop recording data.
   *
   * @param newval : a value among YDataLogger.RECORDING_OFF, YDataLogger.RECORDING_ON and
   * YDataLogger.RECORDING_PENDING corresponding to the activation state of the data logger to
   * start/stop recording data
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_recording(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("recording", rest_val);
  }
  /**
   * Returns the default activation state of the data logger on power up.
   *
   * @return either YDataLogger.AUTOSTART_OFF or YDataLogger.AUTOSTART_ON, according to the default
   * activation state of the data logger on power up
   *
   * On failure, throws an exception or returns YDataLogger.AUTOSTART_INVALID.
   */
  async get_autoStart() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YDataLogger.AUTOSTART_INVALID;
      }
    }
    res = this._autoStart;
    return res;
  }
  /**
   * Changes the default activation state of the data logger on power up.
   * Do not forget to call the saveToFlash() method of the module to save the
   * configuration change.  Note: if the device doesn't have any time source at his disposal when
   * starting up, it will wait for ~8 seconds before automatically starting to record  with
   * an arbitrary timestamp
   *
   * @param newval : either YDataLogger.AUTOSTART_OFF or YDataLogger.AUTOSTART_ON, according to the
   * default activation state of the data logger on power up
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_autoStart(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("autoStart", rest_val);
  }
  /**
   * Returns true if the data logger is synchronised with the localization beacon.
   *
   * @return either YDataLogger.BEACONDRIVEN_OFF or YDataLogger.BEACONDRIVEN_ON, according to true if
   * the data logger is synchronised with the localization beacon
   *
   * On failure, throws an exception or returns YDataLogger.BEACONDRIVEN_INVALID.
   */
  async get_beaconDriven() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YDataLogger.BEACONDRIVEN_INVALID;
      }
    }
    res = this._beaconDriven;
    return res;
  }
  /**
   * Changes the type of synchronisation of the data logger.
   * Remember to call the saveToFlash() method of the module if the
   * modification must be kept.
   *
   * @param newval : either YDataLogger.BEACONDRIVEN_OFF or YDataLogger.BEACONDRIVEN_ON, according to
   * the type of synchronisation of the data logger
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_beaconDriven(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("beaconDriven", rest_val);
  }
  /**
   * Returns the percentage of datalogger memory in use.
   *
   * @return an integer corresponding to the percentage of datalogger memory in use
   *
   * On failure, throws an exception or returns YDataLogger.USAGE_INVALID.
   */
  async get_usage() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YDataLogger.USAGE_INVALID;
      }
    }
    res = this._usage;
    return res;
  }
  async get_clearHistory() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != YAPI_SUCCESS) {
        return _YDataLogger.CLEARHISTORY_INVALID;
      }
    }
    res = this._clearHistory;
    return res;
  }
  async set_clearHistory(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("clearHistory", rest_val);
  }
  /**
   * Retrieves a data logger for a given identifier.
   * The identifier can be specified using several formats:
   *
   * - FunctionLogicalName
   * - ModuleSerialNumber.FunctionIdentifier
   * - ModuleSerialNumber.FunctionLogicalName
   * - ModuleLogicalName.FunctionIdentifier
   * - ModuleLogicalName.FunctionLogicalName
   *
   *
   * This function does not require that the data logger is online at the time
   * it is invoked. The returned object is nevertheless valid.
   * Use the method YDataLogger.isOnline() to test if the data logger is
   * indeed online at a given time. In case of ambiguity when looking for
   * a data logger by logical name, no error is notified: the first instance
   * found is returned. The search is performed first by hardware name,
   * then by logical name.
   *
   * If a call to this object's is_online() method returns FALSE although
   * you are certain that the matching device is plugged, make sure that you did
   * call registerHub() at application initialization time.
   *
   * @param func : a string that uniquely characterizes the data logger, for instance
   *         LIGHTMK4.dataLogger.
   *
   * @return a YDataLogger object allowing you to drive the data logger.
   */
  static FindDataLogger(func) {
    let obj;
    obj = YFunction._FindFromCache("DataLogger", func);
    if (obj == null) {
      obj = new _YDataLogger(YAPI, func);
      YFunction._AddToCache("DataLogger", func, obj);
    }
    return obj;
  }
  /**
   * Retrieves a data logger for a given identifier in a YAPI context.
   * The identifier can be specified using several formats:
   *
   * - FunctionLogicalName
   * - ModuleSerialNumber.FunctionIdentifier
   * - ModuleSerialNumber.FunctionLogicalName
   * - ModuleLogicalName.FunctionIdentifier
   * - ModuleLogicalName.FunctionLogicalName
   *
   *
   * This function does not require that the data logger is online at the time
   * it is invoked. The returned object is nevertheless valid.
   * Use the method YDataLogger.isOnline() to test if the data logger is
   * indeed online at a given time. In case of ambiguity when looking for
   * a data logger by logical name, no error is notified: the first instance
   * found is returned. The search is performed first by hardware name,
   * then by logical name.
   *
   * @param yctx : a YAPI context
   * @param func : a string that uniquely characterizes the data logger, for instance
   *         LIGHTMK4.dataLogger.
   *
   * @return a YDataLogger object allowing you to drive the data logger.
   */
  static FindDataLoggerInContext(yctx, func) {
    let obj;
    obj = YFunction._FindFromCacheInContext(yctx, "DataLogger", func);
    if (obj == null) {
      obj = new _YDataLogger(yctx, func);
      YFunction._AddToCache("DataLogger", func, obj);
    }
    return obj;
  }
  /**
   * Registers the callback function that is invoked on every change of advertised value.
   * The callback is called once when it is registered, passing the current advertised value
   * of the function, provided that it is not an empty string.
   * The callback is then invoked only during the execution of ySleep or yHandleEvents.
   * This provides control over the time when the callback is triggered. For good responsiveness, remember to call
   * one of these two functions periodically. To unregister a callback, pass a null pointer as argument.
   *
   * @param callback : the callback function to call, or a null pointer. The callback function should take two
   *         arguments: the function object of which the value has changed, and the character string describing
   *         the new advertised value.
   * @noreturn
   */
  async registerValueCallback(callback) {
    let val;
    if (callback != null) {
      await YFunction._UpdateValueCallbackList(this, true);
    } else {
      await YFunction._UpdateValueCallbackList(this, false);
    }
    this._valueCallbackDataLogger = callback;
    if (callback != null && await this.isOnline()) {
      val = this._advertisedValue;
      if (!(val == "")) {
        await this._invokeValueCallback(val);
      }
    }
    return 0;
  }
  async _invokeValueCallback(value) {
    if (this._valueCallbackDataLogger != null) {
      try {
        await this._valueCallbackDataLogger(this, value);
      } catch (e) {
        this._yapi.imm_log("Exception in valueCallback:", e);
      }
    } else {
      await super._invokeValueCallback(value);
    }
    return 0;
  }
  /**
   * Clears the data logger memory and discards all recorded data streams.
   * This method also resets the current run index to zero.
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async forgetAllDataStreams() {
    return await this.set_clearHistory(
      1
      /* YDataLogger.CLEARHISTORY.TRUE */
    );
  }
  /**
   * Returns a list of YDataSet objects that can be used to retrieve
   * all measures stored by the data logger.
   *
   * This function only works if the device uses a recent firmware,
   * as YDataSet objects are not supported by firmwares older than
   * version 13000.
   *
   * @return a list of YDataSet object.
   *
   * On failure, throws an exception or returns an empty list.
   */
  async get_dataSets() {
    return await this.parse_dataSets(await this._download("logger.json"));
  }
  async parse_dataSets(jsonbuff) {
    let dslist = [];
    let dataset;
    let res = [];
    dslist = this.imm_json_get_array(jsonbuff);
    res.length = 0;
    for (let ii_0 of dslist) {
      dataset = new YDataSet(this);
      await dataset._parse(this._yapi.imm_bin2str(ii_0));
      res.push(dataset);
    }
    return res;
  }
  /**
   * Continues the enumeration of data loggers started using yFirstDataLogger().
   * Caution: You can't make any assumption about the returned data loggers order.
   * If you want to find a specific a data logger, use DataLogger.findDataLogger()
   * and a hardwareID or a logical name.
   *
   * @return a pointer to a YDataLogger object, corresponding to
   *         a data logger currently online, or a null pointer
   *         if there are no more data loggers to enumerate.
   */
  nextDataLogger() {
    let resolve = this._yapi.imm_resolveFunction(this._className, this._func);
    if (resolve.errorType != YAPI.SUCCESS)
      return null;
    let next_hwid = this._yapi.imm_getNextHardwareId(this._className, resolve.result);
    if (next_hwid == null)
      return null;
    return _YDataLogger.FindDataLoggerInContext(this._yapi, next_hwid);
  }
  /**
   * Starts the enumeration of data loggers currently accessible.
   * Use the method YDataLogger.nextDataLogger() to iterate on
   * next data loggers.
   *
   * @return a pointer to a YDataLogger object, corresponding to
   *         the first data logger currently online, or a null pointer
   *         if there are none.
   */
  static FirstDataLogger() {
    let next_hwid = YAPI.imm_getFirstHardwareId("DataLogger");
    if (next_hwid == null)
      return null;
    return _YDataLogger.FindDataLogger(next_hwid);
  }
  /**
   * Starts the enumeration of data loggers currently accessible.
   * Use the method YDataLogger.nextDataLogger() to iterate on
   * next data loggers.
   *
   * @param yctx : a YAPI context.
   *
   * @return a pointer to a YDataLogger object, corresponding to
   *         the first data logger currently online, or a null pointer
   *         if there are none.
   */
  static FirstDataLoggerInContext(yctx) {
    let next_hwid = yctx.imm_getFirstHardwareId("DataLogger");
    if (next_hwid == null)
      return null;
    return _YDataLogger.FindDataLoggerInContext(yctx, next_hwid);
  }
};
YDataLogger.CURRENTRUNINDEX_INVALID = YAPI_INVALID_UINT;
YDataLogger.TIMEUTC_INVALID = YAPI_INVALID_LONG;
YDataLogger.RECORDING_OFF = 0;
YDataLogger.RECORDING_ON = 1;
YDataLogger.RECORDING_PENDING = 2;
YDataLogger.RECORDING_INVALID = -1;
YDataLogger.AUTOSTART_OFF = 0;
YDataLogger.AUTOSTART_ON = 1;
YDataLogger.AUTOSTART_INVALID = -1;
YDataLogger.BEACONDRIVEN_OFF = 0;
YDataLogger.BEACONDRIVEN_ON = 1;
YDataLogger.BEACONDRIVEN_INVALID = -1;
YDataLogger.USAGE_INVALID = YAPI_INVALID_UINT;
YDataLogger.CLEARHISTORY_FALSE = 0;
YDataLogger.CLEARHISTORY_TRUE = 1;
YDataLogger.CLEARHISTORY_INVALID = -1;
var YSystemEnv = class {
  constructor() {
    this.isNodeJS = false;
    this.hasSSDP = false;
  }
  unknownSystemEnvError() {
    return new YoctoError("Unspecified runtime environment, your project should include at least once either yocto_api_nodejs or yocto_api_html");
  }
  hookUnhandledRejection(handler) {
    throw this.unknownSystemEnvError();
  }
  getWebSocketEngine(hub, runtime_urlInfo) {
    throw this.unknownSystemEnvError();
  }
  getHttpEngine(hub, runtime_urlInfo, infojson) {
    throw this.unknownSystemEnvError();
  }
  getWebSocketCallbackEngine(hub, runtime_urlInfo, ws) {
    throw this.unknownSystemEnvError();
  }
  getHttpCallbackEngine(hub, runtime_urlInfo, incomingMessage, serverResponse) {
    throw this.unknownSystemEnvError();
  }
  getSSDPManager(obj_yapi) {
    throw this.unknownSystemEnvError();
  }
  async loadfile(file) {
    throw this.unknownSystemEnvError();
  }
  async downloadfile(url, yapi) {
    throw this.unknownSystemEnvError();
  }
  async downloadRemoteCertificate(urlinfo) {
    throw this.unknownSystemEnvError();
  }
};
var _UnspecifiedSystemEnv = new YSystemEnv();
var YHubEngine = class {
  constructor(hub, runtime_urlInfo) {
    this.lastPingStamp = 0;
    this._hub = hub;
    this._runtime_urlInfo = runtime_urlInfo;
  }
  /** Attempt to establish a connection to the hub asynchronously.
   *
   * On success, this method should call this.signalHubConnected()
   * On temporary failure, this method should call this.imm_signalHubDisconnected()
   * On fatal failure, this method should call this.imm_commonDisconnect()
   *
   * This method is supposed to be redefined by subclasses
   */
  async reconnectEngine(tryOpenID) {
  }
  imm_disconnectEngineNow(connID = "") {
  }
  /** Perform an HTTP query on the hub
   */
  async request(method, devUrl, obj_body, tcpchan) {
    let res = new YHTTPRequest(null);
    res.errorType = YAPI_NOT_SUPPORTED;
    res.errorMsg = "GenericHub subclass expected";
    return res;
  }
  // default implementation of function that reports a fatal error to the HTTP callback party
  async reportFailure(message) {
  }
  imm_updateLastPinfStamp() {
    this.lastPingStamp = Date.now();
  }
  imm_isConnected() {
    return Date.now() - this.lastPingStamp < this._hub.imm_getNetworkTimeout();
  }
  imm_isForwarded() {
    return false;
  }
  async waitForPendingQueries(ms_duration) {
  }
};
var HubMode;
(function(HubMode2) {
  HubMode2[HubMode2["LEGACY"] = 0] = "LEGACY";
  HubMode2[HubMode2["MIXED"] = 1] = "MIXED";
  HubMode2[HubMode2["SECURE"] = 2] = "SECURE";
  HubMode2[HubMode2["PROTO_UNKNOWN"] = 3] = "PROTO_UNKNOWN";
})(HubMode || (HubMode = {}));
var YGenericHub = class _YGenericHub {
  constructor(yapi, urlInfo) {
    this._lastErrorType = YAPI_IO_ERROR;
    this._lastErrorMsg = "Hub attachment has not been triggered";
    this.hubSerial = "";
    this.serialByYdx = [];
    this._currentState = -6;
    this._targetState = -5;
    this.currentConnID = "";
    this.connResolvers = [];
    this.disconnResolvers = [];
    this.retryDelay = 15;
    this._reconnectionTimer = null;
    this._rwAccess = null;
    this.keepTryingExpiration = 0;
    this.keepTryingTimeoutId = null;
    this.timeoutId = null;
    this.isNotifWorking = false;
    this.devListExpires = 0;
    this.notifPos = -1;
    this.notifCarryOver = "";
    this._firstArrivalCallback = true;
    this._missing = {};
    this._knownUrls = [];
    this._portInfo = [];
    this._usePureHTTP = false;
    this._yapi = yapi;
    this.urlInfo = urlInfo;
    this.stalledTimeoutMs = yapi._networkTimeoutMs;
    this._hubRef = _YGenericHub.globalHubRefCounter++;
    this._hubEngine = null;
    this._hubMode = HubMode.SECURE;
  }
  _throw(int_errType, str_errMsg, obj_retVal) {
    this._lastErrorType = int_errType;
    this._lastErrorMsg = str_errMsg;
    return this._yapi._throw(int_errType, str_errMsg, obj_retVal);
  }
  imm_isFirstArrivalCallback() {
    return this._firstArrivalCallback;
  }
  imm_setFirstArrivalCallback(isfirst) {
    this._firstArrivalCallback = isfirst;
  }
  imm_getNotifyPos() {
    return this.notifPos;
  }
  imm_getcurrentState() {
    return this._currentState;
  }
  imm_getCurrentConnID() {
    return this.currentConnID;
  }
  imm_setCurrentConnID(id) {
    this.currentConnID = id;
  }
  /**
   * Returns the numerical error code of the latest error with the function.
   * This method is mostly useful when using the Yoctopuce library with
   * exceptions disabled.
   *
   * @return a number corresponding to the code of the latest error that occurred while
   *         using the function object
   */
  get_errorType() {
    return this._lastErrorType;
  }
  /**
   * Returns the error message of the latest error with the function.
   * This method is mostly useful when using the Yoctopuce library with
   * exceptions disabled.
   *
   * @return a string corresponding to the latest error message that occured while
   *         using the function object
   */
  get_errorMessage() {
    if (this._lastErrorType == YAPI_SUCCESS) {
      return "";
    }
    return this._lastErrorMsg;
  }
  imm_forceUpdate() {
    this.devListExpires = this._yapi.GetTickCount();
  }
  imm_logrequest(method, devUrl, obj_body) {
    let msg = "Request: " + method + " " + devUrl;
    if (obj_body) {
      msg += " (file=" + obj_body.fname + ")";
    }
    this._yapi.imm_log(msg);
  }
  imm_setState(newState) {
    this._currentState = newState;
  }
  imm_setTargetState(newState) {
    this._targetState = newState;
  }
  imm_isDisconnecting() {
    return this._targetState <= -5;
  }
  imm_isDisconnected() {
    return this._targetState <= -5 && this._currentState <= -5;
  }
  imm_isPreOrRegistered() {
    return this._targetState >= 1;
  }
  // default implementation of isOnline
  imm_isOnline() {
    if (this._hubEngine) {
      return this._hubEngine.imm_isConnected();
    }
    return false;
  }
  imm_getConnectionState() {
    if (this.imm_isOnline()) {
      return YHub.CONNECTED;
    }
    if (this._targetState <= -5) {
      return YHub.ABORTED;
    }
    return YHub.TRYING;
  }
  // default implementation of function that says if a hub is currently forwarded and handled remotely
  imm_isForwarded() {
    if (this._hubEngine) {
      return this._hubEngine.imm_isForwarded();
    }
    return false;
  }
  imm_addKnownUrl(urlInfo) {
    if (!this._knownUrls.includes(urlInfo.imm_getOriginalURL())) {
      this._knownUrls.push(urlInfo.imm_getOriginalURL());
    }
  }
  imm_updateUrl(urlInfo) {
    if (!this._knownUrls.includes(urlInfo.imm_getOriginalURL())) {
      this._knownUrls.push(urlInfo.imm_getOriginalURL());
    }
    if (this.urlInfo.imm_getUrl(false, true, true) == urlInfo.imm_getUrl(false, true, true)) {
      this.urlInfo.imm_updateFrom(urlInfo);
      return;
    }
    this.urlInfo.imm_updateFrom(urlInfo);
    if (this._currentState < -1) {
      if (this._yapi._logLevel >= 4) {
        this._yapi.imm_log("Updating auth credentials for " + this.urlInfo.imm_getRootUrl());
      }
    }
  }
  imm_updateForRedirect(url) {
    let ofs = url.indexOf("://");
    if (ofs > 0) {
      ofs = url.indexOf("/", ofs + 3);
      if (ofs > 0) {
        url = url.substring(0, ofs);
      }
    }
    let new_url = new _YY_UrlInfo(url);
    this.urlInfo.imm_updateForRedirect(new_url.imm_getHost(), new_url.imm_getPort(), new_url.imm_useSecureSocket());
    if (this._yapi._logLevel >= 4) {
      this._yapi.imm_log("Updating URL after HTTP redirection : " + this.urlInfo.imm_getRootUrl());
    }
    let primaryHub = this._yapi._knownHubsByUrl[new_url.imm_getRootUrl()];
    if (primaryHub && primaryHub !== this) {
      if (primaryHub.urlInfo.imm_useSecureSocket()) {
        if (primaryHub._currentState >= this._currentState) {
          primaryHub.imm_inheritFrom(this);
          this._yapi.imm_updateRegisteredHubs(this, false);
          return;
        }
      }
      this._yapi._knownHubsByUrl[this.urlInfo.imm_getRootUrl()] = this;
      this.imm_inheritFrom(primaryHub);
      this._yapi.imm_updateRegisteredHubs(primaryHub, false);
    } else {
      this._yapi._knownHubsByUrl[new_url.imm_getRootUrl()] = this;
    }
  }
  imm_inheritFrom(otherHub) {
    if (this._targetState < otherHub._targetState) {
      this.imm_setTargetState(otherHub._targetState);
    }
    for (let j = 0; j < otherHub.serialByYdx.length; j++) {
      let serial = otherHub.serialByYdx[j];
      if (serial && !this.serialByYdx[j]) {
        this.serialByYdx[j] = serial;
      }
    }
    if (this._currentState >= 0 && otherHub._currentState < 0) {
      let res_struct = { errorType: YAPI_SUCCESS, errorMsg: "Hub " + this.hubSerial + " already connected" };
      let resolvers = otherHub.connResolvers;
      for (let resolveOne of resolvers) {
        resolveOne(res_struct);
      }
    } else {
      for (let resolver of otherHub.connResolvers) {
        this.connResolvers.push(resolver);
      }
    }
    otherHub.connResolvers = [];
    if (this._yapi._logLevel >= 3) {
      this._yapi.imm_log("Hub " + this.hubSerial + " is connected as " + this.urlInfo.imm_getRootUrl() + ", dropping connection to " + otherHub.urlInfo.imm_getRootUrl());
    }
    otherHub.imm_commonDisconnect("inherit", YAPI_SUCCESS, "Hub " + this.hubSerial + " is already connected via " + this.urlInfo.imm_getRootUrl());
    otherHub.imm_disconnectNow();
    for (const url of otherHub.get_knownUrls()) {
      if (!this._knownUrls.includes(url)) {
        this._knownUrls.push(url);
      }
    }
  }
  imm_getNewConnID() {
    let time = /* @__PURE__ */ new Date();
    return (time.getHours() + "h" + time.getMinutes() + "m" + time.getTime() % 6e4 / 1e3).toString() + "_0";
  }
  imm_tryTestConnectFor(mstimeout) {
    let minimalExpiration = Date.now() + mstimeout;
    if (this.keepTryingExpiration < minimalExpiration) {
      this.keepTryingExpiration = minimalExpiration;
      if (this.keepTryingTimeoutId) {
        clearTimeout(this.keepTryingTimeoutId);
      }
      this.keepTryingTimeoutId = setTimeout(() => {
        this.keepTryingTimeoutId = null;
        if (this._targetState == 0) {
          if (this._yapi._logLevel >= 4) {
            this._yapi.imm_log("TestHub timeout reached, disconnecting");
          }
          this.detach(YAPI.IO_ERROR, "TestHub timeout reached");
        }
      }, mstimeout);
    }
  }
  /** Trigger the setup of a connection to the target hub, and return.
   * This method uses a connection helper that is overridden by each type of hub.
   */
  async attach(targetConnType) {
    if (this._targetState <= 0 || targetConnType > 0) {
      this._lastErrorType = 0;
      this._lastErrorMsg = "Reconnecting";
      this.imm_setTargetState(targetConnType);
      if (this._currentState == 0 && targetConnType > 0) {
        try {
          await this._yapi._ensureUpdateDeviceListNotRunning();
          await this._yapi._addConnectedHub(this);
          this.imm_setState(targetConnType);
        } catch (e) {
          this.imm_disconnectNow();
        }
      }
      if (targetConnType == 0) {
        this.imm_tryTestConnectFor(100);
      }
    }
    if (this._currentState <= -5) {
      if (this._yapi._logLevel >= 4) {
        this._yapi.imm_log("New hub is detached connecting...");
      }
      this._hubEngine = null;
      this.stalledTimeoutMs = this._yapi._networkTimeoutMs;
      this.imm_setState(
        -1
        /* Y_YHubConnType.HUB_CONNECTING */
      );
      this.reconnect(this.imm_getNewConnID());
    } else if (this._currentState == -3) {
      if (this._reconnectionTimer) {
        if (this._yapi._logLevel >= 4) {
          this._yapi.imm_log("New hub connection requested, retry now (drop [" + this.currentConnID + "])");
        }
        clearTimeout(this._reconnectionTimer);
        this._reconnectionTimer = null;
        this.currentConnID = "";
      } else {
        if (this._yapi._logLevel >= 4) {
          this._yapi.imm_log("New hub connection requested, retry now (no pending reconnection ?!?)");
        }
      }
      this.reconnect(this.imm_getNewConnID());
    } else if (this._currentState == -4 || this._currentState == -2) {
      if (this._yapi._logLevel >= 4) {
        this._yapi.imm_log("Hub is currently disconnecting, reconnection will be triggered soon [" + this.currentConnID + "]");
        this._yapi.imm_log("Current state: " + this._currentState);
        this._yapi.imm_log("Target state: " + this._targetState + " (" + targetConnType + ")");
      }
    }
  }
  /** Wait until the connection to the hub is established
   */
  async waitForConnection(mstimeout, errmsg) {
    if (this._targetState < 0) {
      errmsg.msg = this._lastErrorMsg;
      return this._lastErrorType;
    }
    if (this._currentState >= 0) {
      return YAPI_SUCCESS;
    }
    if (mstimeout <= 1) {
      errmsg.msg = "Hub not connected";
      return YAPI_TIMEOUT;
    }
    if (this._targetState == 0) {
      this.imm_tryTestConnectFor(mstimeout);
    }
    let connOpenPromise = null;
    let connOpenTimeoutObj = null;
    let addResolverPromise;
    addResolverPromise = new Promise((resolverReady, noResolver) => {
      connOpenPromise = new Promise((resolve, reject) => {
        this.connResolvers.push(resolve);
        resolverReady(resolve);
        connOpenTimeoutObj = setTimeout(() => {
          if (this._yapi._logLevel >= 4) {
            this._yapi.imm_log("Timeout waiting for hub connection");
          }
          resolve({ errorType: YAPI_TIMEOUT, errorMsg: "Timeout waiting for hub connection" });
        }, mstimeout);
      });
    });
    let resolver = await addResolverPromise;
    if (this._targetState < 0) {
      clearTimeout(connOpenTimeoutObj);
      errmsg.msg = this._lastErrorMsg;
      return this._lastErrorType;
    }
    if (this._currentState >= 0) {
      clearTimeout(connOpenTimeoutObj);
      return YAPI_SUCCESS;
    }
    let openRes = await connOpenPromise;
    clearTimeout(connOpenTimeoutObj);
    if (openRes.errorType != YAPI_SUCCESS) {
      if (errmsg) {
        errmsg.msg = openRes.errorMsg;
      }
    }
    return openRes.errorType;
  }
  /** Attempt to establish a connection to the hub asynchronously.
   *
   * On success, this method should call this.signalHubConnected()
   * On temporary failure, this method should call this.imm_signalHubDisconnected()
   * On fatal failure, this method should call this.imm_commonDisconnect()
   *
   * This method is supposed to be redefined by subclasses
   */
  async reconnect(tryOpenID) {
    if (!this._hubEngine) {
      if (this._yapi._logLevel >= 4) {
        this._yapi.imm_log("look for suitable Hub engine [" + tryOpenID + "]");
      }
      this._usePureHTTP = false;
      this._portInfo = [];
      let infoJson = null;
      if (this.urlInfo.imm_testInfoJson()) {
        let https_req = this.urlInfo.imm_useSecureSocket();
        if (this.urlInfo.imm_getPort() == YAPI.YOCTO_DEFAULT_HTTPS_PORT) {
          https_req = true;
        }
        let url = (https_req ? "https://" : "http://") + this.urlInfo.imm_getUrl(false, false, true) + "info.json";
        if (this._yapi._logLevel >= 4) {
          this._yapi.imm_log("look for info.json at " + url + " [" + tryOpenID + "]");
        }
        try {
          let data = await this._yapi.system_env.downloadfile(url, this._yapi);
          infoJson = JSON.parse(YAPI.imm_bin2str(data));
          if (infoJson) {
            infoJson.stamp = YAPI.GetTickCount();
            if (infoJson.serialNumber) {
              this.imm_setSerialNumber(infoJson.serialNumber);
            }
            if (infoJson.securityMode !== void 0 && infoJson.securityMode == 0) {
              this.imm_commonDisconnect(tryOpenID, YAPI.UNCONFIGURED, "Remote hub is not yet configured");
              this.imm_disconnectNow();
              return;
            }
            if (infoJson.protocol && infoJson.protocol == "HTTP/1.1") {
              this._usePureHTTP = true;
            }
            if (infoJson.port) {
              let i = 0;
              while (i < infoJson.port.length) {
                let proto_port = infoJson.port[i++];
                let split = proto_port.split(":");
                let proto = split[0];
                let port = YAPIContext.imm_atoi(split[1]);
                if (port == 0) {
                  break;
                }
                this._portInfo.push({ proto, port });
              }
            }
          }
          if (this._yapi._logLevel >= 4) {
            this._yapi.imm_log("info.json successfully parsed " + url + " [" + tryOpenID + "]");
          }
        } catch (e) {
          if (e.errorType == YAPI.SSL_UNK_CERT) {
            this.imm_commonDisconnect(tryOpenID, YAPI.SSL_UNK_CERT, e.message);
            this.imm_disconnectNow();
            return;
          } else {
            if (this._yapi._logLevel >= 4) {
              this._yapi.imm_log("Unable to get info.json from " + url + " [" + tryOpenID + "]");
            }
            let serialurl = (https_req ? "https://" : "http://") + this.urlInfo.imm_getUrl(false, false, false) + "/api/module/serialNumber";
            try {
              let data = await this._yapi.system_env.downloadfile(serialurl, this._yapi);
              this.imm_setSerialNumber(YAPI.imm_bin2str(data));
            } catch (e2) {
              this.imm_commonDisconnect(tryOpenID, YAPI.IO_ERROR, e2.message);
              return;
            }
          }
        }
      }
      const runtimeUrl = this.imm_UseBestProto();
      if (runtimeUrl.imm_useWebSocket()) {
        if (this._yapi._logLevel >= 4) {
          this._yapi.imm_log("Use WebSocket hub engine [" + tryOpenID + "]");
        }
        this._hubEngine = this._yapi.system_env.getWebSocketEngine(this, runtimeUrl);
      } else {
        if (this._yapi._logLevel >= 4) {
          this._yapi.imm_log("Use HTTP hub engine [" + tryOpenID + "]");
        }
        this._hubEngine = this._yapi.system_env.getHttpEngine(this, runtimeUrl, infoJson);
      }
      if (!this._hubEngine) {
        this.imm_commonDisconnect(tryOpenID, YAPI_NOT_SUPPORTED, "Unsupported hub protocol: " + runtimeUrl.imm_getProto());
        return;
      }
    }
    await this._hubEngine.reconnectEngine(tryOpenID);
  }
  /** Invoked by this.reconnect() to handle successful hub connection
   */
  async signalHubConnected(tryOpenID, hubSerial) {
    this.imm_setState(
      0
      /* Y_YHubConnType.HUB_CONNECTED */
    );
    this.hubSerial = hubSerial;
    if (this._yapi._logLevel >= 4) {
      this._yapi.imm_log("Hub " + hubSerial + " connected [" + tryOpenID + "]");
    }
    let primaryHub = this._yapi.imm_getPrimaryHub(this);
    if (primaryHub._targetState >= 1) {
      if (primaryHub._currentState < 1) {
        try {
          await primaryHub._yapi._ensureUpdateDeviceListNotRunning();
          await primaryHub._yapi._addConnectedHub(primaryHub);
        } catch (e) {
          primaryHub.imm_disconnectNow();
          return;
        }
      }
      if (primaryHub._currentState < primaryHub._targetState) {
        primaryHub.imm_setState(primaryHub._targetState);
      }
    } else {
      primaryHub.keepTryingExpiration = 0;
      primaryHub.imm_tryTestConnectFor(100);
    }
    let res_struct = { errorType: YAPI_SUCCESS, errorMsg: "Hub " + hubSerial + " connected" };
    let resolvers = primaryHub.connResolvers;
    primaryHub.connResolvers = [];
    primaryHub._lastErrorType = res_struct.errorType;
    primaryHub._lastErrorMsg = res_struct.errorMsg;
    for (let resolveOne of resolvers) {
      resolveOne(res_struct);
    }
  }
  /** Invoked by the network handler to signal hub disconnection
   *
   * Returns true if a reconnection has been scheduled
   *     or false if the target state is "detached"
   */
  imm_signalHubDisconnected(tryOpenID) {
    if (this._yapi._logLevel >= 4) {
      this._yapi.imm_log("imm_signalHubDisconnected  " + this.urlInfo.imm_getRootUrl());
    }
    if (this._currentState > -3) {
      this.imm_setState(
        -3
        /* Y_YHubConnType.HUB_DISCONNECTED */
      );
    }
    this.isNotifWorking = false;
    this.devListExpires = 0;
    this._yapi.imm_dropConnectedHub(this);
    this._firstArrivalCallback = true;
    let resolvers = this.disconnResolvers;
    this.disconnResolvers = [];
    for (let resolveOne of resolvers) {
      resolveOne({ errorType: YAPI_SUCCESS, errorMsg: "Hub disconnect completed" });
    }
    if (this.imm_isDisconnecting()) {
      this.imm_setState(
        -5
        /* Y_YHubConnType.HUB_DETACHED */
      );
      if (this._yapi._logLevel >= 4) {
        this._yapi.imm_log("Hub " + this.urlInfo.imm_getRootUrl() + " detached");
      }
      return false;
    }
    if (this._reconnectionTimer) {
      if (this._yapi._logLevel >= 4) {
        this._yapi.imm_log("Hub disconnected, reconnection is already scheduled [" + this.currentConnID + "]");
      }
      return true;
    }
    let openIDwords = tryOpenID.split("_");
    let nextOpenID = openIDwords[0] + "_" + (parseInt(openIDwords[1]) + 1).toString();
    if (this.retryDelay < 5e3)
      this.retryDelay *= 2;
    if (this._yapi._logLevel >= 4) {
      this._yapi.imm_log("Hub reconnection scheduled in " + this.retryDelay / 1e3 + "s [" + nextOpenID + "]");
    }
    this.currentConnID = nextOpenID;
    this._reconnectionTimer = setTimeout(() => {
      this._reconnectionTimer = null;
      this.currentConnID = "";
      if (this.imm_isDisconnecting()) {
        return;
      }
      if (this._yapi._logLevel >= 4) {
        this._yapi.imm_log("Retry hub connection now [" + nextOpenID + "]");
      }
      this.reconnect(nextOpenID);
    }, this.retryDelay);
    return true;
  }
  // Cancel current connection and report the fatal connection failure to the initiator.
  // This function may be called with YAPI_SUCCESS in case of desired disconnection
  //
  // This function should be called FIRST by any implementors of async detach()
  // in order to prevent automatic reconnect
  imm_commonDisconnect(tryOpenID, errType, errMsg) {
    this._lastErrorType = errType;
    this._lastErrorMsg = errMsg;
    if (this._currentState >= -2) {
      this.imm_setState(
        -4
        /* Y_YHubConnType.HUB_DETACHING */
      );
    } else if (this._currentState == -3) {
      this.imm_setState(
        -5
        /* Y_YHubConnType.HUB_DETACHED */
      );
    }
    this.imm_setTargetState(
      -5
      /* Y_YHubConnType.HUB_DETACHED */
    );
    if (this._reconnectionTimer) {
      clearTimeout(this._reconnectionTimer);
      this._reconnectionTimer = null;
    }
    if (this.timeoutId) {
      clearTimeout(this.timeoutId);
      this.timeoutId = null;
    }
    if (errType != YAPI_SUCCESS && this._yapi._logLevel >= 4 && tryOpenID != "detach") {
      this._yapi.imm_log("Hub connection failed: " + errMsg + " [" + tryOpenID + "]");
    }
    this._firstArrivalCallback = true;
    let res_struct = { errorType: errType, errorMsg: errMsg };
    let resolvers = this.connResolvers;
    this.connResolvers = [];
    for (let resolveOne of resolvers) {
      resolveOne(res_struct);
    }
  }
  // Implementation of function to abort communication channel immediately
  //
  // If a connectionID is passed as argument, only abort the
  // communication channel if the ID matched current connection
  //
  // Return true if the connection os getting aborted
  //
  // Subclasses are expected to invoke imm_signalHubDisconnected() after cleaning
  // up current communication, to bring back the link again later
  imm_disconnectNow(connID = "") {
    if (connID && connID != this.currentConnID) {
      return false;
    }
    if (this._currentState > -2) {
      this.imm_setState(
        -2
        /* Y_YHubConnType.HUB_DISCONNECTING */
      );
    }
    if (this.timeoutId) {
      clearTimeout(this.timeoutId);
      this.timeoutId = null;
    }
    if (this._hubEngine) {
      this._hubEngine.imm_disconnectEngineNow(connID);
    } else {
      this.imm_signalHubDisconnected(connID);
    }
    return true;
  }
  /** Invoked by UnregisterHub
   *
   * Free ressources allocated by the hub, close requests,
   * call this.imm_commonDisconnect() and bring the link down.
   *
   * This method may be redefined by subclasses to do additional
   * cleanup before invoking this.imm_commonDisconnect() to bring
   * communication down, to prevent automatic reconnect.
   */
  async detach(errType = YAPI.IO_ERROR, errMsg = "Hub has been forcibly detached") {
    this.imm_commonDisconnect("detach", errType, errMsg);
    this.imm_disconnectNow();
    this.stalledTimeoutMs = this._yapi._networkTimeoutMs;
    this._hubEngine = null;
  }
  /** Wait until the hub is fully disconnected
   */
  async waitForDisconnection(mstimeout) {
    let disconnPromise = null;
    let disconnTimeoutObj = null;
    disconnPromise = new Promise((resolve, reject) => {
      this.disconnResolvers.push(resolve);
      disconnTimeoutObj = setTimeout(() => {
        if (this._yapi._logLevel >= 4) {
          this._yapi.imm_log("Timeout waiting for hub disconnection");
        }
        resolve({ errorType: YAPI_TIMEOUT, errorMsg: "Timeout waiting for hub connection" });
      }, mstimeout);
    });
    await disconnPromise;
    clearTimeout(disconnTimeoutObj);
  }
  // Return the number of NEW devices discovered, or a negative error code
  async hubUpdateDeviceList() {
    let hubDev = this._yapi.imm_getDevice(this.urlInfo.imm_getRootUrl());
    try {
      hubDev.imm_dropCache();
      let retcode = await hubDev.refresh();
      if (retcode != YAPI_SUCCESS) {
        if (this._currentState >= 1) {
          await this._yapi.updateDeviceList_process(this, hubDev, [], {});
        }
        this.imm_disconnectNow();
        return this._throw(retcode, hubDev._lastErrorMsg, retcode);
      }
      let yreq = await hubDev.requestAPI(this._yapi.defaultCacheValidity);
      if (yreq.errorType != YAPI_SUCCESS) {
        if (this._currentState >= 1) {
          await this._yapi.updateDeviceList_process(this, hubDev, [], {});
        }
        this.imm_disconnectNow();
        return yreq.errorType;
      }
      let whitePages = yreq.obj_result.services.whitePages;
      let yellowPages = yreq.obj_result.services.yellowPages;
      if (!whitePages) {
        this.imm_disconnectNow();
        return this._throw(YAPI_IO_ERROR, "Device " + hubDev.imm_describe() + " is not a hub", YAPI_IO_ERROR);
      }
      retcode = await this._yapi.updateDeviceList_process(this, hubDev, whitePages, yellowPages);
      if (retcode < 0) {
        this.imm_disconnectNow();
        return this._throw(this._yapi._lastErrorType, this._yapi._lastErrorMsg, this._yapi._lastErrorType);
      }
      if (this.isNotifWorking) {
        this.devListExpires = this._yapi.GetTickCount() + this._yapi._deviceListValidityMs;
      } else {
        this.devListExpires = this._yapi.GetTickCount() + 500;
      }
      return retcode;
    } catch (e) {
      if (this._yapi._logLevel >= 3) {
        this._yapi.imm_log("Exception during device enumeration: ", e);
      }
      if (this._currentState >= 1) {
        try {
          await this._yapi.updateDeviceList_process(this, hubDev, [], {});
        } catch (e2) {
        }
      }
      this.imm_disconnectNow();
      return YAPI_IO_ERROR;
    }
  }
  /** Perform an HTTP query on the hub
   */
  async request(method, devUrl, obj_body, tcpchan) {
    if (!this._hubEngine) {
      let res = new YHTTPRequest(null);
      res.errorType = YAPI_IO_ERROR;
      res.errorMsg = "HubEngine is not initialised";
      return res;
    }
    return await this._hubEngine.request(method, devUrl, obj_body, tcpchan);
  }
  /** Create a new random boundary for form-encoding
   */
  imm_getBoundary() {
    return "Zz" + Math.floor(Math.random() * 16777215).toString(16) + "zZ";
  }
  /** Form-encode a body object into an raw Uint8Array to send
   */
  imm_formEncodeBody(obj_body, str_boundary) {
    let hdr = this._yapi.imm_str2bin('Content-Disposition: form-data; name="' + obj_body.fname + '"; filename="api"\r\nContent-Type: application/octet-stream\r\nContent-Transfer-Encoding: binary\r\n\r\n');
    let boundary = this._yapi.imm_str2bin(str_boundary);
    let dash = this._yapi.imm_str2bin("--");
    let crlf = this._yapi.imm_str2bin("\r\n");
    let parts = [dash, boundary, crlf, hdr, obj_body.data, crlf, dash, boundary, dash, crlf];
    let i, len = 0;
    for (i = 0; i < parts.length; i++) {
      len += parts[i].length;
    }
    let res = new Uint8Array(len);
    len = 0;
    for (i = 0; i < parts.length; i++) {
      res.set(parts[i], len);
      len += parts[i].length;
    }
    return res;
  }
  /** Return an array of serial numbers
   */
  async getBootloaders() {
    let yreq = await this.request("GET", "/flash.json?a=list", null, 1);
    if (yreq.errorType != YAPI_SUCCESS) {
      return this._throw(yreq.errorType, yreq.errorMsg, []);
    }
    let flashState = JSON.parse(YAPI.imm_bin2str(yreq.bin_result));
    return flashState["list"];
  }
  /** Perform a firmware update
   */
  async firmwareUpdate(serial, firmware, settings, progress) {
    let use_self_flash = false;
    let baseUrl = "";
    let need_reboot = true;
    let _throw = ((msg) => {
      return this._throw(YAPI.IO_ERROR, msg, [msg]);
    });
    progress(5, "Check bootloader type");
    let yreq = await this.request("GET", "/api/module.json", null, 0);
    if (yreq.errorType != YAPI_SUCCESS) {
      return _throw("Hub is not responding");
    }
    let json = JSON.parse(this._yapi.imm_bin2str(yreq.bin_result));
    let ownSerial = json.serialNumber;
    if (ownSerial.slice(0, 7) == "VIRTHUB") {
      use_self_flash = false;
    } else if (serial == ownSerial) {
      use_self_flash = true;
    } else {
      yreq = await this.request("GET", "/bySerial/" + serial + "/flash.json?a=state", null, 0);
      if (yreq.errorType == YAPI_SUCCESS && yreq.bin_result.length > 0) {
        use_self_flash = true;
        baseUrl = "/bySerial/" + serial;
      }
    }
    let bootloaders = await this.getBootloaders();
    let is_shield = serial.slice(0, 7) == "YHUBSHL";
    let i;
    for (i = 0; i < bootloaders.length; i++) {
      let bl = bootloaders[i];
      if (bl == serial) {
        need_reboot = false;
      } else if (is_shield) {
        if (bl.slice(0, 7) == "YHUBSHL") {
          return _throw("Only one YoctoHub-Shield is allowed in update mode");
        }
      }
    }
    if (!use_self_flash && need_reboot) {
      if (bootloaders.length >= 4) {
        return _throw("Too many devices in update mode");
      }
    }
    yreq = await this.request("GET", baseUrl + "/flash.json?a=state", null, 0);
    if (yreq.errorType != YAPI_SUCCESS) {
      return _throw("Cannot check state of firmware upload");
    }
    json = JSON.parse(this._yapi.imm_bin2str(yreq.bin_result));
    if (json["state"] == "uploading" || json["state"] == "flashing") {
      return _throw("Cannot start firmware update: busy (" + json["state"] + ")");
    }
    progress(10, "Send firmware file");
    let progressCb = function(curr, total) {
      curr >>= 10;
      total >>= 10;
      progress(10 + (28 * curr / total >> 0), "Send firmware file: " + curr + "KB / " + total + "KB");
    };
    yreq = await this.request("POST", baseUrl + "/upload.html", new YHTTPBody("firmware", firmware.imm_getData(), progressCb), 0);
    if (yreq.errorType != YAPI_SUCCESS) {
      return _throw("Firmware upload failed: " + yreq.errorMsg);
    }
    yreq = await this.request("GET", baseUrl + "/flash.json?a=state", null, 0);
    if (yreq.errorType != YAPI_SUCCESS) {
      return _throw("Cannot check state of firmware upload");
    }
    json = JSON.parse(this._yapi.imm_bin2str(yreq.bin_result));
    if (json["state"] != "valid") {
      return _throw("Upload of firmware failed: invalid firmware(" + json["state"] + ")");
    }
    if (json["progress"] != "100") {
      return _throw("Upload of firmware failed: incomplete upload");
    }
    if (use_self_flash) {
      let settingsStr = this._yapi.imm_bin2str(settings);
      let settingsAndFiles = JSON.parse(settingsStr);
      let settingsOnly = settingsAndFiles["api"];
      let startupApi = {};
      for (let key in settingsOnly) {
        if (key != "services") {
          startupApi[key] = settingsOnly[key];
        }
      }
      let startupConf = this._yapi.imm_str2bin(JSON.stringify(startupApi));
      progress(38, "Save current settings");
      yreq = await this.request("POST", baseUrl + "/upload.html", new YHTTPBody("startupConf.json", startupConf, null), 0);
      if (yreq.errorType != YAPI_SUCCESS) {
        return _throw("Failed to save settings on hub");
      }
      progress(39, "Save current settings");
      yreq = await this.request("POST", baseUrl + "/upload.html", new YHTTPBody("firmwareConf", startupConf, null), 0);
      if (yreq.errorType != YAPI_SUCCESS) {
        return _throw("Failed to save settings on hub");
      }
    }
    if (use_self_flash) {
      progress(40, "Flash firmware");
      await this.request("GET", baseUrl + "/api/module/rebootCountdown?rebootCountdown=-1003", null, 0);
      await this._yapi.Sleep(7e3);
    } else {
      if (need_reboot) {
        await this.request("GET", "/bySerial/" + serial + "/api/module/rebootCountdown?rebootCountdown=-2", null, 0);
      }
      let timeout = YAPI.GetTickCount() + 2e4;
      let res;
      let found = false;
      progress(40, "Wait for device to be in bootloader");
      do {
        bootloaders = await this.getBootloaders();
        for (i = 0; i < bootloaders.length; i++) {
          let bl = bootloaders[i];
          if (bl == serial) {
            found = true;
            break;
          }
        }
        if (!found) {
          await this._yapi.Sleep(500);
        }
      } while (!found && YAPI.GetTickCount() < timeout);
      progress(45, "Flash firmware");
      let fwsize = firmware.imm_getData().length + 512 >> 10;
      let checkTimer;
      let checkFlash = (() => {
        this.request("GET", baseUrl + "/flash.json?a=state", null, 1).then((flashReq) => {
          if (flashReq.errorType == YAPI_SUCCESS) {
            let jsonState = YAPI.imm_bin2str(flashReq.bin_result);
            let res2 = JSON.parse(jsonState);
            if (res2.state == "flashing") {
              if (res2.progress < 20) {
                progress(45 + (res2.progress / 3 >> 0), "Erasing previous firmware: " + (fwsize * (res2.progress - 3) / 18 >> 0) + "KB / " + fwsize + "KB");
              } else {
                progress(45 + (res2.progress / 3 >> 0), "Flashing new firmware: " + (fwsize * (res2.progress - 20) / 76 >> 0) + "KB / " + fwsize + "KB");
              }
            }
          }
          checkTimer = setTimeout(checkFlash, 500);
        }).catch((e) => {
          this._yapi.imm_log("Exception during firmware flash: ", e);
          checkTimer = setTimeout(checkFlash, 500);
        });
      });
      checkTimer = setTimeout(checkFlash, 1e3);
      yreq = await this.request("GET", "/flash.json?a=flash&s=" + serial, null, 0);
      clearTimeout(checkTimer);
      if (yreq.errorType != YAPI_SUCCESS) {
        return _throw("Cannot check state of firmware flash");
      }
      return JSON.parse(this._yapi.imm_bin2str(yreq.bin_result));
    }
    return null;
  }
  async reportFailure(message) {
    if (this._hubEngine) {
      await this._hubEngine.reportFailure(message);
    }
  }
  // check if a hub connection provides read-write access to the devices
  async hasRwAccess() {
    if (this._rwAccess == null) {
      let yreq = await this.request("GET", "/api/module/serialNumber.json?serialNumber=rwTest", null, 0);
      this._rwAccess = yreq.errorType == YAPI_SUCCESS;
    }
    return this._rwAccess;
  }
  imm_isRwAccess() {
    if (this._rwAccess == null) {
      return false;
    }
    return this._rwAccess;
  }
  imm_setRwAccess(rwAccess) {
    this._rwAccess = rwAccess;
  }
  getHubRef() {
    return this._hubRef;
  }
  get_knownUrls() {
    let res = this._knownUrls.slice();
    return res;
  }
  imm_forgetUrls() {
    this._knownUrls = [];
  }
  imm_getOriginalURL() {
    return this.urlInfo.imm_getOriginalURL();
  }
  imm_getRootUrl() {
    return this.urlInfo.imm_getRootUrl();
  }
  imm_getSerialNumber() {
    return this.hubSerial;
  }
  imm_setSerialNumber(serial) {
    this.hubSerial = serial;
  }
  imm_getNetworkTimeout() {
    return this.stalledTimeoutMs;
  }
  imm_setNetworkTimeout(mstimeout) {
    this.stalledTimeoutMs = mstimeout;
  }
  imm_setHubEngine(engine) {
    this._hubEngine = engine;
  }
  imm_setRetryDelay(value) {
    this.retryDelay = value;
  }
  imm_SetErr(errorType, errorMsg) {
    this._lastErrorType = errorType;
    this._lastErrorMsg = errorMsg;
  }
  async WebSocketJoin(ws, arr_credentials, closeCallback) {
    return this._hubEngine.websocketJoin(ws, arr_credentials, closeCallback);
  }
  imm_UseBestProto() {
    let cur_proto = this.urlInfo.imm_getProto();
    let runtime_urlInfo = this.urlInfo;
    this._hubMode = HubMode.SECURE;
    if (this._portInfo.length > 0) {
      if (this._usePureHTTP) {
        if (cur_proto == "ws" || cur_proto == "wss") {
          this._yapi._throw(YAPI.NOT_SUPPORTED, "Websocket protocol is not supported by VirtualHub-4web.");
        }
        for (let i = 0; i < this._portInfo.length; i++) {
          let portInfo = this._portInfo[i];
          if (portInfo.proto.startsWith("http")) {
            if (this._yapi._logLevel >= 3) {
              this._yapi.imm_log("Hub " + this.urlInfo.imm_getHost() + " will use " + portInfo.proto + " proto on port " + portInfo.port);
            }
            runtime_urlInfo = this.urlInfo;
            runtime_urlInfo.imm_updateBestProto(portInfo.proto, portInfo.port);
            break;
          }
        }
      } else {
        let best_port = 0;
        let best_proto = "ws";
        if (this._portInfo[0].proto == "http" || this._portInfo[0].proto == "ws") {
          this._hubMode = HubMode.LEGACY;
        }
        for (let i = 0; i < this._portInfo.length; i++) {
          let portInfo = this._portInfo[i];
          if (this._hubMode == HubMode.SECURE && (portInfo.proto == "http" || portInfo.proto == "ws")) {
            if (this._yapi._logLevel >= 3) {
              this._yapi.imm_log("Hub " + this.urlInfo.imm_getHost() + " use mixed or legacy mode");
            }
            this._hubMode = HubMode.MIXED;
          }
          if (cur_proto == "auto" && best_port == 0) {
            if (portInfo.proto.startsWith("http") || portInfo.proto.startsWith("ws")) {
              best_proto = portInfo.proto;
              best_port = portInfo.port;
            }
          }
          if (cur_proto == "secure" && best_port == 0) {
            if (portInfo.proto == "https" || portInfo.proto == "wss") {
              best_proto = portInfo.proto;
              best_port = portInfo.port;
            }
          }
        }
        if (best_port != 0) {
          if (this._yapi._logLevel >= 3) {
            this._yapi.imm_log("Hub " + this.urlInfo.imm_getHost() + " will use " + best_proto + " proto on port " + best_port);
          }
          runtime_urlInfo = this.urlInfo;
          runtime_urlInfo.imm_updateBestProto(best_proto, best_port);
        }
      }
    }
    return runtime_urlInfo;
  }
  imm_useMixedMode() {
    return this._hubMode == HubMode.MIXED || this._hubMode == HubMode.LEGACY;
  }
  async waitForPendingQueries(ms_timeout) {
    if (this._hubEngine) {
      await this._hubEngine.waitForPendingQueries(ms_timeout);
    }
  }
};
YGenericHub.globalHubRefCounter = 0;
var YHttpEngine = class extends YHubEngine {
  constructor(hub, runtime_urlInfo, firstInfoJson) {
    super(hub, runtime_urlInfo);
    this.infoJson = null;
    this.ha1 = "";
    this.realm = "";
    this.nonce = "";
    this.opaque = "";
    this.nonceCount = 0;
    this.notbynRequest = null;
    this.infoJson = firstInfoJson;
  }
  // Low-level function to create an HTTP client request (abstraction layer)
  imm_makeRequest(method, relUrl, contentType, body, onProgress, onSuccess, onError) {
  }
  // abort an HTTP client request immediately (abstraction layer)
  imm_abortRequest(clientRequest) {
  }
  // Initiate an HTTP client request with proper authentication settings and mime type
  // Handle header-based client authentication (to prevent browser pop-ups)
  imm_sendRequest(method, relUrl, obj_body, onProgress, onSuccess, onError) {
    let body = null;
    let contentType = "text/plain; charset=x-user-defined";
    if (this.infoJson && this.infoJson.realm && this.infoJson.nonce) {
      if (this.realm != this.infoJson.realm || this.nonce != this.infoJson.nonce) {
        this.realm = this.infoJson.realm;
        this.nonce = this.infoJson.nonce;
        this.nonceCount = 0;
      }
      let shorturi = this._runtime_urlInfo.imm_getSubDomain() + relUrl;
      let jsonBody = {
        "x-yauth": {
          method,
          uri: shorturi,
          nonce: this.nonce
        }
      };
      if (this._runtime_urlInfo.imm_hasAuthParam()) {
        let cnonce = Math.floor(Math.random() * 2147483647).toString(16).toLowerCase();
        let nc = (++this.nonceCount).toString(16).toLowerCase();
        let ha1_str = this._runtime_urlInfo.imm_getUser() + ":" + this.realm + ":" + this._runtime_urlInfo.imm_getPass();
        let ha2_str = method + ":" + shorturi;
        let A1 = this._hub._yapi.imm_bin2hexstr(this._hub._yapi.imm_yMD5(ha1_str)).toLowerCase();
        let A2 = this._hub._yapi.imm_bin2hexstr(this._hub._yapi.imm_ySHA1(ha2_str)).toLowerCase();
        let signature = A1 + ":" + this.nonce + ":" + nc + ":" + cnonce + ":auth:" + A2;
        let response = this._hub._yapi.imm_bin2hexstr(this._hub._yapi.imm_ySHA1(signature)).toLowerCase();
        jsonBody["x-yauth"]["username"] = this._runtime_urlInfo.imm_getUser();
        jsonBody["x-yauth"]["cnonce"] = cnonce;
        jsonBody["x-yauth"]["nc"] = nc;
        jsonBody["x-yauth"]["qop"] = "auth";
        jsonBody["x-yauth"]["response"] = response;
      }
      if (obj_body) {
        let binstr = this._hub._yapi.imm_bin2str(obj_body.data);
        jsonBody["body"] = {
          filename: obj_body.fname,
          b64content: btoa(binstr)
        };
      }
      method = "POST";
      body = JSON.stringify(jsonBody);
      let qpos = relUrl.indexOf("?");
      if (qpos > 0) {
        relUrl = relUrl.slice(0, qpos);
      }
    } else if (obj_body != null) {
      let boundary = this._hub.imm_getBoundary();
      if (this.infoJson && this.infoJson.nonce) {
        contentType = "x-upload; boundary=" + boundary;
      } else {
        contentType = "multipart/form-data; boundary=" + boundary;
      }
      body = this._hub.imm_formEncodeBody(obj_body, boundary);
    }
    return this.imm_makeRequest(method, relUrl, contentType, body, onProgress, onSuccess, onError);
  }
  // Internal method to perform a simple HTTP GET using a hub-relative URL
  async tryFetch(relUrl) {
    if (relUrl.slice(0, 1) != "/") {
      relUrl = "/" + relUrl;
    }
    return new Promise((resolve, reject) => {
      this.imm_sendRequest("GET", relUrl, null, null, (responseText) => {
        resolve({ errorType: YAPI_SUCCESS, errorMsg: "", result: responseText });
      }, (errorType, errorMsg, can_be_retry) => {
        if (can_be_retry) {
          this.imm_sendRequest("GET", relUrl, null, null, (responseText2) => {
            resolve({ errorType: YAPI_SUCCESS, errorMsg: "", result: responseText2 });
          }, (errorType2, errorMsg2, can_be_retry2) => {
            resolve({ errorType: errorType2, errorMsg: errorMsg2 });
          });
        } else {
          resolve({ errorType, errorMsg });
        }
      });
    });
  }
  /** Handle HTTP-based event-monitoring work on a registered hub
   */
  async reconnectEngine(tryOpenID) {
    this._hub.imm_setCurrentConnID(tryOpenID);
    if (this.infoJson && this.infoJson.nonce && YAPI.GetTickCount() - this.infoJson.stamp > 12e3) {
      if (this._hub._yapi._logLevel >= 4) {
        this._hub._yapi.imm_log("Trying info.json [" + tryOpenID + "]");
      }
      let res_struct = await this.tryFetch("info.json");
      if (res_struct.errorType == YAPI_SUCCESS && res_struct.result) {
        this.infoJson = JSON.parse(res_struct.result);
        this.infoJson.stamp = YAPI.GetTickCount();
      }
    }
    let primaryHub = this._hub._yapi.imm_getPrimaryHub(this._hub);
    if (primaryHub !== this._hub) {
      this._hub.imm_commonDisconnect(tryOpenID, YAPI_SUCCESS, "Hub " + this._hub.imm_getSerialNumber() + " is already connected");
      this._hub.imm_setCurrentConnID("");
      this._hub.imm_signalHubDisconnected(tryOpenID);
      return;
    }
    let args = "";
    if (this._hub.imm_getNotifyPos() >= 0) {
      args = "?abs=" + this._hub.imm_getNotifyPos().toString();
    } else {
      this._hub.imm_setFirstArrivalCallback(true);
    }
    if (this._hub._yapi._logLevel >= 4) {
      this._hub._yapi.imm_log("Opening http connection to hub (" + args + ") [" + tryOpenID + "]");
    }
    this.notbynRequest = this.imm_sendRequest("GET", "/not.byn" + args, null, (moreText) => {
      if (tryOpenID != this._hub.imm_getCurrentConnID()) {
        if (this._hub._yapi._logLevel >= 3) {
          this._hub._yapi.imm_log("Previous request still sending data [" + tryOpenID + "]");
        }
        return;
      }
      if (this.infoJson) {
        this.infoJson.stamp = YAPI.GetTickCount();
      }
      if (this._hub.imm_getcurrentState() < 0) {
        this._hub.signalHubConnected(tryOpenID, this._hub.imm_getSerialNumber());
      }
      this.imm_updateLastPinfStamp();
      this._hub._yapi.parseEvents(this._hub, moreText);
    }, (resultText) => {
      if (tryOpenID != this._hub.imm_getCurrentConnID()) {
        if (this._hub._yapi._logLevel >= 3) {
          this._hub._yapi.imm_log("Previous request completed [" + tryOpenID + "]");
        }
        return;
      }
      this.reconnectEngine(tryOpenID);
    }, (errorType, errorMsg, can_be_retry) => {
      if (tryOpenID != this._hub.imm_getCurrentConnID()) {
        if (this._hub._yapi._logLevel >= 3) {
          this._hub._yapi.imm_log("Previous not.byn request says: " + errorMsg + " [" + tryOpenID + "]");
        }
        return;
      }
      if (!this._hub.imm_isDisconnecting()) {
        if (this._hub._yapi._logLevel >= 3) {
          this._hub._yapi.imm_log("Failed to load not.byn (" + args + "): " + errorMsg + " [" + tryOpenID + "]");
        }
      }
      this._hub.imm_SetErr(errorType, errorMsg);
      if (!can_be_retry) {
        if (errorType === YAPI_SSL_UNK_CERT) {
          this._hub.imm_commonDisconnect(tryOpenID, errorType, errorMsg);
        } else if (errorType === YAPI_UNAUTHORIZED && YAPI.GetTickCount() - this.infoJson.stamp <= 12e3) {
          this._hub.imm_commonDisconnect(tryOpenID, errorType, errorMsg);
        }
      }
      this._hub.imm_disconnectNow();
    });
  }
  // abort communication channel immediately
  //
  // If a connectionID is passed as argument, only abort the
  // communication channel if the ID matched current connection
  //
  imm_disconnectEngineNow(connID = "") {
    if (this._hub._yapi._logLevel >= 4) {
      this._hub._yapi.imm_log("YHTTPEngine.imm_disconnectEngineNow " + connID);
    }
    if (!this.notbynRequest) {
      return;
    }
    let closeConnID = connID ? connID : this._hub.imm_getCurrentConnID();
    this.imm_abortRequest(this.notbynRequest);
    this.notbynRequest = null;
    this._hub.imm_setCurrentConnID("");
    this._hub.imm_signalHubDisconnected(closeConnID);
  }
  /** Perform an HTTP query on the hub
   */
  async request(method, devUrl, obj_body, tcpchan) {
    if (this._hub._yapi._logLevel >= 3) {
      this._hub.imm_logrequest(method, devUrl, obj_body);
    }
    if (this._hub.imm_getcurrentState() < 0) {
      return new YHTTPRequest(null, YAPI.IO_ERROR, "Hub is currently unavailable");
    }
    return new Promise((resolve, reject) => {
      this.imm_sendRequest(method, devUrl, obj_body, null, (responseText) => {
        if (this._hub.imm_getcurrentState() < 0) {
          resolve(new YHTTPRequest(null, YAPI.IO_ERROR, "Hub is currently unavailable"));
        } else {
          if (this._hub._yapi._logLevel >= 4) {
            this._hub._yapi.imm_log(method + " " + devUrl + " succeeded");
          }
          resolve(new YHTTPRequest(this._hub._yapi.imm_str2bin(responseText)));
        }
      }, (errorType, errorMsg, can_be_retry) => {
        if (this._hub._yapi._logLevel >= 4) {
          this._hub._yapi.imm_log(method + " " + devUrl + " failed (" + errorMsg + ")");
        }
        resolve(new YHTTPRequest(null, errorType, errorMsg));
      });
    });
  }
};
var YWebSocketEngine = class extends YHubEngine {
  constructor(hub, runtime_urlInfo) {
    super(hub, runtime_urlInfo);
    this._DEFAULT_TCP_ROUND_TRIP_TIME = 30;
    this._DEFAULT_TCP_MAX_WINDOW_SIZE = 4 * 65536;
    this._YIO_DEFAULT_TCP_TIMEOUT = 2e4;
    this._YIO_1_MINUTE_TCP_TIMEOUT = 6e4;
    this._YIO_10_MINUTES_TCP_TIMEOUT = 6e5;
    this._USB_META_UTCTIME_SIZE = 5;
    this._USB_META_DLFLUSH_SIZE = 1;
    this._USB_META_ACK_D2H_PACKET_SIZE = 2;
    this._USB_META_WS_ANNOUNCE_SIZE = 8 + 20;
    this._USB_META_WS_AUTHENTICATION_SIZE = 28;
    this._USB_META_WS_ERROR_SIZE = 6;
    this._USB_META_ACK_UPLOAD_SIZE = 6;
    this._USB_META_WS_VALID_SHA1 = 1;
    this._USB_META_WS_RW = 2;
    this.websocket = null;
    this.tcpChan = [];
    this.nextAsyncId = 48;
    this._connectionTime = 0;
    this._remoteVersion = 0;
    this._remoteSerial = "";
    this._remoteNonce = -1;
    this._nonce = -1;
    this._session_error = null;
    this._session_errno = null;
    this._lastUploadAckBytes = [0];
    this._lastUploadAckTime = [0];
    this._lastUploadRateBytes = [0];
    this._lastUploadRateTime = [0];
    this._uploadRate = [0];
    this.fwd_nonce = -1;
    this.fwd_websocket = null;
    this.fwd_credentials = [];
    this.fwd_closeCallback = null;
    this._connectionState = 2;
    this._tcpRoundTripTime = this._DEFAULT_TCP_ROUND_TRIP_TIME;
    this._tcpMaxWindowSize = this._DEFAULT_TCP_MAX_WINDOW_SIZE;
    this.fwd_connectionState = 1;
  }
  /** Report a low-level asynchronous websocket error
   **/
  imm_asyncWebSocketError(errorType, message) {
    if (this._hub._yapi._logLevel >= 3) {
      this._hub._yapi.imm_log("WS: " + message + " on " + this._runtime_urlInfo.imm_getRootUrl());
    }
  }
  /** Handle websocket-based event-monitoring work on a registered hub
   */
  async reconnectEngine(tryOpenID) {
    this._connectionState = 2;
    if (this._hub._yapi._logLevel >= 4) {
      this._hub._yapi.imm_log("Opening websocket connection [" + tryOpenID + "]");
    }
    this._hub.imm_setCurrentConnID(tryOpenID);
    let url = (this._runtime_urlInfo.imm_useSecureSocket() ? "wss://" : "ws://") + this._runtime_urlInfo.imm_getUrl(false, false, true);
    this.imm_webSocketOpen(url + "not.byn");
    this._hub.imm_setFirstArrivalCallback(true);
    if (!this.websocket) {
      this._hub.imm_commonDisconnect(tryOpenID, YAPI_IO_ERROR, "Failed to create WebSocket");
      return;
    }
    this.websocket.onmessage = ((evt) => {
      if (this._hub.imm_getCurrentConnID() != tryOpenID) {
        if (this._hub._yapi._logLevel >= 4) {
          this._hub._yapi.imm_log("Incoming WebSocket data for previous connection [" + tryOpenID + "]");
        }
        return;
      }
      this._webSocketMsg(new Uint8Array(evt.data));
      if (this._connectionState == 4) {
        this._connectionState = 5;
        this._hub.signalHubConnected(tryOpenID, this._remoteSerial);
      } else if (this._connectionState == 0) {
        let errMsg = this._session_error ? "WebSocket error: " + this._session_error : "Websocket I/O error";
        if (this._session_errno == 401) {
          this._hub.imm_commonDisconnect(tryOpenID, YAPI_UNAUTHORIZED, errMsg);
        } else {
          this._hub.imm_SetErr(YAPI_IO_ERROR, errMsg);
        }
        this._hub.imm_disconnectNow();
      }
    });
    this.websocket.onclose = ((evt) => {
      if (this._hub.imm_getCurrentConnID() != tryOpenID) {
        if (this._hub._yapi._logLevel >= 4) {
          this._hub._yapi.imm_log("WebSocket close received for previous connection [" + tryOpenID + "], now using [" + this._hub.imm_getCurrentConnID() + "]");
        }
        return;
      }
      if (this._hub._yapi._logLevel >= 4) {
        this._hub._yapi.imm_log("WebSocket connection closed [" + tryOpenID + "]");
      }
      this._connectionState = 1;
      this.websocket = null;
      if (this._hub.retryDelay < 0) {
        this._hub.imm_commonDisconnect(tryOpenID, YAPI_IO_ERROR, "Websocket callback connection closed");
      }
      this.imm_dropAllPendingConnection();
      this._hub.imm_signalHubDisconnected(tryOpenID);
    });
    this.websocket.onerror = ((evt) => {
      if (this._hub.imm_getCurrentConnID() != tryOpenID) {
        if (this._hub._yapi._logLevel >= 4) {
          this._hub._yapi.imm_log("WebSocket error received for previous connection [" + tryOpenID + "]");
        }
        return;
      }
      if (evt.message && (!/ ETIMEDOUT /.test(evt.message) || this._hub._yapi._logLevel >= 4)) {
        if (this._hub._yapi._logLevel >= 3) {
          this._hub._yapi.imm_log("WebSocket error [" + tryOpenID + "]: ", evt);
        }
        if (evt.error.code == "DEPTH_ZERO_SELF_SIGNED_CERT") {
          this._hub.imm_commonDisconnect(tryOpenID, YAPI_SSL_UNK_CERT, evt.message);
        } else {
          this._hub.imm_SetErr(YAPI_IO_ERROR, evt.message);
        }
      }
      if (this._hub.retryDelay < 0) {
        this._hub.imm_commonDisconnect(tryOpenID, YAPI_IO_ERROR, "Websocket callback connection closed");
      }
      this._hub.imm_disconnectNow();
      this._hub.imm_signalHubDisconnected(tryOpenID);
    });
    if (this._hub.timeoutId) {
      clearTimeout(this._hub.timeoutId);
    }
    this._hub.timeoutId = setTimeout(() => {
      if (!this.imm_isForwarded()) {
        if (this._hub._yapi._logLevel >= 3) {
          this._hub._yapi.imm_log("WS: connection stalled during open [" + tryOpenID + "]");
        }
        this._hub.imm_disconnectNow();
      }
    }, this._hub.imm_getNetworkTimeout());
  }
  /** Compute websocket authentication sha1 key
   */
  imm_computeAuth(user, pass, serial, nonce) {
    let ha1_str = user + ":" + serial + ":" + pass;
    let ha1 = this._hub._yapi.imm_bin2hexstr(this._hub._yapi.imm_yMD5(ha1_str)).toLowerCase();
    let nonce8 = new Uint8Array([(nonce & 255) >>> 0, (nonce & 65280) >>> 8, (nonce & 16711680) >>> 16, nonce >>> 24]);
    let sha1_raw = ha1 + this._hub._yapi.imm_bin2hexstr(nonce8).toLowerCase();
    return this._hub._yapi.imm_ySHA1(sha1_raw.toLowerCase());
  }
  /** Tell if a websocket hub is currently forwarded and handled remotely
   */
  imm_isForwarded() {
    return this.fwd_connectionState == 5 && this.fwd_websocket !== null;
  }
  /** Handle an incoming packet
   **/
  async _webSocketMsg(arr_bytes) {
    try {
      if (this.imm_isForwarded()) {
        this.imm_updateLastPinfStamp();
        this.fwd_websocket.send(arr_bytes);
        return;
      }
      let reltime = (this._hub._yapi.GetTickCount() - this._connectionTime) / 1e3;
      let ystream = arr_bytes[0] >>> 3;
      let text = "";
      if (ystream == 8) {
        for (let i = 1; i < arr_bytes.length; i++) {
          text += String.fromCharCode(arr_bytes[i]);
        }
        this.imm_updateLastPinfStamp();
        await this._hub._yapi.parseEvents(this._hub, text);
        return;
      }
      let ws = this.websocket;
      let tcpchan = arr_bytes[0] & 7;
      if (ystream == 1 || ystream == 2 || ystream == 9) {
        if (tcpchan > 3) {
          this.imm_asyncWebSocketError(YAPI_IO_ERROR, "Unexpected frame for tcpChan " + tcpchan + " (" + ystream + ")");
          return;
        }
        let tcp_end = arr_bytes.length;
        let yreq = this.tcpChan[tcpchan];
        if (!yreq) {
          this.imm_asyncWebSocketError(YAPI_IO_ERROR, "Drop frame for closed tcpChan " + tcpchan + " (" + ystream + ")");
          return;
        }
        if (ystream == 9) {
          tcp_end--;
          let rcvId = arr_bytes[tcp_end];
          if (this._hub._yapi._logLevel >= 5) {
            this._hub._yapi.imm_log("async-" + rcvId + " close received");
          }
          if (yreq.asyncId == 0) {
            if (this._hub._yapi._logLevel >= 4) {
              this._hub._yapi.imm_log("async-" + rcvId + " close received while req @" + yreq._creat + " was pending");
            }
            this.imm_asyncWebSocketError(YAPI_IO_ERROR, "Asynchronous close received, sync reply request");
            return;
          } else if (yreq.asyncId != rcvId) {
            if (this._hub._yapi._logLevel >= 4) {
              this._hub._yapi.imm_log("async-" + rcvId + " close received instead of async-" + yreq.asyncId + " close");
            }
            this.imm_asyncWebSocketError(YAPI_IO_ERROR, "Incorrect async-close signature on tcpChan " + tcpchan);
            return;
          }
        }
        let oldArr = yreq.bin_result;
        let newArr = new Uint8Array(oldArr.length + tcp_end - 1);
        newArr.set(oldArr, 0);
        newArr.set(arr_bytes.subarray(1, tcp_end), oldArr.length);
        yreq.bin_result = newArr;
        if (ystream == 2 || ystream == 9) {
          this.tcpChan[tcpchan] = yreq.next;
          if (ystream == 2) {
            if (yreq.asyncId != 0) {
              if (this._hub._yapi._logLevel >= 4) {
                this._hub._yapi.imm_log("Synchronous close received instead of async-" + yreq.asyncId + " close");
              }
              this.imm_asyncWebSocketError(YAPI_IO_ERROR, "Synchronous close received, async ack expected");
              return;
            } else if (this.websocket) {
              if (yreq.toBeSent && yreq.sendPos < yreq.toBeSent.length) {
                if (this._hub._yapi._logLevel >= 3) {
                  this._hub._yapi.imm_log("WS: tcpclose at " + yreq.sendPos + " < " + yreq.toBeSent.length);
                }
                this._hub.imm_disconnectNow();
                if (yreq.timeoutId) {
                  clearTimeout(yreq.timeoutId);
                }
                if (yreq.asyncId == 0) {
                  yreq.errorType = YAPI_IO_ERROR;
                  yreq.errorMsg = "TCP closed during upload";
                  if (yreq.acceptor) {
                    try {
                      yreq.acceptor(yreq);
                    } catch (e) {
                    }
                  }
                }
                return;
              }
              if (yreq.timeoutId) {
                let frame = new Uint8Array(1);
                frame[0] = (2 << 3) + tcpchan;
                this.websocket.send(frame);
              }
            }
          }
          if (yreq.timeoutId) {
            clearTimeout(yreq.timeoutId);
            yreq.timeoutId = 0;
          }
          let pos = yreq.bin_result.indexOf(13);
          if (pos < 0) {
            yreq.errorType = YAPI_IO_ERROR;
            yreq.errorMsg = "Bad response header";
          } else {
            let header = this._hub._yapi.imm_bin2str(yreq.bin_result.subarray(0, pos));
            let words = header.split(" ");
            if (words[0] == "OK") {
              yreq.errorType = YAPI_SUCCESS;
              let nextpos = yreq.bin_result.indexOf(13, pos + 2);
              while (nextpos > pos + 2) {
                pos = nextpos;
                nextpos = yreq.bin_result.indexOf(13, pos + 2);
              }
              if (nextpos < 0) {
                nextpos = pos;
              }
              yreq.bin_result = yreq.bin_result.subarray(nextpos + 2);
            } else if (words[0] == "0K") {
              yreq.errorType = YAPI_IO_ERROR;
              yreq.errorMsg = "Unexpected persistent connection";
            } else {
              let status = parseInt(words[1]);
              yreq.errorType = status == 401 ? YAPI_UNAUTHORIZED : YAPI_IO_ERROR;
              yreq.errorMsg = "HTTP error " + header.slice(words[0].length + 1) + " on " + yreq.devUrl;
            }
          }
          if (yreq.asyncId == 0) {
            if (this._hub._yapi._logLevel >= 5) {
              this._hub._yapi.imm_log("request @" + yreq._creat + " done, status=" + yreq.errorType);
            }
            this.imm_sendPendingRequest(tcpchan);
            if (yreq.acceptor) {
              yreq.acceptor(yreq);
            }
          } else {
            if (yreq.errorType != YAPI_SUCCESS) {
              this.imm_asyncWebSocketError(YAPI_IO_ERROR, "Async request error: " + yreq.errorMsg);
            }
          }
        }
        return;
      }
      if (!this.websocket) {
        return;
      }
      if (ystream == 5) {
        let metatype = arr_bytes[1];
        switch (metatype) {
          case 4:
            if (arr_bytes.length < 1 + this._USB_META_WS_ANNOUNCE_SIZE) {
              return;
            }
            this._remoteVersion = arr_bytes[2];
            if (this._remoteVersion < 1) {
              return;
            }
            let maxtcpws = (arr_bytes[3] << 4) + (arr_bytes[4] << 12);
            if (maxtcpws > 0) {
              this._tcpMaxWindowSize = maxtcpws;
            }
            this._remoteNonce = arr_bytes[5] + (arr_bytes[6] << 8) + (arr_bytes[7] << 16) + (arr_bytes[8] << 24);
            for (let i = 9; i < 9 + 20; i++) {
              if (arr_bytes[i] == 0) {
                this._remoteSerial = this._hub._yapi.imm_bin2str(arr_bytes.subarray(9, i));
                break;
              }
            }
            let nonce = new Uint8Array(4);
            this.imm_getRandomValues(nonce);
            this._nonce = nonce[0] + (nonce[1] << 8) + (nonce[2] << 16) + (nonce[3] << 24);
            this._connectionTime = this._hub._yapi.GetTickCount();
            this._connectionState = 3;
            let frame = new Uint8Array(1 + this._USB_META_WS_AUTHENTICATION_SIZE);
            let version = this._remoteVersion < 2 ? this._remoteVersion : 2;
            let flags = 0;
            frame[0] = 5 << 3;
            frame[1] = 5;
            frame[2] = version;
            if (this._runtime_urlInfo.imm_getPass() != "") {
              flags = this._USB_META_WS_VALID_SHA1;
              let sha1 = this.imm_computeAuth(this._runtime_urlInfo.imm_getUser(), this._runtime_urlInfo.imm_getPass(), this._remoteSerial, this._remoteNonce);
              for (let i = 0; i < sha1.length; i++) {
                frame[9 + i] = sha1[i];
              }
            }
            frame[3] = flags & 255;
            frame[4] = flags >>> 8;
            frame[5] = this._nonce & 255;
            frame[6] = this._nonce >>> 8 & 255;
            frame[7] = this._nonce >>> 16 & 255;
            frame[8] = this._nonce >>> 24 & 255;
            this.websocket.send(frame);
            break;
          case 5:
            if (this._connectionState != 3) {
              return;
            }
            if (arr_bytes.length < 1 + this._USB_META_WS_AUTHENTICATION_SIZE) {
              return;
            }
            this._tcpRoundTripTime = this._hub._yapi.GetTickCount() - this._connectionTime + 1;
            if (this._tcpMaxWindowSize < 2048 && this._tcpRoundTripTime < 7) {
              this._tcpRoundTripTime = 7;
            }
            let uploadRate = this._tcpMaxWindowSize * 1e3 / this._tcpRoundTripTime >> 0;
            if (this._hub._yapi._logLevel >= 4) {
              this._hub._yapi.imm_log("RTT=" + this._tcpRoundTripTime + "ms, WS=" + this._tcpMaxWindowSize + ", uploadRate=" + uploadRate / 1e3 + " KB/s");
            }
            this._remoteVersion = arr_bytes[2];
            if (this._remoteVersion < 1) {
              return;
            }
            let inflags = arr_bytes[3] + (arr_bytes[4] << 8);
            if ((inflags & this._USB_META_WS_RW) != 0) {
              this._hub.imm_setRwAccess(true);
            } else {
              this._hub.imm_setRwAccess(false);
            }
            if ((inflags & this._USB_META_WS_VALID_SHA1) != 0) {
              let remote_sha1 = arr_bytes.subarray(9, 29);
              let sha1 = this.imm_computeAuth(this._runtime_urlInfo.imm_getUser(), this._runtime_urlInfo.imm_getPass(), this._remoteSerial, this._nonce);
              for (let i = 0; i < sha1.length; i++) {
                if (sha1[i] != remote_sha1[i]) {
                  this._session_errno = 401;
                  this._session_error = "Authentication failed";
                  this._connectionState = 0;
                  return;
                }
              }
              this._connectionState = 4;
            } else {
              if (this._runtime_urlInfo.imm_getPass() == "") {
                this._connectionState = 4;
              } else {
                this._session_errno = 401;
                if (this._runtime_urlInfo.imm_getUser() == "admin" && !this._hub.imm_isRwAccess()) {
                  this._session_error = "Authentication as admin failed";
                } else {
                  this._session_error = "Password not set on remote hub";
                }
                this._connectionState = 0;
                return;
              }
            }
            break;
          case 6:
            this._session_errno = arr_bytes[3] + (arr_bytes[4] << 8);
            if (this._session_errno == 401) {
              this._session_error = "Authentication failed";
            } else {
              this._session_error = "Remote hub closed connection with error " + this._session_errno;
            }
            this._connectionState = 0;
            break;
          case 7:
            tcpchan = arr_bytes[2];
            if (this.tcpChan[tcpchan]) {
              let yreq = this.tcpChan[tcpchan];
              let ackBytes = arr_bytes[3] + (arr_bytes[4] << 8) + (arr_bytes[5] << 16) + (arr_bytes[6] << 24);
              let ackTime = this._hub._yapi.GetTickCount();
              if (this._lastUploadAckTime[tcpchan] != 0 && ackBytes > this._lastUploadAckBytes[tcpchan]) {
                this._lastUploadAckBytes[tcpchan] = ackBytes;
                this._lastUploadAckTime[tcpchan] = ackTime;
                let deltaBytes = ackBytes - this._lastUploadRateBytes[tcpchan];
                let deltaTime = ackTime - this._lastUploadRateTime[tcpchan];
                if (deltaTime < 500)
                  break;
                if (deltaTime < 1e3 && deltaBytes < 65536)
                  break;
                this._lastUploadRateBytes[tcpchan] = ackBytes;
                this._lastUploadRateTime[tcpchan] = ackTime;
                if (yreq.progressCb && yreq.toBeSent) {
                  yreq.progressCb(ackBytes, yreq.toBeSent.length);
                }
                let newRate = deltaBytes * 1e3 / deltaTime;
                this._uploadRate[tcpchan] = 0.8 * this._uploadRate[tcpchan] + 0.3 * newRate >> 0;
                if (this._hub._yapi._logLevel >= 5) {
                  this._hub._yapi.imm_log("New rate: " + this._uploadRate[tcpchan] / 1e3 + " KB/s (last " + (deltaBytes / 1e3 >> 0) + "KB sent at " + (newRate >> 0) / 1e3 + " KB/s)");
                }
              } else {
                this._lastUploadAckBytes[tcpchan] = ackBytes;
                this._lastUploadAckTime[tcpchan] = ackTime;
                this._lastUploadRateBytes[tcpchan] = ackBytes;
                this._lastUploadRateTime[tcpchan] = ackTime;
                if (yreq.progressCb && yreq.toBeSent) {
                  yreq.progressCb(ackBytes, yreq.toBeSent.length);
                }
                this.imm_sendPendingRequest(tcpchan);
              }
            }
            break;
        }
        return;
      }
      this.imm_asyncWebSocketError(YAPI_IO_ERROR, "Unsupported message: " + this._hub._yapi.imm_bin2hexstr(arr_bytes));
    } catch (e) {
      this._hub._yapi.imm_log("Unhandled exception in _webSocketMsg:", e);
    }
  }
  /** Send an outgoing packet
   **/
  imm_webSocketSend(arr_bytes) {
    if (this.websocket) {
      this.websocket.send(arr_bytes);
    }
  }
  imm_hasPendingRequest() {
    for (let tcpchan = 0; tcpchan < 4; tcpchan++) {
      let queue = this.tcpChan[tcpchan];
      if (queue) {
        return true;
      }
    }
    return false;
  }
  async waitForPendingQueries(ms_duration) {
    let end = this._hub._yapi.GetTickCount() + ms_duration;
    let remaining = ms_duration;
    while (this.imm_hasPendingRequest() && remaining > 0) {
      let waitTime = Math.min(remaining, 25);
      await new Promise((resolve, reject) => {
        setTimeout(resolve, waitTime);
      });
      remaining = end - this._hub._yapi.GetTickCount();
    }
  }
  /** Perform an HTTP query on the hub
   */
  async request(method, devUrl, obj_body, tcpchan) {
    if (this._hub._yapi._logLevel >= 3) {
      this._hub.imm_logrequest(method, devUrl, obj_body);
    }
    let httpPromise = new Promise((resolve, reject) => {
      let subReq = method + " " + devUrl + " \r\n\r\n";
      let ws = this.websocket;
      let isAsync = this._remoteVersion > 0 && devUrl.slice(-2) == "&.";
      let yreq = new YHTTPRequest(new Uint8Array(0));
      if (this._hub._yapi._logLevel >= 5) {
        yreq._creat = (Date.now() % 6e5).toString();
        this._hub._yapi.imm_log("request @" + yreq._creat + ": " + method + " " + devUrl);
      }
      yreq.acceptor = resolve;
      yreq.devUrl = devUrl;
      yreq.sendPos = 0;
      if (obj_body) {
        let boundary = this._hub.imm_getBoundary();
        let body = this._hub.imm_formEncodeBody(obj_body, boundary);
        subReq = subReq.slice(0, -2) + "Content-Type: x-upload, boundary=" + boundary + "\r\n\r\n";
        yreq.toBeSent = new Uint8Array(subReq.length + body.length);
        yreq.toBeSent.set(body, subReq.length);
        yreq.progressCb = obj_body.progressCb;
      } else {
        yreq.toBeSent = new Uint8Array(subReq.length);
      }
      for (let i = 0; i < subReq.length; i++) {
        yreq.toBeSent[i] = subReq.charCodeAt(i);
      }
      if (tcpchan > 3) {
        yreq.errorType = YAPI_IO_ERROR;
        yreq.errorMsg = "Unsupported tcpChan " + tcpchan;
        try {
          yreq.acceptor(yreq);
        } catch (e) {
        }
        return;
      }
      if (!ws || this._hub.imm_isDisconnecting() || this._connectionState != 5) {
        if (this._hub._yapi._logLevel >= 4) {
          let wsState = ws ? " websocket=NULL" : "";
          let dsState = this._hub.imm_isDisconnecting() ? " disconnecting" : "";
          let cnState = this._connectionState != 5 ? " connState=" + this._connectionState : "";
          this._hub._yapi.imm_log("request @" + yreq._creat + " failed, websocket is down:" + wsState + dsState + cnState);
        }
        yreq.errorType = YAPI_IO_ERROR;
        yreq.errorMsg = "WebSocket not connected";
        try {
          yreq.acceptor(yreq);
        } catch (e) {
        }
        return;
      }
      if (isAsync) {
        yreq.asyncId = this.nextAsyncId++;
        if (this.nextAsyncId >= 127) {
          this.nextAsyncId = 48;
        }
      }
      let queue = this.tcpChan[tcpchan];
      if (queue) {
        while (queue.next) {
          queue = queue.next;
        }
        queue.next = yreq;
      } else {
        this.tcpChan[tcpchan] = yreq;
      }
      this.imm_sendPendingRequest(tcpchan);
    });
    return httpPromise;
  }
  /** Send all possible pending requests on specified tcpchan
   */
  imm_sendPendingRequest(tcpchan) {
    let yreq = this.tcpChan[tcpchan];
    while (yreq) {
      if (!this.websocket || this._hub.imm_isDisconnecting() || this._connectionState != 5) {
        if (this._hub._yapi._logLevel >= 4) {
          this._hub._yapi.imm_log("request @" + yreq._creat + " failed, websocket is down");
        }
        yreq.errorType = YAPI_IO_ERROR;
        yreq.errorMsg = "WebSocket not connected";
        if (yreq.acceptor) {
          try {
            yreq.acceptor(yreq);
          } catch (e) {
          }
        }
        yreq = yreq.next;
        continue;
      }
      let pendingCount = 1;
      for (let yr = yreq; yr.next; yr = yr.next) {
        pendingCount++;
      }
      if (!yreq.toBeSent) {
        if (yreq.asyncId == 0) {
          if (this._hub._yapi._logLevel >= 5) {
            this._hub._yapi.imm_log(pendingCount.toString() + " req pending, @" + yreq._creat + " not completed");
          }
          return;
        }
        yreq = yreq.next;
        continue;
      }
      let isAsync = yreq.asyncId != 0;
      let asyncCloseSet = false;
      let pos = yreq.sendPos;
      let end = yreq.toBeSent.length;
      let i, frame;
      if (end > 2108 && this._remoteVersion >= 2 && tcpchan == 0) {
        if (pos == 0) {
          end = 2108;
          this._lastUploadAckBytes[tcpchan] = 0;
          this._lastUploadAckTime[tcpchan] = 0;
          this._uploadRate[tcpchan] = this._tcpMaxWindowSize * 1e3 / this._tcpRoundTripTime >> 0;
        } else if (this._lastUploadAckTime[tcpchan] == 0) {
          if (yreq.sendTimeoutId)
            clearTimeout(yreq.sendTimeoutId);
          yreq.sendTimeoutId = setTimeout(() => {
            this.imm_sendPendingRequest(tcpchan);
          }, this._tcpRoundTripTime);
          return;
        } else {
          let bytesOnTheAir = pos - this._lastUploadAckBytes[tcpchan];
          let uploadRate = this._uploadRate[tcpchan];
          let timeOnTheAir = this._hub._yapi.GetTickCount() - this._lastUploadAckTime[tcpchan];
          let toBeSent = 2 * uploadRate + 1024 - bytesOnTheAir + uploadRate * timeOnTheAir / 1e3 >> 0;
          if (toBeSent + bytesOnTheAir > this._DEFAULT_TCP_MAX_WINDOW_SIZE) {
            toBeSent = this._DEFAULT_TCP_MAX_WINDOW_SIZE - bytesOnTheAir;
          }
          if (toBeSent < 64) {
            let waitTime = 1e3 * (128 - toBeSent) / uploadRate >> 0;
            if (waitTime < 2)
              waitTime = 2;
            if (yreq.sendTimeoutId)
              clearTimeout(yreq.sendTimeoutId);
            yreq.sendTimeoutId = setTimeout(() => {
              this.imm_sendPendingRequest(tcpchan);
            }, waitTime);
            return;
          }
          if (end > pos + toBeSent) {
            if (toBeSent > 124) {
              toBeSent = (toBeSent / 124 >> 0) * 124;
            }
            end = pos + toBeSent;
          }
        }
      }
      while (pos < end) {
        let framelen = 1 + end - pos;
        if (framelen > 125)
          framelen = 125;
        let datalen = framelen - 1;
        if (pos < 180 && pos + datalen >= 192) {
          datalen = 191 - pos;
          framelen = datalen + 1;
        }
        if (isAsync && pos + datalen == yreq.toBeSent.length && framelen < 125) {
          frame = new Uint8Array(framelen + 1);
          frame[0] = 8 * 9 + tcpchan;
          frame[framelen] = yreq.asyncId;
          asyncCloseSet = true;
        } else {
          frame = new Uint8Array(framelen);
          frame[0] = 8 * 1 + tcpchan;
        }
        frame.set(yreq.toBeSent.subarray(pos, pos + datalen), 1);
        pos += datalen;
        this.imm_webSocketSend(frame);
      }
      let sent = pos - yreq.sendPos;
      yreq.sendPos = pos;
      if (yreq.sendPos < yreq.toBeSent.length) {
        let waitTime = 1e3 * sent / this._uploadRate[tcpchan] >> 0;
        if (waitTime < 2)
          waitTime = 2;
        if (yreq.sendTimeoutId)
          clearTimeout(yreq.sendTimeoutId);
        yreq.sendTimeoutId = setTimeout(() => {
          this.imm_sendPendingRequest(tcpchan);
        }, waitTime);
        return;
      }
      if (isAsync && !asyncCloseSet) {
        frame = new Uint8Array(2);
        frame[0] = 8 * 9 + tcpchan;
        frame[1] = yreq.asyncId;
        this.imm_webSocketSend(frame);
      }
      yreq.toBeSent = null;
      if (isAsync && yreq.acceptor) {
        try {
          yreq.acceptor(yreq);
        } catch (e) {
          this._hub._yapi.imm_log("WS: async acceptor exception: ", e);
        }
      }
      let mstimeout = this._hub._yapi._networkTimeoutMs;
      if (yreq.devUrl) {
        if (yreq.devUrl.indexOf("/testcb.txt") >= 0) {
          mstimeout = this._YIO_1_MINUTE_TCP_TIMEOUT;
        } else if (yreq.devUrl.indexOf("/logger.json") >= 0) {
          mstimeout = this._YIO_1_MINUTE_TCP_TIMEOUT;
        } else if (yreq.devUrl.indexOf("/rxmsg.json") >= 0) {
          mstimeout = this._YIO_1_MINUTE_TCP_TIMEOUT;
        } else if (yreq.devUrl.indexOf("/rxdata.bin") >= 0) {
          mstimeout = this._YIO_1_MINUTE_TCP_TIMEOUT;
        } else if (yreq.devUrl.indexOf("/at.txt") >= 0) {
          mstimeout = this._YIO_1_MINUTE_TCP_TIMEOUT;
        } else if (yreq.devUrl.indexOf("/files.json") >= 0) {
          mstimeout = this._YIO_1_MINUTE_TCP_TIMEOUT;
        } else if (yreq.devUrl.indexOf("/upload.html") >= 0) {
          mstimeout = this._YIO_10_MINUTES_TCP_TIMEOUT;
        } else if (yreq.devUrl.indexOf("/flash.json") >= 0) {
          mstimeout = this._YIO_10_MINUTES_TCP_TIMEOUT;
        } else if (yreq.devUrl.indexOf("/Yvw4I.js") >= 0) {
          mstimeout = this._YIO_10_MINUTES_TCP_TIMEOUT;
        }
      }
      yreq.timeoutId = setTimeout((chan, yr) => {
        this.imm_abortRequest(chan, yr);
      }, mstimeout, tcpchan, yreq);
      yreq._sent = (Date.now() % 6e5).toString();
      if (this._hub._yapi._logLevel >= 5) {
        this._hub._yapi.imm_log("req @" + yreq._creat + " sent (1/" + pendingCount.toString() + ")" + (isAsync ? " async-" + yreq.asyncId + ", continue" : ", waiting for reply"));
      }
      if (!isAsync) {
        return;
      }
      yreq = yreq.next;
    }
  }
  // Abort a request and send close packet to peer
  //
  imm_abortRequest(tcpchan, yreq) {
    if (!yreq.timeoutId)
      return;
    yreq.timeoutId = null;
    if (yreq.asyncId == 0) {
      let frame = new Uint8Array(1);
      frame[0] = 8 * 2 + tcpchan;
      this.imm_webSocketSend(frame);
      if (this._hub._yapi._logLevel >= 4) {
        let pendingCount = 1;
        for (let yr = yreq; yr.next; yr = yr.next) {
          pendingCount++;
        }
        this._hub._yapi.imm_log(pendingCount.toString() + " req pending, @" + yreq._creat + " is in timeout");
      }
      setTimeout((chan, yr) => {
        this._hub._yapi.imm_log("Dropping synchronous request after timeout: " + yr.devUrl);
        this.imm_forgetRequest(chan, yr);
      }, 5e3, tcpchan, yreq);
    }
    this.imm_asyncWebSocketError(YAPI_IO_ERROR, "Timeout on " + yreq.devUrl + " (tcpchan " + tcpchan + ")");
  }
  // Drop a request from queue in case of timeout after abort
  //
  imm_forgetRequest(tcpchan, yreq) {
    let queue = this.tcpChan[tcpchan];
    if (queue == yreq) {
      this.tcpChan[tcpchan] = yreq.next;
      if (yreq.asyncId == 0) {
        yreq.errorType = YAPI_IO_ERROR;
        yreq.errorMsg = "Timeout on " + yreq.devUrl + " (tcpchan " + tcpchan + ")";
        if (yreq.acceptor) {
          try {
            yreq.acceptor(yreq);
          } catch (e) {
          }
        }
      }
      this.imm_sendPendingRequest(tcpchan);
    }
  }
  // Drop all pending requests from queues, as well as forwarded connection, when a hub connection is dropped
  //
  imm_dropAllPendingConnection() {
    if (this.fwd_connectionState != 1 && this.fwd_websocket) {
      this.fwd_connectionState = 1;
      this.fwd_websocket.close();
      this.fwd_websocket = null;
    }
    for (let tcpchan = 0; tcpchan < this.tcpChan.length; tcpchan++) {
      for (let yreq = this.tcpChan[tcpchan]; yreq; yreq = yreq.next) {
        this.tcpChan[tcpchan] = yreq.next;
        if (yreq.timeoutId) {
          clearTimeout(yreq.timeoutId);
          yreq.timeoutId = 0;
        }
        if (yreq.asyncId == 0) {
          if (this._hub._yapi._logLevel >= 4) {
            this._hub._yapi.imm_log("drop @" + yreq._creat + " (websocket down)");
          }
          yreq.errorType = YAPI_IO_ERROR;
          yreq.errorMsg = "Request " + yreq.devUrl + " dropped (websocket down)";
          if (yreq.acceptor) {
            try {
              yreq.acceptor(yreq);
            } catch (e) {
            }
          }
        }
      }
    }
  }
  async websocketJoin(ws, arr_credentials, close_callback) {
    if (this._connectionState != 5) {
      this.imm_asyncWebSocketError(YAPI_IO_ERROR, "Hub is disconnected, cannot join");
      return false;
    }
    this.fwd_websocket = ws;
    this.fwd_credentials = arr_credentials;
    this.fwd_closeCallback = close_callback;
    this.fwd_connectionState = 2;
    ws.onmessage = ((evt) => {
      if (this.fwd_connectionState == 5) {
        if (this._connectionState == 5) {
          this.imm_webSocketSend(evt.data);
        } else {
          this._hub._yapi.imm_log("WS: drop packet from fwd API (state=" + this._connectionState + ")");
        }
      } else if (this.fwd_connectionState == 3) {
        this.imm_handleAPIAuthPkt(evt.data);
      } else {
        this._hub._yapi.imm_log("WS: drop packet from fwd API (fwd_state=" + this.fwd_connectionState + ")");
      }
    });
    ws.onclose = ((evt) => {
      this.fwd_connectionState = 1;
      this.fwd_websocket = null;
      if (this.fwd_closeCallback) {
        this.fwd_closeCallback();
      }
    });
    return this.imm_sendAPIAnnouncePkt();
  }
  imm_sendAPIAnnouncePkt() {
    if (!this.fwd_websocket) {
      return false;
    }
    let frame = new Uint8Array(1 + this._USB_META_WS_ANNOUNCE_SIZE);
    let nonce = new Uint8Array(4);
    this.imm_getRandomValues(nonce);
    frame[0] = 5 << 3;
    frame[1] = 4;
    frame[2] = 2;
    frame[3] = this._tcpMaxWindowSize >> 4 & 255;
    frame[4] = this._tcpMaxWindowSize >> 12 & 255;
    for (let i = 0; i < 4; i++) {
      frame[5 + i] = nonce[i];
    }
    for (let i = 0; i < this._remoteSerial.length && i < 20; i++) {
      frame[9 + i] = this._remoteSerial.charCodeAt(i);
    }
    this.fwd_nonce = frame[5] + (frame[6] << 8) + (frame[7] << 16) + (frame[8] << 24);
    this.fwd_connectionState = 3;
    this.fwd_websocket.send(frame);
    return true;
  }
  imm_handleAPIAuthPkt(msg) {
    if (msg.length < 1 + this._USB_META_WS_AUTHENTICATION_SIZE || msg[0] != 5 << 3) {
      if (this._hub._yapi._logLevel >= 3) {
        this._hub._yapi.imm_log("bad-apiauth1\n");
      }
      this.fwd_connectionState = 0;
      return;
    }
    if (msg[1] != 5 || msg[2] > 2) {
      if (this._hub._yapi._logLevel >= 3) {
        this._hub._yapi.imm_log("bad-apiauth2\n");
      }
      this.fwd_connectionState = 0;
      return;
    }
    this._remoteVersion = msg[2];
    let flags = msg[3] + (msg[4] << 8);
    if ((flags & this._USB_META_WS_VALID_SHA1) == 0) {
      if (this._hub._yapi._logLevel >= 3) {
        this._hub._yapi.imm_log("bad-apiauth3\n");
      }
      this.fwd_connectionState = 0;
      return;
    }
    if (!this.fwd_websocket) {
      if (this._hub._yapi._logLevel >= 3) {
        this._hub._yapi.imm_log("no-fwd-ws\n");
      }
      this.fwd_connectionState = 0;
      return;
    }
    let credIdx, remote_sha1 = msg.subarray(9, 29);
    let credentials = this.fwd_credentials;
    for (credIdx = 0; credIdx < credentials.length; credIdx++) {
      let j, sha12 = this.imm_computeAuth(credentials[credIdx].user, credentials[credIdx].pass, this._remoteSerial, this.fwd_nonce);
      for (j = 0; j < sha12.length; j++) {
        if (sha12[j] != remote_sha1[j])
          break;
      }
      if (j >= sha12.length)
        break;
    }
    if (credIdx >= credentials.length) {
      if (this._hub._yapi._logLevel >= 3) {
        this._hub._yapi.imm_log("bad-apiauth4\n");
      }
      msg.fill(0, 3);
      this.fwd_websocket.send(msg);
      this.fwd_connectionState = 0;
      return;
    }
    msg[3] |= this._USB_META_WS_RW;
    this.fwd_nonce = msg[5] + (msg[6] << 8) + (msg[7] << 16) + (msg[8] << 24);
    let sha1 = this.imm_computeAuth(credentials[credIdx].user, credentials[credIdx].pass, this._remoteSerial, this.fwd_nonce);
    for (let i = 0; i < sha1.length; i++) {
      msg[9 + i] = sha1[i];
    }
    this.fwd_websocket.send(msg);
    this.fwd_connectionState = 5;
  }
  async detach(errType = YAPI.IO_ERROR, errMsg = "Hub has been forcibly detached") {
    let tcpchan_busy;
    let timeout = this._hub._yapi.GetTickCount() + 3e3;
    do {
      tcpchan_busy = false;
      for (let tcpchan = 0; tcpchan < 4; tcpchan++) {
        if (this.tcpChan[tcpchan] != null) {
          tcpchan_busy = true;
          break;
        }
      }
      if (tcpchan_busy) {
        await this._hub._yapi._microSleep_internal();
      }
    } while (tcpchan_busy && timeout > this._hub._yapi.GetTickCount());
    this._hub.imm_commonDisconnect("detach", errType, errMsg);
    this._hub.imm_disconnectNow();
  }
  // abort communication channel immediately
  //
  // If a connectionID is passed as argument, only abort the
  // communication channel if the ID matched current connection
  //
  imm_disconnectEngineNow(connID = "") {
    if (!this.websocket) {
      return;
    }
    this._connectionState = 1;
    let prevOpenID = connID ? connID : this._hub.imm_getCurrentConnID();
    let websocket = this.websocket;
    this._hub.imm_setCurrentConnID("");
    this.websocket = null;
    websocket.onclose = null;
    websocket.onerror = null;
    try {
      websocket.close();
    } catch (e) {
    }
    if (websocket.terminate) {
      setTimeout(() => {
        try {
          if (websocket.terminate) {
            websocket.terminate();
          }
        } catch (e) {
        }
      }, 900);
    }
    this.imm_dropAllPendingConnection();
    this._hub.imm_signalHubDisconnected(prevOpenID);
  }
  imm_isConnected() {
    if (this._connectionState != 5) {
      return false;
    }
    return super.imm_isConnected();
  }
};
var YGenericSSDPManager = class {
  constructor(obj_yapi) {
    this._started = false;
    this._callback = null;
    this._SSDPCache = {};
    this._thread = null;
    this.YSSDP_PORT = 1900;
    this.YSSDP_MCAST_ADDR_STR = "239.255.255.250";
    this.YSSDP_URN_YOCTOPUCE = "urn:yoctopuce-com:device:hub:1";
    this.YSSDP_DISCOVERY_MSG = "M-SEARCH * HTTP/1.1\r\nHOST:" + this.YSSDP_MCAST_ADDR_STR + ":" + this.YSSDP_PORT + '\r\nMAN:"ssdp:discover"\r\nMX:5\r\nST:' + this.YSSDP_URN_YOCTOPUCE + "\r\n\r\n";
    this._yapi = obj_yapi;
  }
  async _invokeCallback(str_serial, str_addUrl, str_removeUrl) {
    if (this._callback) {
      try {
        await this._callback(str_serial, str_addUrl, str_removeUrl);
      } catch (e) {
        this._yapi.imm_log("Exception in hub discovery callback:", e);
      }
    }
  }
  imm_uuidToSerial(str_uuid) {
    let s = "", pad = "";
    let i = 0, u = 0;
    for (; i < 4; i++, u += 2) {
      s += String.fromCharCode(parseInt(str_uuid.substr(u, 2), 16));
    }
    u++;
    for (; i < 6; i++, u += 2) {
      s += String.fromCharCode(parseInt(str_uuid.substr(u, 2), 16));
    }
    u++;
    for (; i < 8; i++, u += 2) {
      s += String.fromCharCode(parseInt(str_uuid.substr(u, 2), 16));
    }
    s += "-";
    u = str_uuid.indexOf("-COFF-EE");
    if (u < 0) {
      return null;
    }
    u += 8;
    while (str_uuid.charAt(u) === "0") {
      u++;
    }
    if (s.substr(0, 8) === "VIRTHUB0") {
      pad = "0000000000";
    } else {
      pad = "00000";
    }
    s += pad.substr(str_uuid.length - u);
    s += str_uuid.substr(u);
    return s;
  }
  async ySSDPUpdateCache(str_uuid, str_url, int_cacheValidity) {
    if (int_cacheValidity <= 0) {
      int_cacheValidity = 1800;
    }
    int_cacheValidity *= 1e3;
    let now = this._yapi.GetTickCount();
    let p = this._SSDPCache[str_uuid];
    if (p) {
      p.detectedTime = now;
      p.maxAge = int_cacheValidity;
      if (str_url !== p.url) {
        await this._invokeCallback(p.serial, str_url, p.url);
        p.url = str_url;
      } else {
        await this._invokeCallback(p.serial, str_url, null);
      }
    } else {
      let serial = this.imm_uuidToSerial(str_uuid);
      if (serial) {
        this._SSDPCache[str_uuid] = {
          "serial": serial,
          "url": str_url,
          "detectedTime": now,
          "maxAge": int_cacheValidity
        };
        await this._invokeCallback(serial, str_url, null);
      }
    }
  }
  async ySSDPParseMessage(str_msg) {
    let SSDP_HTTP = "HTTP/1.1 200 OK";
    let SSDP_NOTIFY = "NOTIFY * HTTP/1.1";
    let lines = str_msg.split("\r\n");
    let values = {};
    if (lines[0] === SSDP_HTTP || lines[0] === SSDP_NOTIFY) {
      for (let i = 1; i < lines.length; i++) {
        let parts = lines[i].split(": ");
        if (parts.length === 2) {
          values[parts[0].trim()] = parts[1].trim();
        }
      }
      if (values["LOCATION"] && values["USN"] && values["CACHE-CONTROL"] && values["USN"].indexOf(this.YSSDP_URN_YOCTOPUCE) > 0) {
        let uuid = values["USN"].split(":")[1];
        let location2 = values["LOCATION"].split("/")[2];
        let cacheVal = parseInt(values["CACHE-CONTROL"]);
        await this.ySSDPUpdateCache(uuid, location2, cacheVal);
      }
    }
  }
  async ySSDPCheckExpiration() {
    let now = this._yapi.GetTickCount();
    if (this._thread) {
      clearTimeout(this._thread);
      this._thread = null;
    }
    for (let uuid in this._SSDPCache) {
      let p = this._SSDPCache[uuid];
      if (!p)
        continue;
      if (now - p.detectedTime > p.maxAge) {
        p.maxAge = 0;
        await this._invokeCallback(p.serial, null, p.url);
      }
    }
    this._thread = setTimeout(() => {
      this.ySSDPCheckExpiration();
    }, 3e3);
  }
  async ySSDPStart(func_callback) {
    if (this._started) {
      return YAPI.SUCCESS;
    }
    this._callback = func_callback;
    await this.ySSDPOpenSockets();
    this.ySSDPDiscover();
    this._started = true;
    await this.ySSDPCheckExpiration();
    return YAPI.SUCCESS;
  }
  async ySSDPStop() {
    if (this._thread) {
      clearTimeout(this._thread);
      this._thread = null;
    }
    await this.ySSDPCloseSockets();
    for (let uuid in this._SSDPCache) {
      let p = this._SSDPCache[uuid];
      if (!p)
        continue;
      if (p.maxAge) {
        await this._yapi.UnregisterHub(p.url);
        p.maxAge = 0;
        await this._invokeCallback(p.serial, null, p.url);
      }
    }
    this._SSDPCache = {};
    this._started = false;
  }
  async ySSDPDiscover() {
    for (let rep = 0; rep < 3; rep++) {
      await YAPI.Sleep(10 << rep);
      await this.ySSDPSendPacket(this.YSSDP_DISCOVERY_MSG, this.YSSDP_PORT, this.YSSDP_MCAST_ADDR_STR);
    }
  }
};
var YHub = class _YHub {
  //--- (end of generated code: YHub attributes declaration)
  constructor(obj_yapi, hubref) {
    this._hubref = 0;
    this.TRYING = 1;
    this.CONNECTED = 2;
    this.RECONNECTING = 3;
    this.ABORTED = 4;
    this.UNREGISTERED = 5;
    this._ctx = obj_yapi;
    this._hubref = hubref;
  }
  _imm_getStrAttr(attrName) {
    let hub = this._ctx.getGenHub(this._hubref);
    if (hub == null) {
      return "";
    }
    switch (attrName) {
      case "registeredUrl":
        return hub.imm_getOriginalURL();
      case "connectionUrl":
        return hub.imm_getRootUrl();
      case "serialNumber":
        return hub.imm_getSerialNumber();
      case "errorMessage":
        return hub.get_errorMessage();
      default:
        return "";
    }
  }
  _imm_getIntAttr(attrName) {
    let hub = this._ctx.getGenHub(this._hubref);
    if (attrName == "isInUse") {
      return hub != null ? 1 : 0;
    }
    if (attrName == "connectionState") {
      if (hub == null) {
        return _YHub.UNREGISTERED;
      }
      return hub.imm_getConnectionState();
    }
    if (hub == null) {
      return -1;
    }
    switch (attrName) {
      case "isOnline":
        return hub.imm_isOnline() ? 1 : 0;
      case "networkTimeout":
        return hub.imm_getNetworkTimeout();
      case "errorType":
        return hub.get_errorType();
      default:
        return -1;
    }
  }
  async _getIntAttr(attrName) {
    if (attrName == "isReadOnly") {
      let hub = this._ctx.getGenHub(this._hubref);
      if (hub == null) {
        return -1;
      }
      return await hub.hasRwAccess() ? 0 : 1;
    }
    return this._imm_getIntAttr(attrName);
  }
  _imm_setIntAttr(attrName, value) {
    let hub = this._ctx.getGenHub(this._hubref);
    if (hub != null && attrName == "networkTimeout") {
      hub.imm_setNetworkTimeout(value);
    }
  }
  /**
   * Returns all known URLs that have been used to register this hub.
   * URLs are pointing to the same hub when the devices connected
   * are sharing the same serial number.
   */
  get_knownUrls() {
    let hub = this._ctx.getGenHub(this._hubref);
    if (hub != null) {
      return hub.get_knownUrls();
    }
    return [];
  }
  //--- (generated code: YHub implementation)
  /**
   * Returns the URL that has been used first to register this hub.
   */
  get_registeredUrl() {
    return this._imm_getStrAttr("registeredUrl");
  }
  /**
   * Returns the URL currently in use to communicate with this hub.
   */
  get_connectionUrl() {
    return this._imm_getStrAttr("connectionUrl");
  }
  /**
   * Returns the state of the connection with this hub. (TRYING, CONNECTED, RECONNECTING, ABORTED, UNREGISTERED)
   */
  get_connectionState() {
    return this._imm_getIntAttr("connectionState");
  }
  /**
   * Returns the hub serial number, if the hub was already connected once.
   */
  get_serialNumber() {
    return this._imm_getStrAttr("serialNumber");
  }
  /**
   * Tells if this hub is still registered within the API.
   *
   * @return true if the hub has not been unregistered.
   */
  async isInUse() {
    return await this._getIntAttr("isInUse") > 0;
  }
  /**
   * Tells if there is an active communication channel with this hub.
   *
   * @return true if the hub is currently connected.
   */
  async isOnline() {
    return await this._getIntAttr("isOnline") > 0;
  }
  /**
   * Tells if write access on this hub is blocked. Return true if it
   * is not possible to change attributes on this hub
   *
   * @return true if it is not possible to change attributes on this hub.
   */
  async isReadOnly() {
    return await this._getIntAttr("isReadOnly") > 0;
  }
  /**
   * Modifies tthe network connection delay for this hub.
   * The default value is inherited from ySetNetworkTimeout
   * at the time when the hub is registered, but it can be updated
   * afterward for each specific hub if necessary.
   *
   * @param networkMsTimeout : the network connection delay in milliseconds.
   * @noreturn
   */
  set_networkTimeout(networkMsTimeout) {
    this._imm_setIntAttr("networkTimeout", networkMsTimeout);
  }
  /**
   * Returns the network connection delay for this hub.
   * The default value is inherited from ySetNetworkTimeout
   * at the time when the hub is registered, but it can be updated
   * afterward for each specific hub if necessary.
   *
   * @return the network connection delay in milliseconds.
   */
  get_networkTimeout() {
    return this._imm_getIntAttr("networkTimeout");
  }
  /**
   * Returns the numerical error code of the latest error with the hub.
   * This method is mostly useful when using the Yoctopuce library with
   * exceptions disabled.
   *
   * @return a number corresponding to the code of the latest error that occurred while
   *         using the hub object
   */
  async get_errorType() {
    return this._imm_getIntAttr("errorType");
  }
  /**
   * Returns the error message of the latest error with the hub.
   * This method is mostly useful when using the Yoctopuce library with
   * exceptions disabled.
   *
   * @return a string corresponding to the latest error message that occured while
   *         using the hub object
   */
  async get_errorMessage() {
    return this._imm_getStrAttr("errorMessage");
  }
  /**
   * Returns the value of the userData attribute, as previously stored
   * using method set_userData.
   * This attribute is never touched directly by the API, and is at
   * disposal of the caller to store a context.
   *
   * @return the object stored previously by the caller.
   */
  async get_userData() {
    return this._userData;
  }
  /**
   * Stores a user context provided as argument in the userData
   * attribute of the function.
   * This attribute is never touched by the API, and is at
   * disposal of the caller to store a context.
   *
   * @param data : any kind of object to be stored
   * @noreturn
   */
  async set_userData(data) {
    this._userData = data;
  }
  /**
   * Starts the enumeration of hubs currently in use by the API.
   * Use the method YHub.nextHubInUse() to iterate on the
   * next hubs.
   *
   * @return a pointer to a YHub object, corresponding to
   *         the first hub currently in use by the API, or a
   *         null pointer if none has been registered.
   */
  static FirstHubInUse() {
    return YAPI.nextHubInUseInternal(-1);
  }
  /**
   * Starts the enumeration of hubs currently in use by the API
   * in a given YAPI context.
   * Use the method YHub.nextHubInUse() to iterate on the
   * next hubs.
   *
   * @param yctx : a YAPI context
   *
   * @return a pointer to a YHub object, corresponding to
   *         the first hub currently in use by the API, or a
   *         null pointer if none has been registered.
   */
  static FirstHubInUseInContext(yctx) {
    return yctx.nextHubInUseInternal(-1);
  }
  /**
   * Retrieves hub for a given identifier. The identifier can be the URL or the
   * serial of the hub.
   *
   * @param url : The url or serial of the hub.
   *
   * @return a pointer to a YHub object, corresponding to
   *         the first hub currently in use by the API, or a
   *         null pointer if none has been registered.
   */
  static async FindHubInUse(url) {
    return await YAPI.findYHubFromID(url);
  }
  /**
   * Retrieves hub for a given identifier in a given YAPI context. The identifier can be the URL or the
   * serial of the hub.
   *
   * @param yctx : a YAPI context
   * @param url : The url or serial of the hub.
   *
   * @return a pointer to a YHub object, corresponding to
   *         the first hub currently in use by the API, or a
   *         null pointer if none has been registered.
   */
  static async FindHubInUseInContext(yctx, url) {
    return await yctx.findYHubFromID(url);
  }
  /**
   * Continues the module enumeration started using YHub.FirstHubInUse().
   * Caution: You can't make any assumption about the order of returned hubs.
   *
   * @return a pointer to a YHub object, corresponding to
   *         the next hub currently in use, or a null pointer
   *         if there are no more hubs to enumerate.
   */
  nextHubInUse() {
    return this._ctx.nextHubInUseInternal(this._hubref);
  }
};
YHub.TRYING = 1;
YHub.CONNECTED = 2;
YHub.RECONNECTING = 3;
YHub.ABORTED = 4;
YHub.UNREGISTERED = 5;
var YAPIContext = class {
  constructor(system_env) {
    this._detectType = Y_DETECT_NONE;
    this._knownHubsBySerial = {};
    this._knownHubsByUrl = {};
    this._connectedHubs = [];
    this._registeredHubs = [];
    this._trustedCertificate = [];
    this._networkSecurityOptions = 0;
    this._yhub_cache = {};
    this._ssdpManager = null;
    this._devs = {};
    this._snByUrl = {};
    this._snByName = {};
    this._fnByType = {};
    this._lastErrorType = YAPI_SUCCESS;
    this._lastErrorMsg = "no error";
    this._updateDevListStarted = 0;
    this._pendingCallbacks = [];
    this._logLevel = 4;
    this._logCallback = null;
    this._arrivalCallback = null;
    this._namechgCallback = null;
    this._removalCallback = null;
    this._hubDiscoveryCallback = null;
    this._forwardValues = 0;
    this._calibHandlers = [];
    this._ValueCallbackList = [];
    this._TimedReportCallbackList = [];
    this._beacons = {};
    this._isNodeJS = false;
    this._networkTimeoutMs = DEFAULT_NETWORK_TIMEOUT_MS;
    this._deviceListValidityMs = DEFAULT_DEVICE_LIST_VALIDITY_MS;
    this._crcTable = null;
    this.defaultEncoding = "binary";
    this.exceptionsDisabled = false;
    this.SUCCESS = 0;
    this.NOT_INITIALIZED = -1;
    this.INVALID_ARGUMENT = -2;
    this.NOT_SUPPORTED = -3;
    this.DEVICE_NOT_FOUND = -4;
    this.VERSION_MISMATCH = -5;
    this.DEVICE_BUSY = -6;
    this.TIMEOUT = -7;
    this.IO_ERROR = -8;
    this.NO_MORE_DATA = -9;
    this.EXHAUSTED = -10;
    this.DOUBLE_ACCES = -11;
    this.UNAUTHORIZED = -12;
    this.RTC_NOT_READY = -13;
    this.FILE_NOT_FOUND = -14;
    this.SSL_ERROR = -15;
    this.RFID_SOFT_ERROR = -16;
    this.RFID_HARD_ERROR = -17;
    this.BUFFER_TOO_SMALL = -18;
    this.DNS_ERROR = -19;
    this.SSL_UNK_CERT = -20;
    this.UNCONFIGURED = -21;
    this.NO_TRUSTED_CA_CHECK = 1;
    this.NO_EXPIRATION_CHECK = 2;
    this.NO_HOSTNAME_CHECK = 4;
    this.LEGACY = 8;
    this.defaultCacheValidity = 5;
    this.INVALID_INT = YAPI_INVALID_INT;
    this.INVALID_UINT = YAPI_INVALID_UINT;
    this.INVALID_LONG = YAPI_INVALID_LONG;
    this.INVALID_DOUBLE = YAPI_INVALID_DOUBLE;
    this.MIN_DOUBLE = YAPI_MIN_DOUBLE;
    this.MAX_DOUBLE = YAPI_MAX_DOUBLE;
    this.INVALID_STRING = YAPI_INVALID_STRING;
    this.HASH_BUF_SIZE = YOCTO_HASH_BUF_SIZE;
    this.DETECT_NONE = Y_DETECT_NONE;
    this.DETECT_USB = Y_DETECT_USB;
    this.DETECT_NET = Y_DETECT_NET;
    this.DETECT_ALL = Y_DETECT_ALL;
    this.YOCTO_DEFAULT_HTTP_PORT = 4444;
    this.YOCTO_DEFAULT_HTTPS_PORT = 4443;
    this.system_env = system_env || YAPI && YAPI.system_env || _UnspecifiedSystemEnv;
    this._isNodeJS = this.system_env.isNodeJS;
    this._uniqueID = String.fromCharCode(Math.random() * 79 + 48, Math.random() * 79 + 48, Math.random() * 79 + 48);
    this.imm_ResetToDefaults();
  }
  imm_ResetToDefaults() {
    this._detectType = this.DETECT_NONE;
    this._knownHubsBySerial = {};
    this._knownHubsByUrl = {};
    this._connectedHubs = [];
    this._registeredHubs = [];
    this._trustedCertificate = [];
    this._networkSecurityOptions = 0;
    this._devs = {};
    this._snByUrl = {};
    this._snByName = {};
    this._fnByType = {};
    this._fnByType.Module = new YFunctionType(this, "Module");
    this._lastErrorType = YAPI_SUCCESS;
    this._lastErrorMsg = "no error";
    this._updateDevListStarted = 0;
    this._pendingCallbacks = [];
    this._logLevel = 2;
    this._logCallback = null;
    this._arrivalCallback = null;
    this._namechgCallback = null;
    this._removalCallback = null;
    this._hubDiscoveryCallback = null;
    this._forwardValues = 0;
    this._ValueCallbackList = [];
    this._TimedReportCallbackList = [];
    this._beacons = {};
    this._calibHandlers = [];
    for (let i = 1; i <= 20; i++) {
      this.RegisterCalibrationHandler(i, this.LinearCalibrationHandler);
    }
    this.RegisterCalibrationHandler(YOCTO_CALIB_TYPE_OFS, this.LinearCalibrationHandler);
    this.exceptionsDisabled = false;
  }
  _throw(int_errType, str_errMsg, obj_retVal) {
    this._lastErrorType = int_errType;
    this._lastErrorMsg = str_errMsg;
    if (!this.exceptionsDisabled) {
      let exc = new YoctoError(str_errMsg);
      exc.errorType = int_errType;
      throw exc;
    }
    return obj_retVal;
  }
  imm_setErr(errmsg, int_errType, str_errMsg, obj_retVal) {
    if (errmsg) {
      errmsg.msg = str_errMsg;
    }
    this._lastErrorType = int_errType;
    this._lastErrorMsg = str_errMsg;
    return obj_retVal;
  }
  imm_setSystemEnv(env) {
    this.system_env = env;
    this._isNodeJS = env.isNodeJS;
  }
  // Log a message, either to the user defined function or to the console if none is defined
  imm_log(msg, ...moreArgs) {
    let now = /* @__PURE__ */ new Date();
    let day = now.getFullYear().toString() + "-" + ("0" + (now.getMonth() + 1)).slice(-2) + "-" + ("0" + now.getDate()).slice(-2);
    let time = ("0" + now.getHours().toString()).slice(-2) + ":" + ("0" + now.getMinutes()).slice(-2) + ":" + ("0" + now.getSeconds()).slice(-2);
    let prefix = day + " " + time + " [" + this._uniqueID + "] ";
    let isError = false;
    if (moreArgs.length > 0) {
      if (moreArgs[0].constructor && /Error/i.test(moreArgs[0].constructor.name)) {
        isError = true;
      }
    }
    if (this._logCallback) {
      try {
        if (moreArgs.length > 0) {
          if (moreArgs[0].message) {
            msg += moreArgs[0].message;
          } else {
            msg += moreArgs[0].toString();
          }
        }
        this._logCallback(prefix + msg);
      } catch (e) {
        console.error(prefix + "Exception in custom log callback: ", e);
        console.log("... while trying to log:");
        if (isError) {
          console.error(prefix + msg, ...moreArgs);
        } else {
          console.log(prefix + msg, ...moreArgs);
        }
      }
    } else {
      if (isError) {
        console.error(prefix + msg, ...moreArgs);
      } else {
        console.log(prefix + msg, ...moreArgs);
      }
    }
  }
  /**
   * Registers a log callback function. This callback will be called each time
   * the API have something to say. Quite useful to debug the API.
   *
   * @param logfun : a procedure taking a string parameter, or null
   *         to unregister a previously registered  callback.
   */
  async RegisterLogFunction(logfun) {
    this._logCallback = logfun;
    return YAPI_SUCCESS;
  }
  // Search for an existing a hub object for a given URL
  imm_getHub(obj_urlInfo) {
    let hub = this._knownHubsByUrl[obj_urlInfo.imm_getRootUrl()];
    if (!hub) {
      for (const url in this._knownHubsByUrl) {
        if (this._knownHubsByUrl[url].imm_getOriginalURL() == obj_urlInfo.imm_getOriginalURL()) {
          return this._knownHubsByUrl[url];
        }
      }
    }
    return hub;
  }
  // Check if a given connected YGenericHub should be used as the primary hub object
  // Update the internal list of hubs on the fly
  //
  // This function chooses between equivalent hubs based connection state and precedence.
  // A disconnected hub is NEVER returned in place of a connected hub
  //
  imm_getPrimaryHub(hub) {
    let primaryHub = this._knownHubsBySerial[hub.imm_getSerialNumber()];
    if (!primaryHub || primaryHub === hub) {
      this._knownHubsBySerial[hub.imm_getSerialNumber()] = hub;
      this._knownHubsByUrl[hub.imm_getRootUrl()] = hub;
      return hub;
    }
    if (primaryHub.urlInfo.imm_useSecureSocket() || !hub.urlInfo.imm_useSecureSocket()) {
      if (primaryHub.imm_getcurrentState() >= hub.imm_getcurrentState()) {
        primaryHub.imm_inheritFrom(hub);
        this.imm_updateRegisteredHubs(hub, false);
        return primaryHub;
      }
    }
    this._knownHubsBySerial[hub.imm_getSerialNumber()] = hub;
    hub.imm_inheritFrom(primaryHub);
    this.imm_updateRegisteredHubs(primaryHub, false);
    return hub;
  }
  // Add a hub object to the list of actively attached hub
  async _addConnectedHub(newhub) {
    let serial = this._snByUrl[newhub.imm_getRootUrl()];
    if (!serial) {
      let newdev = new YDevice(this, newhub.imm_getRootUrl(), null, null);
      await newdev.refresh();
    }
    let hubFound = false;
    for (let i = 0; i < this._connectedHubs.length; i++) {
      let url = this._connectedHubs[i].imm_getRootUrl();
      if (newhub.imm_getRootUrl() == url) {
        hubFound = true;
        break;
      }
    }
    if (!hubFound) {
      this._connectedHubs.push(newhub);
    }
  }
  // Tell if a hub with a given serial number is already registered actively
  imm_isActiveHub(hubSerial) {
    for (let i = 0; i < this._connectedHubs.length; i++) {
      let hubSerials = this._connectedHubs[i].serialByYdx;
      if (hubSerials && hubSerials[0] == hubSerial) {
        return true;
      }
    }
    return false;
  }
  imm_dropConnectedHub(hub) {
    let idx = this._connectedHubs.indexOf(hub);
    if (idx < 0) {
      return;
    }
    for (let j = 0; j < hub.serialByYdx.length; j++) {
      let serial = hub.serialByYdx[j];
      if (serial && this._devs[serial]) {
        if (this._removalCallback) {
          let module = YModule.FindModuleInContext(this, serial + ".module");
          this._pendingCallbacks.push({ event: "-", serial, module });
        }
        try {
          this.imm_forgetDevice(this._devs[serial]);
        } catch (e) {
        }
      }
    }
    idx = this._connectedHubs.indexOf(hub);
    if (idx >= 0) {
      this._connectedHubs.splice(idx, 1);
    }
    this.imm_updateRegisteredHubs(hub, false);
  }
  // Wait until updateDeviceList is completed to avoid course conditions
  async _ensureUpdateDeviceListNotRunning() {
    while (this._updateDevListStarted && this.GetTickCount() - this._updateDevListStarted < 30 * 1e3) {
      await this.Sleep(25);
    }
  }
  // Trigger an update of connected devices by querying all hubs
  async _updateDeviceList_internal(bool_forceupdate, bool_invokecallbacks) {
    if (this._updateDevListStarted && this.GetTickCount() - this._updateDevListStarted < 30 * 1e3) {
      return {
        errorType: YAPI_SUCCESS,
        errorMsg: "no error"
      };
    }
    for (let i = 0; i < this._connectedHubs.length; i++) {
      if (this._connectedHubs[i].imm_isFirstArrivalCallback() && bool_invokecallbacks && this._arrivalCallback) {
        bool_forceupdate = true;
        break;
      }
    }
    if (bool_forceupdate) {
      for (let i = 0; i < this._connectedHubs.length; i++) {
        this._connectedHubs[i].imm_forceUpdate();
      }
    }
    try {
      this._updateDevListStarted = this.GetTickCount();
      let hubs = [];
      for (let i = 0; i < this._connectedHubs.length; i++) {
        let hub = this._connectedHubs[i];
        let rootUrl = hub.imm_getRootUrl();
        let hubDev = this.imm_getDevice(rootUrl);
        if (!hubDev) {
          continue;
        }
        if (hub.imm_getcurrentState() < 1) {
          if (this._logLevel >= 4) {
            this.imm_log("Skip updateDeviceList for hub " + hub.urlInfo.imm_getHost() + ", currently offline");
          }
          continue;
        }
        if (hub.devListExpires <= this.GetTickCount()) {
          hub._missing = {};
          hubs.push(hub);
        }
      }
      for (let serial in this._devs) {
        let rooturl = this._devs[serial].imm_getRootUrl();
        for (let i = 0; i < hubs.length; i++) {
          let huburl = hubs[i].imm_getRootUrl();
          if (rooturl.substr(0, huburl.length) == huburl) {
            hubs[i]._missing[serial] = true;
          }
        }
      }
      let update_promises = [];
      for (let i = 0; i < hubs.length; i++) {
        let prom = hubs[i].hubUpdateDeviceList();
        update_promises.push(prom);
      }
      let newDeviceCounts = await Promise.all(update_promises);
      let newDeviceArrived = false;
      for (let res of newDeviceCounts) {
        newDeviceArrived = newDeviceArrived || res > 0;
      }
      if (bool_invokecallbacks) {
        let nbEvents = this._pendingCallbacks.length;
        for (let i = 0; i < nbEvents; i++) {
          let evt = this._pendingCallbacks[i];
          switch (evt.event) {
            case "+":
              if (this._logLevel >= 3) {
                this.imm_log("Device " + evt.serial + " plugged");
              }
              if (this._arrivalCallback) {
                try {
                  await evt.module.load(this.defaultCacheValidity);
                  await this._arrivalCallback(evt.module);
                } catch (e) {
                  this.imm_log("Exception in device arrival callback:", e);
                }
              }
              break;
            case "/":
              if (this._namechgCallback) {
                try {
                  await this._namechgCallback(evt.module);
                } catch (e) {
                  this.imm_log("Exception in device change callback:", e);
                }
              }
              break;
            case "-":
              if (this._logLevel >= 3) {
                this.imm_log("Device " + evt.serial + " unplugged");
              }
              if (this._removalCallback) {
                try {
                  await this._removalCallback(evt.module);
                } catch (e) {
                  this.imm_log("Exception in device removal callback:", e);
                }
              }
              break;
          }
        }
        this._pendingCallbacks = this._pendingCallbacks.slice(nbEvents);
      }
      if (newDeviceArrived) {
        for (let fun of this._ValueCallbackList) {
          if (!fun._hwId) {
            fun.isOnline();
          }
        }
        for (let fun of this._TimedReportCallbackList) {
          if (!fun._hwId) {
            fun.isOnline();
          }
        }
      }
    } finally {
      this._updateDevListStarted = 0;
    }
    return {
      errorType: YAPI_SUCCESS,
      errorMsg: "no error"
    };
  }
  // process a hub white-pages/yellow-pages records to update the device data
  // return the number of NEW devices discovered, or a negative error code
  async updateDeviceList_process(hub, hubDev, whitePages, yellowPages) {
    let newDevices = 0;
    let refresh = {};
    let serial = null;
    for (let classname in yellowPages) {
      let obj_yprecs = yellowPages[classname];
      let ftype = this._fnByType[classname];
      if (ftype == void 0) {
        ftype = new YFunctionType(this, classname);
        this._fnByType[classname] = ftype;
      }
      for (let i = 0; i < obj_yprecs.length; i++) {
        let yprec = obj_yprecs[i];
        let hwid = yprec.hardwareId;
        let basetype = yprec.baseType;
        if (ftype.imm_reindexFunction(hwid, yprec["logicalName"], yprec["advertisedValue"], basetype)) {
          serial = hwid.substr(0, hwid.indexOf("."));
          refresh[serial] = true;
        }
      }
    }
    for (let i = 0; i < whitePages.length; i++) {
      let devinfo = whitePages[i];
      serial = devinfo.serialNumber;
      let devydx = devinfo.index;
      let rooturl = devinfo.networkUrl.slice(0, -3);
      if (rooturl.charAt(0) == "/")
        rooturl = hubDev.imm_getRootUrl() + rooturl.substr(1);
      let currdev = this._devs[serial];
      if (this._logLevel >= 5) {
        this.imm_log("Device " + serial + " present, currdev " + (currdev ? "" : "NOT ") + "set" + (hub.imm_isFirstArrivalCallback() ? ", firstArrival" : ""));
      }
      if (currdev && hub.imm_isFirstArrivalCallback()) {
        newDevices++;
        if (this._arrivalCallback) {
          let module = YModule.FindModuleInContext(this, serial + ".module");
          this._pendingCallbacks.push({ event: "+", serial, module });
        }
      }
      hub.serialByYdx[devydx] = serial;
      if (!currdev) {
        new YDevice(this, rooturl, devinfo, yellowPages);
        newDevices++;
        if (this._arrivalCallback) {
          let module = YModule.FindModuleInContext(this, serial + ".module");
          this._pendingCallbacks.push({ event: "+", serial, module });
        }
      } else if (currdev.imm_getLogicalName() != devinfo["logicalName"]) {
        await currdev.refresh();
        if (this._namechgCallback) {
          let module = YModule.FindModuleInContext(this, serial + ".module");
          this._pendingCallbacks.push({ event: "/", serial, module });
        }
      } else if (refresh[serial] || currdev.imm_getRootUrl() != rooturl || currdev.imm_getBeacon() != devinfo["beacon"]) {
        await currdev.refresh();
      }
      hub._missing[serial] = false;
    }
    if (this._arrivalCallback && hub.imm_isFirstArrivalCallback()) {
      hub.imm_setFirstArrivalCallback(false);
    }
    for (serial in hub._missing) {
      if (hub._missing[serial] && this._devs[serial]) {
        if (this._removalCallback) {
          let module = YModule.FindModuleInContext(this, serial + ".module");
          this._pendingCallbacks.push({ event: "-", serial, module });
        }
        this.imm_forgetDevice(this._devs[serial]);
      }
    }
    return newDevices;
  }
  /** process event data produced by a hub
   */
  async parseEvents(hub, str_lines) {
    if (hub.imm_isDisconnecting()) {
      return;
    }
    hub.isNotifWorking = true;
    if (hub.timeoutId) {
      clearTimeout(hub.timeoutId);
    }
    hub.timeoutId = setTimeout(() => {
      if (!hub.imm_isForwarded()) {
        this.imm_log("Closing stalled connection after " + hub.imm_getNetworkTimeout() / 1e3 + "s");
        hub.imm_disconnectNow();
      }
    }, hub.imm_getNetworkTimeout());
    let rows = (hub.notifCarryOver + str_lines).split("\n");
    let nrows = rows.length;
    let value;
    if (str_lines.substr(-1) != "\n") {
      hub.notifCarryOver = rows[--nrows];
    } else {
      nrows--;
      hub.notifCarryOver = "";
    }
    for (let idx = 0; idx < nrows; idx++) {
      let ev = rows[idx];
      if (ev.length == 0)
        continue;
      let firstCode = ev.charAt(0);
      if (ev.length >= 3 && firstCode >= NOTIFY_NETPKT_CONFCHGYDX && firstCode <= NOTIFY_NETPKT_TIMEAVGYDX) {
        hub.retryDelay = 15;
        if (hub.notifPos >= 0)
          hub.notifPos += ev.length + 1;
        let devydx = ev.charCodeAt(1) - 65;
        let funydx = ev.charCodeAt(2) - 48;
        if (funydx >= 64) {
          funydx -= 64;
          devydx += 128;
        }
        let serial = hub.serialByYdx[devydx];
        if (serial && this._devs[serial]) {
          let funcid = funydx == 15 ? "time" : this._devs[serial].imm_functionIdByFunYdx(funydx);
          if (funcid != "") {
            let dev;
            value = ev.slice(3);
            switch (firstCode) {
              case NOTIFY_NETPKT_FUNCVALYDX:
                if (value != "")
                  value = value.split("\0")[0];
                await this.setFunctionValue(serial + "." + funcid, value);
                break;
              case NOTIFY_NETPKT_DEVLOGYDX:
                dev = this._devs[serial];
                if (dev != null) {
                  dev.imm_triggerLogPull();
                }
                break;
              case NOTIFY_NETPKT_CONFCHGYDX:
                await this.setConfChange(serial);
                break;
              case NOTIFY_NETPKT_TIMEVALYDX:
              case NOTIFY_NETPKT_TIMEAVGYDX:
              case NOTIFY_NETPKT_TIMEV2YDX:
                let pos, arr = [firstCode == "x" ? 0 : firstCode == "z" ? 1 : 2];
                for (pos = 0; pos < value.length; pos += 2) {
                  arr.push(parseInt(value.substr(pos, 2), 16));
                }
                dev = this._devs[serial];
                if (funcid == "time") {
                  let time = arr[1] + 256 * arr[2] + 65536 * arr[3] + 16777216 * arr[4];
                  let ms = arr[5] * 4;
                  let duration;
                  if (arr.length >= 7) {
                    ms += arr[6] >> 6;
                    let duration_ms = arr[7];
                    duration_ms += (arr[6] & 15) * 256;
                    if (arr[6] & 16) {
                      duration = duration_ms;
                    } else {
                      duration = duration_ms / 1e3;
                    }
                  } else {
                    duration = 0;
                  }
                  dev.imm_setTimeRef(time + ms / 1e3, duration);
                } else {
                  await this.setTimedReport(serial + "." + funcid, dev.imm_getLastTimeRef(), dev.imm_getLastDuration(), arr);
                }
                break;
              case NOTIFY_NETPKT_FUNCV2YDX:
                let rawval = this.imm_decodeNetFuncValV2(value);
                if (rawval != null) {
                  let decodedval = this.imm_decodePubVal(rawval[0], rawval, 1, 6);
                  await this.setFunctionValue(serial + "." + funcid, decodedval);
                }
                break;
              case NOTIFY_NETPKT_FLUSHV2YDX:
              // To be implemented later
              default:
                break;
            }
          }
        }
      } else if (ev.length > 5 && ev.substr(0, 4) == "YN01") {
        hub.retryDelay = 15;
        if (hub.notifPos >= 0)
          hub.notifPos += ev.length + 1;
        let notype = ev.substr(4, 1);
        let parts;
        if (notype == "@") {
          hub.notifPos = parseInt(ev.slice(5));
        } else {
          switch (notype) {
            case NOTIFY_NETPKT_NAME:
              parts = ev.slice(5).split(",");
              if (parts.length > 2) {
                let int_beacon = parseInt(parts[2]);
                await this.setBeaconChange(parts[0], int_beacon);
              }
              hub.devListExpires = 0;
              break;
            // no break on purpose
            case NOTIFY_NETPKT_CHILD:
              parts = ev.split(",");
              if (parts[2] == "0") {
                let serial = parts[1];
                if (this._removalCallback) {
                  let module = YModule.FindModuleInContext(this, serial + ".module");
                  this._pendingCallbacks.push({ event: "-", serial, module });
                }
                this.imm_forgetDevice(this._devs[serial]);
              }
            // no break on purpose
            case NOTIFY_NETPKT_FUNCNAME:
            // function name change
            case NOTIFY_NETPKT_FUNCNAMEYDX:
              hub.devListExpires = 0;
              break;
            case NOTIFY_NETPKT_FUNCVAL:
              parts = ev.slice(5).split(",");
              if (parts.length > 2) {
                value = parts[2].split("\0");
                await this.setFunctionValue(parts[0] + "." + parts[1], value[0]);
              }
              break;
          }
        }
      } else {
        hub.devListExpires = 0;
        this.imm_log("Bad event on received from server:", ev);
        hub.notifPos = -1;
      }
    }
    if (this._forwardValues > 0) {
      await this.HandleEvents(new YErrorMsg());
    }
  }
  /** Network notification format: 7x7bit (mapped to 7 chars in range 32..159)
   *                               used to represent 1 flag (RAW6BYTES) + 6 bytes
   * INPUT:  [R765432][1076543][2107654][3210765][4321076][5432107][6543210]
   * OUTPUT: 7 bytes array (1 byte for the funcint_TypeV2 and 6 bytes of USB like data
   *                     funcTypeV2 + [R][-byte 0][-byte 1-][-byte 2-][-byte 3-][-byte 4-][-byte 5-]
   *
   * @return {number[]}
   */
  imm_decodeNetFuncValV2(p) {
    let p_ofs = 0;
    let ch = p.charCodeAt(p_ofs) & 255;
    let len = 0;
    let funcVal = [0, 0, 0, 0, 0, 0, 0];
    if (ch < 32 || ch > 32 + 127) {
      return null;
    }
    ch -= 32;
    funcVal[0] = (ch & 64) != 0 ? NOTIFY_V2_6RAWBYTES : NOTIFY_V2_TYPEDDATA;
    ch &= 63;
    while (len < YOCTO_PUBVAL_SIZE) {
      p_ofs++;
      if (p_ofs >= p.length) {
        break;
      }
      let newCh = p.charCodeAt(p_ofs) & 255;
      if (newCh == NOTIFY_NETPKT_STOP) {
        break;
      }
      if (newCh < 32 || newCh > 32 + 127) {
        return null;
      }
      newCh -= 32;
      ch = (ch << 7) + newCh;
      funcVal[len + 1] = ch >>> 5 - len & 255;
      len++;
    }
    return funcVal;
  }
  /** Decode an enhanced notification (V2) buffer
   */
  imm_decodePubVal(int_typeV2, arr_funcval, int_ofs, int_funcvalen) {
    let buffer = "";
    let endp;
    if (int_typeV2 == NOTIFY_V2_6RAWBYTES || int_typeV2 == NOTIFY_V2_TYPEDDATA) {
      let funcValType;
      if (int_typeV2 == NOTIFY_V2_6RAWBYTES) {
        funcValType = PUBVAL_6RAWBYTES;
      } else {
        funcValType = arr_funcval[int_ofs++];
      }
      switch (funcValType) {
        case PUBVAL_LEGACY:
          break;
        case PUBVAL_1RAWBYTE:
        case PUBVAL_2RAWBYTES:
        case PUBVAL_3RAWBYTES:
        case PUBVAL_4RAWBYTES:
        case PUBVAL_5RAWBYTES:
        case PUBVAL_6RAWBYTES:
          for (let i = 0; i < funcValType; i++) {
            let c = arr_funcval[int_ofs++];
            let b = c >>> 4;
            buffer += b.toString(16);
            b = c & 15;
            buffer += b.toString(16);
          }
          return buffer;
        case PUBVAL_C_LONG:
        case PUBVAL_YOCTO_FLOAT_E3:
          let numVal = arr_funcval[int_ofs++];
          numVal += arr_funcval[int_ofs++] << 8;
          numVal += arr_funcval[int_ofs++] << 16;
          numVal += arr_funcval[int_ofs++] << 24;
          if (funcValType == PUBVAL_C_LONG) {
            return String(Math.round(numVal));
          } else {
            buffer = String(Math.round(numVal * 1e3) / 1e6);
            endp = buffer.length;
            while (endp > 0 && buffer[endp - 1] == "0") {
              --endp;
            }
            if (endp > 0 && buffer[endp - 1] == ".") {
              --endp;
              buffer = buffer.substr(0, endp);
            }
            return buffer;
          }
        case PUBVAL_C_FLOAT:
          let v = arr_funcval[int_ofs++];
          v += arr_funcval[int_ofs++] << 8;
          v += arr_funcval[int_ofs++] << 16;
          v += arr_funcval[int_ofs++] << 24;
          let fraction = (v & (1 << 23) - 1) + (1 << 23) * (v >>> 31 | 1);
          let exp = (v >>> 23 & 255) - 127;
          let floatVal = fraction * Math.pow(2, exp - 23);
          buffer = String(Math.round(floatVal * 1e6) / 1e6);
          endp = buffer.length;
          while (endp > 0 && buffer[endp - 1] == "0") {
            --endp;
          }
          if (endp > 0 && buffer[endp - 1] == ".") {
            --endp;
            buffer = buffer.substr(0, endp);
          }
          return buffer;
        default:
          return "?";
      }
      let len = 0;
      buffer = "";
      while (len < YOCTO_PUBVAL_SIZE && len < int_funcvalen) {
        if (arr_funcval[len] == 0) {
          break;
        }
        buffer += String.fromCharCode(arr_funcval[len]);
        len++;
      }
    }
    return buffer;
  }
  imm_decExp(int_pow) {
    const arr = [
      1e-6,
      1e-5,
      1e-4,
      1e-3,
      0.01,
      0.1,
      1,
      10,
      100,
      1e3,
      1e4,
      1e5,
      1e6,
      1e7,
      1e8,
      1e9
    ];
    return arr[int_pow];
  }
  // Convert Yoctopuce 16-bit decimal floats to standard double-precision floats
  //
  imm_decimalToDouble(val) {
    let negate = false;
    let res;
    let mantis = val & 2047;
    if (mantis == 0)
      return 0;
    if (val > 32767) {
      negate = true;
      val = 65536 - val;
    } else if (val < 0) {
      negate = true;
      val = -val;
    }
    let decexp = this.imm_decExp(val >>> 11);
    if (decexp >= 1) {
      res = mantis * decexp;
    } else {
      res = mantis / Math.round(1 / decexp);
    }
    return negate ? -res : res;
  }
  // Convert standard double-precision floats to Yoctopuce 16-bit decimal floats
  //
  imm_doubleToDecimal(val) {
    let negate = false;
    let comp, mant;
    let decpow;
    let res;
    if (val == 0) {
      return 0;
    }
    if (val < 0) {
      negate = true;
      val = -val;
    }
    comp = val / 1999;
    decpow = 0;
    while (comp > this.imm_decExp(decpow) && decpow < 15) {
      decpow++;
    }
    mant = val / this.imm_decExp(decpow);
    if (decpow == 15 && mant > 2047) {
      res = (15 << 11) + 2047;
    } else {
      res = (decpow << 11) + Math.round(mant);
    }
    return negate ? -res : res;
  }
  imm_getCalibrationHandler(calibType) {
    return this._calibHandlers[calibType];
  }
  // Parse an array of u16 encoded in a base64-like string with memory-based compression
  imm_decodeWords(data) {
    let udata = [];
    for (let i = 0; i < data.length; ) {
      let c = data[i];
      let val = 0;
      if (c == "*") {
        i++;
      } else if (c == "X") {
        val = 65535;
        i++;
      } else if (c == "Y") {
        val = 32767;
        i++;
      } else if (c >= "a") {
        let srcpos = udata.length - 1 - (data.charCodeAt(i++) - 97);
        if (srcpos >= 0) {
          val = udata[srcpos];
        }
      } else {
        if (i + 3 > data.length) {
          return udata;
        }
        val = data.charCodeAt(i++) - 48;
        val += data.charCodeAt(i++) - 48 << 5;
        let lastcode = data.charCodeAt(i++);
        if (lastcode == 122)
          lastcode = 92;
        val += lastcode - 48 << 10;
      }
      udata.push(val);
    }
    return udata;
  }
  // Parse an array of u16 encoded in a base64-like string with memory-based compresssion
  imm_decodeFloats(data) {
    let idata = [];
    let p = 0;
    let datalen = data.length;
    while (p < datalen) {
      let val = 0;
      let sign = 1;
      let dec = 0;
      let decInc = 0;
      let c = data[p++];
      while (c != "-" && (c < "0" || c > "9")) {
        if (p >= datalen) {
          return idata;
        }
        c = data[p++];
      }
      if (c == "-") {
        if (p >= datalen) {
          return idata;
        }
        sign = -sign;
        c = data[p++];
      }
      while (c >= "0" && c <= "9" || c == ".") {
        if (c == ".") {
          decInc = 1;
        } else if (dec < 3) {
          val = val * 10 + (c.charCodeAt(0) - 48);
          dec += decInc;
        }
        if (p < datalen) {
          c = data[p++];
        } else {
          c = "\0";
        }
      }
      if (dec < 3) {
        if (dec == 0) {
          val *= 1e3;
        } else if (dec == 1) {
          val *= 100;
        } else {
          val *= 10;
        }
      }
      idata.push(sign * val);
    }
    return idata;
  }
  /** Convert a numeric string to an integer
   */
  static imm_atoi(str_data) {
    let num = parseInt(str_data);
    if (isNaN(num)) {
      return 0;
    }
    return Math.floor(num);
  }
  /** Convert a numeric string to an float
   */
  static imm_atof(str_data) {
    let num = parseFloat(str_data);
    if (isNaN(num)) {
      return 0;
    }
    return num;
  }
  /** Convert a binary object to string
   */
  imm_bin2str(bin_data) {
    let len = bin_data.length;
    let res = "";
    for (let i = 0; i < len; i += 20) {
      let subdata = bin_data.subarray(i, Math.min(i + 20, len));
      res += String.fromCharCode.apply(null, Array.from(subdata));
    }
    return res;
  }
  /** Convert a string to binary object
   */
  imm_str2bin(str_data) {
    let len = str_data.length;
    let res = new Uint8Array(len);
    for (let i = 0; i < len; i++) {
      res[i] = str_data.charCodeAt(i);
    }
    return res;
  }
  /** Convert a binary object to hex string
   */
  imm_bin2hexstr(bin_data) {
    let len = bin_data.length;
    let res = "";
    for (let i = 0; i < len; i++) {
      let n = bin_data[i].toString(16);
      res += n.length < 2 ? "0" + n : n;
    }
    return res.toUpperCase();
  }
  /** Compute the 32-bit CRC of a binary object
   */
  imm_bincrc(bin_data, ofs, size) {
    let table = this._crcTable;
    if (!table) {
      table = new Int32Array(256);
      for (let i = 0; i < 256; i++) {
        let crc2 = i;
        for (let bit = 0; bit < 8; bit++) {
          crc2 = crc2 & 1 ? 3988292384 ^ crc2 >>> 1 : crc2 >>> 1;
        }
        table[i] = crc2;
      }
      this._crcTable = table;
    }
    let end = ofs + size;
    let crc = -1;
    while (ofs < end) {
      crc = crc >>> 8 ^ table[(crc ^ bin_data[ofs]) & 255];
      ofs++;
    }
    return crc ^ -1;
  }
  /** Convert a hex string to binary object
   */
  imm_hexstr2bin(str_data) {
    let len = str_data.length >>> 1;
    let res = new Uint8Array(len);
    for (let i = 0; i < len; i++) {
      res[i] = parseInt(str_data.substr(2 * i, 2), 16);
    }
    return res;
  }
  /** Return a Device object for a specified URL, serial number or logical device name
   *
   * This function will not cause any network access (not async !)
   */
  imm_getDevice(str_device) {
    let dev = null;
    let serial;
    if (str_device.substr(0, 7) == "http://" || str_device.substr(0, 5) == "ws://" || str_device.substr(0, 8) == "https://" || str_device.substr(0, 6) == "wss://") {
      serial = this._snByUrl[str_device];
      if (serial != void 0)
        dev = this._devs[serial];
    } else {
      if (this._devs[str_device]) {
        dev = this._devs[str_device];
      } else {
        serial = this._snByName[str_device];
        if (serial) {
          dev = this._devs[serial];
        }
      }
    }
    return dev;
  }
  /** Add or remove a value change callback
   */
  async _UpdateValueCallbackList(obj_func, bool_add) {
    let index = this._ValueCallbackList.indexOf(obj_func);
    if (bool_add) {
      await obj_func.isOnline();
      if (index < 0) {
        this._ValueCallbackList.push(obj_func);
      }
    } else if (index >= 0) {
      this._ValueCallbackList.splice(index, 1);
    }
  }
  /** Add or remove a timed report callback
   */
  async _UpdateTimedReportCallbackList(obj_func, bool_add) {
    let index = this._TimedReportCallbackList.indexOf(obj_func);
    if (bool_add) {
      await obj_func.isOnline();
      if (index < 0) {
        this._TimedReportCallbackList.push(obj_func);
      }
    } else if (index >= 0) {
      this._TimedReportCallbackList.splice(index, 1);
    }
  }
  // Return the class name for a given function ID or full Hardware Id
  // Also make sure that the function type is registered in the API
  imm_functionClass(str_funcid) {
    let dotpos = str_funcid.indexOf(".");
    if (dotpos >= 0)
      str_funcid = str_funcid.substr(dotpos + 1);
    let classlen = str_funcid.length;
    while (str_funcid.substr(classlen - 1, 1) <= "9") {
      classlen--;
    }
    let classname = str_funcid.substr(0, 1).toUpperCase() + str_funcid.substr(1, classlen - 1);
    if (this._fnByType[classname] == void 0) {
      this._fnByType[classname] = new YFunctionType(this, classname);
    }
    return classname;
  }
  // Reindex a device in YAPI after a name change detected by device refresh
  imm_reindexDevice(obj_dev) {
    let rootUrl = obj_dev.imm_getRootUrl();
    let serial = obj_dev.imm_getSerialNumber();
    let lname = obj_dev.imm_getLogicalName();
    this._devs[serial] = obj_dev;
    this._snByUrl[rootUrl] = serial;
    if (lname != "")
      this._snByName[lname] = serial;
    this._fnByType["Module"].imm_reindexFunction(serial + ".module", lname, null, null);
    let i, count = obj_dev.imm_functionCount();
    for (i = 0; i < count; i++) {
      let funcid = obj_dev.imm_functionId(i);
      if (funcid != "") {
        let funcname = obj_dev.imm_functionName(i);
        let classname = this.imm_functionClass(funcid);
        this._fnByType[classname].imm_reindexFunction(serial + "." + funcid, funcname, null, null);
      }
    }
  }
  // Remove a device from YAPI after an unplug detected by device refresh
  imm_forgetDevice(obj_dev) {
    let rootUrl = obj_dev.imm_getRootUrl();
    let serial = obj_dev.imm_getSerialNumber();
    let lname = obj_dev.imm_getLogicalName();
    delete this._devs[serial];
    delete this._snByUrl[rootUrl];
    if (this._snByName[lname] == serial) {
      delete this._snByName[lname];
    }
    this._fnByType["Module"].imm_forgetFunction(serial + ".module");
    let i, count = obj_dev.imm_functionCount();
    for (i = 0; i < count; i++) {
      let funcid = obj_dev.imm_functionId(i);
      if (funcid != "") {
        let classname = this.imm_functionClass(funcid);
        this._fnByType[classname].imm_forgetFunction(serial + "." + funcid);
      }
    }
  }
  // Find the best known identifier (hardware Id) for a given function
  imm_resolveFunction(str_className, str_func) {
    if (Y_BASETYPES[str_className] == void 0) {
      if (this._fnByType[str_className] == void 0) {
        this._fnByType[str_className] = new YFunctionType(this, str_className);
      }
      return this._fnByType[str_className].imm_resolve(str_func);
    }
    let baseType = Y_BASETYPES[str_className];
    let res;
    for (str_className in this._fnByType) {
      if (this._fnByType[str_className].imm_matchBaseType(baseType)) {
        res = this._fnByType[str_className].imm_resolve(str_func);
        if (res.errorType == YAPI_SUCCESS)
          return res;
      }
    }
    return {
      errorType: YAPI_DEVICE_NOT_FOUND,
      errorMsg: "No " + str_className + " [" + str_func + "] found (old firmware?)"
    };
  }
  // Find the best known identifier (hardware Id) for a given function
  imm_getFriendlyNameFunction(str_className, str_func) {
    if (Y_BASETYPES[str_className] == void 0) {
      if (this._fnByType[str_className] == void 0) {
        this._fnByType[str_className] = new YFunctionType(this, str_className);
      }
      return this._fnByType[str_className].imm_getFriendlyName(str_func);
    }
    let baseType = Y_BASETYPES[str_className];
    let res;
    for (str_className in this._fnByType) {
      if (this._fnByType[str_className].imm_matchBaseType(baseType)) {
        res = this._fnByType[str_className].imm_getFriendlyName(str_func);
        if (res.errorType == YAPI_SUCCESS)
          return res;
      }
    }
    return {
      errorType: YAPI_DEVICE_NOT_FOUND,
      errorMsg: "No " + str_className + " [" + str_func + "] found (old firmware?)"
    };
  }
  // Retrieve a function object by hardware id, updating the indexes on the fly if needed
  imm_setFunction(str_className, str_func, obj_func) {
    if (this._fnByType[str_className] == void 0) {
      this._fnByType[str_className] = new YFunctionType(this, str_className);
    }
    this._fnByType[str_className].imm_setFunction(str_func, obj_func);
  }
  // Retrieve a function object by hardware id, updating the indexes on the fly if needed
  imm_getFunction(str_className, str_func) {
    if (this._fnByType[str_className] == void 0) {
      this._fnByType[str_className] = new YFunctionType(this, str_className);
    }
    return this._fnByType[str_className].imm_getFunction(str_func);
  }
  // Set a function advertised value by hardware id
  async setFunctionValue(str_hwid, str_pubval) {
    let classname = this.imm_functionClass(str_hwid);
    if (this._fnByType[classname].imm_setFunctionValue(str_hwid, str_pubval)) {
      let receivers = this._ValueCallbackList;
      for (let i = 0; i < receivers.length; i++) {
        let fun = receivers[i];
        if (!fun._hwId)
          continue;
        if (fun._hwId == str_hwid) {
          await fun._invokeValueCallback(str_pubval);
        }
      }
    }
  }
  // Set a timed value report for a function
  async setTimedReport(str_hwid, float_timestamp, float_duration, arr_report) {
    let classname = this.imm_functionClass(str_hwid);
    let receivers = this._TimedReportCallbackList;
    for (let i = 0; i < receivers.length; i++) {
      let fun = receivers[i];
      if (!fun._hwId)
        continue;
      if (fun._hwId == str_hwid) {
        let dev = this.imm_getDevice(fun._serial);
        if (dev) {
          let sensor = fun;
          let report = await sensor._decodeTimedReport(float_timestamp, float_duration, arr_report);
          await sensor._invokeTimedReportCallback(report);
        }
      }
    }
  }
  // Publish a configuration change event
  async setConfChange(str_serial) {
    let module = YModule.FindModuleInContext(this, str_serial + ".module");
    await module._invokeConfigChangeCallback();
  }
  // Publish a beacon change event
  async setBeaconChange(str_serial, int_beacon) {
    if (this._beacons[str_serial] === void 0 || this._beacons[str_serial] != int_beacon) {
      this._beacons[str_serial] = int_beacon;
      let dev = this.imm_getDevice(str_serial);
      if (dev) {
        dev._beacon = int_beacon;
      }
      let module = YModule.FindModuleInContext(this, str_serial + ".module");
      await module._invokeBeaconCallback(int_beacon);
    }
  }
  // Retrieve a function advertised value by hardware id
  imm_getFunctionValue(str_hwid) {
    let classname = this.imm_functionClass(str_hwid);
    return this._fnByType[classname].imm_getFunctionValue(str_hwid);
  }
  // Retrieve a function advertised value by hardware id
  imm_getFunctionBaseType(str_hwid) {
    let classname = this.imm_functionClass(str_hwid);
    return this._fnByType[classname].imm_getBaseType();
  }
  // Find the hardwareId for the first instance of a given function class
  imm_getFirstHardwareId(str_className) {
    if (Y_BASETYPES[str_className] == void 0) {
      if (this._fnByType[str_className] == void 0) {
        this._fnByType[str_className] = new YFunctionType(this, str_className);
      }
      return this._fnByType[str_className].imm_getFirstHardwareId();
    }
    let baseType = Y_BASETYPES[str_className];
    let res;
    for (str_className in this._fnByType) {
      if (this._fnByType[str_className].imm_matchBaseType(baseType)) {
        res = this._fnByType[str_className].imm_getFirstHardwareId();
        if (res)
          return res;
      }
    }
    return null;
  }
  // Find the hardwareId for the next instance of a given function class
  imm_getNextHardwareId(str_className, str_hwid) {
    if (Y_BASETYPES[str_className] == void 0) {
      return this._fnByType[str_className].imm_getNextHardwareId(str_hwid);
    }
    let baseType = Y_BASETYPES[str_className];
    let prevclass = this.imm_functionClass(str_hwid);
    let res = this._fnByType[prevclass].imm_getNextHardwareId(str_hwid);
    if (res)
      return res;
    for (str_className in this._fnByType) {
      if (prevclass != "") {
        if (str_className != prevclass)
          continue;
        prevclass = "";
        continue;
      }
      if (this._fnByType[str_className].imm_matchBaseType(baseType)) {
        res = this._fnByType[str_className].imm_getFirstHardwareId();
        if (res)
          return res;
      }
    }
    return null;
  }
  /** Perform an HTTP request on a device, by URL or identifier.
   * When loading the REST API from a device by identifier, the device cache will be used.
   */
  async devRequest(str_device, str_request, obj_body = null, int_tcpchan = 0) {
    let lines = str_request.split("\n");
    let res = new YHTTPRequest(null);
    let lockdev = null;
    let baseUrl;
    if (str_device.substr(0, 7) == "http://" || str_device.substr(0, 5) == "ws://" || str_device.substr(0, 8) == "https://" || str_device.substr(0, 6) == "wss://" || str_device.substr(0, 9) == "secure://" || str_device.substr(0, 7) == "auto://") {
      baseUrl = str_device;
      if (baseUrl.slice(-1) != "/")
        baseUrl = baseUrl + "/";
      if (lines[0].substr(0, 12) != "GET /not.byn") {
        let serial = this._snByUrl[baseUrl];
        if (serial) {
          lockdev = this._devs[serial];
        }
      }
    } else {
      lockdev = this.imm_getDevice(str_device);
      if (!lockdev) {
        res.errorType = YAPI_DEVICE_NOT_FOUND;
        res.errorMsg = "Device [" + str_device + "] not online";
        return res;
      }
      if (lines[0] == "GET /api.json") {
        return lockdev.requestAPI(this.defaultCacheValidity);
      }
      baseUrl = lockdev.imm_getRootUrl();
    }
    let words = lines[0].split(" ");
    if (words.length < 2) {
      res.errorType = YAPI_INVALID_ARGUMENT;
      res.errorMsg = "Invalid request, not enough words; expected a method name and a URL";
      return res;
    } else if (words.length > 2) {
      res.errorType = YAPI_INVALID_ARGUMENT;
      res.errorMsg = "Invalid request, too many words; make sure the URL is URI-encoded";
      return res;
    }
    let hub = null;
    for (let i = 0; i < this._connectedHubs.length; i++) {
      let hubUrl = this._connectedHubs[i].imm_getRootUrl();
      if (baseUrl.slice(0, hubUrl.length) == hubUrl) {
        hub = this._connectedHubs[i];
        break;
      }
    }
    if (!hub && this._knownHubsByUrl[str_device]) {
      hub = this._knownHubsByUrl[str_device];
    }
    if (!hub) {
      res.errorType = YAPI_DEVICE_NOT_FOUND;
      res.errorMsg = "No hub found for URL " + baseUrl;
      return res;
    }
    let method = words[0];
    let devUrl = words[1];
    if (devUrl.substr(0, 1) == "/")
      devUrl = devUrl.substr(1);
    if (baseUrl.substr(0, hub.imm_getRootUrl().length) == hub.imm_getRootUrl()) {
      devUrl = baseUrl.substr(hub.imm_getRootUrl().length - 1) + devUrl;
    } else {
      let pos = baseUrl.indexOf("//");
      pos = baseUrl.indexOf("/", pos + 3);
      devUrl = baseUrl.slice(pos) + devUrl;
    }
    if (devUrl.slice(-2) == "&." && !await hub.hasRwAccess()) {
      res.errorType = YAPI_UNAUTHORIZED;
      res.errorMsg = "Access denied: admin credentials required";
      return res;
    }
    let delayedCode = function delayedRequest() {
      return hub.request(method, devUrl, obj_body, int_tcpchan).catch((e) => {
        let res2 = new YHTTPRequest(null);
        res2.errorType = YAPI_IO_ERROR;
        res2.errorMsg = e.message;
        return res2;
      });
    };
    if (lockdev && int_tcpchan == 0) {
      let newPromise = lockdev._pendingQueries.then(delayedCode, delayedCode);
      lockdev._pendingQueries = newPromise;
      res = await newPromise;
    } else {
      res = await delayedCode();
    }
    return res;
  }
  async isReadOnly(str_device) {
    let lockdev = this.imm_getDevice(str_device);
    if (!lockdev) {
      return true;
    }
    let baseUrl = lockdev.imm_getRootUrl();
    let hub = null;
    for (let i = 0; i < this._connectedHubs.length; i++) {
      let hubUrl = this._connectedHubs[i].imm_getRootUrl();
      if (baseUrl.slice(0, hubUrl.length) == hubUrl) {
        hub = this._connectedHubs[i];
        break;
      }
    }
    if (!hub || !await hub.hasRwAccess()) {
      return true;
    }
    return false;
  }
  /** Locate the device to access a specified function, without causing any I/O
   */
  imm_funcDev_internal(str_className, str_func) {
    let res = new YFuncRequest(null);
    let resolve = this.imm_resolveFunction(str_className, str_func);
    if (resolve.errorType != YAPI_SUCCESS) {
      res.errorType = resolve.errorType;
      res.errorMsg = resolve.errorMsg;
    } else {
      str_func = resolve.result;
      let dotpos = str_func.indexOf(".");
      let devid = str_func.substr(0, dotpos);
      let funcid = str_func.substr(dotpos + 1);
      let dev = this.imm_getDevice(devid);
      if (dev == null) {
        res.errorType = YAPI_DEVICE_NOT_FOUND;
        res.errorMsg = "Device [" + devid + "] not found";
      } else {
        res.obj_result = { _expiration: -1, device: dev, deviceid: devid, functionid: funcid, hwid: str_func };
      }
    }
    return res;
  }
  /** Locate the device to access a specified function. May cause device list update if needed
   */
  async _funcDev(str_className, str_func) {
    let res = this.imm_funcDev_internal(str_className, str_func);
    if (res.errorType == YAPI_SUCCESS) {
      return res;
    } else if (res.errorType == YAPI_DEVICE_NOT_FOUND && this._connectedHubs.length == 0) {
      res.errorMsg = "Impossible to contact any device because no hub has been registered";
      return res;
    }
    let updRes = await this._updateDeviceList_internal(true, false);
    if (updRes.errorType != YAPI_SUCCESS) {
      res.errorType = updRes.errorType;
      res.errorMsg = updRes.errorMsg;
      return res;
    }
    return this.imm_funcDev_internal(str_className, str_func);
  }
  /** Load and parse the REST API for a function given by class name and identifier, possibly applying changes
   * Device cache will be preloaded when loading function 'module' and leveraged for other modules
   */
  async funcRequest(str_className, str_func, str_extra, int_msValidity = 0) {
    let funcreq = this.imm_funcDev_internal(str_className, str_func);
    if (funcreq.errorType != YAPI_SUCCESS) {
      funcreq = await this._funcDev(str_className, str_func);
      if (funcreq.errorType != YAPI_SUCCESS) {
        return funcreq;
      }
    }
    let devreq = funcreq.obj_result;
    let loadval = null;
    if (str_extra == "") {
      let yreq = await devreq.device.requestAPI(int_msValidity);
      if (yreq != null) {
        if (yreq.errorType != YAPI_SUCCESS || !yreq.obj_result) {
          let res = new YFuncRequest(null);
          res.errorType = yreq.errorType;
          res.errorMsg = yreq.errorMsg;
          return res;
        }
        loadval = yreq.obj_result[devreq.functionid];
      }
    } else {
      devreq.device.imm_dropCache();
    }
    if (!loadval) {
      if (str_extra == "")
        str_extra = ".json";
      let httpreq = "GET /api/" + devreq.functionid + str_extra;
      let yreq = await this.devRequest(devreq.deviceid, httpreq, null, 0);
      if (yreq.errorType != YAPI_SUCCESS)
        return yreq;
      let replyBuff = yreq.bin_result;
      if (replyBuff.length == 0 && httpreq.indexOf("?") >= 0) {
        funcreq.obj_result = null;
        return funcreq;
      }
      try {
        loadval = JSON.parse(this.imm_bin2str(replyBuff));
      } catch (err) {
      }
    }
    if (!loadval) {
      funcreq.errorType = YAPI_IO_ERROR;
      funcreq.errorMsg = "Request failed, could not parse API value for function " + devreq.hwid;
    } else {
      for (let key in devreq) {
        loadval[key] = devreq[key];
      }
      funcreq.obj_result = loadval;
    }
    return funcreq;
  }
  /** Perform an HTTP request on a device and return the result string
   */
  async HTTPRequest(str_device, str_request) {
    let yreq = await this.devRequest(str_device, str_request, null, 0);
    if (yreq.errorType != YAPI_SUCCESS) {
      return this._throw(yreq.errorType, yreq.errorMsg, null);
    }
    return yreq.bin_result;
  }
  async ForceDeviceRefresh(str_device) {
    let dev = this.imm_getDevice(str_device);
    if (!dev)
      return YAPI_DEVICE_NOT_FOUND;
    let rootUrl = dev.imm_getRootUrl();
    for (let i = 0; i < this._connectedHubs.length; i++) {
      let hub = this._connectedHubs[i];
      let hubUrl = hub.urlInfo.imm_getRootUrl();
      if (rootUrl.substr(0, hubUrl.length) === hubUrl) {
        let hubDev = this.imm_getDevice(hubUrl);
        hubDev.imm_dropCache();
        let retcode = await hubDev.refresh();
        if (retcode != YAPI_SUCCESS) {
          return this._throw(retcode, hubDev._lastErrorMsg, retcode);
        }
        let yreq = await hubDev.requestAPI(this.defaultCacheValidity);
        if (yreq.errorType != YAPI_SUCCESS) {
          return yreq.errorType;
        }
        let yellowPages = yreq.obj_result["services"]["yellowPages"];
        dev.imm_updateFromYP(yellowPages);
      }
    }
    dev.imm_dropCache();
    return YAPI_SUCCESS;
  }
  async SetDeviceListValidity_internal(deviceListValidity) {
    this._deviceListValidityMs = deviceListValidity * 1e3;
  }
  async GetDeviceListValidity_internal() {
    return this._deviceListValidityMs / 1e3 >> 0;
  }
  async SetNetworkTimeout_internal(networkMsTimeout) {
    this._networkTimeoutMs = networkMsTimeout;
  }
  async GetNetworkTimeout_internal() {
    return this._networkTimeoutMs;
  }
  async GetYAPISharedLibraryPath_internal() {
    return "";
  }
  async AddUdevRule_internal(force) {
    return "error: Not supported in TypeScript";
  }
  async DownloadHostCertificate_internal(url, mstimeout) {
    return await this.system_env.downloadRemoteCertificate(new _YY_UrlInfo(url));
  }
  async SetTrustedCertificatesList_internal(certificatePath) {
    return "error: Not supported in TypeScript";
  }
  async SetNetworkSecurityOptions_internal(opts) {
    this._networkSecurityOptions = opts;
    return "";
  }
  async AddTrustedCertificates_internal(certificate) {
    this._trustedCertificate.push(certificate);
    return "";
  }
  imm_updateRegisteredHubs(hub, add) {
    let i;
    for (i = 0; i < this._registeredHubs.length; i++) {
      if (this._registeredHubs[i] === hub) {
        if (!add) {
          if (this._logLevel >= 4) {
            this.imm_log("Unlisting registered hub: " + hub.imm_getOriginalURL());
          }
          this._registeredHubs.splice(i, 1);
        }
        return;
      }
    }
    if (add) {
      if (this._logLevel >= 4) {
        this.imm_log("Adding registered hub: " + hub.imm_getOriginalURL());
      }
      this._registeredHubs.push(hub);
    } else {
      if (this._logLevel >= 4) {
        this.imm_log("Could not unlist registered hub: " + hub.imm_getOriginalURL());
        for (i = 0; i < this._registeredHubs.length; i++) {
          this.imm_log("- " + this._registeredHubs[i].imm_getOriginalURL());
        }
      }
    }
  }
  //--- (generated code: YAPIContext implementation)
  /**
   * Modifies the delay between each forced enumeration of the used YoctoHubs.
   * By default, the library performs a full enumeration every 10 seconds.
   * To reduce network traffic, you can increase this delay.
   * It's particularly useful when a YoctoHub is connected to the GSM network
   * where traffic is billed. This parameter doesn't impact modules connected by USB,
   * nor the working of module arrival/removal callbacks.
   * Note: you must call this function after yInitAPI.
   *
   * @param deviceListValidity : nubmer of seconds between each enumeration.
   * @noreturn
   */
  async SetDeviceListValidity(deviceListValidity) {
    return await this.SetDeviceListValidity_internal(deviceListValidity);
  }
  /**
   * Returns the delay between each forced enumeration of the used YoctoHubs.
   * Note: you must call this function after yInitAPI.
   *
   * @return the number of seconds between each enumeration.
   */
  async GetDeviceListValidity() {
    return await this.GetDeviceListValidity_internal();
  }
  /**
   * Returns the path to the dynamic YAPI library. This function is useful for debugging problems loading the
   * dynamic library YAPI. This function is supported by the C#, Python and VB languages. The other
   * libraries return an
   * empty string.
   *
   * @return a string containing the path of the YAPI dynamic library.
   */
  async GetYAPISharedLibraryPath() {
    return await this.GetYAPISharedLibraryPath_internal();
  }
  /**
   * Adds a UDEV rule which authorizes all users to access Yoctopuce modules
   * connected to the USB ports. This function works only under Linux. The process that
   * calls this method must have root privileges because this method changes the Linux configuration.
   *
   * @param force : if true, overwrites any existing rule.
   *
   * @return an empty string if the rule has been added.
   *
   * On failure, returns a string that starts with "error:".
   */
  async AddUdevRule(force) {
    return await this.AddUdevRule_internal(force);
  }
  /**
   * Download the TLS/SSL certificate from the hub. This function allows to download a TLS/SSL certificate to add it
   * to the list of trusted certificates using the AddTrustedCertificates method.
   *
   * @param url : the root URL of the VirtualHub V2 or HTTP server.
   * @param mstimeout : the number of milliseconds available to download the certificate.
   *
   * @return a string containing the certificate. In case of error, returns a string starting with "error:".
   */
  async DownloadHostCertificate(url, mstimeout) {
    return await this.DownloadHostCertificate_internal(url, mstimeout);
  }
  /**
   * Adds a TLS/SSL certificate to the list of trusted certificates. By default, the library
   * library will reject TLS/SSL connections to servers whose certificate is not known. This function
   * function allows to add a list of known certificates. It is also possible to disable the verification
   * using the SetNetworkSecurityOptions method.
   *
   * @param certificate : a string containing one or more certificates.
   *
   * @return an empty string if the certificate has been added correctly.
   *         In case of error, returns a string starting with "error:".
   */
  async AddTrustedCertificates(certificate) {
    return await this.AddTrustedCertificates_internal(certificate);
  }
  /**
   * Set the path of Certificate Authority file on local filesystem. This method takes as a parameter
   * the path of a file containing all certificates in PEM format.
   * For technical reasons, only one file can be specified. So if you need to connect to several Hubs
   * instances with self-signed certificates, you'll need to use
   * a single file containing all the certificates end-to-end. Passing a empty string will restore the
   * default settings. This option is only supported by PHP library.
   *
   * @param certificatePath : the path of the file containing all certificates in PEM format.
   *
   * @return an empty string if the certificate has been added correctly.
   *         In case of error, returns a string starting with "error:".
   */
  async SetTrustedCertificatesList(certificatePath) {
    return await this.SetTrustedCertificatesList_internal(certificatePath);
  }
  /**
   * Enables or disables certain TLS/SSL certificate checks.
   *
   * @param opts : The options are YAPI.NO_TRUSTED_CA_CHECK,
   *         YAPI.NO_EXPIRATION_CHECK, YAPI.NO_HOSTNAME_CHECK.
   *
   * @return an empty string if the options are taken into account.
   *         On error, returns a string beginning with "error:".
   */
  async SetNetworkSecurityOptions(opts) {
    return await this.SetNetworkSecurityOptions_internal(opts);
  }
  /**
   * Modifies the network connection delay for yRegisterHub() and yUpdateDeviceList().
   * This delay impacts only the YoctoHubs and VirtualHub
   * which are accessible through the network. By default, this delay is of 20000 milliseconds,
   * but depending on your network you may want to change this delay,
   * gor example if your network infrastructure is based on a GSM connection.
   *
   * @param networkMsTimeout : the network connection delay in milliseconds.
   * @noreturn
   */
  async SetNetworkTimeout(networkMsTimeout) {
    return await this.SetNetworkTimeout_internal(networkMsTimeout);
  }
  /**
   * Returns the network connection delay for yRegisterHub() and yUpdateDeviceList().
   * This delay impacts only the YoctoHubs and VirtualHub
   * which are accessible through the network. By default, this delay is of 20000 milliseconds,
   * but depending on your network you may want to change this delay,
   * for example if your network infrastructure is based on a GSM connection.
   *
   * @return the network connection delay in milliseconds.
   */
  async GetNetworkTimeout() {
    return await this.GetNetworkTimeout_internal();
  }
  /**
   * Change the validity period of the data loaded by the library.
   * By default, when accessing a module, all the attributes of the
   * module functions are automatically kept in cache for the standard
   * duration (5 ms). This method can be used to change this standard duration,
   * for example in order to reduce network or USB traffic. This parameter
   * does not affect value change callbacks
   * Note: This function must be called after yInitAPI.
   *
   * @param cacheValidityMs : an integer corresponding to the validity attributed to the
   *         loaded function parameters, in milliseconds.
   * @noreturn
   */
  async SetCacheValidity(cacheValidityMs) {
    this.defaultCacheValidity = cacheValidityMs;
  }
  /**
   * Returns the validity period of the data loaded by the library.
   * This method returns the cache validity of all attributes
   * module functions.
   * Note: This function must be called after yInitAPI .
   *
   * @return an integer corresponding to the validity attributed to the
   *         loaded function parameters, in milliseconds
   */
  async GetCacheValidity() {
    return this.defaultCacheValidity;
  }
  nextHubInUseInternal(hubref) {
    return this.nextHubInUseInternal_internal(hubref);
  }
  getYHubObj(hubref) {
    let obj;
    obj = this._findYHubFromCache(hubref);
    if (obj == null) {
      obj = new YHub(this, hubref);
      this._addYHubToCache(hubref, obj);
    }
    return obj;
  }
  async findYHubFromID(id) {
    let rhub;
    rhub = this.nextHubInUseInternal(-1);
    while (!(rhub == null)) {
      if (rhub.get_serialNumber() == id) {
        return rhub;
      }
      if (rhub.get_registeredUrl() == id) {
        return rhub;
      }
      rhub = rhub.nextHubInUse();
    }
    return rhub;
  }
  //--- (end of generated code: YAPIContext implementation)
  /**
   * Returns the version identifier for the Yoctopuce library in use.
   * The version is a string in the form "Major.Minor.Build",
   * for instance "1.01.5535". For languages using an external
   * DLL (for instance C#, VisualBasic or Delphi), the character string
   * includes as well the DLL version, for instance
   * "1.01.5535 (1.01.5439)".
   *
   * If you want to verify in your code that the library version is
   * compatible with the version that you have used during development,
   * verify that the major number is strictly equal and that the minor
   * number is greater or equal. The build number is not relevant
   * with respect to the library compatibility.
   *
   * @return a character string describing the library version.
   */
  async GetAPIVersion() {
    return this.imm_GetAPIVersion();
  }
  imm_GetAPIVersion() {
    return (
      /* version number patched automatically */
      "2.1.11996"
    );
  }
  /**
   * Initializes the Yoctopuce programming library explicitly.
   * It is not strictly needed to call yInitAPI(), as the library is
   * automatically  initialized when calling yRegisterHub() for the
   * first time.
   *
   * When YAPI.DETECT_NONE is used as detection mode,
   * you must explicitly use yRegisterHub() to point the API to the
   * VirtualHub on which your devices are connected before trying to access them.
   *
   * @param mode : an integer corresponding to the type of automatic
   *         device detection to use. Possible values are
   *         YAPI.DETECT_NONE, YAPI.DETECT_USB, YAPI.DETECT_NET,
   *         and YAPI.DETECT_ALL.
   * @param errmsg : a string passed by reference to receive any error message.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure returns a negative error code.
   */
  async InitAPI(mode, errmsg) {
    this._detectType = mode;
    if (this.system_env.hasSSDP) {
      if ((mode & this.DETECT_NET) !== 0) {
        await this.TriggerHubDiscovery();
      }
    } else {
    }
    return YAPI_SUCCESS;
  }
  /**
   * Waits for all pending communications with Yoctopuce devices to be
   * completed then frees dynamically allocated resources used by
   * the Yoctopuce library.
   *
   * From an operating system standpoint, it is generally not required to call
   * this function since the OS will automatically free allocated resources
   * once your program is completed. However, there are two situations when
   * you may really want to use that function:
   *
   * - Free all dynamically allocated memory blocks in order to
   * track a memory leak.
   *
   * - Send commands to devices right before the end
   * of the program. Since commands are sent in an asynchronous way
   * the program could exit before all commands are effectively sent.
   *
   * You should not call any other library function after calling
   * yFreeAPI(), or your program will crash.
   */
  async FreeAPI() {
    for (let serial in this._devs) {
      await this._devs[serial].waitPendingQueries();
    }
    await this.KillAPI();
  }
  /**
   * Abort any ongoing API activity immediately by closing all open hubs. Then
   * frees dynamically allocated memory blocks used by the Yoctopuce library.
   * You should not call any other library function after calling
   * yDropAPI(), or your program will crash.
   */
  async KillAPI() {
    if (this._ssdpManager) {
      await this._ssdpManager.ySSDPStop();
      this._ssdpManager = null;
    }
    for (let hub of this._connectedHubs) {
      this.imm_dropConnectedHub(hub);
    }
    for (let serial in this._knownHubsBySerial) {
      let hub = this._knownHubsBySerial[serial];
      if (hub.imm_getcurrentState() > -5) {
        await hub.detach(YAPI.IO_ERROR, "Connection closed by FreeAPI");
      }
    }
    this.imm_ResetToDefaults();
  }
  /**
   * Disables the use of exceptions to report runtime errors.
   * When exceptions are disabled, every function returns a specific
   * error value which depends on its type and which is documented in
   * this reference manual.
   */
  async DisableExceptions() {
    this.exceptionsDisabled = true;
  }
  /**
   * Re-enables the use of exceptions for runtime error handling.
   * Be aware than when exceptions are enabled, every function that fails
   * triggers an exception. If the exception is not caught by the user code,
   * it either fires the debugger or aborts (i.e. crash) the program.
   */
  async EnableExceptions() {
    this.exceptionsDisabled = false;
  }
  /**
   * Enable logging to the console for unhandled promise rejections,
   * such as exceptions in async functions without a try/catch.
   * This is not really a Yoctopuce thing, but since it is not obvious
   * to find out and since the code differs depending on the environment,
   * we provide it here for convenience.
   */
  async LogUnhandledPromiseRejections() {
    this.system_env.hookUnhandledRejection((reason, promise) => {
      this.imm_log("Unhandled Rejection at: Promise ", promise, " reason: ", reason);
    });
  }
  /**
   * Set up the Yoctopuce library to use modules connected on a given machine. Idealy this
   * call will be made once at the begining of your application.  The
   * parameter will determine how the API will work. Use the following values:
   *
   * <b>usb</b>: When the usb keyword is used, the API will work with
   * devices connected directly to the USB bus. Some programming languages such a JavaScript,
   * PHP, and Java don't provide direct access to USB hardware, so usb will
   * not work with these. In this case, use a VirtualHub or a networked YoctoHub (see below).
   *
   * <b><i>x.x.x.x</i></b> or <b><i>hostname</i></b>: The API will use the devices connected to the
   * host with the given IP address or hostname. That host can be a regular computer
   * running a <i>native VirtualHub</i>, a <i>VirtualHub for web</i> hosted on a server,
   * or a networked YoctoHub such as YoctoHub-Ethernet or
   * YoctoHub-Wireless. If you want to use the VirtualHub running on you local
   * computer, use the IP address 127.0.0.1. If the given IP is unresponsive, yRegisterHub
   * will not return until a time-out defined by ySetNetworkTimeout has elapsed.
   * However, it is possible to preventively test a connection  with yTestHub.
   * If you cannot afford a network time-out, you can use the non-blocking yPregisterHub
   * function that will establish the connection as soon as it is available.
   *
   *
   * <b>callback</b>: that keyword make the API run in "<i>HTTP Callback</i>" mode.
   * This a special mode allowing to take control of Yoctopuce devices
   * through a NAT filter when using a VirtualHub or a networked YoctoHub. You only
   * need to configure your hub to call your server script on a regular basis.
   * This mode is currently available for PHP and Node.JS only.
   *
   * Be aware that only one application can use direct USB access at a
   * given time on a machine. Multiple access would cause conflicts
   * while trying to access the USB modules. In particular, this means
   * that you must stop the VirtualHub software before starting
   * an application that uses direct USB access. The workaround
   * for this limitation is to set up the library to use the VirtualHub
   * rather than direct USB access.
   *
   * If access control has been activated on the hub, virtual or not, you want to
   * reach, the URL parameter should look like:
   *
   * http://username:password@address:port
   *
   * You can call <i>RegisterHub</i> several times to connect to several machines. On
   * the other hand, it is useless and even counterproductive to call <i>RegisterHub</i>
   * with to same address multiple times during the life of the application.
   *
   * @param url : a string containing either "usb","callback" or the
   *         root URL of the hub to monitor
   * @param errmsg : a string passed by reference to receive any error message.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure returns a negative error code.
   */
  async RegisterHub(url, errmsg) {
    if (this._logLevel >= 4) {
      this.imm_log("Registering hub: " + url);
    }
    if (url === "net") {
      if (this.system_env.hasSSDP) {
        this._detectType |= this.DETECT_NET;
        return this.TriggerHubDiscovery();
      } else {
        return this.imm_setErr(errmsg, YAPI_NOT_SUPPORTED, "Network discovery is not possible in a browser", YAPI_NOT_SUPPORTED);
      }
    }
    if (url === "usb") {
      return this.imm_setErr(errmsg, YAPI_NOT_SUPPORTED, "Use the VirtualHub on 127.0.0.1 to access local USB devices", YAPI_NOT_SUPPORTED);
    }
    let urlInfo = new _YY_UrlInfo(url);
    let hub = this.imm_getHub(urlInfo);
    if (!hub) {
      if (this._logLevel >= 3) {
        this.imm_log("Registering new hub: " + urlInfo.imm_getRootUrl());
      }
      hub = new YGenericHub(this, urlInfo);
      hub.imm_addKnownUrl(urlInfo);
      this._knownHubsByUrl[urlInfo.imm_getRootUrl()] = hub;
    } else {
      if (this._logLevel >= 3) {
        this.imm_log("Registering existing hub: " + urlInfo.imm_getRootUrl() + " old=" + hub.imm_getRootUrl());
      }
      hub.imm_updateUrl(urlInfo);
    }
    this.imm_updateRegisteredHubs(hub, true);
    await hub.attach(
      2
      /* Y_YHubConnType.HUB_REGISTERED */
    );
    let sub_errmsg = new YErrorMsg();
    let retcode = await hub.waitForConnection(this._networkTimeoutMs, sub_errmsg);
    if (retcode != YAPI_SUCCESS) {
      this.imm_dropConnectedHub(hub);
      this.imm_updateRegisteredHubs(hub, false);
      await hub.detach(retcode, sub_errmsg.msg);
      hub.imm_forgetUrls();
      return this.imm_setErr(errmsg, retcode, sub_errmsg.msg, retcode);
    }
    let yreq = await this._updateDeviceList_internal(true, false);
    if (yreq.errorType != YAPI_SUCCESS) {
      if (this._logLevel >= 3) {
        this.imm_log("Registering failed with" + yreq.errorType + " (" + yreq.errorMsg + ")");
      }
      this.imm_dropConnectedHub(hub);
      this.imm_updateRegisteredHubs(hub, false);
      await hub.detach(yreq.errorType, yreq.errorMsg);
      hub.imm_forgetUrls();
      return this.imm_setErr(errmsg, yreq.errorType, yreq.errorMsg, yreq.errorType);
    }
    return YAPI_SUCCESS;
  }
  /**
   * Fault-tolerant alternative to yRegisterHub(). This function has the same
   * purpose and same arguments as yRegisterHub(), but does not trigger
   * an error when the selected hub is not available at the time of the function call.
   * If the connexion cannot be established immediately, a background task will automatically
   * perform periodic retries. This makes it possible to register a network hub independently of the current
   * connectivity, and to try to contact it only when a device is actively needed.
   *
   * @param url : a string containing either "usb","callback" or the
   *         root URL of the hub to monitor
   * @param errmsg : a string passed by reference to receive any error message.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure returns a negative error code.
   */
  async PreregisterHub(url, errmsg) {
    let urlInfo = new _YY_UrlInfo(url);
    let hub = this.imm_getHub(urlInfo);
    if (!hub) {
      if (this._logLevel >= 3) {
        this.imm_log("Preregistering new hub: " + urlInfo.imm_getRootUrl());
      }
      hub = new YGenericHub(this, urlInfo);
      if (!hub) {
        return this.imm_setErr(errmsg, YAPI_NOT_SUPPORTED, "Unsupported hub protocol: " + urlInfo.imm_getProto(), YAPI_NOT_SUPPORTED);
      }
      hub.imm_addKnownUrl(urlInfo);
      this._knownHubsByUrl[urlInfo.imm_getRootUrl()] = hub;
    } else {
      if (this._logLevel >= 3) {
        this.imm_log("Preregistering existing hub: " + urlInfo.imm_getRootUrl());
      }
      hub.imm_updateUrl(urlInfo);
    }
    await hub.attach(
      1
      /* Y_YHubConnType.HUB_PREREGISTERED */
    );
    this.imm_updateRegisteredHubs(hub, true);
    return YAPI_SUCCESS;
  }
  /**
   * Setup the Yoctopuce library to use modules connected on a remote hub
   * performing an incoming connection to an HTTP server.
   *
   * @param incomingMessage {IncomingMessage} : node http incomingMessage object.
   * @param serverResponse  {ServerResponse} : node http serverResponse object.
   * @param errmsg {YErrorMsg} : a string passed by reference to receive any error message.
   *
   * @return {number} YAPI_SUCCESS when the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async RegisterHubHttpCallback(incomingMessage, serverResponse, errmsg) {
    let url = "http://callback:4444";
    let urlInfo = new _YY_UrlInfo(url);
    let hub = this.imm_getHub(urlInfo);
    if (!hub) {
      hub = new YGenericHub(this, urlInfo);
      let engine = this.system_env.getHttpCallbackEngine(hub, urlInfo, incomingMessage, serverResponse);
      if (!engine) {
        return this.imm_setErr(errmsg, YAPI_NOT_SUPPORTED, "HTTP Callback mode is not available in this environment", YAPI_NOT_SUPPORTED);
      }
      hub.imm_setHubEngine(engine);
    }
    await hub.attach(
      3
      /* Y_YHubConnType.HUB_CALLBACK */
    );
    let sub_errmsg = new YErrorMsg();
    let retcode = await hub.waitForConnection(this._networkTimeoutMs, sub_errmsg);
    if (retcode != YAPI_SUCCESS) {
      this.imm_dropConnectedHub(hub);
      await hub.detach(retcode, sub_errmsg.msg);
      return this.imm_setErr(errmsg, retcode, sub_errmsg.msg, retcode);
    }
    let yreq = await this._updateDeviceList_internal(true, false);
    if (yreq.errorType != YAPI_SUCCESS) {
      await hub.reportFailure(yreq.errorMsg);
      return this.imm_setErr(errmsg, yreq.errorType, yreq.errorMsg, yreq.errorType);
    }
    this.imm_updateRegisteredHubs(hub, true);
    return YAPI_SUCCESS;
  }
  /**
   * Setup the Yoctopuce library to use modules connected on a remote hub
   * performing an incoming connection to a websocket server.
   *
   * @param ws {WebSocket} : node WebSocket object for the incoming websocket callback connection.
   * @param errmsg {YErrorMsg} : a string passed by reference to receive any error message.
   * @param authpwd {string} : an optional authentication password
   *
   * @return {number} YAPI_SUCCESS when the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async RegisterHubWebSocketCallback(ws, errmsg, authpwd) {
    let authstr = authpwd ? "ws:" + authpwd + "@" : "";
    let url = "http://" + authstr + "callback:4444";
    let urlInfo = new _YY_UrlInfo(url);
    let hub = this.imm_getHub(urlInfo);
    if (!hub) {
      hub = new YGenericHub(this, urlInfo);
      let engine = this.system_env.getWebSocketCallbackEngine(hub, urlInfo, ws);
      if (!engine) {
        return this.imm_setErr(errmsg, YAPI_NOT_SUPPORTED, "WebSocket Callback mode is not available in this environment", YAPI_NOT_SUPPORTED);
      }
      hub.imm_setHubEngine(engine);
    }
    await hub.attach(
      3
      /* Y_YHubConnType.HUB_CALLBACK */
    );
    let sub_errmsg = new YErrorMsg();
    let retcode = await hub.waitForConnection(this._networkTimeoutMs, sub_errmsg);
    if (retcode != YAPI_SUCCESS) {
      this.imm_dropConnectedHub(hub);
      await hub.detach(retcode, sub_errmsg.msg);
      return this.imm_setErr(errmsg, retcode, sub_errmsg.msg, retcode);
    }
    let yreq = await this._updateDeviceList_internal(true, false);
    if (yreq.errorType != YAPI_SUCCESS) {
      this.imm_dropConnectedHub(hub);
      await hub.detach(yreq.errorType, yreq.errorMsg);
      return this.imm_setErr(errmsg, yreq.errorType, yreq.errorMsg, yreq.errorType);
    }
    this.imm_updateRegisteredHubs(hub, true);
    return YAPI_SUCCESS;
  }
  async WebSocketJoin(ws, arr_credentials, closeCallback) {
    if (this._connectedHubs.length == 0) {
      return false;
    }
    let hub = this._connectedHubs[0];
    return await hub.WebSocketJoin(ws, arr_credentials, closeCallback);
  }
  /**
   * Set up the Yoctopuce library to no more use modules connected on a previously
   * registered machine with RegisterHub.
   *
   * @param url : a string containing either "usb" or the
   *         root URL of the hub to monitor
   */
  async UnregisterHub(url) {
    let urlInfo = new _YY_UrlInfo(url);
    let hub = this.imm_getHub(urlInfo);
    if (hub) {
      for (let serial in this._devs) {
        await this._devs[serial].waitPendingQueries();
      }
      await hub.waitForPendingQueries(200);
      let serialNumber = hub.imm_getSerialNumber();
      if (serialNumber) {
        let activeHub = this._knownHubsBySerial[serialNumber];
        if (activeHub) {
          hub.imm_forgetUrls();
          hub = activeHub;
          urlInfo = hub.urlInfo;
        }
      }
      if (this._logLevel >= 3) {
        this.imm_log("Unregistering hub " + url + " (" + urlInfo.imm_getRootUrl() + ")");
      }
      this.imm_dropConnectedHub(hub);
      if (hub.imm_isDisconnected()) {
        if (this._logLevel >= 3) {
          this.imm_log("Hub " + urlInfo.imm_getRootUrl() + " is already disconnected");
        }
        this.imm_updateRegisteredHubs(hub, false);
        return;
      }
      let before = this.GetTickCount();
      let disconnected = hub.waitForDisconnection(500);
      if (hub.imm_isDisconnecting()) {
        if (this._logLevel >= 3) {
          this.imm_log("Hub " + urlInfo.imm_getRootUrl() + " is already disconnecting");
        }
      } else {
        await hub.detach(YAPI.IO_ERROR, "Hub has been unregistered");
      }
      await disconnected;
      hub.imm_forgetUrls();
      this.imm_updateRegisteredHubs(hub, false);
      if (this._logLevel >= 4) {
        this.imm_log("Disconnected after " + (this.GetTickCount() - before) + " ms");
      }
    } else {
      if (this._logLevel >= 4) {
        this.imm_log("No hub to Unregister with " + url + " (" + urlInfo.imm_getRootUrl() + ")");
      }
    }
  }
  /**
   * Test if the hub is reachable. This method do not register the hub, it only test if the
   * hub is usable. The url parameter follow the same convention as the yRegisterHub
   * method. This method is useful to verify the authentication parameters for a hub. It
   * is possible to force this method to return after mstimeout milliseconds.
   *
   * @param url : a string containing either "usb","callback" or the
   *         root URL of the hub to monitor
   * @param mstimeout : the number of millisecond available to test the connection.
   * @param errmsg : a string passed by reference to receive any error message.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure returns a negative error code.
   */
  async TestHub(url, mstimeout, errmsg) {
    if (url == "net") {
      return this.imm_setErr(errmsg, YAPI_INVALID_ARGUMENT, "Not supported", YAPI_INVALID_ARGUMENT);
    }
    let urlInfo = new _YY_UrlInfo(url);
    let hub = this.imm_getHub(urlInfo);
    if (!hub) {
      if (this._logLevel >= 4) {
        this.imm_log("Testing new hub: " + urlInfo.imm_getRootUrl());
      }
      hub = new YGenericHub(this, urlInfo);
      if (!hub) {
        return this.imm_setErr(errmsg, YAPI_NOT_SUPPORTED, "Unsupported hub protocol: " + urlInfo.imm_getProto(), YAPI_NOT_SUPPORTED);
      }
    } else {
      if (this._logLevel >= 4) {
        this.imm_log("Testing existing hub: " + hub.imm_getRootUrl());
      }
    }
    await hub.attach(
      0
      /* Y_YHubConnType.HUB_CONNECTED */
    );
    let sub_errmsg = new YErrorMsg();
    let retcode = await hub.waitForConnection(mstimeout, sub_errmsg);
    if (retcode != YAPI_SUCCESS) {
      return this.imm_setErr(errmsg, retcode, sub_errmsg.msg, retcode);
    }
    return YAPI_SUCCESS;
  }
  /**
   * Triggers a (re)detection of connected Yoctopuce modules.
   * The library searches the machines or USB ports previously registered using
   * yRegisterHub(), and invokes any user-defined callback function
   * in case a change in the list of connected devices is detected.
   *
   * This function can be called as frequently as desired to refresh the device list
   * and to make the application aware of hot-plug events. However, since device
   * detection is quite a heavy process, UpdateDeviceList shouldn't be called more
   * than once every two seconds.
   *
   * @param errmsg : a string passed by reference to receive any error message.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure returns a negative error code.
   */
  async UpdateDeviceList(errmsg = null) {
    let yreq = await this._updateDeviceList_internal(false, true);
    if (yreq.errorType !== YAPI_SUCCESS) {
      return this.imm_setErr(errmsg, yreq.errorType, yreq.errorMsg, yreq.errorType);
    }
    return YAPI_SUCCESS;
  }
  async _hubDiscoveryCallback_internal(serial, urlToRegister, urlToUnregister) {
    if (this._hubDiscoveryCallback && urlToRegister) {
      try {
        await this._hubDiscoveryCallback(serial, urlToRegister);
      } catch (e) {
        this.imm_log("Exception in hub discovery callback:", e);
      }
    }
    if ((this._detectType & Y_DETECT_NET) !== 0) {
      if (urlToRegister) {
        if (urlToUnregister) {
          await this.UnregisterHub(urlToUnregister);
        }
        await this.PreregisterHub(urlToRegister, new YErrorMsg());
      }
    }
  }
  /**
   * Force a hub discovery, if a callback as been registered with yRegisterHubDiscoveryCallback it
   * will be called for each net work hub that will respond to the discovery.
   *
   * @param errmsg : a string passed by reference to receive any error message.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *         On failure returns a negative error code.
   */
  async TriggerHubDiscovery(errmsg = null) {
    if (!this._ssdpManager) {
      this._ssdpManager = this.system_env.getSSDPManager(this);
      if (!this._ssdpManager)
        return this._lastErrorType;
      await this._ssdpManager.ySSDPStart((serial, newUrl, oldUrl) => {
        this._hubDiscoveryCallback_internal(serial, newUrl, oldUrl);
      });
    } else {
      await this._ssdpManager.ySSDPDiscover();
    }
    return YAPI_SUCCESS;
  }
  /**
   * Maintains the device-to-library communication channel.
   * If your program includes significant loops, you may want to include
   * a call to this function to make sure that the library takes care of
   * the information pushed by the modules on the communication channels.
   * This is not strictly necessary, but it may improve the reactivity
   * of the library for the following commands.
   *
   * This function may signal an error in case there is a communication problem
   * while contacting a module.
   *
   * @param errmsg : a string passed by reference to receive any error message.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure returns a negative error code.
   */
  async HandleEvents(errmsg = null) {
    return YAPI_SUCCESS;
  }
  /**
   * Pauses the execution flow for a specified duration.
   * This function implements a passive waiting loop, meaning that it does not
   * consume CPU cycles significantly. The processor is left available for
   * other threads and processes. During the pause, the library nevertheless
   * reads from time to time information from the Yoctopuce modules by
   * calling yHandleEvents(), in order to stay up-to-date.
   *
   * This function may signal an error in case there is a communication problem
   * while contacting a module.
   *
   * @param ms_duration : an integer corresponding to the duration of the pause,
   *         in milliseconds.
   * @param errmsg : a string passed by reference to receive any error message.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure returns a negative error code.
   */
  async Sleep(ms_duration, errmsg = null) {
    let end = this.GetTickCount() + ms_duration;
    let remaining = ms_duration;
    while (remaining > 0) {
      let waitTime = Math.min(remaining, 25);
      await new Promise((resolve, reject) => {
        setTimeout(resolve, waitTime);
      });
      remaining = end - this.GetTickCount();
    }
    return YAPI_SUCCESS;
  }
  // internal async function to wait for a very short period
  _microSleep_internal() {
    return new Promise(function(resolve, reject) {
      setTimeout(resolve, 3);
    });
  }
  /**
   * Invoke the specified callback function after a given timeout.
   * This function behaves more or less like Javascript setTimeout,
   * but during the waiting time, it will call yHandleEvents
   * and yUpdateDeviceList periodically, in order to
   * keep the API up-to-date with current devices.
   *
   * @param callback : the function to call after the timeout occurs.
   *         On Microsoft Internet Explorer, the callback must
   *         be provided as a string to be evaluated.
   * @param ms_timeout : an integer corresponding to the duration of the
   *         timeout, in milliseconds.
   * @param args : additional arguments to be passed to the
   *         callback function can be provided, if needed
   *         (not supported on Microsoft Internet Explorer).
   *
   * @return YAPI.SUCCESS
   */
  SetTimeout(callback, ms_timeout, args) {
    let endtime = this.GetTickCount() + ms_timeout;
    let setTimeout_internal = async () => {
      let delay = endtime - this.GetTickCount();
      if (delay < 40) {
        if (delay > 3) {
          await new Promise((resolve, reject) => {
            setTimeout(resolve, delay);
          });
        }
        callback.apply(null, args);
      } else {
        if (delay >= 150) {
          await this.UpdateDeviceList();
          delay = Math.min(endtime - YAPI.GetTickCount(), 110);
        }
        await this.Sleep(delay - 20);
        setTimeout_internal();
      }
    };
    setTimeout_internal();
    return YAPI_SUCCESS;
  }
  /**
   * Returns the current value of a monotone millisecond-based time counter.
   * This counter can be used to compute delays in relation with
   * Yoctopuce devices, which also uses the millisecond as timebase.
   *
   * @return a long integer corresponding to the millisecond counter.
   */
  GetTickCount() {
    return Date.now();
  }
  imm_CheckLogicalName(name) {
    if (name == "")
      return true;
    if (!name)
      return false;
    if (name.length > 19)
      return false;
    return /^[A-Za-z0-9_\-]*$/.test(name);
  }
  /**
   * Checks if a given string is valid as logical name for a module or a function.
   * A valid logical name has a maximum of 19 characters, all among
   * A...Z, a...z, 0...9, _, and -.
   * If you try to configure a logical name with an incorrect string,
   * the invalid characters are ignored.
   *
   * @param name : a string containing the name to check.
   *
   * @return true if the name is valid, false otherwise.
   */
  async CheckLogicalName(name) {
    return this.imm_CheckLogicalName(name);
  }
  /**
   * Register a callback function, to be called each time
   * a device is plugged. This callback will be invoked while yUpdateDeviceList
   * is running. You will have to call this function on a regular basis.
   *
   * @param arrivalCallback : a procedure taking a YModule parameter, or null
   *         to unregister a previously registered  callback.
   */
  async RegisterDeviceArrivalCallback(arrivalCallback) {
    this._arrivalCallback = arrivalCallback;
  }
  async RegisterDeviceChangeCallback(changeCallback) {
    this._namechgCallback = changeCallback;
  }
  /**
   * Register a callback function, to be called each time
   * a device is unplugged. This callback will be invoked while yUpdateDeviceList
   * is running. You will have to call this function on a regular basis.
   *
   * @param removalCallback : a procedure taking a YModule parameter, or null
   *         to unregister a previously registered  callback.
   */
  async RegisterDeviceRemovalCallback(removalCallback) {
    this._removalCallback = removalCallback;
  }
  /**
   * Register a callback function, to be called each time an Network Hub send
   * an SSDP message. The callback has two string parameter, the first one
   * contain the serial number of the hub and the second contain the URL of the
   * network hub (this URL can be passed to RegisterHub). This callback will be invoked
   * while yUpdateDeviceList is running. You will have to call this function on a regular basis.
   *
   * @param hubDiscoveryCallback : a procedure taking two string parameter, the serial
   *         number and the hub URL. Use null to unregister a previously registered  callback.
   */
  async RegisterHubDiscoveryCallback(hubDiscoveryCallback) {
    if (!this.system_env.hasSSDP) {
      return this._throw(YAPI_NOT_SUPPORTED, "Hub discovery is not supported in this environment", YAPI_NOT_SUPPORTED);
    }
    this._hubDiscoveryCallback = hubDiscoveryCallback;
    return this.TriggerHubDiscovery();
  }
  // Register a new value calibration handler for a given calibration type
  //
  async RegisterCalibrationHandler(calibrationType, calibrationHandler) {
    this._calibHandlers[calibrationType] = calibrationHandler;
  }
  // Standard value calibration handler (n-point linear error correction)
  //
  LinearCalibrationHandler(float_rawValue, int_calibType, arr_calibParams, arr_calibRawValues, arr_calibRefValues) {
    let npt;
    let x = arr_calibRawValues[0];
    let adj = arr_calibRefValues[0] - x;
    let i = 0;
    if (int_calibType < YOCTO_CALIB_TYPE_OFS) {
      npt = Math.min(int_calibType % 10, arr_calibRawValues.length, arr_calibRefValues.length);
    } else {
      npt = arr_calibRefValues.length;
    }
    while (float_rawValue > arr_calibRawValues[i] && ++i < npt) {
      let x2 = x;
      let adj2 = adj;
      x = arr_calibRawValues[i];
      adj = arr_calibRefValues[i] - x;
      if (float_rawValue < x && x > x2) {
        adj = adj2 + (adj - adj2) * (float_rawValue - x2) / (x - x2);
      }
    }
    return float_rawValue + adj;
  }
  /**
   * Compute the MD5 digest for a given ASCII string
   *
   * @param text {string} : the ASCII string to hash
   *
   * @return {Uint8Array} the 16-bytes MD5 hash key
   */
  imm_yMD5(text) {
    let ctx = new Y_MD5Ctx();
    ctx.addData(this.imm_str2bin(text));
    return ctx.calculate();
  }
  // SHA1 and WPA preshared-key computation
  //
  imm_initshaw(str_s, int_ofs, int_pad, int_xinit, _shaw) {
    let ii;
    let j = -1;
    let k = 0;
    let n = str_s.length;
    for (ii = 0; ii < 64; ii++) {
      let i = int_ofs + ii;
      let c = 0;
      if (i < n) {
        c = str_s.charCodeAt(i);
      } else if (int_pad != 0) {
        if ((int_pad & 128) != 0) {
          if (i == n)
            c = int_pad;
        } else {
          if (i == n + 3) {
            c = int_pad;
          } else if (i == n + 4)
            c = 128;
        }
      }
      if (k == 0) {
        j++;
        _shaw[j] = 0;
        k = 32;
      }
      k -= 8;
      _shaw[j] |= c << k;
    }
    if (int_pad != 0) {
      if (int_pad == 128) {
        if (n <= int_ofs + 55) {
          _shaw[15] = 8 * n;
        }
      } else {
        _shaw[15] = 8 * (64 + n + 4);
      }
    }
    if (int_xinit != 0) {
      let xdw = int_xinit << 16 | int_xinit;
      for (j = 0; j < 16; j++) {
        _shaw[j] ^= xdw;
      }
    }
  }
  imm_itershaw(s, _shaw) {
    let a, b, c, d, e, t, k;
    a = s[0];
    b = s[1];
    c = s[2];
    d = s[3];
    e = s[4];
    for (k = 16; k < 80; k++) {
      t = _shaw[k - 3] ^ _shaw[k - 8] ^ _shaw[k - 14] ^ _shaw[k - 16];
      _shaw[k] = t << 1 | t >>> 31;
    }
    for (k = 0; k < 20; k++) {
      t = (a << 5 | a >>> 27) + e + _shaw[k] + 1518500249 + (b & c | ~b & d);
      e = d;
      d = c;
      c = b << 30 | b >>> 2;
      b = a;
      a = t & 4294967295;
    }
    for (k = 20; k < 40; k++) {
      t = (a << 5 | a >>> 27) + e + _shaw[k] + 1859775393 + (b ^ c ^ d);
      e = d;
      d = c;
      c = b << 30 | b >>> 2;
      b = a;
      a = t & 4294967295;
    }
    for (k = 40; k < 60; k++) {
      t = (a << 5 | a >>> 27) + e + _shaw[k] + 2400959708 + (b & c | b & d | c & d);
      e = d;
      d = c;
      c = b << 30 | b >>> 2;
      b = a;
      a = t & 4294967295;
    }
    for (k = 60; k < 80; k++) {
      t = (a << 5 | a >>> 27) + e + _shaw[k] + 3395469782 + (b ^ c ^ d);
      e = d;
      d = c;
      c = b << 30 | b >>> 2;
      b = a;
      a = t & 4294967295;
    }
    _shaw[0] = s[0] + a & 4294967295;
    _shaw[1] = s[1] + b & 4294967295;
    _shaw[2] = s[2] + c & 4294967295;
    _shaw[3] = s[3] + d & 4294967295;
    _shaw[4] = s[4] + e & 4294967295;
  }
  /**
   * Compute the SHA1 digest for a given ASCII string
   *
   * @param text {string} : the ASCII string to hash
   *
   * @return {Uint8Array} the 20-bytes SHA1 hash key
   */
  imm_ySHA1(text) {
    let shau = [1732584193, 4023233417, 2562383102, 271733878, 3285377520];
    let i, ofs = 0;
    let n = text.length;
    let _shaw = new Uint32Array(80);
    do {
      this.imm_initshaw(text, ofs, 128, 0, _shaw);
      this.imm_itershaw(shau, _shaw);
      for (i = 0; i < 5; i++) {
        shau[i] = _shaw[i];
      }
      ofs += 64;
    } while (n > ofs - 9);
    let res = new Uint8Array(20);
    for (i = 0; i < 20; i++) {
      res[i] = shau[i >>> 2] >>> 24 - 8 * (i & 3) & 255;
    }
    return res;
  }
  /**
   * Compute the WPA Preshared key for a given SSID and passphrase
   *
   * @param ssid {string} : the access point SSID
   * @param pass {string} : the access point WPA/WPA2 passphrase
   *
   * @return {string} an hexadecimal string for the preshared key
   */
  async ComputePSK(ssid, pass) {
    let sha1_init = [1732584193, 4023233417, 2562383102, 271733878, 3285377520];
    let inner = [], outer = [], shau = [], res = [];
    let iter, pos, k, _shaw;
    _shaw = new Uint32Array(80);
    this.imm_initshaw(pass, 0, 0, 13878, _shaw);
    this.imm_itershaw(sha1_init, _shaw);
    for (k = 0; k < 5; k++) {
      inner[k] = _shaw[k];
    }
    _shaw = new Uint32Array(80);
    this.imm_initshaw(pass, 0, 0, 23644, _shaw);
    this.imm_itershaw(sha1_init, _shaw);
    for (k = 0; k < 5; k++) {
      outer[k] = _shaw[k];
    }
    pos = 0;
    for (k = 0; k < 5; k++) {
      shau[k] = 0;
    }
    _shaw = new Uint32Array(80);
    this.imm_initshaw(ssid, 0, 1, 0, _shaw);
    for (iter = 0; iter < 8192; ) {
      this.imm_itershaw(inner, _shaw);
      _shaw[5] = 2147483648;
      for (k = 6; k < 15; k++) {
        _shaw[k] = 0;
      }
      _shaw[15] = 8 * (64 + 20);
      this.imm_itershaw(outer, _shaw);
      shau[0] ^= _shaw[0];
      shau[1] ^= _shaw[1];
      shau[2] ^= _shaw[2];
      shau[3] ^= _shaw[3];
      shau[4] ^= _shaw[4];
      iter++;
      if ((iter & 4095) == 0) {
        for (k = 0; k < 5 && pos < 32; k++) {
          res[pos++] = shau[k] >>> 24 & 255;
          res[pos++] = shau[k] >>> 16 & 255;
          res[pos++] = shau[k] >>> 8 & 255;
          res[pos++] = shau[k] & 255;
        }
        if (iter == 4096) {
          for (k = 0; k < 5; k++) {
            shau[k] = 0;
          }
          _shaw = new Uint32Array(80);
          this.imm_initshaw(ssid, 0, 2, 0, _shaw);
        }
      }
    }
    let hex = "";
    for (k = 0; k < 32; k++) {
      hex += ("0" + Number(res[k]).toString(16)).slice(-2);
    }
    return hex;
  }
  nextHubInUseInternal_internal(hubref) {
    let nextref = hubref < 0 ? 0 : hubref + 1;
    let restart;
    let has_higher_hubref;
    do {
      has_higher_hubref = false;
      restart = false;
      for (let url in this._registeredHubs) {
        let hub = this._registeredHubs[url];
        let hubRef = hub.getHubRef();
        if (hubRef == nextref) {
          return this.getYHubObj(nextref);
        } else if (hubRef > nextref) {
          has_higher_hubref = true;
        }
      }
      if (has_higher_hubref) {
        nextref++;
        restart = true;
      }
    } while (restart);
    return null;
  }
  getGenHub(hubref) {
    for (let i = 0; i < this._registeredHubs.length; i++) {
      let hub = this._registeredHubs[i];
      if (hub.getHubRef() == hubref) {
        return hub;
      }
    }
    return null;
  }
  _findYHubFromCache(hubref) {
    return this._yhub_cache[hubref];
  }
  _addYHubToCache(hubref, obj) {
    this._yhub_cache[hubref] = obj;
  }
};
YAPIContext.SUCCESS = 0;
YAPIContext.NOT_INITIALIZED = -1;
YAPIContext.INVALID_ARGUMENT = -2;
YAPIContext.NOT_SUPPORTED = -3;
YAPIContext.DEVICE_NOT_FOUND = -4;
YAPIContext.VERSION_MISMATCH = -5;
YAPIContext.DEVICE_BUSY = -6;
YAPIContext.TIMEOUT = -7;
YAPIContext.IO_ERROR = -8;
YAPIContext.NO_MORE_DATA = -9;
YAPIContext.EXHAUSTED = -10;
YAPIContext.DOUBLE_ACCES = -11;
YAPIContext.UNAUTHORIZED = -12;
YAPIContext.RTC_NOT_READY = -13;
YAPIContext.FILE_NOT_FOUND = -14;
YAPIContext.SSL_ERROR = -15;
YAPIContext.RFID_SOFT_ERROR = -16;
YAPIContext.RFID_HARD_ERROR = -17;
YAPIContext.BUFFER_TOO_SMALL = -18;
YAPIContext.DNS_ERROR = -19;
YAPIContext.SSL_UNK_CERT = -20;
YAPIContext.UNCONFIGURED = -21;
YAPIContext.NO_TRUSTED_CA_CHECK = 1;
YAPIContext.NO_EXPIRATION_CHECK = 2;
YAPIContext.NO_HOSTNAME_CHECK = 4;
YAPIContext.LEGACY = 8;
var YAPI = new YAPIContext();

// obj/full/Api/yocto_api_html.js
var YSystemEnvHtml = class extends YSystemEnv {
  constructor() {
    super(...arguments);
    this.isNodeJS = false;
    this.hasSSDP = false;
  }
  hookUnhandledRejection(handler) {
    window.addEventListener("onunhandledrejection", (event) => {
      let promiseRejectionEvent = event;
      handler(promiseRejectionEvent.reason, promiseRejectionEvent.promise);
    });
  }
  getWebSocketEngine(hub, runtime_urlInfo) {
    return new YWebSocketHtmlEngine(hub, runtime_urlInfo);
  }
  getHttpEngine(hub, runtime_urlInfo, firstInfoJson) {
    return new YHttpHtmlEngine(hub, runtime_urlInfo, firstInfoJson);
  }
  getWebSocketCallbackHub(hub, ws) {
    return hub._yapi._throw(YAPI.NOT_SUPPORTED, "WebSocket Callback mode is not available in this environment", null);
  }
  getHttpCallbackHub(hub, incomingMessage, serverResponse) {
    return hub._yapi._throw(YAPI.NOT_SUPPORTED, "HTTP Callback mode is not available in this environment", null);
  }
  getSSDPManager(obj_yapi) {
    return obj_yapi._throw(YAPI.NOT_SUPPORTED, "Hub discovery is not available in this environment", null);
  }
  loadfile(file) {
    return new Promise((resolve, reject) => {
      let reader = new FileReader();
      reader.onerror = function(evt) {
        reject(evt.target.error);
      };
      reader.onload = function(evt) {
        resolve(new Uint8Array(evt.target.result));
      };
      reader.readAsArrayBuffer(file);
    });
  }
  downloadfile(url, yapi) {
    return new Promise((resolve, reject) => {
      let httpRequest = new XMLHttpRequest();
      httpRequest.open("GET", url, true);
      httpRequest.overrideMimeType("text/plain; charset=x-user-defined");
      httpRequest.onreadystatechange = (() => {
        if (httpRequest.readyState == 4) {
          if (httpRequest.status != 200 && httpRequest.status != 304) {
            if (httpRequest.status) {
              reject(new Error("HTTP error " + httpRequest.status));
            } else {
              reject(new Error("Unable to complete HTTP request, network down?"));
            }
          } else {
            resolve(YAPI.imm_str2bin(httpRequest.responseText));
          }
        }
      });
      httpRequest.send("");
    });
  }
  async downloadRemoteCertificate(urlinfo) {
    return "error: Not supported in browser";
  }
};
var _HtmlSystemEnv = new YSystemEnvHtml();
YAPI.imm_setSystemEnv(_HtmlSystemEnv);
var YHttpHtmlEngine = class extends YHttpEngine {
  constructor(hub, runtime_urlInfo, firstInfoJson) {
    super(hub, runtime_urlInfo, firstInfoJson);
  }
  // Low-level function to create an HTTP client request (abstraction layer)
  imm_makeRequest(method, relUrl, contentType, body, onProgress, onSuccess, onError) {
    let xhr = new XMLHttpRequest();
    let currPos = 0;
    xhr.open(method, this._runtime_urlInfo.imm_getUrl(true, true, false) + relUrl, true, "", "");
    xhr.setRequestHeader("Content-Type", contentType);
    xhr.overrideMimeType("text/plain; charset=x-user-defined");
    xhr.onreadystatechange = () => {
      if (xhr.readyState >= 3) {
        let httpStatus = xhr.status >> 0;
        if (xhr.readyState == 4 && httpStatus != 200 && httpStatus != 304) {
          if (httpStatus == 401 || httpStatus == 204) {
            this.infoJson.stamp = 0;
            onError(YAPI.UNAUTHORIZED, "Unauthorized access (" + xhr.status + ")", false);
          } else if (httpStatus == 404) {
            onError(YAPI.FILE_NOT_FOUND, "HTTP request return status 404 (not found)", false);
          } else if (this._hub.imm_isDisconnecting()) {
            onError(YAPI.IO_ERROR, "Hub is disconnecting", false);
          } else {
            onError(YAPI.IO_ERROR, "HTTP request failed with status " + xhr.status, false);
          }
          return;
        }
        if (this._hub.imm_isDisconnecting()) {
          if (this._hub._yapi._logLevel >= 4) {
            this._hub._yapi.imm_log("Dropping request " + relUrl + " because hub is disconnecting");
          }
          return;
        }
        if (onProgress && xhr.responseText) {
          let newlen = xhr.responseText.length;
          if (newlen > currPos) {
            onProgress(xhr.responseText.slice(currPos, newlen));
            currPos = newlen;
          }
        }
        if (onSuccess && xhr.readyState == 4) {
          onSuccess(xhr.responseText);
        }
      }
    };
    xhr.onerror = () => {
      onError(YAPI.IO_ERROR, "HTTP request failed without status", false);
    };
    xhr.send(body);
    return xhr;
  }
  // abort communication channel immediately
  imm_abortRequest(clientRequest) {
    clientRequest.abort();
  }
};
var YWebSocketHtmlEngine = class extends YWebSocketEngine {
  /** Open an outgoing websocket
   **/
  imm_webSocketOpen(str_url) {
    let websock = new WebSocket(str_url);
    websock.binaryType = "arraybuffer";
    this.websocket = websock;
  }
  /** Fills a buffer with random numbers
   **/
  imm_getRandomValues(arr) {
    return window.crypto.getRandomValues(arr);
  }
};

// obj/full/Api/yocto_network.js
var YNetwork = class _YNetwork extends YFunction {
  //--- (end of YNetwork attributes declaration)
  constructor(yapi, func) {
    super(yapi, func);
    this._readiness = _YNetwork.READINESS_INVALID;
    this._macAddress = _YNetwork.MACADDRESS_INVALID;
    this._ipAddress = _YNetwork.IPADDRESS_INVALID;
    this._subnetMask = _YNetwork.SUBNETMASK_INVALID;
    this._router = _YNetwork.ROUTER_INVALID;
    this._currentDNS = _YNetwork.CURRENTDNS_INVALID;
    this._ipConfig = _YNetwork.IPCONFIG_INVALID;
    this._primaryDNS = _YNetwork.PRIMARYDNS_INVALID;
    this._secondaryDNS = _YNetwork.SECONDARYDNS_INVALID;
    this._ntpServer = _YNetwork.NTPSERVER_INVALID;
    this._userPassword = _YNetwork.USERPASSWORD_INVALID;
    this._adminPassword = _YNetwork.ADMINPASSWORD_INVALID;
    this._httpPort = _YNetwork.HTTPPORT_INVALID;
    this._httpsPort = _YNetwork.HTTPSPORT_INVALID;
    this._securityMode = _YNetwork.SECURITYMODE_INVALID;
    this._defaultPage = _YNetwork.DEFAULTPAGE_INVALID;
    this._discoverable = _YNetwork.DISCOVERABLE_INVALID;
    this._wwwWatchdogDelay = _YNetwork.WWWWATCHDOGDELAY_INVALID;
    this._callbackUrl = _YNetwork.CALLBACKURL_INVALID;
    this._callbackMethod = _YNetwork.CALLBACKMETHOD_INVALID;
    this._callbackEncoding = _YNetwork.CALLBACKENCODING_INVALID;
    this._callbackTemplate = _YNetwork.CALLBACKTEMPLATE_INVALID;
    this._callbackCredentials = _YNetwork.CALLBACKCREDENTIALS_INVALID;
    this._callbackInitialDelay = _YNetwork.CALLBACKINITIALDELAY_INVALID;
    this._callbackSchedule = _YNetwork.CALLBACKSCHEDULE_INVALID;
    this._callbackMinDelay = _YNetwork.CALLBACKMINDELAY_INVALID;
    this._callbackMaxDelay = _YNetwork.CALLBACKMAXDELAY_INVALID;
    this._poeCurrent = _YNetwork.POECURRENT_INVALID;
    this._valueCallbackNetwork = null;
    this.READINESS_DOWN = 0;
    this.READINESS_EXISTS = 1;
    this.READINESS_LINKED = 2;
    this.READINESS_LAN_OK = 3;
    this.READINESS_WWW_OK = 4;
    this.READINESS_INVALID = -1;
    this.MACADDRESS_INVALID = YAPI.INVALID_STRING;
    this.IPADDRESS_INVALID = YAPI.INVALID_STRING;
    this.SUBNETMASK_INVALID = YAPI.INVALID_STRING;
    this.ROUTER_INVALID = YAPI.INVALID_STRING;
    this.CURRENTDNS_INVALID = YAPI.INVALID_STRING;
    this.IPCONFIG_INVALID = YAPI.INVALID_STRING;
    this.PRIMARYDNS_INVALID = YAPI.INVALID_STRING;
    this.SECONDARYDNS_INVALID = YAPI.INVALID_STRING;
    this.NTPSERVER_INVALID = YAPI.INVALID_STRING;
    this.USERPASSWORD_INVALID = YAPI.INVALID_STRING;
    this.ADMINPASSWORD_INVALID = YAPI.INVALID_STRING;
    this.HTTPPORT_INVALID = YAPI.INVALID_UINT;
    this.HTTPSPORT_INVALID = YAPI.INVALID_UINT;
    this.SECURITYMODE_UNDEFINED = 0;
    this.SECURITYMODE_LEGACY = 1;
    this.SECURITYMODE_MIXED = 2;
    this.SECURITYMODE_SECURE = 3;
    this.SECURITYMODE_INVALID = -1;
    this.DEFAULTPAGE_INVALID = YAPI.INVALID_STRING;
    this.DISCOVERABLE_FALSE = 0;
    this.DISCOVERABLE_TRUE = 1;
    this.DISCOVERABLE_INVALID = -1;
    this.WWWWATCHDOGDELAY_INVALID = YAPI.INVALID_UINT;
    this.CALLBACKURL_INVALID = YAPI.INVALID_STRING;
    this.CALLBACKMETHOD_POST = 0;
    this.CALLBACKMETHOD_GET = 1;
    this.CALLBACKMETHOD_PUT = 2;
    this.CALLBACKMETHOD_INVALID = -1;
    this.CALLBACKENCODING_FORM = 0;
    this.CALLBACKENCODING_JSON = 1;
    this.CALLBACKENCODING_JSON_ARRAY = 2;
    this.CALLBACKENCODING_CSV = 3;
    this.CALLBACKENCODING_YOCTO_API = 4;
    this.CALLBACKENCODING_JSON_NUM = 5;
    this.CALLBACKENCODING_EMONCMS = 6;
    this.CALLBACKENCODING_AZURE = 7;
    this.CALLBACKENCODING_INFLUXDB = 8;
    this.CALLBACKENCODING_MQTT = 9;
    this.CALLBACKENCODING_YOCTO_API_JZON = 10;
    this.CALLBACKENCODING_PRTG = 11;
    this.CALLBACKENCODING_INFLUXDB_V2 = 12;
    this.CALLBACKENCODING_INVALID = -1;
    this.CALLBACKTEMPLATE_OFF = 0;
    this.CALLBACKTEMPLATE_ON = 1;
    this.CALLBACKTEMPLATE_INVALID = -1;
    this.CALLBACKCREDENTIALS_INVALID = YAPI.INVALID_STRING;
    this.CALLBACKINITIALDELAY_INVALID = YAPI.INVALID_UINT;
    this.CALLBACKSCHEDULE_INVALID = YAPI.INVALID_STRING;
    this.CALLBACKMINDELAY_INVALID = YAPI.INVALID_UINT;
    this.CALLBACKMAXDELAY_INVALID = YAPI.INVALID_UINT;
    this.POECURRENT_INVALID = YAPI.INVALID_UINT;
    this._className = "Network";
  }
  //--- (YNetwork implementation)
  imm_parseAttr(name, val) {
    switch (name) {
      case "readiness":
        this._readiness = val;
        return 1;
      case "macAddress":
        this._macAddress = val;
        return 1;
      case "ipAddress":
        this._ipAddress = val;
        return 1;
      case "subnetMask":
        this._subnetMask = val;
        return 1;
      case "router":
        this._router = val;
        return 1;
      case "currentDNS":
        this._currentDNS = val;
        return 1;
      case "ipConfig":
        this._ipConfig = val;
        return 1;
      case "primaryDNS":
        this._primaryDNS = val;
        return 1;
      case "secondaryDNS":
        this._secondaryDNS = val;
        return 1;
      case "ntpServer":
        this._ntpServer = val;
        return 1;
      case "userPassword":
        this._userPassword = val;
        return 1;
      case "adminPassword":
        this._adminPassword = val;
        return 1;
      case "httpPort":
        this._httpPort = val;
        return 1;
      case "httpsPort":
        this._httpsPort = val;
        return 1;
      case "securityMode":
        this._securityMode = val;
        return 1;
      case "defaultPage":
        this._defaultPage = val;
        return 1;
      case "discoverable":
        this._discoverable = val;
        return 1;
      case "wwwWatchdogDelay":
        this._wwwWatchdogDelay = val;
        return 1;
      case "callbackUrl":
        this._callbackUrl = val;
        return 1;
      case "callbackMethod":
        this._callbackMethod = val;
        return 1;
      case "callbackEncoding":
        this._callbackEncoding = val;
        return 1;
      case "callbackTemplate":
        this._callbackTemplate = val;
        return 1;
      case "callbackCredentials":
        this._callbackCredentials = val;
        return 1;
      case "callbackInitialDelay":
        this._callbackInitialDelay = val;
        return 1;
      case "callbackSchedule":
        this._callbackSchedule = val;
        return 1;
      case "callbackMinDelay":
        this._callbackMinDelay = val;
        return 1;
      case "callbackMaxDelay":
        this._callbackMaxDelay = val;
        return 1;
      case "poeCurrent":
        this._poeCurrent = val;
        return 1;
    }
    return super.imm_parseAttr(name, val);
  }
  /**
   * Returns the current established working mode of the network interface.
   * Level zero (DOWN_0) means that no hardware link has been detected. Either there is no signal
   * on the network cable, or the selected wireless access point cannot be detected.
   * Level 1 (LIVE_1) is reached when the network is detected, but is not yet connected.
   * For a wireless network, this shows that the requested SSID is present.
   * Level 2 (LINK_2) is reached when the hardware connection is established.
   * For a wired network connection, level 2 means that the cable is attached at both ends.
   * For a connection to a wireless access point, it shows that the security parameters
   * are properly configured. For an ad-hoc wireless connection, it means that there is
   * at least one other device connected on the ad-hoc network.
   * Level 3 (DHCP_3) is reached when an IP address has been obtained using DHCP.
   * Level 4 (DNS_4) is reached when the DNS server is reachable on the network.
   * Level 5 (WWW_5) is reached when global connectivity is demonstrated by properly loading the
   * current time from an NTP server.
   *
   * @return a value among YNetwork.READINESS_DOWN, YNetwork.READINESS_EXISTS,
   * YNetwork.READINESS_LINKED, YNetwork.READINESS_LAN_OK and YNetwork.READINESS_WWW_OK corresponding to
   * the current established working mode of the network interface
   *
   * On failure, throws an exception or returns YNetwork.READINESS_INVALID.
   */
  async get_readiness() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.READINESS_INVALID;
      }
    }
    res = this._readiness;
    return res;
  }
  /**
   * Returns the MAC address of the network interface. The MAC address is also available on a sticker
   * on the module, in both numeric and barcode forms.
   *
   * @return a string corresponding to the MAC address of the network interface
   *
   * On failure, throws an exception or returns YNetwork.MACADDRESS_INVALID.
   */
  async get_macAddress() {
    let res;
    if (this._cacheExpiration == 0) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.MACADDRESS_INVALID;
      }
    }
    res = this._macAddress;
    return res;
  }
  /**
   * Returns the IP address currently in use by the device. The address may have been configured
   * statically, or provided by a DHCP server.
   *
   * @return a string corresponding to the IP address currently in use by the device
   *
   * On failure, throws an exception or returns YNetwork.IPADDRESS_INVALID.
   */
  async get_ipAddress() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.IPADDRESS_INVALID;
      }
    }
    res = this._ipAddress;
    return res;
  }
  /**
   * Returns the subnet mask currently used by the device.
   *
   * @return a string corresponding to the subnet mask currently used by the device
   *
   * On failure, throws an exception or returns YNetwork.SUBNETMASK_INVALID.
   */
  async get_subnetMask() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.SUBNETMASK_INVALID;
      }
    }
    res = this._subnetMask;
    return res;
  }
  /**
   * Returns the IP address of the router on the device subnet (default gateway).
   *
   * @return a string corresponding to the IP address of the router on the device subnet (default gateway)
   *
   * On failure, throws an exception or returns YNetwork.ROUTER_INVALID.
   */
  async get_router() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.ROUTER_INVALID;
      }
    }
    res = this._router;
    return res;
  }
  /**
   * Returns the IP address of the DNS server currently used by the device.
   *
   * @return a string corresponding to the IP address of the DNS server currently used by the device
   *
   * On failure, throws an exception or returns YNetwork.CURRENTDNS_INVALID.
   */
  async get_currentDNS() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.CURRENTDNS_INVALID;
      }
    }
    res = this._currentDNS;
    return res;
  }
  /**
   * Returns the IP configuration of the network interface.
   *
   * If the network interface is set up to use a static IP address, the string starts with "STATIC:" and
   * is followed by three
   * parameters, separated by "/". The first is the device IP address, followed by the subnet mask
   * length, and finally the
   * router IP address (default gateway). For instance: "STATIC:192.168.1.14/16/192.168.1.1"
   *
   * If the network interface is configured to receive its IP from a DHCP server, the string start with
   * "DHCP:" and is followed by
   * three parameters separated by "/". The first is the fallback IP address, then the fallback subnet
   * mask length and finally the
   * fallback router IP address. These three parameters are used when no DHCP reply is received.
   *
   * @return a string corresponding to the IP configuration of the network interface
   *
   * On failure, throws an exception or returns YNetwork.IPCONFIG_INVALID.
   */
  async get_ipConfig() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.IPCONFIG_INVALID;
      }
    }
    res = this._ipConfig;
    return res;
  }
  async set_ipConfig(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("ipConfig", rest_val);
  }
  /**
   * Returns the IP address of the primary name server to be used by the module.
   *
   * @return a string corresponding to the IP address of the primary name server to be used by the module
   *
   * On failure, throws an exception or returns YNetwork.PRIMARYDNS_INVALID.
   */
  async get_primaryDNS() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.PRIMARYDNS_INVALID;
      }
    }
    res = this._primaryDNS;
    return res;
  }
  /**
   * Changes the IP address of the primary name server to be used by the module.
   * When using DHCP, if a value is specified, it overrides the value received from the DHCP server.
   * Remember to call the saveToFlash() method and then to reboot the module to apply this setting.
   *
   * @param newval : a string corresponding to the IP address of the primary name server to be used by the module
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_primaryDNS(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("primaryDNS", rest_val);
  }
  /**
   * Returns the IP address of the secondary name server to be used by the module.
   *
   * @return a string corresponding to the IP address of the secondary name server to be used by the module
   *
   * On failure, throws an exception or returns YNetwork.SECONDARYDNS_INVALID.
   */
  async get_secondaryDNS() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.SECONDARYDNS_INVALID;
      }
    }
    res = this._secondaryDNS;
    return res;
  }
  /**
   * Changes the IP address of the secondary name server to be used by the module.
   * When using DHCP, if a value is specified, it overrides the value received from the DHCP server.
   * Remember to call the saveToFlash() method and then to reboot the module to apply this setting.
   *
   * @param newval : a string corresponding to the IP address of the secondary name server to be used by the module
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_secondaryDNS(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("secondaryDNS", rest_val);
  }
  /**
   * Returns the IP address of the NTP server to be used by the device.
   *
   * @return a string corresponding to the IP address of the NTP server to be used by the device
   *
   * On failure, throws an exception or returns YNetwork.NTPSERVER_INVALID.
   */
  async get_ntpServer() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.NTPSERVER_INVALID;
      }
    }
    res = this._ntpServer;
    return res;
  }
  /**
   * Changes the IP address of the NTP server to be used by the module. Use an empty
   * string to restore the factory set  address.
   * Remember to call the saveToFlash() method and then to reboot the module to apply this setting.
   *
   * @param newval : a string corresponding to the IP address of the NTP server to be used by the module
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_ntpServer(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("ntpServer", rest_val);
  }
  /**
   * Returns a hash string if a password has been set for "user" user,
   * or an empty string otherwise.
   *
   * @return a string corresponding to a hash string if a password has been set for "user" user,
   *         or an empty string otherwise
   *
   * On failure, throws an exception or returns YNetwork.USERPASSWORD_INVALID.
   */
  async get_userPassword() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.USERPASSWORD_INVALID;
      }
    }
    res = this._userPassword;
    return res;
  }
  /**
   * Changes the password for the "user" user. This password becomes instantly required
   * to perform any use of the module. If the specified value is an
   * empty string, a password is not required anymore.
   * Remember to call the saveToFlash() method of the module if the
   * modification must be kept.
   *
   * @param newval : a string corresponding to the password for the "user" user
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_userPassword(newval) {
    let rest_val;
    if (!this._is_valid_pass(newval)) {
      return YAPI.INVALID_ARGUMENT;
    }
    rest_val = String(newval);
    return await this._setAttr("userPassword", rest_val);
  }
  /**
   * Returns a hash string if a password has been set for user "admin",
   * or an empty string otherwise.
   *
   * @return a string corresponding to a hash string if a password has been set for user "admin",
   *         or an empty string otherwise
   *
   * On failure, throws an exception or returns YNetwork.ADMINPASSWORD_INVALID.
   */
  async get_adminPassword() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.ADMINPASSWORD_INVALID;
      }
    }
    res = this._adminPassword;
    return res;
  }
  /**
   * Changes the password for the "admin" user. This password becomes instantly required
   * to perform any change of the module state. If the specified value is an
   * empty string, a password is not required anymore.
   * Remember to call the saveToFlash() method of the module if the
   * modification must be kept.
   *
   * @param newval : a string corresponding to the password for the "admin" user
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_adminPassword(newval) {
    let rest_val;
    if (!this._is_valid_pass(newval)) {
      return YAPI.INVALID_ARGUMENT;
    }
    rest_val = String(newval);
    return await this._setAttr("adminPassword", rest_val);
  }
  /**
   * Returns the TCP port used to serve the hub web UI.
   *
   * @return an integer corresponding to the TCP port used to serve the hub web UI
   *
   * On failure, throws an exception or returns YNetwork.HTTPPORT_INVALID.
   */
  async get_httpPort() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.HTTPPORT_INVALID;
      }
    }
    res = this._httpPort;
    return res;
  }
  /**
   * Changes the the TCP port used to serve the hub web UI. The default value is port 80,
   * which is the default for all Web servers. Regardless of the value set here,
   * the hub will always reply on port 4444, which is used by default by Yoctopuce
   * API library. When you change this parameter, remember to call the saveToFlash()
   * method of the module if the modification must be kept.
   *
   * @param newval : an integer corresponding to the the TCP port used to serve the hub web UI
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_httpPort(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("httpPort", rest_val);
  }
  /**
   * Returns the secure TCP port used to serve the hub web UI.
   *
   * @return an integer corresponding to the secure TCP port used to serve the hub web UI
   *
   * On failure, throws an exception or returns YNetwork.HTTPSPORT_INVALID.
   */
  async get_httpsPort() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.HTTPSPORT_INVALID;
      }
    }
    res = this._httpsPort;
    return res;
  }
  /**
   * Changes the secure TCP port used to serve the hub web UI. The default value is port 4443,
   * which is the default for all Web servers. When you change this parameter, remember to call the saveToFlash()
   * method of the module if the modification must be kept.
   *
   * @param newval : an integer corresponding to the secure TCP port used to serve the hub web UI
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_httpsPort(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("httpsPort", rest_val);
  }
  /**
   * Returns the security level chosen to prevent unauthorized access to the server.
   *
   * @return a value among YNetwork.SECURITYMODE_UNDEFINED, YNetwork.SECURITYMODE_LEGACY,
   * YNetwork.SECURITYMODE_MIXED and YNetwork.SECURITYMODE_SECURE corresponding to the security level
   * chosen to prevent unauthorized access to the server
   *
   * On failure, throws an exception or returns YNetwork.SECURITYMODE_INVALID.
   */
  async get_securityMode() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.SECURITYMODE_INVALID;
      }
    }
    res = this._securityMode;
    return res;
  }
  /**
   * Changes the security level used to prevent unauthorized access to the server.
   * The value UNDEFINED causes the security configuration wizard to be
   * displayed the next time you log on to the Web console.
   * The value LEGACY offers unencrypted HTTP access by default, and
   * is designed to provide compatibility with legacy applications that do not
   * handle password or do not support HTTPS. But it should
   * only be used when system security is guaranteed by other means, such as the
   * use of a firewall.
   * The value MIXED requires the configuration of passwords, and allows
   * access via both HTTP (unencrypted) and HTTPS (encrypted), while requiring
   * the Yoctopuce API to be tolerant of certificate characteristics.
   * The value SECURE requires the configuration of passwords and the
   * use of secure communications in all cases.
   * When you change this parameter, remember to call the saveToFlash()
   * method of the module if the modification must be kept.
   *
   * @param newval : a value among YNetwork.SECURITYMODE_UNDEFINED, YNetwork.SECURITYMODE_LEGACY,
   * YNetwork.SECURITYMODE_MIXED and YNetwork.SECURITYMODE_SECURE corresponding to the security level
   * used to prevent unauthorized access to the server
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_securityMode(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("securityMode", rest_val);
  }
  /**
   * Returns the HTML page to serve for the URL "/"" of the hub.
   *
   * @return a string corresponding to the HTML page to serve for the URL "/"" of the hub
   *
   * On failure, throws an exception or returns YNetwork.DEFAULTPAGE_INVALID.
   */
  async get_defaultPage() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.DEFAULTPAGE_INVALID;
      }
    }
    res = this._defaultPage;
    return res;
  }
  /**
   * Changes the default HTML page returned by the hub. If not value are set the hub return
   * "index.html" which is the web interface of the hub. It is possible to change this page
   * for file that has been uploaded on the hub. The maximum filename size is 15 characters.
   * When you change this parameter, remember to call the saveToFlash()
   * method of the module if the modification must be kept.
   *
   * @param newval : a string corresponding to the default HTML page returned by the hub
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_defaultPage(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("defaultPage", rest_val);
  }
  /**
   * Returns the activation state of the multicast announce protocols to allow easy
   * discovery of the module in the network neighborhood (uPnP/Bonjour protocol).
   *
   * @return either YNetwork.DISCOVERABLE_FALSE or YNetwork.DISCOVERABLE_TRUE, according to the
   * activation state of the multicast announce protocols to allow easy
   *         discovery of the module in the network neighborhood (uPnP/Bonjour protocol)
   *
   * On failure, throws an exception or returns YNetwork.DISCOVERABLE_INVALID.
   */
  async get_discoverable() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.DISCOVERABLE_INVALID;
      }
    }
    res = this._discoverable;
    return res;
  }
  /**
   * Changes the activation state of the multicast announce protocols to allow easy
   * discovery of the module in the network neighborhood (uPnP/Bonjour protocol).
   * Remember to call the saveToFlash()
   * method of the module if the modification must be kept.
   *
   * @param newval : either YNetwork.DISCOVERABLE_FALSE or YNetwork.DISCOVERABLE_TRUE, according to the
   * activation state of the multicast announce protocols to allow easy
   *         discovery of the module in the network neighborhood (uPnP/Bonjour protocol)
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_discoverable(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("discoverable", rest_val);
  }
  /**
   * Returns the allowed downtime of the WWW link (in seconds) before triggering an automated
   * reboot to try to recover Internet connectivity. A zero value disables automated reboot
   * in case of Internet connectivity loss.
   *
   * @return an integer corresponding to the allowed downtime of the WWW link (in seconds) before
   * triggering an automated
   *         reboot to try to recover Internet connectivity
   *
   * On failure, throws an exception or returns YNetwork.WWWWATCHDOGDELAY_INVALID.
   */
  async get_wwwWatchdogDelay() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.WWWWATCHDOGDELAY_INVALID;
      }
    }
    res = this._wwwWatchdogDelay;
    return res;
  }
  /**
   * Changes the allowed downtime of the WWW link (in seconds) before triggering an automated
   * reboot to try to recover Internet connectivity. A zero value disables automated reboot
   * in case of Internet connectivity loss. The smallest valid non-zero timeout is
   * 90 seconds. Remember to call the saveToFlash()
   * method of the module if the modification must be kept.
   *
   * @param newval : an integer corresponding to the allowed downtime of the WWW link (in seconds)
   * before triggering an automated
   *         reboot to try to recover Internet connectivity
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_wwwWatchdogDelay(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("wwwWatchdogDelay", rest_val);
  }
  /**
   * Returns the callback URL to notify of significant state changes.
   *
   * @return a string corresponding to the callback URL to notify of significant state changes
   *
   * On failure, throws an exception or returns YNetwork.CALLBACKURL_INVALID.
   */
  async get_callbackUrl() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.CALLBACKURL_INVALID;
      }
    }
    res = this._callbackUrl;
    return res;
  }
  /**
   * Changes the callback URL to notify significant state changes. Remember to call the
   * saveToFlash() method of the module if the modification must be kept.
   *
   * @param newval : a string corresponding to the callback URL to notify significant state changes
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_callbackUrl(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("callbackUrl", rest_val);
  }
  /**
   * Returns the HTTP method used to notify callbacks for significant state changes.
   *
   * @return a value among YNetwork.CALLBACKMETHOD_POST, YNetwork.CALLBACKMETHOD_GET and
   * YNetwork.CALLBACKMETHOD_PUT corresponding to the HTTP method used to notify callbacks for
   * significant state changes
   *
   * On failure, throws an exception or returns YNetwork.CALLBACKMETHOD_INVALID.
   */
  async get_callbackMethod() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.CALLBACKMETHOD_INVALID;
      }
    }
    res = this._callbackMethod;
    return res;
  }
  /**
   * Changes the HTTP method used to notify callbacks for significant state changes.
   * Remember to call the saveToFlash() method of the module if the
   * modification must be kept.
   *
   * @param newval : a value among YNetwork.CALLBACKMETHOD_POST, YNetwork.CALLBACKMETHOD_GET and
   * YNetwork.CALLBACKMETHOD_PUT corresponding to the HTTP method used to notify callbacks for
   * significant state changes
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_callbackMethod(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("callbackMethod", rest_val);
  }
  /**
   * Returns the encoding standard to use for representing notification values.
   *
   * @return a value among YNetwork.CALLBACKENCODING_FORM, YNetwork.CALLBACKENCODING_JSON,
   * YNetwork.CALLBACKENCODING_JSON_ARRAY, YNetwork.CALLBACKENCODING_CSV,
   * YNetwork.CALLBACKENCODING_YOCTO_API, YNetwork.CALLBACKENCODING_JSON_NUM,
   * YNetwork.CALLBACKENCODING_EMONCMS, YNetwork.CALLBACKENCODING_AZURE,
   * YNetwork.CALLBACKENCODING_INFLUXDB, YNetwork.CALLBACKENCODING_MQTT,
   * YNetwork.CALLBACKENCODING_YOCTO_API_JZON, YNetwork.CALLBACKENCODING_PRTG and
   * YNetwork.CALLBACKENCODING_INFLUXDB_V2 corresponding to the encoding standard to use for
   * representing notification values
   *
   * On failure, throws an exception or returns YNetwork.CALLBACKENCODING_INVALID.
   */
  async get_callbackEncoding() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.CALLBACKENCODING_INVALID;
      }
    }
    res = this._callbackEncoding;
    return res;
  }
  /**
   * Changes the encoding standard to use for representing notification values.
   * Remember to call the saveToFlash() method of the module if the
   * modification must be kept.
   *
   * @param newval : a value among YNetwork.CALLBACKENCODING_FORM, YNetwork.CALLBACKENCODING_JSON,
   * YNetwork.CALLBACKENCODING_JSON_ARRAY, YNetwork.CALLBACKENCODING_CSV,
   * YNetwork.CALLBACKENCODING_YOCTO_API, YNetwork.CALLBACKENCODING_JSON_NUM,
   * YNetwork.CALLBACKENCODING_EMONCMS, YNetwork.CALLBACKENCODING_AZURE,
   * YNetwork.CALLBACKENCODING_INFLUXDB, YNetwork.CALLBACKENCODING_MQTT,
   * YNetwork.CALLBACKENCODING_YOCTO_API_JZON, YNetwork.CALLBACKENCODING_PRTG and
   * YNetwork.CALLBACKENCODING_INFLUXDB_V2 corresponding to the encoding standard to use for
   * representing notification values
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_callbackEncoding(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("callbackEncoding", rest_val);
  }
  /**
   * Returns the activation state of the custom template file to customize callback
   * format. If the custom callback template is disabled, it will be ignored even
   * if present on the YoctoHub.
   *
   * @return either YNetwork.CALLBACKTEMPLATE_OFF or YNetwork.CALLBACKTEMPLATE_ON, according to the
   * activation state of the custom template file to customize callback
   *         format
   *
   * On failure, throws an exception or returns YNetwork.CALLBACKTEMPLATE_INVALID.
   */
  async get_callbackTemplate() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.CALLBACKTEMPLATE_INVALID;
      }
    }
    res = this._callbackTemplate;
    return res;
  }
  /**
   * Enable the use of a template file to customize callbacks format.
   * When the custom callback template file is enabled, the template file
   * will be loaded for each callback in order to build the data to post to the
   * server. If template file does not exist on the YoctoHub, the callback will
   * fail with an error message indicating the name of the expected template file.
   * Remember to call the saveToFlash() method of the module if the
   * modification must be kept.
   *
   * @param newval : either YNetwork.CALLBACKTEMPLATE_OFF or YNetwork.CALLBACKTEMPLATE_ON
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_callbackTemplate(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("callbackTemplate", rest_val);
  }
  /**
   * Returns a hashed version of the notification callback credentials if set,
   * or an empty string otherwise.
   *
   * @return a string corresponding to a hashed version of the notification callback credentials if set,
   *         or an empty string otherwise
   *
   * On failure, throws an exception or returns YNetwork.CALLBACKCREDENTIALS_INVALID.
   */
  async get_callbackCredentials() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.CALLBACKCREDENTIALS_INVALID;
      }
    }
    res = this._callbackCredentials;
    return res;
  }
  /**
   * Changes the credentials required to connect to the callback address. The credentials
   * must be provided as returned by function get_callbackCredentials,
   * in the form username:hash. The method used to compute the hash varies according
   * to the the authentication scheme implemented by the callback, For Basic authentication,
   * the hash is the MD5 of the string username:password. For Digest authentication,
   * the hash is the MD5 of the string username:realm:password. For a simpler
   * way to configure callback credentials, use function callbackLogin instead.
   * Remember to call the saveToFlash() method of the module if the
   * modification must be kept.
   *
   * @param newval : a string corresponding to the credentials required to connect to the callback address
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_callbackCredentials(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("callbackCredentials", rest_val);
  }
  /**
   * Connects to the notification callback and saves the credentials required to
   * log into it. The password is not stored into the module, only a hashed
   * copy of the credentials are saved. Remember to call the
   * saveToFlash() method of the module if the modification must be kept.
   *
   * @param username : username required to log to the callback
   * @param password : password required to log to the callback
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async callbackLogin(username, password) {
    let rest_val;
    rest_val = username + ":" + password;
    return await this._setAttr("callbackCredentials", rest_val);
  }
  /**
   * Returns the initial waiting time before first callback notifications, in seconds.
   *
   * @return an integer corresponding to the initial waiting time before first callback notifications, in seconds
   *
   * On failure, throws an exception or returns YNetwork.CALLBACKINITIALDELAY_INVALID.
   */
  async get_callbackInitialDelay() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.CALLBACKINITIALDELAY_INVALID;
      }
    }
    res = this._callbackInitialDelay;
    return res;
  }
  /**
   * Changes the initial waiting time before first callback notifications, in seconds.
   * Remember to call the saveToFlash() method of the module if the modification must be kept.
   *
   * @param newval : an integer corresponding to the initial waiting time before first callback
   * notifications, in seconds
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_callbackInitialDelay(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("callbackInitialDelay", rest_val);
  }
  /**
   * Returns the HTTP callback schedule strategy, as a text string.
   *
   * @return a string corresponding to the HTTP callback schedule strategy, as a text string
   *
   * On failure, throws an exception or returns YNetwork.CALLBACKSCHEDULE_INVALID.
   */
  async get_callbackSchedule() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.CALLBACKSCHEDULE_INVALID;
      }
    }
    res = this._callbackSchedule;
    return res;
  }
  /**
   * Changes the HTTP callback schedule strategy, as a text string.
   * Remember to call the saveToFlash()
   * method of the module if the modification must be kept.
   *
   * @param newval : a string corresponding to the HTTP callback schedule strategy, as a text string
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_callbackSchedule(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("callbackSchedule", rest_val);
  }
  /**
   * Returns the minimum waiting time between two HTTP callbacks, in seconds.
   *
   * @return an integer corresponding to the minimum waiting time between two HTTP callbacks, in seconds
   *
   * On failure, throws an exception or returns YNetwork.CALLBACKMINDELAY_INVALID.
   */
  async get_callbackMinDelay() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.CALLBACKMINDELAY_INVALID;
      }
    }
    res = this._callbackMinDelay;
    return res;
  }
  /**
   * Changes the minimum waiting time between two HTTP callbacks, in seconds.
   * Remember to call the saveToFlash() method of the module if the modification must be kept.
   *
   * @param newval : an integer corresponding to the minimum waiting time between two HTTP callbacks, in seconds
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_callbackMinDelay(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("callbackMinDelay", rest_val);
  }
  /**
   * Returns the waiting time between two HTTP callbacks when there is nothing new.
   *
   * @return an integer corresponding to the waiting time between two HTTP callbacks when there is nothing new
   *
   * On failure, throws an exception or returns YNetwork.CALLBACKMAXDELAY_INVALID.
   */
  async get_callbackMaxDelay() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.CALLBACKMAXDELAY_INVALID;
      }
    }
    res = this._callbackMaxDelay;
    return res;
  }
  /**
   * Changes the waiting time between two HTTP callbacks when there is nothing new.
   * Remember to call the saveToFlash() method of the module if the modification must be kept.
   *
   * @param newval : an integer corresponding to the waiting time between two HTTP callbacks when there
   * is nothing new
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_callbackMaxDelay(newval) {
    let rest_val;
    rest_val = String(newval);
    return await this._setAttr("callbackMaxDelay", rest_val);
  }
  /**
   * Returns the current consumed by the module from Power-over-Ethernet (PoE), in milliamps.
   * The current consumption is measured after converting PoE source to 5 Volt, and should
   * never exceed 1800 mA.
   *
   * @return an integer corresponding to the current consumed by the module from Power-over-Ethernet
   * (PoE), in milliamps
   *
   * On failure, throws an exception or returns YNetwork.POECURRENT_INVALID.
   */
  async get_poeCurrent() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YNetwork.POECURRENT_INVALID;
      }
    }
    res = this._poeCurrent;
    return res;
  }
  /**
   * Retrieves a network interface for a given identifier.
   * The identifier can be specified using several formats:
   *
   * - FunctionLogicalName
   * - ModuleSerialNumber.FunctionIdentifier
   * - ModuleSerialNumber.FunctionLogicalName
   * - ModuleLogicalName.FunctionIdentifier
   * - ModuleLogicalName.FunctionLogicalName
   *
   *
   * This function does not require that the network interface is online at the time
   * it is invoked. The returned object is nevertheless valid.
   * Use the method YNetwork.isOnline() to test if the network interface is
   * indeed online at a given time. In case of ambiguity when looking for
   * a network interface by logical name, no error is notified: the first instance
   * found is returned. The search is performed first by hardware name,
   * then by logical name.
   *
   * If a call to this object's is_online() method returns FALSE although
   * you are certain that the matching device is plugged, make sure that you did
   * call registerHub() at application initialization time.
   *
   * @param func : a string that uniquely characterizes the network interface, for instance
   *         YHUBETH1.network.
   *
   * @return a YNetwork object allowing you to drive the network interface.
   */
  static FindNetwork(func) {
    let obj;
    obj = YFunction._FindFromCache("Network", func);
    if (obj == null) {
      obj = new _YNetwork(YAPI, func);
      YFunction._AddToCache("Network", func, obj);
    }
    return obj;
  }
  /**
   * Retrieves a network interface for a given identifier in a YAPI context.
   * The identifier can be specified using several formats:
   *
   * - FunctionLogicalName
   * - ModuleSerialNumber.FunctionIdentifier
   * - ModuleSerialNumber.FunctionLogicalName
   * - ModuleLogicalName.FunctionIdentifier
   * - ModuleLogicalName.FunctionLogicalName
   *
   *
   * This function does not require that the network interface is online at the time
   * it is invoked. The returned object is nevertheless valid.
   * Use the method YNetwork.isOnline() to test if the network interface is
   * indeed online at a given time. In case of ambiguity when looking for
   * a network interface by logical name, no error is notified: the first instance
   * found is returned. The search is performed first by hardware name,
   * then by logical name.
   *
   * @param yctx : a YAPI context
   * @param func : a string that uniquely characterizes the network interface, for instance
   *         YHUBETH1.network.
   *
   * @return a YNetwork object allowing you to drive the network interface.
   */
  static FindNetworkInContext(yctx, func) {
    let obj;
    obj = YFunction._FindFromCacheInContext(yctx, "Network", func);
    if (obj == null) {
      obj = new _YNetwork(yctx, func);
      YFunction._AddToCache("Network", func, obj);
    }
    return obj;
  }
  /**
   * Registers the callback function that is invoked on every change of advertised value.
   * The callback is called once when it is registered, passing the current advertised value
   * of the function, provided that it is not an empty string.
   * The callback is then invoked only during the execution of ySleep or yHandleEvents.
   * This provides control over the time when the callback is triggered. For good responsiveness, remember to call
   * one of these two functions periodically. To unregister a callback, pass a null pointer as argument.
   *
   * @param callback : the callback function to call, or a null pointer. The callback function should take two
   *         arguments: the function object of which the value has changed, and the character string describing
   *         the new advertised value.
   * @noreturn
   */
  async registerValueCallback(callback) {
    let val;
    if (callback != null) {
      await YFunction._UpdateValueCallbackList(this, true);
    } else {
      await YFunction._UpdateValueCallbackList(this, false);
    }
    this._valueCallbackNetwork = callback;
    if (callback != null && await this.isOnline()) {
      val = this._advertisedValue;
      if (!(val == "")) {
        await this._invokeValueCallback(val);
      }
    }
    return 0;
  }
  async _invokeValueCallback(value) {
    if (this._valueCallbackNetwork != null) {
      try {
        await this._valueCallbackNetwork(this, value);
      } catch (e) {
        this._yapi.imm_log("Exception in valueCallback:", e);
      }
    } else {
      await super._invokeValueCallback(value);
    }
    return 0;
  }
  /**
   * Changes the configuration of the network interface to enable the use of an
   * IP address received from a DHCP server. Until an address is received from a DHCP
   * server, the module uses the IP parameters specified to this function.
   * Remember to call the saveToFlash() method and then to reboot the module to apply this setting.
   *
   * @param fallbackIpAddr : fallback IP address, to be used when no DHCP reply is received
   * @param fallbackSubnetMaskLen : fallback subnet mask length when no DHCP reply is received, as an
   *         integer (e.g. 24 means 255.255.255.0)
   * @param fallbackRouter : fallback router IP address, to be used when no DHCP reply is received
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async useDHCP(fallbackIpAddr, fallbackSubnetMaskLen, fallbackRouter) {
    return await this.set_ipConfig("DHCP:" + fallbackIpAddr + "/" + String(Math.round(fallbackSubnetMaskLen)) + "/" + fallbackRouter);
  }
  /**
   * Changes the configuration of the network interface to enable the use of an
   * IP address received from a DHCP server. Until an address is received from a DHCP
   * server, the module uses an IP of the network 169.254.0.0/16 (APIPA).
   * Remember to call the saveToFlash() method and then to reboot the module to apply this setting.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async useDHCPauto() {
    return await this.set_ipConfig("DHCP:");
  }
  /**
   * Changes the configuration of the network interface to use a static IP address.
   * Remember to call the saveToFlash() method and then to reboot the module to apply this setting.
   *
   * @param ipAddress : device IP address
   * @param subnetMaskLen : subnet mask length, as an integer (e.g. 24 means 255.255.255.0)
   * @param router : router IP address (default gateway)
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async useStaticIP(ipAddress, subnetMaskLen, router) {
    return await this.set_ipConfig("STATIC:" + ipAddress + "/" + String(Math.round(subnetMaskLen)) + "/" + router);
  }
  /**
   * Pings host to test the network connectivity. Sends four ICMP ECHO_REQUEST requests from the
   * module to the target host. This method returns a string with the result of the
   * 4 ICMP ECHO_REQUEST requests.
   *
   * @param host : the hostname or the IP address of the target
   *
   * @return a string with the result of the ping.
   */
  async ping(host) {
    let content;
    content = await this._download("ping.txt?host=" + host);
    return this._yapi.imm_bin2str(content);
  }
  /**
   * Trigger an HTTP callback quickly. This function can even be called within
   * an HTTP callback, in which case the next callback will be triggered 5 seconds
   * after the end of the current callback, regardless if the minimum time between
   * callbacks configured in the device.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async triggerCallback() {
    return await this.set_callbackMethod(await this.get_callbackMethod());
  }
  /**
   * Set up periodic HTTP callbacks (simplified function).
   *
   * @param interval : a string representing the callback periodicity, expressed in
   *         seconds, minutes or hours, eg. "60s", "5m", "1h", "48h".
   * @param offset : an integer representing the time offset relative to the period
   *         when the callback should occur. For instance, if the periodicity is
   *         24h, an offset of 7 will make the callback occur each day at 7AM.
   *
   * @return YAPI.SUCCESS when the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async set_periodicCallbackSchedule(interval, offset) {
    return await this.set_callbackSchedule("every " + interval + "+" + String(Math.round(offset)));
  }
  /**
   * Continues the enumeration of network interfaces started using yFirstNetwork().
   * Caution: You can't make any assumption about the returned network interfaces order.
   * If you want to find a specific a network interface, use Network.findNetwork()
   * and a hardwareID or a logical name.
   *
   * @return a pointer to a YNetwork object, corresponding to
   *         a network interface currently online, or a null pointer
   *         if there are no more network interfaces to enumerate.
   */
  nextNetwork() {
    let resolve = this._yapi.imm_resolveFunction(this._className, this._func);
    if (resolve.errorType != YAPI.SUCCESS)
      return null;
    let next_hwid = this._yapi.imm_getNextHardwareId(this._className, resolve.result);
    if (next_hwid == null)
      return null;
    return _YNetwork.FindNetworkInContext(this._yapi, next_hwid);
  }
  /**
   * Starts the enumeration of network interfaces currently accessible.
   * Use the method YNetwork.nextNetwork() to iterate on
   * next network interfaces.
   *
   * @return a pointer to a YNetwork object, corresponding to
   *         the first network interface currently online, or a null pointer
   *         if there are none.
   */
  static FirstNetwork() {
    let next_hwid = YAPI.imm_getFirstHardwareId("Network");
    if (next_hwid == null)
      return null;
    return _YNetwork.FindNetwork(next_hwid);
  }
  /**
   * Starts the enumeration of network interfaces currently accessible.
   * Use the method YNetwork.nextNetwork() to iterate on
   * next network interfaces.
   *
   * @param yctx : a YAPI context.
   *
   * @return a pointer to a YNetwork object, corresponding to
   *         the first network interface currently online, or a null pointer
   *         if there are none.
   */
  static FirstNetworkInContext(yctx) {
    let next_hwid = yctx.imm_getFirstHardwareId("Network");
    if (next_hwid == null)
      return null;
    return _YNetwork.FindNetworkInContext(yctx, next_hwid);
  }
};
YNetwork.READINESS_DOWN = 0;
YNetwork.READINESS_EXISTS = 1;
YNetwork.READINESS_LINKED = 2;
YNetwork.READINESS_LAN_OK = 3;
YNetwork.READINESS_WWW_OK = 4;
YNetwork.READINESS_INVALID = -1;
YNetwork.MACADDRESS_INVALID = YAPI.INVALID_STRING;
YNetwork.IPADDRESS_INVALID = YAPI.INVALID_STRING;
YNetwork.SUBNETMASK_INVALID = YAPI.INVALID_STRING;
YNetwork.ROUTER_INVALID = YAPI.INVALID_STRING;
YNetwork.CURRENTDNS_INVALID = YAPI.INVALID_STRING;
YNetwork.IPCONFIG_INVALID = YAPI.INVALID_STRING;
YNetwork.PRIMARYDNS_INVALID = YAPI.INVALID_STRING;
YNetwork.SECONDARYDNS_INVALID = YAPI.INVALID_STRING;
YNetwork.NTPSERVER_INVALID = YAPI.INVALID_STRING;
YNetwork.USERPASSWORD_INVALID = YAPI.INVALID_STRING;
YNetwork.ADMINPASSWORD_INVALID = YAPI.INVALID_STRING;
YNetwork.HTTPPORT_INVALID = YAPI.INVALID_UINT;
YNetwork.HTTPSPORT_INVALID = YAPI.INVALID_UINT;
YNetwork.SECURITYMODE_UNDEFINED = 0;
YNetwork.SECURITYMODE_LEGACY = 1;
YNetwork.SECURITYMODE_MIXED = 2;
YNetwork.SECURITYMODE_SECURE = 3;
YNetwork.SECURITYMODE_INVALID = -1;
YNetwork.DEFAULTPAGE_INVALID = YAPI.INVALID_STRING;
YNetwork.DISCOVERABLE_FALSE = 0;
YNetwork.DISCOVERABLE_TRUE = 1;
YNetwork.DISCOVERABLE_INVALID = -1;
YNetwork.WWWWATCHDOGDELAY_INVALID = YAPI.INVALID_UINT;
YNetwork.CALLBACKURL_INVALID = YAPI.INVALID_STRING;
YNetwork.CALLBACKMETHOD_POST = 0;
YNetwork.CALLBACKMETHOD_GET = 1;
YNetwork.CALLBACKMETHOD_PUT = 2;
YNetwork.CALLBACKMETHOD_INVALID = -1;
YNetwork.CALLBACKENCODING_FORM = 0;
YNetwork.CALLBACKENCODING_JSON = 1;
YNetwork.CALLBACKENCODING_JSON_ARRAY = 2;
YNetwork.CALLBACKENCODING_CSV = 3;
YNetwork.CALLBACKENCODING_YOCTO_API = 4;
YNetwork.CALLBACKENCODING_JSON_NUM = 5;
YNetwork.CALLBACKENCODING_EMONCMS = 6;
YNetwork.CALLBACKENCODING_AZURE = 7;
YNetwork.CALLBACKENCODING_INFLUXDB = 8;
YNetwork.CALLBACKENCODING_MQTT = 9;
YNetwork.CALLBACKENCODING_YOCTO_API_JZON = 10;
YNetwork.CALLBACKENCODING_PRTG = 11;
YNetwork.CALLBACKENCODING_INFLUXDB_V2 = 12;
YNetwork.CALLBACKENCODING_INVALID = -1;
YNetwork.CALLBACKTEMPLATE_OFF = 0;
YNetwork.CALLBACKTEMPLATE_ON = 1;
YNetwork.CALLBACKTEMPLATE_INVALID = -1;
YNetwork.CALLBACKCREDENTIALS_INVALID = YAPI.INVALID_STRING;
YNetwork.CALLBACKINITIALDELAY_INVALID = YAPI.INVALID_UINT;
YNetwork.CALLBACKSCHEDULE_INVALID = YAPI.INVALID_STRING;
YNetwork.CALLBACKMINDELAY_INVALID = YAPI.INVALID_UINT;
YNetwork.CALLBACKMAXDELAY_INVALID = YAPI.INVALID_UINT;
YNetwork.POECURRENT_INVALID = YAPI.INVALID_UINT;

// obj/full/Api/yocto_files.js
var YFileRecord = class {
  // API symbols as static members
  //--- (end of generated code: YFileRecord attributes declaration)
  constructor(str_json) {
    this._name = "";
    this._size = 0;
    this._crc = 0;
    const loadval = JSON.parse(str_json);
    this._name = loadval.name;
    this._size = loadval.size;
    this._crc = loadval.crc;
  }
  //--- (generated code: YFileRecord implementation)
  /**
   * Returns the name of the file.
   *
   * @return a string with the name of the file.
   */
  get_name() {
    return this._name;
  }
  /**
   * Returns the size of the file in bytes.
   *
   * @return the size of the file.
   */
  get_size() {
    return this._size;
  }
  /**
   * Returns the 32-bit CRC of the file content.
   *
   * @return the 32-bit CRC of the file content.
   */
  get_crc() {
    return this._crc;
  }
};
var YFiles = class _YFiles extends YFunction {
  //--- (end of generated code: YFiles attributes declaration)
  constructor(yapi, func) {
    super(yapi, func);
    this._filesCount = _YFiles.FILESCOUNT_INVALID;
    this._freeSpace = _YFiles.FREESPACE_INVALID;
    this._valueCallbackFiles = null;
    this._ver = 0;
    this.FILESCOUNT_INVALID = YAPI.INVALID_UINT;
    this.FREESPACE_INVALID = YAPI.INVALID_UINT;
    this._className = "Files";
  }
  //--- (generated code: YFiles implementation)
  imm_parseAttr(name, val) {
    switch (name) {
      case "filesCount":
        this._filesCount = val;
        return 1;
      case "freeSpace":
        this._freeSpace = val;
        return 1;
    }
    return super.imm_parseAttr(name, val);
  }
  /**
   * Returns the number of files currently loaded in the filesystem.
   *
   * @return an integer corresponding to the number of files currently loaded in the filesystem
   *
   * On failure, throws an exception or returns YFiles.FILESCOUNT_INVALID.
   */
  async get_filesCount() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YFiles.FILESCOUNT_INVALID;
      }
    }
    res = this._filesCount;
    return res;
  }
  /**
   * Returns the free space for uploading new files to the filesystem, in bytes.
   *
   * @return an integer corresponding to the free space for uploading new files to the filesystem, in bytes
   *
   * On failure, throws an exception or returns YFiles.FREESPACE_INVALID.
   */
  async get_freeSpace() {
    let res;
    if (this._cacheExpiration <= this._yapi.GetTickCount()) {
      if (await this.load(this._yapi.defaultCacheValidity) != this._yapi.SUCCESS) {
        return _YFiles.FREESPACE_INVALID;
      }
    }
    res = this._freeSpace;
    return res;
  }
  /**
   * Retrieves a filesystem for a given identifier.
   * The identifier can be specified using several formats:
   *
   * - FunctionLogicalName
   * - ModuleSerialNumber.FunctionIdentifier
   * - ModuleSerialNumber.FunctionLogicalName
   * - ModuleLogicalName.FunctionIdentifier
   * - ModuleLogicalName.FunctionLogicalName
   *
   *
   * This function does not require that the filesystem is online at the time
   * it is invoked. The returned object is nevertheless valid.
   * Use the method YFiles.isOnline() to test if the filesystem is
   * indeed online at a given time. In case of ambiguity when looking for
   * a filesystem by logical name, no error is notified: the first instance
   * found is returned. The search is performed first by hardware name,
   * then by logical name.
   *
   * If a call to this object's is_online() method returns FALSE although
   * you are certain that the matching device is plugged, make sure that you did
   * call registerHub() at application initialization time.
   *
   * @param func : a string that uniquely characterizes the filesystem, for instance
   *         YRGBLED2.files.
   *
   * @return a YFiles object allowing you to drive the filesystem.
   */
  static FindFiles(func) {
    let obj;
    obj = YFunction._FindFromCache("Files", func);
    if (obj == null) {
      obj = new _YFiles(YAPI, func);
      YFunction._AddToCache("Files", func, obj);
    }
    return obj;
  }
  /**
   * Retrieves a filesystem for a given identifier in a YAPI context.
   * The identifier can be specified using several formats:
   *
   * - FunctionLogicalName
   * - ModuleSerialNumber.FunctionIdentifier
   * - ModuleSerialNumber.FunctionLogicalName
   * - ModuleLogicalName.FunctionIdentifier
   * - ModuleLogicalName.FunctionLogicalName
   *
   *
   * This function does not require that the filesystem is online at the time
   * it is invoked. The returned object is nevertheless valid.
   * Use the method YFiles.isOnline() to test if the filesystem is
   * indeed online at a given time. In case of ambiguity when looking for
   * a filesystem by logical name, no error is notified: the first instance
   * found is returned. The search is performed first by hardware name,
   * then by logical name.
   *
   * @param yctx : a YAPI context
   * @param func : a string that uniquely characterizes the filesystem, for instance
   *         YRGBLED2.files.
   *
   * @return a YFiles object allowing you to drive the filesystem.
   */
  static FindFilesInContext(yctx, func) {
    let obj;
    obj = YFunction._FindFromCacheInContext(yctx, "Files", func);
    if (obj == null) {
      obj = new _YFiles(yctx, func);
      YFunction._AddToCache("Files", func, obj);
    }
    return obj;
  }
  /**
   * Registers the callback function that is invoked on every change of advertised value.
   * The callback is called once when it is registered, passing the current advertised value
   * of the function, provided that it is not an empty string.
   * The callback is then invoked only during the execution of ySleep or yHandleEvents.
   * This provides control over the time when the callback is triggered. For good responsiveness, remember to call
   * one of these two functions periodically. To unregister a callback, pass a null pointer as argument.
   *
   * @param callback : the callback function to call, or a null pointer. The callback function should take two
   *         arguments: the function object of which the value has changed, and the character string describing
   *         the new advertised value.
   * @noreturn
   */
  async registerValueCallback(callback) {
    let val;
    if (callback != null) {
      await YFunction._UpdateValueCallbackList(this, true);
    } else {
      await YFunction._UpdateValueCallbackList(this, false);
    }
    this._valueCallbackFiles = callback;
    if (callback != null && await this.isOnline()) {
      val = this._advertisedValue;
      if (!(val == "")) {
        await this._invokeValueCallback(val);
      }
    }
    return 0;
  }
  async _invokeValueCallback(value) {
    if (this._valueCallbackFiles != null) {
      try {
        await this._valueCallbackFiles(this, value);
      } catch (e) {
        this._yapi.imm_log("Exception in valueCallback:", e);
      }
    } else {
      await super._invokeValueCallback(value);
    }
    return 0;
  }
  async sendCommand(command) {
    let url;
    url = "files.json?a=" + command;
    return await this._download(url);
  }
  async _getVersion() {
    let json;
    if (this._ver > 0) {
      return this._ver;
    }
    json = await this.sendCommand("info");
    if (json[0] != 123) {
      this._ver = 30;
    } else {
      this._ver = YAPIContext.imm_atoi(this.imm_json_get_key(json, "ver"));
    }
    return this._ver;
  }
  /**
   * Reinitialize the filesystem to its clean, unfragmented, empty state.
   * All files previously uploaded are permanently lost.
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async format_fs() {
    let json;
    let res;
    json = await this.sendCommand("format");
    res = this.imm_json_get_key(json, "res");
    if (!(res == "ok")) {
      return this._throw(this._yapi.IO_ERROR, "format failed", this._yapi.IO_ERROR);
    }
    return this._yapi.SUCCESS;
  }
  /**
   * Returns a list of YFileRecord objects that describe files currently loaded
   * in the filesystem.
   *
   * @param pattern : an optional filter pattern, using star and question marks
   *         as wild cards. When an empty pattern is provided, all file records
   *         are returned.
   *
   * @return a list of YFileRecord objects, containing the file path
   *         and name, byte size and 32-bit CRC of the file content.
   *
   * On failure, throws an exception or returns an empty list.
   */
  async get_list(pattern) {
    let json;
    let filelist = [];
    let res = [];
    json = await this.sendCommand("dir&f=" + pattern);
    filelist = this.imm_json_get_array(json);
    res.length = 0;
    for (let ii_0 of filelist) {
      res.push(new YFileRecord(this._yapi.imm_bin2str(ii_0)));
    }
    return res;
  }
  /**
   * Tests if a file exists on the filesystem of the module.
   *
   * @param filename : the filename to test.
   *
   * @return true if the file exists, false otherwise.
   *
   * On failure, throws an exception.
   */
  async fileExist(filename) {
    let json;
    let filelist = [];
    if (filename.length == 0) {
      return false;
    }
    json = await this.sendCommand("dir&f=" + filename);
    filelist = this.imm_json_get_array(json);
    if (filelist.length > 0) {
      return true;
    }
    return false;
  }
  /**
   * Downloads the requested file and returns a binary buffer with its content.
   *
   * @param pathname : path and name of the file to download
   *
   * @return a binary buffer with the file content
   *
   * On failure, throws an exception or returns an empty content.
   */
  async download(pathname) {
    return await this._download(pathname);
  }
  /**
   * Uploads a file to the filesystem, to the specified full path name.
   * If a file already exists with the same path name, its content is overwritten.
   *
   * @param pathname : path and name of the new file to create
   * @param content : binary buffer with the content to set
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async upload(pathname, content) {
    return await this._upload(pathname, content);
  }
  /**
   * Deletes a file, given by its full path name, from the filesystem.
   * Because of filesystem fragmentation, deleting a file may not always
   * free up the whole space used by the file. However, rewriting a file
   * with the same path name will always reuse any space not freed previously.
   * If you need to ensure that no space is taken by previously deleted files,
   * you can use format_fs to fully reinitialize the filesystem.
   *
   * @param pathname : path and name of the file to remove.
   *
   * @return YAPI.SUCCESS if the call succeeds.
   *
   * On failure, throws an exception or returns a negative error code.
   */
  async remove(pathname) {
    let json;
    let res;
    json = await this.sendCommand("del&f=" + pathname);
    res = this.imm_json_get_key(json, "res");
    if (!(res == "ok")) {
      return this._throw(this._yapi.IO_ERROR, "unable to remove file", this._yapi.IO_ERROR);
    }
    return this._yapi.SUCCESS;
  }
  /**
   * Returns the expected file CRC for a given content.
   * Note that the CRC value may vary depending on the version
   * of the filesystem used by the hub, so it is important to
   * use this method if a reference value needs to be computed.
   *
   * @param content : a buffer representing a file content
   *
   * @return the 32-bit CRC summarizing the file content, as it would
   *         be returned by the get_crc() method of
   *         YFileRecord objects returned by get_list().
   */
  async get_content_crc(content) {
    let fsver;
    let sz;
    let blkcnt;
    let meta;
    let blkidx;
    let blksz;
    let part;
    let res;
    sz = content.length;
    fsver = await this._getVersion();
    if (fsver < 40) {
      res = this._yapi.imm_bincrc(content, 0, sz);
      res = (res & 2147483647) - 2 * (res >> 1 & 1073741824);
      return res;
    }
    blkcnt = (sz + 255) / 256 >> 0;
    meta = new Uint8Array(4 * blkcnt);
    blkidx = 0;
    while (blkidx < blkcnt) {
      blksz = sz - blkidx * 256;
      if (blksz > 256) {
        blksz = 256;
      }
      part = this._yapi.imm_bincrc(content, blkidx * 256, blksz) ^ 4294967295;
      meta.set([part & 255], 4 * blkidx);
      meta.set([part >> 8 & 255], 4 * blkidx + 1);
      meta.set([part >> 16 & 255], 4 * blkidx + 2);
      meta.set([part >> 24 & 255], 4 * blkidx + 3);
      blkidx = blkidx + 1;
    }
    res = this._yapi.imm_bincrc(meta, 0, 4 * blkcnt) ^ 4294967295;
    res = (res & 2147483647) - 2 * (res >> 1 & 1073741824);
    return res;
  }
  /**
   * Continues the enumeration of filesystems started using yFirstFiles().
   * Caution: You can't make any assumption about the returned filesystems order.
   * If you want to find a specific a filesystem, use Files.findFiles()
   * and a hardwareID or a logical name.
   *
   * @return a pointer to a YFiles object, corresponding to
   *         a filesystem currently online, or a null pointer
   *         if there are no more filesystems to enumerate.
   */
  nextFiles() {
    let resolve = this._yapi.imm_resolveFunction(this._className, this._func);
    if (resolve.errorType != YAPI.SUCCESS)
      return null;
    let next_hwid = this._yapi.imm_getNextHardwareId(this._className, resolve.result);
    if (next_hwid == null)
      return null;
    return _YFiles.FindFilesInContext(this._yapi, next_hwid);
  }
  /**
   * Starts the enumeration of filesystems currently accessible.
   * Use the method YFiles.nextFiles() to iterate on
   * next filesystems.
   *
   * @return a pointer to a YFiles object, corresponding to
   *         the first filesystem currently online, or a null pointer
   *         if there are none.
   */
  static FirstFiles() {
    let next_hwid = YAPI.imm_getFirstHardwareId("Files");
    if (next_hwid == null)
      return null;
    return _YFiles.FindFiles(next_hwid);
  }
  /**
   * Starts the enumeration of filesystems currently accessible.
   * Use the method YFiles.nextFiles() to iterate on
   * next filesystems.
   *
   * @param yctx : a YAPI context.
   *
   * @return a pointer to a YFiles object, corresponding to
   *         the first filesystem currently online, or a null pointer
   *         if there are none.
   */
  static FirstFilesInContext(yctx) {
    let next_hwid = yctx.imm_getFirstHardwareId("Files");
    if (next_hwid == null)
      return null;
    return _YFiles.FindFilesInContext(yctx, next_hwid);
  }
};
YFiles.FILESCOUNT_INVALID = YAPI.INVALID_UINT;
YFiles.FREESPACE_INVALID = YAPI.INVALID_UINT;

// obj/full/constants.js
var constants = class _constants {
  static get versionInfo() {
    return (
      /* version number patched automatically */
      ["2.1.11996", "71996"]
    );
  }
  static get buildVersion() {
    return this.versionInfo[0];
  }
  static get trueBuild() {
    return this.versionInfo[1];
  }
  static get deviceScreenWidth() {
    return screen.width * window.devicePixelRatio;
  }
  static get deviceScreenHeight() {
    return screen.height * window.devicePixelRatio;
  }
  // if screen DPI is greater than 96 and screen width is less the 12 " (~A4) then it's probably a phone a tablet
  static get isPhoneOrTablet() {
    return _constants.ScreenDPI > 96 && Math.max(_constants.deviceScreenWidth, _constants.deviceScreenHeight) / _constants.ScreenDPI < 12;
  }
  static get captureSizePolicy() {
    return _constants._defaultCaptureSizePolicy;
  }
  static set captureSizePolicy(value) {
    _constants._defaultCaptureSizePolicy = value;
  }
  static get dbleClickBringsUpContextMenu() {
    return _constants._defaulDbleClickBringsUpContextMenu;
  }
  static set dbleClickBringsUpContextMenu(value) {
    _constants._defaulDbleClickBringsUpContextMenu = value;
  }
  static get maxPointsPerGraphSerie() {
    return _constants._defaultMaxPointsPerGraphSerie;
  }
  static set maxPointsPerGraphSerie(value) {
    if (value >= 0)
      _constants._defaultMaxPointsPerGraphSerie = value >> 0;
  }
  static get maxPointsPerDataloggerSerie() {
    return _constants._defaultMaxPointsPerDataloggerSerie;
  }
  static set maxPointsPerDataloggerSerie(value) {
    if (value >= -1)
      _constants._defaultMaxPointsPerDataloggerSerie = value >> 0;
  }
  static get maxDataRecordsPerSensor() {
    return _constants._defaultMaxDataRecordsPerSensor;
  }
  static set maxDataRecordsPerSensor(value) {
    if (value >= 0)
      _constants._defaultMaxDataRecordsPerSensor = value >> 0;
  }
  static get captureWidth() {
    return _constants._defaultCaptureWidth;
  }
  static set captureWidth(value) {
    if (value > 0)
      _constants._defaultCaptureWidth = value;
  }
  static get captureHeight() {
    return _constants._defaultCaptureHeight;
  }
  static set captureHeight(value) {
    if (value > 0)
      _constants._defaultCaptureHeight = value;
  }
  static get captureDPI() {
    return _constants._defaultCaptureDPI;
  }
  static set captureDPI(value) {
    if (value > 0)
      _constants._defaultCaptureDPI = value;
  }
  static get captureType() {
    return _constants._defaultCaptureType;
  }
  static set captureType(value) {
    _constants._defaultCaptureType = value;
  }
  // UI constants
  static get generalFontFamily() {
    return _constants.FontFamily;
  }
  static get generalFontSize() {
    return _constants.FontSize;
  }
  static get generalSizeCoef() {
    return _constants.FontSize / 12;
  }
  static get screenDPI() {
    return _constants.ScreenDPI;
  }
  static get guiDPIFactor() {
    return _constants.DPIfactor;
  }
  static get guiDPIFactorWasOverriden() {
    return _constants.DPIfactorOverriden;
  }
  static get guiDPIFactorIsOverriden() {
    let vs = _constants.getCookie(_constants.DPIFactorKey);
    if (vs == null)
      return false;
    let v = parseFloat(vs);
    if (v > 0)
      return true;
    return false;
  }
  static get guiDPIFactorOverrideValue() {
    let vs = _constants.getCookie(_constants.DPIFactorKey);
    if (vs == null)
      return _constants.DPIfactor;
    let v = parseFloat(vs);
    if (v > 0)
      return v;
    return _constants.DPIfactor;
  }
  static overrideGuiDPIFactor(override, value) {
    if (override && typeof value !== "undefined") {
      _constants.setCookie(_constants.DPIFactorKey, value.toString(), 3650);
    } else {
      _constants.setCookie(_constants.DPIFactorKey, "none", 3650);
    }
  }
  static get RunningOnAndroid() {
    return _constants.isAndroid;
  }
  static get WindowBackgroundColor() {
    return "#f0f0f0";
  }
  static get WindowBorder() {
    return "1px solid #808080";
  }
  static get WindowPadding() {
    return 2;
  }
  static get WindowInnerBorderColor() {
    return " #A0A0A0";
  }
  static get WindowInnerBackgroundColor() {
    return "#fAfAfA";
  }
  static get WindowInnerBorder() {
    return "1px solid " + _constants.WindowInnerBorderColor;
  }
  static get WindowHeaderBackgroundColor() {
    return "#0072ca";
  }
  static get WindowHeaderColor() {
    return "white";
  }
  static get WindowHeaderBorder() {
    return "1px solid #0072ca";
  }
  static get WindowHeaderHeight() {
    return 20;
  }
  static get WindowHeaderFontSize() {
    return 8 * _constants.FontSize / 6;
  }
  static get WindowHeaderFontFamily() {
    return _constants.FontFamily;
  }
  static get mustCheckForUpdate() {
    return _constants._checkForUpdate;
  }
  static set mustCheckForUpdate(value) {
    if (value != _constants._checkForUpdate) {
      _constants._checkForUpdate = value;
      _constants.edited = true;
    }
  }
  static get edited() {
    return _constants._edited;
  }
  static set edited(value) {
    if (_constants._edited != value) {
      _constants._edited = value;
      YWebPage.ShowSaveReminder(value);
    }
  }
  static InitColorHistory(node) {
    let nodes = node.get_childsByIndex();
    for (let i = 0; i < nodes.length; i++) {
      let node2 = nodes[i];
      switch (nodes[i].Name) {
        case "Color":
          try {
            let colordef = node2.Attributes["value"];
            let color = YColor.FromString(colordef);
            if (color != null)
              colorEditor.AddColorToHistory(color);
          } catch (_a) {
          }
      }
    }
  }
  //#endif
  static findDPI() {
    let DPI = 1;
    while (!matchMedia("(max-resolution: " + DPI.toString() + "dpi)").matches) {
      DPI = DPI << 1;
    }
    let a = DPI >> 1;
    let b = DPI;
    while (b - a > 1) {
      let pivot = b + a >> 1;
      if (matchMedia("(max-resolution: " + pivot.toString() + "dpi)").matches) {
        b = pivot;
      } else {
        a = pivot;
      }
    }
    return b * (_constants.isAndroid ? window.devicePixelRatio : 1);
  }
  static InitCaptureParams(node) {
    let nodes = node.get_childsByName();
    for (let nodeName in nodes) {
      let node2 = nodes[nodeName];
      let value;
      switch (nodeName) {
        case "Target":
          if ("value" in node2.Attributes) {
            _constants._captureTarget = YDataRenderer.CaptureTarget.fromString(YDataRenderer.CaptureTarget, node2.Attributes["value"]);
          }
          break;
        case "Type":
          if ("value" in node2.Attributes) {
            _constants._defaultCaptureType = YDataRenderer.CaptureType.fromString(YDataRenderer.CaptureType, node2.Attributes["value"]);
          }
          break;
        case "Size":
          if ("value" in node2.Attributes) {
            _constants._defaultCaptureSizePolicy = YDataRenderer.CaptureFormats.fromString(YDataRenderer.CaptureFormats, node2.Attributes["value"]);
          }
          break;
        case "Resolution":
          if ("value" in node2.Attributes) {
            _constants.captureDPI = parseInt(node2.Attributes["value"]);
          }
          break;
        case "Width":
          if ("value" in node2.Attributes) {
            _constants.captureWidth = value = parseInt(node2.Attributes["value"]);
          }
          break;
        case "Height":
          if ("value" in node2.Attributes) {
            _constants.captureHeight = parseInt(node2.Attributes["value"]);
          }
          break;
        case "Folder":
          if ("value" in node2.Attributes) {
            _constants._captureFolder = node2.Attributes["value"];
          }
          break;
      }
    }
  }
  static InitUIParams(node) {
    let nodes = node.get_childsByName();
    for (let nodeName in nodes) {
      let node2 = nodes[nodeName];
      switch (nodeName) {
        case "VerticalDragZoom":
          if ("value" in node2.Attributes) {
            YGraph.verticalDragZoomEnabled = node2.Attributes["value"].toUpperCase() == "TRUE";
          }
          break;
        case "DbleClickContextMenu":
          if ("value" in node2.Attributes) {
            _constants.dbleClickBringsUpContextMenu = node2.Attributes["value"].toUpperCase() == "TRUE";
          }
          break;
      }
    }
  }
  static InitCheckForUpdateParams(node) {
    let nodes = node.get_childsByName();
    for (let nodeName in nodes) {
      let node2 = nodes[nodeName];
      switch (nodeName) {
        case "checkForUpdate":
          if ("value" in node2.Attributes) {
            _constants._checkForUpdate = node2.Attributes["value"].toUpperCase() == "TRUE";
          }
          break;
        case "ignoreBuild":
          if ("value" in node2.Attributes) {
            _constants._ignoreBuild = parseInt(node2.Attributes["value"].toUpperCase());
          }
          break;
      }
    }
  }
  static InitMemoryUsageParams(node) {
    let nodes = node.get_childsByName();
    for (let nodeName in nodes) {
      let node2 = nodes[nodeName];
      switch (nodeName) {
        case "maxPointsPerGraphSerie":
          if ("value" in node2.Attributes) {
            let value = parseInt(node2.Attributes["value"]);
            _constants.maxPointsPerGraphSerie = value;
            DataSerie.MaxPointsPerSeries = value;
          }
          break;
        case "maxDataRecordsPerSensor":
          if ("value" in node2.Attributes) {
            let value = parseInt(node2.Attributes["value"]);
            if (value >= 0) {
              _constants.maxDataRecordsPerSensor = value;
              CustomYSensor.MaxDataRecords = value;
            }
          }
          break;
        case "maxPointsPerDataloggerSerie":
          if ("value" in node2.Attributes) {
            let value = parseInt(node2.Attributes["value"]);
            _constants.maxPointsPerDataloggerSerie = value;
            CustomYSensor.MaxLoggerRecords = value;
          }
          break;
        case "deviceListValidity":
          if ("value" in node2.Attributes) {
            let value = parseInt(node2.Attributes["value"]);
            if (value > 0) {
              _constants._deviceListValidity = value;
              YAPI.SetDeviceListValidity(value).then();
            }
          }
          break;
      }
    }
  }
  static Init(initData) {
    let nodes = initData.get_childsByName();
    for (let nodeName in nodes) {
      let node = nodes[nodeName];
      switch (nodeName) {
        case "UseUSB":
          if ("value" in node.Attributes) {
            _constants._useUSB = node.Attributes["value"].toUpperCase() == "TRUE";
          }
          break;
        //#ifndef READONLY
        case "Colors":
          _constants.InitColorHistory(node);
          break;
        //#endif
        case "Capture":
          _constants.InitCaptureParams(node);
          break;
        case "UI":
          _constants.InitUIParams(node);
          break;
        case "Updates":
          _constants.InitCheckForUpdateParams(node);
          break;
        case "MemoryUsage":
          _constants.InitMemoryUsageParams(node);
          break;
        case "UseVirtualHub":
          if ("value" in node.Attributes) {
            _constants._useVirtualHub = node.Attributes["value"].toUpperCase() == "TRUE";
          }
          break;
        case "Hubs":
          sensorsManager.InitHubList(node);
          break;
      }
    }
  }
  static get CRCTable() {
    if (_constants._crcTable != null)
      return _constants._crcTable;
    _constants._crcTable = [];
    let c;
    var crcTable = [];
    for (var n = 0; n < 256; n++) {
      c = n;
      for (var k = 0; k < 8; k++) {
        c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
      }
      _constants._crcTable[n] = c;
    }
    return _constants._crcTable;
  }
  static crc32(str) {
    var crcTable = _constants.CRCTable;
    var crc = 0 ^ -1;
    for (var i = 0; i < str.length; i++) {
      crc = crc >>> 8 ^ crcTable[(crc ^ str.charCodeAt(i)) & 255];
    }
    return (crc ^ -1) >>> 0;
  }
  static setCookie(cname, cvalue, exdays) {
    let d = /* @__PURE__ */ new Date();
    d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1e3);
    let expires = "expires=" + d.toUTCString();
    document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
  }
  static getCookie(cname) {
    let name = cname + "=";
    let decodedCookie = decodeURIComponent(document.cookie);
    let ca = decodedCookie.split(";");
    for (let i = 0; i < ca.length; i++) {
      let c = ca[i];
      while (c.charAt(0) == " ") {
        c = c.substring(1);
      }
      if (c.indexOf(name) == 0) {
        return c.substring(name.length, c.length);
      }
    }
    return null;
  }
  //#ifndef READONLY
  static XMLquote(st) {
    return st.replace("'", "&apos;").replace('"', "&quot;");
  }
  static GetXMLConfiguration() {
    let res = '<Config>\n  <UseUSB value="' + (_constants._useUSB ? "TRUE" : "FALSE") + '"/>\n  <UseVirtualHub value="' + (_constants._useVirtualHub ? "TRUE" : "FALSE") + '"/>\n  <Hubs>\n';
    res += sensorsManager.getXmlHublist();
    res += "  </Hubs>\n";
    res += "  <Colors>\n";
    for (let i = 0; i < colorEditor.colorHistory.length; i++) {
      res = res + '    <Color value="' + colorEditor.colorHistory[i].toString() + '"/>\n';
    }
    res += "  </Colors>\n";
    res += "  <MemoryUsage>\n";
    res += '    <maxPointsPerGraphSerie value= "' + _constants.maxPointsPerGraphSerie.toString() + '"/>\n';
    res += '    <maxDataRecordsPerSensor value= "' + _constants.maxDataRecordsPerSensor.toString() + '"/>\n';
    res += '    <maxPointsPerDataloggerSerie value= "' + _constants.maxPointsPerDataloggerSerie.toString() + '"/>\n';
    res += '    <deviceListValidity value= "' + _constants._deviceListValidity.toString() + '"/>\n';
    res += "  </MemoryUsage>\n";
    res += "  <Capture>\n";
    res += '    <Target value= "' + _constants._captureTarget.toString + '"/>\n';
    res += '    <Type value= "' + _constants.captureType.toString + '"/>\n';
    res += '    <Size value= "' + _constants.captureSizePolicy.toString + '"/>\n';
    res += '    <Resolution value= "' + _constants.captureDPI.toString() + '"/>\n';
    res += '    <Folder value= "' + _constants.XMLquote(_constants._captureFolder) + '"/>\n';
    res += '    <Width value= "' + _constants.captureWidth.toString() + '"/>\n';
    res += '    <Height value= "' + _constants.captureHeight.toString() + '"/>\n';
    res += "  </Capture>\n";
    res += "  <Updates>\n";
    res += '    <checkForUpdate  value = "' + (_constants._checkForUpdate ? "True" : "False") + '"/>\n';
    res += '    <ignoreBuild  value = "' + _constants._ignoreBuild.toString() + '"/>\n';
    res += "  </Updates>\n";
    res += "  <UI>\n";
    res += '    <VerticalDragZoom value= "' + (YGraph.verticalDragZoomEnabled ? "True" : "False") + '"/>\n';
    res += '    <DbleClickContextMenu value= "' + (_constants.dbleClickBringsUpContextMenu ? "True" : "False") + '"/>\n';
    res += "  </UI>\n";
    res += "</Config>\n";
    return res;
  }
};
constants.DPIFactorKey = "YoctoVisualization4WebDPIFactor";
constants.isAndroid = navigator.userAgent.toLowerCase().indexOf("android") >= 0;
constants.ScreenDPI = constants.findDPI();
constants.FontFamily = "Arial, Helvetica, sans-serif";
constants.DPIfactorCookieValue = constants.getCookie(constants.DPIFactorKey);
constants.DPIfactorOverriden = constants.DPIfactorCookieValue != null;
constants.DPIfactor = constants.DPIfactorOverriden && parseFloat(constants.DPIfactorCookieValue) > 0 ? parseFloat(constants.DPIfactorCookieValue) : constants.isPhoneOrTablet ? Math.round(10 * constants.ScreenDPI / (96 * 1.2)) / 10 : 1;
constants.FontSize = 12 * constants.DPIfactor;
constants._defaultCaptureType = YDataRenderer.CaptureType.PNG;
constants._defaultCaptureWidth = 1024;
constants._defaultCaptureHeight = 1024;
constants._defaultCaptureDPI = 70;
constants._defaultCaptureSizePolicy = YDataRenderer.CaptureFormats.Keep;
constants._defaultMaxPointsPerGraphSerie = 0;
constants._defaultMaxPointsPerDataloggerSerie = 0;
constants._defaultMaxDataRecordsPerSensor = 0;
constants._defaulDbleClickBringsUpContextMenu = false;
constants._useUSB = true;
constants._useVirtualHub = false;
constants._captureTarget = YDataRenderer.CaptureTarget.ToFile;
constants._captureFolder = "";
constants._checkForUpdate = true;
constants._ignoreBuild = 0;
constants._deviceListValidity = 3600;
constants._edited = false;
constants._crcTable = null;

// obj/full/sensorManager.js
var Hub = class _Hub {
  // not really crypto functions, but since source code will end up in the client browser,
  // there is no point in using techniques more complicated than obfuscation
  static Decrypt(data, loginCypherPassword) {
    if (data == "")
      return "";
    if (data.length % 4)
      return "";
    let buffer = new Uint16Array(data.length >> 2);
    for (let i = 0; i < buffer.length; i++) {
      buffer[i] = parseInt(data.slice(i * 4, i * 4 + 4), 16);
    }
    let checksum = buffer[0];
    for (let i = 1; i < buffer.length - 1; i++) {
      checksum ^= buffer[i];
    }
    if (checksum != buffer[buffer.length - 1])
      return "";
    let res = "";
    for (let i = 0; i < buffer.length - 2; i++) {
      res += String.fromCharCode(buffer[i + 1] & 255 ^ loginCypherPassword.charCodeAt(i % loginCypherPassword.length) ^ buffer[0] + i & 255 ^ (buffer[i + 1] & 2048) >> 11 << ((buffer[i + 1] & 1792) >> 8) ^ (buffer[i + 1] & 32768) >> 15 << ((buffer[i + 1] & 28672) >> 12));
    }
    return res;
  }
  static Encrypt(data, loginCypherPassword) {
    if (data == "")
      return "";
    let buffer = crypto.getRandomValues(new Uint16Array(data.length + 2));
    for (let i = 0; i < data.length; i++) {
      buffer[i + 1] = buffer[i + 1] & 65280 | data.charCodeAt(i) ^ loginCypherPassword.charCodeAt(i % loginCypherPassword.length) ^ buffer[0] + i & 255 ^ (buffer[i + 1] & 2048) >> 11 << ((buffer[i + 1] & 1792) >> 8) ^ (buffer[i + 1] & 32768) >> 15 << ((buffer[i + 1] & 28672) >> 12);
    }
    buffer[buffer.length - 1] = buffer[0];
    for (let i = 1; i < buffer.length - 1; i++) {
      buffer[buffer.length - 1] ^= buffer[i];
    }
    let res = "";
    for (let i = 0; i < buffer.length; i++) {
      res += ("000" + buffer[i].toString(16)).slice(-4).toUpperCase();
    }
    return res;
  }
  static encryptPassword(clearPassword) {
    return clearPassword == "" ? "" : _Hub.Encrypt(clearPassword, _Hub.loginCypherPassword);
  }
  set_apiHub(hub) {
    this._apiHub = hub;
  }
  get hubType() {
    return this._hubType;
  }
  set hubType(value) {
    this._hubType = value;
  }
  get protocol() {
    return this._protocol;
  }
  set protocol(value) {
    this._protocol = value;
  }
  get user() {
    return this._user;
  }
  set user(value) {
    this._user = value;
  }
  get encryptedPassword() {
    return this._password;
  }
  set encryptedPassword(value) {
    this._password = value;
  }
  get clearPassword() {
    return this._password == "" ? "" : _Hub.Decrypt(this._password, _Hub.loginCypherPassword);
  }
  set clearPassword(value) {
    this._password = value == "" ? "" : _Hub.encryptPassword(value);
  }
  get addr() {
    return this._addr;
  }
  set addr(value) {
    this._addr = value;
  }
  get port() {
    return this._port;
  }
  set port(value) {
    this._port = value;
  }
  get removable() {
    return this._removable;
  }
  set removable(value) {
    this._removable = value;
  }
  get path() {
    return this._path;
  }
  set path(value) {
    this._path = value;
  }
  get_serialNumber() {
    if (this._apiHub == null)
      return "?";
    return this._apiHub.get_serialNumber();
  }
  ConnectionState() {
    if (this._apiHub != null)
      switch (this._apiHub.get_connectionState()) {
        case this._apiHub.TRYING:
          this._state = 1;
          break;
        case this._apiHub.CONNECTED:
          this._state = 2;
          break;
        case this._apiHub.RECONNECTING:
          this._state = 1;
          break;
        case this._apiHub.ABORTED:
          this._state = 3;
          break;
        case this._apiHub.UNREGISTERED:
          this._state = 3;
          break;
      }
    return this._state;
  }
  async ConnectionDescription() {
    switch (this._state) {
      case 1:
        if (this._apiHub != null) {
          if (await this._apiHub.get_errorType() != YAPI_SUCCESS) {
            this._state = 3;
            return await this._apiHub.get_errorMessage();
          }
        }
        return "Connecting..";
        break;
      case 2:
        return (this._logicname != "" ? this._logicname : this._netname) + " OK";
      case 3:
        if (this._apiHub != null)
          return await this._apiHub.get_errorMessage();
        return "Connection failed ";
      default:
        return "Not connected.";
    }
  }
  constructor(hubType, protocol, user, password, clearPassword, addr, port, path, removeable) {
    this._netname = "";
    this._module = null;
    this._logicname = "";
    this._previousURL = "";
    this._previousobfuscatedURL = "";
    this._apiHub = null;
    this._hubType = 2;
    this._protocol = "";
    this._user = "";
    this._password = "";
    this._addr = "";
    this._port = "";
    this._removable = true;
    this._path = "";
    this._state = 0;
    while (/^\//.test(path)) {
      path = path.slice(1);
    }
    while (/\/$/.test(path)) {
      path = path.slice(0, -1);
    }
    this._hubType = hubType;
    this._protocol = protocol;
    this._user = user;
    this._port = port;
    this._password = clearPassword ? password != "" ? _Hub.Encrypt(password, _Hub.loginCypherPassword) : "" : password;
    this._addr = addr;
    this._path = path;
    this._removable = typeof removeable === "undefined" ? true : removeable;
  }
  static HubFromXml(subnode) {
    let hubType = 2;
    let protocol = "ws";
    let removable = true;
    if ("protocol" in subnode.Attributes)
      protocol = subnode.Attributes["protocol"];
    let user = "";
    if ("user" in subnode.Attributes)
      user = subnode.Attributes["user"];
    let password = "";
    if ("password" in subnode.Attributes)
      password = subnode.Attributes["password"];
    let port = "";
    if ("port" in subnode.Attributes)
      port = subnode.Attributes["port"];
    let path = "";
    if ("path" in subnode.Attributes)
      path = subnode.Attributes["path"];
    let addr = "";
    if ("addr" in subnode.Attributes)
      addr = subnode.Attributes["addr"];
    if ("removable" in subnode.Attributes)
      removable = subnode.Attributes["removable"].toUpperCase() == "TRUE";
    return new _Hub(hubType, protocol, user, password, false, addr, port, path, removable);
  }
  async Connect() {
    let errmsg = new YErrorMsg();
    this._state = 1;
    let ofsUrl = this.get_obfuscatedURL();
    logForm.log("preregistering  " + ofsUrl);
    if (window.location.protocol.toString().toLowerCase() == "https:") {
      if (ofsUrl.substring(0, 3).toLowerCase() == "ws:" || ofsUrl.substring(0, 5).toLowerCase() == "http:")
        logForm.log("Warning: Host page is HTTPS, ws:// and http:// protocols are unlikely to work due to CORS rules.");
    }
    if (!YWebPage.readonly)
      configForm.hubStateChanged(this);
    let url = this.get_fullUrl();
    if (await YAPI.PreregisterHub(url, errmsg) != YAPI_SUCCESS) {
      logForm.log("[!] preregistering  " + this.get_obfuscatedURL() + " failed (" + errmsg.msg + ")");
      this._state = 3;
      this._previousURL = "";
      this._previousobfuscatedURL = "";
    } else {
      let h = YHub.FirstHubInUse();
      while (h != null) {
        if (await this.matches(h))
          this._apiHub = h;
        h = h.nextHubInUse();
      }
      this._previousURL = url;
      this._previousobfuscatedURL = this.get_obfuscatedURL();
    }
    if (!YWebPage.readonly)
      configForm.hubStateChanged(this);
  }
  async Disconnect() {
    if (this._previousURL == "")
      return;
    logForm.log("Unregistering  " + this._previousobfuscatedURL);
    await YAPI.UnregisterHub(this._previousURL);
    this._previousURL = "";
    this._previousobfuscatedURL = "";
    this._state = 0;
    this._apiHub = null;
    if (!YWebPage.readonly)
      configForm.hubStateChanged(this);
  }
  arrival(ip, netname, module, logicname) {
    this._state = 2;
    this._netname = netname;
    this._module = module;
    this._logicname = logicname;
    this._state = 2;
    if (!YWebPage.readonly) {
      configForm.hubStateChanged(this);
    }
  }
  get address() {
    return this._addr;
  }
  get_consoleUrl() {
    let fullurl = this._protocol + "://" + this._addr;
    if (this._port != "")
      fullurl = fullurl + ":" + this._port;
    return fullurl;
  }
  toString() {
    return this.get_consoleUrl();
  }
  get_fullUrl() {
    let fullurl = "";
    if (this._protocol != "")
      fullurl = this._protocol + "://";
    if (this._user != "") {
      fullurl = fullurl + this._user;
      if (this._password != "")
        fullurl = fullurl + ":" + _Hub.Decrypt(this._password, _Hub.loginCypherPassword);
      fullurl = fullurl + "@";
    }
    fullurl = fullurl + this._addr;
    if (this._port != "")
      fullurl = fullurl + ":" + this._port;
    else
      fullurl = fullurl + ":4444";
    fullurl = fullurl + "/";
    if (this._path) {
      fullurl = fullurl + this._path + "/";
    }
    return fullurl;
  }
  get_fullUrlNoCredentials() {
    let fullurl = "";
    if (this._protocol != "")
      fullurl = this._protocol + "://";
    fullurl = fullurl + this._addr;
    if (this._port != "")
      fullurl = fullurl + ":" + this._port;
    else
      fullurl = fullurl + ":4444";
    fullurl = fullurl + "/";
    if (this._path) {
      fullurl = fullurl + this._path + "/";
    }
    return fullurl;
  }
  get_connexionUrl() {
    let fullurl;
    if (this._protocol != "")
      fullurl = this._protocol + "://";
    else
      fullurl = "ws://";
    fullurl = fullurl + this._addr;
    if (this._port != "")
      fullurl = fullurl + ":" + this._port;
    else
      fullurl = fullurl + ":4444";
    fullurl = fullurl + "/";
    return fullurl.toLowerCase();
  }
  get_obfuscatedURL() {
    let fullurl = "";
    if (this._protocol != "")
      fullurl = this._protocol + "://";
    if (this._user != "") {
      fullurl = fullurl + this._user;
      if (this._password != "")
        fullurl = fullurl + ":#####";
      fullurl = fullurl + "@";
    }
    fullurl = fullurl + this._addr;
    if (this._port != "")
      fullurl = fullurl + ":" + this._port;
    if (this._path != "")
      fullurl = fullurl + "/" + this._path;
    return fullurl;
  }
  async matches(apihub) {
    let URL1 = this.get_connexionUrl() + this._path;
    let URL2 = await apihub.get_connectionUrl();
    if (URL1.slice(-1) == "/")
      URL1 = URL1.substr(0, URL1.length - 1);
    if (URL2.slice(-1) == "/")
      URL2 = URL2.substr(0, URL2.length - 1);
    return URL1 == URL2;
  }
  //#ifndef READONLY
  XmlCode() {
    let NodeLine = "<Hub ";
    if (this._protocol != "")
      NodeLine = NodeLine + 'protocol="' + constants.XMLquote(this._protocol) + '" ';
    if (this._user != "")
      NodeLine = NodeLine + 'user="' + constants.XMLquote(this._user) + '" ';
    if (this._password != "")
      NodeLine = NodeLine + 'password="' + constants.XMLquote(this._password) + '" ';
    NodeLine = NodeLine + 'addr="' + constants.XMLquote(this._addr) + '" ';
    if (this._port != "")
      NodeLine = NodeLine + 'port="' + constants.XMLquote(this.port) + '" ';
    if (this._path != "")
      NodeLine = NodeLine + 'path="' + constants.XMLquote(this._path) + '" ';
    if (!this._removable)
      NodeLine = NodeLine + 'removable="FALSE" ';
    return NodeLine + "/>";
  }
};
Hub.loginCypherPassword = "+>*X[?_ih$N7wA!}";
var TimedSensorValue = class {
  constructor(DateTime, value) {
    this._DateTime = DateTime;
    this._value = value;
  }
  get DateTime() {
    return this._DateTime;
  }
  get value() {
    return this._value;
  }
};
var AlarmSettings = class {
  static ExecuteCommand(source, command) {
  }
  constructor(index, owner, xmldata) {
    this.index = 0;
    this.Condition = 0;
    this.Source = 0;
    this.Value = 0;
    this.Delay = 15;
    this.Commandline = "";
    this.lastAlarm = /* @__PURE__ */ new Date(0);
    this.index = index;
    this.parent = owner;
    if (typeof xmldata !== "undefined") {
      if ("Source" in xmldata.Attributes)
        this.Source = parseInt(xmldata.Attributes["Source"]);
      if ("Condition" in xmldata.Attributes)
        this.Condition = parseInt(xmldata.Attributes["Condition"]);
      if ("Value" in xmldata.Attributes)
        this.Value = parseFloat(xmldata.Attributes["Value"]);
      if ("Cmd" in xmldata.Attributes)
        this.Commandline = xmldata.Attributes["Cmd"];
      if ("Delay" in xmldata.Attributes)
        this.Delay = parseInt(xmldata.Attributes["Delay"]);
    }
  }
  //#ifndef READONLY
  getXmlData() {
    return '<Alarm Source="' + this.Source.toString() + '" Condition="' + this.Condition.toString() + '" Value="' + this.Value.toString() + '" Cmd="' + GenericProperties.escapeXml(this.Commandline) + '" Delay="' + this.Delay.toString() + '"/>\n';
  }
  //#endif
  setCondition(condition) {
    this.Condition = condition;
  }
  getCondition() {
    return this.Condition;
  }
  setSource(source) {
    this.Source = source;
  }
  getSource() {
    return this.Source;
  }
  setValue(value) {
    this.Value = value;
  }
  getValue() {
    return this.Value;
  }
  setDelay(value) {
    if (value < 0)
      throw new Error("delay must be a positive value");
    this.Delay = value;
  }
  getDelay() {
    return this.Delay;
  }
  setCommandline(value) {
    this.Commandline = value;
  }
  getCommandline() {
    return this.Commandline;
  }
  check(m) {
    let alarm = false;
    let reason = "";
    let src;
    let SensorValue;
    switch (this.Source) {
      case 1:
        src = "MIN";
        SensorValue = m.get_minValue();
        break;
      case 2:
        src = "MAX";
        SensorValue = m.get_maxValue();
        break;
      default:
        src = "AVG";
        SensorValue = m.get_averageValue();
        break;
    }
    switch (this.Condition) {
      default:
        return;
      // alarm disabled
      case 1:
        reason = ">";
        if (SensorValue > this.Value)
          alarm = true;
        break;
      case 2:
        reason = ">=";
        if (SensorValue >= this.Value)
          alarm = true;
        break;
      case 3:
        reason = "=";
        if (SensorValue == this.Value)
          alarm = true;
        break;
      case 4:
        reason = "<=";
        if (SensorValue <= this.Value)
          alarm = true;
        break;
      case 5:
        reason = "<";
        if (SensorValue < this.Value)
          alarm = true;
        break;
    }
    let now = /* @__PURE__ */ new Date();
    if (!alarm)
      return;
    if (now.getTime() - this.lastAlarm.getTime() < 1e3 * this.Delay)
      return;
    let source = "ALARM " + (this.index + 1).toString();
    logForm.log(source + " on " + this.parent.get_hardwareId() + "/" + this.parent.get_friendlyName() + " (" + SensorValue.toString() + reason + this.Value.toString() + ")");
    let Execute = this.Commandline;
    Execute = Execute.replace("$SENSORVALUE$", SensorValue.toString());
    Execute = Execute.replace("$HWDID$", this.parent.get_hardwareId());
    Execute = Execute.replace("$NAME$", this.parent.get_friendlyName());
    Execute = Execute.replace("$UNIT$", this.parent.get_unit());
    Execute = Execute.replace("$CONDITION$", reason);
    Execute = Execute.replace("$DATATYPE$", src);
    Execute = Execute.replace("$TRIGGER$", this.Value.toString());
    Execute = Execute.replace("$NOW$", logForm.dateToString(now));
    try {
      Function(Execute)();
    } catch (e) {
      logForm.log("ALARM " + (this.index + 1) + " code (" + Execute + ") caused an exception :" + e.message);
    }
    this.lastAlarm = now;
  }
};
var DataLoggerBoundary = class {
  constructor(start, stop) {
    this._start = 0;
    this._stop = 0;
    this._start = start;
    this._stop = stop;
  }
  get start() {
    return this._start;
  }
  get stop() {
    return this._stop;
  }
};
var MergeSourceRange = class {
  constructor(start, stop) {
    this.MergeSourceStart = 0;
    this.MergeSourceStop = 0;
    this.MergeSourceStart = start;
    this.MergeSourceStop = stop;
  }
};
var CustomYSensor = class _CustomYSensor {
  get isReadOnly() {
    return this._readonly || !this._online;
  }
  get dataloggerLoadisRunning() {
    return this._dataloggerLoadisRunning;
  }
  get dataloggerLoadProgress() {
    return this._dataloggerLoadProgress;
  }
  setDataloggerLoadProgress(value) {
    this._dataloggerLoadProgress = value;
    for (let i = 0; i < this.FormsToNotify.length; i++) {
      if (this.FormsToNotify[i] instanceof graphWidget) {
        this.FormsToNotify[i].dataLoggerLoadprocessIsRunningNotification(this);
      }
    }
  }
  static get MaxDataRecords() {
    return _CustomYSensor._MaxDataRecords;
  }
  static set MaxDataRecords(value) {
    _CustomYSensor._MaxDataRecords = value;
  }
  static get MaxLoggerRecords() {
    return _CustomYSensor._MaxLoggerRecords;
  }
  static set MaxLoggerRecords(value) {
    _CustomYSensor._MaxLoggerRecords = value;
  }
  get_lastAvgValue() {
    if (this._online)
      return this._lastAvgValue;
    return Number.NaN;
  }
  get_lastMaxValue() {
    if (this._online)
      return this._lastMaxValue;
    return Number.NaN;
  }
  get_lastMinValue() {
    if (this._online)
      return this._lastMinValue;
    return Number.NaN;
  }
  async ConfigHasChanged() {
    this.cfgChgNotificationsSupported = true;
    this.mustReloadConfig = true;
    this._online = true;
    await this.reloadConfig();
    await this.forceUpdate();
  }
  constructor(s, name, SensorLocalConfig) {
    this.unit = "";
    this._frequency = "";
    this.resolution = 1;
    this._recording = false;
    this._online = false;
    this.preloadDone = false;
    this.loadDone = false;
    this.cfgChgNotificationsSupported = false;
    this.mustReloadConfig = false;
    this._readonly = false;
    this._plzCancelDataloggerLoading = false;
    this._predloadProcessIsBusy = false;
    this._loadProcessIsBusy = false;
    this.lastGetConfig = 0;
    this.recordedDataLoadProgress = 0;
    this.recordedData = null;
    this.dataLoggerFeature = false;
    this.dataLoggerLoadCompleted = false;
    this.loadFailed = false;
    this.loadCanceled = false;
    this.firstLiveDataTimeStamp = 0;
    this.firstDataloggerTimeStamp = 0;
    this.lastDataTimeStamp = 0;
    this.lastDataSource = "";
    this.consecutiveBadTimeStamp = 0;
    this.minData = [];
    this.curData = [];
    this.maxData = [];
    this.previewMinData = [];
    this.previewCurData = [];
    this.previewMaxData = [];
    this._lastAvgValue = Number.NaN;
    this._lastMinValue = Number.NaN;
    this._lastMaxValue = Number.NaN;
    this._dataloggerLoadisRunning = true;
    this._dataloggerLoadProgress = 0;
    this.dataLoggerStartReadTime = 0;
    this.Alarms = [];
    this.FormsToNotify = [];
    this.sensor = s;
    this.hwdName = name;
    this._friendlyname = name;
    if (s == null)
      return;
    if (SensorLocalConfig != null) {
      let index = 0;
      let childs = SensorLocalConfig.get_childsByIndex();
      for (let i = 0; i < childs.length; i++) {
        let n = childs[i];
        if (n.Name == "Alarm") {
          this.checkAlarmIndex(index);
          this.Alarms[index] = new AlarmSettings(index, this, n);
          index++;
        }
      }
    }
  }
  checkAlarmIndex(index) {
    while (this.Alarms.length < index + 1) {
      this.Alarms.push(new AlarmSettings(this.Alarms.length, this));
    }
  }
  getAlarmCount() {
    return this.Alarms.length;
  }
  setAlarmCondition(index, condition) {
    this.checkAlarmIndex(index);
    this.Alarms[index].setCondition(condition);
  }
  getAlarmCondition(index) {
    this.checkAlarmIndex(index);
    return this.Alarms[index].getCondition();
  }
  setAlarmSource(index, source) {
    this.checkAlarmIndex(index);
    this.Alarms[index].setSource(source);
  }
  getAlarmSource(index) {
    this.checkAlarmIndex(index);
    return this.Alarms[index].getSource();
  }
  setAlarmValue(index, value) {
    this.checkAlarmIndex(index);
    this.Alarms[index].setValue(value);
  }
  getAlarmValue(index) {
    this.checkAlarmIndex(index);
    return this.Alarms[index].getValue();
  }
  setAlarmDelay(index, value) {
    this.checkAlarmIndex(index);
    this.Alarms[index].setDelay(value);
  }
  getAlarmDelay(index) {
    this.checkAlarmIndex(index);
    return this.Alarms[index].getDelay();
  }
  setAlarmCommandline(index, value) {
    this.checkAlarmIndex(index);
    this.Alarms[index].setCommandline(value);
  }
  getAlarmCommandline(index) {
    this.checkAlarmIndex(index);
    return this.Alarms[index].getCommandline();
  }
  //#ifndef READONLY
  GetXmlData() {
    let res = '<Sensor ID="' + this.get_hardwareId() + '">\n';
    for (let i = 0; i < this.getAlarmCount(); i++) {
      res = res + this.Alarms[i].getXmlData();
    }
    res = res + "</Sensor>\n";
    return res;
  }
  //#endif
  get_firstLiveDataTimeStamp() {
    return this.firstLiveDataTimeStamp;
  }
  get_firstDataloggerTimeStamp() {
    return this.firstDataloggerTimeStamp;
  }
  get_lastDataTimeStamp() {
    return this.lastDataTimeStamp;
  }
  async preload_DoWork(arg) {
    if (this._predloadProcessIsBusy) {
      console.log("!!! multiple datalogger load attempt");
      return;
    }
    if (this.dataLoggerLoadCompleted) {
      console.log("!!!  datalogger load attempt although datalogger is already loaded.");
      return;
    }
    this._predloadProcessIsBusy = true;
    logForm.log(this.hwdName + ": preloading data from " + arg.start.toString() + " to " + arg.stop.toString() + "(delta= " + (arg.stop - arg.start).toFixed(3) + ")");
    this.setDataloggerLoadProgress(1);
    this.recordedData = await this.sensor.get_recordedData(arg.start, arg.stop);
    try {
      this.recordedDataLoadProgress = await this.recordedData.loadMore();
    } catch (e) {
      logForm.log(this.hwdName + ": load more caused an exception " + e.message);
    }
    let measures = await this.recordedData.get_preview();
    this.previewMinData = [];
    this.previewCurData = [];
    this.previewMaxData = [];
    let startIndex = 0;
    let errCount = 0;
    let maxErrorCount = 10;
    let lastT = -1;
    if (_CustomYSensor._MaxDataRecords > 0 && measures.length > _CustomYSensor._MaxDataRecords)
      startIndex = measures.length - _CustomYSensor._MaxDataRecords;
    for (let i = startIndex; i < measures.length; i++) {
      let t = measures[i].get_endTimeUTC();
      if (t >= arg.start && t <= arg.stop) {
        this.previewMinData.push(new TimedSensorValue(t, measures[i].get_minValue()));
        this.previewCurData.push(new TimedSensorValue(t, measures[i].get_averageValue()));
        this.previewMaxData.push(new TimedSensorValue(t, measures[i].get_maxValue()));
        if (t < lastT && errCount < maxErrorCount) {
          logForm.log(this.hwdName + " preloading warning:  timestamp going back in time " + t + "<" + lastT + (errCount < maxErrorCount - 1 ? "" : ", further similar errors wont be logged."));
          errCount++;
        } else if (t == lastT && errCount < maxErrorCount) {
          logForm.log(this.hwdName + " preloading warning: duplicate timestamp  " + t + (errCount < maxErrorCount - 1 ? "" : ", further similar errors wont be logged."));
          errCount++;
        }
        lastT = t;
      }
    }
    if (this.previewCurData.length > 1) {
      let a = this.previewCurData[0].DateTime;
      let b = this.previewCurData[this.previewCurData.length - 1].DateTime;
      logForm.log(this.hwdName + ": preloaded data from " + a.toString() + " to " + b.toString() + " (delta=" + (b - a).toFixed(3) + ")");
      if (_CustomYSensor._MaxLoggerRecords > 0 && arg.start == 0) {
        let list = await this.recordedData.get_privateDataStreams();
        let index = list.length - 1;
        let totalRecords = 0;
        while (index > 0 && totalRecords < _CustomYSensor._MaxLoggerRecords) {
          totalRecords += await list[index].get_rowCount();
          this.dataLoggerStartReadTime = await list[index].get_startTimeUTC();
          index--;
        }
        let n = 0;
        while (n < this.previewMinData.length && this.previewMinData[n].DateTime < this.dataLoggerStartReadTime) {
          n++;
        }
        if (n > 1) {
          this.previewMinData.splice(0, n - 1);
          this.previewCurData.splice(0, n - 1);
          this.previewMaxData.splice(0, n - 1);
        }
      }
    }
    this.preload_Completed(arg);
  }
  findMergeBoundaries(previewMinData) {
    let MergeSourceStart = 0;
    let MergeSourceStop = 0;
    if (this.minData.length > 0) {
      while (MergeSourceStart < this.minData.length && previewMinData[0].DateTime > this.minData[MergeSourceStart].DateTime) {
        MergeSourceStart++;
      }
      MergeSourceStop = MergeSourceStart;
      while (MergeSourceStop < this.minData.length && previewMinData[previewMinData.length - 1].DateTime >= this.minData[MergeSourceStop].DateTime) {
        MergeSourceStop++;
      }
    }
    return new MergeSourceRange(MergeSourceStart, MergeSourceStop);
  }
  preload_Completed(arg) {
    if (this.previewMinData == null)
      return;
    logForm.log(this.hwdName + " : datalogger preloading completed (" + this.previewMinData.length + " rows)");
    if (this.previewMinData.length > 1) {
      let it = this.findMergeBoundaries(this.previewMinData);
      let insertIndex = it.MergeSourceStart;
      let deleteCount = it.MergeSourceStop - it.MergeSourceStart;
      this.minData = this.minData.slice(0, insertIndex).concat(this.previewMinData, this.minData.slice(insertIndex + deleteCount));
      this.curData = this.curData.slice(0, insertIndex).concat(this.previewCurData, this.curData.slice(insertIndex + deleteCount));
      this.maxData = this.maxData.slice(0, insertIndex).concat(this.previewMaxData, this.maxData.slice(insertIndex + deleteCount));
      for (let i = 0; i < this.FormsToNotify.length; i++) {
        if (this.FormsToNotify[i] instanceof graphWidget) {
          this.FormsToNotify[i].SensorNewDataBlock(this, it.MergeSourceStart, it.MergeSourceStart + this.previewMinData.length - 1, 0, true);
        }
      }
    }
    let count = this.curData.length;
    if (count > 0) {
      if (this.curData[count - 1].DateTime > this.lastDataTimeStamp) {
        this.lastDataTimeStamp = this.curData[count - 1].DateTime;
        this.lastDataSource = "last preload timestamp";
      }
    }
    logForm.log(this.hwdName + " : start datalogger loading");
    this._predloadProcessIsBusy = false;
    this.load_DoWork(arg).then();
  }
  get_frequency() {
    return this._frequency;
  }
  async updateFrequncy(frequencyToSet) {
    if (await this.sensor.isOnline()) {
      this._frequency = frequencyToSet;
      await this.sensor.set_reportFrequency(this._frequency);
      let lfreq = await this.sensor.get_logFrequency();
      try {
        if (lfreq != "OFF")
          await this.sensor.set_logFrequency(this._frequency);
        let m = await this.sensor.get_module();
        await m.saveToFlash();
      } catch (e) {
        logForm.log("failed to change " + this.hwdName + " log frequency (" + e.message + ")");
      }
    } else {
      this._online = false;
    }
  }
  set_frequency(frequencyToSet) {
    if (this._online) {
      this.updateFrequncy(frequencyToSet).then();
    }
  }
  get_recording() {
    return this._recording;
  }
  set_recording(recordingStatus) {
    this.updaterecording(recordingStatus).then();
  }
  async updaterecording(recordingStatus) {
    if (!this.dataLoggerFeature)
      return;
    if (await this.sensor.isOnline()) {
      this._recording = recordingStatus;
      try {
        await this.sensor.set_logFrequency(this._recording ? this._frequency : "OFF");
        let module = await this.sensor.get_module();
        let serial = await module.get_serialNumber();
        let dl = YDataLogger.FindDataLogger(serial + ".dataLogger");
        await dl.set_recording(this._recording ? YDataLogger.RECORDING_ON : YDataLogger.RECORDING_OFF);
        await dl.set_autoStart(this._recording ? YDataLogger.AUTOSTART_ON : YDataLogger.AUTOSTART_OFF);
        await module.saveToFlash();
      } catch (e) {
        logForm.log("failed to change " + this.hwdName + " recording (" + e.message + ")");
      }
    } else {
      this._online = false;
    }
  }
  reportDataloggerLoadProgress(progress) {
  }
  async load_DoWork(arg) {
    if (this._loadProcessIsBusy)
      return;
    this._loadProcessIsBusy = true;
    if (this.dataLoggerLoadCompleted)
      return;
    logForm.log(this.hwdName + " loading main data from datalogger");
    if (this.dataLoggerStartReadTime > 0) {
      this.recordedData = await this.sensor.get_recordedData(this.dataLoggerStartReadTime, 0);
    }
    let errCount = 0;
    let maxErrorCount = 10;
    let lastT = -1;
    let lastProgress = this.recordedDataLoadProgress;
    while (this.recordedDataLoadProgress < 100) {
      if (this._plzCancelDataloggerLoading) {
        this.loadDone = true;
        this.loadFailed = false;
        this.loadCanceled = true;
        this.setDataloggerLoadProgress(100);
        break;
      }
      try {
        this.recordedDataLoadProgress = await this.recordedData.loadMore();
      } catch (Exception) {
        this.loadFailed = true;
        return;
      }
      if (lastProgress != this.recordedDataLoadProgress) {
        lastProgress = this.recordedDataLoadProgress;
        let p = lastProgress;
        if (p < 1)
          p = 1;
        if (p > 99)
          p = 99;
        this.setDataloggerLoadProgress(p < 1 ? 1 : p > 99 ? 99 : p);
      }
      if (this.recordedDataLoadProgress - lastProgress >= 2) {
        lastProgress = this.recordedDataLoadProgress;
      }
    }
    let measures = await this.recordedData.get_measures();
    this.previewMinData = [];
    this.previewCurData = [];
    this.previewMaxData = [];
    for (let i = 0; i < measures.length; i++) {
      let t = measures[i].get_endTimeUTC();
      if (t >= arg.start && t <= arg.stop) {
        if (t < lastT && errCount < maxErrorCount) {
          logForm.log(this.hwdName + " loading warning:  timestamp going back in time " + t + "<" + lastT + (errCount < maxErrorCount - 1 ? "" : ", further similar errors wont be logged."));
          errCount++;
        } else if (t == lastT && errCount < maxErrorCount) {
          logForm.log(this.hwdName + " loading warning: duplicate timestamp  " + t + (errCount < maxErrorCount - 1 ? "" : ", further similar errors wont be logged."));
          errCount++;
        }
        lastT = t;
        if (this.previewMinData.length == 0 || t > this.previewMinData[this.previewMinData.length - 1].DateTime) {
          this.previewMinData.push(new TimedSensorValue(t, measures[i].get_minValue()));
          this.previewCurData.push(new TimedSensorValue(t, measures[i].get_averageValue()));
          this.previewMaxData.push(new TimedSensorValue(t, measures[i].get_maxValue()));
        }
      } else {
        let d = new Date(t * 1e3);
        let d1 = new Date(arg.start * 1e3);
        let d2 = new Date(arg.stop * 1e3);
        let dstr = d.toLocaleDateString() + " " + d.toLocaleTimeString();
        let d1str = d1.toLocaleDateString() + " " + d1.toLocaleTimeString();
        let d2str = d2.toLocaleDateString() + " " + d2.toLocaleTimeString();
        logForm.log(this.hwdName + " note: skipping measure at " + dstr + ", not in range " + d1str + " ... " + d2str);
      }
    }
    if (_CustomYSensor._MaxDataRecords > 0)
      this.previewDataCleanUp();
    for (let i = 0; i < this.previewMinData.length - 1; i++) {
      if (this.previewMinData[i].DateTime >= this.previewMinData[i + 1].DateTime) {
        throw new Error("Time-stamp inconsistency");
      }
    }
    if (this.previewCurData.length > 1) {
      logForm.log(this.hwdName + " loaded " + this.previewCurData.length.toString() + "/" + measures.length.toString() + " records over " + (this.previewCurData[this.previewCurData.length - 1].DateTime - this.previewCurData[0].DateTime).toFixed(3) + " sec, last timed stamp was " + this.previewCurData[this.previewCurData.length - 1].DateTime);
    } else {
      logForm.log(this.hwdName + " loaded " + this.previewCurData.length.toString() + " records");
    }
    if (this.previewMinData.length > 2) {
      let lastPreviewTimeStamp = this.previewMinData[this.previewMinData.length - 1].DateTime;
      let it = this.findMergeBoundaries(this.previewMinData);
      let deleteCount = it.MergeSourceStop - it.MergeSourceStart;
      this.minData = this.minData.slice(0, it.MergeSourceStart).concat(this.previewMinData, this.minData.slice(it.MergeSourceStart + deleteCount));
      this.curData = this.curData.slice(0, it.MergeSourceStart).concat(this.previewCurData, this.curData.slice(it.MergeSourceStart + deleteCount));
      this.maxData = this.maxData.slice(0, it.MergeSourceStart).concat(this.previewMaxData, this.maxData.slice(it.MergeSourceStart + deleteCount));
      this.firstDataloggerTimeStamp = this.curData[0].DateTime;
    }
    this.loadDone = true;
    this.loadFailed = false;
    let count = this.curData.length;
    if (count > 0) {
      if (this.curData[count - 1].DateTime > this.lastDataTimeStamp) {
        this.lastDataTimeStamp = this.curData[count - 1].DateTime;
        this.lastDataSource = "end of datalogger";
      }
    }
    this.load_Completed();
  }
  load_Completed() {
    logForm.log(this.hwdName + ".loadcompleted()");
    if (this.loadFailed) {
      logForm.log(this.hwdName + " : datalogger loading failed");
      this._loadProcessIsBusy = false;
      return;
    }
    if (this.loadCanceled) {
      logForm.log(this.hwdName + " : datalogger loading was canceled");
      this.previewMinData = [];
      this.previewCurData = [];
      this.previewMaxData = [];
      this.loadCanceled = false;
      this._loadProcessIsBusy = false;
      this.preloadDone = false;
      this.loadDone = false;
      return;
    }
    if (this.previewCurData.length <= 2) {
      this.preloadDone = false;
      this.loadDone = false;
    }
    logForm.log(this.hwdName + " : datalogger loading completed  (" + this.previewMinData.length + " rows)");
    this.resetDataloggerLoader();
    this.dataLoggerLoadCompleted = true;
    for (let i = 0; i < this.FormsToNotify.length; i++) {
      if (this.FormsToNotify[i] instanceof graphWidget) {
        this.FormsToNotify[i].DataloggerCompleted(this);
      }
    }
    this.setDataloggerLoadProgress(100);
  }
  get dataloggerWasUsed() {
    return this.dataLoggerLoadCompleted;
  }
  dataCleanUp() {
    if (_CustomYSensor._MaxDataRecords <= 0)
      return;
    let newsize = _CustomYSensor._MaxDataRecords * 90 / 100 >> 0;
    if (this.curData != null && _CustomYSensor._MaxDataRecords < this.curData.length) {
      this.minData.splice(0, this.minData.length - newsize);
      this.curData.splice(0, this.curData.length - newsize);
      this.maxData.splice(0, this.maxData.length - newsize);
    }
  }
  previewDataCleanUp() {
    if (_CustomYSensor._MaxDataRecords <= 0)
      return;
    let newsize = _CustomYSensor._MaxDataRecords * 90 / 100 >> 0;
    if (this.previewMinData != null && _CustomYSensor._MaxDataRecords < this.previewMinData.length) {
      this.previewMinData.splice(0, this.previewMinData.length - newsize);
      this.previewCurData.splice(0, this.previewCurData.length - newsize);
      this.previewMaxData.splice(0, this.previewMaxData.length - newsize);
    }
  }
  stopDataloggerloading() {
  }
  /*  protected load_ProgressChanged()
      {
         for (let i :number = 0; i < this.FormsToNotify.length; i++)
           if  (this.FormsToNotify[i] instanceof  YoctoVisualization.graphWidget)
            (this.FormsToNotify[i] as YoctoVisualization.graphWidget).DataLoggerProgress();
  
      }*/
  isOnline() {
    return this._online;
  }
  async reloadConfig() {
    if (this._online) {
      let ison = await this.sensor.isOnline();
      if (ison) {
        try {
          this.unit = await this.sensor.get_unit();
          this._friendlyname = await this.sensor.get_friendlyName();
          this.resolution = await this.sensor.get_resolution();
          this._frequency = await this.sensor.get_reportFrequency();
          this._readonly = await this.sensor.isReadOnly();
          if (this.dataLoggerFeature) {
            this._recording = await this.sensor.get_logFrequency() != "OFF";
          } else {
            this._recording = false;
          }
          this.lastGetConfig = YAPI.GetTickCount();
          this.mustReloadConfig = false;
        } catch (e) {
          logForm.log("reload configuration error: " + e.message);
        }
      } else {
        this._online = false;
      }
    }
  }
  get_unit() {
    if (this.cfgChgNotificationsSupported && !this.mustReloadConfig)
      return this.unit;
    if (this.lastGetConfig <= 0 || YAPI.GetTickCount() - this.lastGetConfig > 5e3)
      this.reloadConfig().then();
    return this.unit;
  }
  get_resolution() {
    if (this.cfgChgNotificationsSupported && !this.mustReloadConfig)
      return this.resolution;
    if (this.lastGetConfig <= 0 || YAPI.GetTickCount() - this.lastGetConfig > 5e3)
      this.reloadConfig().then();
    return this.resolution;
  }
  resetDataloggerLoader() {
    this._predloadProcessIsBusy = false;
    this._loadProcessIsBusy = false;
    this.dataLoggerLoadCompleted = false;
    this.preloadDone = false;
    this.loadDone = false;
    this._dataloggerLoadisRunning = false;
    this.previewMinData = [];
    this.previewCurData = [];
    this.previewMaxData = [];
  }
  loadDatalogger(start, stop) {
    if (!this.dataLoggerFeature)
      return;
    if (this._predloadProcessIsBusy)
      return;
    if (this._loadProcessIsBusy)
      return;
    if (constants.maxPointsPerDataloggerSerie < 0) {
      logForm.log(this.hwdName + " : datalogger access is disabled");
      return;
    }
    if (!this.preloadDone && this.dataLoggerFeature) {
      logForm.log(this.hwdName + " : start datalogger preloading");
      this.preload_DoWork(new DataLoggerBoundary(start, stop)).then();
    }
  }
  async arrival(dataloggerOn) {
    await this.configureSensor();
    this._online = true;
    await this.reloadConfig();
    let dt = await this.sensor.get_dataLogger();
    if (dt == null)
      return;
    let now = Math.floor((/* @__PURE__ */ new Date()).getTime() / 1e3);
    if (this.curData.length > 0 && this.dataLoggerLoadCompleted) {
      let end = await dt.get_timeUTC();
      let start = this.curData[this.curData.length - 1].DateTime;
      let duration = end - start;
      if (duration == 0) {
        start = this.curData[this.curData.length - 2].DateTime;
        duration = end - start;
      }
      if (duration > 1) {
        logForm.log(this.hwdName + " is back online trying to load " + duration.toFixed(3) + " sec of data from datalogger ");
        this.resetDataloggerLoader();
        this.loadDatalogger(start, end);
      }
    }
    if (this.isReadOnly)
      logForm.log(this.hwdName + " is read only");
    this.notifySensorStateChange();
    this.notifySensorArrival();
  }
  async startDataloggerload(source) {
    if (!this.dataLoggerFeature)
      return;
    if (this.sensor == null)
      return;
    if (this._loadProcessIsBusy)
      return;
    if (this._predloadProcessIsBusy)
      return;
    if (this.dataLoggerLoadCompleted) {
      source.DataloggerCompleted(this);
      return;
    }
    let dt = await this.sensor.get_dataLogger();
    if (dt == null)
      return;
    this.loadDatalogger(0, await dt.get_timeUTC());
  }
  notifySensorArrival() {
    for (let i = 0; i < this.FormsToNotify.length; i++) {
      this.FormsToNotify[i].SensorArrivalcallback(this);
    }
  }
  notifySensorStateChange() {
    for (let i = 0; i < this.FormsToNotify.length; i++) {
      this.FormsToNotify[i].SensorStateChangedcallback(this);
    }
  }
  removal() {
    this._online = false;
    this.forceUpdate();
    this.notifySensorStateChange();
  }
  async configureSensor() {
    logForm.log("Configuring  " + this.hwdName);
    if (!await this.sensor.isOnline())
      return;
    let mustSave = false;
    let olfreq = await this.sensor.get_logFrequency();
    let orfreq = await this.sensor.get_reportFrequency();
    let readOnly = await this.sensor.isReadOnly();
    let value = await this.sensor.get_currentValue();
    this._lastAvgValue = value;
    this._lastMinValue = value;
    this._lastMaxValue = value;
    let lfreq = olfreq;
    let rfreq = orfreq;
    let m = await this.sensor.get_module();
    for (let i = 0; i < await m.functionCount(); i++) {
      if (await m.functionType(i) == "DataLogger") {
        this.dataLoggerFeature = true;
      }
    }
    try {
      if (this.dataLoggerFeature) {
        let m2 = await this.sensor.get_module();
        let dl = YDataLogger.FindDataLogger(await m2.get_serialNumber() + ".dataLogger");
        let dataloggerOn = await dl.get_recording() != YDataLogger.RECORDING_OFF;
        let dataloggerAS = await dl.get_autoStart() != YDataLogger.AUTOSTART_OFF;
        if (!dataloggerOn) {
          lfreq = "OFF";
        }
        if (lfreq != "OFF") {
          rfreq = lfreq;
        } else if (rfreq == "OFF") {
          rfreq = "1/s";
        }
        if (lfreq != olfreq) {
          if (readOnly)
            throw new Error(this.hwdName + " is read only, cannot change its logFrequency from " + olfreq + " to " + lfreq);
          await this.sensor.set_logFrequency(lfreq);
          mustSave = true;
        }
        if (rfreq != orfreq) {
          if (readOnly)
            throw new Error(this.hwdName + " is read only, connot change its  reportFrequency from " + orfreq + " to " + rfreq);
          await this.sensor.set_reportFrequency(rfreq);
          mustSave = true;
        }
        if (lfreq != "OFF") {
          if (!dataloggerOn) {
            if (readOnly)
              throw new Error(this.hwdName + " is read only, cannot change set its  datalogger recording to ON");
            await dl.set_recording(YDataLogger.RECORDING_ON);
          }
          if (!dataloggerAS) {
            if (readOnly)
              throw new Error(this.hwdName + " is read only, cannot change set its datalogger autostart to ON");
            await dl.set_autoStart(YDataLogger.AUTOSTART_ON);
            mustSave = true;
          }
        }
      } else {
        lfreq = "OFF";
        if (rfreq == "OFF") {
          rfreq = "1/s";
          if (readOnly)
            throw new Error(this.hwdName + " is read only, connot change is reportFrequency from OFF to " + rfreq);
          await this.sensor.set_reportFrequency(rfreq);
          mustSave = true;
        }
      }
      if (mustSave)
        await m.saveToFlash();
    } catch (e) {
      logForm.log("failed to configure " + this.hwdName + "  (" + e.message + ")");
    }
    logForm.log("registering timed callback for  " + await this.sensor.get_hardwareId());
    await this.sensor.registerTimedReportCallback((callbacksource, M) => {
      this.TimedCallback(callbacksource, M);
    });
    this._recording = lfreq != "OFF";
    this._frequency = rfreq;
  }
  registerCallback(f) {
    if (this.FormsToNotify.indexOf(f) >= 0)
      return;
    this.FormsToNotify.push(f);
    return;
  }
  forceUpdate() {
    this.TimedCallback(this.sensor, null).then();
  }
  async TimedCallback(source, M) {
    this._online = true;
    if (M != null) {
      let t = M.get_endTimeUTC();
      if (this.firstLiveDataTimeStamp == 0)
        this.firstLiveDataTimeStamp = t;
      if (t > this.lastDataTimeStamp) {
        this.lastDataTimeStamp = t;
        this.lastDataSource = "last timedReport";
        this.consecutiveBadTimeStamp = 0;
      } else {
        this.consecutiveBadTimeStamp++;
        if (this.consecutiveBadTimeStamp < 10) {
          logForm.log(this.hwdName + ": ignoring bad timestamp " + t.toFixed(3) + " (previous " + this.lastDataSource + " at " + this.lastDataTimeStamp.toFixed(3) + ")");
        }
      }
      if (this.consecutiveBadTimeStamp == 0 || this.consecutiveBadTimeStamp >= 10) {
        this._lastAvgValue = M.get_averageValue();
        this._lastMinValue = M.get_minValue();
        this._lastMaxValue = M.get_maxValue();
        this.curData.push(new TimedSensorValue(t, this._lastAvgValue));
        this.minData.push(new TimedSensorValue(t, this._lastMinValue));
        this.maxData.push(new TimedSensorValue(t, this._lastMaxValue));
        if (_CustomYSensor._MaxDataRecords > 0)
          this.dataCleanUp();
      }
      for (let i = 0; i < this.Alarms.length; i++) {
        this.Alarms[i].check(M);
      }
      for (let i = 0; i < this.FormsToNotify.length; i++) {
        this.FormsToNotify[i].SensorValuecallback(this, M);
      }
    }
  }
  forgetForm(source) {
    for (let i = this.FormsToNotify.length - 1; i >= 0; i--) {
      if (source == this.FormsToNotify[i]) {
        this.FormsToNotify.splice(i, 1);
      }
    }
  }
  get_sensor() {
    return this.sensor;
  }
  get_hardwareId() {
    return this.hwdName;
  }
  get_friendlyName() {
    return this._friendlyname;
  }
  toString() {
    let name = this._friendlyname;
    if (this._readonly)
      name += " (readonly)";
    if (this._online)
      return name;
    return name + " (OFFLINE)";
  }
};
CustomYSensor._MaxDataRecords = 0;
CustomYSensor._MaxLoggerRecords = 0;
var NullYSensor = class extends CustomYSensor {
  constructor() {
    super(null, "", null);
    this.hwdName = "NOTAREALSENSOR";
    this._friendlyname = "NOTAREALSENSOR";
    this.dataLoggerFeature = false;
  }
  async preload_DoWork(arg) {
    return;
  }
  get dataloggerLoadisRunning() {
    return false;
  }
  get_unit() {
    return "";
  }
  registerCallback(Form) {
  }
  forceUpdate() {
  }
  get_frequency() {
    return "";
  }
  set_frequency(frequencyToSet) {
  }
  get_sensor() {
    return null;
  }
  toString() {
    return "(none)";
  }
  setAlarmCondition(index, condition) {
  }
  getAlarmCondition(index) {
    return 0;
  }
  setAlarmValue(index, value) {
  }
  getAlarmValue(index) {
    return 0;
  }
  setAlarmDelay(index, value) {
  }
  getAlarmDelay(index) {
    return 0;
  }
  setAlarmCommandline(index, value) {
  }
  getAlarmCommandline(index) {
    return "";
  }
};
var sensorsManager = class _sensorsManager {
  static async clearHublist() {
    for (let i = _sensorsManager._hubList.length - 1; i >= 0; i--) {
      if (_sensorsManager._hubList[i].removable) {
        await YAPI.UnregisterHub(_sensorsManager._hubList[i].get_fullUrl());
        _sensorsManager._hubList.splice(i, 1);
      }
    }
  }
  static registerChangeCallback(changeCallback) {
    this._changeCallback = changeCallback;
  }
  static registerChangeExternalCallback(changeCallback) {
    this._changeExternalCallback = changeCallback;
  }
  static forgetForm(source) {
    for (let i = 0; i < _sensorsManager.sensorList.length; i++) {
      _sensorsManager.sensorList[i].forgetForm(source);
    }
  }
  // load config data  from XML config file
  static async InitHubList(node) {
    let nodes = node.get_childsByIndex();
    let promises = [];
    for (let i = 0; i < nodes.length; i++) {
      if (nodes[i].Name.toUpperCase() == "HUB") {
        let h = Hub.HubFromXml(nodes[i]);
        let alreadthere = false;
        for (let j = 0; j < _sensorsManager._hubList.length; j++) {
          let url1 = h.get_connexionUrl();
          let url2 = _sensorsManager._hubList[j].get_fullUrlNoCredentials();
          if (url1 == url2)
            alreadthere = true;
        }
        if (!alreadthere) {
          _sensorsManager._hubList.push(h);
          promises.push(h.Connect());
        }
      }
    }
    await Promise.all(promises);
  }
  static get hubList() {
    return _sensorsManager._hubList;
  }
  static hubWasremoved(h) {
    for (let i = _sensorsManager._hubList.length - 1; i >= 0; i--) {
      if (_sensorsManager._hubList[i] == h)
        _sensorsManager._hubList.splice(i, 1);
    }
  }
  static newHubCreated(h) {
    let url = h.get_connexionUrl();
    for (let i = 0; i < _sensorsManager._hubList.length; i++) {
      if (_sensorsManager._hubList[i].get_connexionUrl() == url) {
        alert("This connection already exists");
        return false;
      }
    }
    _sensorsManager._hubList.push(h);
    return true;
  }
  static removeExtraInfoFromUrl(url) {
    let it = new URL(url);
    let res = it.hostname + it.pathname;
    if (res.slice(0, 4).toLowerCase() === "www.") {
      res = res.slice(4);
    }
    return res;
  }
  static async NetworkArrival(net) {
    logForm.log("Network device detected: " + await net.get_hardwareId());
    let ip = await net.get_ipAddress();
    let netname = await net.get_logicalName();
    let module = await net.get_module();
    let loginame = await module.get_logicalName();
    let url = _sensorsManager.removeExtraInfoFromUrl(await module.get_url());
    for (let i = 0; i < _sensorsManager._hubList.length; i++) {
      let str = _sensorsManager.removeExtraInfoFromUrl(_sensorsManager.hubList[i].get_fullUrl());
      if (str == url) {
        _sensorsManager.hubList[i].arrival(ip, netname, module, loginame);
      }
    }
  }
  //#ifndef READONLY
  static getXmlHublist() {
    let res = "";
    for (let i = 0; i < _sensorsManager._hubList.length; i++) {
      res += "    " + _sensorsManager._hubList[i].XmlCode() + "\n";
    }
    return res;
  }
  static getXMLSensorsConfig() {
    let res = "<Sensors>\n";
    _sensorsManager.sensorList.forEach((s) => {
      if (!(s instanceof NullYSensor)) {
        res = res + s.GetXmlData();
      }
    });
    res = res + "</Sensors>\n";
    return res;
  }
  //#endif
  static setKnownSensors(sensorXMLList) {
    this.KnownSensors = sensorXMLList;
  }
  static FindSensorLastLocalConfig(hwdId) {
    let SensorConfig = null;
    if (_sensorsManager.KnownSensors != null) {
      let childs = _sensorsManager.KnownSensors.get_childsByIndex();
      for (let i = 0; i < childs.length; i++) {
        let node = childs[i];
        if (node.Name == "Sensor") {
          let id = node.get_attributes()["ID"];
          if (id == hwdId) {
            SensorConfig = node;
          }
        }
      }
    }
    return SensorConfig;
  }
  static async deviceConfigChanged(m) {
    logForm.log("Configuration change on device  " + await m.get_serialNumber());
    let serialprefix = (await m.get_serialNumber()).substring(0, 8);
    for (let i = 0; i < _sensorsManager.sensorList.length; i++) {
      if (_sensorsManager.sensorList[i].get_hardwareId().substring(0, 8) == serialprefix) {
        await _sensorsManager.sensorList[i].ConfigHasChanged();
      }
    }
    if (_sensorsManager._changeCallback != null)
      _sensorsManager._changeCallback();
    if (_sensorsManager._changeExternalCallback != null) {
      let data = await _sensorsManager._changeExternalCallback(m);
      if (data != null)
        YWebPage.ConfigChanged(data);
    }
  }
  static async deviceArrival(m) {
    try {
      let count = await m.functionCount();
      let serial = await m.get_serialNumber();
      let luminosity = await m.get_luminosity();
      logForm.log("Device Arrival " + serial);
      let recording = false;
      for (let i = 0; i < count; i++) {
        let ftype = await m.functionType(i);
        let fid = await m.functionId(i);
        if (ftype == "Network") {
          let net = await YNetwork.FindNetwork(serial + "." + fid);
          await sensorsManager.NetworkArrival(net);
        } else if (ftype == "DataLogger") {
          let dlog = YDataLogger.FindDataLogger(serial + "." + fid);
          let state = await dlog.get_recording();
          if (state == YDataLogger.RECORDING_ON || state == YDataLogger.RECORDING_PENDING) {
            recording = true;
          }
        }
      }
      for (let i = 0; i < count; i++) {
        let fbasetype = await m.functionBaseType(i);
        let fid = await m.functionId(i);
        if (fbasetype == "Sensor") {
          let hwdID = serial + "." + fid;
          logForm.log("New sensor arrival: " + hwdID);
          let found = false;
          for (let j = 0; j < _sensorsManager.sensorList.length && !found; j++) {
            if (_sensorsManager.sensorList[j].get_hardwareId() == hwdID) {
              found = true;
              await _sensorsManager.sensorList[j].arrival(recording);
            }
          }
          if (!found) {
            let s = YSensor.FindSensor(hwdID);
            let hwd = await s.get_hardwareId();
            let cs = new CustomYSensor(s, hwd, _sensorsManager.FindSensorLastLocalConfig(hwd));
            _sensorsManager.sensorList.push(cs);
            await cs.configureSensor();
            YWebPage.refreshEditor();
            cs.notifySensorStateChange();
          }
        }
      }
      await m.registerConfigChangeCallback(_sensorsManager.deviceConfigChanged);
      setTimeout(() => {
        _sensorsManager.deviceConfigChanged(m);
      }, 100);
      if (_sensorsManager._changeCallback != null)
        _sensorsManager._changeCallback();
    } catch (e) {
      logForm.log("Device Arrival Error: " + e.message);
    }
    if (_sensorsManager._customArrivalCallback != null)
      _sensorsManager._customArrivalCallback(m);
  }
  static async deviceRemoval(m) {
    let serial = await m.get_serialNumber();
    logForm.log("Device removal " + serial);
    _sensorsManager.sensorList.forEach((alreadyThereSensor) => {
      if (!(alreadyThereSensor instanceof NullYSensor)) {
        let hwd = alreadyThereSensor.get_hardwareId();
        if (hwd.length >= serial.length) {
          if (hwd.substring(0, serial.length) == serial) {
            alreadyThereSensor.removal();
          }
        }
      }
    });
    if (_sensorsManager._changeCallback != null)
      _sensorsManager._changeCallback();
    if (_sensorsManager._customRemovalCallback != null)
      _sensorsManager._customRemovalCallback(m);
  }
  static RegisterDeviceArrivalCallback(arrivalCallback) {
    _sensorsManager._customArrivalCallback = arrivalCallback;
  }
  static RegisterDeviceRemovalCallback(removalCallback) {
    _sensorsManager._customRemovalCallback = removalCallback;
  }
  static AddNewSensor(hwdID) {
    for (let i = 0; i < _sensorsManager.sensorList.length; i++) {
      if (_sensorsManager.sensorList[i] != null) {
        if (_sensorsManager.sensorList[i].get_hardwareId() == hwdID)
          return _sensorsManager.sensorList[i];
      }
    }
    let s = YSensor.FindSensor(hwdID);
    let cs = new CustomYSensor(s, hwdID, _sensorsManager.FindSensorLastLocalConfig(hwdID));
    _sensorsManager.sensorList.push(cs);
    return cs;
  }
  static getNullSensor() {
    return _sensorsManager.NullSensor;
  }
  static async UpdateDeviceList() {
    let err = new YErrorMsg();
    try {
      if (await YAPI.UpdateDeviceList(err) != YAPI_SUCCESS) {
        logForm.log("UpdateDeviceList failed :" + err.msg);
      }
    } catch (e) {
      logForm.log("UpdateDeviceList failed :" + e.message);
    }
    let hub = YHub.FirstHubInUse();
    while (hub != null) {
      let lastError = await hub.get_errorType();
      if (lastError != YAPI_SUCCESS) {
        let logline = "Cannot connect to " + await hub.get_connectionUrl() + " (" + await hub.get_errorMessage() + ")";
        if (lastError == YAPI_UNAUTHORIZED) {
          for (let i = 0; i < this._hubList.length; i++) {
            if (await this._hubList[i].matches(hub) && !this._hubList[i].removable) {
              logline += ". Update hub credentials in 'Global configuration' window.";
            }
          }
        }
        logForm.log(logline);
      }
      hub = hub.nextHubInUse();
    }
  }
  static async _runAsync() {
    let errmsg = new YErrorMsg();
    await YAPI.RegisterDeviceArrivalCallback((m) => {
      _sensorsManager.deviceArrival(m);
    });
    await YAPI.RegisterDeviceRemovalCallback((m) => {
      _sensorsManager.deviceRemoval(m);
    });
    await _sensorsManager.UpdateDeviceList();
    setInterval(() => {
      _sensorsManager.UpdateDeviceList();
    }, 2e3);
  }
  static run() {
    _sensorsManager.NullSensor = new NullYSensor();
    _sensorsManager.sensorList = [];
    _sensorsManager.sensorList.push(_sensorsManager.NullSensor);
    this._runAsync().then();
  }
};
sensorsManager.counter = 0;
sensorsManager.KnownSensors = null;
sensorsManager._hubList = [];
sensorsManager._customArrivalCallback = null;
sensorsManager._customRemovalCallback = null;
sensorsManager._changeCallback = null;
sensorsManager._changeExternalCallback = null;

// obj/full/formManager.js
var ResizeMoveHandle = class _ResizeMoveHandle {
  constructor(container, index, mouseDownListener, touchStartlistener) {
    this.index = index;
    this.container = container;
    this.isactive = false;
    this.div = document.createElement("DIV");
    let size = _ResizeMoveHandle.HANDLESIZE - 2;
    if (this.index == _ResizeMoveHandle.RESIZEHANDLE_CENTER)
      size = size * 2;
    this.div.style.position = "absolute";
    this.div.style.border = "0px solid black";
    this.div.style.background = "transparent";
    this.div.style.pointerEvents = "auto";
    this.div.style.width = size.toString() + "px";
    this.div.style.height = size.toString() + "px";
    switch (index) {
      case _ResizeMoveHandle.RESIZEHANDLE_TOPLEFT:
        this.div.style.cursor = "nw-resize";
        this.div.style.left = "0px";
        this.div.style.top = "0px";
        break;
      case _ResizeMoveHandle.RESIZEHANDLE_TOPCENTER:
        this.div.style.cursor = "n-resize";
        this.div.style.top = "0px";
        this.div.style.left = "50%";
        this.div.style.transform = "translate(-50%,0)";
        break;
      case _ResizeMoveHandle.RESIZEHANDLE_TOPRIGHT:
        this.div.style.cursor = "ne-resize";
        this.div.style.right = "0px";
        this.div.style.top = "0px";
        break;
      case _ResizeMoveHandle.RESIZEHANDLE_CENTERRIGHT:
        this.div.style.cursor = "e-resize";
        this.div.style.right = "0px";
        this.div.style.top = "50%";
        this.div.style.transform = "translate(0,-50%)";
        break;
      case _ResizeMoveHandle.RESIZEHANDLE_BOTTOMRIGHT:
        this.div.style.cursor = "se-resize";
        this.div.style.right = "0px";
        this.div.style.bottom = "0px";
        break;
      case _ResizeMoveHandle.RESIZEHANDLE_BOTTOMCENTER:
        this.div.style.cursor = "s-resize";
        this.div.style.bottom = "0px";
        this.div.style.left = "50%";
        this.div.style.transform = "translate(-50%,0)";
        break;
      case _ResizeMoveHandle.RESIZEHANDLE_BOTTOMLEFT:
        this.div.style.cursor = "sw-resize";
        this.div.style.left = "0px";
        this.div.style.bottom = "0px";
        break;
      case _ResizeMoveHandle.RESIZEHANDLE_CENTERLEFT:
        this.div.style.cursor = "w-resize";
        this.div.style.left = "0px";
        this.div.style.top = "50%";
        this.div.style.transform = "translate(0,-50%)";
        break;
      case _ResizeMoveHandle.RESIZEHANDLE_CENTER:
        this.div.style.cursor = "move";
        this.div.style.left = "50%";
        this.div.style.top = "50%";
        this.div.style.transform = "translate(-50%,-50%)";
        break;
    }
    this.container.appendChild(this.div);
    this.div.addEventListener("mousedown", mouseDownListener);
    this.div.addEventListener("touchstart", touchStartlistener);
    this.svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    this.svg.setAttribute("viewBox", "0 0 " + size + " " + size);
    this.svg.setAttribute("version", "1.1");
    this.svg.setAttribute("width", size.toString());
    this.svg.setAttribute("height", size.toString());
    let x = 0;
    let y = 0;
    let r = size;
    switch (this.index) {
      case _ResizeMoveHandle.RESIZEHANDLE_TOPLEFT:
        break;
      case _ResizeMoveHandle.RESIZEHANDLE_TOPCENTER:
        x = size / 2;
        break;
      case _ResizeMoveHandle.RESIZEHANDLE_TOPRIGHT:
        x = size;
        break;
      case _ResizeMoveHandle.RESIZEHANDLE_CENTERRIGHT:
        x = size;
        y = size / 2;
        break;
      case _ResizeMoveHandle.RESIZEHANDLE_BOTTOMRIGHT:
        x = size;
        y = size;
        break;
      case _ResizeMoveHandle.RESIZEHANDLE_BOTTOMCENTER:
        x = size / 2;
        y = size;
        break;
      case _ResizeMoveHandle.RESIZEHANDLE_BOTTOMLEFT:
        y = size;
        break;
      case _ResizeMoveHandle.RESIZEHANDLE_CENTERLEFT:
        y = size / 2;
        break;
      case _ResizeMoveHandle.RESIZEHANDLE_CENTER:
        x = size / 2;
        y = size / 2;
        r = size / 2;
        break;
    }
    this.circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
    this.circle.setAttribute("cx", x.toString());
    this.circle.setAttribute("cy", y.toString());
    this.circle.setAttribute("r", r.toString());
    this.circle.setAttribute("style", "fill:white");
    this.circle.setAttribute("opacity", "0.75");
    this.svg.appendChild(this.circle);
    this.arrow = document.createElementNS("http://www.w3.org/2000/svg", "path");
    let d = "";
    for (let i = 0; i < _ResizeMoveHandle.RESIZEHANDLE_data[index].length >> 1; i++) {
      let x2 = 1 + (size - 2) * _ResizeMoveHandle.RESIZEHANDLE_data[index][i << 1] / 12;
      let y2 = 1 + (size - 2) * _ResizeMoveHandle.RESIZEHANDLE_data[index][1 + (i << 1)] / 12;
      d += i == 0 ? "M " : "L ";
      d += Math.round(x2).toString() + " " + Math.round(y2).toString() + " ";
    }
    d += "z";
    this.arrow.setAttribute("d", d);
    this.arrow.setAttribute("fill", _ResizeMoveHandle.inactiveColor);
    this.svg.appendChild(this.arrow);
    this.div.appendChild(this.svg);
    this.container.appendChild(this.div);
  }
  get active() {
    return this.isactive;
  }
  set active(value) {
    if (value != this.isactive) {
      this.isactive = value;
      this.arrow.setAttribute("fill", this.active ? _ResizeMoveHandle.activeColor : _ResizeMoveHandle.inactiveColor);
    }
  }
};
ResizeMoveHandle.HANDLESIZE = Math.round(20 * (1 + 2 * (constants.guiDPIFactor - 1)));
ResizeMoveHandle.activeColor = "lightgreen";
ResizeMoveHandle.inactiveColor = "grey";
ResizeMoveHandle.RESIZEHANDLE_TOPLEFT = 0;
ResizeMoveHandle.RESIZEHANDLE_TOPCENTER = 1;
ResizeMoveHandle.RESIZEHANDLE_TOPRIGHT = 2;
ResizeMoveHandle.RESIZEHANDLE_CENTERRIGHT = 3;
ResizeMoveHandle.RESIZEHANDLE_BOTTOMRIGHT = 4;
ResizeMoveHandle.RESIZEHANDLE_BOTTOMCENTER = 5;
ResizeMoveHandle.RESIZEHANDLE_BOTTOMLEFT = 6;
ResizeMoveHandle.RESIZEHANDLE_CENTERLEFT = 7;
ResizeMoveHandle.RESIZEHANDLE_CENTER = 8;
ResizeMoveHandle.RESIZEHANDLE_data = [
  [0, 0, 0, 7, 2, 5, 7, 10, 10, 7, 5, 2, 7, 0],
  [6, 0, 11, 5, 8, 5, 8, 12, 4, 12, 4, 5, 1, 5],
  [12, 0, 12, 7, 10, 5, 5, 10, 2, 7, 7, 2, 5, 0],
  [12, 6, 7, 11, 7, 8, 0, 8, 0, 4, 7, 4, 7, 1],
  [12, 12, 5, 12, 7, 10, 2, 5, 5, 2, 10, 7, 12, 5],
  [6, 12, 1, 7, 4, 7, 4, 0, 8, 0, 8, 7, 11, 7],
  [0, 12, 0, 5, 2, 7, 7, 2, 10, 5, 5, 10, 7, 12],
  [0, 6, 5, 1, 5, 4, 12, 4, 12, 8, 5, 8, 5, 11],
  [
    6,
    0,
    8,
    2,
    7,
    2,
    7,
    5,
    10,
    5,
    10,
    4,
    12,
    6,
    10,
    8,
    10,
    7,
    7,
    7,
    7,
    10,
    8,
    10,
    6,
    12,
    4,
    10,
    5,
    10,
    5,
    7,
    2,
    7,
    2,
    8,
    0,
    6,
    2,
    4,
    2,
    5,
    5,
    5,
    5,
    2,
    4,
    2
  ]
];
var YWidget = class _YWidget {
  static log(st) {
    logForm.log(st);
  }
  SourceChanged(src, index) {
  }
  loadRecordedDataIfNeeded() {
  }
  removeDataloggerData() {
  }
  //#ifndef READONLY
  delete() {
    this.editStopped();
    sensorsManager.forgetForm(this);
    this.destroy();
  }
  get confirmDeleteString() {
    return "not defined";
  }
  //#endif
  snapshot() {
    let w = 1024;
    let h = 1024;
    let ratio = this.UIContainer.offsetHeight != 0 ? this.UIContainer.offsetWidth / this.UIContainer.offsetHeight : 1;
    switch (constants.captureSizePolicy) {
      case YDataRenderer.CaptureFormats.Keep:
        w = this.UIContainer.offsetWidth;
        h = this.UIContainer.offsetHeight;
        break;
      case YDataRenderer.CaptureFormats.FixedHeight:
        h = constants.captureHeight;
        w = h * ratio;
        break;
      case YDataRenderer.CaptureFormats.FixedWidth:
        w = constants.captureWidth;
        h = w / ratio;
        break;
      case YDataRenderer.CaptureFormats.Fixed:
        w = constants.captureWidth;
        h = constants.captureHeight;
        break;
    }
    this._genRenderer.captureAndDownloadImage(constants.captureType, "", w, h, constants.captureDPI);
  }
  get BackColor() {
    return this._BackColor;
  }
  set BackColor(value) {
    this._BackColor = value;
    this.UIContainer.style.backgroundColor = this._BackColor.htmlCode;
  }
  get BorderColor() {
    return this._BorderColor;
  }
  set BorderColor(value) {
    this._BorderColor = value;
    this.UIContainer.style.border = "1px solid " + this._BorderColor.htmlCode;
  }
  get parentWidth() {
    if (this.UIContainer.parentNode == document.body || this.UIContainer.parentNode == null)
      return window.innerWidth;
    return this.UIContainer.parentNode.clientWidth;
  }
  get parentHeight() {
    if (this.UIContainer.parentNode == document.body || this.UIContainer.parentNode == null)
      return window.innerHeight;
    return this.UIContainer.parentNode.clientHeight;
  }
  get relativePositionX() {
    if (this._SizeIsRelative)
      return this._relativePositionX;
    return this.sizeRound(100 * this.UIContainer.offsetLeft / this.parentWidth);
  }
  set relativePositionX(value) {
    this._relativePositionX = value;
    this._PositionX = Math.round(value * this.parentWidth / 100);
  }
  set relativePositionY(value) {
    this._relativePositionY = value;
    this._PositionY = Math.round(value * this.parentHeight / 100);
  }
  get relativePositionY() {
    if (this._SizeIsRelative)
      return this._relativePositionY;
    return this.sizeRound(100 * this.UIContainer.offsetTop / this.parentHeight);
  }
  get PositionX() {
    if (this._SizeIsRelative)
      return this.relativePositionX;
    return this.UIContainer.offsetLeft;
  }
  set PositionX(value) {
    if (this._SizeIsRelative)
      this.relativePositionX = value;
    else
      this._PositionX = value;
    this.UIContainer.style.left = this._PositionX.toString() + "px";
    if (this._genRenderer != null)
      this._genRenderer.clearTransformationMatrix();
    this.DrawHandles(this.UIContainer.offsetLeft, this.UIContainer.offsetTop, this.UIContainer.offsetWidth, this.UIContainer.offsetHeight);
  }
  get PositionY() {
    if (this._SizeIsRelative)
      return this.relativePositionY;
    return this.UIContainer.offsetTop;
  }
  set PositionY(value) {
    if (this._SizeIsRelative)
      this.relativePositionY = value;
    else
      this._PositionY = value;
    this.UIContainer.style.top = this._PositionY.toString() + "px";
    if (this._genRenderer != null)
      this._genRenderer.clearTransformationMatrix();
    this.DrawHandles(this.UIContainer.offsetLeft, this.UIContainer.offsetTop, this.UIContainer.offsetWidth, this.UIContainer.offsetHeight);
  }
  sizeRound(v) {
    return Math.round(100 * v) / 100;
  }
  get relativeWidth() {
    if (this._SizeIsRelative)
      return this._relativeWidth;
    return this.sizeRound(100 * this.UIContainer.offsetWidth / this.parentWidth);
  }
  set relativeWidth(value) {
    if (value <= 0)
      throw new Error("Value must be strictly positive");
    this._relativeWidth = value;
    this._Width = Math.round(value * this.parentWidth / 100);
  }
  set relativeHeight(value) {
    if (value <= 0)
      throw new Error("Value must be strictly positive");
    this._relativeHeight = value;
    this._Height = Math.round(value * this.parentHeight / 100);
  }
  get relativeHeight() {
    if (this._SizeIsRelative)
      return this._relativeHeight;
    return this.sizeRound(100 * this.UIContainer.offsetHeight / this.parentHeight);
  }
  updateRelativeSize() {
    if (Math.abs(this._relativeWidth * this.parentWidth / 100 - this.UIContainer.offsetWidth) > 1)
      this._relativeWidth = this.sizeRound(100 * this.UIContainer.offsetWidth / this.parentWidth);
    if (Math.abs(this._relativeHeight * this.parentHeight / 100 - this.UIContainer.offsetHeight) > 1)
      this._relativeHeight = this.sizeRound(100 * this.UIContainer.offsetHeight / this.parentHeight);
    if (Math.abs(this._relativePositionX * this.parentWidth / 100 - this.UIContainer.offsetLeft) > 1)
      this._relativePositionX = this.sizeRound(100 * this.UIContainer.offsetLeft / this.parentWidth);
    if (Math.abs(this._relativePositionY * this.parentHeight / 100 - this.UIContainer.offsetTop) > 1)
      this._relativePositionY = this.sizeRound(100 * this.UIContainer.offsetTop / this.parentHeight);
  }
  get Width() {
    if (this._SizeIsRelative)
      return this.relativeWidth;
    else
      return this.UIContainer.offsetWidth;
  }
  set Width(value) {
    if (value <= 0)
      throw new Error("Value must be strictly positive");
    if (this._SizeIsRelative)
      this.relativeWidth = value;
    else
      this._Width = value;
    this.UIContainer.style.width = (this._Width - 2).toString() + "px";
    this.UIContainer.width = this._Width - 2;
    if (this._genRenderer != null)
      this._genRenderer.clearTransformationMatrix();
    this.DrawHandles(this.UIContainer.offsetLeft, this.UIContainer.offsetTop, this.UIContainer.offsetWidth, this.UIContainer.offsetHeight);
    this.containerResized();
  }
  get Height() {
    if (this._SizeIsRelative)
      return this.relativeHeight;
    else
      return this.UIContainer.offsetHeight;
  }
  set Height(value) {
    if (value <= 0)
      throw new Error("Value must be strictly positive");
    if (this._SizeIsRelative)
      this.relativeHeight = value;
    else
      this._Height = value;
    this.UIContainer.style.height = (this._Height - 2).toString() + "px";
    this.UIContainer.height = this._Height - 2;
    if (this._genRenderer != null)
      this._genRenderer.clearTransformationMatrix();
    this.DrawHandles(this.UIContainer.offsetLeft, this.UIContainer.offsetTop, this.UIContainer.offsetWidth, this.UIContainer.offsetHeight);
    this.containerResized();
  }
  get SizeIsRelative() {
    return this._SizeIsRelative;
  }
  set SizeIsRelative(value) {
    if (this._SizeIsRelative != value) {
      this._SizeIsRelative = value;
      if (this._SizeIsRelative) {
        this._relativePositionX = this.sizeRound(100 * this.UIContainer.offsetLeft / this.parentWidth);
        this._relativePositionY = this.sizeRound(100 * this.UIContainer.offsetTop / this.parentHeight);
        this._relativeWidth = this.sizeRound(100 * this.UIContainer.offsetWidth / this.parentWidth);
        this._relativeHeight = this.sizeRound(100 * this.UIContainer.offsetHeight / this.parentHeight);
      }
      this.windowResized();
    }
  }
  get containerID() {
    return this._containerID;
  }
  set containerID(value) {
    if (this._containerID == value)
      return;
    let target = value != "" ? document.getElementById(value) : null;
    if (value != "" && target == null)
      throw new Error("No HTMLElement on the page with such ID (" + value + ")");
    if (this.UIContainer.parentNode != null) {
      this.UIContainer.parentNode.removeChild(this.UIContainer);
    } else {
      document.body.removeChild(this.UIContainer);
    }
    if (target != null) {
      this.UIContainer.style.position = "relative";
      target.insertBefore(this.UIContainer, target.firstChild);
    } else {
      this.UIContainer.style.position = "absolute";
      document.body.insertBefore(this.UIContainer, document.body.firstChild);
    }
    this._genRenderer.clearTransformationMatrix();
    this.windowResized();
    this.DrawHandles(this.UIContainer.offsetLeft, this.UIContainer.offsetTop, this.UIContainer.offsetWidth, this.UIContainer.offsetHeight);
    this._containerID = value;
  }
  get Text() {
    return this._Text;
  }
  set Text(value) {
    this._Text = value;
    this.UIContainer.title = value;
  }
  set_name(name) {
    this.Text = name;
    this._genProp.Form_Text = name;
  }
  windowResized() {
    this._genRenderer.DisableRedraw();
    if (this._SizeIsRelative) {
      this.PositionX = this._relativePositionX;
      this.PositionY = this._relativePositionY;
      this.Height = this._relativeHeight;
      this.Width = this._relativeWidth;
    }
    this.containerResized();
    this._genRenderer.AllowRedraw();
  }
  ApplyRelativeSizeIfRequired() {
    if (this._initialContainerID != "") {
      if (document.getElementById(this._initialContainerID) != null) {
        this.containerID = this._initialContainerID;
      } else {
        logForm.log('Warning: cannot place widget in "' + this._initialContainerID + '" : no such HTML element.');
      }
    }
    this._SizeIsRelative = this._InitialSizeIsRelative;
    this.windowResized();
  }
  destroy() {
    window.removeEventListener("resize", this._windowResizeCallback);
    this._genRenderer.destroy();
    Object.entries(this).forEach((pair) => {
      Reflect.set(this, pair[0], null);
    });
  }
  getContainerOffset() {
    let it = this.UIContainer.offsetParent;
    let absx = 0;
    let absy = 0;
    while (it) {
      absy += it.offsetTop;
      absx += it.offsetLeft;
      it = it.offsetParent;
    }
    return new pointXY(absx, absy);
  }
  contextMenuCallBack(mouseX, mouseY) {
    let p = this._genRenderer.Scr2ElmMatrix.multiplyByV(Vector3.FromXYCoord(mouseX, mouseY)).toPoint();
    if (p.X >= 0 && p.X <= this.UIContainer.offsetWidth && p.Y >= 0 && p.Y <= this.UIContainer.offsetHeight) {
      this.contextMenuIsOpening();
    }
  }
  contextMenuIsOpening() {
  }
  constructor(node, editor, default_x, default_y, default_width, default_height) {
    this._genProp = null;
    this._genRenderer = null;
    this._BackColor = YColor.Transparent;
    this._BorderColor = YColor.Gray;
    this._relativePositionX = 0;
    this._relativePositionY = 0;
    this._PositionX = 0;
    this._PositionY = 0;
    this._relativeWidth = 100;
    this._relativeHeight = 100;
    this._Width = 300;
    this._Height = 0;
    this._InitialSizeIsRelative = false;
    this._SizeIsRelative = false;
    this._initialContainerID = "";
    this._containerID = "";
    this._Text = "";
    this.resizeCaptureRunning = false;
    this.resizeCaptureXOrigineMouse = 0;
    this.resizeCaptureYOrigineMouse = 0;
    this.resizeCaptureXOrigine = 0;
    this.resizeCaptureYOrigine = 0;
    this.resizeCaptureOriginalTop = 0;
    this.resizeCaptureOriginalLeft = 0;
    this.resizeCaptureOriginalWidth = 0;
    this.resizeCaptureOriginalHeight = 0;
    this.resizeCaptureHandleIndex = 0;
    this.resizeCaptureOriginalMatrix = null;
    this.resizeCaptureOriginalMatrixInv = null;
    this.previousTRansform = "";
    this.previousWidth = 0;
    this.previousHeight = 0;
    let left = typeof default_x != "undefined" ? default_x : 0;
    let top = typeof default_y != "undefined" ? default_y : 0;
    let width = typeof default_width != "undefined" ? default_width : 0;
    let height = typeof default_height != "undefined" ? default_height : 0;
    let bottom = Number.NaN;
    let right = Number.NaN;
    if (node != null) {
      let childnodes = node.get_childsByName();
      if ("location" in childnodes) {
        let it = childnodes["location"];
        let attributes = it.get_attributes();
        if ("x" in attributes)
          left = parseInt(attributes["x"]);
        if ("y" in attributes)
          top = parseInt(attributes["y"]);
      }
      if ("size" in childnodes) {
        let it = childnodes["size"];
        let attributes = it.get_attributes();
        if ("w" in attributes)
          width = parseInt(attributes["w"]);
        if ("h" in attributes)
          height = parseInt(attributes["h"]);
      }
      if ("relativeCoord" in childnodes) {
        let it = childnodes["relativeCoord"];
        let attributes = it.get_attributes();
        if ("x" in attributes)
          this._relativePositionX = this.sizeRound(parseFloat(attributes["x"]));
        if ("y" in attributes)
          this._relativePositionY = this.sizeRound(parseFloat(attributes["y"]));
        if ("w" in attributes)
          this._relativeWidth = this.sizeRound(parseFloat(attributes["w"]));
        if ("h" in attributes)
          this._relativeHeight = this.sizeRound(parseFloat(attributes["h"]));
        if ("active" in attributes)
          this._InitialSizeIsRelative = attributes["active"].toUpperCase() == "TRUE";
      }
      if ("container" in childnodes) {
        let it = childnodes["container"];
        let attributes = it.get_attributes();
        if ("id" in attributes)
          this._initialContainerID = attributes["id"];
      }
    }
    this.UIContainer = document.createElement("CANVAS");
    this.UIContainer.style.position = "absolute";
    _YWidget.currentEdited = null;
    let h = window.innerHeight;
    let w = window.innerWidth;
    this.PositionX = this._InitialSizeIsRelative ? Math.round(w * (this._relativePositionX / 100)) : left;
    this.PositionY = this._InitialSizeIsRelative ? Math.round(h * (this._relativePositionY / 100)) : top;
    this.Width = this._InitialSizeIsRelative ? Math.round(w * (this._relativeWidth / 100)) : width;
    this.Height = this._InitialSizeIsRelative ? Math.round(h * (this._relativeHeight / 100)) : height;
    this.UIContainer.style.border = "1px solid black";
    this.UIContainer.style.backgroundColor = this._BackColor.htmlCode;
    this.UIContainer.setAttribute("name", "YoctoVisualizationWidget");
    document.body.appendChild(this.UIContainer);
    this._windowResizeCallback = () => this.windowResized();
    window.addEventListener("resize", this._windowResizeCallback);
  }
  rearrangeZindexes() {
    let containers = document.getElementsByName("YoctoVisualizationWidget");
    let n = 0;
    for (let i = 0; i < containers.length; i++) {
      if (containers[i] != this.UIContainer) {
        containers[i].style.zIndex = n.toString();
        n++;
      }
    }
    this.UIContainer.style.zIndex = n.toString();
    n++;
    _YWidget.HandlesDiv.style.zIndex = n.toString();
  }
  SensorArrivalcallback(source) {
  }
  SensorStateChangedcallback(source) {
    if (_YWidget.currentEdited == this) {
      YWebPage.refreshEditor();
    }
  }
  SensorValuecallback(source, M) {
  }
  get isBeingEdited() {
    return _YWidget.currentEdited == this;
  }
  static stopEdition() {
    if (_YWidget.currentEdited != null) {
      _YWidget.currentEdited.editStopped();
      _YWidget.currentEdited = null;
    }
  }
  refreshProperties() {
  }
  edit() {
    _YWidget.stopEdition();
    _YWidget.currentEdited = this;
    if (_YWidget.editHandles.length <= 0) {
      _YWidget.HandlesDiv = document.createElement("DIV");
      _YWidget.HandlesDiv.style.position = "absolute";
      _YWidget.HandlesDiv.style.left = "0px";
      _YWidget.HandlesDiv.style.top = "0px";
      _YWidget.HandlesDiv.style.backgroundColor = "transparent";
      _YWidget.HandlesDiv.style.transformOrigin = "top left";
      _YWidget.HandlesDiv.style.pointerEvents = "none";
      for (let i = 0; i < 9; i++) {
        _YWidget.editHandles.push(new ResizeMoveHandle(_YWidget.HandlesDiv, i, (e) => {
          if (_YWidget.currentEdited != null)
            _YWidget.currentEdited.mouseStartCapture(e, i);
        }, (e) => {
          if (_YWidget.currentEdited != null) {
            _YWidget.currentEdited.touchStartCapture(e, i);
          }
        }));
      }
      document.body.appendChild(_YWidget.HandlesDiv);
      document.addEventListener("touchmove", (e) => {
        if (_YWidget.currentEdited != null)
          _YWidget.currentEdited.touchCaptureRun(e);
      }, { passive: false });
      document.addEventListener("touchend", (e) => {
        if (_YWidget.currentEdited != null)
          _YWidget.currentEdited.touchCaptureStop(e);
      }, { passive: false });
      document.addEventListener("mousemove", (e) => {
        if (_YWidget.currentEdited != null)
          _YWidget.currentEdited.mouseCaptureRun(e);
      }, { passive: false });
      document.addEventListener("mouseup", (e) => {
        if (_YWidget.currentEdited != null)
          _YWidget.currentEdited.mouseCaptureStop(e);
      }, { passive: false });
    }
    this.DrawHandles(this.UIContainer.offsetLeft, this.UIContainer.offsetTop, this.UIContainer.offsetWidth, this.UIContainer.offsetHeight);
    _YWidget.HandlesDiv.style.display = "";
    this.rearrangeZindexes();
  }
  DrawHandles(offsetLeft, offsetTop, offsetWidth, offsetHeight) {
    if (_YWidget.currentEdited == null)
      return;
    if (_YWidget.editHandles.length <= 0)
      return;
    _YWidget.HandlesDiv.style.width = offsetWidth.toString() + "px";
    _YWidget.HandlesDiv.style.height = offsetHeight.toString() + "px";
    if (this._genRenderer == null)
      debugger;
    let borderWidth = parseFloat(this.UIContainer.style.border);
    let borderMatrix = Matrix3x3.newTranslateMatrix(-borderWidth, -borderWidth);
    _YWidget.HandlesDiv.style.transform = borderMatrix.multiplyByM(this._genRenderer.Elm2ScrMatrix).toCSS();
    _YWidget.HandlesDiv.style.transform = "top left";
  }
  //#endif
  updateWindowPositionProperties(prop) {
    prop.Form_PositionX = this.PositionX;
    prop.Form_PositionY = this.PositionY;
    prop.Form_Width = this.Width;
    prop.Form_Height = this.Height;
  }
  touchStartCapture(e, handleIndex) {
    if (e.touches.length == 1) {
      this.handleMouseStartCapture(e.touches[0].pageX, e.touches[0].pageY, handleIndex);
      e.preventDefault();
    }
  }
  mouseStartCapture(e, handleIndex) {
    if (e.button != 0)
      return;
    this.handleMouseStartCapture(e.pageX, e.pageY, handleIndex);
  }
  handleMouseStartCapture(pageX, pageY, handleIndex) {
    this.resizeCaptureOriginalMatrix = this._genRenderer.Scr2ElmMatrix;
    this.resizeCaptureOriginalMatrixInv = this._genRenderer.Elm2ScrMatrix;
    let p = this.resizeCaptureOriginalMatrix.multiplyByV(Vector3.FromXYCoord(pageX, pageY)).toPoint();
    this.resizeCaptureRunning = true;
    this.resizeCaptureXOrigine = p.X;
    this.resizeCaptureYOrigine = p.Y;
    this.resizeCaptureOriginalLeft = this.UIContainer.offsetLeft;
    this.resizeCaptureOriginalTop = this.UIContainer.offsetTop;
    this.resizeCaptureOriginalWidth = this.UIContainer.offsetWidth;
    this.resizeCaptureOriginalHeight = this.UIContainer.offsetHeight;
    this.resizeCaptureHandleIndex = handleIndex;
    _YWidget.editHandles[this.resizeCaptureHandleIndex].active = true;
    this.previousTRansform = _YWidget.HandlesDiv.style.transform;
  }
  touchCaptureRun(e) {
    if (e.touches.length == 1) {
      this.handleCaptureRun(e.touches[0].pageX, e.touches[0].pageY);
      e.preventDefault();
    }
  }
  mouseCaptureRun(e) {
    this.handleCaptureRun(e.pageX, e.pageY);
  }
  handleCaptureRun(pageX, pageY) {
    if (!this.resizeCaptureRunning)
      return;
    let p = this.resizeCaptureOriginalMatrix.multiplyByV(Vector3.FromXYCoord(pageX, pageY)).toPoint();
    let dx = p.X - this.resizeCaptureXOrigine;
    let dy = p.Y - this.resizeCaptureYOrigine;
    let newLeft = this.resizeCaptureOriginalLeft;
    let newTop = this.resizeCaptureOriginalTop;
    let newWidth = this.resizeCaptureOriginalWidth;
    let newHeight = this.resizeCaptureOriginalHeight;
    switch (this.resizeCaptureHandleIndex) {
      case 0:
        newLeft += dx;
        newTop += dy;
        newWidth -= dx;
        newHeight -= dy;
        break;
      // TopLeft
      case 1:
        newTop += dy;
        newHeight -= dy;
        break;
      // Top
      case 2:
        newTop += dy;
        newWidth += dx;
        newHeight -= dy;
        break;
      // TopRight
      case 3:
        newWidth += dx;
        break;
      // Right
      case 4:
        newWidth += dx;
        newHeight += dy;
        break;
      // bottom right
      case 5:
        newHeight += dy;
        break;
      // bottom
      case 6:
        newLeft += dx;
        newWidth -= dx;
        newHeight += dy;
        break;
      // bottom left
      case 7:
        newLeft += dx;
        newWidth -= dx;
        break;
      //  left
      case 8:
        newLeft += dx;
        newTop += dy;
        break;
    }
    if (newWidth < 2 * _YWidget.HANDLESIZE)
      newWidth = 2 * _YWidget.HANDLESIZE;
    if (newHeight < 2 * _YWidget.HANDLESIZE)
      newHeight = 2 * _YWidget.HANDLESIZE;
    this.UIContainer.style.left = newLeft.toString() + "px";
    this.UIContainer.style.top = newTop.toString() + "px";
    if (this.resizeCaptureOriginalWidth != newWidth || this.resizeCaptureOriginalHeight != newHeight) {
      this.resize(newWidth - 2, newHeight - 2);
    }
    this._genRenderer.clearTransformationMatrix();
    _YWidget.HandlesDiv.style.width = newWidth.toString() + "px";
    _YWidget.HandlesDiv.style.height = newHeight.toString() + "px";
    _YWidget.HandlesDiv.style.left = "0px";
    _YWidget.HandlesDiv.style.top = "0px";
    let A = this.resizeCaptureOriginalMatrixInv;
    let B = Matrix3x3.newTranslateMatrix(newLeft - this.resizeCaptureOriginalLeft, newTop - this.resizeCaptureOriginalTop);
    let borderWidth = parseFloat(this.UIContainer.style.border);
    let borderMatrix = Matrix3x3.newTranslateMatrix(-borderWidth, -borderWidth);
    _YWidget.HandlesDiv.style.transform = borderMatrix.multiplyByM(A.multiplyByM(B)).toCSS();
  }
  editStopped() {
    this.resizeCaptureRunning = false;
    if (_YWidget.HandlesDiv != null)
      _YWidget.HandlesDiv.style.display = "none";
  }
  touchCaptureStop(e) {
    this.mouseCaptureStop(null);
  }
  mouseCaptureStop(e) {
    if (!this.resizeCaptureRunning)
      return;
    this.resizeCaptureRunning = false;
    _YWidget.editHandles[this.resizeCaptureHandleIndex].active = false;
    if (this.previousTRansform != _YWidget.HandlesDiv.style.transform || this.resizeCaptureOriginalWidth != this.UIContainer.offsetWidth || this.resizeCaptureOriginalHeight != this.UIContainer.offsetHeight) {
      if (this._SizeIsRelative)
        this.updateRelativeSize();
      constants.edited = true;
    }
    this.refreshProperties();
  }
  //#endif
  resize(newWidth, newHeight) {
    this.UIContainer.style.width = newWidth.toString() + "px";
    this.UIContainer.style.height = newHeight.toString() + "px";
    this.containerResized();
  }
  containerResized() {
  }
  // overloaded later on
  //#ifndef READONLY
  getContentsConfigData() {
    return "  <location x='" + this.UIContainer.offsetLeft.toString() + "' y='" + this.UIContainer.offsetTop.toString() + "'/>\n  <size     w='" + this.UIContainer.offsetWidth.toString() + "' h='" + this.UIContainer.offsetHeight.toString() + "'  state='Normal'/>\n  <relativeCoord x='" + this._relativePositionX.toString() + "' y='" + this._relativePositionY.toString() + "' w='" + this._relativeWidth.toString() + "' h='" + this._relativeHeight.toString() + "' active='" + (this._SizeIsRelative ? "True" : "False") + "'/>\n  <container id='" + constants.XMLquote(this._containerID) + "'/>\n" + this.getPropertiesXml();
  }
  //#endif
  getPropertiesXml() {
    return "";
  }
  PatchSensorAnnotationCallback(sensor, text) {
    let name = "None";
    let avgvalue = "N/A";
    let minvalue = "N/A";
    let maxvalue = "N/A";
    let unit = "";
    if (!(sensor instanceof NullYSensor)) {
      let resolution = -Math.round(Math.log10(sensor.get_resolution()));
      name = sensor.get_friendlyName();
      if (sensor.isOnline()) {
        avgvalue = sensor.get_lastAvgValue().toFixed(resolution);
        minvalue = sensor.get_lastMinValue().toFixed(resolution);
        maxvalue = sensor.get_lastMaxValue().toFixed(resolution);
      }
      unit = sensor.get_unit();
    }
    text = text.replace("$NAME$", name);
    text = text.replace("$AVGVALUE$", avgvalue);
    text = text.replace("$MAXVALUE$", maxvalue);
    text = text.replace("$MINVALUE$", minvalue);
    text = text.replace("$UNIT$", unit);
    return text;
  }
};
YWidget.HANDLESIZE = Math.round(20 * (1 + 2 * (constants.guiDPIFactor - 1)));
YWidget.editHandles = [];
YWidget.currentEdited = null;
var gaugeWidget = class _gaugeWidget extends YWidget {
  //#ifndef READONLY
  getPropertiesXml() {
    return this.prop.getXml(1);
  }
  //#endif
  containerResized() {
    if (this._gauge != null)
      this._gauge.containerResized();
  }
  destroy() {
    this.prop.destroy();
    super.destroy();
    Object.entries(this).forEach((pair) => {
      Reflect.set(this, pair[0], null);
    });
  }
  contextMenuIsOpening() {
    YWebPage.snapshotMenuItem.userdata = this;
    YWebPage.snapshotMenuItem.visible = true;
    YWebPage.resetDataViewMenuItem.visible = false;
    if (YWebPage.editMenuItem == null)
      return;
    YWebPage.clearDataloggerMenuItem.visible = true;
    YWebPage.editMenuItem.caption = "Configure this gauge";
    YWebPage.editMenuItem.visible = true;
    YWebPage.deleteMenuItem.caption = "Delete this gauge";
    YWebPage.deleteMenuItem.visible = true;
    YWebPage.addMarkerMenuItem.visible = false;
    YWebPage.disableMarkerMenuItem.visible = false;
    YWebPage.editMenuItem.userdata = this;
    YWebPage.deleteMenuItem.userdata = this;
  }
  get confirmDeleteString() {
    return "Do you really want to delete this gauge ?";
  }
  AnnotationCallback(text) {
    let sensor = this.prop.DataSource_source;
    return this.PatchSensorAnnotationCallback(sensor, text);
  }
  constructor(node, editor, x, y, width, height) {
    super(node, editor, x, y, width, height);
    this._gauge = new YSolidGauge(this.UIContainer, YSolidGauge.DisplayMode.DISPLAY90, YWidget.log);
    this._genRenderer = this._gauge;
    this.noDataSourcepanel = this._gauge.addMessagePanel();
    this.noDataSourcepanel.font.size = constants.generalFontSize;
    this.noDataSourcepanel.text = 'No data source configured\n 1 - Make sure you have a Yoctopuce sensor connected.\n 2 - Do a right-click or a long touch on this widget.\n 3 - Choose "Configure this gauge" to bring up the properties editor.\n 4 - Choose a data source\n';
    this.prop = new GaugeFormProperties(node, this);
    this._genProp = this.prop;
    let propDesc = GenericProperties.getAllProperties(this.prop);
    if (_gaugeWidget.AnnotationPanelCount == 0) {
      for (let propname in propDesc.byName) {
        if (propname.startsWith("SolidGauge_annotationPanel"))
          _gaugeWidget.AnnotationPanelCount++;
      }
    }
    for (let i = 0; i < _gaugeWidget.AnnotationPanelCount; i++) {
      this._gauge.addAnnotationPanel();
    }
    this._gauge.setPatchAnnotationCallback((s) => {
      return this.AnnotationCallback(s);
    });
    this._gauge.valueFormater = (source, value) => {
      return this.valueFormater(source, value);
    };
    this.updateWindowPositionProperties(this.prop);
    this.prop.ApplyAllProperties(this);
    YDataRenderer.minMaxCheckDisabled = true;
    this.prop.ApplyAllProperties(this._gauge);
    YDataRenderer.minMaxCheckDisabled = false;
    this._gauge.resetRefrenceSize();
    this._gauge.resizeRule = Proportional.ResizeRule.RELATIVETOBOTH;
    this.ApplyRelativeSizeIfRequired();
    if (this.SizeIsRelative)
      this.refreshProperties();
    this._gauge.AllowRedraw();
  }
  valueFormater(source, value) {
    if (this.prop.DataSource_source instanceof NullYSensor) {
      return "N/A";
    } else if (!this.prop.DataSource_source.isOnline())
      return "OFFLINE";
    let format = this.prop.DataSource_precision;
    let p = format.indexOf(".");
    let n = 0;
    if (p >= 0)
      n = format.length - p - 1;
    let unit = this.prop.DataSource_source.get_unit();
    return value.toFixed(n) + unit;
  }
  //#ifndef READONLY
  PropertyChanged2(src) {
    let info2 = new PropPathinfo();
    let path = src.ExtractPropPath(info2);
    switch (info2.propType) {
      case "Form":
        GenericProperties.copyProperty_STT(this, this.prop, info2.fullpropname, path);
        break;
      case "SolidGauge":
        GenericProperties.copyProperty_STT(this._gauge, this.prop, info2.fullpropname, path);
        break;
      case "DataSource":
        break;
    }
  }
  Get_PropertyValue(src) {
    let info2 = new PropPathinfo();
    let path = src.ExtractPropPath(info2);
    switch (info2.propType) {
      case "Form":
        return GenericProperties.newGetProperty(this, this.prop, info2.fullpropname, path, null);
      case "SolidGauge":
        return GenericProperties.newGetProperty(this._gauge, this.prop, info2.fullpropname, path, null);
    }
    return null;
  }
  edit() {
    super.edit();
    YWebPage.EditObject(this, this.prop, (src) => {
      this.PropertyChanged2(src);
    }, (src) => {
      return this.Get_PropertyValue(src);
    }, () => {
      this.editStopped();
    });
  }
  refreshProperties() {
    this.prop.RefreshAllProperties(this);
    this.prop.RefreshAllProperties(this._gauge);
    YWebPage.refreshPropertiesForm();
  }
  //#endif
  SourceChanged(value, index) {
    this.noDataSourcepanel.enabled = value instanceof NullYSensor;
    if (value instanceof NullYSensor) {
      this._gauge.value = 0;
    } else {
      value.registerCallback(this);
    }
  }
  SensorValuecallback(source, M) {
    if (this.prop == null)
      return;
    if (source == this.prop.DataSource_source) {
      this._gauge.value = source.isOnline() ? M.get_averageValue() : 0;
    }
  }
  //#ifndef READONLY
  getConfigData() {
    return "<GaugeForm>\n" + this.getContentsConfigData() + "</GaugeForm>\n";
  }
};
gaugeWidget.AnnotationPanelCount = 0;
var angularGaugeWidget = class _angularGaugeWidget extends YWidget {
  //#ifndef READONLY
  getPropertiesXml() {
    return this.prop.getXml(1);
  }
  //#endif
  containerResized() {
    if (this._angularGauge != null)
      this._angularGauge.containerResized();
  }
  destroy() {
    this.prop.destroy();
    super.destroy();
    Object.entries(this).forEach((pair) => {
      Reflect.set(this, pair[0], null);
    });
  }
  AnnotationCallback(text) {
    let sensor = this.prop.DataSource_source;
    return this.PatchSensorAnnotationCallback(sensor, text);
  }
  contextMenuIsOpening() {
    YWebPage.snapshotMenuItem.userdata = this;
    YWebPage.snapshotMenuItem.visible = true;
    YWebPage.resetDataViewMenuItem.visible = false;
    if (YWebPage.editMenuItem == null)
      return;
    YWebPage.clearDataloggerMenuItem.visible = false;
    YWebPage.editMenuItem.caption = "Configure this gauge";
    YWebPage.editMenuItem.visible = true;
    YWebPage.deleteMenuItem.caption = "Delete this gauge";
    YWebPage.deleteMenuItem.visible = true;
    YWebPage.addMarkerMenuItem.visible = false;
    YWebPage.disableMarkerMenuItem.visible = false;
    YWebPage.editMenuItem.userdata = this;
    YWebPage.deleteMenuItem.userdata = this;
  }
  get confirmDeleteString() {
    return "Do you really want to delete this gauge ?";
  }
  constructor(node, editor, x, y, width, height) {
    super(node, editor, x, y, width, height);
    this._angularGauge = new YAngularGauge(this.UIContainer, YWidget.log);
    this._genRenderer = this._angularGauge;
    this.noDataSourcepanel = this._angularGauge.addMessagePanel();
    this.noDataSourcepanel.font.size = constants.generalFontSize;
    this.noDataSourcepanel.text = 'No data source configured\n 1 - Make sure you have a Yoctopuce sensor connected.\n 2 - Do a right-click  or a long touch on this widget.\n 3 - Choose "Configure this gauge" to bring up the properties editor.\n 4 - Choose a data source\n';
    this.prop = new AngularGaugeFormProperties(node, this);
    this._genProp = this.prop;
    let propDesc = GenericProperties.getAllProperties(this.prop);
    if (_angularGaugeWidget.AnnotationPanelCount == 0) {
      for (let propname in propDesc.byName) {
        if (propname.startsWith("AngularGauge_annotationPanel"))
          _angularGaugeWidget.AnnotationPanelCount++;
        if (propname.startsWith("AngularGauge_zone"))
          _angularGaugeWidget.zonesCount++;
      }
    }
    for (let i = 0; i < _angularGaugeWidget.zonesCount; i++) {
      this._angularGauge.AddZone();
    }
    for (let i = 0; i < _angularGaugeWidget.AnnotationPanelCount; i++) {
      this._angularGauge.addAnnotationPanel();
    }
    this._angularGauge.setPatchAnnotationCallback((s) => {
      return this.AnnotationCallback(s);
    });
    this.initDataNode = node;
    this.updateWindowPositionProperties(this.prop);
    this.prop.ApplyAllProperties(this);
    YDataRenderer.minMaxCheckDisabled = true;
    this.prop.ApplyAllProperties(this._angularGauge);
    YDataRenderer.minMaxCheckDisabled = false;
    this._angularGauge.resetRefrenceSize();
    this._angularGauge.resizeRule = Proportional.ResizeRule.RELATIVETOBOTH;
    this.ApplyRelativeSizeIfRequired();
    if (this.SizeIsRelative)
      this.refreshProperties();
    this._angularGauge.AllowRedraw();
  }
  //#ifndef READONLY
  PropertyChanged2(src) {
    let info2 = new PropPathinfo();
    let path = src.ExtractPropPath(info2);
    switch (info2.propType) {
      case "Form":
        GenericProperties.copyProperty_STT(this, this.prop, info2.fullpropname, path);
        break;
      case "AngularGauge":
        GenericProperties.copyProperty_STT(this._angularGauge, this.prop, info2.fullpropname, path);
        break;
      case "DataSource":
        break;
    }
  }
  Get_PropertyValue(src) {
    let info2 = new PropPathinfo();
    let path = src.ExtractPropPath(info2);
    switch (info2.propType) {
      case "Form":
        return GenericProperties.newGetProperty(this, this.prop, info2.fullpropname, path, null);
      case "AngularGauge":
        return GenericProperties.newGetProperty(this._angularGauge, this.prop, info2.fullpropname, path, null);
    }
    return null;
  }
  edit() {
    super.edit();
    YWebPage.EditObject(this, this.prop, (src) => {
      this.PropertyChanged2(src);
    }, (src) => {
      return this.Get_PropertyValue(src);
    }, () => {
      this.editStopped();
    });
  }
  refreshProperties() {
    this.prop.RefreshAllProperties(this);
    this.prop.RefreshAllProperties(this._angularGauge);
    YWebPage.refreshPropertiesForm();
  }
  //#endif
  showStatus(status) {
    if (status != "")
      this._angularGauge.value = 0;
    this._angularGauge.showNeedle = status == "";
    this._angularGauge.statusLine = status;
  }
  SourceChanged(value, index) {
    this._angularGauge.DisableRedraw();
    this.noDataSourcepanel.enabled = value instanceof NullYSensor;
    if (value instanceof NullYSensor) {
      this.showStatus("N/A");
      this._angularGauge.unit = "";
    } else if (!value.isOnline()) {
      this.showStatus("OFFLINE");
    } else {
      this.showStatus("");
      this._angularGauge.unit = value.get_unit();
    }
    this._angularGauge.AllowRedraw();
    value.registerCallback(this);
  }
  SensorValuecallback(source, M) {
    if (this.prop == null)
      return;
    if (source == this.prop.DataSource_source) {
      this._angularGauge.DisableRedraw();
      if (this.prop.DataSource_source instanceof NullYSensor) {
        this.showStatus("N/A");
        this._angularGauge.unit = "";
      } else if (!this.prop.DataSource_source.isOnline()) {
        this.showStatus("OFFLINE");
      } else {
        this._angularGauge.DisableRedraw();
        this.showStatus("");
        this._angularGauge.unit = source.get_unit();
        this._angularGauge.value = M.get_averageValue();
        this._angularGauge.AllowRedraw();
      }
      this._angularGauge.AllowRedraw();
    }
  }
  //#ifndef READONLY
  getConfigData() {
    return "<angularGaugeForm>\n" + this.getContentsConfigData() + "</angularGaugeForm>\n";
  }
};
angularGaugeWidget.AnnotationPanelCount = 0;
angularGaugeWidget.zonesCount = 0;
var digitalDisplayWidget = class _digitalDisplayWidget extends YWidget {
  //#ifndef READONLY
  getPropertiesXml() {
    return this.prop.getXml(1);
  }
  //#endif
  containerResized() {
    if (this._display != null)
      this._display.containerResized();
  }
  destroy() {
    this.prop.destroy();
    super.destroy();
    Object.entries(this).forEach((pair) => {
      Reflect.set(this, pair[0], null);
    });
  }
  AnnotationCallback(text) {
    let sensor = this.prop.DataSource_source;
    return this.PatchSensorAnnotationCallback(sensor, text);
  }
  contextMenuIsOpening() {
    YWebPage.snapshotMenuItem.userdata = this;
    YWebPage.snapshotMenuItem.visible = true;
    YWebPage.resetDataViewMenuItem.visible = false;
    if (YWebPage.editMenuItem == null)
      return;
    YWebPage.clearDataloggerMenuItem.visible = false;
    YWebPage.editMenuItem.caption = "Configure this digital display";
    YWebPage.editMenuItem.visible = true;
    YWebPage.deleteMenuItem.caption = "Delete this digital display";
    YWebPage.deleteMenuItem.visible = true;
    YWebPage.addMarkerMenuItem.visible = false;
    YWebPage.disableMarkerMenuItem.visible = false;
    YWebPage.editMenuItem.userdata = this;
    YWebPage.deleteMenuItem.userdata = this;
  }
  get confirmDeleteString() {
    return "Do you really want to delete this digital display ?";
  }
  constructor(node, editor, x, y, width, height) {
    super(node, editor, x, y, width, height);
    this._unit = "";
    this._display = new YDigitalDisplay(this.UIContainer, YWidget.log);
    this._genRenderer = this._display;
    this.noDataSourcepanel = this._display.addMessagePanel();
    this.noDataSourcepanel.font.size = constants.generalFontSize;
    this.noDataSourcepanel.text = 'No data source configured\n 1 - Make sure you have a Yoctopuce sensor connected.\n 2 - Do a right-click  or a long touch on this widget.\n 3 - Choose "Configure this digital display" to bring up the properties editor.\n 4 - Choose a data source\n';
    this.prop = new digitalDisplayFormProperties(node, this);
    this._genProp = this.prop;
    let propDesc = GenericProperties.getAllProperties(this.prop);
    if (_digitalDisplayWidget.AnnotationPanelCount == 0) {
      for (let propname in propDesc.byName) {
        if (propname.startsWith("display_annotationPanel"))
          _digitalDisplayWidget.AnnotationPanelCount++;
      }
    }
    for (let i = 0; i < _digitalDisplayWidget.AnnotationPanelCount; i++) {
      this._display.addAnnotationPanel();
    }
    this._display.setPatchAnnotationCallback((s) => {
      return this.AnnotationCallback(s);
    });
    this._display.valueFormater = (source, value) => {
      return this.valueFormater(source, value);
    };
    this.updateWindowPositionProperties(this.prop);
    this.prop.ApplyAllProperties(this);
    YDataRenderer.minMaxCheckDisabled = true;
    this.prop.ApplyAllProperties(this._display);
    YDataRenderer.minMaxCheckDisabled = false;
    this._display.resetRefrenceSize();
    this._display.AllowPrintScreenCapture = true;
    this._display.resetRefrenceSize();
    this._display.resizeRule = Proportional.ResizeRule.RELATIVETOBOTH;
    this.ApplyRelativeSizeIfRequired();
    if (this.SizeIsRelative)
      this.refreshProperties();
    this._display.AllowRedraw();
  }
  valueFormater(source, value) {
    if (this.prop.DataSource_source instanceof NullYSensor) {
      return "N/A";
    } else if (!this.prop.DataSource_source.isOnline())
      return "OFFLINE";
    let format = this.prop.DataSource_precision;
    let p = format.indexOf(".");
    let n = 0;
    if (p >= 0)
      n = format.length - p - 1;
    let unit = this.prop.DataSource_source.get_unit();
    return value.toFixed(n) + unit;
  }
  //#ifndef READONLY
  PropertyChanged2(src) {
    let info2 = new PropPathinfo();
    let path = src.ExtractPropPath(info2);
    switch (info2.propType) {
      case "Form":
        GenericProperties.copyProperty_STT(this, this.prop, info2.fullpropname, path);
        break;
      case "display":
        GenericProperties.copyProperty_STT(this._display, this.prop, info2.fullpropname, path);
        break;
      case "DataSource":
        break;
    }
  }
  Get_PropertyValue(src) {
    let info2 = new PropPathinfo();
    let path = src.ExtractPropPath(info2);
    switch (info2.propType) {
      case "Form":
        return GenericProperties.newGetProperty(this, this.prop, info2.fullpropname, path, null);
      case "display":
        return GenericProperties.newGetProperty(this._display, this.prop, info2.fullpropname, path, null);
    }
    return null;
  }
  edit() {
    super.edit();
    YWebPage.EditObject(this, this.prop, (src) => {
      this.PropertyChanged2(src);
    }, (src) => {
      return this.Get_PropertyValue(src);
    }, () => {
      this.editStopped();
    });
  }
  refreshProperties() {
    this.prop.RefreshAllProperties(this);
    this.prop.RefreshAllProperties(this._display);
    YWebPage.refreshPropertiesForm();
  }
  //#endif
  SourceChanged(value, index) {
    this.noDataSourcepanel.enabled = value instanceof NullYSensor;
    this.SensorValuecallback(value, null);
    if (value instanceof NullYSensor) {
      this._display.alternateValue = "N/A";
    } else if (!value.isOnline())
      this._display.alternateValue = "OFFLINE";
    value.registerCallback(this);
  }
  SensorValuecallback(source, M) {
    if (this.prop == null)
      return;
    if (this.prop.DataSource_source instanceof NullYSensor) {
      this._display.alternateValue = "N/A";
    } else if (!this.prop.DataSource_source.isOnline()) {
      this._display.alternateValue = "OFFLINE";
    } else if (M == null) {
      this._display.alternateValue = "--" + this._unit;
    } else if (source == this.prop.DataSource_source) {
      this._display.DisableRedraw();
      this._display.alternateValue = null;
      this._display.value = M.get_averageValue();
      this._display.AllowRedraw();
    }
  }
  //#ifndef READONLY
  getConfigData() {
    return "<digitalDisplayForm>\n" + this.getContentsConfigData() + "</digitalDisplayForm>\n";
  }
};
digitalDisplayWidget.AnnotationPanelCount = 0;
var graphWidget = class _graphWidget extends YWidget {
  AnnotationCallback(text) {
    for (let i = 0; i < _graphWidget.SeriesCount; i++) {
      let s = Reflect.get(this.prop, "Graph_series" + i.toString());
      let sensor = s.DataSource_source;
      let name = "None";
      let avgvalue = "N/A";
      let minvalue = "N/A";
      let maxvalue = "N/A";
      let unit = "";
      if (!(sensor instanceof NullYSensor)) {
        let resolution = -Math.round(Math.log10(sensor.get_resolution()));
        name = s.legend != "" ? s.legend : sensor.get_friendlyName();
        if (sensor.isOnline()) {
          avgvalue = sensor.get_lastAvgValue().toFixed(resolution);
          minvalue = sensor.get_lastMinValue().toFixed(resolution);
          maxvalue = sensor.get_lastMaxValue().toFixed(resolution);
        }
        unit = sensor.get_unit();
      }
      text = text.replace("$NAME" + (i + 1).toString() + "$", name);
      text = text.replace("$AVGVALUE" + (i + 1).toString() + "$", avgvalue);
      text = text.replace("$MAXVALUE" + (i + 1).toString() + "$", maxvalue);
      text = text.replace("$MINVALUE" + (i + 1).toString() + "$", minvalue);
      text = text.replace("$UNIT" + (i + 1).toString() + "$", unit);
    }
    return text;
  }
  destroy() {
    this.prop.destroy();
    super.destroy();
    Object.entries(this).forEach((pair) => {
      Reflect.set(this, pair[0], null);
    });
  }
  //#ifndef READONLY
  getPropertiesXml() {
    return this.prop.getXml(1);
  }
  //#endif
  containerResized() {
    if (this._graph != null)
      this._graph.containerResized();
  }
  contextMenuIsOpening() {
    YWebPage.snapshotMenuItem.userdata = this;
    YWebPage.snapshotMenuItem.visible = true;
    YWebPage.resetDataViewMenuItem.visible = true;
    YWebPage.resetDataViewMenuItem.userdata = this;
    if (YWebPage.editMenuItem == null)
      return;
    YWebPage.clearDataloggerMenuItem.visible = true;
    YWebPage.clearDataloggerMenuItem.userdata = this;
    YWebPage.editMenuItem.caption = "Configure this graph";
    YWebPage.editMenuItem.visible = true;
    YWebPage.deleteMenuItem.caption = "Delete this graph";
    YWebPage.deleteMenuItem.visible = true;
    YWebPage.addMarkerMenuItem.visible = true;
    YWebPage.disableMarkerMenuItem.visible = true;
    YWebPage.editMenuItem.userdata = this;
    YWebPage.deleteMenuItem.userdata = this;
    YWebPage.disableMarkerMenuItem.userdata = this;
    for (let i = 0; i < YWebPage.markersSubMenuItems.length; i++) {
      YWebPage.markersSubMenuItems[i].caption = "Place marker #" + (i + 1).toString();
      YWebPage.markersSubMenuItems[i].userdata = this;
    }
  }
  startMarkerCapture(markerIndex) {
    this.markers[markerIndex].startCapture();
  }
  AxisParamtersChangedAutomatically(source) {
    let yaxis = source.userData;
    yaxis.visible = source.visible;
    YWebPage.refreshPropertiesForm();
  }
  //#ifndef READONLY
  get confirmDeleteString() {
    return "Do you really want to delete this graph ?";
  }
  //#endif
  constructor(node, editor, x, y, width, height) {
    super(node, editor, x, y, width, height);
    this.editedMarkerWasEnabled = false;
    this.editedMarkerPosition = 0;
    this._graph = new YGraph(this.UIContainer, YWidget.log);
    this.noDataSourcepanel = this._graph.addMessagePanel();
    this.noDataSourcepanel.font.size = constants.generalFontSize;
    this.noDataSourcepanel.text = 'No data source configured\n 1 - Make sure you have a Yoctopuce sensor connected.\n 2 - Do a right-click  or a long touch on this widget.\n 3 - Choose "Configure this graph" to bring up the properties editor.\n 4 - Choose a data source\n';
    this.dataloggerProgress = this._graph.addMessagePanel();
    this.dataloggerProgress.font.size = 12 * constants.guiDPIFactor;
    this.dataloggerProgress.font.color = YColor.DarkGray;
    this.dataloggerProgress.borderColor = YColor.Gray;
    this.dataloggerProgress.panelHrzAlign = MessagePanel.HorizontalAlignPos.LEFT;
    this.dataloggerProgress.panelVrtAlign = MessagePanel.VerticalAlignPos.TOP;
    this.dataloggerProgress.text = _graphWidget.DataLoggerLoadingMsg;
    this.dataloggerProgress.enabled = false;
    this.prop = new GraphFormProperties(node, this);
    this._genProp = this.prop;
    let propDesc = GenericProperties.getAllProperties(this.prop);
    if (_graphWidget.YAxisCount == 0) {
      for (let propname in propDesc.byName) {
        if (propname.startsWith("Graph_annotationPanel"))
          _graphWidget.AnnotationPanelCount++;
        if (propname.startsWith("Graph_series"))
          _graphWidget.SeriesCount++;
        if (propname.startsWith("Graph_yAxes"))
          _graphWidget.YAxisCount++;
      }
    }
    if (_graphWidget.ZoneCountPerYaxis == 0) {
      let yAxisProp = new YaxisDescription(0, false);
      let propDesc2 = GenericProperties.getAllProperties(yAxisProp);
      for (let propname in propDesc2.byName) {
        if (propname.startsWith("zones"))
          _graphWidget.ZoneCountPerYaxis++;
      }
    }
    if (_graphWidget.MarkerCountPerXaxis == 0) {
      let xAxisProp = new XaxisDescription();
      let propDesc2 = GenericProperties.getAllProperties(xAxisProp);
      for (let propname in propDesc2.byName) {
        if (propname.startsWith("markers"))
          _graphWidget.MarkerCountPerXaxis++;
      }
    }
    this.offLineSourcesPanel = this._graph.addMessagePanel();
    this.offLineSourcesPanel.bgColor = YColor.FromArgb(192, 255, 192, 192);
    this.offLineSourcesPanel.borderColor = YColor.DarkRed;
    this.offLineSourcesPanel.font.color = YColor.DarkRed;
    this.offLineSourcesPanel.panelHrzAlign = MessagePanel.HorizontalAlignPos.RIGHT;
    this.offLineSourcesPanel.panelVrtAlign = MessagePanel.VerticalAlignPos.TOP;
    this.captureRunningPanel = this._graph.addMessagePanel();
    this.captureRunningPanel.bgColor = YColor.FromArgb(240, 200, 255, 193);
    this.captureRunningPanel.borderColor = YColor.DarkGreen;
    this.captureRunningPanel.font.color = YColor.DarkGreen;
    this.captureRunningPanel.panelHrzAlign = MessagePanel.HorizontalAlignPos.LEFT;
    this.captureRunningPanel.panelVrtAlign = MessagePanel.VerticalAlignPos.TOP;
    for (let i = 0; i < _graphWidget.YAxisCount; i++) {
      let axis = this._graph.addYAxis();
      for (let j = 0; j < _graphWidget.ZoneCountPerYaxis; j++) {
        axis.AddZone();
      }
    }
    this.markers = [];
    for (let i = 0; i < _graphWidget.MarkerCountPerXaxis; i++) {
      let m = this._graph.xAxis.AddMarker();
      m.xposition = TimeConverter.ToUnixTime(/* @__PURE__ */ new Date()) + i * 60;
      this.markers.push(m);
    }
    for (let i = 0; i < _graphWidget.AnnotationPanelCount; i++) {
      this._graph.addAnnotationPanel();
    }
    this.seriesProperties = [];
    for (let i = 0; i < _graphWidget.SeriesCount; i++) {
      this.seriesProperties.push(Reflect.get(this.prop, "Graph_series" + i.toString()));
      this._graph.addSerie();
    }
    this._graph.yAxes[0].visible = true;
    this._graph.setPatchAnnotationCallback((s) => {
      return this.AnnotationCallback(s);
    });
    this._genRenderer = this._graph;
    this._graph.resetRefrenceSize();
    this._graph.AllowPrintScreenCapture = true;
    this._graph.setMarkerCaptureCallbacks((m) => {
      this.MarkedCaptureStarted(m);
    }, (m) => {
      this.MarkedCaptureStopped(m);
    });
    this.offlineMessages = new Array(_graphWidget.SeriesCount);
    this.showOffline = new Array(_graphWidget.SeriesCount);
    this.updateWindowPositionProperties(this.prop);
    this.prop.ApplyAllProperties(this);
    YDataRenderer.minMaxCheckDisabled = true;
    this.prop.ApplyAllProperties(this._graph);
    YDataRenderer.minMaxCheckDisabled = false;
    for (let i = 0; i < _graphWidget.YAxisCount; i++) {
      this._graph.yAxes[i].AxisChanged = () => {
        this.AxisParamtersChangedAutomatically(this._graph.yAxes[i]);
      };
      this._graph.yAxes[i].AllowAutoShow = true;
    }
    for (let i = 0; i < _graphWidget.SeriesCount; i++) {
      let s = Reflect.get(this.prop, "Graph_series" + i.toString());
      s.Init(this, i);
      if (s.DataSource_source != null) {
        this.SourceChanged(s.DataSource_source, i);
      }
    }
    this._graph.resetRefrenceSize();
    this._graph.resizeRule = Proportional.ResizeRule.FIXED;
    this.ApplyRelativeSizeIfRequired();
    if (this.SizeIsRelative)
      this.refreshProperties();
    this.loadRecordedDataIfNeeded();
    this._graph.AllowRedraw();
  }
  MarkedCaptureStarted(src) {
    let str = src.text != "" ? 'Click to place the "' + src.shortText + '" marker.' : "Click to place the marker.";
    str += "\nRight-click to cancel the operation.";
    this.editedMarkerWasEnabled = src.enabled;
    this.editedMarkerPosition = src.positionOnXAxis.value;
    this.captureRunningPanel.text = str;
    this.captureRunningPanel.enabled = true;
  }
  MarkedCaptureStopped(src) {
    if (src != null) {
      if (this.editedMarkerWasEnabled != src.enabled && this.editedMarkerPosition != src.positionOnXAxis.value) {
        constants.edited = true;
      }
    }
    this.captureRunningPanel.enabled = false;
    this.refreshProperties();
  }
  refreshProperties() {
    this.prop.RefreshAllProperties(this);
    this.prop.RefreshAllProperties(this._graph);
    YWebPage.refreshPropertiesForm();
  }
  //#endif
  decomposeToSegments(data, start, dataCount) {
    let n1 = start;
    let n2 = 0;
    let l = [];
    let deltaT = 0;
    while (n1 < start + dataCount - 1) {
      try {
        deltaT = data[n1 + 1].DateTime - data[n1].DateTime;
      } catch (Exception) {
        debugger;
      }
      n2 = n1 + 1;
      while (n2 < dataCount && data[n2].DateTime - data[n2 - 1].DateTime < 2 * deltaT) {
        n2++;
      }
      let count = n2 - n1;
      if (count > 0) {
        let p = new Array(count);
        for (let i = 0; i < count; i++) {
          p[i] = new pointXY(data[n1 + i].DateTime, data[n1 + i].value);
        }
        l.push(p);
      }
      n1 = n2;
    }
    return l;
  }
  SourceChanged(value, index) {
    this._graph.DisableRedraw();
    let s;
    let noDataSource = true;
    for (let i = 0; i < _graphWidget.SeriesCount; i++) {
      s = Reflect.get(this.prop, "Graph_series" + i.toString());
      if (!(s.DataSource_source instanceof NullYSensor))
        noDataSource = false;
    }
    this.noDataSourcepanel.enabled = noDataSource;
    if (value != null) {
      if (!(value instanceof NullYSensor)) {
        if (value.isOnline()) {
          this.showOffline[index] = false;
        } else {
          this.offlineMessages[index] = value.get_friendlyName() + " is OFFLINE";
          this.showOffline[index] = true;
          logForm.log(value.get_friendlyName() + " is OFFLINE");
        }
      } else {
        this.showOffline[index] = false;
      }
    } else {
      this.showOffline[index] = false;
    }
    this.updateOfflinePanel();
    this.preLoadSensorData(value, index);
    this.loadRecordedDataIfNeeded();
    if (value)
      value.registerCallback(this);
    this._graph.AllowRedraw();
  }
  SensorArrivalcallback(source) {
    this.loadRecordedDataIfNeeded();
    for (let i = 0; i < _graphWidget.SeriesCount; i++) {
      let s = this.seriesProperties[i];
      if (s.DataSource_source == source) {
        if (!s.DataSource_source.isOnline())
          this.showOffline[i] = true;
        else
          this.showOffline[i] = false;
      }
    }
    this.updateOfflinePanel();
  }
  preLoadSensorData(value, index) {
    if (value instanceof NullYSensor) {
      this._graph.series[index].clear();
      return;
    }
    let s = Reflect.get(this.prop, "Graph_series" + index.toString());
    let data;
    switch (s.DataSource_datatype) {
      case 1:
        data = value.minData;
        break;
      case 2:
        data = value.maxData;
        break;
      default:
        data = value.curData;
        break;
    }
    let l = this.decomposeToSegments(data, 0, data.length);
    this._graph.series[index].clear();
    for (let i = l.length - 1; i >= 0; i--) {
      this._graph.series[index].InsertPoints(l[i]);
    }
    this._graph.series[index].rebuildSummaries();
  }
  //#ifndef READONLY
  PropertyChanged2(src) {
    let info2 = new PropPathinfo();
    let path = src.ExtractPropPath(info2);
    switch (info2.propType) {
      case "Form":
        GenericProperties.copyProperty_STT(this, this.prop, info2.fullpropname, path);
        break;
      case "Graph":
        GenericProperties.copyProperty_STT(this._graph, this.prop, info2.fullpropname, path);
        break;
      case "DataSource":
        break;
    }
  }
  Get_PropertyValue(src) {
    let info2 = new PropPathinfo();
    let path = src.ExtractPropPath(info2);
    switch (info2.propType) {
      case "Form":
        return GenericProperties.newGetProperty(this, this.prop, info2.fullpropname, path, null);
      case "Graph":
        return GenericProperties.newGetProperty(this._graph, this.prop, info2.fullpropname, path, null);
      case "DataSource":
        return null;
    }
    return null;
  }
  edit() {
    super.edit();
    YWebPage.EditObject(this, this.prop, (src) => {
      this.PropertyChanged2(src);
    }, (src) => {
      return this.Get_PropertyValue(src);
    }, () => {
      this.editStopped();
    });
  }
  //#endif
  updateOfflinePanel() {
    let message = "";
    for (let i = 0; i < _graphWidget.SeriesCount; i++) {
      if (this.showOffline[i])
        message = message + (message != "" ? "\n" : "") + this.offlineMessages[i];
    }
    if (message == "" && this.offLineSourcesPanel.enabled)
      this.offLineSourcesPanel.enabled = false;
    if (message != "" && (this.offLineSourcesPanel.text != message || !this.offLineSourcesPanel.enabled)) {
      this.offLineSourcesPanel.text = message;
      this.offLineSourcesPanel.enabled = true;
    }
  }
  SensorValuecallback(source, M) {
    if (this.prop == null)
      return;
    for (let i = 0; i < _graphWidget.SeriesCount; i++) {
      let s = this.seriesProperties[i];
      if (s.DataSource_source == source) {
        if (!s.DataSource_source.isOnline()) {
          this.offlineMessages[i] = s.DataSource_source.get_friendlyName() + " is OFFLINE";
          this.showOffline[i] = true;
          this.updateOfflinePanel();
          return;
        }
        this.showOffline[i] = false;
        this.updateOfflinePanel();
        let index = s.DataSource_source.curData.length - 1;
        if (index >= 0)
          switch (s.DataSource_datatype) {
            case 1:
              this._graph.series[i].AddPoint(new pointXY(s.DataSource_source.minData[index].DateTime, s.DataSource_source.minData[index].value));
              break;
            case 2:
              this._graph.series[i].AddPoint(new pointXY(s.DataSource_source.maxData[index].DateTime, s.DataSource_source.maxData[index].value));
              break;
            default:
              this._graph.series[i].AddPoint(new pointXY(s.DataSource_source.curData[index].DateTime, s.DataSource_source.curData[index].value));
              break;
          }
        this._graph.series[i].unit = s.DataSource_source.get_unit();
      }
    }
  }
  disableAllMarkers() {
    confirm.ask("Do you really want to disable all markers?", () => {
      for (let i = 0; i < this.markers.length; i++) {
        this.markers[i].enabled = false;
      }
    }, () => {
    }, null);
  }
  clearDataLogger() {
    confirm.ask("Do you really want to erase contents of all dataloggers related to this graph?", () => {
      this.startToClearDataLoggers();
    }, () => {
    }, null);
  }
  truncateView() {
    for (let i = 0; i < _graphWidget.SeriesCount; i++) {
      this._graph.series[i].clear();
    }
    let FirstLiveValue = Math.floor((/* @__PURE__ */ new Date()).getTime() / 1e3);
    this._graph.xAxis.set_minMax(FirstLiveValue, FirstLiveValue + this._graph.xAxis.initialZoom);
  }
  resetDataView() {
    this.prop.Graph_showRecordedData = false;
    this.truncateView();
  }
  async startToClearDataLoggers() {
    let loggers = [];
    for (let i = 0; i < _graphWidget.SeriesCount; i++) {
      let s = Reflect.get(this.prop, "Graph_series" + i.toString());
      let sensor = s.DataSource_source;
      if (!(sensor instanceof NullYSensor)) {
        sensor.stopDataloggerloading();
        let serial = sensor.get_hardwareId();
        let n = serial.indexOf(".");
        serial = serial.substring(0, n);
        let d = await YDataLogger.FindDataLogger(serial + ".dataLogger");
        if (await d.isOnline()) {
          let found = false;
          for (let j = 0; j < loggers.length; j++) {
            if (loggers[j] == d)
              found = true;
          }
          if (!found)
            loggers.push(d);
        }
      }
    }
    for (let i = 0; i < loggers.length; i++) {
      await loggers[i].forgetAllDataStreams();
      await loggers[i].set_recording(YDataLogger.RECORDING_ON);
    }
    let tmp = this.prop.Graph_showRecordedData;
    this.prop.Graph_showRecordedData = false;
    this.truncateView();
    this.prop.Graph_showRecordedData = tmp;
  }
  dataLoggerLoadprocessIsRunningNotification(source) {
    let loadTotalPercent = 0;
    let sensorCount = 0;
    if (!this.prop.Graph_showRecordedData)
      return;
    for (let i = 0; i < _graphWidget.SeriesCount; i++) {
      let s = Reflect.get(this.prop, "Graph_series" + i.toString());
      let sensor = s.DataSource_source;
      if (sensor.dataloggerLoadisRunning) {
        loadTotalPercent += sensor.dataloggerLoadProgress;
        sensorCount++;
      }
    }
    if (sensorCount > 0)
      this.dataloggerProgress.text = _graphWidget.DataLoggerLoadingMsg + " (" + (loadTotalPercent / sensorCount).toFixed(0) + "%)";
    let showPanel = sensorCount > 0 && loadTotalPercent > 0 && loadTotalPercent < sensorCount * 100;
    this.dataloggerProgress.enabled = showPanel;
  }
  /*
      public startDataPreload(source: YoctoVisualization.CustomYSensor)
          {
  
             if (!this.prop.Graph_showRecordedData)
             {
                this.dataloggerProgress.enabled=false;
                return;
            }
              this.dataloggerProgress.enabled=true;
  
          }
  
       */
  SensorNewDataBlock(source, sourceFromIndex, sourcetoIndex, targetIndex, fromDataLogger) {
    if (this.prop == null)
      return;
    if (fromDataLogger && !this.prop.Graph_showRecordedData)
      return;
    let l = null;
    for (let i = 0; i < _graphWidget.SeriesCount; i++) {
      let s = Reflect.get(this.prop, "Graph_series" + i.toString());
      if (s.DataSource_source == source) {
        let count = sourcetoIndex - sourceFromIndex + 1;
        if (count > 1) {
          switch (s.DataSource_datatype) {
            case 1:
              l = this.decomposeToSegments(s.DataSource_source.minData, sourceFromIndex, count);
              for (let j = l.length - 1; j >= 0; j--) {
                this._graph.series[i].InsertPoints(l[j]);
              }
              break;
            case 2:
              l = this.decomposeToSegments(s.DataSource_source.maxData, sourceFromIndex, count);
              for (let j = l.length - 1; j >= 0; j--) {
                this._graph.series[i].InsertPoints(l[j]);
              }
              break;
            default:
              l = this.decomposeToSegments(s.DataSource_source.curData, sourceFromIndex, count);
              for (let j = l.length - 1; j >= 0; j--) {
                this._graph.series[i].InsertPoints(l[j]);
              }
              break;
          }
        }
        this._graph.series[i].unit = source.get_unit();
      }
    }
  }
  loadRecordedDataIfNeeded() {
    if (this.prop == null)
      return;
    if (!this.prop.Graph_showRecordedData)
      return;
    for (let i = 0; i < _graphWidget.SeriesCount; i++) {
      let s = Reflect.get(this.prop, "Graph_series" + i.toString());
      s.DataSource_source.startDataloggerload(this);
    }
  }
  removeDataloggerData() {
  }
  /*
      public DataLoggerProgress(): void
      {
          let progress: number = 0;
          let sensorCount: number = 0;
  
          if (!this.prop.Graph_showRecordedData)
          {
              this.dataloggerProgress.enabled=false;
              return;
          }
  
          let props: YoctoVisualization.PropertiesList = YoctoVisualization.GenericProperties.getAllProperties(this.prop);
          for (let i: number = 0; i < props.byIndex.length; i++)
          {
              let name = props.byIndex[i].name;
              if (name.startsWith("Graph_series"))
              {
                  let s: YoctoVisualization.ChartSerie = Reflect.get(this.prop, name);
                  if (!(s.DataSource_source instanceof YoctoVisualization.NullYSensor))
                  {
                      progress += s.DataSource_source.getGetaLoadProgress();
                      sensorCount++;
                  }
              }
          }
  
          if ((progress < 100 * sensorCount) && (sensorCount > 0))
          {  this.dataloggerProgress.text  = graphWidget.DataLoggerLoadingMsg+" (" + (progress / sensorCount).toFixed(0) + "%)";
  
          }
          else
          {
              this.dataloggerProgress.enabled=false;
          }
      }
      */
  DataloggerCompleted(Source) {
    if (!this.prop.Graph_showRecordedData)
      return;
    let props = GenericProperties.getAllProperties(this.prop);
    for (let i = 0; i < this.seriesProperties.length; i++) {
      if (this.seriesProperties[i].DataSource_source == Source) {
        if (!this.seriesProperties[i].dataloggerAlreadyLoaded) {
          this._graph.DisableRedraw();
          this.preLoadSensorData(Source, i);
          this._graph.AllowRedraw();
        }
      }
    }
  }
  //#ifndef READONLY
  getConfigData() {
    return "<GraphForm>\n" + this.getContentsConfigData() + "</GraphForm>\n";
  }
};
graphWidget.AnnotationPanelCount = 0;
graphWidget.SeriesCount = 0;
graphWidget.ZoneCountPerYaxis = 0;
graphWidget.MarkerCountPerXaxis = 0;
graphWidget.YAxisCount = 0;
graphWidget.DataLoggerLoadingMsg = "Loading from datalogger";

// obj/full/propertiesMngmt.js
var YXmlNode = class _YXmlNode {
  constructor(node) {
    this.node = node;
  }
  get Name() {
    return this.node.nodeName;
  }
  get_childsByName() {
    let res = {};
    this.node.childNodes.forEach((child) => {
      res[child.nodeName] = new _YXmlNode(child);
    });
    return res;
  }
  get_childsByIndex() {
    let res = [];
    this.node.childNodes.forEach((child) => {
      res.push(new _YXmlNode(child));
    });
    return res;
  }
  get_attributes() {
    let el = this.node;
    let res = {};
    let keys = el.getAttributeNames();
    for (let i = 0; i < keys.length; i++) {
      res[keys[i]] = el.getAttribute(keys[i]);
    }
    return res;
  }
  get Attributes() {
    return this.get_attributes();
  }
};
var PropertyAccess = class {
  constructor() {
    this.ttype = "";
    this.stype = "";
    this.finalTarget = null;
    this.terminalSource = null;
    this.propertyName = null;
  }
};
var doubleNan = class _doubleNan {
  constructor(v) {
    this._value = Number.NaN;
    if (typeof v == "undefined") {
      return;
    } else if (typeof v == "number") {
      this._value = v;
    } else if (typeof v == "string") {
      if (v == "") {
        this._value = Number.NaN;
      } else {
        this._value = Number(v);
      }
    } else {
      debugger;
    }
  }
  toString() {
    if (Number.isNaN(this._value))
      return "";
    return this._value.toString();
  }
  clone() {
    return new _doubleNan(this.value);
  }
  get value() {
    return this._value;
  }
  set value(value) {
    this._value = value;
  }
};
var PropertyDescriptor = class {
  constructor(name) {
    this.isWritable = false;
    this.isEnum = false;
    this.type = "";
    this.Attributes = {};
    this.name = name;
  }
};
var PropertiesList = class {
  constructor() {
    this.byIndex = [];
    this.byName = {};
  }
};
var PropDescription = class {
  constructor(name, prop) {
    this.name = name;
    this.prop = prop;
  }
};
var GenericProperties = class _GenericProperties {
  static NoFilter(propNname) {
    return true;
  }
  static escapeXml(unsafe) {
    return unsafe.replace(/[^ !#$%(-;=?-z]/g, (c) => "&#" + c.charCodeAt(0) + ";");
  }
  constructor(Owner) {
    this._Form_Text = "New window";
    this._Form_BackColor = YColor.FromArgb(255, 240, 240, 240);
    this._Form_BorderColor = YColor.LightGray;
    this._SizeIsRelative = false;
    this._Form_PositionX = 0;
    this._Form_PositionY = 0;
    this._Form_Width = 300;
    this._Form_Height = 200;
    this._containerID = "";
    this.ownerForm = Owner;
    this._Form_Text = "new window";
  }
  destroy() {
    Object.entries(this).forEach((pair) => {
      Reflect.set(this, pair[0], null);
    });
  }
  initFromXmlData(initData) {
    if (initData != null)
      this.loadProperties(initData, this.ownerForm, this);
  }
  loadProperties(initData, Owner, o) {
    let value;
    this.ownerForm = Owner;
    if (initData != null) {
      let properties = _GenericProperties.getAllProperties(o);
      let childs = initData.get_childsByName();
      for (let propname in properties.byName) {
        let p = properties.byName[propname];
        if (p.isWritable) {
          for (let childName in childs) {
            if (childName == propname) {
              let target = Reflect.get(o, propname);
              let Mustload = true;
              if ("NotSavedInXMLAttribute" in p.Attributes) {
                Mustload = p.Attributes["NotSavedInXMLAttribute"];
              }
              if (Mustload) {
                if (_GenericProperties.IsStructured(target)) {
                  this.loadProperties(childs[childName], Owner, target);
                } else {
                  let targetType = typeof target;
                  let attributes = childs[childName].get_attributes();
                  let value2 = attributes["value"];
                  if (target instanceof CustomYSensor) {
                    if (value2.toUpperCase() != "NULL" && value2 != "") {
                      let s = sensorsManager.AddNewSensor(value2);
                      Reflect.set(o, propname, s);
                    } else {
                      Reflect.set(o, propname, sensorsManager.getNullSensor());
                    }
                  } else if (target instanceof YEnumItem) {
                    Reflect.set(o, propname, target.fromString(value2));
                  } else if (target instanceof doubleNan) {
                    let d = new doubleNan(value2);
                    Reflect.set(o, propname, d);
                  } else if (target instanceof xAxisPosition) {
                    let xpos = Number(value2);
                    let rel = attributes["relative"].toUpperCase() == "TRUE";
                    Reflect.set(o, propname, new xAxisPosition(xpos, rel));
                  } else if (target instanceof YColor) {
                    let c = YColor.FromString(value2);
                    if (c == null)
                      c = YColor.Black;
                    Reflect.set(o, propname, c.clone());
                  } else if (target instanceof YFont) {
                    let size = attributes["size"];
                    let color = attributes["color"];
                    let italic = attributes["italic"];
                    let bold = attributes["bold"];
                    let f = Reflect.get(o, propname);
                    f.name = value2;
                    f.size = size !== "undefined" ? Number(size) : 10;
                    let c = YColor.FromString(color);
                    f.color = c != null ? c : YColor.Black.clone();
                    f.italic = italic.toUpperCase() == "TRUE";
                    f.bold = bold.toUpperCase() == "TRUE";
                  } else {
                    switch (targetType) {
                      case "string":
                        Reflect.set(o, propname, value2);
                        break;
                      case "number":
                        Reflect.set(o, propname, Number(value2));
                        break;
                      case "boolean":
                        let v = value2.toUpperCase() == "TRUE";
                        Reflect.set(o, propname, v);
                        break;
                      default:
                        debugger;
                        throw new Error("unhandled target type : " + targetType + "(" + target.constructor.name + ")");
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  get ATTR_Form_Text__DisplayName() {
    return "Window title";
  }
  get ATTR_Form_Text__CategoryAttribute() {
    return "Window";
  }
  get ATTR_Form_Text__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_Form_Text__DescriptionAttribute() {
    return "Widget name. This name is not show on display, but it might help you to identify each widget. ";
  }
  get Form_Text() {
    return this._Form_Text;
  }
  set Form_Text(value) {
    this._Form_Text = value;
  }
  get ATTR_Form_BackColor__DisplayName() {
    return "Background color";
  }
  get ATTR_Form_BackColor__CategoryAttribute() {
    return "Window";
  }
  get ATTR_Form_BackColor__DescriptionAttribute() {
    return "Window background color.";
  }
  get Form_BackColor() {
    return this._Form_BackColor;
  }
  set Form_BackColor(value) {
    this._Form_BackColor = value;
  }
  get ATTR_Form_BorderColor__DisplayName() {
    return "Border color";
  }
  get ATTR_Form_BorderColor__CategoryAttribute() {
    return "Window";
  }
  get ATTR_Form_BorderColor__DescriptionAttribute() {
    return "Window border color, use transparent color to hide the border.";
  }
  get Form_BorderColor() {
    return this._Form_BorderColor;
  }
  set Form_BorderColor(value) {
    this._Form_BorderColor = value;
  }
  get ATTR_Form_SizeIsRelative__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_Form_SizeIsRelative__DisplayName() {
    return "Rel. size and pos.";
  }
  get ATTR_Form_SizeIsRelative__CategoryAttribute() {
    return "Window";
  }
  get ATTR_Form_SizeIsRelative__DescriptionAttribute() {
    return "Makes widget size and position relative to container size. This, for instance, allows to make a widget automatically occupy all of the browser viewport, no matter the browser window size. In relative mode, position and size value are expressed in %";
  }
  get ATTR_Form_SizeIsRelative__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get Form_SizeIsRelative() {
    return this._SizeIsRelative;
  }
  set Form_SizeIsRelative(value) {
    this._SizeIsRelative = value;
  }
  // Form/widget positions are handled differently (inheritance from the C# version)
  get ATTR_Form_PositionX__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_Form_PositionX__DisplayName() {
    return "Left";
  }
  get ATTR_Form_PositionX__CategoryAttribute() {
    return "Window";
  }
  get ATTR_Form_PositionX__DescriptionAttribute() {
    return "Widget left border X position";
  }
  get Form_PositionX() {
    return this._Form_PositionX;
  }
  set Form_PositionX(value) {
    this._Form_PositionX = value;
  }
  // Form/widget positions are handled differently (inheritance from the C# version)
  get ATTR_Form_PositionY__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_Form_PositionY__DisplayName() {
    return "Top";
  }
  get ATTR_Form_PositionY__CategoryAttribute() {
    return "Window";
  }
  get ATTR_Form_PositionY__DescriptionAttribute() {
    return "Widget top border Y position";
  }
  get Form_PositionY() {
    return this._Form_PositionY;
  }
  set Form_PositionY(value) {
    this._Form_PositionY = value;
  }
  // Form/widget positions are handled differently (inheritance from the C# version)
  get ATTR_Form_Width__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_Form_Width__DisplayName() {
    return "Width";
  }
  get ATTR_Form_Width__CategoryAttribute() {
    return "Window";
  }
  get ATTR_Form_Width__DescriptionAttribute() {
    return "Widget width";
  }
  get Form_Width() {
    return this._Form_Width;
  }
  set Form_Width(value) {
    this._Form_Width = value;
  }
  // Form/widget positions are handled differently (inheritance from the C# version)
  get ATTR_Form_Height__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_Form_Height__DisplayName() {
    return "Height";
  }
  get ATTR_Form_Height__CategoryAttribute() {
    return "Window";
  }
  get ATTR_Form_Height__DescriptionAttribute() {
    return "Widget height";
  }
  get Form_Height() {
    return this._Form_Height;
  }
  set Form_Height(value) {
    this._Form_Height = value;
  }
  get ATTR_Form_containerID__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_Form_containerID__DisplayName() {
    return "Container ID";
  }
  get ATTR_Form_containerID__CategoryAttribute() {
    return "Window";
  }
  get ATTR_Form_containerID__DescriptionAttribute() {
    return "Forces the widget to be shown inside an HTML element with a specific ID already present on the page. If ID is empty, the widget is inserted at root level. Be aware that X,Y offsets are kept, you may want to set them to Zero. ";
  }
  get Form_containerID() {
    return this._containerID;
  }
  set Form_containerID(value) {
    this._containerID = value;
  }
  indent(n) {
    return " ".repeat(n);
  }
  //#ifndef READONLY
  getXml(deep, o) {
    if (typeof o == "undefined")
      o = this;
    let res = "";
    let value = "";
    let properties = _GenericProperties.getAllProperties(o);
    let propName = "";
    for (propName in properties.byName) {
      let p = properties.byName[propName];
      if (p.isWritable) {
        let Mustsave = true;
        if ("NotSavedInXMLAttribute" in p.Attributes)
          Mustsave = !p.Attributes["NotSavedInXMLAttribute"];
        let child = Reflect.get(o, p.name);
        if (_GenericProperties.IsStructured(child)) {
          if (Mustsave) {
            res = res + this.indent(2 * deep) + "<" + p.name + ">\r\n" + this.getXml(deep + 1, child) + this.indent(2 * deep) + "</" + p.name + ">\r\n";
          }
        } else {
          if (Mustsave) {
            if (child instanceof YEnumItem) {
              value = child.toString;
            } else {
              let type = typeof child;
              if (type == "object")
                type = child.constructor.name;
              switch (type) {
                case "boolean":
                  value = child ? "TRUE" : "FALSE";
                  break;
                case "number":
                  value = child.toString();
                  break;
                case "string":
                  value = GenericProperties.escapeXml(child);
                  break;
                case YColor.name:
                  value = child.toString();
                  break;
                case doubleNan.name:
                  value = child.value.toString();
                  break;
                case xAxisPosition.name:
                  let it = child;
                  value = it.value.toString() + '" relative="' + (it.relative ? "True" : "False");
                  break;
                case NullYSensor.name:
                case CustomYSensor.name:
                  let s = child;
                  value = s == null || s instanceof NullYSensor ? "NULL" : s.get_hardwareId();
                  break;
                default:
                  throw new Error("XML generation : unhandled type (" + p.type + ")");
              }
            }
            res = res + this.indent(2 * deep) + "<" + p.name + ' value="' + value + '"/>\n';
          }
        }
      }
    }
    return res;
  }
  //#endif
  static IsStructured(o) {
    if (o == null)
      return false;
    if (typeof o === "string" || o instanceof String)
      return false;
    if (typeof o === "number" || o instanceof Number)
      return false;
    if (typeof o === "boolean" || o instanceof Boolean)
      return false;
    if (o instanceof YEnumItem)
      return false;
    if (o instanceof CustomYSensor)
      return false;
    if (o instanceof YColor)
      return false;
    if (o instanceof doubleNan)
      return false;
    if (o instanceof xAxisPosition)
      return false;
    if (Object.getOwnPropertyNames(o).length > 0)
      return true;
    return false;
  }
  static getObjectFromPath(rootTarget, path) {
    let FinalTarget = rootTarget;
    for (let i = 0; i < path.length - 1; i++) {
      let name = path[i];
      let index = "";
      while (name.charCodeAt(name.length - 1) >= 48 && name.charCodeAt(name.length - 1) <= 57) {
        index = name.charAt(name.length - 1) + index;
        name = name.substring(0, name.length - 1);
      }
      if (index == "") {
        FinalTarget = Reflect.get(FinalTarget, name);
      } else {
        let arrayObject = Reflect.get(FinalTarget, name);
        FinalTarget = Reflect.get(arrayObject, parseInt(index));
      }
    }
    return FinalTarget;
  }
  //Direction Settings From Target
  static computePropertyAccess_SFT(WidgetObject, SettingObject, propertySourceName, path) {
    let res = new PropertyAccess();
    res.ttype = "";
    res.stype = "";
    res.finalTarget = null;
    res.terminalSource = null;
    res.terminalSource = _GenericProperties.getObjectFromPath(WidgetObject, path);
    let props = _GenericProperties.getAllProperties(res.terminalSource);
    if (!(path[path.length - 1] in props.byName))
      return res;
    res.stype = typeof res.terminalSource[path[path.length - 1]];
    if (res.stype == "object" && res.terminalSource[path[path.length - 1]] != null) {
      res.stype = res.terminalSource[path[path.length - 1]].constructor.name;
    }
    res.terminalSource = res.terminalSource[path[path.length - 1]];
    if (res.terminalSource == null)
      return res;
    res.propertyName = propertySourceName;
    res.finalTarget = SettingObject;
    let o = res.finalTarget[propertySourceName];
    res.ttype = typeof o;
    if (res.ttype == "object" && o != null)
      res.ttype = o.constructor.name;
    if (path.length <= 1)
      return res;
    for (let i = 1; i < path.length; i++) {
      res.finalTarget = res.finalTarget[res.propertyName];
      res.propertyName = path[i];
    }
    res.ttype = typeof res.finalTarget[res.propertyName];
    if (res.ttype == "object" && res.finalTarget[res.propertyName] != null)
      res.ttype = res.finalTarget[res.propertyName].constructor.name;
    return res;
  }
  static Reflect_getIndexed(o, indexedProp) {
    let res = Reflect.get(o, indexedProp);
    if (typeof res !== "undefined")
      return res;
    let name = indexedProp;
    let index = "";
    while (name.charCodeAt(name.length - 1) >= 48 && name.charCodeAt(name.length - 1) <= 57) {
      index = name.charAt(name.length - 1) + index;
      name = name.substring(0, name.length - 1);
    }
    if (index == "") {
      return null;
    }
    res = Reflect.get(o, name);
    return Reflect.get(res, parseInt(index));
  }
  static computePropertyAccess(rootTarget, source, propertySourceName, path) {
    let res = new PropertyAccess();
    res.ttype = "";
    res.finalTarget = _GenericProperties.getObjectFromPath(rootTarget, path);
    res.terminalSource = source;
    res.terminalSource = Reflect.get(res.terminalSource, propertySourceName);
    for (let i = 1; i < path.length; i++) {
      res.terminalSource = Reflect.get(res.terminalSource, path[i]);
    }
    res.stype = typeof res.terminalSource;
    if (res.stype == "object")
      res.stype = res.terminalSource.constructor.name;
    if (path.length > 0) {
      res.propertyName = path[path.length - 1];
      let o = _GenericProperties.Reflect_getIndexed(rootTarget, path[0]);
      if (o == null)
        return null;
      for (let i = 1; i < path.length; i++) {
        try {
          o = _GenericProperties.Reflect_getIndexed(o, path[i]);
        } catch (e) {
          debugger;
        }
      }
      res.ttype = typeof o;
      if (res.ttype == "object")
        res.ttype = o.constructor.name;
    } else {
      let targetname = propertySourceName;
      let n = targetname.indexOf("__");
      res.propertyName = targetname.substring(n + 2);
      let o = Reflect.get(rootTarget, res.propertyName);
      res.ttype = typeof o;
      if (res.ttype == "object")
        res.ttype = o.constructor.name;
    }
    return res;
  }
  static newGetProperty(rootTarget, source, propertySourceName, path, filterAllow) {
    let access = _GenericProperties.computePropertyAccess(rootTarget, source, propertySourceName, path);
    if (access == null)
      return null;
    return Reflect.get(access.finalTarget, access.propertyName);
  }
  // Direction : settings From Target
  //Direction : Settings From Target
  //   public static   copyProperty_SFT( rootTarget:object , source :object ,  propertySourceName: string, path: string[]): void
  //     {
  //     copyProperty_SFT(rootTarget, source, propertySourceName, path,  GenericProperties.NoFilter);
  //     }
  static copyProperty_SFT(rootTarget, source, propertySourceName, path, filterAllow) {
    let ttype = "";
    let stype = "";
    let TerminalSource = null;
    let p = _GenericProperties.computePropertyAccess_SFT(rootTarget, source, propertySourceName, path);
    if (p.finalTarget == null)
      return;
    ttype = p.ttype;
    stype = p.stype;
    if (stype == YColor.name && ttype == YColor.name) {
      Reflect.set(p.finalTarget, p.propertyName, p.terminalSource.clone());
    } else if (stype == xAxisPosition.name && ttype == xAxisPosition.name) {
      Reflect.set(p.finalTarget, p.propertyName, p.terminalSource.clone());
    } else if (p.stype == "number" && ttype == doubleNan.name) {
      Reflect.set(p.finalTarget, p.propertyName, new doubleNan(p.terminalSource));
    } else {
      Reflect.set(p.finalTarget, p.propertyName, p.terminalSource);
    }
  }
  //Direction :Settings To Target
  /*
  static public  copyProperty_STT(object rootTarget, object source, string propertySourceName, List<string> path) : void
  {
  copyProperty_STT(rootTarget, source, propertySourceName, path,  NoFilter);
  }
   */
  //Direction :Settings To Target
  static copyProperty_STT(rootTarget, source, propertySourceName, path, filterAllow) {
    let p = _GenericProperties.computePropertyAccess(rootTarget, source, propertySourceName, path);
    if (p == null)
      return;
    if (p.stype == doubleNan.name && p.ttype == "number") {
      let v = p.terminalSource.value;
      if (Reflect.get(p.finalTarget, p.propertyName) != v) {
        Reflect.set(p.finalTarget, p.propertyName, v);
        constants.edited = true;
      }
    } else if (p.stype != p.ttype) {
      debugger;
    } else if (p.stype == YColor.name && p.ttype == YColor.name) {
      let color = p.terminalSource;
      if (!color.equal(Reflect.get(p.finalTarget, p.propertyName))) {
        Reflect.set(p.finalTarget, p.propertyName, color.clone());
        constants.edited = true;
      }
    } else if (p.stype == doubleNan.name && p.ttype == doubleNan.name) {
      let v = p.terminalSource;
      if (Reflect.get(p.finalTarget, p.propertyName) != v) {
        Reflect.set(p.finalTarget, p.propertyName, v.clone());
        constants.edited = true;
      }
    } else if (p.stype == xAxisPosition.name && p.ttype == xAxisPosition.name) {
      let x = p.terminalSource;
      let x2 = Reflect.get(p.finalTarget, p.propertyName);
      if (x.relative != x2.relative || x.value != x2.value) {
        Reflect.set(p.finalTarget, p.propertyName, x.clone());
        constants.edited = true;
      }
    } else if (Reflect.get(p.finalTarget, p.propertyName) != p.terminalSource) {
      Reflect.set(p.finalTarget, p.propertyName, p.terminalSource);
      constants.edited = true;
    }
  }
  ApplyAllProperties(target) {
    this.ApplyAllPropertiesEx(
      target,
      _GenericProperties.NoFilter,
      0
      /* GenericProperties.APPLYDIRECTION.SETTINGS_TO_TARGET */
    );
  }
  RefreshAllProperties(target) {
    this.ApplyAllPropertiesEx(
      target,
      _GenericProperties.NoFilter,
      1
      /* GenericProperties.APPLYDIRECTION.SETTINGS_FROM_TARGET */
    );
  }
  ApplyProperties(rootSource, rootTarget, fullpropname, sourceValue, path, direction) {
    if (sourceValue instanceof AlarmSection)
      return;
    let sourceproperties = _GenericProperties.getAllProperties(this);
    if ("CopyToTarget" in sourceproperties.byName[fullpropname].Attributes) {
      if (!sourceproperties.byName[fullpropname].Attributes["CopyToTarget"]) {
        return;
      }
    }
    if (!_GenericProperties.IsStructured(sourceValue)) {
      if (direction == 0) {
        _GenericProperties.copyProperty_STT(rootTarget, this, fullpropname, path);
      } else {
        _GenericProperties.copyProperty_SFT(rootTarget, this, fullpropname, path);
      }
    } else {
      let path2 = path.slice();
      path2.push("");
      let target = _GenericProperties.getObjectFromPath(rootTarget, path2);
      Reflect.set(target, "userData", sourceValue);
      let sourceproperties2 = _GenericProperties.getAllProperties(sourceValue);
      for (let subpropname in sourceproperties2.byName) {
        let shouldcopy = true;
        if ("CopyToTarget" in sourceproperties2.byName[subpropname].Attributes) {
          shouldcopy = sourceproperties2.byName[subpropname].Attributes["CopyToTarget"];
        }
        if (sourceproperties2.byName[subpropname].isWritable && shouldcopy) {
          path.push(subpropname);
          this.ApplyProperties(rootSource, rootTarget, fullpropname, Reflect.get(sourceValue, subpropname), path, direction);
          path.splice(path.length - 1, 1);
        }
      }
    }
  }
  // will return true  if property has a getter or a setter
  static isProperty(proto, propName) {
    let d = Reflect.getOwnPropertyDescriptor(proto, propName);
    let list = Object.getOwnPropertyNames(d);
    for (let i = 0; i < list.length; i++) {
      if (list[i] == "set")
        return true;
      if (list[i] == "get")
        return true;
    }
    return false;
  }
  // will return true if property has  an implemented  setter
  static isWritable(proto, propName) {
    let d = Reflect.getOwnPropertyDescriptor(proto, propName);
    let list = Object.getOwnPropertyNames(d);
    for (let i = 0; i < list.length; i++) {
      if (list[i] == "set") {
        if (typeof d.set !== "undefined")
          return true;
      }
    }
    return false;
  }
  static getAllProperties(o) {
    if (typeof o === "undefined")
      debugger;
    if (o.hasOwnProperty("PropertiesDescriptionCache")) {
      return o["PropertiesDescriptionCache"];
    }
    let proto = Object.getPrototypeOf(o);
    let res = new PropertiesList();
    let p = proto;
    let entries = [];
    let genealogy = [];
    while (p != null) {
      genealogy.push(p);
      p = Object.getPrototypeOf(p);
    }
    for (let j = genealogy.length - 1; j >= 0; j--) {
      let names = Object.getOwnPropertyNames(genealogy[j]);
      let index = 0;
      for (let i = 0; i < names.length; i++) {
        if (names[i] != "constructor" && !names[i].startsWith("__")) {
          entries.splice(index, 0, new PropDescription(names[i], genealogy[j]));
          index++;
          for (let k = entries.length - 1; k >= index; k--) {
            if (entries[k].name == names[i])
              entries.splice(k, 1);
          }
        }
      }
    }
    for (let i = 0; i < entries.length; i++) {
      if (!entries[i].name.startsWith("ATTR") && entries[i].name != "constructor" && !entries[i].name.startsWith("__")) {
        if (_GenericProperties.isProperty(entries[i].prop, entries[i].name)) {
          let d = new PropertyDescriptor(entries[i].name);
          res.byIndex.push(d);
          res.byName[d.name] = d;
          d.isWritable = _GenericProperties.isWritable(entries[i].prop, entries[i].name);
          d.isEnum = o[entries[i].name] instanceof YEnumItem;
          d.type = typeof o[entries[i].name];
          if (d.type == "object" && o[entries[i].name] != null) {
            d.type = o[entries[i].name].constructor.name;
          }
        }
      }
    }
    for (let i = 0; i < entries.length; i++) {
      if (entries[i].name.startsWith("ATTR_")) {
        let p2 = entries[i].name.indexOf("__");
        if (p2 < 5)
          debugger;
        let name = entries[i].name.substring(5, p2);
        let attr = entries[i].name.substring(p2 + 2);
        if (typeof res.byName[name] === "undefined")
          debugger;
        try {
          res.byName[name].Attributes[attr] = Reflect.get(o, entries[i].name);
        } catch (e) {
          debugger;
        }
      }
    }
    o["PropertiesDescriptionCache"] = res;
    return res;
  }
  ApplyAllPropertiesEx(target, filter, direction) {
    let properties = _GenericProperties.getAllProperties(this);
    for (let fullpropname in properties.byName) {
      if (properties.byName[fullpropname].isWritable) {
        let index = fullpropname.indexOf("_");
        if (index < 0)
          throw "invalid Property name";
        let propType = fullpropname.substring(0, index);
        let propname = fullpropname.substring(index + 1);
        let path = [];
        path.push(propname);
        if (target instanceof YWidget && propType == "Form") {
          this.ApplyProperties(this, target, fullpropname, Reflect.get(this, fullpropname), path, direction);
        }
        if (target instanceof YAngularGauge && propType == "AngularGauge") {
          this.ApplyProperties(this, target, fullpropname, Reflect.get(this, fullpropname), path, direction);
        }
        if (target instanceof YDigitalDisplay && propType == "display") {
          this.ApplyProperties(this, target, fullpropname, Reflect.get(this, fullpropname), path, direction);
        }
        if (target instanceof YSolidGauge && propType == "SolidGauge") {
          this.ApplyProperties(this, target, fullpropname, Reflect.get(this, fullpropname), path, direction);
        }
        if (target instanceof YGraph && propType == "Graph") {
          this.ApplyProperties(this, target, fullpropname, Reflect.get(this, fullpropname), path, direction);
        }
      }
    }
  }
};
GenericProperties.XmlFileVersion = -1;

// obj/full/properties.js
var GenericHints = class {
};
GenericHints.DevConfAffected = " Changing this value will affect the device configuration.";
GenericHints.CheckSensor = "If the sensor you want to use is connected, but not listed or listed as OFFLINE, check USB / Network configuration in the global configuration.";
GenericHints.AnnotationGraph = "Annotation text.  Use \\n for carriage returns. Some variables are available: $DAY$ $MONTH$ $YEAR$ for date, $HOUR$ $MINUTE$ $SECOND$ for time, $AVGVALUE1$ $MINVALUE1$ $MAXVALUE1$ $NAME1$ $UNIT1$  for first series data, $AVGVALUE2$ $MINVALUE2$ $MAXVALUE2$ $NAME2$ $UNIT2$  for second series data and so on";
GenericHints.Annotation = "Annotation text.  Use \\n for carriage returns. Some variables are available: $DAY$ $MONTH$ $YEAR$ for date, $HOUR$ $MINUTE$ $SECOND$ for time, $AVGVALUE$ $MINVALUE$ $MAXVALUE$ $NAME$ $UNIT$ for sensor related data.";
var TypeDescription = class {
  static get AllowedValues() {
    return [];
  }
};
var sensorFreqTypeDescription = class {
  static get AllowedValues() {
    return this._AllowedValues;
  }
};
sensorFreqTypeDescription._AllowedValues = [
  "25/s",
  "10/s",
  "5/s",
  "4/s",
  "3/s",
  "2/s",
  "1/s",
  "60/m",
  "30/m",
  "12/m",
  "6/m",
  "4/m",
  "3/m",
  "2/m",
  "1/m",
  "30/h",
  "12/h",
  "6/h",
  "4/h",
  "3/h",
  "2/h",
  "1/h"
];
var yAxisDescription = class _yAxisDescription {
  static initialize() {
    let yaxiscount = 0;
    let obj = new GraphFormProperties(null, null);
    let names = Object.getOwnPropertyNames(obj);
    names.forEach((name) => {
      if (name.startsWith("_Graph_yAxes"))
        yaxiscount++;
    });
    for (let i = 0; i < yaxiscount; i++) {
      switch (i) {
        case 0:
          this._AllowedValues.push("1rst Y axis");
          break;
        case 1:
          this._AllowedValues.push("2nd Y axis");
          break;
        case 2:
          this._AllowedValues.push("3rd Y axis");
          break;
        default:
          this._AllowedValues.push((i + 1).toString() + "th Y axis");
          break;
      }
    }
    _yAxisDescription.initialized = true;
  }
  static get AllowedValues() {
    if (!_yAxisDescription.initialized)
      _yAxisDescription.initialize();
    return this._AllowedValues;
  }
};
yAxisDescription._AllowedValues = [];
var AlarmTestTypeDescription = class {
  static get AllowedValues() {
    return this._AllowedValues;
  }
};
AlarmTestTypeDescription._AllowedValues = ["Disabled", ">", ">=", "=", "<=", "<"];
var fontNameTypeDescription = class {
  static fontNameTypeDescription() {
  }
  static get AllowedValues() {
    return this._AllowedValues;
  }
};
fontNameTypeDescription._AllowedValues = [
  "Arial",
  "Brush Script MT",
  "Comic Sans MS",
  "Courier New",
  "Garamond",
  "Georgia",
  "Helvetica",
  "Impact",
  "Tahoma",
  "Times New Roman",
  "Trebuchet MS",
  "Verdana"
];
var sensorPrecisionTypeDescription = class {
  static get AllowedValues() {
    return this._AllowedValues;
  }
};
sensorPrecisionTypeDescription._AllowedValues = ["0", "0.1", "0.12", "0.123"];
var sensorDataTypeDescription = class {
  static get AllowedValues() {
    return this._AllowedValues;
  }
};
sensorDataTypeDescription._AllowedValues = ["Avg values", "Min values", "Max values"];
var GaugeFormProperties = class extends GenericProperties {
  constructor(initData, owner) {
    super(owner);
    this._DataSource_source = sensorsManager.getNullSensor();
    this._DataSource_precision = "0.1";
    this._DataSource_AlarmSection0 = new AlarmSection(0);
    this._DataSource_AlarmSection1 = new AlarmSection(1);
    this._SolidGauge_min = 0;
    this._SolidGauge_max = 100;
    this._SolidGauge_showMinMax = true;
    this._SolidGauge_color1 = YColor.LightGreen;
    this._SolidGauge_color2 = YColor.Red;
    this._SolidGauge_font = new FontDescription("Arial", 20, YColor.Black, false, true);
    this._SolidGauge_minMaxFont = new FontDescription("Arial", 10, YColor.Black, false, true);
    this._SolidGauge_displayMode = YSolidGauge.DisplayMode.DISPLAY90;
    this._SolidGauge_borderColor = YColor.Black;
    this._SolidGauge_borderThickness = 2;
    this._SolidGauge_backgroundColor1 = YColor.FromArgb(255, 240, 240, 240);
    this._SolidGauge_backgroundColor2 = YColor.FromArgb(255, 200, 200, 200);
    this._SolidGauge_thickness = 25;
    this._SolidGauge_maxSpeed = 1;
    this._annotationPanels0 = new AnnotationPanelDescription(GenericPanel.HorizontalAlignPos.CENTER, GenericPanel.VerticalAlignPos.BOTTOM, 0, false, "$NAME$", YColor.FromArgb(0, 127, 127, 127), YColor.FromArgb(0, 127, 127, 127), 10, YColor.FromArgb(255, 0, 0, 0));
    this._annotationPanels1 = new AnnotationPanelDescription(GenericPanel.HorizontalAlignPos.CENTER, GenericPanel.VerticalAlignPos.BOTTOM, 0, false, "$NAME$", YColor.FromArgb(0, 127, 127, 127), YColor.FromArgb(0, 127, 127, 127), 10, YColor.FromArgb(255, 0, 0, 0));
    this.initFromXmlData(initData);
    this.PropagateDataSourceChange();
  }
  PropagateDataSourceChange() {
    this.ownerForm.SourceChanged(this._DataSource_source, 0);
    let props = GenericProperties.getAllProperties(this).byName;
    let name;
    for (name in props) {
      if (name.startsWith("DataSource_AlarmSection")) {
        this[name].setDataSource(this._DataSource_source);
      }
    }
  }
  get ATTR_DataSource_source__DisplayName() {
    return "Sensor";
  }
  get ATTR_DataSource_source__CategoryAttribute() {
    return "Data source";
  }
  get ATTR_DataSource_source__ParamCategorySummaryAttribute() {
    return "sensorDescription";
  }
  get ATTR_DataSource_source__PreExpandedCategoryAttribute() {
    return true;
  }
  get ATTR_DataSource_source__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_DataSource_source__DescriptionAttribute() {
    return "Yoctopuce sensor feeding the gauge. ";
  }
  get DataSource_source() {
    return this._DataSource_source;
  }
  set DataSource_source(value) {
    let prev = this._DataSource_source;
    this._DataSource_source = value;
    this.PropagateDataSourceChange();
    if (this._DataSource_source != prev)
      constants.edited = true;
  }
  get sensorDescription() {
    return this._DataSource_source instanceof NullYSensor ? "none" : this._DataSource_source.get_friendlyName();
  }
  get isSensorReadOnly() {
    return this._DataSource_source.isReadOnly;
  }
  get ATTR_DataSource_freq__DisplayName() {
    return "Sensor freq";
  }
  get ATTR_DataSource_freq__CategoryAttribute() {
    return "Data source";
  }
  get ATTR_DataSource_freq__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_DataSource_freq__IsReadonlyCallAttribute() {
    return this.isSensorReadOnly;
  }
  get ATTR_DataSource_freq__AllowedValues() {
    return sensorFreqTypeDescription.AllowedValues;
  }
  get ATTR_DataSource_freq__DescriptionAttribute() {
    return "Sensor data acquisition frequency." + GenericHints.DevConfAffected;
  }
  get DataSource_freq() {
    return this._DataSource_source.get_frequency();
  }
  set DataSource_freq(value) {
    this._DataSource_source.set_frequency(value);
  }
  get ATTR_DataSource_precision__DisplayName() {
    return "Precision";
  }
  get ATTR_DataSource_precision__CategoryAttribute() {
    return "Data source";
  }
  get ATTR_DataSource_precision__DescriptionAttribute() {
    return "How many digits shown after the decimal point";
  }
  get ATTR_DataSource_precision__AllowedValues() {
    return sensorPrecisionTypeDescription.AllowedValues;
  }
  get DataSource_precision() {
    return this._DataSource_precision;
  }
  set DataSource_precision(value) {
    this._DataSource_precision = value;
  }
  get ATTR_DataSource_AlarmSection0__DisplayName() {
    return "Sensor value alarm 1";
  }
  get ATTR_DataSource_AlarmSection0__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_DataSource_AlarmSection0__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_DataSource_AlarmSection0__CategoryAttribute() {
    return "Data source";
  }
  get ATTR_DataSource_AlarmSection0__DescriptionAttribute() {
    return "Alarm 1 for this data source, expand for more.";
  }
  get DataSource_AlarmSection0() {
    return this._DataSource_AlarmSection0;
  }
  set DataSource_AlarmSection0(value) {
    this._DataSource_AlarmSection0 = value;
  }
  get ATTR_DataSource_AlarmSection1__DisplayName() {
    return "Sensor value alarm 2";
  }
  get ATTR_DataSource_AlarmSection1__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_DataSource_AlarmSection1__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_DataSource_AlarmSection1__CategoryAttribute() {
    return "Data source";
  }
  get ATTR_DataSource_AlarmSection1__DescriptionAttribute() {
    return "Alarm 2 for this data source, expand for more.";
  }
  get DataSource_AlarmSection1() {
    return this._DataSource_AlarmSection1;
  }
  set DataSource_AlarmSection1(value) {
    this._DataSource_AlarmSection1 = value;
  }
  get ATTR_SolidGauge_min__DisplayName() {
    return "Minimum value";
  }
  get ATTR_SolidGauge_min__CategoryAttribute() {
    return "Values range";
  }
  get ATTR_SolidGauge_min__DescriptionAttribute() {
    return "Minimum value displayable by the gauge.";
  }
  get SolidGauge_min() {
    return this._SolidGauge_min;
  }
  set SolidGauge_min(value) {
    this._SolidGauge_min = value;
  }
  get ATTR_SolidGauge_max__DisplayName() {
    return "Maximum value";
  }
  get ATTR_SolidGauge_max__CategoryAttribute() {
    return "Values range";
  }
  get ATTR_SolidGauge_max__DescriptionAttribute() {
    return "Maximum value displayable by the gauge.";
  }
  get SolidGauge_max() {
    return this._SolidGauge_max;
  }
  set SolidGauge_max(value) {
    this._SolidGauge_max = value;
  }
  get ATTR_SolidGauge_showMinMax__DisplayName() {
    return "show  min/max";
  }
  get ATTR_SolidGauge_showMinMax__CategoryAttribute() {
    return "Values range";
  }
  get ATTR_SolidGauge_showMinMax__DescriptionAttribute() {
    return "Show the min / max values.";
  }
  get SolidGauge_showMinMax() {
    return this._SolidGauge_showMinMax;
  }
  set SolidGauge_showMinMax(value) {
    this._SolidGauge_showMinMax = value;
  }
  get ATTR_SolidGauge_color1__DisplayName() {
    return "Min Color";
  }
  get ATTR_SolidGauge_color1__CategoryAttribute() {
    return "Values range";
  }
  get ATTR_SolidGauge_color1__DescriptionAttribute() {
    return "Color for minimum value.";
  }
  get SolidGauge_color1() {
    return this._SolidGauge_color1;
  }
  set SolidGauge_color1(value) {
    this._SolidGauge_color1 = value;
  }
  get ATTR_SolidGauge_color2__DisplayName() {
    return "Max Color";
  }
  get ATTR_SolidGauge_color2__CategoryAttribute() {
    return "Values range";
  }
  get ATTR_SolidGauge_color2__DescriptionAttribute() {
    return "Color for maximum value.";
  }
  get SolidGauge_color2() {
    return this._SolidGauge_color2;
  }
  set SolidGauge_color2(value) {
    this._SolidGauge_color2 = value;
  }
  get ATTR_SolidGauge_font__DisplayName() {
    return "Unit  Font";
  }
  get ATTR_SolidGauge_font__CategoryAttribute() {
    return "Fonts";
  }
  get ATTR_SolidGauge_font__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_SolidGauge_font__DescriptionAttribute() {
    return "Font for displaying the value/status indicator";
  }
  get SolidGauge_font() {
    return this._SolidGauge_font;
  }
  set SolidGauge_font(value) {
    this._SolidGauge_font = value;
  }
  get ATTR_SolidGauge_minMaxFont__DisplayName() {
    return "Min Max  Font";
  }
  get ATTR_SolidGauge_minMaxFont__CategoryAttribute() {
    return "Fonts";
  }
  get ATTR_SolidGauge_minMaxFont__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_SolidGauge_minMaxFont__DescriptionAttribute() {
    return "Font for displaying min/max values";
  }
  get SolidGauge_minMaxFont() {
    return this._SolidGauge_minMaxFont;
  }
  set SolidGauge_minMaxFont(value) {
    this._SolidGauge_minMaxFont = value;
  }
  get ATTR_SolidGauge_displayMode__DisplayName() {
    return "Display mode";
  }
  get ATTR_SolidGauge_displayMode__CategoryAttribute() {
    return "Dial";
  }
  get ATTR_SolidGauge_displayMode__DescriptionAttribute() {
    return "Dial general shape";
  }
  get SolidGauge_displayMode() {
    return this._SolidGauge_displayMode;
  }
  set SolidGauge_displayMode(value) {
    this._SolidGauge_displayMode = value;
  }
  get ATTR_SolidGauge_borderColor__DisplayName() {
    return "Border color";
  }
  get ATTR_SolidGauge_borderColor__CategoryAttribute() {
    return "Dial";
  }
  get ATTR_SolidGauge_borderColor__DescriptionAttribute() {
    return "Dial border color.";
  }
  get SolidGauge_borderColor() {
    return this._SolidGauge_borderColor;
  }
  set SolidGauge_borderColor(value) {
    this._SolidGauge_borderColor = value;
  }
  get ATTR_SolidGauge_borderThickness__DisplayName() {
    return "Border thickness ";
  }
  get ATTR_SolidGauge_borderThickness__CategoryAttribute() {
    return "Dial";
  }
  get ATTR_SolidGauge_borderThickness__DescriptionAttribute() {
    return "Thickness of the dial border";
  }
  get SolidGauge_borderThickness() {
    return this._SolidGauge_borderThickness;
  }
  set SolidGauge_borderThickness(value) {
    this._SolidGauge_borderThickness = value;
  }
  get ATTR_SolidGauge_backgroundColor1__DisplayName() {
    return "Background color 1";
  }
  get ATTR_SolidGauge_backgroundColor1__CategoryAttribute() {
    return "Dial";
  }
  get ATTR_SolidGauge_backgroundColor1__DescriptionAttribute() {
    return "Dial background gradient color 1.";
  }
  get SolidGauge_backgroundColor1() {
    return this._SolidGauge_backgroundColor1;
  }
  set SolidGauge_backgroundColor1(value) {
    this._SolidGauge_backgroundColor1 = value;
  }
  get ATTR_SolidGauge_backgroundColor2__DisplayName() {
    return "Background color 2";
  }
  get ATTR_SolidGauge_backgroundColor2__CategoryAttribute() {
    return "Dial";
  }
  get ATTR_SolidGauge_backgroundColor2__DescriptionAttribute() {
    return "Dial background gradient color 2.";
  }
  get SolidGauge_backgroundColor2() {
    return this._SolidGauge_backgroundColor2;
  }
  set SolidGauge_backgroundColor2(value) {
    this._SolidGauge_backgroundColor2 = value;
  }
  get ATTR_SolidGauge_thickness__DisplayName() {
    return "Dial thickness (%) ";
  }
  get ATTR_SolidGauge_thickness__CategoryAttribute() {
    return "Dial";
  }
  get ATTR_SolidGauge_thickness__DescriptionAttribute() {
    return "Thickness of the dial, in percentage relative to radius";
  }
  get SolidGauge_thickness() {
    return this._SolidGauge_thickness;
  }
  set SolidGauge_thickness(value) {
    this._SolidGauge_thickness = value;
  }
  get ATTR_SolidGauge_maxSpeed__DisplayName() {
    return "Max speed (%) ";
  }
  get ATTR_SolidGauge_maxSpeed__CategoryAttribute() {
    return "Dial";
  }
  get ATTR_SolidGauge_maxSpeed__DescriptionAttribute() {
    return 'Maximum speed of the dial in percentage relative to Max-Min. This is meant to limit "teleporting" effects.';
  }
  get SolidGauge_maxSpeed() {
    return this._SolidGauge_maxSpeed;
  }
  set SolidGauge_maxSpeed(value) {
    this._SolidGauge_maxSpeed = value;
  }
  IsDataSourceAssigned() {
    return !(this._DataSource_source instanceof NullYSensor);
  }
  get ATTR_SolidGauge_annotationPanels0__DisplayName() {
    return "Annotation 1";
  }
  get ATTR_SolidGauge_annotationPanels0__CategoryAttribute() {
    return "Annotations";
  }
  get ATTR_SolidGauge_annotationPanels0__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_SolidGauge_annotationPanels0__DescriptionAttribute() {
    return "Customizable text panels";
  }
  get SolidGauge_annotationPanels0() {
    return this._annotationPanels0;
  }
  set SolidGauge_annotationPanels0(value) {
    this._annotationPanels0 = value;
  }
  get ATTR_SolidGauge_annotationPanels1__DisplayName() {
    return "Annotation 2";
  }
  get ATTR_SolidGauge_annotationPanels1__CategoryAttribute() {
    return "Annotations";
  }
  get ATTR_SolidGauge_annotationPanels1__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_SolidGauge_annotationPanels1__DescriptionAttribute() {
    return "Customizable text panels";
  }
  get SolidGauge_annotationPanels1() {
    return this._annotationPanels1;
  }
  set SolidGauge_annotationPanels1(value) {
    this._annotationPanels1 = value;
  }
};
var AngularGaugeFormProperties = class extends GenericProperties {
  constructor(initData, owner) {
    super(owner);
    this._DataSource_source = sensorsManager.getNullSensor();
    this._DataSource_AlarmSection0 = new AlarmSection(0);
    this._DataSource_AlarmSection1 = new AlarmSection(1);
    this._AngularGauge_min = 0;
    this._AngularGauge_max = 100;
    this._AngularGauge_unitFactor = 1;
    this._AngularGauge_graduation = 10;
    this._AngularGauge_graduationSize = 10;
    this._AngularGauge_graduationOuterRadiusSize = 98;
    this._AngularGauge_graduationThickness = 2;
    this._AngularGauge_graduationColor = YColor.Black;
    this._AngularGauge_graduationFont = new FontDescription("Arial", 20, YColor.Black, false, true);
    this._AngularGauge_subgraduationCount = 5;
    this._AngularGauge_subgraduationSize = 5;
    this._AngularGauge_subgraduationThickness = 1;
    this._AngularGauge_subgraduationColor = YColor.Black;
    this._AngularGauge_needleColor = YColor.Red;
    this._AngularGauge_needleContourColor = YColor.DarkRed;
    this._AngularGauge_needleContourThickness = 1;
    this._AngularGauge_needleLength1 = 90;
    this._AngularGauge_needleLength2 = 15;
    this._AngularGauge_needleWidth = 5;
    this._AngularGauge_needleMaxSpeed = 0.5;
    this._AngularGauge_unitFont = new FontDescription("Arial", 24, YColor.DarkGray, false, true);
    this._AngularGauge_statusFont = new FontDescription("Arial", 24, YColor.DarkGray, false, true);
    this._AngularGauge_borderColor = YColor.Black;
    this._AngularGauge_borderThickness = 5;
    this._AngularGauge_backgroundColor1 = YColor.FromArgb(255, 240, 240, 240);
    this._AngularGauge_backgroundColor2 = YColor.FromArgb(255, 200, 200, 200);
    this._AngularGauge_zones0 = new AngularZoneDescription(0, 50, YColor.LightGreen);
    this._AngularGauge_zones1 = new AngularZoneDescription(50, 80, YColor.Yellow);
    this._AngularGauge_zones2 = new AngularZoneDescription(80, 100, YColor.Red);
    this._annotationPanels0 = new AnnotationPanelDescription(GenericPanel.HorizontalAlignPos.CENTER, GenericPanel.VerticalAlignPos.CENTER, 0, false, "$NAME$", YColor.FromArgb(0, 127, 127, 127), YColor.FromArgb(0, 127, 127, 127), 10, YColor.FromArgb(255, 0, 0, 0));
    this._annotationPanels1 = new AnnotationPanelDescription(GenericPanel.HorizontalAlignPos.CENTER, GenericPanel.VerticalAlignPos.BOTTOM, 0, false, "$NAME$", YColor.FromArgb(0, 127, 127, 127), YColor.FromArgb(0, 127, 127, 127), 10, YColor.FromArgb(255, 0, 0, 0));
    this.initFromXmlData(initData);
    this.PropagateDataSourceChange();
  }
  PropagateDataSourceChange() {
    this.ownerForm.SourceChanged(this._DataSource_source, 0);
    let props = GenericProperties.getAllProperties(this).byName;
    let name;
    for (name in props) {
      if (name.startsWith("DataSource_AlarmSection")) {
        this[name].setDataSource(this._DataSource_source);
      }
    }
  }
  IsDataSourceAssigned() {
    return !(this._DataSource_source instanceof NullYSensor);
  }
  get ATTR_DataSource_source__DisplayName() {
    return "Sensor";
  }
  get ATTR_DataSource_source__CategoryAttribute() {
    return "Data source";
  }
  get ATTR_DataSource_source__ParamCategorySummaryAttribute() {
    return "sensorDescription";
  }
  get ATTR_DataSource_source__PreExpandedCategoryAttribute() {
    return true;
  }
  get ATTR_DataSource_source__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_DataSource_source__DescriptionAttribute() {
    return "Yoctopuce sensor feeding the gauge. " + GenericHints.CheckSensor;
  }
  get DataSource_source() {
    return this._DataSource_source;
  }
  set DataSource_source(value) {
    let prev = this._DataSource_source;
    this._DataSource_source = value;
    this.PropagateDataSourceChange();
    if (this._DataSource_source != prev)
      constants.edited = true;
  }
  get sensorDescription() {
    return this._DataSource_source instanceof NullYSensor ? "none" : this._DataSource_source.get_friendlyName();
  }
  get isSensorReadOnly() {
    return this._DataSource_source.isReadOnly;
  }
  get ATTR_DataSource_freq__DisplayName() {
    return "Sensor freq";
  }
  get ATTR_DataSource_freq__CategoryAttribute() {
    return "Data source";
  }
  get ATTR_DataSource_freq__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_DataSource_freq__IsReadonlyCallAttribute() {
    return "isSensorReadOnly";
  }
  get ATTR_DataSource_freq__DescriptionAttribute() {
    return "Sensor data acquisition frequency." + GenericHints.DevConfAffected;
  }
  get ATTR_DataSource_freq__AllowedValues() {
    return sensorFreqTypeDescription.AllowedValues;
  }
  get DataSource_freq() {
    return this._DataSource_source.get_frequency();
  }
  set DataSource_freq(value) {
    this._DataSource_source.set_frequency(value);
  }
  get ATTR_DataSource_AlarmSection0__DisplayName() {
    return "Sensor value alarm 1";
  }
  get ATTR_DataSource_AlarmSection0__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_DataSource_AlarmSection0__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_DataSource_AlarmSection0__CategoryAttribute() {
    return "Data source";
  }
  get ATTR_DataSource_AlarmSection0__DescriptionAttribute() {
    return "Alarm 1 for this data source, expand for more.";
  }
  get DataSource_AlarmSection0() {
    return this._DataSource_AlarmSection0;
  }
  set DataSource_AlarmSection0(value) {
    this._DataSource_AlarmSection0 = value;
  }
  get ATTR_DataSource_AlarmSection1__DisplayName() {
    return "Sensor value alarm 2";
  }
  get ATTR_DataSource_AlarmSection1__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_DataSource_AlarmSection1__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_DataSource_AlarmSection1__CategoryAttribute() {
    return "Data source";
  }
  get ATTR_DataSource_AlarmSection1__DescriptionAttribute() {
    return "Alarm 2 for this data source, expand for more.";
  }
  get DataSource_AlarmSection1() {
    return this._DataSource_AlarmSection1;
  }
  set DataSource_AlarmSection1(value) {
    this._DataSource_AlarmSection1 = value;
  }
  get ATTR_AngularGauge_min__DisplayName() {
    return "Minimum value";
  }
  get ATTR_AngularGauge_min__CategoryAttribute() {
    return "Gauge gradations";
  }
  get ATTR_AngularGauge_min__DescriptionAttribute() {
    return "Minimum value displayable by the gauge.";
  }
  get AngularGauge_min() {
    return this._AngularGauge_min;
  }
  set AngularGauge_min(value) {
    this._AngularGauge_min = value;
  }
  get ATTR_AngularGauge_max__DisplayName() {
    return "Maximum value";
  }
  get ATTR_AngularGauge_max__CategoryAttribute() {
    return "Gauge gradations";
  }
  get ATTR_AngularGauge_max__DescriptionAttribute() {
    return "Maximum value displayable by the gauge.";
  }
  get AngularGauge_max() {
    return this._AngularGauge_max;
  }
  set AngularGauge_max(value) {
    this._AngularGauge_max = value;
  }
  get ATTR_AngularGauge_unitFactor__DisplayName() {
    return "Unit factor";
  }
  get ATTR_AngularGauge_unitFactor__CategoryAttribute() {
    return "Gauge gradations";
  }
  get ATTR_AngularGauge_unitFactor__DescriptionAttribute() {
    return "Data  will be divided by this value before being displayed, this allows simpler gradation marks.";
  }
  get AngularGauge_unitFactor() {
    return this._AngularGauge_unitFactor;
  }
  set AngularGauge_unitFactor(value) {
    this._AngularGauge_unitFactor = value;
  }
  get ATTR_AngularGauge_graduation__DisplayName() {
    return "Main gradation steps";
  }
  get ATTR_AngularGauge_graduation__CategoryAttribute() {
    return "Gauge gradations";
  }
  get ATTR_AngularGauge_graduation__DescriptionAttribute() {
    return "Difference between two consecutive main gradation marks";
  }
  get AngularGauge_graduation() {
    return this._AngularGauge_graduation;
  }
  set AngularGauge_graduation(value) {
    this._AngularGauge_graduation = value;
  }
  get ATTR_AngularGauge_graduationSize__DisplayName() {
    return "Main gradation size (%)";
  }
  get ATTR_AngularGauge_graduationSize__CategoryAttribute() {
    return "Gauge gradations";
  }
  get ATTR_AngularGauge_graduationSize__DescriptionAttribute() {
    return "Main gradation marks size in percent, relative to dial radius";
  }
  get AngularGauge_graduationSize() {
    return this._AngularGauge_graduationSize;
  }
  set AngularGauge_graduationSize(value) {
    this._AngularGauge_graduationSize = value;
  }
  get ATTR_AngularGauge_graduationOuterRadiusSize__DisplayName() {
    return "Main gradation radius (%)";
  }
  get ATTR_AngularGauge_graduationOuterRadiusSize__CategoryAttribute() {
    return "Gauge gradations";
  }
  get ATTR_AngularGauge_graduationOuterRadiusSize__DescriptionAttribute() {
    return "Main gradation marks outer radius in percent, relative to dial radius";
  }
  get AngularGauge_graduationOuterRadiusSize() {
    return this._AngularGauge_graduationOuterRadiusSize;
  }
  set AngularGauge_graduationOuterRadiusSize(value) {
    this._AngularGauge_graduationOuterRadiusSize = value;
  }
  get ATTR_AngularGauge_graduationThickness__DisplayName() {
    return "Main gradation thickness";
  }
  get ATTR_AngularGauge_graduationThickness__CategoryAttribute() {
    return "Gauge gradations";
  }
  get ATTR_AngularGauge_graduationThickness__DescriptionAttribute() {
    return "Main gradation marks thickness";
  }
  get AngularGauge_graduationThickness() {
    return this._AngularGauge_graduationThickness;
  }
  set AngularGauge_graduationThickness(value) {
    this._AngularGauge_graduationThickness = value;
  }
  get ATTR_AngularGauge_graduationColor__DisplayName() {
    return "Main gradation color";
  }
  get ATTR_AngularGauge_graduationColor__CategoryAttribute() {
    return "Gauge gradations";
  }
  get ATTR_AngularGauge_graduationColor__DescriptionAttribute() {
    return "Main gradation marks color.";
  }
  get AngularGauge_graduationColor() {
    return this._AngularGauge_graduationColor;
  }
  set AngularGauge_graduationColor(value) {
    this._AngularGauge_graduationColor = value;
  }
  get ATTR_AngularGauge_graduationFont__DisplayName() {
    return "Main gradation font";
  }
  get ATTR_AngularGauge_graduationFont__CategoryAttribute() {
    return "Gauge gradations";
  }
  get ATTR_AngularGauge_graduationFont__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_AngularGauge_graduationFont__DescriptionAttribute() {
    return "Font used for gradation labels";
  }
  get AngularGauge_graduationFont() {
    return this._AngularGauge_graduationFont;
  }
  set AngularGauge_graduationFont(value) {
    this._AngularGauge_graduationFont = value;
  }
  get ATTR_AngularGauge_subgraduationCount__DisplayName() {
    return "Sub-gradation count";
  }
  get ATTR_AngularGauge_subgraduationCount__CategoryAttribute() {
    return "Gauge gradations";
  }
  get ATTR_AngularGauge_subgraduationCount__DescriptionAttribute() {
    return "How many sub-gradation (+1) marks between two consecutive main graduation marks";
  }
  get AngularGauge_subgraduationCount() {
    return this._AngularGauge_subgraduationCount;
  }
  set AngularGauge_subgraduationCount(value) {
    this._AngularGauge_subgraduationCount = value;
  }
  get ATTR_AngularGauge_subgraduationSize__DisplayName() {
    return "Sub-gradation size (%)";
  }
  get ATTR_AngularGauge_subgraduationSize__CategoryAttribute() {
    return "Gauge gradations";
  }
  get ATTR_AngularGauge_subgraduationSize__DescriptionAttribute() {
    return "Sub-gradation marks size in percent, relative to dial radius";
  }
  get AngularGauge_subgraduationSize() {
    return this._AngularGauge_subgraduationSize;
  }
  set AngularGauge_subgraduationSize(value) {
    this._AngularGauge_subgraduationSize = value;
  }
  get ATTR_AngularGauge_subgraduationThickness__DisplayName() {
    return "Sub-gradation thickness";
  }
  get ATTR_AngularGauge_subgraduationThickness__CategoryAttribute() {
    return "Gauge gradations";
  }
  get ATTR_AngularGauge_subgraduationThickness__DescriptionAttribute() {
    return "Sub-gradation marks thickness";
  }
  get AngularGauge_subgraduationThickness() {
    return this._AngularGauge_subgraduationThickness;
  }
  set AngularGauge_subgraduationThickness(value) {
    this._AngularGauge_subgraduationThickness = value;
  }
  get ATTR_AngularGauge_subgraduationColor__DisplayName() {
    return "Sub-gradation color";
  }
  get ATTR_AngularGauge_subgraduationColor__CategoryAttribute() {
    return "Gauge gradations";
  }
  get ATTR_AngularGauge_subgraduationColor__DescriptionAttribute() {
    return "Sub-gradation marks color.";
  }
  get AngularGauge_subgraduationColor() {
    return this._AngularGauge_subgraduationColor;
  }
  set AngularGauge_subgraduationColor(value) {
    this._AngularGauge_subgraduationColor = value;
  }
  get ATTR_AngularGauge_needleColor__DisplayName() {
    return "Needle color";
  }
  get ATTR_AngularGauge_needleColor__CategoryAttribute() {
    return "Needle";
  }
  get ATTR_AngularGauge_needleColor__DescriptionAttribute() {
    return "Needle filling color.";
  }
  get AngularGauge_needleColor() {
    return this._AngularGauge_needleColor;
  }
  set AngularGauge_needleColor(value) {
    this._AngularGauge_needleColor = value;
  }
  get ATTR_AngularGauge_needleContourColor__DisplayName() {
    return "Needle contour color";
  }
  get ATTR_AngularGauge_needleContourColor__CategoryAttribute() {
    return "Needle";
  }
  get ATTR_AngularGauge_needleContourColor__DescriptionAttribute() {
    return "Needle contour color.";
  }
  get AngularGauge_needleContourColor() {
    return this._AngularGauge_needleContourColor;
  }
  set AngularGauge_needleContourColor(value) {
    this._AngularGauge_needleContourColor = value;
  }
  get ATTR_AngularGauge_needleContourThickness__DisplayName() {
    return "Needle contour thickness";
  }
  get ATTR_AngularGauge_needleContourThickness__CategoryAttribute() {
    return "Needle";
  }
  get ATTR_AngularGauge_needleContourThickness__DescriptionAttribute() {
    return "Thickness of the needle contour";
  }
  get AngularGauge_needleContourThickness() {
    return this._AngularGauge_needleContourThickness;
  }
  set AngularGauge_needleContourThickness(value) {
    this._AngularGauge_needleContourThickness = value;
  }
  get ATTR_AngularGauge_needleLength1__DisplayName() {
    return "Needle main size (%)";
  }
  get ATTR_AngularGauge_needleLength1__CategoryAttribute() {
    return "Needle";
  }
  get ATTR_AngularGauge_needleLength1__DescriptionAttribute() {
    return "Length of the needle part pointing to gradations, in % relative to radius";
  }
  get AngularGauge_needleLength1() {
    return this._AngularGauge_needleLength1;
  }
  set AngularGauge_needleLength1(value) {
    this._AngularGauge_needleLength1 = value;
  }
  get ATTR_AngularGauge_needleLength2__DisplayName() {
    return "Needle foot size (%)";
  }
  get ATTR_AngularGauge_needleLength2__CategoryAttribute() {
    return "Needle";
  }
  get ATTR_AngularGauge_needleLength2__DescriptionAttribute() {
    return "Length of the needle part not pointing to gradations, in % relative to radius";
  }
  get AngularGauge_needleLength2() {
    return this._AngularGauge_needleLength2;
  }
  set AngularGauge_needleLength2(value) {
    this._AngularGauge_needleLength2 = value;
  }
  get ATTR_AngularGauge_needleWidth__DisplayName() {
    return "Needle width (%)";
  }
  get ATTR_AngularGauge_needleWidth__CategoryAttribute() {
    return "Needle";
  }
  get ATTR_AngularGauge_needleWidth__DescriptionAttribute() {
    return "Width of the needle, in % relative to radius";
  }
  get AngularGauge_needleWidth() {
    return this._AngularGauge_needleWidth;
  }
  set AngularGauge_needleWidth(value) {
    this._AngularGauge_needleWidth = value;
  }
  get ATTR_AngularGauge_needleMaxSpeed__DisplayName() {
    return "Needle max speed (%)";
  }
  get ATTR_AngularGauge_needleMaxSpeed__CategoryAttribute() {
    return "Needle";
  }
  get ATTR_AngularGauge_needleMaxSpeed__DescriptionAttribute() {
    return 'Needle Maximum speed, in % relative to (max-min). This is meant to limit "teleporting" effects.';
  }
  get AngularGauge_needleMaxSpeed() {
    return this._AngularGauge_needleMaxSpeed;
  }
  set AngularGauge_needleMaxSpeed(value) {
    this._AngularGauge_needleMaxSpeed = value;
  }
  get ATTR_AngularGauge_unitFont__DisplayName() {
    return "Unit Line Font";
  }
  get ATTR_AngularGauge_unitFont__CategoryAttribute() {
    return "Text lines";
  }
  get ATTR_AngularGauge_unitFont__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_AngularGauge_unitFont__DescriptionAttribute() {
    return "Font used in the text line describing unit";
  }
  get AngularGauge_unitFont() {
    return this._AngularGauge_unitFont;
  }
  set AngularGauge_unitFont(value) {
    this._AngularGauge_unitFont = value;
  }
  get ATTR_AngularGauge_statusFont__DisplayName() {
    return "Status Line Font";
  }
  get ATTR_AngularGauge_statusFont__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_AngularGauge_statusFont__CategoryAttribute() {
    return "Text lines";
  }
  get ATTR_AngularGauge_statusFont__DescriptionAttribute() {
    return "Font used in the text line gauge status";
  }
  get AngularGauge_statusFont() {
    return this._AngularGauge_statusFont;
  }
  set AngularGauge_statusFont(value) {
    this._AngularGauge_statusFont = value;
  }
  get ATTR_AngularGauge_borderColor__DisplayName() {
    return "Border color";
  }
  get ATTR_AngularGauge_borderColor__CategoryAttribute() {
    return "Dial";
  }
  get ATTR_AngularGauge_borderColor__DescriptionAttribute() {
    return "Dial border color.";
  }
  get AngularGauge_borderColor() {
    return this._AngularGauge_borderColor;
  }
  set AngularGauge_borderColor(value) {
    this._AngularGauge_borderColor = value;
  }
  get ATTR_AngularGauge_borderThickness__DisplayName() {
    return "Border thickness ";
  }
  get ATTR_AngularGauge_borderThickness__CategoryAttribute() {
    return "Dial";
  }
  get ATTR_AngularGauge_borderThickness__DescriptionAttribute() {
    return "Thickness of the dial border";
  }
  get AngularGauge_borderThickness() {
    return this._AngularGauge_borderThickness;
  }
  set AngularGauge_borderThickness(value) {
    this._AngularGauge_borderThickness = value;
  }
  get ATTR_AngularGauge_backgroundColor1__DisplayName() {
    return "Background color 1";
  }
  get ATTR_AngularGauge_backgroundColor1__CategoryAttribute() {
    return "Dial";
  }
  get ATTR_AngularGauge_backgroundColor1__DescriptionAttribute() {
    return "Dial background gradient color 1.";
  }
  get AngularGauge_backgroundColor1() {
    return this._AngularGauge_backgroundColor1;
  }
  set AngularGauge_backgroundColor1(value) {
    this._AngularGauge_backgroundColor1 = value;
  }
  get ATTR_AngularGauge_backgroundColor2__DisplayName() {
    return "Background color 2";
  }
  get ATTR_AngularGauge_backgroundColor2__CategoryAttribute() {
    return "Dial";
  }
  get ATTR_AngularGauge_backgroundColor2__DescriptionAttribute() {
    return "Dial background gradient color 2.";
  }
  get AngularGauge_backgroundColor2() {
    return this._AngularGauge_backgroundColor2;
  }
  set AngularGauge_backgroundColor2(value) {
    this._AngularGauge_backgroundColor2 = value;
  }
  get ATTR_AngularGauge_zones0__DisplayName() {
    return "Zone 1";
  }
  get ATTR_AngularGauge_zones0__CategoryAttribute() {
    return "Zones";
  }
  get ATTR_AngularGauge_zones0__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_AngularGauge_zones0__DescriptionAttribute() {
    return "Zone 1 parameters";
  }
  get AngularGauge_zones0() {
    return this._AngularGauge_zones0;
  }
  set AngularGauge_zones0(value) {
    this._AngularGauge_zones0 = value;
  }
  get ATTR_AngularGauge_zones1__DisplayName() {
    return "Zone 2";
  }
  get ATTR_AngularGauge_zones1__CategoryAttribute() {
    return "Zones";
  }
  get ATTR_AngularGauge_zones1__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_AngularGauge_zones1__DescriptionAttribute() {
    return "Zone 2 parameters";
  }
  get AngularGauge_zones1() {
    return this._AngularGauge_zones1;
  }
  set AngularGauge_zones1(value) {
    this._AngularGauge_zones1 = value;
  }
  get ATTR_AngularGauge_zones2__DisplayName() {
    return "Zone 2";
  }
  get ATTR_AngularGauge_zones2__CategoryAttribute() {
    return "Zones";
  }
  get ATTR_AngularGauge_zones2__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_AngularGauge_zones2__DescriptionAttribute() {
    return "Zone 2 parameters";
  }
  get AngularGauge_zones2() {
    return this._AngularGauge_zones2;
  }
  set AngularGauge_zones2(value) {
    this._AngularGauge_zones2 = value;
  }
  get ATTR_AngularGauge_annotationPanels0__DisplayName() {
    return "Annotation 1";
  }
  get ATTR_AngularGauge_annotationPanels0__CategoryAttribute() {
    return "Annotations";
  }
  get ATTR_AngularGauge_annotationPanels0__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_AngularGauge_annotationPanels0__DescriptionAttribute() {
    return "Customizable text panels";
  }
  get AngularGauge_annotationPanels0() {
    return this._annotationPanels0;
  }
  set AngularGauge_annotationPanels0(value) {
    this._annotationPanels0 = value;
  }
  get ATTR_AngularGauge_annotationPanels1__DisplayName() {
    return "Annotation 2";
  }
  get ATTR_AngularGauge_annotationPanels1__CategoryAttribute() {
    return "Annotations";
  }
  get ATTR_AngularGauge_annotationPanels1__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_AngularGauge_annotationPanels1__DescriptionAttribute() {
    return "Customizable text panels";
  }
  get AngularGauge_annotationPanels1() {
    return this._annotationPanels1;
  }
  set AngularGauge_annotationPanels1(value) {
    this._annotationPanels1 = value;
  }
};
var ZoneDescription = class {
  constructor(min, max, color) {
    this._visible = false;
    this._min = min;
    this._max = max;
    this._color = color;
  }
  get summary() {
    return this._visible ? this._min.toString() + ".." + this._max.toString() : "Disabled";
  }
  get ATTR_visible__DisplayName() {
    return "Visible";
  }
  get ATTR_visible__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_visible__DescriptionAttribute() {
    return "Zone visibility.";
  }
  get visible() {
    return this._visible;
  }
  set visible(value) {
    this._visible = value;
  }
  get ATTR_min__DisplayName() {
    return "Minimum value";
  }
  get ATTR_min__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_min__DescriptionAttribute() {
    return "Zone minimum value";
  }
  get min() {
    return this._min;
  }
  set min(value) {
    this._min = value;
  }
  get ATTR_max__DisplayName() {
    return "Maximum value";
  }
  get ATTR_max__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_max__DescriptionAttribute() {
    return "Zone maximum value";
  }
  get max() {
    return this._max;
  }
  set max(value) {
    this._max = value;
  }
  get ATTR_color__DisplayName() {
    return "Color";
  }
  get ATTR_color__DescriptionAttribute() {
    return "Zone color";
  }
  get color() {
    return this._color;
  }
  set color(value) {
    this._color = value;
  }
};
var AngularZoneDescription = class {
  constructor(min, max, color) {
    this._width = 5;
    this._outerRadius = 98;
    this._visible = false;
    this._min = min;
    this._max = max;
    this._color = color;
  }
  get summary() {
    return this._visible ? this._min.toString() + ".." + this._max.toString() : "Disabled";
  }
  get ATTR_visible__DisplayName() {
    return "Visible";
  }
  get ATTR_visible__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_visible__DescriptionAttribute() {
    return "Zone visibility.";
  }
  get visible() {
    return this._visible;
  }
  set visible(value) {
    this._visible = value;
  }
  get ATTR_min__DisplayName() {
    return "Minimum value";
  }
  get ATTR_min__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_min__DescriptionAttribute() {
    return "Zone minimum value";
  }
  get min() {
    return this._min;
  }
  set min(value) {
    this._min = value;
  }
  get ATTR_max__DisplayName() {
    return "Maximum value";
  }
  get ATTR_max__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_max__DescriptionAttribute() {
    return "Zone maximum value";
  }
  get max() {
    return this._max;
  }
  set max(value) {
    this._max = value;
  }
  get ATTR_color__DisplayName() {
    return "Color";
  }
  get ATTR_color__DescriptionAttribute() {
    return "Zone color";
  }
  get color() {
    return this._color;
  }
  set color(value) {
    this._color = value;
  }
  get ATTR_outerRadius__DisplayName() {
    return "Outer radius (%)";
  }
  get ATTR_outerRadius__DescriptionAttribute() {
    return "Zone outer radius, in percentage relative to dial radius ";
  }
  get outerRadius() {
    return this._outerRadius;
  }
  set outerRadius(value) {
    this._outerRadius = value;
  }
  get ATTR_width__DisplayName() {
    return "Width (%)";
  }
  get ATTR_width__DescriptionAttribute() {
    return "Zone  width, in percentage relative to dial radius ";
  }
  get width() {
    return this._width;
  }
  set width(value) {
    this._width = value;
  }
};
var FontDescription = class {
  constructor(name, size, color, italic, bold) {
    this._name = name;
    this._size = size;
    this._color = color;
    this._italic = italic;
    this._bold = bold;
  }
  ToString() {
    return this._name + " " + this._size.toString();
  }
  get summary() {
    return this._name + " " + this._size.toString();
  }
  get ATTR_name__DisplayName() {
    return "Font name";
  }
  get ATTR_name__AllowedValues() {
    return fontNameTypeDescription.AllowedValues;
  }
  get ATTR_name__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_name__DescriptionAttribute() {
    return "Name of the font";
  }
  get name() {
    return this._name;
  }
  set name(value) {
    this._name = value;
  }
  get ATTR_size__DisplayName() {
    return "Font size";
  }
  get ATTR_size__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_size__DescriptionAttribute() {
    return "Size of the font";
  }
  get size() {
    return this._size;
  }
  set size(value) {
    this._size = Math.round(100 * value) / 100;
  }
  get ATTR_color__DisplayName() {
    return "Font color";
  }
  get ATTR_color__DescriptionAttribute() {
    return "Color of the font.";
  }
  get color() {
    return this._color;
  }
  set color(value) {
    this._color = value;
  }
  get ATTR_italic__DisplayName() {
    return "Italic";
  }
  get ATTR_italic__DescriptionAttribute() {
    return "Is the font style italic?.";
  }
  get italic() {
    return this._italic;
  }
  set italic(value) {
    this._italic = value;
  }
  get ATTR_bold__DisplayName() {
    return "Bold";
  }
  get ATTR_bold__DescriptionAttribute() {
    return "Is the font style bold?.";
  }
  get bold() {
    return this._bold;
  }
  set bold(value) {
    this._bold = value;
  }
};
var digitalDisplayFormProperties = class extends GenericProperties {
  constructor(initData, owner) {
    super(owner);
    this._DataSource_source = sensorsManager.getNullSensor();
    this._DataSource_precision = "0.1";
    this._DataSource_AlarmSection0 = new AlarmSection(0);
    this._DataSource_AlarmSection1 = new AlarmSection(1);
    this._font = new FontDescription("Arial", 48, YColor.LightGreen, false, true);
    this._display_backgroundColor1 = YColor.Black;
    this._display_backgroundColor2 = YColor.Black;
    this._hrzAlignment = YDigitalDisplay.HrzAlignment.RIGHT;
    this._hrzAlignmentOfset = 5;
    this._outOfRangeMin = new doubleNan(Number.NaN);
    this._outOfRangeMax = new doubleNan(Number.NaN);
    this._outOfRangeColor = YColor.Red;
    this._annotationPanels0 = new AnnotationPanelDescription(GenericPanel.HorizontalAlignPos.CENTER, GenericPanel.VerticalAlignPos.CENTER, 0, false, "$NAME$", YColor.FromArgb(0, 127, 127, 127), YColor.FromArgb(0, 127, 127, 127), 10, YColor.FromArgb(255, 0, 0, 0));
    this._annotationPanels1 = new AnnotationPanelDescription(GenericPanel.HorizontalAlignPos.CENTER, GenericPanel.VerticalAlignPos.BOTTOM, 0, false, "$NAME$", YColor.FromArgb(0, 127, 127, 127), YColor.FromArgb(0, 127, 127, 127), 10, YColor.FromArgb(255, 0, 0, 0));
    this.initFromXmlData(initData);
    this.PropagateDataSourceChange();
  }
  PropagateDataSourceChange() {
    this.ownerForm.SourceChanged(this._DataSource_source, 0);
    let props = GenericProperties.getAllProperties(this).byName;
    let name;
    for (name in props) {
      if (name.startsWith("DataSource_AlarmSection")) {
        this[name].setDataSource(this._DataSource_source);
      }
    }
  }
  IsDataSourceAssigned() {
    return !(this._DataSource_source instanceof NullYSensor);
  }
  get ATTR_DataSource_source__DisplayName() {
    return "Sensor";
  }
  get ATTR_DataSource_source__CategoryAttribute() {
    return "Data source";
  }
  get ATTR_DataSource_source__ParamCategorySummaryAttribute() {
    return "sensorDescription";
  }
  get ATTR_DataSource_source__PreExpandedCategoryAttribute() {
    return true;
  }
  get ATTR_DataSource_source__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_DataSource_source__DescriptionAttribute() {
    return "Yoctopuce sensor feeding the display. " + GenericHints.CheckSensor;
  }
  get DataSource_source() {
    return this._DataSource_source;
  }
  set DataSource_source(value) {
    let prev = this._DataSource_source;
    this._DataSource_source = value;
    this.PropagateDataSourceChange();
    if (this._DataSource_source != prev)
      constants.edited = true;
  }
  get isSensorReadOnly() {
    return this._DataSource_source.isReadOnly;
  }
  get sensorDescription() {
    return this._DataSource_source instanceof NullYSensor ? "none" : this._DataSource_source.get_friendlyName();
  }
  get ATTR_DataSource_freq__DisplayName() {
    return "Sensor freq";
  }
  get ATTR_DataSource_freq__CategoryAttribute() {
    return "Data source";
  }
  get ATTR_DataSource_freq__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_DataSource_freq__IsReadonlyCallAttribute() {
    return "isSensorReadOnly";
  }
  get ATTR_DataSource_freq__DescriptionAttribute() {
    return "Sensor data acquisition frequency." + GenericHints.DevConfAffected;
  }
  get ATTR_DataSource_freq__AllowedValues() {
    return sensorFreqTypeDescription.AllowedValues;
  }
  get DataSource_freq() {
    return this._DataSource_source.get_frequency();
  }
  set DataSource_freq(value) {
    this._DataSource_source.set_frequency(value);
  }
  get ATTR_DataSource_precision__DisplayName() {
    return "Precision";
  }
  get ATTR_DataSource_precision__CategoryAttribute() {
    return "Data source";
  }
  get ATTR_DataSource_precision__DescriptionAttribute() {
    return "How many digits shown after the decimal point.";
  }
  get ATTR_DataSource_precision__AllowedValues() {
    return sensorPrecisionTypeDescription.AllowedValues;
  }
  get DataSource_precision() {
    return this._DataSource_precision;
  }
  set DataSource_precision(value) {
    this._DataSource_precision = value;
  }
  get ATTR_DataSource_AlarmSection0__DisplayName() {
    return "Sensor value alarm 1";
  }
  get ATTR_DataSource_AlarmSection0__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_DataSource_AlarmSection0__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_DataSource_AlarmSection0__CategoryAttribute() {
    return "Data source";
  }
  get ATTR_DataSource_AlarmSection0__DescriptionAttribute() {
    return "Alarm 1 for this data source, expand for more.";
  }
  get DataSource_AlarmSection0() {
    return this._DataSource_AlarmSection0;
  }
  set DataSource_AlarmSection0(value) {
    this._DataSource_AlarmSection0 = value;
  }
  get ATTR_DataSource_AlarmSection1__DisplayName() {
    return "Sensor value alarm 2";
  }
  get ATTR_DataSource_AlarmSection1__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_DataSource_AlarmSection1__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_DataSource_AlarmSection1__CategoryAttribute() {
    return "Data source";
  }
  get ATTR_DataSource_AlarmSection1__DescriptionAttribute() {
    return "Alarm 2 for this data source, expand for more.";
  }
  get DataSource_AlarmSection1() {
    return this._DataSource_AlarmSection1;
  }
  set DataSource_AlarmSection1(value) {
    this._DataSource_AlarmSection1 = value;
  }
  get ATTR_display_font__DisplayName() {
    return "Font";
  }
  get ATTR_display_font__CategoryAttribute() {
    return "Display";
  }
  get ATTR_display_font__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_display_font__DescriptionAttribute() {
    return "Display font";
  }
  get display_font() {
    return this._font;
  }
  set display_font(value) {
    this._font = value;
  }
  get ATTR_display_backgroundColor1__DisplayName() {
    return "Background color 1";
  }
  get ATTR_display_backgroundColor1__CategoryAttribute() {
    return "Display";
  }
  get ATTR_display_backgroundColor1__DescriptionAttribute() {
    return "Display background gradient color 1.";
  }
  get display_backgroundColor1() {
    return this._display_backgroundColor1;
  }
  set display_backgroundColor1(value) {
    this._display_backgroundColor1 = value;
  }
  get ATTR_display_backgroundColor2__DisplayName() {
    return "Background color 2";
  }
  get ATTR_display_backgroundColor2__CategoryAttribute() {
    return "Display";
  }
  get ATTR_display_backgroundColor2__DescriptionAttribute() {
    return "Display background gradient color 2.";
  }
  get display_backgroundColor2() {
    return this._display_backgroundColor2;
  }
  set display_backgroundColor2(value) {
    this._display_backgroundColor2 = value;
  }
  get ATTR_display_hrzAlignment__DisplayName() {
    return "Hrz alignment method";
  }
  get ATTR_display_hrzAlignment__CategoryAttribute() {
    return "Display";
  }
  get ATTR_display_hrzAlignment__DescriptionAttribute() {
    return "Horizontal alignment method";
  }
  get display_hrzAlignment() {
    return this._hrzAlignment;
  }
  set display_hrzAlignment(value) {
    this._hrzAlignment = value;
  }
  get ATTR_display_hrzAlignmentOfset__DisplayName() {
    return "Hrz alignment offset";
  }
  get ATTR_display_hrzAlignmentOfset__CategoryAttribute() {
    return "Display";
  }
  get ATTR_display_hrzAlignmentOfset__DescriptionAttribute() {
    return "Horizontal alignment offset in percentage. No effect when chosen horizontal alignment is CENTER";
  }
  get display_hrzAlignmentOfset() {
    return this._hrzAlignmentOfset;
  }
  set display_hrzAlignmentOfset(value) {
    this._hrzAlignmentOfset = value;
  }
  get ATTR_display_outOfRangeMin__DisplayName() {
    return "Minimum value";
  }
  get ATTR_display_outOfRangeMin__CategoryAttribute() {
    return "Range Control";
  }
  get ATTR_display_outOfRangeMin__DescriptionAttribute() {
    return `Regular range minimum value. if value goes  outside regular  range, color will turn to "Out of range Color".Leave blank if you don't want to define such a range. `;
  }
  get display_outOfRangeMin() {
    return this._outOfRangeMin;
  }
  set display_outOfRangeMin(value) {
    this._outOfRangeMin = value;
  }
  get ATTR_display_outOfRangeMax__DisplayName() {
    return "Maximum value";
  }
  get ATTR_display_outOfRangeMax__CategoryAttribute() {
    return "Range Control";
  }
  get ATTR_display_outOfRangeMax__DescriptionAttribute() {
    return `Regular range minimum value. if value goes  outside regular  range, color will turn to "Out of range Color".Leave blank if you don't want to define such a range. `;
  }
  get display_outOfRangeMax() {
    return this._outOfRangeMax;
  }
  set display_outOfRangeMax(value) {
    this._outOfRangeMax = value;
  }
  get ATTR_display_outOfRangeColor__DisplayName() {
    return "Out of range Color";
  }
  get ATTR_display_outOfRangeColor__CategoryAttribute() {
    return "Range Control";
  }
  get ATTR_display_outOfRangeColor__DescriptionAttribute() {
    return "Digits color when value is out of range.";
  }
  get display_outOfRangeColor() {
    return this._outOfRangeColor;
  }
  set display_outOfRangeColor(value) {
    this._outOfRangeColor = value;
  }
  get ATTR_display_annotationPanels0__DisplayName() {
    return "Annotation 1";
  }
  get ATTR_display_annotationPanels0__CategoryAttribute() {
    return "Annotations";
  }
  get ATTR_display_annotationPanels0__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_display_annotationPanels0__DescriptionAttribute() {
    return "Customizable text panels";
  }
  get display_annotationPanels0() {
    return this._annotationPanels0;
  }
  set display_annotationPanels0(value) {
    this._annotationPanels0 = value;
  }
  get ATTR_display_annotationPanels1__DisplayName() {
    return "Annotation 2";
  }
  get ATTR_display_annotationPanels1__CategoryAttribute() {
    return "Annotations";
  }
  get ATTR_display_annotationPanels1__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_display_annotationPanels1__DescriptionAttribute() {
    return "Customizable text panels";
  }
  get display_annotationPanels1() {
    return this._annotationPanels1;
  }
  set display_annotationPanels1(value) {
    this._annotationPanels1 = value;
  }
};
var AlarmSection = class {
  get summary() {
    let c = this._sensor.getAlarmCondition(this._index);
    if (c == 0)
      return "Disabled";
    return "Enabled";
  }
  constructor(index) {
    this._sensor = sensorsManager.getNullSensor();
    this._index = 0;
    this._index = index;
  }
  get ATTR_source__DisplayName() {
    return "Data source type";
  }
  get ATTR_source__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_source__AllowedValues() {
    return sensorDataTypeDescription.AllowedValues;
  }
  get ATTR_source__DescriptionAttribute() {
    return "Alarm sensor data source (Average, minimum or maximum value during last interval)";
  }
  get source() {
    return this._sensor.getAlarmSource(this._index);
  }
  set source(value) {
    this._sensor.setAlarmSource(this._index, value);
  }
  get ATTR_condition__DisplayName() {
    return "Test Condition";
  }
  get ATTR_condition__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_condition__AllowedValues() {
    return AlarmTestTypeDescription.AllowedValues;
  }
  get ATTR_condition__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_condition__DescriptionAttribute() {
    return "Alarm trigger condition";
  }
  get condition() {
    return this._sensor.getAlarmCondition(this._index);
  }
  set condition(value) {
    this._sensor.setAlarmCondition(this._index, value);
  }
  setDataSource(sensor) {
    this._sensor = sensor;
  }
  get ATTR_value__DisplayName() {
    return "Test Value";
  }
  get ATTR_value__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_value__DescriptionAttribute() {
    return "Value for the Alarm trigger";
  }
  get value() {
    return this._sensor.getAlarmValue(this._index);
  }
  set value(value) {
    this._sensor.setAlarmValue(this._index, value);
  }
  get ATTR_commandLine__DisplayName() {
    return "Trigger action";
  }
  get ATTR_commandLine__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_commandLine__DescriptionAttribute() {
    return "Javascript snippet executed in your browser each time the alarm is triggered, you can use the following variables: $SENSORVALUE$, $UNIT$, $HWDID$, $NAME$, $CONDITION$, $TRIGGER$, $DATATYPE$, $NOW$. Example : <tt>alarm('Warning $NAME$ = $SENSORVALUE$!');</tt>. You can check logs to find out if your alarm code works.";
  }
  get commandLine() {
    return this._sensor.getAlarmCommandline(this._index);
  }
  set commandLine(value) {
    this._sensor.setAlarmCommandline(this._index, value);
  }
  get ATTR_delay__DisplayName() {
    return "Trigger delay";
  }
  get ATTR_delay__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_delay__DescriptionAttribute() {
    return "Minimum delay, in seconds, between two alarms. Think carefully and make sure you won't create alarm storms.";
  }
  get delay() {
    return this._sensor.getAlarmDelay(this._index);
  }
  set delay(value) {
    this._sensor.setAlarmDelay(this._index, value);
  }
};
var ChartSerie = class {
  constructor(defaultColor) {
    this.ownerForm = null;
    this.index = -1;
    this.dataloggerAlreadyLoaded = false;
    this._DataSource_source = sensorsManager.getNullSensor();
    this._DataType = 0;
    this._thickness = 2;
    this._legend = "";
    this._color = YColor.Red;
    this._yAxisIndex = 0;
    this._DataSource_AlarmSection0 = new AlarmSection(0);
    this._DataSource_AlarmSection1 = new AlarmSection(1);
    this._color = defaultColor;
  }
  get summary() {
    return this._DataSource_source instanceof NullYSensor ? "none" : this._DataSource_source.get_friendlyName() + (this._DataSource_source.isOnline() ? "" : " - OFFLINE");
  }
  Init(owner, serieIndex) {
    this.ownerForm = owner;
    this.index = serieIndex;
    this.PropagateDataSourceChange(this._DataSource_source);
  }
  PropagateDataSourceChange(value) {
    this.ownerForm.SourceChanged(this._DataSource_source, this.index);
    let props = GenericProperties.getAllProperties(this).byName;
    let name;
    for (name in props) {
      if (name.startsWith("DataSource_AlarmSection")) {
        this[name].setDataSource(this._DataSource_source);
      }
    }
  }
  get ATTR_DataSource_source__DisplayName() {
    return "Sensor";
  }
  get ATTR_DataSource_source__CopyToTarget() {
    return false;
  }
  // this extra property doesn't exist is graph's series
  get ATTR_DataSource_source__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_DataSource_source__DescriptionAttribute() {
    return "Yoctopuce sensor feeding the graph. " + GenericHints.CheckSensor;
  }
  get DataSource_source() {
    return this._DataSource_source;
  }
  set DataSource_source(value) {
    let prev = this._DataSource_source;
    this._DataSource_source = value;
    if (this._DataSource_source != prev)
      constants.edited = true;
    if (this.ownerForm != null)
      this.PropagateDataSourceChange(this._DataSource_source);
  }
  get isSensorReadOnly() {
    return this._DataSource_source.isReadOnly;
  }
  get ATTR_DataSource_freq__DisplayName() {
    return "Sensor frequency";
  }
  get ATTR_DataSource_freq__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_DataSource_freq__CopyToTarget() {
    return false;
  }
  // this extra property doesn't exist is graph's series
  get ATTR_DataSource_freq__IsReadonlyCallAttribute() {
    return "isSensorReadOnly";
  }
  get ATTR_DataSource_freq__DescriptionAttribute() {
    return "Sensor data acquisition frequency." + GenericHints.DevConfAffected;
  }
  get ATTR_DataSource_freq__AllowedValues() {
    return sensorFreqTypeDescription.AllowedValues;
  }
  get DataSource_freq() {
    return this._DataSource_source.get_frequency();
  }
  set DataSource_freq(value) {
    this._DataSource_source.set_frequency(value);
  }
  get ATTR_DataSource_datatype__DisplayName() {
    return "Sensor data";
  }
  get ATTR_DataSource_datatype__AllowedValues() {
    return sensorDataTypeDescription.AllowedValues;
  }
  get ATTR_DataSource_datatype__CopyToTarget() {
    return false;
  }
  // this extra property doesn't exist is graph's series
  get ATTR_DataSource_datatype__DescriptionAttribute() {
    return "Which data for sensor are displayed on the graph. Min and Max are available only for frequencies <1Hz";
  }
  get DataSource_datatype() {
    return this._DataType;
  }
  set DataSource_datatype(value) {
    this._DataType = value;
    if (this.ownerForm != null) {
      this.ownerForm.SourceChanged(this._DataSource_source, this.index);
    }
  }
  get ATTR_DataSource_recording__DisplayName() {
    return "Sensor recording";
  }
  get ATTR_DataSource_recording__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_DataSource_recording__IsReadonlyCallAttribute() {
    return "isSensorReadOnly";
  }
  get ATTR_DataSource_recording__CopyToTarget() {
    return false;
  }
  // this extra property doesn't exist is graph's series
  get ATTR_DataSource_recording__DescriptionAttribute() {
    return "Enable/ disable sensor data recording in the device on-board datalogger." + GenericHints.DevConfAffected;
  }
  get DataSource_recording() {
    return this._DataSource_source.get_recording();
  }
  set DataSource_recording(value) {
    this._DataSource_source.set_recording(value);
  }
  get ATTR_thickness__DisplayName() {
    return "Thickness";
  }
  get ATTR_thickness__DescriptionAttribute() {
    return "Line thickness.";
  }
  get thickness() {
    return this._thickness;
  }
  set thickness(value) {
    this._thickness = value;
  }
  get ATTR_legend__DisplayName() {
    return "Legend";
  }
  get ATTR_legend__DescriptionAttribute() {
    return "Short description of the series.";
  }
  get legend() {
    return this._legend;
  }
  set legend(value) {
    this._legend = value;
  }
  get ATTR_color__DisplayName() {
    return "Color";
  }
  get ATTR_color__DescriptionAttribute() {
    return "Line color.";
  }
  get color() {
    return this._color;
  }
  set color(value) {
    this._color = value;
  }
  get ATTR_yAxisIndex__AllowedValues() {
    return yAxisDescription.AllowedValues;
  }
  get ATTR_yAxisIndex__DisplayName() {
    return "Y axis";
  }
  get ATTR_yAxisIndex__DescriptionAttribute() {
    return "Choose which Y axis the data with be scaled to.";
  }
  get yAxisIndex() {
    return this._yAxisIndex;
  }
  set yAxisIndex(value) {
    this._yAxisIndex = value;
  }
  get ATTR_DataSource_AlarmSection0__DisplayName() {
    return "Sensor value alarm 1";
  }
  get ATTR_DataSource_AlarmSection0__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_DataSource_AlarmSection0__DescriptionAttribute() {
    return "Alarm 1 for this data source, expand for more.";
  }
  get DataSource_AlarmSection0() {
    return this._DataSource_AlarmSection0;
  }
  set DataSource_AlarmSection0(value) {
    this._DataSource_AlarmSection0 = value;
  }
  get ATTR_DataSource_AlarmSection1__DisplayName() {
    return "Sensor value alarm 2";
  }
  get ATTR_DataSource_AlarmSection1__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_DataSource_AlarmSection1__DescriptionAttribute() {
    return "Alarm 2 for this data source, expand for more.";
  }
  get DataSource_AlarmSection1() {
    return this._DataSource_AlarmSection1;
  }
  set DataSource_AlarmSection1(value) {
    this._DataSource_AlarmSection1 = value;
  }
};
var LegendPanelDescription = class {
  constructor() {
    this._enabled = false;
    this._overlap = false;
    this._position = LegendPanel.Position.RIGHT;
    this._font = new FontDescription("Arial", 7, YColor.FromArgb(255, 32, 32, 32), false, true);
    this._bgColor = YColor.FromArgb(200, 255, 255, 255);
    this._borderColor = YColor.Black;
    this._borderthickness = 1;
    this._padding = 10;
    this._verticalMargin = 10;
    this._horizontalMargin = 10;
    this._traceWidthFactor = 1;
  }
  get summary() {
    return this._enabled ? "Enabled" : "Disabled";
  }
  get ATTR_enabled__DisplayName() {
    return "Enabled";
  }
  get ATTR_enabled__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_enabled__DescriptionAttribute() {
    return "Should the legend panel be shown or not";
  }
  get enabled() {
    return this._enabled;
  }
  set enabled(value) {
    this._enabled = value;
  }
  get ATTR_overlap__DisplayName() {
    return "Overlap";
  }
  get ATTR_overlap__DescriptionAttribute() {
    return "Can the panel overlap the graph data, or should we explicitly make space for it?";
  }
  get overlap() {
    return this._overlap;
  }
  set overlap(value) {
    this._overlap = value;
  }
  get ATTR_position__DisplayName() {
    return "Position";
  }
  get ATTR_position__DescriptionAttribute() {
    return "Position of the legend panel";
  }
  get position() {
    return this._position;
  }
  set position(value) {
    this._position = value;
  }
  get ATTR_font__DisplayName() {
    return "Font";
  }
  get ATTR_font__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_font__DescriptionAttribute() {
    return "Legend panel contents fonts";
  }
  get font() {
    return this._font;
  }
  set font(value) {
    this._font = value;
  }
  get ATTR_bgColor__DisplayName() {
    return "Background color ";
  }
  get ATTR_bgColor__DescriptionAttribute() {
    return "Legend panel background color.";
  }
  get bgColor() {
    return this._bgColor;
  }
  set bgColor(value) {
    this._bgColor = value;
  }
  get ATTR_borderColor__DisplayName() {
    return "Border color ";
  }
  get ATTR_borderColor__DescriptionAttribute() {
    return "Legend panel border color.";
  }
  get borderColor() {
    return this._borderColor;
  }
  set borderColor(value) {
    this._borderColor = value;
  }
  get ATTR_borderthickness__DisplayName() {
    return "Border thickness ";
  }
  get ATTR_borderthickness__DescriptionAttribute() {
    return "Legend panel border thickness";
  }
  get borderthickness() {
    return this._borderthickness;
  }
  set borderthickness(value) {
    this._borderthickness = value;
  }
  get ATTR_padding__DisplayName() {
    return "Padding ";
  }
  get ATTR_padding__DescriptionAttribute() {
    return "Distance between the panel border and the panel contents";
  }
  get padding() {
    return this._padding;
  }
  set padding(value) {
    this._padding = value;
  }
  get ATTR_verticalMargin__DisplayName() {
    return "Vertical margin ";
  }
  get ATTR_verticalMargin__DescriptionAttribute() {
    return "Vertical distance between the panel border and its surroundings";
  }
  get verticalMargin() {
    return this._verticalMargin;
  }
  set verticalMargin(value) {
    this._verticalMargin = value;
  }
  get ATTR_horizontalMargin__DisplayName() {
    return "Horizontal margin ";
  }
  get ATTR_horizontalMargin__DescriptionAttribute() {
    return "Distance between the panel border and its surroundings";
  }
  get horizontalMargin() {
    return this._horizontalMargin;
  }
  set horizontalMargin(value) {
    this._horizontalMargin = value;
  }
  get ATTR_traceWidthFactor__DisplayName() {
    return "Color Indicator Factor";
  }
  get ATTR_traceWidthFactor__DescriptionAttribute() {
    return "Factor used to enlarge series color indicators shown in the legend panel";
  }
  get traceWidthFactor() {
    return this._traceWidthFactor;
  }
  set traceWidthFactor(value) {
    this._traceWidthFactor = value;
  }
};
var AnnotationPanelDescription = class {
  get summary() {
    return this._enabled ? "Enabled" : "Disabled";
  }
  get ATTR_enabled__DisplayName() {
    return "Enabled";
  }
  get ATTR_enabled__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_enabled__DescriptionAttribute() {
    return "Should the annotation panel be shown or not";
  }
  get enabled() {
    return this._enabled;
  }
  set enabled(value) {
    this._enabled = value;
  }
  get ATTR_overlap__DisplayName() {
    return "Overlap";
  }
  get ATTR_overlap__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_overlap__DescriptionAttribute() {
    return "Can the annotation panel overlap the display zone, or should the display zone be squeezed to make space for the panel?";
  }
  get overlap() {
    return this._overlap;
  }
  set overlap(value) {
    this._overlap = value;
  }
  get ATTR_text__DisplayName() {
    return "Text ";
  }
  get ATTR_text__DescriptionAttribute() {
    return GenericHints.Annotation;
  }
  get text() {
    return this._text;
  }
  set text(value) {
    this._text = value;
  }
  get ATTR_panelTextAlign__DisplayName() {
    return "Text Alignment";
  }
  get ATTR_panelTextAlign__DescriptionAttribute() {
    return "How text is aligned, makes sense on multi-lines text only.";
  }
  get panelTextAlign() {
    return this._panelTextAlign;
  }
  set panelTextAlign(value) {
    this._panelTextAlign = value;
  }
  get ATTR_panelHrzAlign__DisplayName() {
    return "X Position";
  }
  get ATTR_panelHrzAlign__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_panelHrzAlign__DescriptionAttribute() {
    return "Annotation panel X position";
  }
  get panelHrzAlign() {
    return this._panelHrzAlign;
  }
  set panelHrzAlign(value) {
    this._panelHrzAlign = value;
  }
  get ATTR_positionOffsetX__DisplayName() {
    return "X Offset";
  }
  get ATTR_positionOffsetX__DescriptionAttribute() {
    return "X Position offset, in %  (overlap mode only)";
  }
  get positionOffsetX() {
    return this._positionOffsetX;
  }
  set positionOffsetX(value) {
    this._positionOffsetX = value;
  }
  get ATTR_panelVrtAlign__DisplayName() {
    return "Y Position";
  }
  get ATTR_panelVrtAlign__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_panelVrtAlign__DescriptionAttribute() {
    return "Annotation panel Y position";
  }
  get panelVrtAlign() {
    return this._panelVrtAlign;
  }
  set panelVrtAlign(value) {
    this._panelVrtAlign = value;
  }
  get ATTR_positionOffsetY__DisplayName() {
    return "Y Offset";
  }
  get ATTR_positionOffsetY__DescriptionAttribute() {
    return "Y Position offset, in %  (overlap mode only)";
  }
  get positionOffsetY() {
    return this._positionOffsetY;
  }
  set positionOffsetY(value) {
    this._positionOffsetY = value;
  }
  get ATTR_font__DisplayName() {
    return "Font";
  }
  get ATTR_font__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_font__DescriptionAttribute() {
    return "Panel font";
  }
  get font() {
    return this._font;
  }
  set font(value) {
    this._font = value;
  }
  get ATTR_bgColor__DisplayName() {
    return "Background color ";
  }
  get ATTR_bgColor__DescriptionAttribute() {
    return "Legend panel background color.";
  }
  get bgColor() {
    return this._bgColor;
  }
  set bgColor(value) {
    this._bgColor = value;
  }
  get ATTR_borderColor__DisplayName() {
    return "Border color ";
  }
  get ATTR_borderColor__DescriptionAttribute() {
    return "Legend panel border color.";
  }
  get borderColor() {
    return this._borderColor;
  }
  set borderColor(value) {
    this._borderColor = value;
  }
  get ATTR_borderthickness__DisplayName() {
    return "Border thickness ";
  }
  get ATTR_borderthickness__DescriptionAttribute() {
    return "Panel border thickness";
  }
  get borderthickness() {
    return this._borderthickness;
  }
  set borderthickness(value) {
    this._borderthickness = value;
  }
  get ATTR_padding__DisplayName() {
    return "Padding ";
  }
  get ATTR_padding__DescriptionAttribute() {
    return "Distance between the panel border and the panel contents";
  }
  get padding() {
    return this._padding;
  }
  set padding(value) {
    this._padding = value;
  }
  constructor(hrzAlignInit, vrtAlignInit, offsetY, overlap, textInit, BgColorInit, BorderColorInit, fontSizeInit, FontColorInit) {
    this._enabled = false;
    this._overlap = false;
    this._text = "Date: $DAY$/$MONTH$/$YEAR$";
    this._panelTextAlign = GenericPanel.TextAlign.CENTER;
    this._panelHrzAlign = GenericPanel.HorizontalAlignPos.CENTER;
    this._positionOffsetX = 0;
    this._panelVrtAlign = GenericPanel.VerticalAlignPos.TOP;
    this._positionOffsetY = 0;
    this._font = new FontDescription("Arial", 12, YColor.FromArgb(255, 32, 32, 32), false, false);
    this._bgColor = YColor.FromArgb(200, 255, 255, 255);
    this._borderColor = YColor.Black;
    this._borderthickness = 1;
    this._padding = 5;
    if (typeof textInit != "undefined")
      this._text = textInit;
    if (typeof offsetY != "undefined")
      this._positionOffsetY = offsetY;
    if (typeof overlap != "undefined")
      this._overlap = overlap;
    if (typeof BgColorInit != "undefined")
      this._bgColor = BgColorInit;
    if (typeof BorderColorInit != "undefined")
      this._borderColor = BorderColorInit;
    if (typeof fontSizeInit != "undefined")
      this._font.size = fontSizeInit;
    if (typeof FontColorInit != "undefined")
      this._font.color = FontColorInit;
    if (typeof vrtAlignInit != "undefined")
      this._panelVrtAlign = vrtAlignInit;
    if (typeof hrzAlignInit != "undefined")
      this._panelHrzAlign = hrzAlignInit;
  }
};
var AnnotationPanelDescriptionGraph = class extends AnnotationPanelDescription {
  // position,
  get ATTR_enabled__DisplayName() {
    return "Enabled";
  }
  get ATTR_enabled__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_enabled__DescriptionAttribute() {
    return "Should the annotation panel be shown or not";
  }
  get enabled() {
    return this._enabled;
  }
  set enabled(value) {
    this._enabled = value;
  }
  get ATTR_overlap__DisplayName() {
    return "Overlap";
  }
  get ATTR_overlap__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_overlap__DescriptionAttribute() {
    return "Can the annotation panel overlap the graph, or should the graph be squeezed to make space for the panel?";
  }
  get overlap() {
    return this._overlap;
  }
  set overlap(value) {
    this._overlap = value;
  }
  get ATTR_text__DisplayName() {
    return "Text ";
  }
  get ATTR_text__DescriptionAttribute() {
    return GenericHints.AnnotationGraph;
  }
  get text() {
    return this._text;
  }
  set text(value) {
    this._text = value;
  }
};
var DataTrackerDescription = class _DataTrackerDescription {
  constructor() {
    this._enabled = false;
    this._showSerieName = false;
    this._showTimeStamp = false;
    this._dataPrecision = _DataTrackerDescription.DataPrecision.PRECISION_NOLIMIT;
    this._diameter = 5;
    this._handleLength = 25;
    this._font = new FontDescription("Arial", 7, YColor.FromArgb(255, 32, 32, 32), false, false);
    this._bgColor = YColor.FromArgb(200, 255, 255, 255);
    this._borderColor = YColor.Black;
    this._borderthickness = 1;
    this._padding = 5;
    this._detectionDistance = 50;
  }
  get summary() {
    return this._enabled ? "Enabled" : "Disabled";
  }
  get ATTR_enabled__DisplayName() {
    return "Enabled";
  }
  get ATTR_enabled__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_enabled__DescriptionAttribute() {
    return "Should the data tracker be shown or not.";
  }
  get enabled() {
    return this._enabled;
  }
  set enabled(value) {
    this._enabled = value;
  }
  get ATTR_showSerieName__DisplayName() {
    return "Show Series legend";
  }
  get ATTR_showSerieName__DescriptionAttribute() {
    return "Should the data tracker show the value's series legend.";
  }
  get showSerieName() {
    return this._showSerieName;
  }
  set showSerieName(value) {
    this._showSerieName = value;
  }
  get ATTR_showTimeStamp__DisplayName() {
    return "Show Timestamp";
  }
  get ATTR_showTimeStamp__DescriptionAttribute() {
    return "Should the data tracker show the value's timestamp.";
  }
  get showTimeStamp() {
    return this._showTimeStamp;
  }
  set showTimeStamp(value) {
    this._showTimeStamp = value;
  }
  get ATTR_dataPrecision__DisplayName() {
    return "Precision";
  }
  get ATTR_dataPrecision__DescriptionAttribute() {
    return "A way to limit data precision to meaningful digits in the data tracker.";
  }
  get dataPrecision() {
    return this._dataPrecision;
  }
  set dataPrecision(value) {
    this._dataPrecision = value;
  }
  get ATTR_diameter__DisplayName() {
    return "Point Diameter";
  }
  get ATTR_diameter__DescriptionAttribute() {
    return "Data point diameter, in pixels";
  }
  get diameter() {
    return this._diameter;
  }
  set diameter(value) {
    this._diameter = value;
  }
  get ATTR_handleLength__DisplayName() {
    return "Handle size";
  }
  get ATTR_handleLength__DescriptionAttribute() {
    return "size of the handle between the data point and the value panel";
  }
  get handleLength() {
    return this._handleLength;
  }
  set handleLength(value) {
    this._handleLength = value;
  }
  get ATTR_font__DisplayName() {
    return "Font";
  }
  get ATTR_font__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_font__DescriptionAttribute() {
    return "Data tracker label fonts";
  }
  get font() {
    return this._font;
  }
  set font(value) {
    this._font = value;
  }
  get ATTR_bgColor__DisplayName() {
    return "Background color";
  }
  get ATTR_bgColor__DescriptionAttribute() {
    return "Value panel ground color.";
  }
  get bgColor() {
    return this._bgColor;
  }
  set bgColor(value) {
    this._bgColor = value;
  }
  get ATTR_borderColor__DisplayName() {
    return "Border color";
  }
  get ATTR_borderColor__DescriptionAttribute() {
    return "Value panel border and handle  color.";
  }
  get borderColor() {
    return this._borderColor;
  }
  set borderColor(value) {
    this._borderColor = value;
  }
  get ATTR_borderthickness__DisplayName() {
    return "Border thickness ";
  }
  get ATTR_borderthickness__DescriptionAttribute() {
    return "Value panel border and handle  thickness";
  }
  get borderthickness() {
    return this._borderthickness;
  }
  set borderthickness(value) {
    this._borderthickness = value;
  }
  get ATTR_padding__DisplayName() {
    return "Padding";
  }
  get ATTR_padding__DescriptionAttribute() {
    return "Distance between the panel border and its contents ";
  }
  get padding() {
    return this._padding;
  }
  set padding(value) {
    this._padding = value;
  }
  get ATTR_detectionDistance__DisplayName() {
    return "Detection distance";
  }
  get ATTR_detectionDistance__DescriptionAttribute() {
    return "Maximum distance, in pixels, between the mouse and a data point for data tracker to show. Use zero for infinite distance";
  }
  get detectionDistance() {
    return this._detectionDistance;
  }
  set detectionDistance(value) {
    this._detectionDistance = value;
  }
};
(function(DataTrackerDescription2) {
  class DataPrecisionEnumItem extends YEnumItem {
    constructor(value, humanreadable, container) {
      super(value, humanreadable, DataPrecision);
    }
  }
  DataTrackerDescription2.DataPrecisionEnumItem = DataPrecisionEnumItem;
  class DataPrecision extends YEnum {
  }
  DataPrecision.PRECISION_NOLIMIT = new DataPrecisionEnumItem("PRECISION_NOLIMIT", "As is");
  DataPrecision.PRECISION_1 = new DataPrecisionEnumItem("PRECISION_1", "1");
  DataPrecision.PRECISION_01 = new DataPrecisionEnumItem("PRECISION_01", "0.1");
  DataPrecision.PRECISION_001 = new DataPrecisionEnumItem("PRECISION_001", "0.01");
  DataPrecision.PRECISION_0001 = new DataPrecisionEnumItem("PRECISION_0001", "0.001");
  DataTrackerDescription2.DataPrecision = DataPrecision;
})(DataTrackerDescription || (DataTrackerDescription = {}));
var NavigatorDescription = class {
  get summary() {
    return this._enabled ? "Enabled" : "Disabled";
  }
  constructor() {
    this._enabled = false;
    this._bgColor1 = YColor.FromArgb(255, 225, 225, 225);
    this._bgColor2 = YColor.FromArgb(255, 225, 225, 225);
    this._borderThickness = 1;
    this._borderColor = YColor.Black;
    this._cursorColor = YColor.FromArgb(25, 0, 255, 0);
    this._cursorBorderColor = YColor.FromArgb(255, 96, 96, 96);
    this._yAxisHandling = Navigator.YAxisHandling.AUTO;
    this._xAxisThickness = 1;
    this._xAxisColor = YColor.Black;
    this._font = new FontDescription("Arial", 10, YColor.FromArgb(255, 32, 32, 32), false, false);
  }
  get ATTR_enabled__DisplayName() {
    return "Enabled";
  }
  get ATTR_enabled__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_enabled__DescriptionAttribute() {
    return "Should the navigator be shown or not.";
  }
  get enabled() {
    return this._enabled;
  }
  set enabled(value) {
    this._enabled = value;
  }
  get ATTR_bgColor1__DisplayName() {
    return "Background color 1";
  }
  get ATTR_bgColor1__DescriptionAttribute() {
    return "Navigator background gradient color 1.";
  }
  get bgColor1() {
    return this._bgColor1;
  }
  set bgColor1(value) {
    this._bgColor1 = value;
  }
  get ATTR_bgColor2__DisplayName() {
    return "Background color 2";
  }
  get ATTR_bgColor2__DescriptionAttribute() {
    return "Navigator background gradient color 2.";
  }
  get bgColor2() {
    return this._bgColor2;
  }
  set bgColor2(value) {
    this._bgColor2 = value;
  }
  get ATTR_borderThickness__DisplayName() {
    return "Border thickness";
  }
  get ATTR_borderThickness__DescriptionAttribute() {
    return "Navigator";
  }
  get borderThickness() {
    return this._borderThickness;
  }
  set borderThickness(value) {
    this._borderThickness = value;
  }
  get ATTR_borderColor__DisplayName() {
    return "Border color";
  }
  get ATTR_borderColor__DescriptionAttribute() {
    return "Navigator Border color.";
  }
  get borderColor() {
    return this._borderColor;
  }
  set borderColor(value) {
    this._borderColor = value;
  }
  get ATTR_cursorColor__DisplayName() {
    return "Navigator cursor fill color";
  }
  get ATTR_cursorColor__DescriptionAttribute() {
    return "Navigator";
  }
  get cursorColor() {
    return this._cursorColor;
  }
  set cursorColor(value) {
    this._cursorColor = value;
  }
  get ATTR_cursorBorderColor__DisplayName() {
    return "Navigator cursor left/right border color.";
  }
  get ATTR_cursorBorderColor__DescriptionAttribute() {
    return "Navigator";
  }
  get cursorBorderColor() {
    return this._cursorBorderColor;
  }
  set cursorBorderColor(value) {
    this._cursorBorderColor = value;
  }
  get ATTR_yAxisHandling__DisplayName() {
    return "Y axis range";
  }
  get ATTR_yAxisHandling__DescriptionAttribute() {
    return "Is navigator Y axis zoom automatic or inherited from main view settings?";
  }
  get yAxisHandling() {
    return this._yAxisHandling;
  }
  set yAxisHandling(value) {
    this._yAxisHandling = value;
  }
  get ATTR_xAxisThickness__DisplayName() {
    return "X axis thickness";
  }
  get ATTR_xAxisThickness__DescriptionAttribute() {
    return "Navigator";
  }
  get xAxisThickness() {
    return this._xAxisThickness;
  }
  set xAxisThickness(value) {
    this._xAxisThickness = value;
  }
  get ATTR_xAxisColor__DisplayName() {
    return "X axis color";
  }
  get ATTR_xAxisColor__DescriptionAttribute() {
    return "Navigator X axis color.";
  }
  get xAxisColor() {
    return this._xAxisColor;
  }
  set xAxisColor(value) {
    this._xAxisColor = value;
  }
  get ATTR_font__DisplayName() {
    return "X-Axis Font";
  }
  get ATTR_font__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_font__DescriptionAttribute() {
    return "Navigator X axis font";
  }
  get font() {
    return this._font;
  }
  set font(value) {
    this._font = value;
  }
};
var LegendDescription = class {
  constructor() {
    this._title = "";
    this._font = new FontDescription("Arial", 12, YColor.Black, false, true);
  }
  get ATTR_title__DisplayName() {
    return "Text";
  }
  get ATTR_title__DescriptionAttribute() {
    return "Legend text";
  }
  get title() {
    return this._title;
  }
  set title(value) {
    this._title = value;
  }
  get ATTR_font__DisplayName() {
    return "Font";
  }
  get ATTR_font__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_font__DescriptionAttribute() {
    return "Legend font";
  }
  get font() {
    return this._font;
  }
  set font(value) {
    this._font = value;
  }
};
var YaxisDescription = class {
  constructor(index, shown) {
    this._visible = false;
    this._position = YAxis.HrzPosition.LEFT;
    this._min = new doubleNan(Number.NaN);
    this._max = new doubleNan(Number.NaN);
    this._step = new doubleNan(Number.NaN);
    this._color = YColor.FromArgb(255, 127, 127, 127);
    this._thickness = 1;
    this._showGrid = false;
    this._gridColor = YColor.FromArgb(255, 210, 210, 210);
    this._gridThickness = 1;
    this._font = new FontDescription("Arial", 10, YColor.Black, false, false);
    this._legend = new LegendDescription();
    this._zones0 = new ZoneDescription(0, 50, YColor.LightGreen);
    this._zones1 = new ZoneDescription(50, 80, YColor.Yellow);
    this._zones2 = new ZoneDescription(80, 100, YColor.Red);
    this._visible = shown;
    this._showGrid = index == 0;
    this._position = index == 0 ? YAxis.HrzPosition.LEFT : YAxis.HrzPosition.RIGHT;
  }
  get summary() {
    return this._visible ? "Enabled" : "Disabled";
  }
  get ATTR_visible__DisplayName() {
    return "Visible";
  }
  get ATTR_visible__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_visible__DescriptionAttribute() {
    return "Should that YAxis be shown?.";
  }
  get visible() {
    return this._visible;
  }
  set visible(value) {
    this._visible = value;
  }
  get ATTR_position__DisplayName() {
    return "Position";
  }
  get ATTR_position__DescriptionAttribute() {
    return "Y Axis position (left / right)";
  }
  get position() {
    return this._position;
  }
  set position(value) {
    this._position = value;
  }
  get ATTR_min__DisplayName() {
    return "Minimum value";
  }
  get ATTR_min__DescriptionAttribute() {
    return "YAxis minimum value, leave blank for automatic behavior.";
  }
  get min() {
    return this._min;
  }
  set min(value) {
    this._min = value;
  }
  get ATTR_max__DisplayName() {
    return "Maximum value";
  }
  get ATTR_max__DescriptionAttribute() {
    return "YAxis maximum value, leave blank for automatic behavior.";
  }
  get max() {
    return this._max;
  }
  set max(value) {
    this._max = value;
  }
  get ATTR_step__DisplayName() {
    return "Steps";
  }
  get ATTR_step__DescriptionAttribute() {
    return "YAxis step size, leave blank for automatic behavior.";
  }
  get step() {
    return this._step;
  }
  set step(value) {
    this._step = value;
  }
  get ATTR_color__DisplayName() {
    return "Color";
  }
  get ATTR_color__DescriptionAttribute() {
    return "Y Axis Color.";
  }
  get color() {
    return this._color;
  }
  set color(value) {
    this._color = value;
  }
  get ATTR_thickness__DisplayName() {
    return "Thickness";
  }
  get ATTR_thickness__DescriptionAttribute() {
    return "Axis thickness";
  }
  get thickness() {
    return this._thickness;
  }
  set thickness(value) {
    this._thickness = value;
  }
  get ATTR_showGrid__DisplayName() {
    return "Show Grid";
  }
  get ATTR_showGrid__DescriptionAttribute() {
    return "Show grid horizontal lines, or not.";
  }
  get showGrid() {
    return this._showGrid;
  }
  set showGrid(value) {
    this._showGrid = value;
  }
  get ATTR_gridColor__DisplayName() {
    return "Grid Color";
  }
  get ATTR_gridColor__DescriptionAttribute() {
    return "Grid horizontal lines color.";
  }
  get gridColor() {
    return this._gridColor;
  }
  set gridColor(value) {
    this._gridColor = value;
  }
  get ATTR_gridThickness__DisplayName() {
    return "Grid thickness";
  }
  get ATTR_gridThickness__DescriptionAttribute() {
    return "Grid horizontal lines thickness";
  }
  get gridThickness() {
    return this._gridThickness;
  }
  set gridThickness(value) {
    this._gridThickness = value;
  }
  get ATTR_font__DisplayName() {
    return "Font";
  }
  get ATTR_font__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_font__DescriptionAttribute() {
    return "Axis font";
  }
  get font() {
    return this._font;
  }
  set font(value) {
    this._font = value;
  }
  get ATTR_legend__DisplayName() {
    return "Legend";
  }
  get ATTR_legend__DescriptionAttribute() {
    return "Axis legend";
  }
  get legend() {
    return this._legend;
  }
  set legend(value) {
    this._legend = value;
  }
  get ATTR_zones0__DisplayName() {
    return "Zone 1";
  }
  get ATTR_zones0__CategoryAttribute() {
    return "Zones";
  }
  get ATTR_zones0__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_zones0__DescriptionAttribute() {
    return "Zone 1 parameters";
  }
  get zones0() {
    return this._zones0;
  }
  set zones0(value) {
    this._zones0 = value;
  }
  get ATTR_zones1__DisplayName() {
    return "Zone 2";
  }
  get ATTR_zones1__CategoryAttribute() {
    return "Zones";
  }
  get ATTR_zones1__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_zones1__DescriptionAttribute() {
    return "Zone 2 parameters";
  }
  get zones1() {
    return this._zones1;
  }
  set zones1(value) {
    this._zones1 = value;
  }
  get ATTR_zones2__DisplayName() {
    return "Zone 3";
  }
  get ATTR_zones2__CategoryAttribute() {
    return "Zones";
  }
  get ATTR_zones2__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_zones2__DescriptionAttribute() {
    return "Zone 3 parameters";
  }
  get zones2() {
    return this._zones2;
  }
  set zones2(value) {
    this._zones2 = value;
  }
};
var MarkerDescription = class {
  constructor(defaultText) {
    this._enabled = false;
    this._text = "";
    this._textAlign = Marker.TextAlign.CENTER;
    this._timereference = TimeConverter.TimeReference.ABSOLUTE;
    this._positionOnXAxis = new xAxisPosition(TimeConverter.ToUnixTime(/* @__PURE__ */ new Date()), false);
    this._yposition = 95;
    this._bgColor = YColor.FromArgb(200, 255, 255, 192);
    this._borderColor = YColor.DarkRed;
    this._borderthickness = 1;
    this._padding = 5;
    this._font = new FontDescription("Arial", 7, YColor.Black, false, false);
    this._text = defaultText;
  }
  get summary() {
    return this._enabled ? "Enabled" : "Disabled";
  }
  get ATTR_enabled__DisplayName() {
    return "Enabled";
  }
  get ATTR_enabled__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_enabled__DescriptionAttribute() {
    return "Should that marker be shown?.";
  }
  get enabled() {
    return this._enabled;
  }
  set enabled(value) {
    this._enabled = value;
  }
  get ATTR_text__DisplayName() {
    return "Text";
  }
  get ATTR_text__DescriptionAttribute() {
    return "Marker text. Use \\n for multi-line text. Some variables are allowed such as $MARKERTIME$, $LEGEND1$, $VALUE1$, $UNIT1$, $LEGEND1$, $VALUE2$ etc.. Extensive use of marker variables migh make the graph rendering significantly slower.";
  }
  get text() {
    return this._text;
  }
  set text(value) {
    this._text = value;
  }
  get ATTR_textAlign__DisplayName() {
    return "Text Alignment";
  }
  get ATTR_textAlign__DescriptionAttribute() {
    return "How text is aligned, makes sense on multi-lines text only.";
  }
  get textAlign() {
    return this._textAlign;
  }
  set textAlign(value) {
    this._textAlign = value;
  }
  get ATTR_timereference__DisplayName() {
    return "Time reference";
  }
  get ATTR_timereference__ChangeCausesParentRefreshAttribute() {
    return true;
  }
  get ATTR_timereference__NotSavedInXMLAttribute() {
    return true;
  }
  get ATTR_timereference__DescriptionAttribute() {
    return "Should the marker time position be absolute or relative to first data timestamp? Note: relative markers won't be drawn until there is actual data.";
  }
  get timereference() {
    return this._timereference;
  }
  set timereference(value) {
    this._timereference = value;
    this._positionOnXAxis.relative = value == TimeConverter.TimeReference.RELATIVE;
  }
  get ATTR_positionOnXAxis__DisplayName() {
    return "Time position";
  }
  get ATTR_positionOnXAxis__DescriptionAttribute() {
    return "Marker position on X axis.";
  }
  get positionOnXAxis() {
    return this._positionOnXAxis;
  }
  set positionOnXAxis(value) {
    this._positionOnXAxis = value;
    this.timereference = value.relative ? TimeConverter.TimeReference.RELATIVE : TimeConverter.TimeReference.ABSOLUTE;
  }
  get ATTR_yposition__DisplayName() {
    return "Vrt position (%)";
  }
  get ATTR_yposition__DescriptionAttribute() {
    return "Vertical position of the marker label in % of available space. Zero is bottom";
  }
  get yposition() {
    return this._yposition;
  }
  set yposition(value) {
    this._yposition = value;
  }
  get ATTR_bgColor__DisplayName() {
    return "Background color ";
  }
  get ATTR_bgColor__DescriptionAttribute() {
    return "Marker  background color.";
  }
  get bgColor() {
    return this._bgColor;
  }
  set bgColor(value) {
    this._bgColor = value;
  }
  get ATTR_borderColor__DisplayName() {
    return "Border color ";
  }
  get ATTR_borderColor__DescriptionAttribute() {
    return "Marker border color.";
  }
  get borderColor() {
    return this._borderColor;
  }
  set borderColor(value) {
    this._borderColor = value;
  }
  get ATTR_borderthickness__DisplayName() {
    return "Border thickness ";
  }
  get ATTR_borderthickness__DescriptionAttribute() {
    return "Marker border thickness, in pixels.";
  }
  get borderthickness() {
    return this._borderthickness;
  }
  set borderthickness(value) {
    this._borderthickness = value;
  }
  get ATTR_padding__DisplayName() {
    return "Padding ";
  }
  get ATTR_padding__DescriptionAttribute() {
    return "Distance between the marker border and the marker contents, in pixels.";
  }
  get padding() {
    return this._padding;
  }
  set padding(value) {
    this._padding = value;
  }
  get ATTR_font__DisplayName() {
    return "Font";
  }
  get ATTR_font__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_font__DescriptionAttribute() {
    return "Marker Font";
  }
  get font() {
    return this._font;
  }
  set font(value) {
    this._font = value;
  }
};
var XaxisDescription = class {
  constructor() {
    this._initialZoom = 60;
    this._initialOffset = 0;
    this._position = XAxis.VrtPosition.BOTTOM;
    this._timeReference = TimeConverter.TimeReference.ABSOLUTE;
    this._overflowHandling = XAxis.OverflowHandling.SCROLL;
    this._color = YColor.FromArgb(255, 127, 127, 127);
    this._thickness = 1;
    this._showGrid = false;
    this._gridColor = YColor.FromArgb(50, 0, 0, 0);
    this._gridThickness = 1;
    this._font = new FontDescription("Arial", 10, YColor.Black, false, false);
    this._legend = new LegendDescription();
    this._markers0 = new MarkerDescription("Marker 1");
    this._markers1 = new MarkerDescription("Marker 2");
    this._markers2 = new MarkerDescription("Marker 3");
    this._markers3 = new MarkerDescription("Marker 4");
    this._markers4 = new MarkerDescription("Marker 5");
    this._markers5 = new MarkerDescription("Marker 6");
    this._markers6 = new MarkerDescription("Marker 7");
    this._markers7 = new MarkerDescription("Marker 8");
  }
  get ATTR_initialZoom__DisplayName() {
    return "Initial Zoom";
  }
  get ATTR_initialZoom__DescriptionAttribute() {
    return "Zoom level at application startup, i.e. width of the view-port in seconds.";
  }
  get initialZoom() {
    return this._initialZoom;
  }
  set initialZoom(value) {
    this._initialZoom = value;
  }
  get ATTR_initialOffset__DisplayName() {
    return "Initial Offset";
  }
  get ATTR_initialOffset__DescriptionAttribute() {
    return "Offset of the first data point in percentage of the viewport width. For instance a 50% value will put the first point in the middle of the viewport. This can be used to give some room for datalogger contents.";
  }
  get initialOffset() {
    return this._initialOffset;
  }
  set initialOffset(value) {
    this._initialOffset = value;
  }
  get ATTR_position__DisplayName() {
    return "Position";
  }
  get ATTR_position__DescriptionAttribute() {
    return "X Axis position (top / bottom)";
  }
  get position() {
    return this._position;
  }
  set position(value) {
    this._position = value;
  }
  get ATTR_timeReference__DisplayName() {
    return "Time reference";
  }
  get ATTR_timeReference__DescriptionAttribute() {
    return "Are gradation timestamps absolute or relative to experiment start time? ";
  }
  get timeReference() {
    return this._timeReference;
  }
  set timeReference(value) {
    this._timeReference = value;
  }
  get ATTR_overflowHandling__DisplayName() {
    return "Overflow Handling";
  }
  get ATTR_overflowHandling__DescriptionAttribute() {
    return "What to do when new data are about to reach the graph right border";
  }
  get overflowHandling() {
    return this._overflowHandling;
  }
  set overflowHandling(value) {
    this._overflowHandling = value;
  }
  get ATTR_color__DisplayName() {
    return "Color";
  }
  get ATTR_color__DescriptionAttribute() {
    return "X Axis Color.";
  }
  get color() {
    return this._color;
  }
  set color(value) {
    this._color = value;
  }
  get ATTR_thickness__DisplayName() {
    return "Thickness";
  }
  get ATTR_thickness__DescriptionAttribute() {
    return "Axis thickness";
  }
  get thickness() {
    return this._thickness;
  }
  set thickness(value) {
    this._thickness = value;
  }
  get ATTR_showGrid__DisplayName() {
    return "Show Grid";
  }
  get ATTR_showGrid__DescriptionAttribute() {
    return "Show grid vertical lines, or not.";
  }
  get showGrid() {
    return this._showGrid;
  }
  set showGrid(value) {
    this._showGrid = value;
  }
  get ATTR_gridColor__DisplayName() {
    return "Grid Color";
  }
  get ATTR_gridColor__DescriptionAttribute() {
    return "Grid vertical lines color.";
  }
  get gridColor() {
    return this._gridColor;
  }
  set gridColor(value) {
    this._gridColor = value;
  }
  get ATTR_gridThickness__DisplayName() {
    return "Grid thickness";
  }
  get ATTR_gridThickness__DescriptionAttribute() {
    return "Grid vertical lines thickness";
  }
  get gridThickness() {
    return this._gridThickness;
  }
  set gridThickness(value) {
    this._gridThickness = value;
  }
  get ATTR_font__DisplayName() {
    return "Font";
  }
  get ATTR_font__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_font__DescriptionAttribute() {
    return "Axis font";
  }
  get font() {
    return this._font;
  }
  set font(value) {
    this._font = value;
  }
  get ATTR_legend__DisplayName() {
    return "Legend";
  }
  get ATTR_legend__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_legend__DescriptionAttribute() {
    return "X Axis legend";
  }
  get legend() {
    return this._legend;
  }
  set legend(value) {
    this._legend = value;
  }
  get ATTR_markers0__DisplayName() {
    return "Marker 1";
  }
  get ATTR_markers0__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_markers0__CategoryAttribute() {
    return "Markers";
  }
  get ATTR_markers0__DescriptionAttribute() {
    return "First marker parameters";
  }
  get markers0() {
    return this._markers0;
  }
  set markers0(value) {
    this._markers0 = value;
  }
  get ATTR_markers1__DisplayName() {
    return "Marker 2";
  }
  get ATTR_markers1__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_markers1__CategoryAttribute() {
    return "Markers";
  }
  get ATTR_markers1__DescriptionAttribute() {
    return "Second marker parameters";
  }
  get markers1() {
    return this._markers1;
  }
  set markers1(value) {
    this._markers1 = value;
  }
  get ATTR_markers2__DisplayName() {
    return "Marker 3";
  }
  get ATTR_markers2__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_markers2__CategoryAttribute() {
    return "Markers";
  }
  get ATTR_markers2__DescriptionAttribute() {
    return "Third marker parameters";
  }
  get markers2() {
    return this._markers2;
  }
  set markers2(value) {
    this._markers2 = value;
  }
  get ATTR_markers3__DisplayName() {
    return "Marker 4";
  }
  get ATTR_markers3__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_markers3__CategoryAttribute() {
    return "Markers";
  }
  get ATTR_markers3__DescriptionAttribute() {
    return "Fourth marker parameters";
  }
  get markers3() {
    return this._markers3;
  }
  set markers3(value) {
    this._markers3 = value;
  }
  get ATTR_markers4__DisplayName() {
    return "Marker 5";
  }
  get ATTR_markers4__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_markers4__CategoryAttribute() {
    return "Markers";
  }
  get ATTR_markers4__DescriptionAttribute() {
    return "Fith marker parameters";
  }
  get markers4() {
    return this._markers4;
  }
  set markers4(value) {
    this._markers4 = value;
  }
  get ATTR_markers5__DisplayName() {
    return "Marker 6";
  }
  get ATTR_markers5__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_markers5__CategoryAttribute() {
    return "Markers";
  }
  get ATTR_markers5__DescriptionAttribute() {
    return "Sixth marker parameters";
  }
  get markers5() {
    return this._markers5;
  }
  set markers5(value) {
    this._markers5 = value;
  }
  get ATTR_markers6__DisplayName() {
    return "Marker 7";
  }
  get ATTR_markers6__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_markers6__CategoryAttribute() {
    return "Markers";
  }
  get ATTR_markers6__DescriptionAttribute() {
    return "Seventh marker parameters";
  }
  get markers6() {
    return this._markers6;
  }
  set markers6(value) {
    this._markers6 = value;
  }
  get ATTR_markers7__DisplayName() {
    return "Marker 8";
  }
  get ATTR_markers7__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_markers7__CategoryAttribute() {
    return "Markers";
  }
  get ATTR_markers7__DescriptionAttribute() {
    return "Height marker parameters";
  }
  get markers7() {
    return this._markers7;
  }
  set markers7(value) {
    this._markers7 = value;
  }
};
var GraphFormProperties = class extends GenericProperties {
  constructor(initData, owner) {
    super(owner);
    this._Graph_series0 = new ChartSerie(YColor.Tomato);
    this._Graph_series1 = new ChartSerie(YColor.DodgerBlue);
    this._Graph_series2 = new ChartSerie(YColor.SeaGreen);
    this._Graph_series3 = new ChartSerie(YColor.Gold);
    this._Graph_showRecordedData = false;
    this._Graph_borderColor = YColor.LightGray;
    this._Graph_borderThickness = 1;
    this._Graph_bgColor1 = YColor.FromArgb(255, 220, 220, 220);
    this._Graph_bgColor2 = YColor.FromArgb(55, 240, 240, 240);
    this._Graph_resizeRule = Proportional.ResizeRule.FIXED;
    this._Graph_xAxis = new XaxisDescription();
    this._Graph_yAxes0 = new YaxisDescription(0, true);
    this._Graph_yAxes1 = new YaxisDescription(1, false);
    this._Graph_yAxes2 = new YaxisDescription(2, false);
    this._Graph_legendPanel = new LegendPanelDescription();
    this._Graph_dataTracker = new DataTrackerDescription();
    this._Graph_navigator = new NavigatorDescription();
    this._annotationPanels0 = new AnnotationPanelDescriptionGraph();
    this._annotationPanels1 = new AnnotationPanelDescriptionGraph();
    this.initFromXmlData(initData);
  }
  IsDataSourceAssigned() {
    let propList = Object.getOwnPropertyNames(this);
    propList.forEach((name) => {
      if (name.startsWith("Graphs_series")) {
        let s = this[name].GetValue(this, null);
        if (!(s.DataSource_source instanceof NullYSensor))
          return true;
      }
    });
    return false;
  }
  get ATTR_Graph_series0__DisplayName() {
    return "Series 1";
  }
  get ATTR_Graph_series0__CategoryAttribute() {
    return "Data Sources";
  }
  get ATTR_Graph_series0__PreExpandedCategoryAttribute() {
    return true;
  }
  get ATTR_Graph_series0__PreExpandedAttribute() {
    return true;
  }
  get ATTR_Graph_series0__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_Graph_series0__DescriptionAttribute() {
    return "First data series, expand for more.";
  }
  get Graph_series0() {
    return this._Graph_series0;
  }
  set Graph_series0(value) {
    this._Graph_series0 = value;
  }
  get ATTR_Graph_series1__DisplayName() {
    return "Series 2";
  }
  get ATTR_Graph_series1__CategoryAttribute() {
    return "Data Sources";
  }
  get ATTR_Graph_series1__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_Graph_series1__DescriptionAttribute() {
    return "Second data series, expand for more.";
  }
  get Graph_series1() {
    return this._Graph_series1;
  }
  set Graph_series1(value) {
    this._Graph_series1 = value;
  }
  get ATTR_Graph_series2__DisplayName() {
    return "Series 3";
  }
  get ATTR_Graph_series2__CategoryAttribute() {
    return "Data Sources";
  }
  get ATTR_Graph_series2__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_Graph_series2__DescriptionAttribute() {
    return "Third data series, expand for more.";
  }
  get Graph_series2() {
    return this._Graph_series2;
  }
  set Graph_series2(value) {
    this._Graph_series2 = value;
  }
  get ATTR_Graph_series3__DisplayName() {
    return "Series 4";
  }
  get ATTR_Graph_series3__CategoryAttribute() {
    return "Data Sources";
  }
  get ATTR_Graph_series3__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_Graph_series3__DescriptionAttribute() {
    return "Fourth data series, expand for more.";
  }
  get Graph_series3() {
    return this._Graph_series3;
  }
  set Graph_series3(value) {
    this._Graph_series3 = value;
  }
  get ATTR_Graph_showRecordedData__DisplayName() {
    return "Use datalogger data";
  }
  get ATTR_Graph_showRecordedData__CategoryAttribute() {
    return "Graph";
  }
  get ATTR_Graph_showRecordedData__CopyToTarget() {
    return false;
  }
  // this extra property doesn't exist is graph
  get ATTR_Graph_showRecordedData__DescriptionAttribute() {
    return "Makes the graph load the sensors datalogger contents. After disabling this feature, you may want to reset the dataview with the right-click context menu.";
  }
  get Graph_showRecordedData() {
    return this._Graph_showRecordedData;
  }
  set Graph_showRecordedData(value) {
    if (this._Graph_showRecordedData != value) {
      this._Graph_showRecordedData = value;
      if (this.ownerForm != null) {
        this._Graph_showRecordedData ? this.ownerForm.loadRecordedDataIfNeeded() : this.ownerForm.removeDataloggerData();
      }
    }
  }
  get ATTR_Graph_borderColor__DisplayName() {
    return "Border color";
  }
  get ATTR_Graph_borderColor__CategoryAttribute() {
    return "Graph";
  }
  get ATTR_Graph_borderColor__DescriptionAttribute() {
    return "Canvas borders color.";
  }
  get Graph_borderColor() {
    return this._Graph_borderColor;
  }
  set Graph_borderColor(value) {
    this._Graph_borderColor = value;
  }
  get ATTR_Graph_borderThickness__DisplayName() {
    return "Border thickness";
  }
  get ATTR_Graph_borderThickness__CategoryAttribute() {
    return "Graph";
  }
  get ATTR_Graph_borderThickness__DescriptionAttribute() {
    return "Canvas borders thickness.";
  }
  get Graph_borderThickness() {
    return this._Graph_borderThickness;
  }
  set Graph_borderThickness(value) {
    this._Graph_borderThickness = value;
  }
  get ATTR_Graph_bgColor1__DisplayName() {
    return "Background color 1";
  }
  get ATTR_Graph_bgColor1__CategoryAttribute() {
    return "Graph";
  }
  get ATTR_Graph_bgColor1__DescriptionAttribute() {
    return "Background gradient color 1.";
  }
  get Graph_bgColor1() {
    return this._Graph_bgColor1;
  }
  set Graph_bgColor1(value) {
    this._Graph_bgColor1 = value;
  }
  get ATTR_Graph_bgColor2__DisplayName() {
    return "Background color 2";
  }
  get ATTR_Graph_bgColor2__CategoryAttribute() {
    return "Graph";
  }
  get ATTR_Graph_bgColor2__DescriptionAttribute() {
    return "Background gradient color 2.";
  }
  get Graph_bgColor2() {
    return this._Graph_bgColor2;
  }
  set Graph_bgColor2(value) {
    this._Graph_bgColor2 = value;
  }
  get ATTR_Graph_resizeRule__DisplayName() {
    return "Font sizes";
  }
  get ATTR_Graph_resizeRule__CategoryAttribute() {
    return "Graph";
  }
  get ATTR_Graph_resizeRule__DescriptionAttribute() {
    return "Are font sizes fixed or do they change when window is resized?";
  }
  get Graph_resizeRule() {
    return this._Graph_resizeRule;
  }
  set Graph_resizeRule(value) {
    this._Graph_resizeRule = value;
  }
  get ATTR_Graph_xAxis__DisplayName() {
    return "X Axis";
  }
  get ATTR_Graph_xAxis__CategoryAttribute() {
    return "X/Y Axes";
  }
  get ATTR_Graph_xAxis__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_Graph_xAxis__DescriptionAttribute() {
    return "X-Axis, expand for more";
  }
  get Graph_xAxis() {
    return this._Graph_xAxis;
  }
  set Graph_xAxis(value) {
    this._Graph_xAxis = value;
  }
  get ATTR_Graph_yAxes0__DisplayName() {
    return "YAxis 1";
  }
  get ATTR_Graph_yAxes0__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_Graph_yAxes0__CategoryAttribute() {
    return "X/Y Axes";
  }
  get ATTR_Graph_yAxes0__DescriptionAttribute() {
    return "First Y Axis, expand for more.";
  }
  get Graph_yAxes0() {
    return this._Graph_yAxes0;
  }
  set Graph_yAxes0(value) {
    this._Graph_yAxes0 = value;
  }
  get ATTR_Graph_yAxes1__DisplayName() {
    return "YAxis 2";
  }
  get ATTR_Graph_yAxes1__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_Graph_yAxes1__CategoryAttribute() {
    return "X/Y Axes";
  }
  get ATTR_Graph_yAxes1__DescriptionAttribute() {
    return "Second Y Axis, expand for more.";
  }
  get Graph_yAxes1() {
    return this._Graph_yAxes1;
  }
  set Graph_yAxes1(value) {
    this._Graph_yAxes1 = value;
  }
  get ATTR_Graph_yAxes2__DisplayName() {
    return "YAxis 3";
  }
  get ATTR_Graph_yAxes2__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_Graph_yAxes2__CategoryAttribute() {
    return "X/Y Axes";
  }
  get ATTR_Graph_yAxes2__DescriptionAttribute() {
    return "Third Y Axis, expand for more.";
  }
  get Graph_yAxes2() {
    return this._Graph_yAxes2;
  }
  set Graph_yAxes2(value) {
    this._Graph_yAxes2 = value;
  }
  get ATTR_Graph_legendPanel__DisplayName() {
    return "Legend Panel";
  }
  get ATTR_Graph_legendPanel__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_Graph_legendPanel__CategoryAttribute() {
    return "Graph";
  }
  get ATTR_Graph_legendPanel__DescriptionAttribute() {
    return "Panel containing a description of all data series. Expand for more.";
  }
  get Graph_legendPanel() {
    return this._Graph_legendPanel;
  }
  set Graph_legendPanel(value) {
    this._Graph_legendPanel = value;
  }
  get ATTR_Graph_dataTracker__DisplayName() {
    return "Data tracker";
  }
  get ATTR_Graph_dataTracker__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_Graph_dataTracker__CategoryAttribute() {
    return "Graph";
  }
  get ATTR_Graph_dataTracker__DescriptionAttribute() {
    return "Show dynamic value labels while the mouse is moving over the graph. Expand for more.";
  }
  get Graph_dataTracker() {
    return this._Graph_dataTracker;
  }
  set Graph_dataTracker(value) {
    this._Graph_dataTracker = value;
  }
  get ATTR_Graph_navigator__DisplayName() {
    return "Navigator";
  }
  get ATTR_Graph_navigator__CategoryAttribute() {
    return "Graph";
  }
  get ATTR_Graph_navigator__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_Graph_navigator__DescriptionAttribute() {
    return "Small additional graph showing the whole data set and allowing to quickly navigate among data. Expand for more.";
  }
  get Graph_navigator() {
    return this._Graph_navigator;
  }
  set Graph_navigator(value) {
    this._Graph_navigator = value;
  }
  get ATTR_Graph_annotationPanels0__DisplayName() {
    return "Annotation 1";
  }
  get ATTR_Graph_annotationPanels0__CategoryAttribute() {
    return "Annotations";
  }
  get ATTR_Graph_annotationPanels0__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_Graph_annotationPanels0__DescriptionAttribute() {
    return "Customizable text panel 1";
  }
  get Graph_annotationPanels0() {
    return this._annotationPanels0;
  }
  set Graph_annotationPanels0(value) {
    this._annotationPanels0 = value;
  }
  get ATTR_Graph_annotationPanels1__DisplayName() {
    return "Annotation 2";
  }
  get ATTR_Graph_annotationPanels1__CategoryAttribute() {
    return "Annotations";
  }
  get ATTR_Graph_annotationPanels1__ReadOnlyAttribute() {
    return true;
  }
  get ATTR_Graph_annotationPanels1__DescriptionAttribute() {
    return "Customizable text panel 2";
  }
  get Graph_annotationPanels1() {
    return this._annotationPanels1;
  }
  set Graph_annotationPanels1(value) {
    this._annotationPanels1 = value;
  }
};

// obj/full/contextMenu.js
var ContextMenuSeparator = class {
  constructor() {
    this.TR = document.createElement("TR");
    this.TR["objectData"] = this;
    this.TR.className = ContextMenu.SEPARATOR_MARKER;
    this.TD_caption = document.createElement("TD");
    this.TD_caption.style.backgroundColor = ContextMenu.iconBGColor;
    this.TD_caption.style.paddingLeft = "3px";
    this.TD_caption.style.paddingRight = "3px";
    let hr = document.createElement("HR");
    this.TD_caption.colSpan = 2;
    this.TD_caption.appendChild(hr);
    this.TD_icon = document.createElement("TD");
    this.TD_icon.style.backgroundColor = ContextMenu.captionBgColor;
    this.TR.appendChild(this.TD_icon);
    this.TR.appendChild(this.TD_caption);
  }
  toString() {
    return "Separator";
  }
  get focusable() {
    return false;
  }
  parentMenuisClosing() {
  }
  get domData() {
    return this.TR;
  }
};
var ContextMenuItemBase = class {
  get userdata() {
    return this._userdata;
  }
  set userdata(value) {
    this._userdata = value;
  }
  constructor(menu, icon, caption) {
    this._clickHidesMenu = true;
    this._userdata = null;
    this._menu = menu;
    this.TR = document.createElement("TR");
    this.TR["objectData"] = this;
    this.TD_caption = document.createElement("TD");
    this.TD_caption.style.backgroundColor = ContextMenu.iconBGColor;
    this.TD_caption.style.paddingLeft = "3px";
    this.TD_caption.style.paddingRight = "3px";
    this.TD_caption.style.whiteSpace = "nowrap";
    this.TD_icon = document.createElement("TD");
    if (icon != null)
      this.TD_icon.innerHTML = icon;
    this.TD_icon.style.backgroundColor = ContextMenu.captionBgColor;
    this.A_caption = document.createElement("A");
    this.A_caption.innerHTML = caption;
    this.TR.style.cursor = "pointer";
    this.TR.tabIndex = 0;
    this.TR.style.outline = "none";
    this._TR_keydown = (e) => {
      e.preventDefault();
      this.keydown(e);
    };
    this._TR_mouseover = (e) => {
      this.mouserOver(e);
    };
    this._TR_mouseout = (e) => {
      this.mouserOut(e);
    };
    this._A_mouseover = (e) => {
      this.mouserOver(e);
    };
    this._A_mouseout = (e) => {
      this.mouserOut(e);
    };
    this._A_click = (e) => {
      this.activate();
    };
    this._TR_focusin = () => {
      this.focusIn();
    };
    this._TR_focusout = () => {
      this.focusOut();
    };
    this.TR.addEventListener("mouseover", this._TR_mouseover);
    this.TR.addEventListener("mouseout", this._TR_mouseout);
    this.TR.addEventListener("click", this._A_click);
    this.TR.addEventListener("keydown", this._TR_keydown);
    this.A_caption.addEventListener("mouseover", this._A_mouseover);
    this.A_caption.addEventListener("mouseout", this._A_mouseout);
    this.TD_caption.appendChild(this.A_caption);
    this.TR.appendChild(this.TD_icon);
    this.TR.appendChild(this.TD_caption);
    this.TR.addEventListener("focusin", this._TR_focusin);
    this.TR.addEventListener("focusout", this._TR_focusout);
  }
  toString() {
    return this.TD_caption.innerText;
  }
  get focusable() {
    return this.visible;
  }
  activate() {
  }
  focus() {
    this.TR.focus();
    this._menu.closeSubmenus();
  }
  focusIn() {
    if (this.TD_caption.style.backgroundColor == ContextMenu.iconBGColor)
      this._menu.closeSubmenus();
    this.TD_caption.style.backgroundColor = ContextMenu.ActiveColor;
    this.TD_icon.style.backgroundColor = ContextMenu.ActiveColor;
  }
  focusOut() {
    this.TD_caption.style.backgroundColor = ContextMenu.iconBGColor;
    this.TD_icon.style.backgroundColor = ContextMenu.captionBgColor;
  }
  removeAndDestroy() {
    if (this.TR.parentNode != null)
      this.TR.parentNode.removeChild(this.TR);
    this.TR.removeEventListener("keydown", this._TR_keydown);
    this.TR.removeEventListener("mouseover", this._TR_mouseover);
    this.TR.removeEventListener("mouseout", this._TR_mouseout);
    this.A_caption.removeEventListener("mouseover", this._A_mouseover);
    this.A_caption.removeEventListener("mouseout", this._A_mouseout);
    this.A_caption.removeEventListener("click", this._A_click);
    this.TR.removeEventListener("focusin", this._TR_focusin);
    this.TR.removeEventListener("focusout", this._TR_focusout);
  }
  set caption(value) {
    this.A_caption.innerHTML = value;
  }
  get caption() {
    return this.A_caption.innerHTML;
  }
  keydown(e) {
    if (e.code == "ArrowDown")
      this._menu.activateNext(this);
    if (e.code == "ArrowUp")
      this._menu.activatePrevious(this);
    if (e.code == "Enter")
      this.activate();
    if (e.code == "Escape") {
      this._menu.close();
      if (this._menu.parentMenuItem != null)
        this._menu.parentMenuItem.focus();
    }
    if (e.code == "ArrowLeft") {
      if (this._menu.parentMenuItem != null) {
        this._menu.close();
        this._menu.parentMenuItem.focus();
      }
    }
  }
  mouserOver(e) {
    this.TD_caption.style.backgroundColor = ContextMenu.ActiveColor;
    this.TD_icon.style.backgroundColor = ContextMenu.ActiveColor;
    this._menu.closeSubmenus();
  }
  mouserOut(e) {
    this.TD_caption.style.backgroundColor = ContextMenu.iconBGColor;
    this.TD_icon.style.backgroundColor = ContextMenu.captionBgColor;
  }
  get domData() {
    return this.TR;
  }
  get visible() {
    return this.TR.style.display != "none";
  }
  set visible(value) {
    this.TR.style.display = value ? "" : "none";
  }
  parentMenuisClosing() {
  }
  closeMenu(all) {
    if (all)
      this._menu.closeAll();
    else
      this._menu.close();
  }
};
var ContextMenuItem = class extends ContextMenuItemBase {
  activate() {
    if (this._clickHidesMenu)
      this.closeMenu(true);
    if (this._callback != null)
      this._callback();
  }
  constructor(menu, icon, caption, callback) {
    super(menu, icon, caption);
    this._callback = callback;
    this.TD_caption.colSpan = 2;
  }
};
var ContextMenuItemSubMenuEntry = class extends ContextMenuItem {
  constructor(menu, icon, caption, submenu) {
    super(menu, icon, caption, null);
    this.TD_caption.colSpan = 1;
    this._submenu = submenu;
    this._submenu.parentMenuItem = this;
    this._parentmenu = menu;
    this._clickHidesMenu = false;
    this.TD_arrow = document.createElement("TD");
    this.TD_arrow.style.backgroundColor = ContextMenu.iconBGColor;
    this.TD_arrow.style.textAlign = "right";
    this.TD_arrow.style.paddingRight = "3px";
    this.TD_arrow.innerHTML = "&#9654;";
    this.TR.appendChild(this.TD_arrow);
    this.A_caption.addEventListener("click", (e) => {
      this.showSubmenu();
    });
    this.A_caption.addEventListener("touchstart", (e) => {
      if (e.touches.length == 1)
        this.activate();
    });
    this._submenu.zIndex = this._parentmenu.zIndex + 1;
  }
  keydown(e) {
    super.keydown(e);
    if (e.code == "ArrowRight") {
      this.activate();
      this._submenu.focusFirst();
    }
  }
  removeAndDestroy() {
    this._submenu.close();
    this._submenu.destroy();
    super.removeAndDestroy();
  }
  activate() {
    this.showSubmenu();
  }
  get submenu() {
    return this._submenu;
  }
  showSubmenu() {
    this._submenu.zIndex = this._parentmenu.zIndex + 1;
    let x = 0;
    let y = 0;
    let it = this.TR;
    do {
      x += it.offsetLeft || 0;
      y += it.offsetTop || 0;
      it = it.offsetParent;
    } while (it);
    this._submenu.showContextMenu(x + this._parentmenu.width - 15, y + 5);
  }
  focusIn() {
    super.focusIn();
    this.TD_arrow.style.backgroundColor = ContextMenu.ActiveColor;
  }
  focusOut() {
    super.focusOut();
    this.TD_arrow.style.backgroundColor = ContextMenu.iconBGColor;
  }
  parentMenuisClosing() {
    this._submenu.close();
  }
  mouserOver(e) {
    this.focusIn();
    this._parentmenu.setDelayedAction(() => {
      if (this.visible)
        this.showSubmenu();
    });
  }
  mouserOut(e) {
    this._parentmenu.cancelDelayedAction();
    this.focusOut();
  }
};
var ContextMenuBase = class {
  get parentMenuItem() {
    return this._parentMenuItem;
  }
  set parentMenuItem(value) {
    this._parentMenuItem = value;
  }
  get userdata() {
    return this._userdata;
  }
  set userdata(value) {
    this._userdata = value;
  }
  constructor(baseSize, openCallback, fontFamily) {
    this._visible = false;
    this._zIndex = 100002;
    this._parentmenu = null;
    this._parentMenuItem = null;
    this._userdata = null;
    this._openCallback = openCallback;
    this.Menutable = document.createElement("TABLE");
    this.Menutable.style.position = "absolute";
    this.Menutable.style.border = "1px solid #808080";
    if (typeof fontFamily !== "undefined")
      this.Menutable.style.fontFamily = fontFamily;
    this.Menutable.style.fontSize = (2 * baseSize / 3).toFixed(1) + "px";
    this.Menutable.style.borderSpacing = "0";
    this.Menutable.style.boxShadow = " 4px 4px 4px 1px rgba(0,0,0,0.05)";
    this.Menutable.style.zIndex = this._zIndex.toString();
    this._timer = null;
    this.visible = false;
  }
  closeAll() {
    this.close();
    if (this._parentmenu != null)
      this._parentmenu.closeAll();
  }
  cancelDelayedAction() {
    if (this._timer != null) {
      clearTimeout(this._timer);
      this._timer = null;
    }
  }
  setDelayedAction(action) {
    this.cancelDelayedAction();
    this._timer = setTimeout(() => {
      action();
    }, 500);
  }
  get parentMenu() {
    return this._parentmenu;
  }
  destroy() {
    for (let i = 0; i < this.Menutable.childNodes.length; i++) {
      let item = this.Menutable.childNodes[i]["objectData"];
      item.removeAndDestroy();
    }
    if (this.Menutable.parentNode != null)
      this.Menutable.parentNode.removeChild(this.Menutable);
  }
  focusFirst() {
    for (let i = 0; i < this.Menutable.childNodes.length; i++) {
      let item = this.Menutable.childNodes[i]["objectData"];
      if (item.focusable) {
        item.focus();
        return;
      }
    }
  }
  toString() {
    let res = "";
    for (let i = 0; i < this.Menutable.childNodes.length; i++) {
      let item = this.Menutable.childNodes[i]["objectData"];
      res = res + item.caption + "";
    }
    return res;
  }
  isXYOnMenu(x, y) {
    if (!this.visible)
      return false;
    if (x >= this.Menutable.offsetLeft && x <= this.Menutable.offsetLeft + this.Menutable.offsetWidth && y >= this.Menutable.offsetTop && y <= this.Menutable.offsetTop + this.Menutable.offsetHeight) {
      return true;
    }
    for (let i = 0; i < this.Menutable.childNodes.length; i++) {
      let item = this.Menutable.childNodes[i]["objectData"];
      if (item instanceof ContextMenuItemSubMenuEntry) {
        if (item.submenu.isXYOnMenu(x, y))
          return true;
      }
    }
    return false;
  }
  activateNext(source) {
    let found = false;
    for (let i = 0; i < this.Menutable.childNodes.length; i++) {
      let item = this.Menutable.childNodes[i]["objectData"];
      if (item == source) {
        found = true;
      } else if (found && item.focusable) {
        item.focus();
        return;
      }
    }
  }
  activatePrevious(source) {
    let previous = null;
    for (let i = 0; i < this.Menutable.childNodes.length; i++) {
      let item = this.Menutable.childNodes[i]["objectData"];
      if (item == source) {
        if (previous != null)
          previous.focus();
        return;
      }
      if (item.focusable)
        previous = item;
    }
  }
  get width() {
    return this.Menutable.offsetWidth;
  }
  closeSubmenus() {
    this.closeSubMenusExcept(null);
  }
  get zIndex() {
    return this._zIndex;
  }
  set zIndex(value) {
    this._zIndex = value;
    this.Menutable.style.zIndex = this._zIndex.toString();
  }
  get visible() {
    return this.Menutable.style.display != "none";
  }
  set visible(value) {
    this.Menutable.style.display = value ? "" : "none";
  }
  close() {
    this.cancelDelayedAction();
    for (let i = 0; i < this.Menutable.childNodes.length; i++) {
      let item = this.Menutable.childNodes[i]["objectData"];
      item.parentMenuisClosing();
    }
    this.visible = false;
    this.closeSubmenus();
  }
  refresh() {
    let showSeparation = false;
    for (let i = 0; i < this.Menutable.childNodes.length; i++) {
      let element = this.Menutable.childNodes[i];
      if (element.className == ContextMenu.SEPARATOR_MARKER) {
        element.style.display = showSeparation ? "" : "none";
        showSeparation = false;
      } else if (element.style.display == "")
        showSeparation = true;
    }
  }
  addMenuItem(icon, caption, callback) {
    let menuItem = new ContextMenuItem(this, icon, caption, callback);
    this.Menutable.appendChild(menuItem.domData);
    return menuItem;
  }
  get menuItemsCount() {
    return this.Menutable.childNodes.length;
  }
  closeSubMenusExcept(doNotClose) {
    for (let i = 0; i < this.Menutable.childNodes.length; i++) {
      let item = this.Menutable.childNodes[i]["objectData"];
      if (item instanceof ContextMenuItemSubMenuEntry) {
        if (item.submenu != doNotClose) {
          item.submenu.close();
        }
      }
    }
  }
  subMenuisOpening(source) {
    this.closeSubMenusExcept(source);
  }
  addSubMenuItem(icon, caption, submenu) {
    submenu.zIndex = this.zIndex + 1;
    let menuItem = new ContextMenuItemSubMenuEntry(this, icon, caption, submenu);
    this.Menutable.appendChild(menuItem.domData);
    return menuItem;
  }
  clearAllContents() {
    while (this.Menutable.childNodes.length > 0) {
      let item = this.Menutable.childNodes[0]["objectData"];
      item.removeAndDestroy();
    }
  }
  AddSeparator() {
    let separator = new ContextMenuSeparator();
    this.Menutable.appendChild(separator.domData);
    return separator;
  }
  showContextMenu(x, y) {
  }
};
ContextMenuBase.SEPARATOR_MARKER = "separator";
ContextMenuBase.iconBGColor = "#fdfdfd";
ContextMenuBase.captionBgColor = "#f1f1f1";
ContextMenuBase.ActiveColor = "#c4e1ff";
var ContextSubMenu = class extends ContextMenuBase {
  constructor(baseSize, parentMenu, openCallback, fontFamily) {
    super(baseSize, openCallback, fontFamily);
    this._parentmenu = parentMenu;
    document.body.appendChild(this.Menutable);
  }
  showContextMenu(x, y) {
    if (this._parentmenu != null)
      this._parentmenu.subMenuisOpening(this);
    if (this._openCallback != null)
      this._openCallback(x, y);
    this.Menutable.style.left = x.toString() + "px";
    this.Menutable.style.top = y.toString() + "px";
    this.visible = true;
    let right = this.Menutable.offsetLeft - this.Menutable.scrollLeft + this.Menutable.offsetWidth;
    let bottom = this.Menutable.offsetTop - this.Menutable.scrollTop + this.Menutable.offsetHeight;
    if (right > window.innerWidth)
      this.Menutable.style.left = (x - (right - window.innerWidth)).toString() + "px";
    if (bottom > window.innerHeight)
      this.Menutable.style.top = (y - (bottom - window.innerHeight)).toString() + "px";
  }
};
var ContextMenu = class extends ContextMenuBase {
  constructor(baseSize, openCallback, fontFamily) {
    super(baseSize, openCallback, fontFamily);
    document.addEventListener("contextmenu", (e) => {
      let tagname = "";
      if (e.target != null && constants.RunningOnAndroid)
        tagname = e.target.tagName;
      if (tagname != "INPUT") {
        e.preventDefault();
        this.showContextMenu(e.pageX, e.pageY);
      }
    });
    document.addEventListener("dblclick", (e) => {
      if (constants.dbleClickBringsUpContextMenu)
        this.showContextMenu(e.pageX, e.pageY);
    });
    document.addEventListener("click", (e) => {
      this.click(e);
    });
    document.body.appendChild(this.Menutable);
  }
  click(e) {
    if (!this.visible)
      return;
    if (!this.isXYOnMenu(e.pageX, e.pageY)) {
      this.close();
    }
  }
  showContextMenu(x, y) {
    let z = YWindow.ConTextMenuBestZindex;
    if (z < 0)
      return;
    this.zIndex = z;
    this.Menutable.style.left = x.toString() + "px";
    this.Menutable.style.top = y.toString() + "px";
    this.visible = true;
    let right = this.Menutable.offsetLeft - this.Menutable.scrollLeft + this.Menutable.offsetWidth;
    let bottom = this.Menutable.offsetTop - this.Menutable.scrollTop + this.Menutable.offsetHeight;
    if (right > window.innerWidth)
      this.Menutable.style.left = Math.max(0, x - (right - window.innerWidth)).toString() + "px";
    if (bottom > window.innerHeight)
      this.Menutable.style.top = Math.max(0, y - (bottom - window.innerHeight)).toString() + "px";
    if (this._openCallback != null)
      this._openCallback(x, y);
    if (x <= 5 && y <= 5)
      this.focusFirst();
  }
};

// obj/full/ressources.js
var ressources = class _ressources {
  static SVGheader(size, shadowDef, title) {
    let res = '<svg  xmlns="http://www.w3.org/2000/svg" width="' + size + '" height="' + size + '" viewBox="0 0 128  128">';
    if (typeof title != "undefined")
      res = res + "<title>" + title + "</title>";
    if (shadowDef)
      res = res + '<defs><filter id="f1" x="0" y="0"><feGaussianBlur in="SourceGraphic" stdDeviation="2" /></filter></defs>';
    res = res + '<g transform="translate(0,128)"><g transform="scale(1,-1)">';
    return res;
  }
  static SVGFooter() {
    return "</g></g></svg>";
  }
  static addIconTransparency() {
    return '<g opacity="0.5">';
  }
  static addIconSrcCode() {
    return '<path d="m 8 24 L 8 40 L 24 40 L 24 56 L 40 56 L 40 40 L 56 40 L 56 24 L 40 24 L 40 8 L 24 8 L 24 24   Z" fill="#40ff40" stroke="black" stroke-width="8" stroke-linecap="round"/>';
  }
  static deleteIconSrcCode() {
    return '<path d="m 15 4 L 4 15 L 19 29 L 4 43 L 15 55 L 29 40 L 43 55 L 55 43 L 40 29 L 55 15 L 44 4 L 29 18 Z" fill="#ff4040" stroke="black" stroke-width="8" stroke-linecap="round">';
  }
  static IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, shadowcode, activeCode, title) {
    let res = _ressources.SVGheader(size, shadow, title);
    if (shadow)
      res = res + '<g transform="translate(6,-6)">' + shadowcode + "</g>";
    if (semitransparent)
      res = res + _ressources.addIconTransparency();
    res = res + activeCode;
    if (semitransparent)
      res = res + "</g>";
    if (addIcon)
      res = res + _ressources.addIconSrcCode();
    if (deleteIcon)
      res = res + _ressources.deleteIconSrcCode();
    res = res + _ressources.SVGFooter();
    return res;
  }
  static GraphIcon(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<path d="m 12 24 L 103 24 L 103 107 L 12 107 z" fill="lightgray" filter="url(#f1)" /><path d="m 112 24 L 12 24 L 12 113"  style="fill:none; stroke:lightgray; stroke-width:4px; stroke-linejoin:round;stroke-linecap:round;" filter="url(#f1)"/>', '<path d="m 12 24 L 103 24 L 103 107 L 12 107 z" fill="#ffffe9" /><line x1="43" y1="24" x2="43" y2="107" stroke="#e3e3e3" stroke-width="2" stroke-linecap="round" /><line x1="43" y1="24" x2="43" y2="107" stroke="#e3e3e3" stroke-width="2" stroke-linecap="round" /><line x1="73" y1="24" x2="73" y2="107" stroke="#e3e3e3" stroke-width="2" stroke-linecap="round" /><line x1="12" y1="51" x2="103" y2="51" stroke="#e3e3e3" stroke-width="2" stroke-linecap="round" /><line x1="12" y1="79" x2="103" y2="79" stroke="#e3e3e3" stroke-width="2" stroke-linecap="round" /><line x1="12" y1="107" x2="103" y2="107" stroke="#e3e3e3" stroke-width="2" stroke-linecap="round" /><path d="m 12 77 L 34 65 L 55 85 L 70 77 L 100 93" fill="none" stroke="#FF0000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><line x1="103" y1="24" x2="103" y2="107" stroke="#e3e3e3" stroke-width="2" stroke-linecap="round" /><path d="m 112 24 L 12 24 L 12 113"  style="fill:none; stroke:black; stroke-width:4px; stroke-linejoin:round;stroke-linecap:round;"/>');
  }
  static SolidGaugeIcon(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<path d="M 34 40 L 14 40 14 52 A 50 50 0 0 0 114 52 L  114 40 L 94 40 L 94 52 A 30 30 0 1 1  34 52 Z" style="fill:lightgray;fill-opacity=0.4;" filter="url(#f1)"/><text x="43" y="-30" fill="black" transform="scale(1,-1)" style="font: bold 24px sans-serif;fill:lightgray;fill-opacity=0.4;" filter="url(#f1)">123</text>', '<path d="M 34 40 L 14 40 14 52 A 50 50 0 0 0 114 52 L  114 40 L 94 40 L 94 52 A 30 30 0 1 1  34 52 Z" style="fill:#ffffe9; stroke:none; "/><path d="M 34 40 L 14 40 14 52 A 50 50 0 0 0 29 88 L  43 72.5   A 30 30 0 0 1  34 52 Z" style="fill:red; stroke:none;"/><path d="M 34 40 L 14 40 14 52 A 50 50 0 0 0 114 52 L  114 40 L 94 40 L 94 52 A 30 30 0 1 1  34 52 Z" style="fill:none; stroke:black; stroke-width:4px; stroke-linejoin:round;stroke-linecap:round;"/><text x="43" y="-30" fill="black" transform="scale(1,-1)" style="font: bold 24px sans-serif;">123</text>');
  }
  static RawDataIcon(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<path d="m 16 27 L 107 27 L 107 107 L 16 107 z" fill="lightgray" filter="url(#f1)" />', '<path d="m 16 27 L 107 27 L 107 107 L 16 107 z" style="fill:#ffffe9;" /><line x1="47" y1="27" x2="47" y2="107" stroke="#e3e3e3" stroke-width="2"  /><line x1="76" y1="27" x2="76" y2="107" stroke="#e3e3e3" stroke-width="2"  /><line x1="16" y1="44" x2="107" y2="44" stroke="#e3e3e3" stroke-width="2"  /><line x1="16" y1="60" x2="107" y2="60" stroke="#e3e3e3" stroke-width="2"  /><line x1="16" y1="76" x2="107" y2="76" stroke="#e3e3e3" stroke-width="2"  /><path d="m 16 27 L 107 27 L 107 107 L 16 107 z" style="fill:none;stroke:black; stroke-width:4px; stroke-linejoin:round;" /><path d="m 19 91 L 104 91 L 104 104 L 19 104 z" style="fill:#b2b2b2; stroke:#6f6f6f; stroke-width:4px; stroke-linejoin:round;" /><line x1="47" y1="91" x2="47" y2="104" stroke="#6f6f6f" stroke-width="2"  /><line x1="76" y1="91" x2="76" y2="104" stroke="#6f6f6f" stroke-width="2"  />');
  }
  static ConfigureIcon(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<path d="M85 85 L 102 80 L 113 91 A  41 100 45 0 0  86 71 L 37 21 A  5 11 45 1 0  21 37 L 71 86  A  41 100 45 0 0  91 113 L 80 102 Z" style="fill:lightgray;fill-opacity=0.4;" filter="url(#f1)"/>', '<path d="M85 85 L 102 80 L 113 91 A  41 100 45 0 0  86 71 L 37 21 A  5 11 45 1 0  21 37 L 71 86  A  41 100 45 0 0  91 113 L 80 102 Z" style="fill:grey; stroke:black; stroke-width:4px; stroke-linejoin:round;stroke-linecap:round;"/>');
  }
  static DigitalDisplayIcon(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<path d="m 12 45 L 111 45 L 111 89 L 12 89 z" fill="lightgray" filter="url(#f1)" />', '<path d="m 12 45 L 111 45 L 111 89 L 12 89 z" style="fill:#ffffe9; stroke:black; stroke-width:4px; stroke-linejoin:round;" /><text x="29" y="-52" fill="black" transform="scale(1,-1)" style="font: bold 40px sans-serif;">123</text> ');
  }
  static AngularGaugeIcon(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<circle  cx="64" cy="64" r="40" fill="lightgray" filter="url(#f1)" />', '<circle  cx="64" cy="64" r="40" stroke="black" stroke-width="4" fill="#ffffe9" /><line x1="64" y1="104" x2="64" y2="94" stroke="black" stroke-width="2"   stroke-linecap="round" /><line x1="29" y1="84" x2="38" y2="79" stroke="black" stroke-width="2"   stroke-linecap="round" /><line x1="29" y1="44" x2="38" y2="48" stroke="black" stroke-width="2"   stroke-linecap="round" /><line x1="98" y1="84" x2="91" y2="79" stroke="black" stroke-width="2"   stroke-linecap="round" /><line x1="98" y1="44" x2="91" y2="48" stroke="black" stroke-width="2"   stroke-linecap="round" /><g transform="translate(64,64)"><g transform="rotate(-70,0,0)"><path d="m 5 0 L 0 5 L -25 0 L 0 -5 z" style="fill:black;stroke:black; stroke-width:2px; stroke-linejoin:round;"/></g></g>');
  }
  static EditIcon(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<path d="m 16 48 L 108 48 L 108 88 L 16 88 z" fill="lightgray" filter="url(#f1)" /><path d="m 87 24 L 75 40 L 67 31 L 62 64 L 93 50 L 82 44  L 94 29 z" fill="lightgray" filter="url(#f1)" />', '<path d="m 16 48 L 108 48 L 108 88 L 16 88 z" style="fill:#ffffe9; stroke:black; stroke-width:4px; stroke-linejoin:round;" /><path d="m 87 24 L 75 40 L 67 31 L 62 64 L 93 50 L 82 44  L 94 29 z" style="fill:white; stroke:black; stroke-width:2px; stroke-linejoin:round;" /> ');
  }
  static AllIcon(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<path d="m 60 108 L 46 74 L 8 72 L 38 48 L 28 12 L 60 32 L 92 12  L 82 48 L 112 70  L 74 74  z" fill="lightgray" filter="url(#f1)" />', '<path d="m 60 108 L 46 74 L 8 72 L 38 48 L 28 12 L 60 32 L 92 12  L 82 48 L 112 70  L 74 74  z" style="fill:#fee403; stroke:#918201; stroke-width:4px; stroke-linejoin:round;" />');
  }
  static TrashIcon(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<path d="m 33 12 L 24 94 L 104 94 L 95 12 z" fill="lightgray" filter="url(#f1)" /><g transform="translate(70,105)"><g transform="rotate(-10,0,0)"><path d="m -40 0 L -30 5 L 30 5 L 40 0 z" fill="lightgray" filter="url(#f1)" /><path d="m -9 5 L -9 12 L 9 12 L 9 5" stroke="lightgray" filter="url(#f1)"/> </g></g>', '<path d="m 33 12 L 24 94 L 104 94 L 95 12 z" style="fill:#f8f8f8; stroke:black; stroke-width:4px; stroke-linejoin:round;" /><Line x1="43" y1="82" x2="48" y2="21" style=" stroke:black; stroke-width:4px; stroke-linecap:round;" /><Line x1="64" y1="82" x2="64" y2="21" style=" stroke:black; stroke-width:4px; stroke-linecap:round;" /><Line x1="86" y1="82" x2="80" y2="21" style=" stroke:black; stroke-width:4px; stroke-linecap:round;" /><g transform="translate(70,105)"><g transform="rotate(-10,0,0)"><path d="m -40 0 L -30 5 L 30 5 L 40 0 z" style="fill:#f8f8f8; stroke:black; stroke-width:4px; stroke-linejoin:round;" /><path d="m -9 5 L -9 12 L 9 12 L 9 5" style="fill:none; stroke:black; stroke-width:4px; stroke-linejoin:round;" /></g></g>');
  }
  static SaveIcon(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<path d="m 20 12 A 4 4 0 0 0 16 16 L 16 106  A 4 4 0 0 0 20 110 L 24 110 L 24 108 L 97 108 L 97 110 L 104 110  A 4 4 0 0 0 108 106 L 108 16   A 4 4 0 0 0 104 12  z" fill="lightgray" filter="url(#f1)" />', '<path d="m 20 12 A 4 4 0 0 0 16 16 L 16 106  A 4 4 0 0 0 20 110 L 24 110 L 24 108 L 97 108 L 97 110 L 104 110  A 4 4 0 0 0 108 106 L 108 16   A 4 4 0 0 0 104 12  z" style="fill:#3583dd; stroke:#226fc8; stroke-width:4px; stroke-linejoin:round;" /><path d="m 24 11 L 24 65  A 5 5 0 0 0 29 70 L 95 70  A 5 5 0 0 0 100 65 L 100 11  z" style="fill:#f7f7f7; stroke:#c6c6c6; stroke-width:2px; stroke-linejoin:round;"/><path d="m 36 111 L 90 111 L 90 80 A 4 4 0 0 0 86 76 L 40 76 A 4 4 0 0 0 36 80 z"  style="fill:#e5e5e5; stroke:#c6c6c6; stroke-width:2px; stroke-linejoin:round;" /><rect x="67" y="79" width="14" height ="28"     style="fill:#3583dd; stroke:#c6c6c6; stroke-width:2px; stroke-linejoin:round;"');
  }
  static SaveNeededIcon(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<path d="m 8 16 L 64 110 L 120 16 z " style="fill:lightgray; stroke:lightgray; stroke-width:10px; stroke-linejoin:round;filter:url(#f1)" />', '<path d="m 8 16 L 64 110 L 120 16 z" style="fill:#FAE701; stroke:#FAE701; stroke-width:10px; stroke-linejoin:round;" /><path d="m 8 16 L 64 110 L 120 16 z" style="fill:none; stroke:black; stroke-width:2px; stroke-linejoin:round;" /><g transform="translate(-64,-64) scale(0.4 0.4) translate(260,215)"><path d="m 20 12 A 4 4 0 0 0 16 16 L 16 106  A 4 4 0 0 0 20 110 L 24 110 L 24 108 L 97 108 L 97 110 L 104 110  A 4 4 0 0 0 108 106 L 108 16   A 4 4 0 0 0 104 12  z" style="fill:black; stroke:black; stroke-width:4px; stroke-linejoin:round;" /><path d="m 24 11 L 24 65  A 5 5 0 0 0 29 70 L 95 70  A 5 5 0 0 0 100 65 L 100 11  z" style="fill:#FAE701; stroke:black; stroke-width:2px; stroke-linejoin:round;"/><path d="m 36 111 L 90 111 L 90 80 A 4 4 0 0 0 86 76 L 40 76 A 4 4 0 0 0 36 80 z"  style="fill:#FAE701; stroke:black; stroke-width:2px; stroke-linejoin:round;" /><rect x="67" y="79" width="14" height ="28"     style="fill:#FAE701; stroke:black; stroke-width:2px; stroke-linejoin:round;"</g>', "Don't forget to save.");
  }
  static snapshotIcon(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<path d="m 16 32  L 16 78 A 6 6 0 0 0 22 84 L 46 84 L 48 94 A 5.75 3.25 30 0 0 54 100 L 78 100 A 3.25 5.75  30 0 0 84 94  L 86 84 L 104 84  A 6 6 0 0 0 110 78  L 110 32 A 6 6 0 0 0 104 26 L 22 26 A 6 6 0 0 0 16 32 z"  fill="lightgray" filter="url(#f1)" />', '<path d="m 16 32  L 16 78 A 6 6 0 0 0 22 84 L 46 84 L 48 94 A 5.75 3.25 30 0 0 54 100 L 78 100 A 3.25 5.75  30 0 0 84 94  L 86 84 L 104 84  A 6 6 0 0 0 110 78  L 110 32 A 6 6 0 0 0 104 26 L 22 26 A 6 6 0 0 0 16 32 z" style="fill:#707070; stroke:#464646; stroke-width:4px; stroke-linejoin:round;" /><Circle cx="65" cy="55" r="26"   style="fill:#895e96; stroke:#464646; stroke-width:6px; stroke-linejoin:round;" />');
  }
  static CloseIcon(size, shadow, semitransparent, addIcon, deleteIcon, title) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<path d="m 21.6 38.6 L 47.1 64.3 L 22 89.5 L 38.4 106.2 L 64 81 L 89.3 106.2 L 105.8 89.5 L 80.6 64 L 105.8 38.9 L 89.3 22.2 L 64 47.5  L 38.3 22.1 z"  fill="lightgray" filter="url(#f1)" />', '<path d="m 21.6 38.6 L 47.1 64.3 L 22 89.5 L 38.4 106.2 L 64 81 L 89.3 106.2 L 105.8 89.5 L 80.6 64 L 105.8 38.9 L 89.3 22.2 L 64 47.5  L 38.3 22.1 z" style="fill:#ffffff; stroke:#004a82; stroke-width:4px; stroke-linejoin:round;" />', title);
  }
  static MoveToRightIcon(size, shadow, semitransparent, addIcon, deleteIcon, title) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<rect x="12" y="52" width="4"  height="24" fill="lightgray" filter="url(#f1)" /><rect x="20" y="52" width="8"  height="24" fill="lightgray" filter="url(#f1)" /><rect x="56" y="16" width="16"  height="96" fill="lightgray" filter="url(#f1)" /><path d="m  32 52 L 32 76 L 56 76 L 56 96 L 88 64 L 56 32 L 56 52 z" fill="lightgray" filter="url(#f1)" />', '<rect x="12" y="52" width="4"  height="24" style="fill:#ffffff; stroke:#004a82; stroke-width:4px; stroke-linejoin:round;" /><rect x="20" y="52" width="8"  height="24" style="fill:#ffffff; stroke:#004a82; stroke-width:4px; stroke-linejoin:round;" /><rect x="96" y="16" width="16"  height="96" style="fill:#ffffff; stroke:#004a82; stroke-width:4px; stroke-linejoin:round;" /><path d="m  32 52 L 32 76 L 56 76 L 56 96 L 88 64 L 56 32 L 56 52 z" style="fill:#ffffff; stroke:#004a82; stroke-width:4px; stroke-linejoin:round;" />', title);
  }
  static MoveToLeftIcon(size, shadow, semitransparent, addIcon, deleteIcon, title) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<rect x="112" y="52" width="4"  height="24" fill="lightgray" filter="url(#f1)" /><rect x="100" y="52" width="8"  height="24" fill="lightgray" filter="url(#f1)" /><rect x="16" y="16" width="16"  height="96" fill="lightgray" filter="url(#f1)" /><path d="m  96 52 L 96 76 L 72 76 L 72 96 L 40 64 L 72 32 L 72 52 z" fill="lightgray" filter="url(#f1)" />', '<rect x="112" y="52" width="4"  height="24" style="fill:#ffffff; stroke:#004a82; stroke-width:4px; stroke-linejoin:round;" /><rect x="100" y="52" width="8"  height="24" style="fill:#ffffff; stroke:#004a82; stroke-width:4px; stroke-linejoin:round;" /><rect x="16" y="16" width="16"  height="96" style="fill:#ffffff; stroke:#004a82; stroke-width:4px; stroke-linejoin:round;" /><path d="m  96 52 L 96 76 L 72 76 L 72 96 L 40 64 L 72 32 L 72 52 z" style="fill:#ffffff; stroke:#004a82; stroke-width:4px; stroke-linejoin:round;" />', title);
  }
  static ExpandIcon(size, shadow, semitransparent, addIcon, deleteIcon, title) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<path d="m  24 56 L 24 72 L 56 72 L 56 104 L 72 104 L 72 72 L 104 72 L 104 56 L 72 56 L 72 24 L 56 24 L 56 56 z"  fill="lightgray" filter="url(#f1)" />', '<path d="m 24 56 L 24 72 L 56 72 L 56 104 L 72 104 L 72 72 L 104 72 L 104 56 L 72 56 L 72 24 L 56 24 L 56 56  z" style="fill:black; " />', title);
  }
  static OkIcon(size, shadow, semitransparent, addIcon, deleteIcon, title) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<circle cx="64" cy="64" r="56"   fill="lightgray" filter="url(#f1)" />', '<circle cx="64" cy="64" r="56" fill="#00b20f"/><path d="m 19 72 L 34 87 L 60 62 L 94 96 L 109 81 L 60 32  z" style="fill:white; " />', title);
  }
  static FailedIcon(size, shadow, semitransparent, addIcon, deleteIcon, title) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<circle cx="64" cy="64" r="56"   fill="lightgray" filter="url(#f1)" />', '<circle cx="64" cy="64" r="56" fill="#ff101e"/><path d="m 43 99 L 64 79 L 85 99 L 100 85 L 79 64 L 100 43 L 85 28 L 64 49 L 43 28 L 28  43 L 49 64 L  28 85   z" style="fill:white; " />', title);
  }
  static DontKnowIcon(size, shadow, semitransparent, addIcon, deleteIcon, title) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<circle cx="64" cy="64" r="56"   fill="lightgray" filter="url(#f1)" />', '<circle cx="64" cy="64" r="56" fill="#f1ba00"/><text x="35" y="-30" fill="white" transform="scale(1,-1)" style="font: bold 96px sans-serif;">?</text> ', title);
  }
  static infoIcon(size, shadow, semitransparent, addIcon, deleteIcon, title) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<circle cx="64" cy="64" r="56"   fill="lightgray" filter="url(#f1)" />', '<circle cx="64" cy="64" r="56" fill="DodgerBlue"/><text x="50" y="-30" fill="white" transform="scale(1,-1)" style="font: bold 96px sans-serif;">i</text> ', title);
  }
  static ColapseIcon(size, shadow, semitransparent, addIcon, deleteIcon, title) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, '<path d="m 24 56 L 24 72 L 104 72 L 104 56 z"  fill="lightgray" filter="url(#f1)" />', '<path d="m 24 56 L 24 72 L 104 72 L 104 56 z" style="fill:black; " />', title);
  }
  static AddMarkerIcon(size, index) {
    return _ressources.IconSourceCode(size, false, false, false, false, "", '<circle  cx="64" cy="64"  r="50"   style="fill:lightyellow;" stroke-width="8" stroke="darkred" /><text x="46" y="-40" fill="black" transform="scale(1,-1)" style="font: bold 64px sans-serif;">' + index.toString() + "</text>");
  }
  static AddMarker(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, "", '<line x1="80" y1="8" x2="80" y2="128" stroke="darkred" stroke-width="8" stroke-linecap="round" /><path d="m 60 64 L 100 64 L 80 20 z" style="fill:darkred; " /><rect x="40" y="56" width="80" height="32" style="fill:lightYellow" stroke="darkred" stroke-width="8" />');
  }
  static EraseDatalogger(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, "", '<g transform="rotate(-35,0,0)"><g transform="translate(-56,24)"><path d="m 108 24 L 108 46 L 60 46  A 22 22 0 1 0 60 90 L 108 90  L 108 112 L 60 112 A 44 44 0 1 1 60 24 z" style="fill:#808080; " /><rect x="88" y="24" width="20" height="22" style="fill:red" stroke="none"  /><rect x="88" y="90" width="20" height="22" style="fill:blue" stroke="none"  /><path d="m 108 24 L 108 46 L 60 46  A 22 22 0 1 0 60 90 L 108 90  L 108 112 L 60 112 A 44 44 0 1 1 60 24 z"  style="fill:none" stroke="black"  stroke-width="4" stroke-linejoin="round"/></g></g>');
  }
  static resetViewPort(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, "", '<line x1="116" y1="52" x2="116" y2="12"  stroke="black"  stroke-width="4"/><line x1="12" y1="52" x2="12" y2="12"  stroke="black"  stroke-width="4"/><path d="m 100 36 L 28 36 L 28 48 L 12 32  L 28 16 L 28 28 L 100 28 L 100 16 L 116 32 L 100 48z" style="fill:white" stroke="black"  stroke-width="4" stroke-linejoin="round" /><path d="m 7.3 97.7 L 20 107.4 L 51.6 114.7 L 78.5 112.2 L 101.6 103.3 L 114.4 91.7  L 117.7 81.7 L 104.3 66.9 L 80.9 61.2 L49.9 65 L 25 76.3 L 12.6 87.5 Z " style="fill:white" stroke="none" /><circle  cx="64" cy="88" r="27" style="fill:#6cc87b" stroke="none"  /> <path d="m 37.2 84.8 A 27 27 0 0 0 46.5 108.8 L 64 88 z" style="fill:#8ed698" stroke="none" /> <circle  cx="64" cy="88" r="16" style="fill:black" stroke="none"  /> <path d="m 6 97 A 59 34 -4 0 0 119 80" style="fill:none" stroke="black"  stroke-width="4"  stroke-linecap="round"/> <path d="m 113 79 A 56 31 -16 0 0 9 93 " style="fill:none" stroke="black"  stroke-width="4"  stroke-linecap="round" /> ');
  }
  static DeleteMarker(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, "", '<line x1="80" y1="8" x2="80" y2="128" stroke="darkred" stroke-width="8" stroke-linecap="round" /><path d="m 60 64 L 100 64 L 80 20 z" style="fill:darkred; " /><rect x="40" y="56" width="80" height="32" style="fill:lightYellow" stroke="darkred" stroke-width="8" />');
  }
  static targetIcon(size, shadow, semitransparent, addIcon, deleteIcon, title) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, "", '<circle  cx="64" cy="64"  r="42"   style="fill:none;" stroke-width="8" stroke="black" /><line  x1="0"   y1="64"  x2="42"  y2="64"  style="fill:none;" stroke-width="8" stroke="black" /><line  x1="128" y1="64"  x2="86"  y2="64"  style="fill:none;" stroke-width="8" stroke="black" /><line  y1="0"   x1="64"  y2="42"  x2="64"  style="fill:none;" stroke-width="8" stroke="black" /><line  y1="128" x1="64"  y2="86"  x2="64"  style="fill:none;" stroke-width="8" stroke="black" />', title);
  }
  static DotDotDotIcon(size, shadow, semitransparent, addIcon, deleteIcon, title) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, "", '<rect x="18" y="36" width="18" height="18" style="fill:black" /><rect x="54" y="36" width="18" height="18" style="fill:black" /><rect x="90" y="36" width="18" height="18" style="fill:black" />', title);
  }
  static LogFileIcon(size, shadow, semitransparent, addIcon, deleteIcon) {
    return _ressources.IconSourceCode(size, shadow, semitransparent, addIcon, deleteIcon, "", '<line x1="4" y1="24" x2="30" y2="24"     style=" stroke:black; stroke-width:8px;" /><line x1="34" y1="24" x2="56" y2="24"    style=" stroke:black; stroke-width:8px; " /><line x1="64" y1="24" x2="74" y2="24"    style=" stroke:black; stroke-width:8px; " /><line x1="80" y1="24" x2="88" y2="24"    style=" stroke:black; stroke-width:8px; " /><line x1="4" y1="40" x2="30" y2="40"     style=" stroke:black; stroke-width:8px;" /><line x1="34" y1="40" x2="56" y2="40"    style=" stroke:black; stroke-width:8px; " /><line x1="61" y1="40" x2="90" y2="40"    style=" stroke:black; stroke-width:8px; " /><line x1="94" y1="40" x2="110" y2="40"    style=" stroke:black; stroke-width:8px; " /><line x1="4" y1="56" x2="30" y2="56"     style=" stroke:black; stroke-width:8px;" /><line x1="34" y1="56" x2="56" y2="56"    style=" stroke:black; stroke-width:8px; " /><line x1="61" y1="56" x2="86" y2="56"    style=" stroke:black; stroke-width:8px; " /><line x1="4" y1="72" x2="30" y2="72"     style=" stroke:black; stroke-width:8px;" /><line x1="34" y1="72" x2="50" y2="72"    style=" stroke:black; stroke-width:8px; " /><line x1="56" y1="72" x2="100" y2="72"    style=" stroke:black; stroke-width:8px; " /><line x1="4" y1="88" x2="30" y2="88"     style=" stroke:black; stroke-width:8px;" /><line x1="34" y1="88" x2="56" y2="88"    style=" stroke:black; stroke-width:8px; " /><line x1="61" y1="88" x2="72" y2="88"    style=" stroke:black; stroke-width:8px; " /><line x1="78" y1="88" x2="106" y2="88"    style=" stroke:black; stroke-width:8px; " />');
  }
};

// obj/full/logForm.js
var logForm = class _logForm {
  static show() {
    if (_logForm._window == null) {
      let params = new newWindowParam();
      params.positionType = 0;
      params.width = 8 * window.innerWidth / 10 >> 0;
      params.height = 6 * window.innerHeight / 10 >> 0;
      params.left = 1 * window.innerWidth / 10 >> 0;
      params.top = 2 * window.innerHeight / 10 >> 0;
      params.title = "LogFile";
      _logForm._window = new YWindow(params);
      _logForm._contents = _logForm._window.innerContentDiv;
      _logForm._contents.style.fontFamily = "courrier";
      _logForm._contents.style.fontSize = (10 * constants.generalSizeCoef).toString() + "px";
      _logForm._contents.style.overflow = "auto";
      for (let i = 0; i < _logForm._lines.length; i++) {
        _logForm.addLine(_logForm._lines[i]);
      }
    }
    _logForm._window.show();
  }
  static addLine(st) {
    let pre = document.createElement("PRE");
    pre.style.marginTop = "0";
    pre.style.marginBottom = "0";
    pre.innerText = st;
    _logForm._contents.appendChild(pre);
    while (_logForm._contents.childElementCount > 1e3) {
      _logForm._contents.removeChild(_logForm._contents.firstChild);
    }
  }
  static padStart(st, length) {
    while (st.length < length) {
      st = "0" + st;
    }
    return st;
  }
  static dateToString(d) {
    return d.getFullYear().toString() + "-" + _logForm.padStart((d.getMonth() + 1).toString(), 2) + "-" + _logForm.padStart(d.getDate().toString(), 2) + " " + _logForm.padStart(d.getHours().toString(), 2) + ":" + _logForm.padStart(d.getMinutes().toString(), 2) + ":" + _logForm.padStart((d.getSeconds() + d.getMilliseconds() / 1e3).toFixed(3), 6);
  }
  static log(st) {
    _logForm.logNOTS(_logForm.dateToString(/* @__PURE__ */ new Date()) + " " + st);
  }
  static logNOTS(st) {
    _logForm._lines.push(st);
    if (_logForm._lines.length > 1e3) {
      _logForm._lines.splice(0, _logForm._lines.length - 1e3);
    }
    if (_logForm._window != null)
      _logForm.addLine(st);
  }
};
logForm._lines = [];

// obj/full/rawDataForm.js
var rawDataForm = class _rawDataForm {
  static hide() {
    if (_rawDataForm._window == null)
      return;
    _rawDataForm._window.visible = false;
  }
  static show() {
    if (_rawDataForm._window == null) {
      let params = new newWindowParam();
      params.positionType = 1;
      params.width = 9 * window.innerWidth / 10 >> 0;
      params.height = 8 * window.innerHeight / 10 >> 0;
      params.title = "Raw data";
      _rawDataForm._window = new YWindow(params);
      _rawDataForm._contents = _rawDataForm._window.innerContentDiv;
      _rawDataForm._statusline = document.createElement("DIV");
      _rawDataForm._statusline.style.position = "absolute";
      _rawDataForm._statusline.style.left = constants.WindowPadding.toString() + "px";
      _rawDataForm._statusline.style.right = constants.WindowPadding.toString() + "px";
      _rawDataForm._statusline.style.height = (_rawDataForm.STATUSLINEHEIGHT * constants.generalSizeCoef).toString() + "px";
      _rawDataForm._statusline.style.bottom = constants.WindowPadding.toString() + "px";
      _rawDataForm._statusline.style.paddingTop = Math.round(5 * constants.generalSizeCoef).toString() + "px";
      _rawDataForm._statusline.style.paddingLeft = "5px";
      _rawDataForm._statusline.style.paddingRight = "5px";
      _rawDataForm._statusline.style.overflowX = "auto";
      _rawDataForm._statusline.style.paddingRight = "5px";
      _rawDataForm._contents.append(_rawDataForm._statusline);
      _rawDataForm._sensorsList = document.createElement("DIV");
      _rawDataForm._sensorsList.style.position = "absolute";
      _rawDataForm._sensorsList.style.left = constants.WindowPadding.toString() + "px";
      _rawDataForm._sensorsList.style.right = (_rawDataForm.sensorListWidth * constants.generalSizeCoef + 8 * constants.WindowPadding).toString() + "px";
      _rawDataForm._sensorsList.style.top = "2px";
      _rawDataForm._sensorsList.style.overflowY = "auto";
      _rawDataForm._sensorsList.style.height = (_rawDataForm.topMargin * constants.generalSizeCoef).toString() + "px";
      _rawDataForm._sensorsList.style.paddingLeft = "5px";
      _rawDataForm._sensorsList.style.paddingRight = "5px";
      _rawDataForm._sensorsList.style.border = constants.WindowInnerBorder;
      _rawDataForm._sensorsList.style.backgroundColor = constants.WindowInnerBackgroundColor;
      _rawDataForm._contents.append(_rawDataForm._sensorsList);
      _rawDataForm._optionsList = document.createElement("DIV");
      _rawDataForm._optionsList.style.position = "absolute";
      _rawDataForm._optionsList.style.width = (_rawDataForm.sensorListWidth * constants.generalSizeCoef).toString() + "px";
      _rawDataForm._optionsList.style.right = constants.WindowPadding.toString() + "px";
      _rawDataForm._optionsList.style.top = "0px";
      _rawDataForm._optionsList.style.height = (_rawDataForm.topMargin * constants.generalSizeCoef).toString() + "px";
      _rawDataForm._optionsList.style.paddingLeft = "5px";
      _rawDataForm._optionsList.style.paddingRight = "5px";
      _rawDataForm._optionsList.style.border = "1px solid transparent";
      _rawDataForm._contents.append(_rawDataForm._optionsList);
      let br;
      _rawDataForm._optionMIN = document.createElement("INPUT");
      _rawDataForm._optionMIN.type = "input";
      _rawDataForm._optionMIN.checked = false;
      _rawDataForm._optionMIN.style.display = "inline";
      _rawDataForm._optionMIN.type = "checkbox";
      _rawDataForm._optionMIN.style.transform = "scale(" + constants.generalSizeCoef.toString() + ")";
      _rawDataForm._optionMIN.style.marginRight = Math.round(5 * constants.generalSizeCoef).toString() + "px";
      _rawDataForm._optionMIN.addEventListener("change", () => {
        _rawDataForm.refresh();
      });
      _rawDataForm._optionsList.append(_rawDataForm._optionMIN);
      let span;
      span = document.createElement("SPAN");
      span.innerText = "Include minimum values";
      _rawDataForm._optionsList.append(span);
      br = document.createElement("BR");
      _rawDataForm._optionsList.append(br);
      _rawDataForm._optionAVG = document.createElement("INPUT");
      _rawDataForm._optionAVG.type = "input";
      _rawDataForm._optionAVG.checked = true;
      _rawDataForm._optionAVG.style.display = "inline";
      _rawDataForm._optionAVG.type = "checkbox";
      _rawDataForm._optionAVG.style.transform = "scale(" + constants.generalSizeCoef.toString() + ")";
      _rawDataForm._optionAVG.style.marginRight = Math.round(5 * constants.generalSizeCoef).toString() + "px";
      _rawDataForm._optionAVG.addEventListener("change", () => {
        _rawDataForm.refresh();
      });
      _rawDataForm._optionsList.append(_rawDataForm._optionAVG);
      span = document.createElement("SPAN");
      span.innerText = "Include average values";
      _rawDataForm._optionsList.append(span);
      br = document.createElement("BR");
      _rawDataForm._optionsList.append(br);
      _rawDataForm._optionMAX = document.createElement("INPUT");
      _rawDataForm._optionMAX.type = "input";
      _rawDataForm._optionMAX.checked = false;
      _rawDataForm._optionMAX.style.display = "inline";
      _rawDataForm._optionMAX.type = "checkbox";
      _rawDataForm._optionMAX.style.transform = "scale(" + constants.generalSizeCoef.toString() + ")";
      _rawDataForm._optionMAX.style.marginRight = Math.round(5 * constants.generalSizeCoef).toString() + "px";
      _rawDataForm._optionMAX.addEventListener("change", () => {
        _rawDataForm.refresh();
      });
      _rawDataForm._optionsList.append(_rawDataForm._optionMAX);
      span = document.createElement("SPAN");
      span.innerText = "Include maximum values";
      _rawDataForm._optionsList.append(span);
      _rawDataForm._optionsList.append(document.createElement("BR"));
      _rawDataForm._optionsList.append(document.createElement("BR"));
      let p = document.createElement("P");
      p.style.textAlign = "center";
      let refreshBtn = new button("refresh", () => {
        _rawDataForm.refresh();
      });
      p.appendChild(refreshBtn.Element);
      let ExportBtn = new button("export", () => {
        _rawDataForm.export();
      });
      p.appendChild(ExportBtn.Element);
      _rawDataForm._optionsList.append(p);
      _rawDataForm._datacontents = document.createElement("DIV");
      _rawDataForm._datacontents.style.position = "absolute";
      _rawDataForm._datacontents.style.left = constants.WindowPadding.toString() + "px";
      _rawDataForm._datacontents.style.right = constants.WindowPadding.toString() + "px";
      _rawDataForm._datacontents.style.top = (_rawDataForm.topMargin * constants.generalSizeCoef + 5 * constants.WindowPadding).toString() + "px";
      _rawDataForm._datacontents.style.bottom = (4 * constants.WindowPadding + _rawDataForm.STATUSLINEHEIGHT * constants.generalSizeCoef).toString() + "px";
      _rawDataForm._datacontents.style.paddingLeft = "5px";
      _rawDataForm._datacontents.style.paddingRight = "5px";
      _rawDataForm._datacontents.style.overflowX = "auto";
      _rawDataForm._datacontents.style.paddingRight = "5px";
      _rawDataForm._contents.append(_rawDataForm._datacontents);
      _rawDataForm._dataTable = document.createElement("TABLE");
      _rawDataForm._dataTable.style.position = "absolute";
      _rawDataForm._dataTable.style.overflow = "auto";
      _rawDataForm._dataTable.style.left = "0px";
      _rawDataForm._dataTable.style.width = "100%";
      _rawDataForm._dataTable.style.top = "0px";
      _rawDataForm._dataTable.style.bottom = "0px";
      _rawDataForm._dataTable.style.fontSize = constants.generalFontSize.toString() + "px";
      _rawDataForm._dataTable.style.borderCollapse = "colapse";
      _rawDataForm._dataTable.style.borderSpacing = "0px";
      _rawDataForm._dataTable.style.fontFamily = "sans-serif";
      _rawDataForm._datacontents.appendChild(_rawDataForm._dataTable);
    }
    _rawDataForm._window.show();
    _rawDataForm.refresh();
  }
  static refresh() {
    let list = [];
    for (let i = 0; i < sensorsManager.sensorList.length; i++) {
      if (!(sensorsManager.sensorList[i] instanceof NullYSensor)) {
        list.push(sensorsManager.sensorList[i]);
      }
    }
    _rawDataForm._selection = [];
    for (let i = 0; i < list.length; i++) {
      let spanNode;
      let spanText;
      let input;
      if (i >= _rawDataForm._sensorsList.childNodes.length) {
        spanNode = document.createElement("SPAN");
        input = document.createElement("INPUT");
        input.type = "input";
        input.checked = false;
        input.style.display = "inline";
        input.type = "checkbox";
        input.style.transform = "scale(" + constants.generalSizeCoef.toString() + ")";
        input.style.marginRight = Math.round(5 * constants.generalSizeCoef).toString() + "px";
        input.addEventListener("change", () => {
          _rawDataForm.refresh();
        });
        spanNode.appendChild(input);
        spanText = document.createElement("SPAN");
        spanNode.appendChild(spanText);
        _rawDataForm._sensorsList.appendChild(spanNode);
        spanNode.appendChild(document.createElement("BR"));
      } else {
        spanNode = _rawDataForm._sensorsList.childNodes[i];
        input = spanNode.childNodes[0];
        spanText = spanNode.childNodes[1];
      }
      if (input.checked)
        _rawDataForm._selection.push(list[i]);
      input.id = list[i].get_hardwareId();
      spanText.innerText = list[i].get_friendlyName();
    }
    while (_rawDataForm._sensorsList.childNodes.length > list.length) {
      _rawDataForm._sensorsList.removeChild(_rawDataForm._sensorsList.lastChild);
    }
    _rawDataForm.RefreshContents(_rawDataForm._selection);
  }
  static padStart(st, length) {
    while (st.length < length) {
      st = "0" + st;
    }
    return st;
  }
  static UnixTimeStampTostring(ts) {
    let d = new Date(Date.UTC(1970, 0, 1));
    d.setUTCSeconds(ts >> 0, 1e3 * (ts % 1));
    return d.getFullYear().toString() + "-" + _rawDataForm.padStart((d.getMonth() + 1).toString(), 2) + "-" + _rawDataForm.padStart(d.getDate().toString(), 2) + " " + _rawDataForm.padStart(d.getHours().toString(), 2) + ":" + _rawDataForm.padStart(d.getMinutes().toString(), 2) + ":" + _rawDataForm.padStart((d.getSeconds() + d.getMilliseconds() / 1e3).toFixed(3), 6);
  }
  static RefreshContents(slist) {
    let TH = document.createElement("THEAD");
    let TB = document.createElement("TBODY");
    let HR1 = document.createElement("TR");
    let HR2 = document.createElement("TR");
    let indexes = [];
    for (let i = 0; i < slist.length; i++) {
      indexes.push(slist[i].curData.length - 1);
    }
    while (_rawDataForm._dataTable.childNodes.length > 0) {
      _rawDataForm._dataTable.removeChild(_rawDataForm._dataTable.lastChild);
    }
    _rawDataForm._dataTable.appendChild(TH);
    _rawDataForm._dataTable.appendChild(TB);
    let sMin = false;
    let sMax = false;
    let sAvg = false;
    let toShow = 0;
    if (_rawDataForm._optionMIN.checked) {
      sMin = true;
      toShow++;
    }
    if (_rawDataForm._optionAVG.checked) {
      sAvg = true;
      toShow++;
    }
    if (_rawDataForm._optionMAX.checked) {
      sMax = true;
      toShow++;
    }
    TH.appendChild(HR1);
    TH.appendChild(HR2);
    let cell = document.createElement("TH");
    HR1.appendChild(cell);
    cell = document.createElement("TH");
    HR2.appendChild(cell);
    for (let i = 0; i < slist.length; i++) {
      cell = document.createElement("TH");
      cell.innerText = slist[i].get_friendlyName();
      cell.colSpan = Math.max(1, toShow);
      cell.style.backgroundColor = "#f0f0f0";
      cell.style.borderLeft = constants.WindowInnerBorder;
      cell.style.borderRight = constants.WindowInnerBorder;
      cell.style.borderTop = constants.WindowInnerBorder;
      HR1.appendChild(cell);
      if (sMin) {
        cell = document.createElement("TH");
        cell.innerText = "Min";
        cell.style.borderLeft = constants.WindowInnerBorder;
        cell.style.borderRight = sAvg || sMax ? "" : constants.WindowInnerBorder;
        cell.style.backgroundColor = "#f0f0f0";
        cell.style.textAlign = "right";
        HR2.appendChild(cell);
      }
      if (sAvg) {
        cell = document.createElement("TH");
        cell.innerText = "Avg";
        cell.style.borderLeft = sMin ? "" : constants.WindowInnerBorder;
        cell.style.borderRight = sMax ? "" : constants.WindowInnerBorder;
        cell.style.backgroundColor = "#f0f0f0";
        cell.style.textAlign = "right";
        HR2.appendChild(cell);
      }
      if (sMax) {
        cell = document.createElement("TH");
        cell.innerText = "Max";
        cell.style.borderLeft = sAvg || sMin ? "" : constants.WindowInnerBorder;
        cell.style.borderRight = constants.WindowInnerBorder;
        cell.style.backgroundColor = "#f0f0f0";
        cell.style.textAlign = "right";
        HR2.appendChild(cell);
      }
    }
    let colcount = slist.length * toShow;
    let MaxTimeStamp = 0;
    for (let i = 0; i < slist.length; i++) {
      if (indexes[i] >= 0) {
        if (MaxTimeStamp < slist[i].curData[indexes[i]].DateTime) {
          MaxTimeStamp = slist[i].curData[indexes[i]].DateTime;
        }
      }
    }
    let rowcount = 0;
    let currentRow = null;
    while (MaxTimeStamp > 0 && rowcount < _rawDataForm.MAXRAWDATAROWS) {
      let row = new Array(colcount);
      let nextTimeStamp = 0;
      for (let i = 0; i < slist.length; i++) {
        let Shown = false;
        if (indexes[i] >= 0) {
          if (slist[i].curData[indexes[i]].DateTime == MaxTimeStamp) {
            let n = 0;
            if (sMin) {
              row[n + i * toShow] = slist[i].minData[indexes[i]].value.toString();
              n++;
            }
            if (sAvg) {
              row[n + i * toShow] = slist[i].curData[indexes[i]].value.toString();
              n++;
            }
            if (sMax) {
              row[n + i * toShow] = slist[i].maxData[indexes[i]].value.toString();
              n++;
            }
            Shown = true;
            indexes[i]--;
          }
          if (indexes[i] >= 0) {
            if (slist[i].curData[indexes[i]].DateTime > nextTimeStamp) {
              nextTimeStamp = slist[i].curData[indexes[i]].DateTime;
            }
          }
        }
        if (!Shown) {
          let n = 0;
          if (sMin) {
            row[n + i * toShow] = "";
            n++;
          }
          if (sAvg) {
            row[n + i * toShow] = "";
            n++;
          }
          if (sMax) {
            row[n + i * toShow] = "";
            n++;
          }
        }
      }
      if (colcount > 0 && toShow > 0) {
        currentRow = document.createElement("TR");
        let cell2 = document.createElement("TD");
        cell2.innerText = _rawDataForm.UnixTimeStampTostring(MaxTimeStamp).toLocaleString();
        cell2.style.borderLeft = constants.WindowInnerBorder;
        cell2.style.borderTop = rowcount == 0 ? constants.WindowInnerBorder : "";
        cell2.style.backgroundColor = "#f0f0f0";
        currentRow.style.backgroundColor = (rowcount & 1) == 0 ? "white" : "#f0f0f0";
        currentRow.appendChild(cell2);
        for (let j = 0; j < row.length; j++) {
          cell2 = document.createElement("TD");
          cell2.innerText = row[j];
          cell2.style.textAlign = "right";
          cell2.style.borderLeft = j % toShow == 0 ? constants.WindowInnerBorder : "";
          cell2.style.borderRight = (j + 1) % toShow == 0 ? constants.WindowInnerBorder : "";
          cell2.style.fontFamily = "monospace";
          currentRow.appendChild(cell2);
        }
        TB.appendChild(currentRow);
        rowcount++;
      }
      MaxTimeStamp = nextTimeStamp;
    }
    if (currentRow != null) {
      for (let i = 0; i < currentRow.childNodes.length; i++) {
        currentRow.childNodes[i].style.borderBottom = constants.WindowInnerBorder;
      }
    }
    if (toShow == 0) {
      _rawDataForm._statusline.style.backgroundColor = "#FF8080";
      _rawDataForm._statusline.innerText = "No data shown, select at least Min, Avg or Max options in the top-right checkboxes";
    } else if (rowcount == 0) {
      _rawDataForm._statusline.style.backgroundColor = "#FF8080";
      _rawDataForm._statusline.innerText = "No data shown, select at least one sensor in the list above.";
    } else {
      _rawDataForm._statusline.style.backgroundColor = constants.WindowBackgroundColor;
      if (rowcount < _rawDataForm.MAXRAWDATAROWS) {
        _rawDataForm._statusline.innerText = "These are the last " + rowcount.toString() + " data rows.";
      } else {
        _rawDataForm._statusline.innerText = "These are the last " + rowcount.toString() + " data rows.  Use the export feature to get the whole data set.";
      }
    }
  }
  static getCvsdata(slist, sMin, sAvg, sMax) {
    let toShow = 0;
    let indexes = [];
    for (let i = 0; i < slist.length; i++) {
      indexes.push(0);
    }
    if (sMin)
      toShow++;
    if (sAvg)
      toShow++;
    if (sMax)
      toShow++;
    let res;
    let ColumnHeader = "Timestamp";
    for (let i = 0; i < slist.length; i++) {
      let sName = slist[i].toString();
      if (sMin)
        ColumnHeader += ";" + sName + "(min)";
      if (sAvg)
        ColumnHeader += ";" + sName + "(avg)";
      if (sMax)
        ColumnHeader += ";" + sName + "(max)";
    }
    res = ColumnHeader + "\r\n";
    let colcount = slist.length * toShow;
    let MinTimeStamp = Number.POSITIVE_INFINITY;
    for (let i = 0; i < slist.length; i++) {
      if (indexes[i] < slist[i].curData.length) {
        if (MinTimeStamp > slist[i].curData[indexes[i]].DateTime) {
          MinTimeStamp = slist[i].curData[indexes[i]].DateTime;
        }
      }
    }
    while (MinTimeStamp < Number.POSITIVE_INFINITY) {
      let line = "";
      let nextTimeStamp = Number.POSITIVE_INFINITY;
      for (let i = 0; i < slist.length; i++) {
        let Shown = false;
        if (indexes[i] < slist[i].curData.length) {
          if (slist[i].curData[indexes[i]].DateTime == MinTimeStamp) {
            if (sMin)
              line += ";" + slist[i].minData[indexes[i]].value.toString();
            if (sAvg)
              line += ";" + slist[i].curData[indexes[i]].value.toString();
            if (sMax)
              line += ";" + slist[i].maxData[indexes[i]].value.toString();
            Shown = true;
            indexes[i]++;
          }
          if (indexes[i] < slist[i].curData.length) {
            if (slist[i].curData[indexes[i]].DateTime < nextTimeStamp) {
              nextTimeStamp = slist[i].curData[indexes[i]].DateTime;
            }
          }
        }
        if (!Shown) {
          if (sMin)
            line += " ; ";
          if (sAvg)
            line += " ; ";
          if (sMax)
            line += " ; ";
        }
      }
      if (colcount > 0)
        line = _rawDataForm.UnixTimeStampTostring(MinTimeStamp) + line;
      res += line + "\r\n";
      MinTimeStamp = nextTimeStamp;
    }
    return res;
  }
  static export() {
    let element = document.createElement("a");
    let data = "data:text/csv;charset=utf-8," + encodeURI(_rawDataForm.getCvsdata(_rawDataForm._selection, _rawDataForm._optionMIN.checked, _rawDataForm._optionAVG.checked, _rawDataForm._optionMAX.checked));
    element.setAttribute("href", data);
    element.setAttribute("download", "rawdata.csv");
    element.style.display = "none";
    document.body.appendChild(element);
    element.click();
    document.body.removeChild(element);
  }
};
rawDataForm._lines = [];
rawDataForm.topMargin = 100;
rawDataForm.sensorListWidth = 200;
rawDataForm.MAXRAWDATAROWS = 100;
rawDataForm.STATUSLINEHEIGHT = 20;
rawDataForm._selection = [];

// obj/full/windowManager.js
var button = class {
  constructor(caption, action, sizeCoefOverload) {
    this._enabled = true;
    this._visible = true;
    this._caption = "";
    this._caption = caption;
    this._action = action;
    let GUIcoef = constants.generalSizeCoef;
    if (typeof sizeCoefOverload !== "undefined")
      GUIcoef = sizeCoefOverload;
    this._link = document.createElement("A");
    this._link.innerText = this._caption;
    this._link.style.cursor = "pointer";
    this._link.style.display = "inlineblock";
    let padding = Math.round(5 * GUIcoef).toString() + "px";
    let margin = Math.round(3 * GUIcoef).toString() + "px";
    this._link.tabIndex = 0;
    this._link.style.padding = padding + " " + padding + " " + padding + " " + padding;
    this._link.style.margin = margin + " " + margin + " " + margin + " " + margin;
    this._link.style.borderColor = "#808080";
    this._link.style.backgroundColor = "#f0f0f0";
    this._link.style.fontFamily = constants.generalFontFamily;
    this._link.style.fontSize = (16 * GUIcoef).toString() + "px";
    this._link.style.cursor = "pointer";
    this._link.style.border = constants.WindowInnerBorder;
    this._link.addEventListener("mouseover", () => {
      if (this._enabled)
        this._link.style.backgroundColor = "white";
    });
    this._link.addEventListener("mouseout", () => {
      if (this._enabled)
        this._link.style.backgroundColor = "#f0f0f0";
    });
    this._link.addEventListener("click", () => {
      if (this._enabled)
        this._action(this);
    });
    this._link.addEventListener("keypress", (e) => {
      if (this._enabled && (e.key == "Enter" || e.key == " "))
        this._action(this);
    });
  }
  get Element() {
    return this._link;
  }
  set tabIndex(index) {
    this._link.tabIndex = index;
  }
  focus() {
    this._link.focus();
  }
  get visible() {
    return this._visible;
  }
  set visible(value) {
    this._visible = value;
    this._link.style.display = this._visible ? "" : "none";
  }
  get enabled() {
    return this._enabled;
  }
  set enabled(value) {
    this.enable(value);
  }
  performAction() {
    if (!this._enabled)
      return;
    this._action(this);
  }
  showShortcut(enable) {
    if (!this._enabled)
      enable = false;
    if (enable) {
      this._link.innerHTML = "<u>" + this._caption.substring(0, 1) + "</u>" + this._caption.substring(1);
    } else {
      this._link.innerText = this._caption;
    }
  }
  enable(state) {
    if (state == this._enabled)
      return;
    this._enabled = state;
    this._link.tabIndex = state ? 0 : -1;
    if (this._enabled) {
      this._link.style.cursor = "pointer";
      this._link.style.backgroundColor = "#f0f0f0";
      this._link.style.color = "black";
    } else {
      this._link.style.backgroundColor = "#e0e0e0";
      this._link.style.cursor = "default";
      this._link.style.color = "#A0A0A0";
    }
  }
};
var newWindowParam = class _newWindowParam {
  get GUICoef() {
    return this._GUICoef;
  }
  constructor(sizeCoefOverload) {
    this.createNow = false;
    this.left = 100;
    this.top = 100;
    this.positionType = 0;
    this.width = 400;
    this.height = 300;
    this.isModal = false;
    this.showShadow = true;
    this.showContainerBorders = true;
    this.showHeader = true;
    this.title = "new Window";
    this.closeIcon = true;
    this.headerBgColor = "";
    this.bottomKeepClear = 0;
    this.contentsDefaultFontSize = constants.generalFontSize;
    this.contentsDefaultFontFamily = constants.generalFontFamily;
    this.closingCallBack = null;
    this.WindowBackgroundColor = "#f0f0f0";
    this.WindowBorder = "1px solid #808080";
    this.WindowPadding = 2;
    this.WindowInnerBorderColor = " #A0A0A0";
    this.WindowInnerBackgroundColor = "#fAfAfA";
    this.WindowInnerBorder = "1px solid " + this.WindowInnerBorderColor;
    this.WindowHeaderBackgroundColor = "#0072ca";
    this.WindowHeaderColor = "white";
    this.WindowHeaderBorder = "1px solid #0072ca";
    this._GUICoef = constants.generalSizeCoef;
    this.WindowHeaderHeight = Math.round(20 * constants.generalSizeCoef);
    this.WindowHeaderFontSize = 16 * constants.generalSizeCoef;
    this.WindowHeaderFontFamily = constants.generalFontFamily;
    this.buttons = [];
    if (typeof sizeCoefOverload !== "undefined") {
      this.WindowHeaderHeight = 20 * sizeCoefOverload;
      this.WindowHeaderFontSize = 16 * sizeCoefOverload;
      this._GUICoef = sizeCoefOverload;
    }
  }
  clone() {
    let it = new _newWindowParam();
    let keys = Object.keys(this);
    for (let i = 0; i < keys.length; i++) {
      if (keys[i] != "buttons")
        it[keys[i]] = this[keys[i]];
    }
    for (let i = 0; i < this.buttons.length; i++) {
      it.buttons.push(this.buttons[i]);
    }
    return it;
  }
};
var YWindow = class _YWindow {
  static get ConTextMenuBestZindex() {
    for (let i = 0; i < _YWindow.windowList.length; i++) {
      if (_YWindow.windowList[i].isModal())
        return -1;
    }
    return 1e5 + 2 * _YWindow.windowList.length;
  }
  constructor(params) {
    this._div = null;
    this._titleSpan = null;
    this._ModalDivShield = null;
    this._switchSideDiv = null;
    this._buttonsContainer = null;
    this._params = params.clone();
    _YWindow.windowList.push(this);
    this._contents = document.createElement("DIV");
    if (params.createNow)
      this.allocate();
  }
  get innerContentDiv() {
    return this._contents;
  }
  get bottomKeepClear() {
    return this._params.bottomKeepClear;
  }
  set bottomKeepClear(value) {
    this._params.bottomKeepClear = value;
    if (this._contents != null) {
      this._contents.style.bottom = (this._params.WindowPadding + this._params.bottomKeepClear).toString() + "px";
    }
  }
  get outterContentDiv() {
    return this._div;
  }
  isModal() {
    if (this._div == null)
      return false;
    return this._div.style.display == "" && this._params.isModal;
  }
  get visible() {
    if (this._div == null)
      return false;
    return this._div.style.display == "";
  }
  set visible(v) {
    v ? this.show() : this.hide();
  }
  show(width) {
    if (this._div == null)
      this.allocate();
    this._div.style.display = "";
    if (this._ModalDivShield != null)
      this._ModalDivShield.style.display = "";
    this.brintToFront();
    if (typeof width !== "undefined" && this._params.positionType == 2) {
      this._div.style.width = width.toString() + "px";
    }
  }
  allocate() {
    if (this._div != null)
      return;
    if (this._params.isModal) {
      this._ModalDivShield = document.createElement("DIV");
      this._ModalDivShield.style.position = "fixed";
      this._ModalDivShield.style.left = "0px";
      this._ModalDivShield.style.top = "0px";
      this._ModalDivShield.style.right = "0px";
      this._ModalDivShield.style.bottom = "0px";
      this._ModalDivShield.style.backgroundColor = "rgba(255,255,255,0.85) ";
      document.body.appendChild(this._ModalDivShield);
    }
    this._div = document.createElement("DIV");
    this._div.style.display = "none";
    switch (this._params.positionType) {
      case 0:
        this._div.style.position = "absolute";
        this._div.style.left = this._params.left.toString() + "px";
        this._div.style.top = this._params.top.toString() + "px";
        this._div.style.width = this._params.width.toString() + "px";
        this._div.style.height = this._params.height.toString() + "px";
        break;
      case 1:
        this._div.style.position = "absolute";
        this._div.style.left = "50%";
        this._div.style.top = "50%";
        this._div.style.transform = "translate(-50%, -50%)";
        this._div.style.width = this._params.width.toString() + "px";
        this._div.style.height = this._params.height.toString() + "px";
        break;
      case 2:
        this._div.style.position = "fixed";
        this._div.style.top = "5px";
        this._div.style.right = "5px";
        this._div.style.bottom = "5px";
        this._div.style.width = this._params.width.toString() + "px";
        break;
    }
    this._div.style.border = this._params.WindowBorder;
    this._div.style.backgroundColor = this._params.WindowBackgroundColor;
    if (this._params.showShadow)
      this._div.style.boxShadow = " 4px 4px 4px 1px rgba(0,0,0,0.05)";
    let containerTop = this._params.WindowPadding;
    if (this._params.showHeader) {
      containerTop = this._params.WindowHeaderHeight + 3 * this._params.WindowPadding;
      let title = document.createElement("DIV");
      title.style.position = "absolute";
      title.style.left = this._params.WindowPadding.toString() + "px";
      title.style.top = this._params.WindowPadding.toString() + "px";
      title.style.right = this._params.WindowPadding.toString() + "px";
      title.style.height = this._params.WindowHeaderHeight.toString() + "px";
      title.style.border = this._params.WindowHeaderBorder;
      title.style.backgroundColor = this._params.WindowHeaderBackgroundColor;
      title.style.fontFamily = this._params.WindowHeaderFontFamily;
      title.style.color = this._params.WindowHeaderColor;
      title.style.fontSize = this._params.WindowHeaderFontSize.toString() + "px";
      title.style.paddingLeft = "5px";
      title.style.whiteSpace = "nowrap";
      title.addEventListener("click", () => {
        this.brintToFront();
      });
      this._titleSpan = document.createElement("SPAN");
      this._titleSpan.innerText = this._params.title;
      title.appendChild(this._titleSpan);
      if (this._params.closeIcon) {
        let closeDiv = document.createElement("DIV");
        closeDiv.style.position = "absolute";
        closeDiv.style.right = "0px";
        closeDiv.style.top = "0px";
        closeDiv.style.width = Math.round(this._params.WindowHeaderHeight + 5).toString() + "px";
        closeDiv.style.height = this._params.WindowHeaderHeight.toString() + "px";
        closeDiv.style.overflow = "hide";
        closeDiv.style.paddingLeft = "5px";
        closeDiv.tabIndex = 0;
        closeDiv.style.cursor = "pointer";
        closeDiv.style.backgroundColor = this._params.WindowHeaderBackgroundColor;
        closeDiv.innerHTML = ressources.CloseIcon(this._params.WindowHeaderHeight.toString(), false, false, false, false, "Click to close this window");
        closeDiv.addEventListener("click", () => {
          this.hide();
        });
        closeDiv.addEventListener("keypress", (e) => {
          if (e.key == "Enter")
            this.hide();
        });
        title.appendChild(closeDiv);
      }
      if (this._params.positionType == 2) {
        this._switchSideDiv = document.createElement("DIV");
        this._switchSideDiv.style.position = "absolute";
        this._switchSideDiv.style.width = Math.round(this._params.WindowHeaderHeight + 5).toString() + "px";
        this._switchSideDiv.style.right = Math.round(this._params.WindowHeaderHeight + 5) + "px";
        this._switchSideDiv.style.height = this._params.WindowHeaderHeight.toString() + "px";
        this._switchSideDiv.style.overflow = "hide";
        this._switchSideDiv.style.paddingLeft = "5px";
        this._switchSideDiv.style.top = "0px";
        this._switchSideDiv.style.cursor = "pointer";
        this._switchSideDiv.style.backgroundColor = this._params.WindowHeaderBackgroundColor;
        this._switchSideDiv.innerHTML = ressources.MoveToLeftIcon(this._params.WindowHeaderHeight.toString(), false, false, false, false, "Click to move editor on left side");
        this._switchSideDiv.addEventListener("click", () => {
          this.switchSide();
        });
        title.appendChild(this._switchSideDiv);
      }
      this._div.appendChild(title);
    }
    if (this._params.buttons.length > 0) {
      this._buttonsContainer = document.createElement("DIV");
      this._buttonsContainer.style.position = "absolute";
      this._buttonsContainer.style.bottom = Math.round(this._params.WindowPadding).toString() + "px";
      this._buttonsContainer.style.right = Math.round(this._params.WindowPadding).toString() + "px";
      this._buttonsContainer.style.textAlign = "right";
      for (let i = 0; i < this._params.buttons.length; i++) {
        this._buttonsContainer.appendChild(this._params.buttons[i].Element);
      }
      this._div.appendChild(this._buttonsContainer);
      this._buttonsContainer.style.height = (25 * this._params.GUICoef).toString() + "px";
      this._params.bottomKeepClear += Math.round(35 * this._params.GUICoef);
    }
    this._contents.style.position = "absolute";
    this._contents.style.left = this._params.WindowPadding.toString() + "px";
    this._contents.style.right = this._params.WindowPadding.toString() + "px";
    this._contents.style.top = containerTop.toString() + "px";
    this._contents.style.bottom = (this._params.WindowPadding + this._params.bottomKeepClear).toString() + "px";
    this._contents.style.paddingLeft = "5px";
    this._contents.style.paddingRight = "5px";
    if (this._contents.style.fontSize == "") {
      this._contents.style.fontSize = this._params.contentsDefaultFontSize.toString() + "px";
    }
    if (this._contents.style.fontFamily == "") {
      this._contents.style.fontFamily = this._params.contentsDefaultFontFamily;
    }
    if (this._params.showContainerBorders) {
      this._contents.style.border = this._params.WindowInnerBorder;
      this._contents.style.backgroundColor = this._params.WindowInnerBackgroundColor;
    } else {
      this._contents.style.border = "1px solid rgba(0, 0, 0, 0)";
      this._contents.style.backgroundColor = "";
    }
    this._contents.style.overflow = "hide";
    this._div.append(this._contents);
    document.body.appendChild(this._div);
  }
  hide() {
    if (this._div == null)
      return;
    this._div.style.display = "none";
    if (this._ModalDivShield != null)
      this._ModalDivShield.style.display = "none";
    if (this._params.closingCallBack != null)
      this._params.closingCallBack(this);
  }
  set title(value) {
    this._params.title = value;
    if (this._titleSpan != null) {
      this._titleSpan.innerText = value;
    }
  }
  brintToFront() {
    let n = -1;
    for (let i = 0; i < _YWindow.windowList.length; i++) {
      if (_YWindow.windowList[i] == this)
        n = i;
    }
    if (n < 0)
      return;
    _YWindow.windowList.splice(n, 1);
    _YWindow.windowList.push(this);
    for (let i = 0; i < _YWindow.windowList.length; i++) {
      _YWindow.windowList[i].zIndex = i;
    }
  }
  set zIndex(index) {
    if (this._div == null)
      return;
    this._div.style.zIndex = (100001 + 2 * index).toString();
    if (this._params.isModal)
      this._ModalDivShield.style.zIndex = (1e5 + 2 * index).toString();
  }
  switchSide() {
    if (this._div.style.left == "") {
      this._div.style.left = "5px";
      this._div.style.right = "";
      this._switchSideDiv.innerHTML = ressources.MoveToRightIcon(Math.round(20 * this._params.GUICoef).toString(), false, false, false, false, "Click to move editor on right side");
    } else {
      this._div.style.left = "";
      this._div.style.right = "5px";
      this._switchSideDiv.innerHTML = ressources.MoveToLeftIcon(Math.round(20 * this._params.GUICoef).toString(), false, false, false, false, "Click to move editor on left side");
    }
  }
};
YWindow.windowList = [];
var confirm = class _confirm {
  static ask(message, yes, no, userdata) {
    let params = new newWindowParam();
    params.positionType = 1;
    params.width = Math.min(Math.round(400 * constants.generalSizeCoef), Math.round(screen.width * 0.9));
    params.height = Math.min(Math.round(160 * constants.generalSizeCoef), Math.round(screen.height * 0.7));
    params.isModal = true;
    params.showHeader = false;
    params.showContainerBorders = false;
    let YesButton = new button("Yes", () => {
      _confirm._window.hide();
      if (yes != null)
        yes(userdata);
    });
    let NoButton = new button("No", () => {
      _confirm._window.hide();
      if (no != null)
        no(userdata);
    });
    params.buttons.push(YesButton);
    params.buttons.push(NoButton);
    _confirm._window = new YWindow(params);
    let contents = _confirm._window.innerContentDiv;
    let table = document.createElement("TABLE");
    table.style.width = "100%";
    let TR = document.createElement("TR");
    let TD = document.createElement("TD");
    TD.style.width = "10";
    TD.innerHTML = ressources.DontKnowIcon(Math.round(80 * constants.generalSizeCoef).toString(), true, false, false, false, "");
    TR.appendChild(TD);
    TD = document.createElement("TD");
    TD.style.paddingLeft = "20px";
    TD.style.fontFamily = constants.generalFontFamily;
    TD.style.fontSize = constants.generalFontSize.toString() + "px";
    TD.style.textAlign = "justify";
    TD.innerText = message;
    TR.appendChild(TD);
    table.appendChild(TR);
    contents.appendChild(table);
    _confirm._window.show();
    return _confirm._window;
  }
};
var notififaction = class {
  static show(icon, message) {
    let params = new newWindowParam();
    params.positionType = 1;
    params.width = Math.min(Math.round(400 * constants.generalSizeCoef), screen.width);
    params.height = Math.min(Math.round(160 * constants.generalSizeCoef), screen.height);
    params.isModal = true;
    params.showHeader = false;
    params.showContainerBorders = false;
    let okButton = new button("OK", () => {
      alert2._window.hide();
    });
    params.buttons.push(okButton);
    alert2._window = new YWindow(params);
    let contents = alert2._window.innerContentDiv;
    let table = document.createElement("TABLE");
    table.style.width = "100%";
    let TR = document.createElement("TR");
    let TD = document.createElement("TD");
    TD.style.width = "10";
    TD.innerHTML = icon;
    TR.appendChild(TD);
    TD = document.createElement("TD");
    TD.style.paddingLeft = "20px";
    TD.style.fontFamily = constants.generalFontFamily;
    TD.style.fontSize = constants.generalFontSize.toString() + "px";
    TD.style.textAlign = "justify";
    TD.innerText = message;
    TR.appendChild(TD);
    table.appendChild(TR);
    contents.appendChild(table);
    alert2._window.show();
    return alert2._window;
  }
};
var alert2 = class extends notififaction {
  static show(message) {
    return super.show(ressources.FailedIcon(Math.round(80 * constants.generalSizeCoef).toString(), true, false, false, false, ""), message);
  }
};
var info = class extends notififaction {
  static show(message) {
    return super.show(ressources.OkIcon(Math.round(80 * constants.generalSizeCoef).toString(), true, false, false, false, ""), message);
  }
};

// obj/full/mainForm_rw.js
var mainForm = class _mainForm {
  constructor() {
    this.GUIcoef = 1;
    let params = new newWindowParam();
    params.positionType = 1;
    this.GUIcoef = constants.generalSizeCoef;
    if (_mainForm.REFWIDH * this.GUIcoef > screen.width)
      this.GUIcoef = screen.width / _mainForm.REFWIDH;
    if (_mainForm.REFHEIGHT * this.GUIcoef > screen.height)
      this.GUIcoef = screen.height / _mainForm.REFHEIGHT;
    params.width = Math.round(_mainForm.REFWIDH * this.GUIcoef);
    params.height = Math.round(_mainForm.REFHEIGHT * this.GUIcoef);
    params.showHeader = false;
    params.showContainerBorders = false;
    this._window = new YWindow(params);
    let contents = this._window.innerContentDiv;
    let title = document.createElement("p");
    title.style.paddingTop = "0px";
    title.style.marginTop = 4 * this.GUIcoef + "px";
    title.style.fontSize = 16 * this.GUIcoef + "px";
    title.innerText = "Hello, welcome to Yocto-Visualization, what would you like to do?";
    title.style.textAlign = "center";
    contents.appendChild(title);
    let table = document.createElement("TABLE");
    table.style.position = "absolute";
    table.style.top = "0px";
    table.style.fontFamily = constants.generalFontFamily;
    table.style.tableLayout = "fixed";
    table.style.paddingLeft = Math.round(10 * this.GUIcoef).toString() + "px";
    table.style.paddingRight = Math.round(10 * this.GUIcoef).toString() + "px";
    table.style.paddingTop = Math.round(30 * this.GUIcoef).toString() + "px";
    table.style.paddingBottom = Math.round(15 * this.GUIcoef).toString() + "px";
    table.style.width = "100%";
    table.style.height = "100%";
    table.style.borderSpacing = Math.round(5 * this.GUIcoef).toString() + "px";
    let tr1 = document.createElement("TR");
    table.appendChild(tr1);
    let td11 = document.createElement("TD");
    tr1.appendChild(td11);
    let td12 = document.createElement("TD");
    tr1.appendChild(td12);
    let td13 = document.createElement("TD");
    tr1.appendChild(td13);
    let tr2 = document.createElement("TR");
    table.appendChild(tr2);
    let td21 = document.createElement("TD");
    tr2.appendChild(td21);
    let td22 = document.createElement("TD");
    tr2.appendChild(td22);
    let td23 = document.createElement("TD");
    tr2.appendChild(td23);
    contents.appendChild(table);
    let version = document.createElement("p");
    version.style.position = "absolute";
    version.style.right = "0px";
    version.style.bottom = "0px";
    version.style.marginBottom = "0px";
    version.style.paddingBottom = "0px";
    version.style.marginRight = "2px";
    version.style.fontSize = (8 * this.GUIcoef).toString() + "px";
    version.innerText = constants.buildVersion;
    version.style.color = "grey";
    version.style.textAlign = "right";
    contents.appendChild(version);
    let size = (128 * this.GUIcoef).toString();
    this.createButton(td11, "Configure global parameters", ressources.ConfigureIcon(size, true, false, false, false), () => {
      this.openConfig();
    });
    this.createButton(td12, "Create a new solid gauge", ressources.SolidGaugeIcon(size, true, false, false, false), () => {
      YWebPage.newGaugeWidget();
    });
    this.createButton(td13, "Create a new graph", ressources.GraphIcon(size, true, false, false, false), () => {
      YWebPage.newGraphWidget();
    });
    this.createButton(td21, "Get Raw data", ressources.RawDataIcon(size, true, false, false, false), () => {
      this.openRawData();
    });
    this.createButton(td22, "Create a new digital display", ressources.DigitalDisplayIcon(size, true, false, false, false), () => {
      YWebPage.newDigitalDisplayWidget();
    });
    this.createButton(td23, "Create a new angular gauge", ressources.AngularGaugeIcon(size, true, false, false, false), () => {
      YWebPage.newAngularGaugeWidget();
    });
  }
  hide() {
    this._window.hide();
  }
  show() {
    this._window.show();
  }
  createButton(container, caption, icon, callback) {
    container.style.border = "1px solid black";
    container.style.textAlign = "center";
    container.style.fontSize = (10 * this.GUIcoef).toString() + "px";
    container.style.paddingBottom = "3px";
    container.style.borderColor = "#808080";
    container.style.backgroundColor = "#f0f0f0";
    container.style.cursor = "pointer";
    container.addEventListener("mouseover", function() {
      container.style.backgroundColor = "white";
    });
    container.addEventListener("mouseout", function() {
      container.style.backgroundColor = "#f0f0f0";
    });
    let a = document.createElement("a");
    a.style.outline = "none";
    a.innerHTML = icon + "<br>" + caption;
    a.addEventListener("click", callback);
    a.tabIndex = 0;
    a.addEventListener("focusin", () => {
      container.style.backgroundColor = "white";
    });
    a.addEventListener("focusout", () => {
      container.style.backgroundColor = "f0f0f0";
    });
    a.addEventListener("keypress", (e) => {
      if (e.key == "Enter")
        callback();
    });
    container.appendChild(a);
  }
  openConfig() {
    configForm.show();
  }
  openRawData() {
    rawDataForm.show();
  }
};
mainForm.REFWIDH = 500;
mainForm.REFHEIGHT = 360;

// obj/full/Editor_rw.js
var EditedDataSource = class {
  constructor(source, root) {
    this._htmlchilds = [];
    this._source = source;
    this._root = root;
    this._htmlchilds = [];
  }
  storeChilds(childsList) {
    this._htmlchilds = [];
    for (let i = 0; i < childsList.length; i++) {
      this._htmlchilds.push(childsList.item(i));
    }
  }
  getStoredChilds() {
    return this._htmlchilds;
  }
  get root() {
    return this._root;
  }
  refresh() {
    if (this._root != null)
      this._root.refresh();
  }
};
var keyDataObject = class {
  constructor(key, data) {
    this.key = key;
    this.data = data;
  }
};
var objectDictionnary = class {
  constructor() {
    this.list = [];
  }
  isInlist(key) {
    for (let i = 0; i < this.list.length; i++) {
      if (key == this.list[i].key)
        return true;
    }
    return false;
  }
  find(key) {
    for (let i = 0; i < this.list.length; i++) {
      if (key == this.list[i].key)
        return this.list[i].data;
    }
    return null;
  }
  insert(key, data) {
    for (let i = 0; i < this.list.length; i++) {
      if (key == this.list[i].key) {
        this.list[i].data = data;
        return;
      }
    }
    this.list.push(new keyDataObject(key, data));
  }
};
var EditorBaseDef = class {
  refresh() {
  }
  get panel() {
    return null;
  }
  get expanded() {
    return true;
  }
  AllocateControls() {
  }
};
var PropertiesForm2 = class _PropertiesForm2 extends EditorBaseDef {
  get visible() {
    return this._window.visible;
  }
  set visible(v) {
    v ? this.show() : this.hide();
  }
  get text() {
    return this._text;
  }
  set text(value) {
    this._text = value;
    this._window.title = this._text;
  }
  get help() {
    return this._helpText;
  }
  set help(value) {
    this._helpText = value;
    this._helpPanel.innerHTML = this._helpText;
  }
  addTest(parentDiv) {
    _PropertiesForm2.testid++;
    let mainlabel = document.createElement("DIV");
    mainlabel.style.position = "relative";
    mainlabel.style.display = "inline-block;";
    mainlabel.style.left = "0px";
    mainlabel.style.padding = "2px 2px 2px 2px";
    mainlabel.style.right = "5px";
    mainlabel.style.border = "1px solid black";
    mainlabel.style.marginTop = "2px";
    mainlabel.style.backgroundColor = "yellow";
    if (parentDiv != null) {
      parentDiv.appendChild(mainlabel);
    } else {
      this._container.appendChild(mainlabel);
    }
    let expandCtrl = document.createElement("ANCHOR");
    expandCtrl.innerText = "[+]";
    mainlabel.appendChild(expandCtrl);
    let mainText = document.createElement("SPAN");
    mainText.innerText = "Pouet " + _PropertiesForm2.testid.toString();
    mainlabel.appendChild(mainText);
    let subElementPanel = document.createElement("DIV");
    subElementPanel.style.position = "relative";
    subElementPanel.style.display = "inline-block;";
    subElementPanel.style.left = "5px";
    subElementPanel.style.padding = "2px 2px 2px 2px";
    subElementPanel.style.right = "5px";
    subElementPanel.style.marginTop = "2px";
    subElementPanel.style.backgroundColor = "lightgray";
    subElementPanel.innerText = "sub elements";
    if (parentDiv != null) {
      parentDiv.appendChild(subElementPanel);
    } else {
      this._container.appendChild(subElementPanel);
    }
    return subElementPanel;
  }
  editorIsClosing(source) {
    if (this._editorClosedCallback != null)
      this._editorClosedCallback();
  }
  bestWidth() {
    let w = window.innerWidth / 4 >> 0;
    let minWidth = 275 * constants.guiDPIFactor;
    if (w < minWidth)
      w = minWidth;
    if (w > window.innerWidth - 10)
      w = window.innerWidth - 10;
    return w;
  }
  constructor() {
    super();
    this._hostWindow = null;
    this._editorClosedCallback = null;
    this.currentEditedDataSource = null;
    this._changeCallback = null;
    this._getvalueCallback = null;
    this._text = "Properties editor";
    this._helpText = "Properties editor";
    this.EditedDataSourceList = new objectDictionnary();
    let params = new newWindowParam();
    params.positionType = 2;
    params.width = this.bestWidth();
    params.createNow = true;
    params.closingCallBack = (source) => {
      this.editorIsClosing(source);
    };
    params.bottomKeepClear = _PropertiesForm2.HelpHeight + 3 * constants.WindowPadding;
    params.title = this._text;
    this._window = new YWindow(params);
    this._container = this._window.innerContentDiv;
    this._helpPanel = document.createElement("DIV");
    this._helpPanel.style.position = "absolute";
    this._helpPanel.style.left = constants.WindowPadding.toString() + "px";
    this._helpPanel.style.bottom = constants.WindowPadding.toString() + "px";
    this._helpPanel.style.right = constants.WindowPadding.toString() + "px";
    this._helpPanel.style.height = _PropertiesForm2.HelpHeight.toString() + "px";
    this._helpPanel.style.paddingLeft = "5px";
    this._helpPanel.style.paddingTop = "5px";
    this._helpPanel.style.overflowY = "auto";
    this._helpPanel.style.border = constants.WindowInnerBorder;
    this._helpPanel.style.backgroundColor = constants.WindowInnerBackgroundColor;
    this._helpPanel.style.fontFamily = constants.generalFontFamily;
    this._helpPanel.style.fontSize = constants.generalFontSize.toString() + "px";
    this._helpPanel.style.color = "black";
    this._helpPanel.style.display = "";
    this._helpPanel.innerHTML = "Help panel. Editor is loading, please wait....";
    this._window.outterContentDiv.appendChild(this._helpPanel);
    this._container.style.overflowY = "scroll";
    this._container.style.overflowX = "hidden";
    this._container.style.backgroundColor = constants.WindowInnerBackgroundColor;
    window.addEventListener("resize", () => {
      this.windowResized();
    });
  }
  windowResized() {
    if (this._window.innerContentDiv == null)
      return;
    if (this._window.innerContentDiv.offsetHeight < _PropertiesForm2.MinHeightforShowingHelp && this._helpPanel.style.display == "") {
      this._helpPanel.style.display = "none";
      this._window.bottomKeepClear = 0;
    } else if (this._window.innerContentDiv.offsetHeight >= _PropertiesForm2.MinHeightforShowingHelp + _PropertiesForm2.HelpHeight && this._helpPanel.style.display != "") {
      this._helpPanel.style.display = "";
      this._window.bottomKeepClear = _PropertiesForm2.HelpHeight;
    }
  }
  show() {
    this._window.show(this.bestWidth());
    this.windowResized();
  }
  hide() {
    YWidget.stopEdition();
    this._window.hide();
  }
  get panel() {
    return this._container;
  }
  refresh() {
    if (!this.visible)
      return;
    if (this.currentEditedDataSource == null)
      return;
    this.currentEditedDataSource.refresh();
  }
  refreshBanner() {
    if (this._hostWindow != null) {
      this.text = "Properties of " + this._hostWindow.Text;
    }
  }
  showWindow(hostWindow, structData, setvalueCallBack, getvalueCallBack, EditStoppedCallBack) {
    this._hostWindow = hostWindow;
    this._changeCallback = setvalueCallBack;
    this._getvalueCallback = getvalueCallBack;
    this.text = "Properties editor";
    this.show();
    this.refreshBanner();
    this.EditObject(structData, setvalueCallBack, getvalueCallBack, EditStoppedCallBack);
  }
  EditObject(structData, setvalueCallBack, getvalueCallBack, EditStoppedCallBack) {
    this._editorClosedCallback = EditStoppedCallBack;
    let helpYourselfMsg = "This is the property editor. Change any parameter you want. All changes are applied in real time.";
    if (!this.visible)
      return;
    this.help = "Please wait....";
    if (this.EditedDataSourceList.isInlist(structData)) {
      if (this.EditedDataSourceList.find(structData) == this.currentEditedDataSource) {
        this.help = helpYourselfMsg;
        return;
      }
    }
    if (this.currentEditedDataSource != null) {
      this.currentEditedDataSource.root.stopEdit();
      this.currentEditedDataSource.storeChilds(this._container.childNodes);
      while (this._container.firstChild) {
        this._container.removeChild(this._container.lastChild);
      }
    }
    let it;
    if (!this.EditedDataSourceList.isInlist(structData)) {
      let rootparam = new UIElementBaseParams(this._helpPanel, this, null, "root", "Root", "root node", "");
      it = new UIElement(rootparam);
      it.rootEditor = this;
      this.currentEditedDataSource = new EditedDataSource(structData, it);
      this.EditedDataSourceList.insert(structData, this.currentEditedDataSource);
      it.showLabel = false;
      it.expandable = true;
      it.ProcessNewType(structData);
      it.expanded = true;
    } else {
      this.currentEditedDataSource = this.EditedDataSourceList.find(structData);
      let childNodes = this.currentEditedDataSource.getStoredChilds();
      for (let i = 0; i < childNodes.length; i++) {
        this._container.appendChild(childNodes[i]);
      }
      this.currentEditedDataSource.refresh();
    }
    this.currentEditedDataSource.root.startEdit(setvalueCallBack, getvalueCallBack);
    this.currentEditedDataSource.root.resizeAll();
    this.help = helpYourselfMsg;
  }
  getConfigData() {
    let p = this._window.outterContentDiv.getBoundingClientRect();
    return "<PropertiesForm>\n<location x='" + p.left.toString() + "' y='" + p.top.toString() + "'/>\n<size     w='" + p.width.toString() + "' h='" + p.width.toString() + "'/>\n</PropertiesForm>\n";
  }
  Form1_SizeChanged(sender) {
    if (this.currentEditedDataSource == null)
      return;
    if (this.currentEditedDataSource.root != null)
      this.currentEditedDataSource.root.resizeAll();
  }
  valuechange(src) {
    let OriginalPropName = "";
    let fullpropname = "";
    let propType = "";
    if (this._changeCallback != null)
      this._changeCallback(src);
  }
};
PropertiesForm2.HelpHeight = Math.round(75 * constants.generalSizeCoef);
PropertiesForm2.MinHeightforShowingHelp = 175;
PropertiesForm2.testid = 0;

// obj/full/UIElements_rw.js
var UIElementBaseParams = class {
  constructor(p_descriptionLabel, p_parentNode, p_RootNode, p_internalname, p_label, p_description, p_isReadonlyCall) {
    this.expandable = false;
    this.isReadonlyCall = "";
    this.summary = null;
    this.structRoot = null;
    this.HelpZoneLabel = p_descriptionLabel;
    this.parentNode = p_parentNode;
    this.RootNode = p_RootNode;
    this.internalname = p_internalname;
    this.label = p_label;
    this.description = p_description;
    this.changeCausesParentRefresh = false;
    this.preExpanded = false;
    this.isReadonlyCall = p_isReadonlyCall;
  }
};
var PropPathinfo = class {
  constructor() {
    this.OriginalPropName = "";
    this.fullpropname = "";
    this.propType = "";
  }
};
var CustomAttributesExtractor = class {
  constructor(ps) {
    this.available = true;
    this.displayName = "unknown";
    this.category = "";
    this.displayDescription = "Not documented";
    this.prefinedValues = null;
    this.changeCausesParentRefresh = false;
    this.PreExpandedCategory = false;
    this.PreExpanded = false;
    this.summaryPropertyName = "";
    this.isReadOnlyCall = "";
    this.NotSavedInXML = false;
    this.ReadOnly = false;
    this.ParamCategorySummary = "";
    if ("OnlyAvailableon" in ps.Attributes)
      this.available = ps.Attributes["OnlyAvailableon"];
    if ("DisplayName" in ps.Attributes)
      this.displayName = ps.Attributes["DisplayName"];
    if ("CategoryAttribute" in ps.Attributes)
      this.category = ps.Attributes["CategoryAttribute"];
    if ("DescriptionAttribute" in ps.Attributes)
      this.displayDescription = ps.Attributes["DescriptionAttribute"];
    if ("AllowedValues" in ps.Attributes)
      this.prefinedValues = ps.Attributes["AllowedValues"];
    if ("ChangeCausesParentRefreshAttribute" in ps.Attributes)
      this.changeCausesParentRefresh = ps.Attributes["ChangeCausesParentRefreshAttribute"];
    if ("PreExpandedCategoryAttribute" in ps.Attributes)
      this.PreExpandedCategory = ps.Attributes["PreExpandedCategoryAttribute"];
    if ("PreExpandedAttribute" in ps.Attributes)
      this.PreExpanded = ps.Attributes["PreExpandedAttribute"];
    if ("ParamCategorySummaryAttribute" in ps.Attributes)
      this.summaryPropertyName = ps.Attributes["ParamCategorySummaryAttribute"];
    if ("IsReadonlyCallAttribute" in ps.Attributes)
      this.isReadOnlyCall = ps.Attributes["IsReadonlyCallAttribute"];
    if ("NotSavedInXMLAttribute" in ps.Attributes)
      this.NotSavedInXML = ps.Attributes["NotSavedInXMLAttribute"];
    if ("ReadOnlyAttribute " in ps.Attributes)
      this.ReadOnly = ps.Attributes["ReadOnlyAttribute "];
    if ("ParamCategorySummaryAttribute " in ps.Attributes)
      this.ParamCategorySummary = ps.Attributes["ParamCategorySummaryAttribute "];
  }
};
var UIElement = class _UIElement extends EditorBaseDef {
  get parentNode() {
    return this._parentNode;
  }
  get targetFullName() {
    return this._targetFullName;
  }
  get expandIcon() {
    return ressources.ExpandIcon(_UIElement.defaultFontSize.toString(), false, false, false, false, "Click to expand");
  }
  get colapseIcon() {
    return ressources.ColapseIcon(_UIElement.defaultFontSize.toString(), false, false, false, false, "Click to colapse");
  }
  removeFromEditor() {
  }
  get panel() {
    return this.subElementPanel;
  }
  get editorHelpText() {
    return "";
  }
  get isRootElement() {
    return this._rootNode == this;
  }
  addToEditor() {
    if (this.mainlabel != null) {
      for (let i = 0; i < this._parentPanel.children.length; i++) {
        if (this._parentPanel.children[i] == this.mainlabel)
          return;
      }
      this._parentPanel.appendChild(this.mainlabel);
      this._parentPanel.appendChild(this.subElementPanel);
    }
  }
  refresh() {
    if (this.mainText != null)
      this.mainText.innerText = this.name + this.getSummary();
    for (let i = 0; i < this.subElements.length; i++) {
      this.subElements[i].refresh();
    }
    if (this._rootNode != null && this._rootNode.rootEditor != null)
      this._rootNode.rootEditor.refreshBanner();
  }
  tabReOrder(index) {
    if (this._expandable && this.expandCtrl != null)
      this.expandCtrl.tabIndex = index++;
    this.subElements.forEach((e) => {
      index = e.tabReOrder(index);
    });
    return index;
  }
  triggerValueChangeCallback() {
    if (this._valueChangeCallback != null)
      this._valueChangeCallback(this);
  }
  scrollToMakeVisible() {
    if (this.mainlabel == null)
      return;
    if (this._parentPanel == null)
      return;
    this.mainlabel.scrollIntoView();
  }
  control_GotFocus(sender) {
    let extraHelp = this.editorHelpText;
    if (extraHelp != "")
      extraHelp = " " + extraHelp;
    this._helpZoneLabel.innerHTML = this._description + extraHelp;
  }
  control_LostFocus(sender) {
    this._helpZoneLabel.innerHTML = "";
  }
  preExpandIfNeeded() {
    if (this._parentNode != null && this._parentNode.expanded) {
      this.AllocateControls();
      if (this.expandable)
        this.initExpandButton();
      this.initMainLabel();
    }
    this.initExpandButton();
  }
  constructor(p) {
    super();
    this.name = "NoName";
    this._targetFullName = "unknowTargetName";
    this.ExpandableDepth = 0;
    this._expanded = false;
    this._expandButtonSide = 0;
    this._rootEditor = null;
    this._description = "";
    this.expandCtrl = null;
    this.mainlabel = null;
    this.mainText = null;
    this.subElementPanel = null;
    this._rootNode = null;
    this._parentNode = null;
    this._parentPanel = null;
    this._changeCausesParentRefresh = false;
    this._isReadOnlyCall = "";
    this._controlsAllocated = false;
    this.expandButtonInitDone = false;
    this.mainLabelInitDone = false;
    this.subElements = [];
    this._valueChangeCallback = null;
    this._getValueCallback = null;
    this._shown = true;
    this._summaryCallback = null;
    this._showLabel = false;
    this._expandable = false;
    this._preExpanded = false;
    this.name = p.label;
    this._targetFullName = p.internalname;
    this._helpZoneLabel = p.HelpZoneLabel;
    this._description = p.description;
    this._changeCausesParentRefresh = p.changeCausesParentRefresh;
    this._expandable = p.expandable;
    this._preExpanded = p.preExpanded;
    this._structRoot = p.structRoot;
    this._isReadOnlyCall = p.isReadonlyCall;
    this._summaryProperty = p.summary;
    if (_UIElement.baseheight == 0) {
      _UIElement.baseheight = 12;
      _UIElement.expandableShift = _UIElement.baseheight + 2;
      _UIElement.scrollBarWidth = 20;
    }
    this._parentNode = p.parentNode;
    this._rootNode = p.RootNode == null ? this : p.RootNode;
  }
  set rootEditor(value) {
    this._rootEditor = value;
  }
  get rootEditor() {
    return this._rootEditor;
  }
  initMainLabel() {
    if (this.mainlabel == null)
      return;
    if (this.mainLabelInitDone)
      return;
    this.ExpandableDepth = 0;
    let p = this;
    while (p != null) {
      if (p.expandable)
        this.ExpandableDepth++;
      p = p.parentNode;
    }
    this.mainText.innerText = this.name + this.getSummary();
    if (this.expandable && this._expandButtonSide == 0) {
      switch (this.ExpandableDepth) {
        case 1:
          this.mainlabel.style.backgroundColor = "Blue";
          this.mainText.style.color = "black";
          break;
        case 2:
          this.mainlabel.style.backgroundColor = "rgb(7,141,255)";
          this.mainText.style.color = "white";
          break;
        case 3:
          this.mainlabel.style.backgroundColor = "rgb(120,196,255)";
          break;
        case 4:
          this.mainlabel.style.backgroundColor = "rgb(213,237,255)";
          break;
        case 5:
          this.mainlabel.style.backgroundColor = "rgb(237,246,253)";
          break;
        default:
          this.mainlabel.style.backgroundColor = "rgb(246,251,255)";
          break;
      }
    } else {
      this.mainlabel.style.backgroundColor = "rgba( 0, 0, 0,0)";
    }
    this.mainLabelInitDone = true;
  }
  AllocateControls() {
    if (this._controlsAllocated)
      return;
    if (!this.isRootElement)
      this.parentNode.AllocateControls();
    this._parentPanel = this.parentNode.panel;
    if (this.mainlabel == null) {
      this.mainlabel = document.createElement("DIV");
      this.mainlabel.style.position = "relative";
      this.mainlabel.style.display = "inline-block;";
      this.mainlabel.style.left = "0px";
      this.mainlabel.style.padding = "2px 2px 2px 2px";
      this.mainlabel.style.overflow = "hidden";
      this.mainlabel.style.fontFamily = _UIElement.defaultFontFamily;
      this.mainlabel.style.fontSize = _UIElement.defaultFontSize.toString() + "px";
      this.mainlabel.style.right = "5px";
      this.mainlabel.style.whiteSpace = "nowrap";
      this.mainlabel.style.marginTop = "2px";
      this.mainlabel.style.backgroundColor = "yellow";
      this.mainText = document.createElement("SPAN");
      this.mainText.innerText = "Pouet";
      this.mainlabel.appendChild(this.mainText);
      this.mainlabel.style.display = this.isRootElement ? "none" : "";
      this.initMainLabel();
      if (this._parentPanel == null) {
        debugger;
      }
      this._parentPanel.appendChild(this.mainlabel);
    }
    if (this._expandable && this.expandCtrl == null) {
      this.expandCtrl = document.createElement("ANCHOR");
      if (this._expandButtonSide == 0) {
        this.mainlabel.insertBefore(this.expandCtrl, this.mainText);
      }
      this.initExpandButton();
      this.subElementPanel = document.createElement("DIV");
      this.subElementPanel.style.position = "relative";
      this.subElementPanel.style.display = "inline-block;";
      this.subElementPanel.style.left = this.isRootElement ? "0px" : "5px";
      this.subElementPanel.style.padding = "2px 2px 2px 2px";
      this.subElementPanel.style.right = "5px";
      this.subElementPanel.style.display = "none";
      this.subElementPanel.style.marginTop = "2px";
      this.subElementPanel.style.marginRight = "2px";
      this._parentPanel.appendChild(this.subElementPanel);
    }
    this._controlsAllocated = true;
  }
  initExpandButton() {
    if (this.expandCtrl == null)
      return;
    if (this.expandButtonInitDone)
      return;
    this.expandCtrl.innerHTML = this.expandIcon;
    this.expandCtrl.style.top = "0px";
    this.expandCtrl.style.left = "0px";
    this.expandCtrl.style.cursor = "pointer";
    this.expandCtrl.style.backgroundColor = "white";
    this.expandCtrl.style.marginRight = "3px";
    this.expandCtrl.style.paddingLeft = "0px";
    this.expandCtrl.style.paddingRight = "0px";
    this.expandCtrl.style.paddingTop = "1px";
    this.expandCtrl.style.marginTop = "0px";
    this.expandCtrl.style.marginBottom = "0px";
    this.expandCtrl.style.paddingBottom = "0px";
    this.expandCtrl.style.paddingRight = "0px";
    this.expandCtrl.addEventListener("click", () => {
      this.ToggleExpand();
    });
    this.expandCtrl.addEventListener("keyup", (e) => {
      if (e.key === "Enter" || e.key === " ")
        this.ToggleExpand();
    });
    this.expandCtrl.addEventListener("focusin", () => {
      this.control_GotFocus(this);
    });
    this.expandCtrl.addEventListener("focusout", () => {
      this.control_LostFocus(this);
    });
    this.expandButtonInitDone = true;
  }
  get rowHeight() {
    return _UIElement.baseheight;
  }
  ExtractPropPath(info2) {
    let index = -1;
    let path = [];
    let p = this;
    info2.OriginalPropName = p.targetFullName;
    do {
      info2.fullpropname = p.targetFullName;
      index = info2.fullpropname.indexOf("_");
      if (index < 0) {
        if (info2.fullpropname != _UIElement.ARTIFICIALSECTIONNAME)
          path.splice(0, 0, info2.fullpropname);
        p = p.parentNode;
        if (p == null)
          throw "invalid Property name";
      }
    } while (index < 0);
    info2.propType = info2.fullpropname.substring(0, index);
    let propname = info2.fullpropname.substring(index + 1);
    path.splice(0, 0, propname);
    return path;
  }
  startEdit(setcallback, getCallback) {
    this.addToEditor();
    this._valueChangeCallback = setcallback;
    this._getValueCallback = getCallback;
    this.subElements.forEach((item) => {
      item.startEdit(setcallback, getCallback);
    });
  }
  stopEdit() {
    this.removeFromEditor();
    this._valueChangeCallback = null;
    this._getValueCallback = null;
    this.subElements.forEach((item) => {
      item.stopEdit();
    });
  }
  expand() {
    this.subElements.forEach((e) => {
      e.AllocateControls();
      e.show();
    });
    this._rootNode.tabReOrder(0);
    this._expanded = true;
    this.subElementPanel.style.display = "";
    this.expandCtrl.innerHTML = this.colapseIcon;
  }
  colapse() {
    this.subElementPanel.style.display = "none";
    this.expandCtrl.innerHTML = this.expandIcon;
  }
  get expanded() {
    return this._expanded;
  }
  set expanded(value) {
    this._expanded = value;
    if (this._expanded) {
      this.expand();
    } else {
      this.colapse();
    }
    this._rootNode.resizeAll();
  }
  ToggleExpand() {
    this.expanded = !this.expanded;
  }
  getSummary() {
    if (this._summaryProperty != null) {
      return " (" + Reflect.get(this._structRoot, this._summaryProperty.name) + ")";
    }
    return "";
  }
  sensorList() {
    let res = [];
    sensorsManager.sensorList.forEach((s) => {
      res.push(s);
    });
    return res;
  }
  ProcessNewType(dataStucture) {
    let subsections = {};
    this._structRoot = dataStucture;
    let toExpand = [];
    let properties = GenericProperties.getAllProperties(dataStucture);
    for (let i = 0; i < properties.byIndex.length; i++) {
      let ps = properties.byIndex[i];
      if (ps.isWritable) {
        let attr = new CustomAttributesExtractor(ps);
        if (attr.available) {
          let section = this;
          if (attr.category != "") {
            if (attr.category in subsections) {
              section = subsections[attr.category];
            } else {
              let sectionparam = new UIElementBaseParams(this._helpZoneLabel, this, this._rootNode, _UIElement.ARTIFICIALSECTIONNAME, attr.category, attr.category + " section, expand for more...", "");
              sectionparam.preExpanded = attr.PreExpandedCategory;
              sectionparam.expandable = true;
              sectionparam.structRoot = dataStucture;
              if (attr.summaryPropertyName != "") {
                sectionparam.summary = ps;
              }
              section = new _UIElement(sectionparam);
              if (attr.PreExpandedCategory)
                toExpand.push(section);
              section.showLabel = true;
              subsections[attr.category] = section;
              this.addSubElement(section);
            }
          }
          let elParam = new UIElementBaseParams(this._helpZoneLabel, section, this._rootNode, ps.name, attr.displayName, attr.displayDescription, attr.isReadOnlyCall);
          elParam.preExpanded = attr.PreExpanded;
          elParam.changeCausesParentRefresh = attr.changeCausesParentRefresh;
          let targetName = this._targetFullName;
          let targetPrefix = "";
          let p = targetName.indexOf("_");
          if (p >= 0) {
            targetPrefix = targetName.substring(0, p);
            targetName = targetName.substring(p + 1);
          }
          if (ps.isEnum) {
            let s = new UIElementEnum(elParam, dataStucture, ps);
            section.addSubElement(s);
          } else {
            switch (ps.type) {
              case "number":
                if (attr.prefinedValues == null) {
                  let n = new UIElementNumber(elParam, dataStucture, ps);
                  section.addSubElement(n);
                } else {
                  let s2 = new UIElementList(elParam, dataStucture, ps, () => {
                    return attr.prefinedValues;
                  });
                  section.addSubElement(s2);
                }
                break;
              case "boolean":
                let b = new UIElementBoolean(elParam, dataStucture, ps);
                section.addSubElement(b);
                break;
              case doubleNan.name:
                let d = new UIElementDoubleNan(elParam, dataStucture, ps);
                section.addSubElement(d);
                break;
              case NullYSensor.name:
              case CustomYSensor.name:
                let s = new UIElementList(elParam, dataStucture, ps, () => {
                  return this.sensorList();
                });
                section.addSubElement(s);
                break;
              case YColor.name:
                let c = new UIElementColor(elParam, dataStucture, ps);
                section.addSubElement(c);
                break;
              case xAxisPosition.name:
                let p2 = new UIElementMarkerPos(elParam, dataStucture, ps);
                section.addSubElement(p2);
                break;
              case "string":
                if (attr.prefinedValues == null) {
                  let s2 = new UIElementString(elParam, dataStucture, ps);
                  section.addSubElement(s2);
                } else {
                  let s2 = new UIElementList(elParam, dataStucture, ps, () => {
                    return attr.prefinedValues;
                  });
                  section.addSubElement(s2);
                }
                break;
              default:
                {
                  elParam.expandable = true;
                  elParam.preExpanded = attr.PreExpanded;
                  let subobjectsection = new _UIElement(elParam);
                  if (attr.PreExpanded)
                    toExpand.push(subobjectsection);
                  subobjectsection.showLabel = true;
                  section.addSubElement(subobjectsection);
                  subobjectsection.ProcessNewType(dataStucture[ps.name]);
                }
                break;
            }
          }
        }
      } else {
        if (ps.name == "summary") {
          this._summaryProperty = ps;
        }
      }
    }
    if (this._summaryProperty != null)
      this.refresh();
    toExpand.forEach((e) => {
      e.expand();
    });
  }
  show() {
    this._shown = true;
    if (this.mainlabel != null)
      this.mainlabel.style.display = "";
    if (this.expandCtrl != null)
      this.expandCtrl.style.display = this._expandable ? "" : "hidden";
    if (this.expanded) {
      this.subElements.forEach((e) => {
        e.show();
      });
    }
  }
  hide() {
    this._shown = false;
    if (this.mainlabel != null)
      this.mainlabel.style.display = "hidden";
    if (this.expandCtrl != null)
      this.expandCtrl.style.display = this._expandable ? "hidden" : "";
    this.subElements.forEach((e) => {
      e.hide();
    });
  }
  ExpandCtrl_Click_expand(sender) {
    this.expanded = !this._expanded;
  }
  resizeAll() {
    this.resize(0, 0, 0, 0);
    if (this.expanded) {
      this.subElements.forEach((e) => {
        e.resizeAll();
      });
    }
  }
  get showLabel() {
    return this._showLabel;
  }
  set showLabel(value) {
    this._showLabel = value;
    if (this.expandCtrl != null)
      this.expandCtrl.style.display = value && this._expandable ? "" : "hidden";
  }
  get expandable() {
    return this._expandable;
  }
  set expandable(value) {
    this._expandable = value;
    if (!value)
      return;
    if (this.expandCtrl == null) {
      this.AllocateControls();
      this.initExpandButton();
    }
    this.expandCtrl.style.display = value && this._showLabel ? "" : "hidden";
  }
  addSubElement(node) {
    this.subElements.push(node);
  }
  computeHeight() {
    return 0;
  }
  resize(top, left1, left2, width) {
    return 0;
  }
};
UIElement.defaultFontFamily = constants.generalFontFamily;
UIElement.defaultFontSize = constants.generalFontSize;
UIElement.ARTIFICIALSECTIONNAME = "-ignore-";
UIElement.baseheight = 0;
UIElement.indentation = 16;
UIElement.expandableShift = 0;
UIElement.scrollBarWidth = 0;
UIElement.expandableVerticalOffset = 2;
var UIElementGeneric = class extends UIElement {
  constructor(p, dataContainer, prop) {
    super(p);
    this.input = null;
    this.select = null;
    this._prop = prop;
    this._dataContainer = dataContainer;
    this._showLabel = true;
    if (this._preExpanded)
      this.AllocateControls();
    if (this._expandable && this._preExpanded)
      this.expand();
  }
  removeFromEditor() {
    super.removeFromEditor();
  }
  tabReOrder(index) {
    if (this.input != null)
      this.input.tabIndex = index++;
    this.subElements.forEach((e) => {
      index = e.tabReOrder(index);
    });
    return index;
  }
  addToEditor() {
    super.addToEditor();
  }
  show() {
    super.show();
    if (this.input != null)
      this.input.style.visibility = "";
  }
  checkForReadOnly() {
    if (this.input == null && this.select == null)
      return;
    if (this._isReadOnlyCall != "") {
      let ro = Reflect.get(this._dataContainer, this._isReadOnlyCall);
      if (this.input != null)
        this.input.disabled = ro;
      if (this.select != null)
        this.select.disabled = ro;
    }
  }
  hide() {
    if (this.input != null)
      this.input.style.visibility = "hidden";
    super.hide();
  }
  resize(top, left1, left2, width) {
    return 0;
  }
};
var UIElementString = class extends UIElementGeneric {
  constructor(p, dataContainer, prop) {
    super(p, dataContainer, prop);
    this._value = "";
    this.preExpandIfNeeded();
  }
  AllocateControls() {
    super.AllocateControls();
    if (this.input == null) {
      this.input = document.createElement("INPUT");
      this.input.type = "input";
      this.input.value = Reflect.get(this._dataContainer, this._prop.name);
      this.input.style.position = "absolute";
      this.input.style.marginTop = "0px";
      this.input.style.marginBottom = "0px";
      this.input.style.fontFamily = UIElement.defaultFontFamily;
      this.input.style.fontSize = UIElement.defaultFontSize.toString() + "px";
      this.input.style.border = "1px solid grey";
      this.input.style.borderRadius = "1px";
      this.input.style.top = "0px";
      this.input.style.right = "3px";
      this.input.style.width = "50%";
      this.mainlabel.appendChild(this.input);
      new InputFieldManager(this.input, 0, true, Number.NaN, Number.NaN, () => {
        this.Text_TextChanged(null);
      });
      this.input.addEventListener("focusin", () => {
        this.control_GotFocus(this);
      });
      this.input.addEventListener("focusout", () => {
        this.control_LostFocus(this);
      });
      this.checkForReadOnly();
    }
  }
  refresh() {
    if (this.input == null)
      return;
    if (this._getValueCallback == null)
      return;
    let value = this._getValueCallback(this);
    if (value == null)
      return;
    let s = value.toString();
    if (this.input != null) {
      if (s != this.input.value)
        this.input.value = s;
    }
    this.checkForReadOnly();
  }
  Text_TextChanged(sender) {
    if (this.input != null) {
      this._value = this.input.value;
      Reflect.set(this._dataContainer, this._prop.name, this._value);
      if (this._valueChangeCallback != null)
        this._valueChangeCallback(this);
      if (this._changeCausesParentRefresh)
        this.parentNode.refresh();
    }
  }
  get value() {
    return this._value;
  }
  set value(value) {
    this._value = value;
    this.input.value = value;
  }
  resize(top, left1, left2, width) {
    this.mainlabel.style.height = this.input.offsetHeight.toString() + "px";
    return 0;
  }
};
var UIElementNumber = class extends UIElementGeneric {
  constructor(p, dataContainer, prop) {
    super(p, dataContainer, prop);
    this._value = 0;
    this.preExpandIfNeeded();
  }
  AllocateControls() {
    super.AllocateControls();
    if (this.input == null) {
      this.input = document.createElement("INPUT");
      this.input.type = "input";
      this.input.value = Reflect.get(this._dataContainer, this._prop.name);
      this.input.style.textAlign = "right";
      this.input.style.position = "absolute";
      this.input.style.marginTop = "0px";
      this.input.style.marginBottom = "0px";
      this.input.style.fontFamily = UIElement.defaultFontFamily;
      this.input.style.fontSize = UIElement.defaultFontSize.toString() + "px";
      this.input.type = "number";
      this.input.style.border = "1px solid grey";
      this.input.style.borderRadius = "1px";
      this.input.style.top = "0px";
      this.input.style.right = "3px";
      this.input.style.width = "50%";
      this.mainlabel.appendChild(this.input);
      new InputFieldManager(this.input, 4, false, Number.NaN, Number.NaN, () => {
        this.Text_TextChanged(null);
      });
      this.input.addEventListener("focusin", () => {
        this.control_GotFocus(this);
      });
      this.input.addEventListener("focusout", () => {
        this.control_LostFocus(this);
      });
      this.checkForReadOnly();
    }
  }
  refresh() {
    if (this.input == null)
      return;
    if (this._getValueCallback == null)
      return;
    let s = this._getValueCallback(this);
    if (s == null)
      return;
    if (this.input != null) {
      if (s != parseFloat(this.input.value))
        this.input.value = s.toString();
    }
    this.checkForReadOnly();
  }
  Text_TextChanged(sender) {
    if (this.input != null) {
      this._value = parseFloat(this.input.value);
      Reflect.set(this._dataContainer, this._prop.name, this._value);
      if (this._valueChangeCallback != null)
        this._valueChangeCallback(this);
      if (this._changeCausesParentRefresh)
        this.parentNode.refresh();
    }
  }
  get value() {
    return this._value;
  }
  set value(value) {
    this._value = value;
    this.input.value = value.toString();
  }
  resize(top, left1, left2, width) {
    this.mainlabel.style.height = this.input.offsetHeight.toString() + "px";
    return 0;
  }
};
var UIElementBoolean = class extends UIElementGeneric {
  constructor(p, dataContainer, prop) {
    super(p, dataContainer, prop);
    this._text = null;
    this._value = false;
    this.preExpandIfNeeded();
  }
  AllocateControls() {
    super.AllocateControls();
    if (this.input == null) {
      let span = document.createElement("SPAN");
      span.style.position = "absolute";
      span.style.display = "inline-block";
      span.style.width = "50%";
      span.style.right = "0px";
      this.mainlabel.appendChild(span);
      this.input = document.createElement("INPUT");
      this.input.type = "input";
      this.input.checked = Reflect.get(this._dataContainer, this._prop.name);
      this.input.style.textAlign = "right";
      this.input.style.display = "inline";
      this.input.style.marginTop = "0px";
      this.input.style.marginBottom = "0px";
      this.input.type = "checkbox";
      this.input.style.transform = "scale(" + constants.generalSizeCoef.toString() + ")";
      this.input.style.marginRight = Math.round(5 * constants.generalSizeCoef).toString() + "px";
      this.input.style.top = "0px";
      this.input.style.border = "1px solid grey";
      this.input.style.borderRadius = "1px";
      span.appendChild(this.input);
      this.input.addEventListener("keyup", (e) => {
        if (e.key === " ")
          this.value_TextChanged(null);
      });
      this.input.addEventListener("change", () => {
        this.value_TextChanged(null);
      });
      this._text = document.createElement("SPAN");
      this._text.style.display = "inline";
      this._text.style.fontFamily = UIElement.defaultFontFamily;
      this._text.style.fontSize = UIElement.defaultFontSize.toString() + "px";
      span.appendChild(this._text);
      this.updateText();
      this.input.addEventListener("focusin", () => {
        this.control_GotFocus(this);
      });
      this.input.addEventListener("focusout", () => {
        this.control_LostFocus(this);
      });
      this.checkForReadOnly();
    }
  }
  updateText() {
    this._text.innerText = this.input.checked ? "Enabled" : "Disabled";
  }
  refresh() {
    if (this.input == null)
      return;
    this.checkForReadOnly();
    if (this._getValueCallback == null)
      return;
    let v = this._getValueCallback(this);
    if (v == null)
      return;
    let b = v;
    if (this.input != null) {
      this.input.checked = b.valueOf();
      this.updateText();
    }
  }
  value_TextChanged(sender) {
    if (this.input != null) {
      this._value = this.input.checked;
      this.updateText();
      Reflect.set(this._dataContainer, this._prop.name, this._value);
      if (this._valueChangeCallback != null)
        this._valueChangeCallback(this);
      if (this._changeCausesParentRefresh)
        this.parentNode.refresh();
    }
  }
  get value() {
    return this._value;
  }
  set value(value) {
    this._value = value;
    this.input.checked = value;
    this.updateText();
  }
  resize(top, left1, left2, width) {
    return 0;
  }
};
var UIElementDoubleNan = class extends UIElementGeneric {
  constructor(p, dataContainer, prop) {
    super(p, dataContainer, prop);
    this._value = 0;
    this.preExpandIfNeeded();
  }
  AllocateControls() {
    super.AllocateControls();
    if (this.input == null) {
      this.input = document.createElement("INPUT");
      this.input.type = "input";
      this.input.value = Reflect.get(this._dataContainer, this._prop.name).toString();
      this.input.style.textAlign = "right";
      this.input.style.position = "absolute";
      this.input.style.marginTop = "0px";
      this.input.style.marginBottom = "0px";
      this.input.style.fontFamily = UIElement.defaultFontFamily;
      this.input.style.fontSize = UIElement.defaultFontSize.toString() + "px";
      this.input.type = "number";
      this.input.style.border = "1px solid grey";
      this.input.style.borderRadius = "1px";
      this.input.style.top = "0px";
      this.input.style.right = "3px";
      this.input.style.width = "50%";
      this.mainlabel.appendChild(this.input);
      new InputFieldManager(this.input, 4, true, Number.NaN, Number.NaN, () => {
        this.Text_TextChanged(null);
      });
      this.input.addEventListener("focusin", () => {
        this.control_GotFocus(this);
      });
      this.input.addEventListener("focusout", () => {
        this.control_LostFocus(this);
      });
      this.checkForReadOnly();
    }
  }
  refresh() {
    if (this.input == null)
      return;
    if (this._getValueCallback == null)
      return;
    let s = this._getValueCallback(this).toString();
    if (this.input != null) {
      if (s != this.input.value)
        this.input.value = s;
    }
    this.checkForReadOnly();
  }
  Text_TextChanged(sender) {
    if (this.input != null) {
      this._value = this.input.value == "" ? Number.NaN : parseFloat(this.input.value);
      Reflect.set(this._dataContainer, this._prop.name, new doubleNan(this._value));
      if (this._valueChangeCallback != null)
        this._valueChangeCallback(this);
      if (this._changeCausesParentRefresh)
        this.parentNode.refresh();
    }
  }
  get value() {
    return new doubleNan(this._value);
  }
  set value(value) {
    this._value = value.value;
    this.input.value = value.toString();
  }
  resize(top, left1, left2, width) {
    this.mainlabel.style.height = this.input.offsetHeight.toString() + "px";
    return 0;
  }
};
var UIElementEnum = class extends UIElementGeneric {
  constructor(p, dataContainer, prop) {
    super(p, dataContainer, prop);
    this.allValues = [];
    this._value = "";
    this.preExpandIfNeeded();
  }
  AllocateControls() {
    super.AllocateControls();
    if (this.select == null) {
      this.select = document.createElement("SELECT");
      let enumvalue = Reflect.get(this._dataContainer, this._prop.name);
      this.allValues = enumvalue.sibblings;
      for (let i = 0; i < this.allValues.length; i++) {
        let opt = document.createElement("OPTION");
        opt.text = this.allValues[i].description;
        opt.value = this.allValues[i].toString;
        this.select.add(opt, null);
      }
      this.select.value = enumvalue.toString;
      this.select.style.position = "absolute";
      this.select.style.marginTop = "0px";
      this.select.style.marginBottom = "0px";
      this.select.style.fontFamily = UIElement.defaultFontFamily;
      this.select.style.fontSize = UIElement.defaultFontSize.toString() + "px";
      this.select.style.top = "0px";
      this.select.style.right = "3px";
      this.select.style.width = "50%";
      this.mainlabel.appendChild(this.select);
      this.select.addEventListener("change", () => {
        this.valueChanged(null);
      });
      this.select.addEventListener("focusin", () => {
        this.control_GotFocus(this);
      });
      this.select.addEventListener("focusout", () => {
        this.control_LostFocus(this);
      });
      this.checkForReadOnly();
    }
  }
  refresh() {
    if (this.select == null)
      return;
    if (this._getValueCallback == null)
      return;
    let value = this._getValueCallback(this);
    let s = value.toString;
    if (this.select != null) {
      if (s != this.select.value)
        this.select.value = s;
    }
    this.checkForReadOnly();
  }
  valueChanged(sender) {
    if (this.select != null) {
      this._value = this.select.value;
      let it = null;
      for (let i = 0; i < this.allValues.length; i++) {
        if (this.allValues[i].toString == this._value)
          it = this.allValues[i];
      }
      if (it != null)
        Reflect.set(this._dataContainer, this._prop.name, it);
      if (this._valueChangeCallback != null)
        this._valueChangeCallback(this);
      if (this._changeCausesParentRefresh)
        this.parentNode.refresh();
    }
  }
  tabReOrder(index) {
    if (this.select != null)
      this.select.tabIndex = index++;
    return index;
  }
  get value() {
    return this._value;
  }
  set value(value) {
    this._value = value;
    this.select.value = value;
  }
  resize(top, left1, left2, width) {
    this.mainlabel.style.height = this.select.offsetHeight.toString() + "px";
    return 0;
  }
};
var UIElementList = class extends UIElementGeneric {
  constructor(p, dataContainer, prop, allowedValues) {
    super(p, dataContainer, prop);
    this._refreshCallback = null;
    this._indexedValue = true;
    this.allValues = [];
    this.valueType = "";
    this._value = "";
    this._refreshCallback = allowedValues;
    this.preExpandIfNeeded();
  }
  AllocateControls() {
    super.AllocateControls();
    if (this.select == null) {
      this.allValues = this._refreshCallback();
      this.select = document.createElement("SELECT");
      let value = Reflect.get(this._dataContainer, this._prop.name);
      this.valueType = typeof value;
      this._indexedValue = typeof value == "number" && typeof this.allValues[0] != "number";
      let found = false;
      let first = null;
      for (let i = 0; i < this.allValues.length; i++) {
        let opt = document.createElement("OPTION");
        if (i == 0)
          first = opt;
        opt.text = this.allValues[i].toString();
        opt.value = this._indexedValue ? i : this.allValues[i];
        this.select.add(opt, null);
        if (opt.value.toString() == value.toString())
          found = true;
      }
      if (!found && !this._indexedValue) {
        let opt = document.createElement("OPTION");
        opt.text = value.toString();
        opt.value = value;
        this.select.add(opt, first);
      }
      this.select.value = value;
      this.select.style.position = "absolute";
      this.select.style.marginTop = "0px";
      this.select.style.marginBottom = "0px";
      this.select.style.fontFamily = UIElement.defaultFontFamily;
      this.select.style.fontSize = UIElement.defaultFontSize.toString() + "px";
      this.select.style.top = "0px";
      this.select.style.right = "3px";
      this.select.style.width = "50%";
      this.mainlabel.appendChild(this.select);
      this.select.addEventListener("change", () => {
        this.valueChanged(null);
      });
      this.select.addEventListener("focusin", () => {
        this.control_GotFocus(this);
      });
      this.select.addEventListener("focusout", () => {
        this.control_LostFocus(this);
      });
      this.checkForReadOnly();
    }
  }
  refresh() {
    if (this.select == null)
      return;
    if (this._getValueCallback == null)
      return;
    this.allValues = this._refreshCallback();
    for (let i = 0; i < this.allValues.length; i++) {
      let opt = i < this.select.options.length ? this.select.options[i] : document.createElement("OPTION");
      opt.text = this.allValues[i].toString();
      opt.value = this._indexedValue ? i : this.allValues[i];
      if (i >= this.select.options.length)
        this.select.add(opt, null);
    }
    while (this.allValues.length < this.select.options.length) {
      this.select.options[this.select.options.length - 1].remove();
    }
    let v = Reflect.get(this._dataContainer, this._prop.name);
    if (v == null)
      return;
    if (this._indexedValue) {
      let it = Number(v).toString();
      if (this.select.value != it)
        this.select.value = it;
    } else {
      let it = v.toString();
      if (this.select.value != it)
        this.select.value = it;
    }
    this.checkForReadOnly();
  }
  valueChanged(sender) {
    if (this.select != null) {
      this._value = this.select.value;
      if (this.valueType == "number") {
        let it = parseInt(this._value);
        if (it >= 0)
          Reflect.set(this._dataContainer, this._prop.name, it);
      } else {
        let it = "";
        for (let i = 0; i < this.allValues.length; i++) {
          if (this.allValues[i] == this._value)
            it = this.allValues[i];
        }
        if (it != "")
          Reflect.set(this._dataContainer, this._prop.name, it);
      }
      if (this._valueChangeCallback != null)
        this._valueChangeCallback(this);
      if (this._changeCausesParentRefresh) {
        this.parentNode.refresh();
      }
    }
  }
  tabReOrder(index) {
    if (this.select != null)
      this.select.tabIndex = index++;
    return index;
  }
  get value() {
    return this._value;
  }
  set value(value) {
    this._value = value;
    this.select.value = value;
  }
  resize(top, left1, left2, width) {
    this.mainlabel.style.height = this.select.offsetHeight.toString() + "px";
    return 0;
  }
};
var UIElementColor = class extends UIElementGeneric {
  constructor(p, dataContainer, prop) {
    super(p, dataContainer, prop);
    this.colorSample = null;
    this.colorEditor = null;
    this.colorPreview = null;
    this.currentColor = null;
    this._value = "";
    this.preExpandIfNeeded();
  }
  get expandIcon() {
    return ressources.DotDotDotIcon(UIElement.defaultFontSize.toString(), false, false, false, false, "Click to open color chooser");
  }
  get colapseIcon() {
    return ressources.DotDotDotIcon(UIElement.defaultFontSize.toString(), false, false, false, false, "Click to close color chooser");
  }
  AllocateControls() {
    this._expandable = true;
    this._expandButtonSide = 1;
    super.AllocateControls();
    if (this.input == null) {
      let table = document.createElement("TABLE");
      table.style.position = "absolute";
      table.style.top = "0px";
      table.style.right = "3px";
      table.style.width = "50%";
      table.style.borderCollapse = "collapse";
      let tr = document.createElement("TR");
      table.appendChild(tr);
      let td1 = document.createElement("TD");
      tr.appendChild(td1);
      this.colorSample = document.createElement("Div");
      this.colorSample.style.display = "inline-block";
      this.colorSample.style.border = "1px solid black";
      this.colorSample.style.marginRight = "1px";
      this.colorSample.style.marginTop = "0px";
      this.colorSample.style.width = "16px";
      this.colorSample.style.height = "16px";
      this.colorSample.style.top = "0px";
      this.colorSample.style.bottom = "0px";
      this.colorPreview = new ColorSampler(this.colorSample, null);
      td1.appendChild(this.colorSample);
      let td2 = document.createElement("TD");
      td2.style.width = "100%";
      tr.appendChild(td2);
      this.input = document.createElement("INPUT");
      this.input.type = "input";
      this.currentColor = Reflect.get(this._dataContainer, this._prop.name);
      this.colorPreview.color = this.currentColor;
      this.input.value = this.currentColor.toString();
      this.input.style.marginTop = "0px";
      this.input.style.marginBottom = "0px";
      this.input.style.width = "100%";
      this.input.style.border = "1px solid grey";
      this.input.style.borderRadius = "1px";
      this.input.style.fontFamily = UIElement.defaultFontFamily;
      this.input.style.fontSize = UIElement.defaultFontSize.toString() + "px";
      td2.appendChild(this.input);
      let td3 = document.createElement("TD");
      td3.style.paddingLeft = "5px";
      tr.appendChild(td3);
      td3.appendChild(this.expandCtrl);
      this.mainlabel.appendChild(table);
      new InputFieldManager(this.input, 7, false, Number.NaN, Number.NaN, (sender, refreshInput) => {
        this.Text_TextChanged(sender, refreshInput);
      });
      this.input.addEventListener("focusin", () => {
        this.control_GotFocus(this);
      });
      this.input.addEventListener("focusout", () => {
        this.control_LostFocus(this);
      });
      this.checkForReadOnly();
    }
  }
  expand() {
    if (this.colorEditor == null) {
      this.subElementPanel.style.marginLeft = "20px";
      this.colorEditor = new colorEditor(this.subElementPanel, UIElement.defaultFontSize, this.currentColor, (c) => {
        this.coloredited(c);
      });
    }
    this._rootNode.tabReOrder(0);
    this._expanded = true;
    this.subElementPanel.style.display = "";
    this.expandCtrl.innerHTML = this.colapseIcon;
  }
  colapse() {
    this.subElementPanel.style.display = "none";
    this.expandCtrl.innerHTML = this.expandIcon;
  }
  coloredited(newColor) {
    this.currentColor = newColor.clone();
    this.colorPreview.color = this.currentColor;
    this.input.value = this.currentColor.toString();
    Reflect.set(this._dataContainer, this._prop.name, this.currentColor);
    if (this._valueChangeCallback != null)
      this._valueChangeCallback(this);
    if (this._changeCausesParentRefresh)
      this.parentNode.refresh();
  }
  refresh() {
    if (this.input == null)
      return;
    if (this._getValueCallback == null)
      return;
    let c = this._getValueCallback(this);
    if (c != null) {
      if (this.colorPreview != null)
        this.colorPreview.color = c;
      if (this.input != null) {
        let s = c.toString();
        if (s != this.input.value)
          this.input.value = s;
      }
    }
    this.checkForReadOnly();
  }
  Text_TextChanged(sender, refreshInput) {
    if (this.input != null) {
      this._value = this.input.value;
      let c = YColor.FromString(this._value);
      if (c != null) {
        this.colorPreview.color = c;
        this.currentColor = c;
        if (this.colorEditor != null)
          this.colorEditor.color = c;
        Reflect.set(this._dataContainer, this._prop.name, c);
        if (refreshInput)
          this.input.value = c.toString();
        if (this._valueChangeCallback != null)
          this._valueChangeCallback(this);
        if (this._changeCausesParentRefresh)
          this.parentNode.refresh();
      }
    }
  }
  get value() {
    return this._value;
  }
  set value(value) {
    this._value = value;
    this.input.value = value;
  }
  resize(top, left1, left2, width) {
    let h = this.input.offsetHeight;
    this.mainlabel.style.height = h.toString() + "px";
    return 0;
  }
};
var UIElementMarkerPos = class _UIElementMarkerPos extends UIElementGeneric {
  constructor(p, dataContainer, prop) {
    super(p, dataContainer, prop);
    this._cachedValue = null;
    this._value = "";
    this.preExpandIfNeeded();
  }
  static expandIconSVG(size) {
    return ressources.targetIcon(size.toString(), false, false, false, false, "Click to place marker on graph");
  }
  get expandIcon() {
    return _UIElementMarkerPos.expandIconSVG(UIElement.defaultFontSize);
  }
  get colapseIcon() {
    return _UIElementMarkerPos.expandIconSVG(UIElement.defaultFontSize);
  }
  AllocateControls() {
    this._expandable = true;
    this._expandButtonSide = 1;
    super.AllocateControls();
    if (this.input == null) {
      let table = document.createElement("TABLE");
      table.style.position = "absolute";
      table.style.top = "0px";
      table.style.right = "3px";
      table.style.width = "50%";
      table.style.borderCollapse = "collapse";
      let tr = document.createElement("TR");
      table.appendChild(tr);
      let td2 = document.createElement("TD");
      td2.style.width = "100%";
      tr.appendChild(td2);
      this.input = document.createElement("INPUT");
      this.input.type = "input";
      this._cachedValue = Reflect.get(this._dataContainer, this._prop.name);
      this.input.value = this._cachedValue.toString();
      this.input.style.marginTop = "0px";
      this.input.style.marginBottom = "0px";
      this.input.style.textAlign = "right";
      this.input.style.width = "100%";
      this.input.style.border = "1px solid grey";
      this.input.style.borderRadius = "1px";
      this.input.style.fontFamily = UIElement.defaultFontFamily;
      this.input.style.fontSize = UIElement.defaultFontSize.toString() + "px";
      this.input.addEventListener("keyup", () => {
        this.Text_TextChanged(null, true);
      });
      this.input.addEventListener("change", () => {
        this.Text_TextChanged(null, false);
      });
      td2.appendChild(this.input);
      let td3 = document.createElement("TD");
      td3.style.paddingLeft = "5px";
      tr.appendChild(td3);
      td3.appendChild(this.expandCtrl);
      this.mainlabel.appendChild(table);
      this.input.addEventListener("focusin", () => {
        this.control_GotFocus(this);
      });
      this.input.addEventListener("focusout", () => {
        this.control_LostFocus(this);
      });
      this.checkForReadOnly();
    }
  }
  expand() {
    let value = Reflect.get(this._dataContainer, this._prop.name);
    this.expanded = false;
    value.capture = true;
    Reflect.set(this._dataContainer, this._prop.name, value);
    if (this._valueChangeCallback != null)
      this._valueChangeCallback(this);
  }
  colapse() {
  }
  refresh() {
    if (this.input == null)
      return;
    if (this._getValueCallback == null)
      return;
    this._cachedValue = this._getValueCallback(this);
    if (this.input != null) {
      this.input.value = this._cachedValue.toString();
    }
    this.checkForReadOnly();
  }
  Text_TextChanged(sender, refreshInput) {
    let parsedValue = this._cachedValue.TryParse(this.input.value);
    if (parsedValue.success) {
      this._cachedValue.value = parsedValue.result;
      Reflect.set(this._dataContainer, this._prop.name, this._cachedValue);
      if (this._valueChangeCallback != null)
        this._valueChangeCallback(this);
      if (this._changeCausesParentRefresh)
        this.parentNode.refresh();
    }
  }
  get value() {
    return this._value;
  }
  set value(value) {
    this._value = value;
    this.input.value = value;
  }
  resize(top, left1, left2, width) {
    let h = this.input.offsetHeight;
    this.mainlabel.style.height = h.toString() + "px";
    return 0;
  }
};
var InputFieldManager = class {
  static nullCallback(sender) {
  }
  constructor(input, type, allowEmpty, min, max, callback) {
    this._allowEmpty = false;
    this._min = Number.NaN;
    this._max = Number.NaN;
    this._callback = null;
    this._input = input;
    this._type = type;
    this._allowEmpty = allowEmpty;
    this._min = min;
    this._max = max;
    this._callback = callback;
    this.checkData();
    this._input.addEventListener("keyup", () => {
      this.InputChange(this._input, true);
    });
    this._input.addEventListener("change", () => {
      this.InputChange(this._input, false);
    });
  }
  showError(msg) {
    this._input.title = msg;
  }
  clearError() {
    this._input.title = "";
  }
  backGroundColorFeedback(ErrorMsg, state) {
    if (state)
      this.clearError();
    else
      this.showError(ErrorMsg);
    let c = this._input.style.backgroundColor;
    if (state && c != "white")
      this._input.style.backgroundColor = "white";
    if (!state && c != "pink")
      this._input.style.backgroundColor = "pink";
    return state;
  }
  checkData() {
    let intValue = 0;
    let doubleValue = 0;
    if (this._input.disabled)
      return true;
    let strValue = this._input.value;
    if (strValue == "")
      return this.backGroundColorFeedback("This field can't be empty", this._allowEmpty);
    switch (this._type) {
      case 0:
        break;
      case 1:
        intValue = parseInt(strValue);
        doubleValue = parseFloat(strValue);
        if (Number.isNaN(intValue) || intValue != doubleValue)
          return this.backGroundColorFeedback("Integer value expected", false);
        if (!Number.isNaN(this._min) && intValue < this._min)
          return this.backGroundColorFeedback("Integer value greater or equal to " + this._min.toString() + " expected", false);
        if (!Number.isNaN(this._max) && intValue > this._max)
          return this.backGroundColorFeedback("Integer value lower or equal to  " + this._min.toString() + " expected", false);
        break;
      case 2:
        intValue = parseInt(strValue);
        doubleValue = parseFloat(strValue);
        if (Number.isNaN(intValue) || intValue != doubleValue)
          return this.backGroundColorFeedback("Positive integer  expected", false);
        if (intValue < 0)
          return this.backGroundColorFeedback("Positive integer value  expected", false);
        if (!Number.isNaN(this._min) && intValue < this._min)
          return this.backGroundColorFeedback("Positive integer value greater or equal to " + this._min.toString() + " expected", false);
        if (!Number.isNaN(this._max) && intValue > this._max)
          return this.backGroundColorFeedback("Positive integer value lower or equal to  " + this._min.toString() + " expected", false);
        break;
      case 3:
        intValue = parseInt(strValue);
        doubleValue = parseFloat(strValue);
        if (Number.isNaN(intValue) || intValue != doubleValue)
          return this.backGroundColorFeedback("Strictly positive Integer value expected", false);
        if (intValue <= 0)
          return this.backGroundColorFeedback("Strictly positive integer value  expected", false);
        if (!Number.isNaN(this._min) && intValue < this._min)
          return this.backGroundColorFeedback("Positive integer value greater or equal to " + this._min.toString() + " expected", false);
        if (!Number.isNaN(this._max) && intValue > this._max)
          return this.backGroundColorFeedback("Positive integer value lower or equal to  " + this._min.toString() + " expected", false);
        break;
      case 4:
        doubleValue = parseFloat(strValue);
        if (Number.isNaN(intValue))
          return this.backGroundColorFeedback("Floating point value expected", false);
        if (!Number.isNaN(this._min) && doubleValue < this._min)
          return this.backGroundColorFeedback("Floating point value greater or equal to " + this._min.toString() + " expected", false);
        if (!Number.isNaN(this._max) && doubleValue > this._max)
          return this.backGroundColorFeedback("Floating point value lower or equal to  " + this._min.toString() + " expected", false);
        break;
      case 5:
        doubleValue = parseFloat(strValue);
        if (Number.isNaN(doubleValue))
          return this.backGroundColorFeedback("Positive floating point value expected", false);
        if (doubleValue < 0)
          return this.backGroundColorFeedback("Positive floating point value expected", false);
        if (!Number.isNaN(this._min) && doubleValue < this._min)
          return this.backGroundColorFeedback("Floating point value greater or equal to " + this._min.toString() + " expected", false);
        if (!Number.isNaN(this._max) && doubleValue > this._max)
          return this.backGroundColorFeedback("Floating point value lower or equal to  " + this._min.toString() + " expected", false);
        break;
      case 6:
        doubleValue = parseFloat(strValue);
        if (Number.isNaN(doubleValue))
          return this.backGroundColorFeedback("Strictly positive floating point value expected", false);
        if (doubleValue <= 0)
          return this.backGroundColorFeedback("Strictly positive floating point value expected", false);
        if (!Number.isNaN(this._min) && doubleValue < this._min)
          return this.backGroundColorFeedback("Floating point value greater or equal to " + this._min.toString() + " expected", false);
        if (!Number.isNaN(this._max) && doubleValue > this._max)
          return this.backGroundColorFeedback("Floating point value greater or equal to " + this._min.toString() + " expected", false);
        break;
      case 7:
        if (YColor.FromString(strValue) == null)
          return this.backGroundColorFeedback("Value color name/definition expected", false);
        break;
    }
    return this.backGroundColorFeedback("", true);
  }
  InputChange(sender, refreshInput) {
    if (this.checkData()) {
      try {
        if (this._callback != null)
          this._callback(sender, refreshInput);
      } catch (ex) {
        this.backGroundColorFeedback(ex.message, false);
      }
    }
  }
};

// obj/full/colorEditor_rw.js
var ColorEditorTab = class {
  constructor(editor, label, fontSize) {
    this._realTab = false;
    this._div = null;
    this._tabLabel = null;
    this._td = document.createElement("TD");
    this._editor = editor;
    if (label != "") {
      this._div = document.createElement("DIV");
      this._div.style.display = "none";
      this._td.style.paddingTop = "4px";
      this._td.style.fontSize = fontSize.toString() + "px";
      this._td.style.paddingBottom = "2px";
      this._td.style.borderLeft = "1px solid " + this._editor.inactiveBorderColor;
      this._td.style.borderRight = "1px solid " + this._editor.inactiveBorderColor;
      this._td.style.borderTop = "1px solid " + this._editor.inactiveBorderColor;
      this._td.style.borderBottom = "1px solid " + this._editor.activeBorderColor;
      this._td.style.color = this._editor.inactiveColor;
      this._td.style.backgroundColor = this._editor.inactiveBackgroundColor;
      this._td.style.borderTopLeftRadius = "3px";
      this._td.style.borderTopRightRadius = "3px";
      this._td.style.paddingLeft = "5px";
      this._td.style.paddingRight = "5px";
      this._realTab = true;
      this._tabLabel = document.createElement("ANCHOR");
      this._tabLabel.innerText = label;
      this._tabLabel.addEventListener("click", () => {
        this._editor.tabSelected(this);
      });
      this._tabLabel.addEventListener("keyup", (e) => {
        if (e.key === "Enter" || e.key === " ")
          this._editor.tabSelected(this);
      });
      this._tabLabel.style.cursor = "pointer";
      this._td.appendChild(this._tabLabel);
    } else {
      this._td.style.borderBottom = "1px solid " + this._editor.activeBorderColor;
      this._td.innerHTML = "<span>&nbsp;</span>";
    }
  }
  setTabIndex(index) {
    this._tabLabel.tabIndex = index++;
    return index;
  }
  set active(value) {
    if (!this._realTab)
      return;
    this._td.style.borderLeftColor = value ? this._editor.activeBorderColor : this._editor.inactiveBorderColor;
    this._td.style.borderTopColor = value ? this._editor.activeBorderColor : this._editor.inactiveBorderColor;
    this._td.style.borderRightColor = value ? this._editor.activeBorderColor : this._editor.inactiveBorderColor;
    this._td.style.borderBottomColor = value ? "rgba(0, 0, 0, 0)" : this._editor.activeBorderColor;
    this._td.style.color = value ? this._editor.activeColor : this._editor.inactiveColor;
    this._td.style.backgroundColor = value ? this._editor.activeBackgroundColor : this._editor.inactiveBackgroundColor;
    this._div.style.display = value ? "" : "none";
  }
  get active() {
    return this._div.style.display == "";
  }
  get tabElement() {
    return this._td;
  }
  get divElement() {
    return this._div;
  }
};
var ColorEditorSroll = class _ColorEditorSroll {
  setTabIndex(index) {
    this._anchor.tabIndex = index++;
    this._input.tabIndex = index++;
    return index;
  }
  constructor(div, anchor, input, colorCallback, changeCallBack, checkerBoardBg, value) {
    this._value = 0;
    this._colorCallback = colorCallback;
    this._changeCallBack = changeCallBack;
    this._value = value;
    this._div = div;
    let s = _ColorEditorSroll.scrollHeight;
    if (checkerBoardBg)
      this._div.style.background = "url('data:image/svg+xml;utf8," + ColorSampler.checkerboardImage(s, s) + "')";
    this._anchor = anchor;
    this._input = input;
    this._input.size = 3;
    this._input.maxLength = 3;
    this._input.type = "number";
    this._input.min = "0";
    this._input.max = "255";
    this._input.style.fontSize = (12 * constants.guiDPIFactor).toString() + "px";
    this._input.style.textAlign = "right";
    this._input.value = this._value.toString();
    this._input.style.marginLeft = "10px";
    this._div.style.width = "100%";
    this._div.style.position = "relative";
    this._div.style.height = _ColorEditorSroll.scrollHeight.toString() + "px";
    this._div.style.border = "1px solid black";
    this._canvasBg = document.createElement("CANVAS");
    this._div.appendChild(this._canvasBg);
    this._canvasBg.style.left = "0px";
    this._canvasBg.style.top = "0px";
    this._canvasBg.style.width = "100%";
    this._canvasBg.style.height = "100%";
    this._canvasBg.width = 256;
    this._canvasBg.height = _ColorEditorSroll.scrollHeight;
    this._canvasCursor = document.createElement("CANVAS");
    this._div.appendChild(this._canvasCursor);
    this._canvasCursor.style.position = "absolute";
    this._canvasCursor.style.width = "100%";
    this._canvasCursor.style.height = "100%";
    this._canvasCursor.style.left = "0px";
    this._canvasCursor.style.top = "0px";
    this._canvasCursor.style.display = "block";
    this._div.addEventListener("mousedown", (e) => {
      this.MouseDown(this._div, e);
    });
    this._div.addEventListener("mousemove", (e) => {
      this.MouseMove(this._div, e);
    });
    this._anchor.addEventListener("wheel", (e) => {
      this.mouseWheelEvent(this._div, e);
    });
    this._anchor.addEventListener("keydown", (e) => {
      this.KeyDown(this._div, e);
    });
    this._input.addEventListener("keyup", (e) => {
      this.inputKeyUp();
    });
    this._input.addEventListener("change", (e) => {
      this.inputKeyUp();
    });
    this._anchor.tabIndex = 4;
  }
  get value() {
    return this._value;
  }
  set value(v) {
    if (v < 0)
      v = 0;
    if (v > 255)
      v = 255;
    this._value = v;
    this._input.value = this._value.toString();
  }
  inputKeyUp() {
    let value = parseInt(this._input.value);
    if (isNaN(value))
      return;
    if (value < 0)
      value = 0;
    if (value > 255)
      value = 255;
    this._value = value;
    this.refresh();
    this._changeCallBack(this);
  }
  TouchStart(div, e) {
    if (e.touches.length == 1)
      this.handleMouseStartCapture(div, e.touches[0].clientX, e.touches[0].clientY);
  }
  TouchCaptureRun(div, e) {
    if (e.touches.length == 1)
      this.handleMouseRun(div, e.touches[0].clientX, e.touches[0].clientY);
  }
  TouchCaptureEnd(div, e) {
  }
  MouseDown(sender, e) {
    if (e.buttons != 1)
      return;
    this.handleMouseStartCapture(sender, e.clientX, e.clientY);
  }
  handleMouseStartCapture(sender, x, y) {
    let SenderRect = sender.getBoundingClientRect();
    let eX = Math.round(x - SenderRect.left);
    let index = Math.round(256 * eX / this._div.offsetWidth) >> 0;
    if (index < 0)
      index = 0;
    if (index > 255)
      index = 255;
    this._value = index;
    this._input.value = index.toString();
    this.refresh();
    this._changeCallBack(this);
  }
  MouseMove(sender, e) {
    if (e.buttons != 1)
      return;
    this.handleMouseRun(sender, e.clientX, e.clientY);
  }
  handleMouseRun(sender, x, y) {
    let SenderRect = sender.getBoundingClientRect();
    let eX = Math.round(x - SenderRect.left);
    let index = Math.round(256 * eX / this._div.offsetWidth) >> 0;
    if (index < 0)
      index = 0;
    if (index > 255)
      index = 255;
    this._value = index;
    this._input.value = index.toString();
    this.refresh();
    this._changeCallBack(this);
  }
  mouseWheelEvent(sender, e) {
    this._value += e.deltaY > 0 ? -1 : 1;
    if (this._value < 0) {
      this._value = 0;
    } else if (this._value > 255) {
      this._value = 255;
    } else {
      e.preventDefault();
    }
    this._input.value = this._value.toString();
    this.refresh();
    this._changeCallBack(this);
  }
  KeyDown(sender, e) {
    if (e.code == "ArrowRight") {
      this._value++;
      if (this._value > 255)
        this._value = 255;
    }
    if (e.code == "ArrowLeft") {
      this._value--;
      if (this._value < 0)
        this._value = 0;
    }
    this._input.value = this._value.toString();
    this.refresh();
    this._changeCallBack(this);
  }
  refresh() {
    let color = this._colorCallback(this);
    let ctx = this._canvasBg.getContext("2d");
    ctx.clearRect(0, 0, this._canvasBg.width, this._canvasBg.height);
    for (let i = 0; i < 256; i++) {
      ctx.beginPath();
      ctx.moveTo(i + 0.5, 0);
      ctx.lineTo(i + 0.5, _ColorEditorSroll.scrollHeight);
      ctx.strokeStyle = color[i];
      ctx.stroke();
    }
    this._canvasCursor.width = this._div.offsetWidth;
    this._canvasCursor.height = this._div.offsetHeight;
    ctx = this._canvasCursor.getContext("2d");
    let x = 0.5 + this._value / 255 * this._canvasCursor.width;
    ctx.beginPath();
    let y = 0.5;
    ctx.moveTo(x, y + 5);
    ctx.lineTo(x + 3, y);
    ctx.lineTo(x - 3, y);
    ctx.fillStyle = "black";
    ctx.strokeStyle = "white";
    ctx.closePath();
    ctx.fill();
    ctx.stroke();
    y = this._canvasCursor.height - 0.5;
    ctx.beginPath();
    ctx.moveTo(x, y - 5);
    ctx.lineTo(x + 3, y);
    ctx.lineTo(x - 3, y);
    ctx.fillStyle = "black";
    ctx.strokeStyle = "white";
    ctx.closePath();
    ctx.fill();
    ctx.stroke();
  }
};
ColorEditorSroll.scrollHeight = 26 * constants.guiDPIFactor;
var ColorSampler = class _ColorSampler {
  setTabIndex(index) {
    this._colorDiv.tabIndex = index++;
    return index;
  }
  static checkerboardImage(width, height) {
    let halfWidth = width >> 1;
    let halfHeight = height >> 1;
    return '<svg xmlns="http://www.w3.org/2000/svg"   viewbox="0 0 ' + width + " " + height + '" width="' + width + 'px"  height="' + height + 'px" ><rect x="0" y="0" width="' + width + '" height="' + height + '" style="fill:white"/><rect x="0" y="0" width="' + halfWidth + '" height="' + halfHeight + '" style="fill:black"/><rect x="' + halfWidth + '" y="' + halfHeight + '" width="' + halfWidth + '" height="' + halfHeight + '" style="fill:black"/></svg>';
  }
  constructor(containerDiv, selectCallback) {
    this._containerDiv = containerDiv;
    this._selectCallback = selectCallback;
    this._containerDiv.style.border = "1px solid grey";
    this._containerDiv.style.background = "url('data:image/svg+xml;utf8," + _ColorSampler.checkerboardImage(100, 100) + "')";
    this._containerDiv.style.backgroundSize = "cover";
    this._containerDiv.style.backgroundPosition = "center";
    this._containerDiv.style.backgroundRepeat = "no-repeat";
    this._color = YColor.Transparent;
    this._colorDiv = document.createElement("DIV");
    this._colorDiv.style.display = "display-block";
    this._colorDiv.style.position = "relative";
    this._colorDiv.style.width = "100%";
    this._colorDiv.style.height = "100%";
    this._colorDiv.style.backgroundColor = "rgba(0,0,0,0)";
    this._containerDiv.appendChild(this._colorDiv);
    if (this._selectCallback != null) {
      this._colorDiv.addEventListener("click", () => {
        this._selectCallback(this.color);
      });
      this._colorDiv.addEventListener("keyup", (e) => {
        if (e.key === "Enter" || e.key === " ")
          this._selectCallback(this.color);
      });
      this._colorDiv.style.cursor = "pointer";
    }
  }
  set color(value) {
    this._color = value.clone();
    this._colorDiv.style.backgroundColor = this._color.htmlCode;
  }
  get color() {
    return this._color.clone();
  }
};
var colorEditor = class _colorEditor {
  static AddColorToHistory(c) {
    for (let i = 0; i < _colorEditor._colorHistory.length; i++) {
      if (c.equal(_colorEditor._colorHistory[i]))
        return;
    }
    _colorEditor._colorHistory.splice(_colorEditor._colorHistory.length - 1, 1);
    _colorEditor._colorHistory.splice(0, 0, c);
    for (let i = _colorEditor._editorsList.length - 1; i >= 0; i--) {
      _colorEditor._editorsList[i].refreshColorHistory();
    }
  }
  static get colorHistory() {
    return _colorEditor._colorHistory;
  }
  get activeBorderColor() {
    return "#767676";
  }
  get inactiveBorderColor() {
    return "#b0b0b0";
  }
  get activeColor() {
    return "black";
  }
  get inactiveColor() {
    return "grey";
  }
  get activeBackgroundColor() {
    return "white";
  }
  get inactiveBackgroundColor() {
    return "#f8f8f8";
  }
  tabSelected(source) {
    if (source == this._tabRGB) {
      this._tabRGB.active = true;
      this._rgb_R_editor.refresh();
      this._rgb_G_editor.refresh();
      this._rgb_B_editor.refresh();
      this._rgb_T_editor.refresh();
    } else {
      this._tabRGB.active = false;
    }
    if (source == this._tabHSL) {
      this._tabHSL.active = true;
      this._hsl_H_editor.refresh();
      this._hsl_S_editor.refresh();
      this._hsl_L_editor.refresh();
      this._hsl_T_editor.refresh();
    } else {
      this._tabHSL.active = false;
    }
    this._tabPredef.active = source == this._tabPredef;
  }
  HueScrollBg() {
    let s = 100;
    let l = 50;
    let t = this._hsl_T_editor.value / 255;
    let res = new Array(256);
    for (let i = 0; i < 256; i++) {
      res[i] = "hsl(" + (360 * i / 256).toString() + "," + s.toString() + "%," + l.toString() + "%)";
    }
    return res;
  }
  SaturationScrollBg() {
    let h = 360 * this._hsl_H_editor.value / 256;
    let l = 100 * this._hsl_L_editor.value / 255;
    let t = this._hsl_T_editor.value / 255;
    let res = new Array(256);
    for (let i = 0; i < 256; i++) {
      res[i] = "hsl(" + h.toString() + "," + (100 * i / 255).toString() + "%," + l.toString() + "%)";
    }
    return res;
  }
  LuminosityScrollBg() {
    let h = 360 * this._hsl_H_editor.value / 256;
    let s = 100 * this._hsl_S_editor.value / 255;
    let t = this._hsl_T_editor.value / 255;
    let res = new Array(256);
    for (let i = 0; i < 256; i++) {
      res[i] = "hsl(" + h.toString() + "," + s.toString() + "%," + (100 * i / 255).toString() + "%)";
    }
    return res;
  }
  HSLTranspScrollBg() {
    let h = 360 * this._hsl_H_editor.value / 256;
    let s = 100 * this._hsl_S_editor.value / 255;
    let l = 100 * this._hsl_L_editor.value / 255;
    let res = new Array(256);
    for (let i = 0; i < 256; i++) {
      res[i] = "hsla(" + h.toString() + "," + l.toString() + "%," + s.toString() + "%," + (i / 255).toString() + ")";
    }
    return res;
  }
  RedScrollBg() {
    let g = this._rgb_G_editor.value;
    let b = this._rgb_B_editor.value;
    let t = this._rgb_T_editor.value / 255;
    let res = new Array(256);
    for (let i = 0; i < 256; i++) {
      res[i] = "rgb(" + i.toString() + "," + g.toString() + "," + b.toString() + ")";
    }
    return res;
  }
  GreenScrollBg() {
    let r = this._rgb_R_editor.value;
    let b = this._rgb_B_editor.value;
    let t = this._rgb_T_editor.value / 255;
    let res = new Array(256);
    for (let i = 0; i < 256; i++) {
      res[i] = "rgb(" + r.toString() + "," + i.toString() + "," + b.toString() + ")";
    }
    return res;
  }
  BlueScrollBg() {
    let r = this._rgb_R_editor.value;
    let g = this._rgb_G_editor.value;
    let t = this._rgb_T_editor.value / 255;
    let res = new Array(256);
    for (let i = 0; i < 256; i++) {
      res[i] = "rgb(" + r.toString() + "," + g.toString() + "," + i.toString() + ")";
    }
    return res;
  }
  RGBTranspScrollBg() {
    let r = this._rgb_R_editor.value;
    let g = this._rgb_G_editor.value;
    let b = this._rgb_B_editor.value;
    let res = new Array(256);
    for (let i = 0; i < 256; i++) {
      res[i] = "rgba(" + r.toString() + "," + g.toString() + "," + b.toString() + "," + (i / 255).toString() + ")";
    }
    return res;
  }
  static defaultColorHistory() {
    let defaultHistory = [];
    defaultHistory.push(YColor.FromAhsl(100, 0, 0, 0));
    defaultHistory.push(YColor.FromAhsl(100, 0, 0, 51));
    defaultHistory.push(YColor.FromAhsl(100, 0, 0, 102));
    defaultHistory.push(YColor.FromAhsl(100, 0, 0, 153));
    defaultHistory.push(YColor.FromAhsl(100, 0, 0, 204));
    defaultHistory.push(YColor.FromAhsl(100, 0, 0, 255));
    defaultHistory.push(YColor.FromAhsl(100, 0, 255, 127));
    defaultHistory.push(YColor.FromAhsl(100, 42, 255, 127));
    defaultHistory.push(YColor.FromAhsl(100, 85, 255, 127));
    defaultHistory.push(YColor.FromAhsl(100, 96, 255, 127));
    defaultHistory.push(YColor.FromAhsl(100, 127, 255, 127));
    defaultHistory.push(YColor.FromAhsl(100, 170, 255, 127));
    defaultHistory.push(YColor.FromAhsl(100, 192, 255, 127));
    defaultHistory.push(YColor.FromAhsl(100, 212, 255, 127));
    defaultHistory.push(YColor.FromAhsl(255, 226, 217, 214));
    return defaultHistory;
  }
  destroy() {
    for (let i = _colorEditor._editorsList.length - 1; i >= 0; i--) {
      if (_colorEditor._editorsList[i] == this) {
        _colorEditor._editorsList.splice(i, 1);
      }
    }
  }
  refreshColorHistory() {
    for (let i = 0; i < this._samples.length; i++) {
      if (i < _colorEditor._colorHistory.length) {
        this._samples[i].color = _colorEditor._colorHistory[i].clone();
      }
    }
  }
  constructor(container, fontSize, initialColor, colorChangeCallback) {
    this._firstTabIndex = 0;
    this._memorizeTimer = null;
    _colorEditor._editorsList.push(this);
    this._colorChangeCallback = colorChangeCallback;
    this._container = container;
    this._table = document.createElement("TABLE");
    this._table.style.width = "100%";
    this._table.style.borderSpacing = "0";
    this._table.style.paddingLeft = "5px";
    this._table.style.paddingTop = "5px";
    this._table.style.paddingRight = "5px";
    this._table.style.borderCollapse = "separate";
    this._tr1 = document.createElement("TR");
    this._tr2 = document.createElement("TR");
    this._tabRGB = new ColorEditorTab(this, "RGB", fontSize);
    this._tabHSL = new ColorEditorTab(this, "HSL", fontSize);
    this._tabPredef = new ColorEditorTab(this, "Predefined", fontSize);
    this._tabStub = new ColorEditorTab(this, "", fontSize);
    this._contents = document.createElement("TD");
    this._contents.colSpan = 4;
    this._contents.style.borderLeft = "1px solid " + this.activeBorderColor;
    this._contents.style.borderRight = "1px solid " + this.activeBorderColor;
    this._contents.style.borderBottom = "1px solid " + this.activeBorderColor;
    this._contents.style.backgroundColor = this.activeBackgroundColor;
    this._tr1.appendChild(this._tabRGB.tabElement);
    this._tr1.appendChild(this._tabHSL.tabElement);
    this._tr1.appendChild(this._tabPredef.tabElement);
    this._tr1.appendChild(this._tabStub.tabElement);
    this._tr2.appendChild(this._contents);
    this._table.appendChild(this._tr1);
    this._table.appendChild(this._tr2);
    this._contents.appendChild(this._tabRGB.divElement);
    this._contents.appendChild(this._tabHSL.divElement);
    this._contents.appendChild(this._tabPredef.divElement);
    let RGBtable = document.createElement("TABLE");
    RGBtable.style.width = "100%";
    RGBtable.style.borderSpacing = "1";
    this._tabRGB.divElement.appendChild(RGBtable);
    let HSLtable = document.createElement("TABLE");
    HSLtable.style.width = "100%";
    HSLtable.style.borderSpacing = "1";
    this._tabHSL.divElement.appendChild(HSLtable);
    let predefTable = document.createElement("TABLE");
    this._tabPredef.divElement.appendChild(predefTable);
    predefTable.style.width = "100%";
    predefTable.style.borderSpacing = "1";
    predefTable.style.tableLayout = "fixed";
    this._container.appendChild(this._table);
    this._rgb_R_editor = this.AddColorScroll(RGBtable, "R", () => {
      return this.RedScrollBg();
    }, (src) => {
      this.RgbColorChangeCallBack(src);
    }, false, 1);
    this._rgb_G_editor = this.AddColorScroll(RGBtable, "G", () => {
      return this.GreenScrollBg();
    }, (src) => {
      this.RgbColorChangeCallBack(src);
    }, false, 2);
    this._rgb_B_editor = this.AddColorScroll(RGBtable, "B", () => {
      return this.BlueScrollBg();
    }, (src) => {
      this.RgbColorChangeCallBack(src);
    }, false, 3);
    this._rgb_T_editor = this.AddColorScroll(RGBtable, "T", () => {
      return this.RGBTranspScrollBg();
    }, (src) => {
      this.RgbColorChangeCallBack(src);
    }, true, 3);
    this._rgb_R_editor.value = initialColor.red;
    this._rgb_G_editor.value = initialColor.green;
    this._rgb_B_editor.value = initialColor.blue;
    this._rgb_T_editor.value = initialColor.alpha;
    this._rgb_R_editor.refresh();
    this._rgb_G_editor.refresh();
    this._rgb_B_editor.refresh();
    this._rgb_T_editor.refresh();
    this._hsl_H_editor = this.AddColorScroll(HSLtable, "H", () => {
      return this.HueScrollBg();
    }, (src) => {
      this.HslColorChangeCallBack(src);
    }, false, 1);
    this._hsl_S_editor = this.AddColorScroll(HSLtable, "S", () => {
      return this.SaturationScrollBg();
    }, (src) => {
      this.HslColorChangeCallBack(src);
    }, false, 2);
    this._hsl_L_editor = this.AddColorScroll(HSLtable, "L", () => {
      return this.LuminosityScrollBg();
    }, (src) => {
      this.HslColorChangeCallBack(src);
    }, false, 3);
    this._hsl_T_editor = this.AddColorScroll(HSLtable, "T", () => {
      return this.HSLTranspScrollBg();
    }, (src) => {
      this.HslColorChangeCallBack(src);
    }, true, 3);
    this._hsl_H_editor.value = initialColor.hue;
    this._hsl_S_editor.value = initialColor.saturation;
    this._hsl_L_editor.value = initialColor.luminosity;
    this._hsl_T_editor.value = initialColor.alpha;
    this._hsl_H_editor.refresh();
    this._hsl_S_editor.refresh();
    this._hsl_L_editor.refresh();
    this._hsl_T_editor.refresh();
    let samplesTable = document.createElement("TABLE");
    samplesTable.style.position = "relative";
    samplesTable.style.width = "100%";
    samplesTable.style.paddingLeft = "3px";
    samplesTable.style.paddingRight = "3px";
    samplesTable.style.paddingTop = "10px";
    samplesTable.style.bottom = "3px";
    samplesTable.style.tableLayout = "fixed";
    let samplesTD = [];
    let previewTD = null;
    let sampleHeight = 30;
    let sampletr1 = document.createElement("TR");
    sampletr1.style.height = sampleHeight.toString() + "px";
    for (let i = 0; i < 6; i++) {
      let td = document.createElement("TD");
      if (i == 5) {
        td.colSpan = 2;
        td.rowSpan = 3;
        previewTD = td;
      } else {
        samplesTD.push(td);
      }
      sampletr1.appendChild(td);
    }
    samplesTable.appendChild(sampletr1);
    let sampletr2 = document.createElement("TR");
    sampletr2.style.height = sampleHeight.toString() + "px";
    for (let i = 0; i < 5; i++) {
      let td = document.createElement("TD");
      samplesTD.push(td);
      sampletr2.appendChild(td);
    }
    samplesTable.appendChild(sampletr2);
    let sampletr3 = document.createElement("TR");
    sampletr3.style.height = sampleHeight.toString() + "px";
    for (let i = 0; i < 5; i++) {
      let td = document.createElement("TD");
      samplesTD.push(td);
      sampletr3.appendChild(td);
    }
    samplesTable.appendChild(sampletr3);
    this._container.appendChild(samplesTable);
    this._samples = [];
    for (let i = 0; i < samplesTD.length; i++) {
      let div2 = document.createElement("DIV");
      div2.style.position = "relative";
      div2.style.width = "100%";
      div2.style.height = (sampleHeight - 4).toString() + "px";
      div2.style.display = "inline-block";
      div2.title = "Color history";
      samplesTD[i].appendChild(div2);
      let sampler = new ColorSampler(div2, (c) => {
        this.sampleWasSelectedCallback(c);
      });
      sampler.color = _colorEditor._colorHistory[i];
      this._samples.push(sampler);
    }
    let prefinedColors = YColor.predefinedColors;
    let colors = [];
    let predefinedColorNames = Object.keys(prefinedColors);
    for (let i = 0; i < predefinedColorNames.length; i++) {
      colors.push(prefinedColors[predefinedColorNames[i]]);
    }
    colors.sort((c1, c2) => {
      if (c1.alpha < c2.alpha)
        return 1;
      if (c1.alpha > c2.alpha)
        return -1;
      if (c1.luminosity < c2.luminosity)
        return 1;
      if (c1.luminosity > c2.luminosity)
        return -1;
      if (c1.saturation < c2.saturation)
        return 1;
      if (c1.saturation > c2.saturation)
        return -1;
      if (c1.hue < c2.hue)
        return 1;
      if (c1.hue > c2.hue)
        return -1;
      return 0;
    });
    let currenTR = null;
    this._prefedDivs = [];
    for (let i = 0; i < colors.length; i++) {
      let c = colors[i];
      if (i % 16 == 0) {
        currenTR = document.createElement("TR");
        predefTable.appendChild(currenTR);
      }
      let td = document.createElement("TD");
      currenTR.appendChild(td);
      let div2 = document.createElement("DIV");
      this._prefedDivs.push(div2);
      div2.style.position = "relative";
      div2.style.width = "100%";
      div2.style.height = Math.round(10 * constants.guiDPIFactor).toString() + "px";
      div2.style.display = "inline-block";
      div2.title = c.name;
      td.appendChild(div2);
      let sampler = new ColorSampler(div2, (c2) => {
        this.sampleWasSelectedCallback(c2);
      });
      sampler.color = c;
    }
    let div = document.createElement("DIV");
    div.style.position = "relative";
    div.style.width = "100%";
    div.style.height = (3 * sampleHeight).toString() + "px";
    div.style.display = "inline-block";
    div.title = "Color preview";
    previewTD.appendChild(div);
    this._preview = new ColorSampler(div, null);
    this._preview.color = initialColor;
    this.selectApropriateTab(initialColor);
  }
  selectApropriateTab(color) {
    if (color.isPredefined) {
      this._tabPredef.active = true;
      this.tabSelected(this._tabPredef);
    } else {
      this._tabPredef.active = false;
      if (color.isRGB) {
        this._tabRGB.active = true;
        this._tabHSL.active = false;
        this.tabSelected(this._tabRGB);
      } else {
        this._tabRGB.active = false;
        this._tabHSL.active = true;
        this.tabSelected(this._tabHSL);
      }
    }
  }
  sampleWasSelectedCallback(color) {
    this._rgb_R_editor.value = color.red;
    this._rgb_G_editor.value = color.green;
    this._rgb_B_editor.value = color.blue;
    this._rgb_T_editor.value = color.alpha;
    this._hsl_H_editor.value = color.hue;
    this._hsl_S_editor.value = color.saturation;
    this._hsl_L_editor.value = color.luminosity;
    this._hsl_T_editor.value = color.alpha;
    this._preview.color = color;
    this._rgb_R_editor.refresh();
    this._rgb_G_editor.refresh();
    this._rgb_B_editor.refresh();
    this._rgb_T_editor.refresh();
    this._hsl_H_editor.refresh();
    this._hsl_S_editor.refresh();
    this._hsl_L_editor.refresh();
    this._hsl_T_editor.refresh();
    this.initColorMemorization(color);
    if (this._colorChangeCallback != null)
      this._colorChangeCallback(color);
  }
  initColorMemorization(color) {
    if (this._memorizeTimer != null)
      clearTimeout(this._memorizeTimer);
    this._memorizeTimer = setTimeout(() => {
      this._memorizeTimer = null;
      _colorEditor.AddColorToHistory(color);
    }, 2e3);
  }
  RgbColorChangeCallBack(src) {
    if (src != this._rgb_R_editor)
      this._rgb_R_editor.refresh();
    if (src != this._rgb_G_editor)
      this._rgb_G_editor.refresh();
    if (src != this._rgb_B_editor)
      this._rgb_B_editor.refresh();
    if (src != this._rgb_T_editor)
      this._rgb_T_editor.refresh();
    let color = YColor.FromArgb(this._rgb_T_editor.value, this._rgb_R_editor.value, this._rgb_G_editor.value, this._rgb_B_editor.value);
    this._preview.color = color;
    this.initColorMemorization(color);
    if (this._colorChangeCallback != null)
      this._colorChangeCallback(color);
  }
  HslColorChangeCallBack(src) {
    if (src != this._hsl_H_editor)
      this._hsl_H_editor.refresh();
    if (src != this._hsl_S_editor)
      this._hsl_S_editor.refresh();
    if (src != this._hsl_L_editor)
      this._hsl_L_editor.refresh();
    if (src != this._hsl_T_editor)
      this._hsl_T_editor.refresh();
    let color = YColor.FromAhsl(this._hsl_T_editor.value, this._hsl_H_editor.value, this._hsl_S_editor.value, this._hsl_L_editor.value);
    this._preview.color = color;
    this.initColorMemorization(color);
    if (this._colorChangeCallback != null)
      this._colorChangeCallback(color);
  }
  set color(color) {
    this._preview.color = color;
    this._hsl_H_editor.value = color.hue;
    this._hsl_S_editor.value = color.saturation;
    this._hsl_L_editor.value = color.luminosity;
    this._hsl_T_editor.value = color.alpha;
    this._rgb_R_editor.value = color.red;
    this._rgb_G_editor.value = color.green;
    this._rgb_B_editor.value = color.blue;
    this._rgb_T_editor.value = color.alpha;
    this._hsl_H_editor.refresh();
    this._hsl_S_editor.refresh();
    this._hsl_L_editor.refresh();
    this._hsl_T_editor.refresh();
    this._rgb_R_editor.refresh();
    this._rgb_G_editor.refresh();
    this._rgb_B_editor.refresh();
    this._rgb_T_editor.refresh();
    this.selectApropriateTab(color);
  }
  AddColorScroll(container, label, colorCallback, changeCallBack, checkerBoardBg, value) {
    let RGB_R_Row = document.createElement("TR");
    let RGB_R_Col1 = document.createElement("TD");
    RGB_R_Col1.innerHTML = "&nbsp;" + label + "&nbsp;";
    let RGB_R_Col2 = document.createElement("TD");
    let RGB_R_Anchor = document.createElement("ANCHOR");
    RGB_R_Col2.appendChild(RGB_R_Anchor);
    let RGB_R_Div = document.createElement("DIV");
    RGB_R_Col2.style.width = "100%";
    RGB_R_Anchor.appendChild(RGB_R_Div);
    let RGB_R_Col3 = document.createElement("TD");
    let RGB_R_Input = document.createElement("INPUT");
    RGB_R_Col3.appendChild(RGB_R_Input);
    RGB_R_Row.appendChild(RGB_R_Col1);
    RGB_R_Row.appendChild(RGB_R_Col2);
    RGB_R_Row.appendChild(RGB_R_Col3);
    container.appendChild(RGB_R_Row);
    return new ColorEditorSroll(RGB_R_Div, RGB_R_Anchor, RGB_R_Input, colorCallback, changeCallBack, checkerBoardBg, value);
  }
  setTabIndex(index) {
    this._firstTabIndex = index;
    index = this._tabRGB.setTabIndex(index);
    index = this._tabHSL.setTabIndex(index);
    index = this._tabPredef.setTabIndex(index);
    if (this._tabRGB.active) {
      index = this._rgb_R_editor.setTabIndex(index);
      index = this._rgb_G_editor.setTabIndex(index);
      index = this._rgb_B_editor.setTabIndex(index);
      index = this._rgb_T_editor.setTabIndex(index);
    } else if (this._tabHSL.active) {
      index = this._hsl_H_editor.setTabIndex(index);
      index = this._hsl_S_editor.setTabIndex(index);
      index = this._hsl_L_editor.setTabIndex(index);
      index = this._hsl_T_editor.setTabIndex(index);
    } else {
      for (let i = 0; i < this._prefedDivs.length; i++) {
        this._prefedDivs[i].tabIndex = index++;
      }
    }
    for (let i = 0; i < this._samples.length; i++) {
      index = this._samples[i].setTabIndex(index);
    }
    return index;
  }
};
colorEditor._colorHistory = colorEditor.defaultColorHistory();
colorEditor._editorsList = [];

// obj/full/configForm_rw.js
var configEditorTab = class {
  constructor(editor, label, fontSize, GUIcoef) {
    this._realTab = false;
    this._div = null;
    this._tabLabel = null;
    this._td = document.createElement("TD");
    if (label != "") {
      this._div = document.createElement("DIV");
      this._div.style.display = "none";
      let sz = Math.round(5 * GUIcoef).toString() + "px";
      this._div.style.padding = sz + " " + sz + " " + sz + " " + sz;
      this._td.style.paddingTop = Math.round(4 * GUIcoef).toString() + "px";
      this._td.style.fontSize = fontSize.toString() + "px";
      this._td.style.paddingBottom = Math.round(2 * GUIcoef).toString() + "px";
      this._td.tabIndex = 0;
      this._td.style.borderLeft = "1px solid " + configForm.inactiveBorderColor;
      this._td.style.borderRight = "1px solid " + configForm.inactiveBorderColor;
      this._td.style.borderTop = "1px solid " + configForm.inactiveBorderColor;
      this._td.style.borderBottom = "1px solid " + configForm.activeBorderColor;
      this._td.style.color = configForm.inactiveColor;
      this._td.style.backgroundColor = configForm.inactiveBackgroundColor;
      this._td.style.borderTopLeftRadius = Math.round(3 * GUIcoef).toString() + "px";
      this._td.style.borderTopRightRadius = Math.round(3 * GUIcoef).toString() + "px";
      this._td.style.paddingLeft = "5px";
      this._td.style.paddingRight = "5px";
      this._realTab = true;
      this._tabLabel = document.createElement("ANCHOR");
      this._tabLabel.innerText = label;
      this._tabLabel.addEventListener("click", () => {
        configForm.tabSelected(this);
      });
      this._tabLabel.addEventListener("keyup", (e) => {
        if (e.key === "Enter" || e.key === " ")
          configForm.tabSelected(this);
      });
      this._tabLabel.style.cursor = "pointer";
      this._td.appendChild(this._tabLabel);
    } else {
      this._td.style.borderBottom = "1px solid " + configForm.activeBorderColor;
      this._td.innerHTML = "<span>&nbsp;</span>";
    }
  }
  setTabIndex(index) {
    this._tabLabel.tabIndex = index++;
    return index;
  }
  set active(value) {
    if (!this._realTab)
      return;
    this._td.style.borderLeftColor = value ? configForm.activeBorderColor : configForm.inactiveBorderColor;
    this._td.style.borderTopColor = value ? configForm.activeBorderColor : configForm.inactiveBorderColor;
    this._td.style.borderRightColor = value ? configForm.activeBorderColor : configForm.inactiveBorderColor;
    this._td.style.borderBottomColor = value ? "rgba(0, 0, 0, 0)" : configForm.activeBorderColor;
    this._td.style.color = value ? configForm.activeColor : configForm.inactiveColor;
    this._td.style.backgroundColor = value ? configForm.activeBackgroundColor : configForm.inactiveBackgroundColor;
    this._div.style.display = value ? "" : "none";
  }
  get tabElement() {
    return this._td;
  }
  get divElement() {
    return this._div;
  }
};
var configForm = class _configForm {
  static get activeBorderColor() {
    return constants.WindowInnerBorderColor;
  }
  static get inactiveBorderColor() {
    return "#b0b0b0";
  }
  static get activeColor() {
    return "black";
  }
  static get inactiveColor() {
    return "grey";
  }
  static get activeBackgroundColor() {
    return constants.WindowInnerBackgroundColor;
  }
  static get inactiveBackgroundColor() {
    return constants.WindowBackgroundColor;
  }
  static async hubStateChanged(source) {
    if (_configForm._Hubtable == null)
      return;
    let srvNotification = source.removable ? "" : " (server)";
    for (let i = 1; i < _configForm._Hubtable.childElementCount; i++) {
      if (_configForm._Hubtable.childNodes[i]["YHUB"] == source) {
        _configForm._Hubtable.childNodes[i].childNodes[2].innerText = await source.ConnectionDescription() + srvNotification;
        switch (source.ConnectionState()) {
          case 2:
            _configForm._Hubtable.childNodes[i].childNodes[0].innerHTML = ressources.OkIcon((1.2 * _configForm.fontSize).toString(), true, false, false, false, " ");
            break;
          case 3:
            _configForm._Hubtable.childNodes[i].childNodes[0].innerHTML = ressources.FailedIcon((1.2 * _configForm.fontSize).toString(), true, false, false, false, " ");
            break;
          default:
            _configForm._Hubtable.childNodes[i].childNodes[0].innerHTML = ressources.DontKnowIcon((1.2 * _configForm.fontSize).toString(), true, false, false, false, " ");
            break;
        }
      }
    }
  }
  static RefreshUI(h) {
    for (let i = 1; i < _configForm._Hubtable.childElementCount; i++) {
      if (_configForm._Hubtable.childNodes[i]["YHUB"] == h) {
        _configForm._Hubtable.childNodes[i].childNodes[1].innerText = h.toString();
        _configForm._Hubtable.childNodes[i].childNodes[2].innerText = "...";
      }
    }
  }
  static HublineSelected(source) {
    _configForm._selectedHub = null;
    for (let i = 1; i < _configForm._Hubtable.childElementCount; i++) {
      if (_configForm._Hubtable.childNodes[i] == source) {
        _configForm._selectedHub = _configForm._Hubtable.childNodes[i]["YHUB"];
        _configForm._Hubtable.childNodes[i].style.backgroundColor = "#2fa5ff";
        _configForm._Hubtable.childNodes[i].childNodes[0].style.color = "white";
        _configForm._Hubtable.childNodes[i].childNodes[1].style.color = "white";
        _configForm._Hubtable.childNodes[i].childNodes[2].style.color = "white";
      } else {
        _configForm._Hubtable.childNodes[i].style.backgroundColor = "white";
        _configForm._Hubtable.childNodes[i].childNodes[0].style.color = "black";
        _configForm._Hubtable.childNodes[i].childNodes[1].style.color = "black";
        _configForm._Hubtable.childNodes[i].childNodes[2].style.color = "black";
      }
    }
    _configForm._editButton.enabled = _configForm._selectedHub != null;
    _configForm._deleteButton.enabled = _configForm._selectedHub != null;
    this._editButton.showShortcut(_configForm._selectedHub != null);
    this._deleteButton.showShortcut(_configForm._selectedHub != null);
  }
  static newHub() {
    HubEdit.newHub(() => {
      _configForm._window.show();
    });
  }
  static async AddHubToUI(hub) {
    let HubtableTR = document.createElement("TR");
    HubtableTR.style.fontSize = _configForm.fontSize.toString() + "px";
    HubtableTR["YHUB"] = hub;
    HubtableTR.style.backgroundColor = "white";
    HubtableTR.style.cursor = "pointer";
    HubtableTR.tabIndex = 0;
    HubtableTR.addEventListener("keypress", (e) => {
      switch (e.key.toUpperCase()) {
        case "D":
          this._deleteButton.performAction();
          break;
        case "E":
          this._editButton.performAction();
          break;
      }
    });
    HubtableTR.style.outline = "none";
    HubtableTR.addEventListener("click", () => {
      _configForm.HublineSelected(HubtableTR);
    });
    HubtableTR.addEventListener("focusin", () => {
      _configForm.HublineSelected(HubtableTR);
    });
    let HubtableTD = document.createElement("TD");
    switch (hub.ConnectionState()) {
      case 2:
        HubtableTD.innerHTML = ressources.OkIcon((1.2 * _configForm.fontSize).toString(), true, false, false, false, " ");
        break;
      case 3:
        HubtableTD.innerHTML = ressources.FailedIcon((1.2 * _configForm.fontSize).toString(), true, false, false, false, " ");
        break;
      case 1:
      default:
        HubtableTD.innerHTML = ressources.DontKnowIcon((1.2 * _configForm.fontSize).toString(), true, false, false, false, " ");
    }
    HubtableTD.style.width = (2 * _configForm.fontSize).toString() + "px";
    HubtableTD.style.verticalAlign = "center";
    HubtableTD.style.borderBottom = "1px solid " + _configForm.inactiveBorderColor;
    HubtableTD.style.paddingLeft = "5px";
    HubtableTR.appendChild(HubtableTD);
    HubtableTD = document.createElement("TD");
    HubtableTD.innerText = hub.toString();
    HubtableTD.style.borderBottom = "1px solid " + _configForm.inactiveBorderColor;
    HubtableTD.style.verticalAlign = "center";
    HubtableTD.style.height = (2 * _configForm.fontSize).toString() + "px";
    HubtableTR.appendChild(HubtableTD);
    HubtableTD = document.createElement("TD");
    HubtableTD.style.verticalAlign = "center";
    HubtableTD.style.whiteSpace = "nowrap";
    HubtableTD.innerText = await hub.ConnectionDescription() + (hub.removable ? "" : " (server)");
    HubtableTD.style.borderBottom = "1px solid " + _configForm.inactiveBorderColor;
    HubtableTR.appendChild(HubtableTD);
    _configForm._Hubtable.appendChild(HubtableTR);
  }
  static editSelectedHub() {
    if (_configForm._selectedHub == null)
      return;
    HubEdit.editHub(_configForm._selectedHub, () => {
      _configForm._window.show();
    });
  }
  static deleteSelectedHub() {
    if (_configForm._selectedHub == null)
      return;
    let ExtraMessage = _configForm._selectedHub.removable ? "" : "\n\nThis one is also the one serving the Yocto-Visualization software. If you remove it, you won't be able to save later on.";
    confirm.ask("Do you really want to remove connection " + _configForm._selectedHub.toString() + " ?" + ExtraMessage, () => {
      if (_configForm._selectedHub == null)
        return;
      _configForm._selectedHub.Disconnect().then((r) => {
      });
      for (let i = _configForm._Hubtable.childElementCount - 1; i > 0; i--) {
        if (_configForm._Hubtable.childNodes[i]["YHUB"] == _configForm._selectedHub) {
          _configForm._Hubtable.removeChild(_configForm._Hubtable.childNodes[i]);
        }
      }
      sensorsManager.hubWasremoved(_configForm._selectedHub);
      _configForm._selectedHub = null;
      _configForm._editButton.enabled = false;
      _configForm._deleteButton.enabled = false;
      constants.edited = true;
    }, null, null);
  }
  static hide() {
    if (_configForm._window == null)
      return;
    _configForm._window.visible = false;
  }
  static show() {
    if (_configForm._window == null) {
      _configForm.GUIcoef = constants.generalSizeCoef;
      if (_configForm.REFWIDH * _configForm.GUIcoef > screen.width)
        _configForm.GUIcoef = screen.width / _configForm.REFWIDH;
      if (_configForm.REFHEIGHT * _configForm.GUIcoef > screen.height)
        _configForm.GUIcoef = screen.height / _configForm.REFHEIGHT;
      _configForm.fontSize *= _configForm.GUIcoef / constants.generalSizeCoef;
      let params = new newWindowParam(_configForm.GUIcoef);
      params.positionType = 1;
      params.width = Math.round(_configForm.REFWIDH * this.GUIcoef);
      params.height = Math.round(_configForm.REFHEIGHT * this.GUIcoef);
      params.showContainerBorders = false;
      params.title = "Global configuration";
      _configForm._window = new YWindow(params);
      _configForm._contents = _configForm._window.innerContentDiv;
      _configForm._table = document.createElement("TABLE");
      _configForm._contents.appendChild(_configForm._table);
      _configForm._table.style.position = "absolute";
      _configForm._table.style.left = "0px";
      _configForm._table.style.right = "0px";
      _configForm._table.style.width = "100%";
      _configForm._table.style.height = "100%";
      _configForm._table.style.borderSpacing = "0";
      _configForm._table.style.paddingLeft = (5 * this.GUIcoef).toString() + "px";
      _configForm._table.style.paddingTop = (5 * this.GUIcoef).toString() + "px";
      _configForm._table.style.paddingRight = (5 * this.GUIcoef).toString() + "px";
      _configForm._table.style.borderCollapse = "separate";
      _configForm._tr1 = document.createElement("TR");
      _configForm._tr2 = document.createElement("TR");
      _configForm._tr1.style.height = (2 * _configForm.fontSize).toString() + "px";
      _configForm._tabNetwork = new configEditorTab(_configForm, "Network", _configForm.fontSize, _configForm.GUIcoef);
      _configForm._tabCapture = new configEditorTab(_configForm, "Screen capture", _configForm.fontSize, _configForm.GUIcoef);
      _configForm._tabResources = new configEditorTab(_configForm, "Ressources", _configForm.fontSize, _configForm.GUIcoef);
      _configForm._tabUI = new configEditorTab(_configForm, "User interface", _configForm.fontSize, _configForm.GUIcoef);
      _configForm._tabExport = new configEditorTab(_configForm, "Updates & XML", _configForm.fontSize, _configForm.GUIcoef);
      _configForm._tabStub = new configEditorTab(_configForm, "", _configForm.fontSize, _configForm.GUIcoef);
      this._tabPanelContents = document.createElement("TD");
      this._tabPanelContents.colSpan = 6;
      this._tabPanelContents.style.borderLeft = "1px solid " + this.activeBorderColor;
      this._tabPanelContents.style.borderRight = "1px solid " + this.activeBorderColor;
      this._tabPanelContents.style.borderBottom = "1px solid " + this.activeBorderColor;
      this._tabPanelContents.style.backgroundColor = this.activeBackgroundColor;
      _configForm._tr1.appendChild(this._tabNetwork.tabElement);
      _configForm._tr1.appendChild(this._tabCapture.tabElement);
      _configForm._tr1.appendChild(this._tabResources.tabElement);
      _configForm._tr1.appendChild(this._tabUI.tabElement);
      _configForm._tr1.appendChild(this._tabExport.tabElement);
      _configForm._tr1.appendChild(this._tabStub.tabElement);
      _configForm._tr2.appendChild(this._tabPanelContents);
      _configForm._tr2.childNodes[0].style.verticalAlign = "top";
      _configForm._table.appendChild(this._tr1);
      _configForm._table.appendChild(this._tr2);
      let p = document.createElement("P");
      p.style.textAlign = "justify";
      p.style.fontSize = _configForm.fontSize.toString() + "px";
      p.style.paddingTop = "0px";
      p.style.marginTop = Math.round(3 * this.GUIcoef).toString() + "px";
      p.innerText = "Enter the list of VirtualHub or YoctoHub this page can connect to";
      this._tabNetwork.divElement.appendChild(p);
      _configForm._Hubtable = document.createElement("TABLE");
      _configForm._Hubtable.style.position = "absolute";
      _configForm._Hubtable.style.display = "block";
      _configForm._Hubtable.style.left = Math.round(20 * this.GUIcoef).toString() + "px";
      _configForm._Hubtable.style.right = Math.round(20 * this.GUIcoef).toString() + "px";
      _configForm._Hubtable.style.tableLayout = "auto";
      _configForm._Hubtable.style.top = Math.round(80 * this.GUIcoef).toString() + "px";
      _configForm._Hubtable.style.bottom = Math.round(40 * this.GUIcoef).toString() + "px";
      _configForm._Hubtable.style.borderCollapse = "colapse";
      _configForm._Hubtable.style.borderSpacing = "0px";
      _configForm._Hubtable.style.border = "1px solid " + _configForm.activeBorderColor;
      let HubtableTR = document.createElement("TR");
      HubtableTR.style.fontSize = _configForm.fontSize.toString() + "px";
      let HubtableTH = document.createElement("TH");
      HubtableTH.colSpan = 2;
      HubtableTH.innerText = "Address";
      HubtableTH.style.borderBottom = "1px solid " + _configForm.activeBorderColor;
      HubtableTH.style.width = Math.round(400 * this.GUIcoef).toString() + "px";
      HubtableTR.appendChild(HubtableTH);
      HubtableTH = document.createElement("TH");
      HubtableTH.innerText = "Status";
      HubtableTH.style.width = "40%";
      HubtableTH.style.borderBottom = "1px solid " + _configForm.activeBorderColor;
      HubtableTH.style.borderLeft = "1px solid " + _configForm.activeBorderColor;
      HubtableTR.appendChild(HubtableTH);
      _configForm._Hubtable.appendChild(HubtableTR);
      let hubList = sensorsManager.hubList;
      for (let i = 0; i < hubList.length; i++) {
        _configForm.AddHubToUI(hubList[i]);
      }
      let div = document.createElement("DIV");
      div.style.position = "absolute";
      div.style.bottom = "0px";
      div.style.right = Math.round(20 * _configForm.GUIcoef).toString() + "px";
      div.style.height = Math.round(30 * _configForm.GUIcoef).toString() + "px";
      div.style.textAlign = "right";
      div.style.verticalAlign = "center";
      _configForm._newButton = new button("New", () => {
        _configForm._window.hide();
        _configForm.newHub();
      }, _configForm.GUIcoef);
      div.appendChild(_configForm._newButton.Element);
      _configForm._editButton = new button("Edit", () => {
        _configForm._window.hide();
        _configForm.editSelectedHub();
      }, _configForm.GUIcoef);
      _configForm._editButton.enabled = false;
      div.appendChild(_configForm._editButton.Element);
      _configForm._deleteButton = new button("Delete", () => {
        _configForm.deleteSelectedHub();
      }, _configForm.GUIcoef);
      _configForm._deleteButton.enabled = false;
      div.appendChild(_configForm._deleteButton.Element);
      this._tabNetwork.divElement.appendChild(div);
      this._tabNetwork.divElement.appendChild(_configForm._Hubtable);
      p = document.createElement("P");
      p.innerHTML = "Widgets content can be captured at anytime thought the <i>Snapshop</i> option available in right-click contextual menu";
      p.style.textAlign = "justify";
      p.style.fontSize = _configForm.fontSize.toString() + "px";
      this._tabCapture.divElement.appendChild(p);
      let tableSize = document.createElement("TABLE");
      tableSize.style.fontSize = _configForm.fontSize.toString() + "px";
      let row = document.createElement("TR");
      let td = document.createElement("TD");
      td.style.fontSize = _configForm.fontSize.toString() + "px";
      td.innerText = "ImageType";
      row.appendChild(td);
      td = document.createElement("TD");
      td.style.fontSize = _configForm.fontSize.toString() + "px";
      td.style.verticalAlign = "center";
      _configForm.inputPNG = document.createElement("INPUT");
      _configForm.inputSVG = document.createElement("INPUT");
      _configForm.inputPNG.checked = constants.captureType == YDataRenderer.CaptureType.PNG;
      _configForm.inputPNG.style.display = "inline";
      _configForm.inputPNG.type = "checkbox";
      _configForm.inputPNG.style.transform = "scale(" + this.GUIcoef.toString() + ")";
      _configForm.inputPNG.style.marginRight = Math.round(5 * this.GUIcoef).toString() + "px";
      _configForm.inputPNG.style.marginLeft = Math.round(5 * this.GUIcoef).toString() + "px";
      _configForm.inputPNG.addEventListener("change", () => {
        _configForm.captureFormatChange(_configForm.inputPNG);
      });
      td.append(_configForm.inputPNG);
      let span;
      span = document.createElement("SPAN");
      span.innerText = YDataRenderer.CaptureType.PNG.description;
      td.append(span);
      _configForm.inputSVG = document.createElement("INPUT");
      _configForm.inputSVG.checked = constants.captureType == YDataRenderer.CaptureType.SVG;
      _configForm.inputSVG.style.display = "inline";
      _configForm.inputSVG.type = "checkbox";
      _configForm.inputSVG.style.transform = "scale(" + this.GUIcoef.toString() + ")";
      _configForm.inputSVG.style.marginRight = Math.round(5 * this.GUIcoef).toString() + "px";
      _configForm.inputSVG.style.marginLeft = Math.round(5 * this.GUIcoef).toString() + "px";
      _configForm.inputSVG.addEventListener("change", () => {
        _configForm.captureFormatChange(_configForm.inputSVG);
      });
      td.append(_configForm.inputSVG);
      span = document.createElement("SPAN");
      span.innerText = YDataRenderer.CaptureType.SVG.description;
      td.append(span);
      row.appendChild(td);
      tableSize.appendChild(row);
      row = document.createElement("TR");
      td = document.createElement("TD");
      td.style.fontSize = _configForm.fontSize.toString() + "px";
      td.innerText = "Resolution:";
      row.appendChild(td);
      td = document.createElement("TD");
      td.style.fontSize = _configForm.fontSize.toString() + "px";
      _configForm.inputResolution = document.createElement("INPUT");
      _configForm.inputResolution.style.fontFamily = constants.generalFontFamily;
      _configForm.inputResolution.style.fontSize = constants.generalFontSize.toString() + "px";
      _configForm.inputResolution.style.border = "1px solid grey";
      _configForm.inputResolution.type = "number";
      _configForm.inputResolution.style.textAlign = "right";
      _configForm.inputResolution.style.marginRight = "5px";
      _configForm.inputResolution.style.width = "60px";
      _configForm.inputResolution.value = constants.captureDPI.toString();
      _configForm.inputResolution.style.display = "inline";
      new InputFieldManager(_configForm.inputResolution, 6, false, Number.NaN, Number.NaN, (sender, refreshInput) => {
        _configForm.captureDPIChange(_configForm.inputResolution);
      });
      td.append(_configForm.inputResolution);
      span = document.createElement("SPAN");
      span.innerText = "DPI";
      td.append(span);
      row.appendChild(td);
      tableSize.appendChild(row);
      row = document.createElement("TR");
      td = document.createElement("TD");
      td.style.fontSize = _configForm.fontSize.toString() + "px";
      td.innerText = "Size:";
      td.style.verticalAlign = "top";
      row.appendChild(td);
      td = document.createElement("TD");
      td.style.fontSize = _configForm.fontSize.toString() + "px";
      _configForm.inputSizeSelect = document.createElement("SELECT");
      _configForm.inputSizeSelect.style.border = "1px solid grey";
      _configForm.inputSizeSelect.style.fontFamily = constants.generalFontFamily;
      _configForm.inputSizeSelect.style.fontSize = constants.generalFontSize.toString() + "px";
      let sizingOptions = YDataRenderer.CaptureFormats.Keep.sibblings;
      for (let i = 0; i < sizingOptions.length; i++) {
        let option = document.createElement("OPTION");
        option.text = sizingOptions[i].description;
        option.value = sizingOptions[i].toString;
        _configForm.inputSizeSelect.add(option, null);
      }
      _configForm.inputSizeSelect.value = constants.captureSizePolicy.toString;
      _configForm.inputSizeSelect.addEventListener("change", () => {
        _configForm.captureSizeSelectChange(_configForm.inputSizeSelect);
      });
      td.append(_configForm.inputSizeSelect);
      row.appendChild(td);
      tableSize.appendChild(row);
      let subtable = document.createElement("TABLE");
      _configForm.captureWidthTR = document.createElement("TR");
      let subTD = document.createElement("TD");
      subTD.style.fontSize = _configForm.fontSize.toString() + "px";
      subTD.innerText = "Width:";
      _configForm.captureWidthTR.appendChild(subTD);
      subTD = document.createElement("TD");
      subTD.style.fontSize = _configForm.fontSize.toString() + "px";
      _configForm.inputCaptureWidth = document.createElement("INPUT");
      _configForm.inputCaptureWidth.style.border = "1px solid grey";
      _configForm.inputCaptureWidth.style.fontFamily = constants.generalFontFamily;
      _configForm.inputCaptureWidth.style.fontSize = constants.generalFontSize.toString() + "px";
      _configForm.inputCaptureWidth.type = "number";
      _configForm.inputCaptureWidth.style.textAlign = "right";
      _configForm.inputCaptureWidth.style.marginRight = "5px";
      _configForm.inputCaptureWidth.style.width = "60px";
      _configForm.inputCaptureWidth.value = constants.captureWidth.toString();
      _configForm.inputCaptureWidth.style.display = "inline";
      new InputFieldManager(_configForm.inputCaptureWidth, 3, false, Number.NaN, Number.NaN, (sender, refreshInput) => {
        _configForm.captureWidthChange(_configForm.inputCaptureWidth);
      });
      subTD.append(_configForm.inputCaptureWidth);
      _configForm.inputCaptureWidthInfo = document.createElement("SPAN");
      _configForm.inputCaptureWidthInfo.innerText = "pixels";
      subTD.append(_configForm.inputCaptureWidthInfo);
      _configForm.captureWidthTR.appendChild(subTD);
      subtable.appendChild(_configForm.captureWidthTR);
      _configForm.captureHeightTR = document.createElement("TR");
      subTD = document.createElement("TD");
      subTD.style.fontSize = _configForm.fontSize.toString() + "px";
      subTD.innerText = "Height:";
      _configForm.captureHeightTR.appendChild(subTD);
      subTD = document.createElement("TD");
      subTD.style.fontSize = _configForm.fontSize.toString() + "px";
      _configForm.inputCaptureHeight = document.createElement("INPUT");
      _configForm.inputCaptureHeight.style.border = "1px solid grey";
      _configForm.inputCaptureHeight.style.fontFamily = constants.generalFontFamily;
      _configForm.inputCaptureHeight.style.fontSize = constants.generalFontSize.toString() + "px";
      _configForm.inputCaptureHeight.type = "number";
      _configForm.inputCaptureHeight.style.textAlign = "right";
      _configForm.inputCaptureHeight.style.marginRight = "5px";
      _configForm.inputCaptureHeight.style.width = "60px";
      _configForm.inputCaptureHeight.value = constants.captureHeight.toString();
      _configForm.inputCaptureHeight.style.display = "inline";
      new InputFieldManager(_configForm.inputCaptureHeight, 3, false, Number.NaN, Number.NaN, (sender, refreshInput) => {
        _configForm.captureHeightChange(_configForm.inputCaptureHeight);
      });
      subTD.append(_configForm.inputCaptureHeight);
      _configForm.inputCaptureHeightInfo = document.createElement("SPAN");
      _configForm.inputCaptureHeightInfo.innerText = "pixels";
      subTD.append(_configForm.inputCaptureHeightInfo);
      _configForm.captureHeightTR.appendChild(subTD);
      subtable.appendChild(_configForm.captureHeightTR);
      td.appendChild(subtable);
      _configForm._tabCapture.divElement.appendChild(tableSize);
      let tableRessources = document.createElement("TABLE");
      row = document.createElement("TR");
      td = document.createElement("TD");
      td.style.fontSize = _configForm.fontSize.toString() + "px";
      td.innerText = "If you plan to use YoctoVisualization continuously for an extended period of time (i.e. several weeks),  or if you are running it on a computer with limited resources, you may want to limit the application memory consumption.";
      td.colSpan = 2;
      td.style.textAlign = "justify";
      row.appendChild(td);
      tableRessources.appendChild(row);
      row = document.createElement("TR");
      td = document.createElement("TD");
      td.style.fontSize = _configForm.fontSize.toString() + "px";
      td.innerText = "Max count of sensors data records:";
      row.appendChild(td);
      td = document.createElement("TD");
      td.style.fontSize = _configForm.fontSize.toString() + "px";
      let maxSensorRecs = document.createElement("INPUT");
      maxSensorRecs.style.border = "1px solid grey";
      maxSensorRecs.style.fontFamily = constants.generalFontFamily;
      maxSensorRecs.style.fontSize = constants.generalFontSize.toString() + "px";
      maxSensorRecs.type = "number";
      maxSensorRecs.style.textAlign = "right";
      maxSensorRecs.style.marginRight = "5px";
      maxSensorRecs.style.marginLeft = "5px";
      maxSensorRecs.style.width = "60px";
      maxSensorRecs.value = constants.maxDataRecordsPerSensor.toString();
      maxSensorRecs.style.display = "inline";
      new InputFieldManager(maxSensorRecs, 2, false, Number.NaN, Number.NaN, (sender, refreshInput) => {
        _configForm.maxSensorRecsChange(maxSensorRecs);
      });
      td.append(maxSensorRecs);
      span = document.createElement("SPAN");
      span.innerText = "(0 = unlimited)";
      td.append(span);
      row.appendChild(td);
      tableRessources.appendChild(row);
      row = document.createElement("TR");
      td = document.createElement("TD");
      td.style.fontSize = _configForm.fontSize.toString() + "px";
      td.innerText = "Max count of graph series data points:";
      row.appendChild(td);
      td = document.createElement("TD");
      td.style.fontSize = _configForm.fontSize.toString() + "px";
      let maxSeriesRecs = document.createElement("INPUT");
      maxSeriesRecs.style.border = "1px solid grey";
      maxSeriesRecs.style.fontFamily = constants.generalFontFamily;
      maxSeriesRecs.style.fontSize = constants.generalFontSize.toString() + "px";
      maxSeriesRecs.type = "number";
      maxSeriesRecs.style.textAlign = "right";
      maxSeriesRecs.style.marginRight = "5px";
      maxSeriesRecs.style.marginLeft = "5px";
      maxSeriesRecs.style.width = "60px";
      maxSeriesRecs.value = constants.maxPointsPerGraphSerie.toString();
      maxSeriesRecs.style.display = "inline";
      new InputFieldManager(maxSeriesRecs, 2, false, Number.NaN, Number.NaN, (sender, refreshInput) => {
        _configForm.maxSeriesRecsChange(maxSeriesRecs);
      });
      td.append(maxSeriesRecs);
      span = document.createElement("SPAN");
      span.innerText = "(0 = unlimited)";
      td.append(span);
      row.appendChild(td);
      tableRessources.appendChild(row);
      row = document.createElement("TR");
      td = document.createElement("TD");
      td.style.fontSize = _configForm.fontSize.toString() + "px";
      td.innerText = "\nYocto-Visualization automatically loads datalogger contents for all connected devices.  If you are using networked devices featuring  billable data transfers (GSM) you may want to limit or even disable that feature.";
      td.colSpan = 2;
      td.style.textAlign = "justify";
      row.appendChild(td);
      tableRessources.appendChild(row);
      row = document.createElement("TR");
      td = document.createElement("TD");
      td.style.fontSize = _configForm.fontSize.toString() + "px";
      td.innerText = "Max data points/series loaded from dataloggers:";
      row.appendChild(td);
      td = document.createElement("TD");
      td.style.fontSize = _configForm.fontSize.toString() + "px";
      let maxDataLogRecs = document.createElement("INPUT");
      maxDataLogRecs.style.border = "1px solid grey";
      maxDataLogRecs.style.fontFamily = constants.generalFontFamily;
      maxDataLogRecs.style.fontSize = constants.generalFontSize.toString() + "px";
      maxDataLogRecs.type = "number";
      maxDataLogRecs.style.textAlign = "right";
      maxDataLogRecs.style.marginRight = "5px";
      maxDataLogRecs.style.marginLeft = "5px";
      maxDataLogRecs.style.width = "60px";
      maxDataLogRecs.value = constants.maxPointsPerDataloggerSerie.toString();
      maxDataLogRecs.style.display = "inline";
      new InputFieldManager(maxDataLogRecs, 1, false, -1, Number.NaN, (sender, refreshInput) => {
        _configForm.maxDataLogRecsChange(maxDataLogRecs);
      });
      td.append(maxDataLogRecs);
      span = document.createElement("SPAN");
      span.innerText = "(0 = unlimited,\n-1 = disabled)";
      td.append(span);
      row.appendChild(td);
      tableRessources.appendChild(row);
      row = document.createElement("TR");
      td = document.createElement("TD");
      td.style.fontSize = _configForm.fontSize.toString() + "px";
      td.innerText = "After a  limit increase, you'll have to restart the application to get your old data back.";
      td.colSpan = 2;
      td.style.textAlign = "justify";
      row.appendChild(td);
      tableRessources.appendChild(row);
      _configForm._tabResources.divElement.appendChild(tableRessources);
      p = document.createElement("P");
      p.style.fontSize = _configForm.fontSize.toString() + "px";
      p.innerText = "You can alter user interface behavior according to your hardware capabilities.";
      this._tabUI.divElement.appendChild(p);
      let checkbox1 = document.createElement("INPUT");
      checkbox1.style.fontFamily = constants.generalFontFamily;
      checkbox1.style.fontSize = constants.generalFontSize.toString() + "px";
      checkbox1.style.display = "inline";
      checkbox1.type = "checkbox";
      checkbox1.style.transform = "scale(" + this.GUIcoef.toString() + ")";
      checkbox1.style.marginRight = Math.round(5 * this.GUIcoef).toString() + "px";
      checkbox1.checked = constants.dbleClickBringsUpContextMenu;
      checkbox1.addEventListener("change", () => {
        _configForm.dbleClickBringsUpContextMenuChange(checkbox1);
      });
      this._tabUI.divElement.appendChild(checkbox1);
      span = document.createElement("SPAN");
      span.style.fontSize = _configForm.fontSize.toString() + "px";
      span.innerText = "Double click/tap  brings up context menu\n";
      this._tabUI.divElement.appendChild(span);
      let checkbox2 = document.createElement("INPUT");
      checkbox2.style.display = "inline";
      checkbox2.type = "checkbox";
      checkbox2.style.transform = "scale(" + this.GUIcoef.toString() + ")";
      checkbox2.style.marginRight = Math.round(5 * this.GUIcoef).toString() + "px";
      checkbox2.checked = YGraph.verticalDragZoomEnabled;
      checkbox2.addEventListener("change", () => {
        _configForm.verticalDragZoomChange(checkbox2);
      });
      this._tabUI.divElement.appendChild(checkbox2);
      span = document.createElement("SPAN");
      span.style.fontSize = _configForm.fontSize.toString() + "px";
      span.innerText = "Graphs zoom can be driven by vertical drag\n";
      this._tabUI.divElement.appendChild(span);
      p = document.createElement("P");
      p.style.fontSize = _configForm.fontSize.toString() + "px";
      p.innerText = "Yocto-Visualization tries to ajust UI size to screen resolution the best it can through a DPI based zoom factor. Unfortunately not all devices can properly report their screen resolution. That's why you can manually override that factor.";
      this._tabUI.divElement.appendChild(p);
      let DPIoverride = document.createElement("INPUT");
      let checkbox3 = document.createElement("INPUT");
      checkbox3.style.display = "inline";
      checkbox3.type = "checkbox";
      checkbox3.style.transform = "scale(" + this.GUIcoef.toString() + ")";
      checkbox3.style.marginRight = Math.round(5 * this.GUIcoef).toString() + "px";
      checkbox3.checked = constants.guiDPIFactorIsOverriden;
      checkbox3.addEventListener("change", () => {
        _configForm.DPIoverrideChange(checkbox3, DPIoverride);
      });
      this._tabUI.divElement.appendChild(checkbox3);
      span = document.createElement("SPAN");
      span.style.fontSize = _configForm.fontSize.toString() + "px";
      span.innerText = "Override zoom factor to ";
      this._tabUI.divElement.appendChild(span);
      DPIoverride.style.border = "1px solid grey";
      DPIoverride.style.fontFamily = constants.generalFontFamily;
      DPIoverride.style.fontSize = constants.generalFontSize.toString() + "px";
      DPIoverride.disabled = !constants.guiDPIFactorIsOverriden;
      DPIoverride.type = "number";
      DPIoverride.style.textAlign = "right";
      DPIoverride.style.marginRight = "5px";
      DPIoverride.style.marginLeft = "5px";
      DPIoverride.style.width = "60px";
      DPIoverride.value = constants.guiDPIFactorOverrideValue.toString();
      DPIoverride.style.display = "inline";
      new InputFieldManager(DPIoverride, 5, false, Number.NaN, Number.NaN, (sender, refreshInput) => {
        _configForm.DPIoverrideChange(checkbox3, DPIoverride);
      });
      this._tabUI.divElement.appendChild(DPIoverride);
      p = document.createElement("P");
      p.style.fontSize = _configForm.fontSize.toString() + "px";
      p.innerText = "This setting is stored in your browser cookies. So it is specific to this device, browser and server.You will have to reload this page for this setting to take effect";
      this._tabUI.divElement.appendChild(p);
      p = document.createElement("P");
      p.style.fontSize = _configForm.fontSize.toString() + "px";
      p.style.textAlign = "justify";
      p.innerText = "As long as your web browser can reach www.yoctopuce.com, the read/write edition of Yocto-Visualization (for web) can automatically check for new version. If you find this feature anoying, feel free to disable it. ";
      this._tabExport.divElement.appendChild(p);
      let checkbox4 = document.createElement("INPUT");
      checkbox4.style.display = "inline";
      checkbox4.type = "checkbox";
      checkbox4.style.transform = "scale(" + this.GUIcoef.toString() + ")";
      checkbox4.style.marginLeft = Math.round(25 * this.GUIcoef).toString() + "px";
      checkbox4.checked = constants.mustCheckForUpdate;
      checkbox4.addEventListener("change", () => {
        _configForm.mustCheckForUpdateChange(checkbox4);
      });
      this._tabExport.divElement.appendChild(checkbox4);
      span = document.createElement("SPAN");
      span.style.fontSize = _configForm.fontSize.toString() + "px";
      span.innerText = "Automatically check for updates";
      let a2 = document.createElement("A");
      a2.innerText = "Check now";
      a2.style.paddingLeft = "25px";
      a2.style.cursor = "pointer";
      a2.style.color = "#0000EE";
      a2.style.textDecoration = "underline";
      a2.addEventListener("click", () => {
        YWebPage.CheckForNewVersion(true);
      });
      span.appendChild(a2);
      this._tabExport.divElement.appendChild(span);
      p = document.createElement("P");
      p.style.fontSize = _configForm.fontSize.toString() + "px";
      p.style.textAlign = "justify";
      p.innerText = "Yocto-Visualization (for web) stores its configuration in a XML file. This file can be used to create a static web page running Yocto-Visualization (for web) or it can be used in the Yocto-Visualization (for web) installer to create a pre-configured install. Also, it is mostly compatible with the native version of Yocto-Visualization: you can use it instead of the original config.xml file. Just click on the link below to export/download your current configuration.";
      this._tabExport.divElement.appendChild(p);
      p = document.createElement("P");
      p.style.fontSize = _configForm.fontSize.toString() + "px";
      p.style.textAlign = "right";
      let a = document.createElement("A");
      a.innerText = "Export current configuration";
      a.style.cursor = "pointer";
      a.style.color = "#0000EE";
      a.style.textDecoration = "underline";
      a.addEventListener("click", () => {
        _configForm.downloadCurrentConfig();
      });
      p.appendChild(a);
      this._tabExport.divElement.appendChild(p);
      _configForm._tabPanelContents.appendChild(this._tabNetwork.divElement);
      _configForm._tabPanelContents.appendChild(this._tabCapture.divElement);
      _configForm._tabPanelContents.appendChild(this._tabResources.divElement);
      _configForm._tabPanelContents.appendChild(this._tabUI.divElement);
      _configForm._tabPanelContents.appendChild(this._tabExport.divElement);
      _configForm.tabSelected(this._tabNetwork);
      _configForm.refreshSizeParams();
    }
    _configForm._window.show();
  }
  static DPIoverrideChange(checkbox, input) {
    let value = parseFloat(input.value);
    input.disabled = !checkbox.checked;
    if (checkbox.checked && value > 0) {
      constants.overrideGuiDPIFactor(true, value);
    } else {
      constants.overrideGuiDPIFactor(false);
    }
  }
  static hubWasAdded(hub) {
    if (sensorsManager.newHubCreated(hub)) {
      _configForm.AddHubToUI(hub);
      constants.edited = true;
      hub.Connect().then((r) => {
      });
    }
  }
  static hubGotEdited(hub) {
    _configForm.RefreshUI(hub);
    constants.edited = true;
    hub.Disconnect().then((r) => {
      YAPI.Sleep(1);
      hub.Connect();
    });
  }
  static dbleClickBringsUpContextMenuChange(source) {
    let prev = constants.dbleClickBringsUpContextMenu;
    constants.dbleClickBringsUpContextMenu = source.checked;
    if (prev != constants.dbleClickBringsUpContextMenu)
      constants.edited = true;
  }
  static verticalDragZoomChange(source) {
    let prev = YGraph.verticalDragZoomEnabled;
    YGraph.verticalDragZoomEnabled = source.checked;
    if (prev != YGraph.verticalDragZoomEnabled)
      constants.edited = true;
  }
  static mustCheckForUpdateChange(source) {
    let prev = constants.mustCheckForUpdate;
    constants.mustCheckForUpdate = source.checked;
    if (prev != constants.mustCheckForUpdate)
      constants.edited = true;
  }
  static maxDataLogRecsChange(source) {
    let value = parseInt(source.value);
    if (value != constants.maxPointsPerDataloggerSerie || value != CustomYSensor.MaxLoggerRecords) {
      constants.maxPointsPerDataloggerSerie = value;
      CustomYSensor.MaxLoggerRecords = value;
      constants.edited = true;
    }
  }
  static maxSeriesRecsChange(source) {
    let value = parseInt(source.value);
    if (value != constants.maxPointsPerGraphSerie || value != DataSerie.MaxPointsPerSeries) {
      constants.maxPointsPerGraphSerie = value;
      DataSerie.MaxPointsPerSeries = value;
      constants.edited = true;
    }
  }
  static maxSensorRecsChange(source) {
    let value = parseInt(source.value);
    if (value != constants.maxDataRecordsPerSensor || value != CustomYSensor.MaxDataRecords) {
      constants.maxDataRecordsPerSensor = value;
      CustomYSensor.MaxDataRecords = value;
      constants.edited = true;
    }
  }
  static captureDPIChange(source) {
    let prev = constants.captureDPI;
    constants.captureDPI = parseInt(source.value);
    if (constants.captureDPI != prev)
      constants.edited = true;
    _configForm.refreshSizeParams();
  }
  static captureWidthChange(source) {
    let prev = constants.captureWidth;
    constants.captureWidth = parseInt(source.value);
    if (constants.captureWidth != prev)
      constants.edited = true;
    _configForm.refreshSizeParams();
  }
  static captureHeightChange(source) {
    let prev = constants.captureHeight;
    constants.captureHeight = parseInt(source.value);
    if (constants.captureHeight != prev)
      constants.edited = true;
    _configForm.refreshSizeParams();
  }
  static captureSizeSelectChange(source) {
    let prev = constants.captureSizePolicy;
    constants.captureSizePolicy = YDataRenderer.CaptureFormats.fromString(YDataRenderer.CaptureFormats, source.value);
    if (constants.captureSizePolicy != prev)
      constants.edited = true;
    _configForm.refreshSizeParams();
  }
  static refreshSizeParams() {
    let value = YDataRenderer.CaptureFormats.fromString(YDataRenderer.CaptureFormats, _configForm.inputSizeSelect.value);
    if (value == YDataRenderer.CaptureFormats.Keep) {
      _configForm.captureWidthTR.style.display = "none";
      _configForm.captureHeightTR.style.display = "none";
    } else if (value == YDataRenderer.CaptureFormats.FixedWidth) {
      _configForm.captureWidthTR.style.display = "";
      _configForm.captureHeightTR.style.display = "none";
    } else if (value == YDataRenderer.CaptureFormats.FixedHeight) {
      _configForm.captureWidthTR.style.display = "none";
      _configForm.captureHeightTR.style.display = "";
    }
    if (value == YDataRenderer.CaptureFormats.Fixed) {
      _configForm.captureWidthTR.style.display = "";
      _configForm.captureHeightTR.style.display = "";
    }
    let sw = 25.4 * parseInt(_configForm.inputCaptureWidth.value) / parseInt(_configForm.inputResolution.value);
    _configForm.inputCaptureWidthInfo.innerText = "Pixels  (" + sw.toFixed(1) + "mm)";
    let sh = 25.4 * parseInt(_configForm.inputCaptureHeight.value) / parseInt(_configForm.inputResolution.value);
    _configForm.inputCaptureHeightInfo.innerText = "Pixels  (" + sh.toFixed(1) + "mm)";
  }
  static downloadCurrentConfig() {
    let element = document.createElement("a");
    let data = "data:text/xml;base64," + btoa(YWebPage.get_XmlData());
    element.setAttribute("href", data);
    element.setAttribute("download", "config.xml");
    element.style.display = "none";
    document.body.appendChild(element);
    element.click();
    document.body.removeChild(element);
  }
  static captureFormatChange(source) {
    if (source == _configForm.inputPNG)
      _configForm.inputSVG.checked = !source.checked;
    if (source == _configForm.inputSVG)
      _configForm.inputPNG.checked = !source.checked;
    let prev = constants.captureType;
    constants.captureType = _configForm.inputPNG.checked ? YDataRenderer.CaptureType.PNG : YDataRenderer.CaptureType.SVG;
    if (constants.captureType != prev)
      constants.edited = true;
  }
  static tabSelected(source) {
    this._tabNetwork.active = source == this._tabNetwork;
    this._tabCapture.active = source == this._tabCapture;
    this._tabResources.active = source == this._tabResources;
    this._tabUI.active = source == this._tabUI;
    this._tabExport.active = source == this._tabExport;
  }
};
configForm.GUIcoef = 1;
configForm.REFWIDH = 520;
configForm.REFHEIGHT = 300;
configForm.fontSize = constants.generalFontSize;

// obj/full/HubEdit_rw.js
var HubEdit = class _HubEdit {
  static get activeBorderColor() {
    return constants.WindowInnerBorderColor;
  }
  static get inactiveBorderColor() {
    return "#b0b0b0";
  }
  static get activeColor() {
    return "black";
  }
  static get inactiveColor() {
    return "grey";
  }
  static get activeBackgroundColor() {
    return constants.WindowInnerBackgroundColor;
  }
  static get inactiveBackgroundColor() {
    return constants.WindowBackgroundColor;
  }
  static newHub(whenDone) {
    let protocol = "ws";
    if (window.location.protocol.toString().toLowerCase() == "https:")
      protocol = "wss";
    _HubEdit._currenthub = new Hub(2, protocol, "", "", true, "", "", "");
    _HubEdit.show(_HubEdit._currenthub);
    _HubEdit._window.title = "New Connection";
    _HubEdit._thisIsEditing = false;
    _HubEdit._whenDone = whenDone;
  }
  static editHub(hub, whenDone) {
    _HubEdit._currenthub = hub;
    _HubEdit.show(_HubEdit._currenthub);
    _HubEdit._window.title = "Edit Connection";
    _HubEdit._thisIsEditing = true;
    _HubEdit._whenDone = whenDone;
  }
  static updateCorsWarning() {
    let state = "None";
    if (window.location.protocol.toString().toLowerCase() == "https:") {
      if (_HubEdit._protocolSelect.value == "ws" || _HubEdit._protocolSelect.value == "http")
        state = "";
    }
    _HubEdit._coreWarning.style.display = state;
  }
  static show(hub) {
    if (_HubEdit._window == null) {
      let h = window.location.protocol.toString().toLowerCase() == "https:" ? 300 : 280;
      _HubEdit._okButton = new button("Ok", () => {
        _HubEdit.okClicked();
      });
      _HubEdit._cancelButton = new button("Cancel", () => {
        _HubEdit.hide();
      });
      let params = new newWindowParam();
      params.positionType = 1;
      params.width = Math.round(450 * constants.generalSizeCoef);
      params.height = Math.round(h * constants.generalSizeCoef);
      params.isModal = true;
      params.closeIcon = false;
      params.title = "...";
      params.buttons.push(_HubEdit._okButton);
      params.buttons.push(_HubEdit._cancelButton);
      _HubEdit._window = new YWindow(params);
      _HubEdit._contents = _HubEdit._window.innerContentDiv;
      let p = document.createElement("P");
      p.style.paddingTop = "0px";
      p.style.marginTop = Math.round(3 * constants.generalSizeCoef).toString() + "px";
      p.style.lineHeight = Math.round(12 * constants.generalSizeCoef).toString() + "px";
      p.innerText = "Enter the hub connection parameter, The only mandatory parameter is the address, just leave the other fields empty if you want to use default values";
      p.style.textAlign = "justify";
      _HubEdit._contents.appendChild(p);
      p.appendChild(document.createElement("BR"));
      p.appendChild(document.createElement("BR"));
      let span = document.createElement("SPAN");
      span.innerText = "Protocol:";
      span.style.marginLeft = "20px";
      p.appendChild(span);
      _HubEdit._protocolSelect = document.createElement("SELECT");
      _HubEdit._protocolSelect.style.border = "1px solid grey";
      _HubEdit._protocolSelect.style.fontFamily = constants.generalFontFamily;
      _HubEdit._protocolSelect.style.fontSize = constants.generalFontSize.toString() + "px";
      let httpOption = document.createElement("OPTION");
      httpOption.text = "http";
      httpOption.value = "http";
      _HubEdit._protocolSelect.appendChild(httpOption);
      let httpsOption = document.createElement("OPTION");
      httpsOption.text = "https";
      httpsOption.value = "https";
      _HubEdit._protocolSelect.appendChild(httpsOption);
      let wsOption = document.createElement("OPTION");
      wsOption.text = "ws";
      wsOption.value = "ws";
      _HubEdit._protocolSelect.appendChild(wsOption);
      let wssOption = document.createElement("OPTION");
      wssOption.text = "wss";
      wssOption.value = "wss";
      _HubEdit._protocolSelect.appendChild(wssOption);
      p.appendChild(_HubEdit._protocolSelect);
      _HubEdit._protocolSelect.addEventListener("change", () => {
        _HubEdit.updateCorsWarning();
      });
      span = document.createElement("SPAN");
      span.style.marginLeft = "20px";
      span.innerText = "Address:";
      p.appendChild(span);
      _HubEdit._addressInput = document.createElement("INPUT");
      _HubEdit._addressInput.style.border = "1px solid grey";
      _HubEdit._addressInput.style.fontFamily = constants.generalFontFamily;
      _HubEdit._addressInput.style.fontSize = constants.generalFontSize.toString() + "px";
      _HubEdit._addressInput.maxLength = 512;
      _HubEdit._addressInput.size = 15;
      _HubEdit._addressInput.value = hub.addr;
      new InputFieldManager(_HubEdit._addressInput, 0, false, Number.NaN, Number.NaN, null);
      p.appendChild(_HubEdit._addressInput);
      span = document.createElement("SPAN");
      span.style.marginLeft = Math.round(20 * constants.generalSizeCoef).toString() + "px";
      span.innerText = "Port:";
      p.appendChild(span);
      _HubEdit._portInput = document.createElement("INPUT");
      _HubEdit._portInput.style.border = "1px solid grey";
      _HubEdit._portInput.style.fontFamily = constants.generalFontFamily;
      _HubEdit._portInput.style.fontSize = constants.generalFontSize.toString() + "px";
      _HubEdit._portInput.style.textAlign = "right";
      _HubEdit._portInput.maxLength = 5;
      _HubEdit._portInput.size = 5;
      new InputFieldManager(_HubEdit._portInput, 3, true, Number.NaN, Number.NaN, null);
      p.appendChild(_HubEdit._portInput);
      p.appendChild(document.createElement("BR"));
      p.appendChild(document.createElement("BR"));
      span = document.createElement("SPAN");
      span.innerText = "Path:";
      span.style.marginLeft = Math.round(20 * constants.generalSizeCoef).toString() + "px";
      p.appendChild(span);
      _HubEdit._pathInput = document.createElement("INPUT");
      _HubEdit._pathInput.style.border = "1px solid grey";
      _HubEdit._pathInput.style.fontFamily = constants.generalFontFamily;
      _HubEdit._pathInput.style.fontSize = constants.generalFontSize.toString() + "px";
      _HubEdit._pathInput.size = 45;
      p.appendChild(_HubEdit._pathInput);
      p.appendChild(document.createElement("BR"));
      p.appendChild(document.createElement("BR"));
      span = document.createElement("SPAN");
      span.innerText = "Username:";
      span.style.marginLeft = Math.round(20 * constants.generalSizeCoef).toString() + "px";
      p.appendChild(span);
      _HubEdit._usernameInput = document.createElement("INPUT");
      _HubEdit._usernameInput.style.border = "1px solid grey";
      _HubEdit._usernameInput.style.fontFamily = constants.generalFontFamily;
      _HubEdit._usernameInput.style.fontSize = constants.generalFontSize.toString() + "px";
      _HubEdit._usernameInput.size = 15;
      p.appendChild(_HubEdit._usernameInput);
      span = document.createElement("SPAN");
      span.innerText = "Password:";
      span.style.marginLeft = Math.round(20 * constants.generalSizeCoef).toString() + "px";
      p.appendChild(span);
      _HubEdit._passwordInput = document.createElement("INPUT");
      _HubEdit._passwordInput.type = "password";
      _HubEdit._passwordInput.style.border = "1px solid grey";
      _HubEdit._passwordInput.style.fontFamily = constants.generalFontFamily;
      _HubEdit._passwordInput.style.fontSize = constants.generalFontSize.toString() + "px";
      _HubEdit._passwordInput.size = 15;
      p.appendChild(_HubEdit._passwordInput);
      p = document.createElement("P");
      p.style.paddingTop = "0px";
      p.style.marginTop = Math.round(3 * constants.generalSizeCoef).toString() + "px";
      p.style.lineHeight = Math.round(12 * constants.generalSizeCoef).toString() + "px";
      p.innerText = "Important note: credentials are saved in the configuration file wich is easily accessible. Therefore, if hub is write-protected, it might be wise to use ReadOnly credentials here. When installed on a Yoctopuce hub, Yocto-Visualization (for web) will automatically ask for read/write credentials at save time if neccessary.";
      p.style.textAlign = "justify";
      _HubEdit._contents.appendChild(p);
      _HubEdit._coreWarning = document.createElement("P");
      _HubEdit._coreWarning.style.paddingTop = "0px";
      _HubEdit._coreWarning.style.marginTop = Math.round(3 * constants.generalSizeCoef).toString() + "px";
      _HubEdit._coreWarning.style.lineHeight = Math.round(12 * constants.generalSizeCoef).toString() + "px";
      _HubEdit._coreWarning.innerText = "Warning: If the host page is accessed though HTTPS, connecting to a hub though HTTP or WS is unlikely to work due to Cross-Origin Resource Sharing (CORS) rules";
      _HubEdit._coreWarning.style.textAlign = "justify";
      _HubEdit._coreWarning.style.display = "None";
      _HubEdit._contents.appendChild(_HubEdit._coreWarning);
    }
    _HubEdit._protocolSelect.value = hub.protocol;
    _HubEdit._addressInput.value = hub.addr;
    _HubEdit._portInput.value = hub.port;
    _HubEdit._pathInput.value = hub.path;
    _HubEdit._usernameInput.value = hub.user;
    _HubEdit._passwordInput.value = hub.encryptedPassword != "" ? _HubEdit.FakePassword : "";
    _HubEdit.updateCorsWarning();
    _HubEdit._window.show();
  }
  static hide() {
    if (_HubEdit._window == null)
      return;
    _HubEdit._window.hide();
    _HubEdit._passwordInput.value = "";
    if (_HubEdit._whenDone != null)
      _HubEdit._whenDone();
  }
  static okClicked() {
    _HubEdit._currenthub.protocol = _HubEdit._protocolSelect.value;
    _HubEdit._currenthub.addr = _HubEdit._addressInput.value;
    _HubEdit._currenthub.port = _HubEdit._portInput.value;
    _HubEdit._currenthub.path = _HubEdit._pathInput.value;
    _HubEdit._currenthub.user = _HubEdit._usernameInput.value;
    if (_HubEdit._currenthub.user != "") {
      if (_HubEdit._passwordInput.value != _HubEdit.FakePassword) {
        _HubEdit._currenthub.encryptedPassword = Hub.Encrypt(_HubEdit._passwordInput.value, Hub.loginCypherPassword);
      }
    } else {
      _HubEdit._currenthub.encryptedPassword = "";
    }
    if (_HubEdit._thisIsEditing) {
      configForm.hubGotEdited(_HubEdit._currenthub);
    } else {
      configForm.hubWasAdded(_HubEdit._currenthub);
    }
    _HubEdit.hide();
    if (_HubEdit._whenDone != null)
      _HubEdit._whenDone();
  }
};
HubEdit.FakePassword = "******";
HubEdit._thisIsEditing = false;
HubEdit._whenDone = null;
HubEdit.fontSize = constants.generalFontSize;

// obj/full/credPrompt_rw.js
var CredentialsPrompt = class _CredentialsPrompt {
  static get activeBorderColor() {
    return constants.WindowInnerBorderColor;
  }
  static get inactiveBorderColor() {
    return "#b0b0b0";
  }
  static get activeColor() {
    return "black";
  }
  static get inactiveColor() {
    return "grey";
  }
  static get activeBackgroundColor() {
    return constants.WindowInnerBackgroundColor;
  }
  static get inactiveBackgroundColor() {
    return constants.WindowBackgroundColor;
  }
  static get username() {
    return this._username;
  }
  static get password() {
    return this._password;
  }
  static show(targeturl, whenDone) {
    if (_CredentialsPrompt._window == null) {
      _CredentialsPrompt._okButton = new button("Ok", () => {
        _CredentialsPrompt.okClicked();
      });
      _CredentialsPrompt._cancelButton = new button("Cancel", () => {
        _CredentialsPrompt.hide();
      });
      let params = new newWindowParam();
      params.positionType = 1;
      params.width = Math.round(450 * constants.generalSizeCoef);
      params.height = Math.round(175 * constants.generalSizeCoef);
      params.isModal = true;
      params.closeIcon = false;
      params.title = "Some credentials are required";
      params.buttons.push(_CredentialsPrompt._okButton);
      params.buttons.push(_CredentialsPrompt._cancelButton);
      _CredentialsPrompt._window = new YWindow(params);
      _CredentialsPrompt._contents = _CredentialsPrompt._window.innerContentDiv;
      let p = document.createElement("P");
      p.style.paddingTop = "10px";
      p.style.marginTop = Math.round(5 * constants.generalSizeCoef).toString() + "px";
      p.style.lineHeight = Math.round(15 * constants.generalSizeCoef).toString() + "px";
      p.innerText = "Saving configuration on " + targeturl + " requires  read/write credentials.";
      p.style.textAlign = "justify";
      _CredentialsPrompt._contents.appendChild(p);
      p.appendChild(document.createElement("BR"));
      p.appendChild(document.createElement("BR"));
      let span = document.createElement("SPAN");
      span = document.createElement("SPAN");
      span.innerText = "Username:";
      span.style.marginLeft = Math.round(20 * constants.generalSizeCoef).toString() + "px";
      p.appendChild(span);
      _CredentialsPrompt._usernameInput = document.createElement("INPUT");
      _CredentialsPrompt._usernameInput.style.border = "1px solid grey";
      _CredentialsPrompt._usernameInput.style.fontFamily = constants.generalFontFamily;
      _CredentialsPrompt._usernameInput.style.fontSize = constants.generalFontSize.toString() + "px";
      _CredentialsPrompt._usernameInput.size = 15;
      p.appendChild(_CredentialsPrompt._usernameInput);
      span = document.createElement("SPAN");
      span.innerText = "Password:";
      span.style.marginLeft = Math.round(20 * constants.generalSizeCoef).toString() + "px";
      p.appendChild(span);
      _CredentialsPrompt._passwordInput = document.createElement("INPUT");
      _CredentialsPrompt._passwordInput.type = "password";
      _CredentialsPrompt._passwordInput.style.border = "1px solid grey";
      _CredentialsPrompt._passwordInput.style.fontFamily = constants.generalFontFamily;
      _CredentialsPrompt._passwordInput.style.fontSize = constants.generalFontSize.toString() + "px";
      _CredentialsPrompt._passwordInput.size = 15;
      _CredentialsPrompt._passwordInput.addEventListener("keypress", (e) => {
        if (e.key == "Enter")
          this.okClicked();
      });
      p.appendChild(_CredentialsPrompt._passwordInput);
    }
    _CredentialsPrompt._window.show();
    _CredentialsPrompt._usernameInput.focus();
    _CredentialsPrompt._whenDone = whenDone;
  }
  static hide() {
    if (_CredentialsPrompt._window == null)
      return;
    _CredentialsPrompt._window.hide();
    _CredentialsPrompt._passwordInput.value = "";
  }
  static okClicked() {
    _CredentialsPrompt._username = _CredentialsPrompt._usernameInput.value;
    _CredentialsPrompt._password = _CredentialsPrompt._passwordInput.value;
    _CredentialsPrompt.hide();
    if (_CredentialsPrompt._whenDone != null)
      _CredentialsPrompt._whenDone(_CredentialsPrompt._usernameInput.value, _CredentialsPrompt._passwordInput.value);
  }
};
CredentialsPrompt._whenDone = null;
CredentialsPrompt.fontSize = constants.generalFontSize;
CredentialsPrompt._username = "";
CredentialsPrompt._password = "";

// obj/full/Pako/utils/common.js
var utils_common = class {
  // This was orignaly used to manipulate deflate/inflate options
  // stored in anonymous structures
  // Yoctopuce replaced it with a proper options object with name
  // setting names and types check (see Pako_inflate_option and
  // Pako_deflate_option)
  //  public static assign  (obj : any, /*from1, from2, from3, ...*/)
  //    {
  //      const sources : any[] = Array.prototype.slice.call(arguments, 1);
  //      while (sources.length) {
  //     const source = sources.shift();
  //       if (!source) { continue; }
  //
  //       if (typeof source !== 'object') {
  //        throw new TypeError(source + 'must be non-object');
  //      }
  //
  //
  //    for (const p : string in source) {
  //      if (p.hasOwnProperty (source)) {
  //        obj[p] = source[p];
  //      }
  //    }
  //  }
  //
  //  return obj;
  //};
  // Join array of chunks to single array.
  static flattenChunks(chunks) {
    let len = 0;
    for (let i = 0, l = chunks.length; i < l; i++) {
      len += chunks[i].length;
    }
    const result = new Uint8Array(len);
    for (let i = 0, pos = 0, l = chunks.length; i < l; i++) {
      let chunk = chunks[i];
      result.set(chunk, pos);
      pos += chunk.length;
    }
    return result;
  }
};

// obj/full/Pako/utils/strings.js
var Pako_strings = class _Pako_strings {
  static _init() {
    try {
      String.fromCharCode.apply(null, new Uint8Array(1));
    } catch (__) {
      _Pako_strings.STR_APPLY_UIA_OK = false;
    }
    for (let q = 0; q < 256; q++) {
      _Pako_strings._utf8len[q] = q >= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1;
    }
    _Pako_strings._utf8len[254] = _Pako_strings._utf8len[254] = 1;
    return true;
  }
  // convert string to array (typed, when possible)
  static string2buf(str) {
    let buf;
    let c;
    let c2;
    let m_pos;
    let i;
    let str_len = str.length;
    let buf_len = 0;
    for (m_pos = 0; m_pos < str_len; m_pos++) {
      c = str.charCodeAt(m_pos);
      if ((c & 64512) === 55296 && m_pos + 1 < str_len) {
        c2 = str.charCodeAt(m_pos + 1);
        if ((c2 & 64512) === 56320) {
          c = 65536 + (c - 55296 << 10) + (c2 - 56320);
          m_pos++;
        }
      }
      buf_len += c < 128 ? 1 : c < 2048 ? 2 : c < 65536 ? 3 : 4;
    }
    buf = new Uint8Array(buf_len);
    for (i = 0, m_pos = 0; i < buf_len; m_pos++) {
      c = str.charCodeAt(m_pos);
      if ((c & 64512) === 55296 && m_pos + 1 < str_len) {
        c2 = str.charCodeAt(m_pos + 1);
        if ((c2 & 64512) === 56320) {
          c = 65536 + (c - 55296 << 10) + (c2 - 56320);
          m_pos++;
        }
      }
      if (c < 128) {
        buf[i++] = c;
      } else if (c < 2048) {
        buf[i++] = 192 | c >>> 6;
        buf[i++] = 128 | c & 63;
      } else if (c < 65536) {
        buf[i++] = 224 | c >>> 12;
        buf[i++] = 128 | c >>> 6 & 63;
        buf[i++] = 128 | c & 63;
      } else {
        buf[i++] = 240 | c >>> 18;
        buf[i++] = 128 | c >>> 12 & 63;
        buf[i++] = 128 | c >>> 6 & 63;
        buf[i++] = 128 | c & 63;
      }
    }
    return buf;
  }
  // Helper
  static buf2binstring(buf, len) {
    let result = "";
    for (let i = 0; i < len; i++) {
      result += String.fromCharCode(buf[i]);
    }
    return result;
  }
  // convert array to string
  static buf2string(buf, max) {
    let i;
    let out;
    const len = max || buf.length;
    const utf16buf = new Array(len * 2);
    for (out = 0, i = 0; i < len; ) {
      let c = buf[i++];
      if (c < 128) {
        utf16buf[out++] = c;
        continue;
      }
      let c_len = _Pako_strings._utf8len[c];
      if (c_len > 4) {
        utf16buf[out++] = 65533;
        i += c_len - 1;
        continue;
      }
      c &= c_len === 2 ? 31 : c_len === 3 ? 15 : 7;
      while (c_len > 1 && i < len) {
        c = c << 6 | buf[i++] & 63;
        c_len--;
      }
      if (c_len > 1) {
        utf16buf[out++] = 65533;
        continue;
      }
      if (c < 65536) {
        utf16buf[out++] = c;
      } else {
        c -= 65536;
        utf16buf[out++] = 55296 | c >> 10 & 1023;
        utf16buf[out++] = 56320 | c & 1023;
      }
    }
    return _Pako_strings.buf2binstring(utf16buf, out);
  }
  // Calculate max possible position in utf8 buffer,
  // that will not break sequence. If that's not possible
  // - (very small limits) return max size as is.
  //
  // buf[] - utf8 bytes array
  // max   - length limit (mandatory);
  static utf8border(buf, max) {
    max = max || buf.length;
    if (max > buf.length) {
      max = buf.length;
    }
    let pos = max - 1;
    while (pos >= 0 && (buf[pos] & 192) === 128) {
      pos--;
    }
    if (pos < 0) {
      return max;
    }
    if (pos === 0) {
      return max;
    }
    return pos + _Pako_strings._utf8len[buf[pos]] > max ? pos : max;
  }
};
Pako_strings.STR_APPLY_UIA_OK = true;
Pako_strings._utf8len = new Uint8Array(256);
Pako_strings.initDone = Pako_strings._init();

// obj/full/Pako/deflate.js
var Pako_deflate_option = class _Pako_deflate_option {
  constructor(CustOption) {
    this.level = zlib_constants.Z_DEFAULT_COMPRESSION;
    this.method = zlib_constants.Z_DEFLATED;
    this.chunkSize = 16384;
    this.windowBits = 15;
    this.memLevel = 8;
    this.strategy = zlib_constants.Z_DEFAULT_STRATEGY;
    this.raw = false;
    this.gzip = false;
    this.header = null;
    this.dictionary = null;
    this.ignore_os = false;
    _Pako_deflate_option.ApplyCustomOptions(this, CustOption);
  }
  static ApplyCustomOptions(StdOption, CustOption) {
    if (typeof CustOption != "undefined") {
      let stdKeys = Object.keys(StdOption);
      let custKeys = Object.keys(CustOption);
      for (let i = 0; i < custKeys.length; i++) {
        if (stdKeys.indexOf(custKeys[i]) < 0) {
          debugger;
          throw new Error("Invalid Pako option name '" + custKeys[i] + "', check Pako_deflate_option class");
        }
        let srcType = typeof Reflect.get(CustOption, custKeys[i]);
        let trgtType = typeof Reflect.get(StdOption, custKeys[i]);
        if (srcType != trgtType)
          throw new Error("Invalid Pako type for option '" + custKeys[i] + "' (expected '" + trgtType + "', got '" + srcType + "')");
        Reflect.set(StdOption, custKeys[i], Reflect.get(CustOption, custKeys[i]));
      }
    }
  }
};
var Pako_Deflate = class _Pako_Deflate {
  constructor(options) {
    this._dict_set = false;
    this.result = null;
    this.options = new Pako_deflate_option(options);
    let opt = this.options;
    if (opt.raw && opt.windowBits > 0) {
      opt.windowBits = -opt.windowBits;
    } else if (opt.gzip && opt.windowBits > 0 && opt.windowBits < 16) {
      opt.windowBits += 16;
    }
    this.err = 0;
    this.msg = "";
    this.ended = false;
    this.chunks = [];
    this.strm = new zlib_ZStream();
    this.strm.avail_out = 0;
    let status = zlib_deflate.deflateInit2(this.strm, opt.level, opt.method, opt.windowBits, opt.memLevel, opt.strategy);
    if (status !== zlib_constants.Z_OK) {
      throw new Error(zlib_messages.msg(status));
    }
    if (opt.header) {
      zlib_deflate.deflateSetHeader(this.strm, opt.header);
    }
    if (opt.dictionary) {
      let dict;
      if (typeof opt.dictionary === "string") {
        dict = Pako_strings.string2buf(opt.dictionary);
      } else if (toString.call(opt.dictionary) === "[object ArrayBuffer]") {
        dict = new Uint8Array(opt.dictionary);
      } else {
        dict = opt.dictionary;
      }
      status = zlib_deflate.deflateSetDictionary(this.strm, dict);
      if (status !== zlib_constants.Z_OK) {
        throw new Error(zlib_messages.msg(status));
      }
      this._dict_set = true;
    }
  }
  /**
   * Deflate#push(data[, flush_mode]) -> Boolean
   * - data (Uint8Array|ArrayBuffer|String): input data. Strings will be
   *   converted to utf8 byte sequence.
   * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
   *   See constants. Skipped or `false` means Z_NO_FLUSH, `true` means Z_FINISH.
   *
   * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with
   * new compressed chunks. Returns `true` on success. The last data block must
   * have `flush_mode` Z_FINISH (or `true`). That will flush internal pending
   * buffers and call [[Deflate#onEnd]].
   *
   * On fail call [[Deflate#onEnd]] with error code and return false.
   *
   * ##### Example
   *
   * ```javascript
   * push(chunk, false); // push one of data chunks
   * ...
   * push(chunk, true);  // push last chunk
   * ```
   **/
  push(data, flush_mode) {
    const strm = this.strm;
    const chunkSize = this.options.chunkSize;
    let status;
    let _flush_mode;
    if (this.ended) {
      return false;
    }
    if (flush_mode === ~~flush_mode) {
      _flush_mode = flush_mode;
    } else {
      _flush_mode = flush_mode === true ? zlib_constants.Z_FINISH : zlib_constants.Z_NO_FLUSH;
    }
    if (typeof data === "string") {
      strm.input = Pako_strings.string2buf(data);
    } else if (toString.call(data) === "[object ArrayBuffer]") {
      strm.input = new Uint8Array(data);
    } else {
      strm.input = data;
    }
    strm.next_in = 0;
    strm.avail_in = strm.input.length;
    for (; ; ) {
      if (strm.avail_out === 0) {
        strm.output = new Uint8Array(chunkSize);
        strm.next_out = 0;
        strm.avail_out = chunkSize;
      }
      if ((_flush_mode === zlib_constants.Z_SYNC_FLUSH || _flush_mode === zlib_constants.Z_FULL_FLUSH) && strm.avail_out <= 6) {
        this.onData(strm.output.subarray(0, strm.next_out));
        strm.avail_out = 0;
        continue;
      }
      status = zlib_deflate.deflate(strm, _flush_mode);
      if (status === zlib_constants.Z_STREAM_END) {
        if (strm.next_out > 0) {
          this.onData(strm.output.subarray(0, strm.next_out));
        }
        status = zlib_deflate.deflateEnd(this.strm);
        this.onEnd(status);
        this.ended = true;
        return status === zlib_constants.Z_OK;
      }
      if (strm.avail_out === 0) {
        this.onData(strm.output);
        continue;
      }
      if (_flush_mode > 0 && strm.next_out > 0) {
        this.onData(strm.output.subarray(0, strm.next_out));
        strm.avail_out = 0;
        continue;
      }
      if (strm.avail_in === 0)
        break;
    }
    return true;
  }
  /**
   * Deflate#onData(chunk) -> Void
   * - chunk (Uint8Array): output data.
   *
   * By default, stores data blocks in `chunks[]` property and glue
   * those in `onEnd`. Override this handler, if you need another behaviour.
   **/
  onData(chunk) {
    this.chunks.push(chunk);
  }
  /**
   * Deflate#onEnd(status) -> Void
   * - status (Number): deflate status. 0 (Z_OK) on success,
   *   other if not.
   *
   * Called once after you tell deflate that the input stream is
   * complete (Z_FINISH). By default - join collected chunks,
   * free memory and fill `results` / `err` properties.
   **/
  onEnd(status) {
    if (status === zlib_constants.Z_OK) {
      this.result = utils_common.flattenChunks(this.chunks);
    }
    this.chunks = [];
    this.err = status;
    this.msg = this.strm.msg;
  }
  /**
   * deflate(data[, options]) -> Uint8Array
   * - data (Uint8Array|String): input data to compress.
   * - options (Object): zlib deflate options.
   *
   * Compress `data` with deflate algorithm and `options`.
   *
   * Supported options are:
   *
   * - level
   * - windowBits
   * - memLevel
   * - strategy
   * - dictionary
   *
   * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
   * for more information on these.
   *
   * Sugar (options):
   *
   * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
   *   negative windowBits implicitly.
   *
   * ##### Example:
   *
   * ```javascript
   * const pako = require('pako')
   * const data = new Uint8Array([1,2,3,4,5,6,7,8,9]);
   *
   * console.log(pako.deflate(data));
   * ```
   **/
  static deflate(input, options) {
    const deflator = new _Pako_Deflate(options);
    deflator.push(input, true);
    if (deflator.err) {
      throw deflator.msg || zlib_messages.msg(deflator.err);
    }
    return deflator.result;
  }
  /**
   * deflateRaw(data[, options]) -> Uint8Array
   * - data (Uint8Array|String): input data to compress.
   * - options (Object): zlib deflate options.
   *
   * The same as [[deflate]], but creates raw data, without wrapper
   * (header and adler32 crc).
   **/
  static deflateRaw(input, options) {
    options.raw = true;
    return _Pako_Deflate.deflate(input, options);
  }
  /**
   * gzip(data[, options]) -> Uint8Array
   * - data (Uint8Array|String): input data to compress.
   * - options (Object): zlib deflate options.
   *
   * The same as [[deflate]], but create gzip wrapper instead of
   * deflate one.
   **/
  static gzip(input, options) {
    if (typeof options == "undefined")
      options = {};
    options.gzip = true;
    return _Pako_Deflate.deflate(input, options);
  }
};

// obj/full/Pako/zlib/adler32.js
var zlib_adler32 = class {
  static adler32(adler, buf, len, pos) {
    let s1 = adler & 65535 | 0, s2 = adler >>> 16 & 65535 | 0, n = 0;
    while (len !== 0) {
      n = len > 2e3 ? 2e3 : len;
      len -= n;
      do {
        s1 = s1 + buf[pos++] | 0;
        s2 = s2 + s1 | 0;
      } while (--n);
      s1 %= 65521;
      s2 %= 65521;
    }
    return s1 | s2 << 16 | 0;
  }
};

// obj/full/Pako/zlib/constants.js
var zlib_constants = class {
};
zlib_constants.Z_NO_FLUSH = 0;
zlib_constants.Z_PARTIAL_FLUSH = 1;
zlib_constants.Z_SYNC_FLUSH = 2;
zlib_constants.Z_FULL_FLUSH = 3;
zlib_constants.Z_FINISH = 4;
zlib_constants.Z_BLOCK = 5;
zlib_constants.Z_TREES = 6;
zlib_constants.Z_OK = 0;
zlib_constants.Z_STREAM_END = 1;
zlib_constants.Z_NEED_DICT = 2;
zlib_constants.Z_ERRNO = -1;
zlib_constants.Z_STREAM_ERROR = -2;
zlib_constants.Z_DATA_ERROR = -3;
zlib_constants.Z_MEM_ERROR = -4;
zlib_constants.Z_BUF_ERROR = -5;
zlib_constants.Z_NO_COMPRESSION = 0;
zlib_constants.Z_BEST_SPEED = 1;
zlib_constants.Z_BEST_COMPRESSION = 9;
zlib_constants.Z_DEFAULT_COMPRESSION = -1;
zlib_constants.Z_FILTERED = 1;
zlib_constants.Z_HUFFMAN_ONLY = 2;
zlib_constants.Z_RLE = 3;
zlib_constants.Z_FIXED = 4;
zlib_constants.Z_DEFAULT_STRATEGY = 0;
zlib_constants.Z_BINARY = 0;
zlib_constants.Z_TEXT = 1;
zlib_constants.Z_UNKNOWN = 2;
zlib_constants.Z_DEFLATED = 8;

// obj/full/Pako/zlib/crc32.js
var zlib_crc32 = class _zlib_crc32 {
  static makeTable() {
    let c;
    let table = new Int32Array(256);
    for (let n = 0; n < 256; n++) {
      c = n;
      for (let k = 0; k < 8; k++) {
        c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
      }
      table[n] = c;
    }
    return table;
  }
  static crc32(crc, buf, len, pos) {
    let t = _zlib_crc32.crcTable;
    let end = pos + len;
    crc ^= -1;
    for (let i = pos; i < end; i++) {
      crc = crc >>> 8 ^ t[(crc ^ buf[i]) & 255];
    }
    return crc ^ -1;
  }
};
zlib_crc32.crcTable = zlib_crc32.makeTable();

// obj/full/Pako/zlib/deflate.js
var zlib_config = class {
  constructor(good_length, max_lazy, nice_length, max_chain, func) {
    this.good_length = good_length;
    this.max_lazy = max_lazy;
    this.nice_length = nice_length;
    this.max_chain = max_chain;
    this.func = func;
  }
};
var zlib_DeflateState = class {
  /* Number of valid bits in bi_buf.  All bits above the last valid bit
   * are always zero.
   */
  // Used for window memory init. We safely ignore it for JS. That makes
  // sense only for pointers and memory check tools.
  //this.high_water = 0;
  /* High water mark offset in window for initialized bytes -- bytes above
   * this are set to zero in order to avoid memory check warnings when
   * longest match routines access bytes past the input.  This is then
   * updated to the new high water mark.
   */
  constructor() {
    this.strm = null;
    this.status = 0;
    this.pending_buf = null;
    this.pending_buf_size = 0;
    this.pending_out = 0;
    this.pending = 0;
    this.wrap = 0;
    this.gzhead = null;
    this.gzindex = 0;
    this.method = zlib_constants.Z_DEFLATED;
    this.last_flush = -1;
    this.w_size = 0;
    this.w_bits = 0;
    this.w_mask = 0;
    this.window = null;
    this.window_size = 0;
    this.prev = null;
    this.head = null;
    this.ins_h = 0;
    this.hash_size = 0;
    this.hash_bits = 0;
    this.hash_mask = 0;
    this.hash_shift = 0;
    this.block_start = 0;
    this.match_length = 0;
    this.prev_match = 0;
    this.match_available = 0;
    this.strstart = 0;
    this.match_start = 0;
    this.lookahead = 0;
    this.prev_length = 0;
    this.max_chain_length = 0;
    this.max_lazy_match = 0;
    this.level = 0;
    this.strategy = 0;
    this.good_match = 0;
    this.nice_match = 0;
    this.dyn_ltree = new Uint16Array(zlib_Pako_trees.HEAP_SIZE * 2);
    this.dyn_dtree = new Uint16Array((2 * zlib_Pako_trees.D_CODES + 1) * 2);
    this.bl_tree = new Uint16Array((2 * zlib_Pako_trees.BL_CODES + 1) * 2);
    this.l_desc = null;
    this.d_desc = null;
    this.bl_desc = null;
    this.bl_count = new Uint16Array(zlib_Pako_trees.MAX_BITS + 1);
    this.heap = new Uint16Array(2 * zlib_Pako_trees.L_CODES + 1);
    this.heap_len = 0;
    this.heap_max = 0;
    this.depth = new Uint16Array(2 * zlib_Pako_trees.L_CODES + 1);
    this.l_buf = 0;
    this.lit_bufsize = 0;
    this.last_lit = 0;
    this.d_buf = 0;
    this.opt_len = 0;
    this.static_len = 0;
    this.matches = 0;
    this.insert = 0;
    this.bi_buf = 0;
    this.bi_valid = 0;
    zlib_Pako_trees.zero(this.dyn_ltree);
    zlib_Pako_trees.zero(this.dyn_dtree);
    zlib_Pako_trees.zero(this.bl_tree);
    zlib_Pako_trees.zero(this.heap);
    zlib_Pako_trees.zero(this.depth);
  }
};
var zlib_deflate = class _zlib_deflate {
  static err(strm, errorCode) {
    strm.msg = zlib_messages.msg(errorCode);
    return errorCode;
  }
  static rank(f) {
    return (f << 1) - (f > 4 ? 9 : 0);
  }
  zero(buf) {
    let len = buf.length;
    while (--len >= 0) {
      buf[len] = 0;
    }
  }
  /* eslint-disable new-cap */
  static HASH_ZLIB(s, prev, data) {
    return (prev << s.hash_shift ^ data) & s.hash_mask;
  }
  // This hash causes less collisions, https://github.com/nodeca/pako/issues/135
  // But breaks binary compatibility
  //let HASH_FAST = (s, prev, data) => ((prev << 8) + (prev >> 8) + (data << 4)) & s.hash_mask;
  static HASH(s, prev, data) {
    return (prev << s.hash_shift ^ data) & s.hash_mask;
  }
  /* =========================================================================
   * Flush as much pending output as possible. All deflate() output goes
   * through this function so some applications may wish to modify it
   * to avoid allocating a large strm->output buffer and copying into it.
   * (See also read_buf()).
   */
  static flush_pending(strm) {
    const s = strm.state;
    let len = s.pending;
    if (len > strm.avail_out) {
      len = strm.avail_out;
    }
    if (len === 0) {
      return;
    }
    strm.output.set(s.pending_buf.subarray(s.pending_out, s.pending_out + len), strm.next_out);
    strm.next_out += len;
    s.pending_out += len;
    strm.total_out += len;
    strm.avail_out -= len;
    s.pending -= len;
    if (s.pending === 0) {
      s.pending_out = 0;
    }
  }
  static flush_block_only(s, last) {
    zlib_Pako_trees._tr_flush_block(s, s.block_start >= 0 ? s.block_start : -1, s.strstart - s.block_start, last);
    s.block_start = s.strstart;
    _zlib_deflate.flush_pending(s.strm);
  }
  static put_byte(s, b) {
    s.pending_buf[s.pending++] = b;
  }
  /* =========================================================================
   * Put a short in the pending buffer. The 16-bit value is put in MSB order.
   * IN assertion: the stream state is correct and there is enough room in
   * pending_buf.
   */
  static putShortMSB(s, b) {
    s.pending_buf[s.pending++] = b >>> 8 & 255;
    s.pending_buf[s.pending++] = b & 255;
  }
  /* ===========================================================================
   * Read a new buffer from the current input stream, update the adler32
   * and total number of bytes read.  All deflate() input goes through
   * this function so some applications may wish to modify it to avoid
   * allocating a large strm->input buffer and copying from it.
   * (See also flush_pending()).
   */
  static read_buf(strm, buf, start, size) {
    let len = strm.avail_in;
    if (len > size) {
      len = size;
    }
    if (len === 0) {
      return 0;
    }
    strm.avail_in -= len;
    buf.set(strm.input.subarray(strm.next_in, strm.next_in + len), start);
    if (strm.state.wrap === 1) {
      strm.adler = zlib_adler32.adler32(strm.adler, buf, len, start);
    } else if (strm.state.wrap === 2) {
      strm.adler = zlib_crc32.crc32(strm.adler, buf, len, start);
    }
    strm.next_in += len;
    strm.total_in += len;
    return len;
  }
  /* ===========================================================================
   * Set match_start to the longest match starting at the given string and
   * return its length. Matches shorter or equal to prev_length are discarded,
   * in which case the result is equal to prev_length and match_start is
   * garbage.
   * IN assertions: cur_match is the head of the hash chain for the current
   *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
   * OUT assertion: the match length is not greater than s->lookahead.
   */
  static longest_match(s, cur_match) {
    let chain_length = s.max_chain_length;
    let scan = s.strstart;
    let match;
    let len;
    let best_len = s.prev_length;
    let nice_match = s.nice_match;
    const limit = s.strstart > s.w_size - _zlib_deflate.MIN_LOOKAHEAD ? s.strstart - (s.w_size - _zlib_deflate.MIN_LOOKAHEAD) : 0;
    const _win = s.window;
    const wmask = s.w_mask;
    const prev = s.prev;
    const strend = s.strstart + zlib_Pako_trees.MAX_MATCH;
    let scan_end1 = _win[scan + best_len - 1];
    let scan_end = _win[scan + best_len];
    if (s.prev_length >= s.good_match) {
      chain_length >>= 2;
    }
    if (nice_match > s.lookahead) {
      nice_match = s.lookahead;
    }
    do {
      match = cur_match;
      if (_win[match + best_len] !== scan_end || _win[match + best_len - 1] !== scan_end1 || _win[match] !== _win[scan] || _win[++match] !== _win[scan + 1]) {
        continue;
      }
      scan += 2;
      match++;
      do {
      } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && scan < strend);
      len = zlib_Pako_trees.MAX_MATCH - (strend - scan);
      scan = strend - zlib_Pako_trees.MAX_MATCH;
      if (len > best_len) {
        s.match_start = cur_match;
        best_len = len;
        if (len >= nice_match) {
          break;
        }
        scan_end1 = _win[scan + best_len - 1];
        scan_end = _win[scan + best_len];
      }
    } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0);
    if (best_len <= s.lookahead) {
      return best_len;
    }
    return s.lookahead;
  }
  /* ===========================================================================
   * Fill the window when the lookahead becomes insufficient.
   * Updates strstart and lookahead.
   *
   * IN assertion: lookahead < MIN_LOOKAHEAD
   * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
   *    At least one byte has been read, or avail_in == 0; reads are
   *    performed for at least two bytes (required for the zip translate_eol
   *    option -- not supported here).
   */
  static fill_window(s) {
    const _w_size = s.w_size;
    let p;
    let n;
    let m;
    let more;
    let str;
    do {
      more = s.window_size - s.lookahead - s.strstart;
      if (s.strstart >= _w_size + (_w_size - _zlib_deflate.MIN_LOOKAHEAD)) {
        s.window.set(s.window.subarray(_w_size, _w_size + _w_size), 0);
        s.match_start -= _w_size;
        s.strstart -= _w_size;
        s.block_start -= _w_size;
        n = s.hash_size;
        p = n;
        do {
          m = s.head[--p];
          s.head[p] = m >= _w_size ? m - _w_size : 0;
        } while (--n);
        n = _w_size;
        p = n;
        do {
          m = s.prev[--p];
          s.prev[p] = m >= _w_size ? m - _w_size : 0;
        } while (--n);
        more += _w_size;
      }
      if (s.strm.avail_in === 0) {
        break;
      }
      n = _zlib_deflate.read_buf(s.strm, s.window, s.strstart + s.lookahead, more);
      s.lookahead += n;
      if (s.lookahead + s.insert >= zlib_Pako_trees.MIN_MATCH) {
        str = s.strstart - s.insert;
        s.ins_h = s.window[str];
        s.ins_h = _zlib_deflate.HASH(s, s.ins_h, s.window[str + 1]);
        while (s.insert) {
          s.ins_h = _zlib_deflate.HASH(s, s.ins_h, s.window[str + zlib_Pako_trees.MIN_MATCH - 1]);
          s.prev[str & s.w_mask] = s.head[s.ins_h];
          s.head[s.ins_h] = str;
          str++;
          s.insert--;
          if (s.lookahead + s.insert < zlib_Pako_trees.MIN_MATCH) {
            break;
          }
        }
      }
    } while (s.lookahead < _zlib_deflate.MIN_LOOKAHEAD && s.strm.avail_in !== 0);
  }
  /* ===========================================================================
   * Copy without compression as much as possible from the input stream, return
   * the current block state.
   * This function does not insert new strings in the dictionary since
   * uncompressible data is probably not useful. This function is used
   * only for the level=0 compression option.
   * NOTE: this function should be optimized to avoid extra copying from
   * window to pending_buf.
   */
  static deflate_stored(s, flush) {
    let max_block_size = 65535;
    if (max_block_size > s.pending_buf_size - 5) {
      max_block_size = s.pending_buf_size - 5;
    }
    for (; ; ) {
      if (s.lookahead <= 1) {
        _zlib_deflate.fill_window(s);
        if (s.lookahead === 0 && flush === zlib_constants.Z_NO_FLUSH) {
          return _zlib_deflate.BS_NEED_MORE;
        }
        if (s.lookahead === 0) {
          break;
        }
      }
      s.strstart += s.lookahead;
      s.lookahead = 0;
      const max_start = s.block_start + max_block_size;
      if (s.strstart === 0 || s.strstart >= max_start) {
        s.lookahead = s.strstart - max_start;
        s.strstart = max_start;
        _zlib_deflate.flush_block_only(s, false);
        if (s.strm.avail_out === 0) {
          return _zlib_deflate.BS_NEED_MORE;
        }
      }
      if (s.strstart - s.block_start >= s.w_size - _zlib_deflate.MIN_LOOKAHEAD) {
        _zlib_deflate.flush_block_only(s, false);
        if (s.strm.avail_out === 0) {
          return _zlib_deflate.BS_NEED_MORE;
        }
      }
    }
    s.insert = 0;
    if (flush === zlib_constants.Z_FINISH) {
      _zlib_deflate.flush_block_only(s, true);
      if (s.strm.avail_out === 0) {
        return _zlib_deflate.BS_FINISH_STARTED;
      }
      return _zlib_deflate.BS_FINISH_DONE;
    }
    if (s.strstart > s.block_start) {
      _zlib_deflate.flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return _zlib_deflate.BS_NEED_MORE;
      }
    }
    return _zlib_deflate.BS_NEED_MORE;
  }
  /* ===========================================================================
   * Same as above, but achieves better compression. We use a lazy
   * evaluation for matches: a match is finally adopted only if there is
   * no better match at the next window position.
   */
  static deflate_slow(s, flush) {
    let hash_head;
    let bflush;
    let max_insert;
    for (; ; ) {
      if (s.lookahead < _zlib_deflate.MIN_LOOKAHEAD) {
        _zlib_deflate.fill_window(s);
        if (s.lookahead < _zlib_deflate.MIN_LOOKAHEAD && flush === zlib_constants.Z_NO_FLUSH) {
          return _zlib_deflate.BS_NEED_MORE;
        }
        if (s.lookahead === 0) {
          break;
        }
      }
      hash_head = 0;
      if (s.lookahead >= zlib_Pako_trees.MIN_MATCH) {
        s.ins_h = _zlib_deflate.HASH(s, s.ins_h, s.window[s.strstart + zlib_Pako_trees.MIN_MATCH - 1]);
        hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
        s.head[s.ins_h] = s.strstart;
      }
      s.prev_length = s.match_length;
      s.prev_match = s.match_start;
      s.match_length = zlib_Pako_trees.MIN_MATCH - 1;
      if (hash_head !== 0 && s.prev_length < s.max_lazy_match && s.strstart - hash_head <= s.w_size - _zlib_deflate.MIN_LOOKAHEAD) {
        s.match_length = _zlib_deflate.longest_match(s, hash_head);
        if (s.match_length <= 5 && (s.strategy === zlib_constants.Z_FILTERED || s.match_length === zlib_Pako_trees.MIN_MATCH && s.strstart - s.match_start > 4096)) {
          s.match_length = zlib_Pako_trees.MIN_MATCH - 1;
        }
      }
      if (s.prev_length >= zlib_Pako_trees.MIN_MATCH && s.match_length <= s.prev_length) {
        max_insert = s.strstart + s.lookahead - zlib_Pako_trees.MIN_MATCH;
        bflush = zlib_Pako_trees._tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - zlib_Pako_trees.MIN_MATCH);
        s.lookahead -= s.prev_length - 1;
        s.prev_length -= 2;
        do {
          if (++s.strstart <= max_insert) {
            s.ins_h = _zlib_deflate.HASH(s, s.ins_h, s.window[s.strstart + zlib_Pako_trees.MIN_MATCH - 1]);
            hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
            s.head[s.ins_h] = s.strstart;
          }
        } while (--s.prev_length !== 0);
        s.match_available = 0;
        s.match_length = zlib_Pako_trees.MIN_MATCH - 1;
        s.strstart++;
        if (bflush) {
          _zlib_deflate.flush_block_only(s, false);
          if (s.strm.avail_out === 0) {
            return _zlib_deflate.BS_NEED_MORE;
          }
        }
      } else if (s.match_available) {
        bflush = zlib_Pako_trees._tr_tally(s, 0, s.window[s.strstart - 1]);
        if (bflush) {
          _zlib_deflate.flush_block_only(s, false);
        }
        s.strstart++;
        s.lookahead--;
        if (s.strm.avail_out === 0) {
          return _zlib_deflate.BS_NEED_MORE;
        }
      } else {
        s.match_available = 1;
        s.strstart++;
        s.lookahead--;
      }
    }
    if (s.match_available) {
      bflush = zlib_Pako_trees._tr_tally(s, 0, s.window[s.strstart - 1]);
      s.match_available = 0;
    }
    s.insert = s.strstart < zlib_Pako_trees.MIN_MATCH - 1 ? s.strstart : zlib_Pako_trees.MIN_MATCH - 1;
    if (flush === zlib_constants.Z_FINISH) {
      _zlib_deflate.flush_block_only(s, true);
      if (s.strm.avail_out === 0) {
        return _zlib_deflate.BS_FINISH_STARTED;
      }
      return _zlib_deflate.BS_FINISH_DONE;
    }
    if (s.last_lit) {
      _zlib_deflate.flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return _zlib_deflate.BS_NEED_MORE;
      }
    }
    return _zlib_deflate.BS_BLOCK_DONE;
  }
  /* ===========================================================================
   * For Pako_constants.Z_RLE, simply look for runs of bytes, generate matches only of distance
   * one.  Do not maintain a hash table.  (It will be regenerated if this run of
   * deflate switches away from Pako_constants.Z_RLE.)
   */
  static deflate_rle(s, flush) {
    let bflush;
    let prev;
    let scan;
    let strend;
    const _win = s.window;
    for (; ; ) {
      if (s.lookahead <= zlib_Pako_trees.MAX_MATCH) {
        _zlib_deflate.fill_window(s);
        if (s.lookahead <= zlib_Pako_trees.MAX_MATCH && flush === zlib_constants.Z_NO_FLUSH) {
          return _zlib_deflate.BS_NEED_MORE;
        }
        if (s.lookahead === 0) {
          break;
        }
      }
      s.match_length = 0;
      if (s.lookahead >= zlib_Pako_trees.MIN_MATCH && s.strstart > 0) {
        scan = s.strstart - 1;
        prev = _win[scan];
        if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) {
          strend = s.strstart + zlib_Pako_trees.MAX_MATCH;
          do {
          } while (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && scan < strend);
          s.match_length = zlib_Pako_trees.MAX_MATCH - (strend - scan);
          if (s.match_length > s.lookahead) {
            s.match_length = s.lookahead;
          }
        }
      }
      if (s.match_length >= zlib_Pako_trees.MIN_MATCH) {
        bflush = zlib_Pako_trees._tr_tally(s, 1, s.match_length - zlib_Pako_trees.MIN_MATCH);
        s.lookahead -= s.match_length;
        s.strstart += s.match_length;
        s.match_length = 0;
      } else {
        bflush = zlib_Pako_trees._tr_tally(s, 0, s.window[s.strstart]);
        s.lookahead--;
        s.strstart++;
      }
      if (bflush) {
        _zlib_deflate.flush_block_only(s, false);
        if (s.strm.avail_out === 0) {
          return _zlib_deflate.BS_NEED_MORE;
        }
      }
    }
    s.insert = 0;
    if (flush === zlib_constants.Z_FINISH) {
      _zlib_deflate.flush_block_only(s, true);
      if (s.strm.avail_out === 0) {
        return _zlib_deflate.BS_FINISH_STARTED;
      }
      return _zlib_deflate.BS_FINISH_DONE;
    }
    if (s.last_lit) {
      _zlib_deflate.flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return _zlib_deflate.BS_NEED_MORE;
      }
    }
    return _zlib_deflate.BS_BLOCK_DONE;
  }
  /* ===========================================================================
   * For Pako_constants.Z_HUFFMAN_ONLY, do not look for matches.  Do not maintain a hash table.
   * (It will be regenerated if this run of deflate switches away from Huffman.)
   */
  static deflate_huff(s, flush) {
    let bflush;
    for (; ; ) {
      if (s.lookahead === 0) {
        _zlib_deflate.fill_window(s);
        if (s.lookahead === 0) {
          if (flush === zlib_constants.Z_NO_FLUSH) {
            return _zlib_deflate.BS_NEED_MORE;
          }
          break;
        }
      }
      s.match_length = 0;
      bflush = zlib_Pako_trees._tr_tally(s, 0, s.window[s.strstart]);
      s.lookahead--;
      s.strstart++;
      if (bflush) {
        _zlib_deflate.flush_block_only(s, false);
        if (s.strm.avail_out === 0) {
          return _zlib_deflate.BS_NEED_MORE;
        }
      }
    }
    s.insert = 0;
    if (flush === zlib_constants.Z_FINISH) {
      _zlib_deflate.flush_block_only(s, true);
      if (s.strm.avail_out === 0) {
        return _zlib_deflate.BS_FINISH_STARTED;
      }
      return _zlib_deflate.BS_FINISH_DONE;
    }
    if (s.last_lit) {
      _zlib_deflate.flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return _zlib_deflate.BS_NEED_MORE;
      }
    }
    return _zlib_deflate.BS_BLOCK_DONE;
  }
  /* ===========================================================================
   * Initialize the "longest match" routines for a new zlib stream
   */
  static lm_init(s) {
    s.window_size = 2 * s.w_size;
    zlib_Pako_trees.zero(s.head);
    s.max_lazy_match = _zlib_deflate.configuration_table[s.level].max_lazy;
    s.good_match = _zlib_deflate.configuration_table[s.level].good_length;
    s.nice_match = _zlib_deflate.configuration_table[s.level].nice_length;
    s.max_chain_length = _zlib_deflate.configuration_table[s.level].max_chain;
    s.strstart = 0;
    s.block_start = 0;
    s.lookahead = 0;
    s.insert = 0;
    s.match_length = s.prev_length = zlib_Pako_trees.MIN_MATCH - 1;
    s.match_available = 0;
    s.ins_h = 0;
  }
  static deflateResetKeep(strm) {
    if (!strm || !strm.state) {
      return _zlib_deflate.err(strm, zlib_constants.Z_STREAM_ERROR);
    }
    strm.total_in = strm.total_out = 0;
    strm.data_type = zlib_constants.Z_UNKNOWN;
    let s = strm.state;
    s.pending = 0;
    s.pending_out = 0;
    if (s.wrap < 0) {
      s.wrap = -s.wrap;
    }
    s.status = s.wrap ? _zlib_deflate.INIT_STATE : _zlib_deflate.BUSY_STATE;
    strm.adler = s.wrap === 2 ? 0 : 1;
    s.last_flush = zlib_constants.Z_NO_FLUSH;
    zlib_Pako_trees._tr_init(s);
    return zlib_constants.Z_OK;
  }
  static deflateReset(strm) {
    const ret = _zlib_deflate.deflateResetKeep(strm);
    if (ret === zlib_constants.Z_OK) {
      _zlib_deflate.lm_init(strm.state);
    }
    return ret;
  }
  static deflateSetHeader(strm, head) {
    if (!strm || !strm.state) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    if (strm.state.wrap !== 2) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    strm.state.gzhead = head;
    return zlib_constants.Z_OK;
  }
  static deflateInit2(strm, level, method, windowBits, memLevel, strategy) {
    if (!strm) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    let wrap = 1;
    if (level === zlib_constants.Z_DEFAULT_COMPRESSION) {
      level = 6;
    }
    if (windowBits < 0) {
      wrap = 0;
      windowBits = -windowBits;
    } else if (windowBits > 15) {
      wrap = 2;
      windowBits -= 16;
    }
    if (memLevel < 1 || memLevel > _zlib_deflate.MAX_MEM_LEVEL || method !== zlib_constants.Z_DEFLATED || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > zlib_constants.Z_FIXED) {
      return _zlib_deflate.err(strm, zlib_constants.Z_STREAM_ERROR);
    }
    if (windowBits === 8) {
      windowBits = 9;
    }
    const s = new zlib_DeflateState();
    strm.state = s;
    s.strm = strm;
    s.wrap = wrap;
    s.gzhead = null;
    s.w_bits = windowBits;
    s.w_size = 1 << s.w_bits;
    s.w_mask = s.w_size - 1;
    s.hash_bits = memLevel + 7;
    s.hash_size = 1 << s.hash_bits;
    s.hash_mask = s.hash_size - 1;
    s.hash_shift = ~~((s.hash_bits + zlib_Pako_trees.MIN_MATCH - 1) / zlib_Pako_trees.MIN_MATCH);
    s.window = new Uint8Array(s.w_size * 2);
    s.head = new Uint16Array(s.hash_size);
    s.prev = new Uint16Array(s.w_size);
    s.lit_bufsize = 1 << memLevel + 6;
    s.pending_buf_size = s.lit_bufsize * 4;
    s.pending_buf = new Uint8Array(s.pending_buf_size);
    s.d_buf = 1 * s.lit_bufsize;
    s.l_buf = (1 + 2) * s.lit_bufsize;
    s.level = level;
    s.strategy = strategy;
    s.method = method;
    return _zlib_deflate.deflateReset(strm);
  }
  static deflateInit(strm, level) {
    return _zlib_deflate.deflateInit2(strm, level, zlib_constants.Z_DEFLATED, _zlib_deflate.MAX_WBITS, _zlib_deflate.DEF_MEM_LEVEL, zlib_constants.Z_DEFAULT_STRATEGY);
  }
  static deflate(strm, flush) {
    let beg, val;
    if (!strm || !strm.state || flush > zlib_constants.Z_BLOCK || flush < 0) {
      return strm ? _zlib_deflate.err(strm, zlib_constants.Z_STREAM_ERROR) : zlib_constants.Z_STREAM_ERROR;
    }
    let s = strm.state;
    if (!strm.output || !strm.input && strm.avail_in !== 0 || s.status === _zlib_deflate.FINISH_STATE && flush !== zlib_constants.Z_FINISH) {
      return _zlib_deflate.err(strm, strm.avail_out === 0 ? zlib_constants.Z_BUF_ERROR : zlib_constants.Z_STREAM_ERROR);
    }
    s.strm = strm;
    const old_flush = s.last_flush;
    s.last_flush = flush;
    if (s.status === _zlib_deflate.INIT_STATE) {
      if (s.wrap === 2) {
        strm.adler = 0;
        _zlib_deflate.put_byte(s, 31);
        _zlib_deflate.put_byte(s, 139);
        _zlib_deflate.put_byte(s, 8);
        if (!s.gzhead) {
          _zlib_deflate.put_byte(s, 0);
          _zlib_deflate.put_byte(s, 0);
          _zlib_deflate.put_byte(s, 0);
          _zlib_deflate.put_byte(s, 0);
          _zlib_deflate.put_byte(s, 0);
          _zlib_deflate.put_byte(s, s.level === 9 ? 2 : s.strategy >= zlib_constants.Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0);
          _zlib_deflate.put_byte(s, _zlib_deflate.OS_CODE);
          s.status = _zlib_deflate.BUSY_STATE;
        } else {
          _zlib_deflate.put_byte(s, (s.gzhead.text ? 1 : 0) + (s.gzhead.hcrc ? 2 : 0) + (!s.gzhead.extra ? 0 : 4) + (!s.gzhead.name ? 0 : 8) + (!s.gzhead.comment ? 0 : 16));
          _zlib_deflate.put_byte(s, s.gzhead.time & 255);
          _zlib_deflate.put_byte(s, s.gzhead.time >> 8 & 255);
          _zlib_deflate.put_byte(s, s.gzhead.time >> 16 & 255);
          _zlib_deflate.put_byte(s, s.gzhead.time >> 24 & 255);
          _zlib_deflate.put_byte(s, s.level === 9 ? 2 : s.strategy >= zlib_constants.Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0);
          _zlib_deflate.put_byte(s, s.gzhead.os & 255);
          if (s.gzhead.extra && s.gzhead.extra.length) {
            _zlib_deflate.put_byte(s, s.gzhead.extra.length & 255);
            _zlib_deflate.put_byte(s, s.gzhead.extra.length >> 8 & 255);
          }
          if (s.gzhead.hcrc) {
            strm.adler = zlib_crc32.crc32(strm.adler, s.pending_buf, s.pending, 0);
          }
          s.gzindex = 0;
          s.status = _zlib_deflate.EXTRA_STATE;
        }
      } else {
        let header = zlib_constants.Z_DEFLATED + (s.w_bits - 8 << 4) << 8;
        let level_flags = -1;
        if (s.strategy >= zlib_constants.Z_HUFFMAN_ONLY || s.level < 2) {
          level_flags = 0;
        } else if (s.level < 6) {
          level_flags = 1;
        } else if (s.level === 6) {
          level_flags = 2;
        } else {
          level_flags = 3;
        }
        header |= level_flags << 6;
        if (s.strstart !== 0) {
          header |= _zlib_deflate.PRESET_DICT;
        }
        header += 31 - header % 31;
        s.status = _zlib_deflate.BUSY_STATE;
        _zlib_deflate.putShortMSB(s, header);
        if (s.strstart !== 0) {
          _zlib_deflate.putShortMSB(s, strm.adler >>> 16);
          _zlib_deflate.putShortMSB(s, strm.adler & 65535);
        }
        strm.adler = 1;
      }
    }
    if (s.status === _zlib_deflate.EXTRA_STATE) {
      if (s.gzhead.extra) {
        beg = s.pending;
        while (s.gzindex < (s.gzhead.extra.length & 65535)) {
          if (s.pending === s.pending_buf_size) {
            if (s.gzhead.hcrc && s.pending > beg) {
              strm.adler = zlib_crc32.crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
            }
            _zlib_deflate.flush_pending(strm);
            beg = s.pending;
            if (s.pending === s.pending_buf_size) {
              break;
            }
          }
          _zlib_deflate.put_byte(s, s.gzhead.extra[s.gzindex] & 255);
          s.gzindex++;
        }
        if (s.gzhead.hcrc && s.pending > beg) {
          strm.adler = zlib_crc32.crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
        }
        if (s.gzindex === s.gzhead.extra.length) {
          s.gzindex = 0;
          s.status = _zlib_deflate.NAME_STATE;
        }
      } else {
        s.status = _zlib_deflate.NAME_STATE;
      }
    }
    if (s.status === _zlib_deflate.NAME_STATE) {
      if (s.gzhead.name) {
        beg = s.pending;
        do {
          if (s.pending === s.pending_buf_size) {
            if (s.gzhead.hcrc && s.pending > beg) {
              strm.adler = zlib_crc32.crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
            }
            _zlib_deflate.flush_pending(strm);
            beg = s.pending;
            if (s.pending === s.pending_buf_size) {
              val = 1;
              break;
            }
          }
          if (s.gzindex < s.gzhead.name.length) {
            val = s.gzhead.name.charCodeAt(s.gzindex++) & 255;
          } else {
            val = 0;
          }
          _zlib_deflate.put_byte(s, val);
        } while (val !== 0);
        if (s.gzhead.hcrc && s.pending > beg) {
          strm.adler = zlib_crc32.crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
        }
        if (val === 0) {
          s.gzindex = 0;
          s.status = _zlib_deflate.COMMENT_STATE;
        }
      } else {
        s.status = _zlib_deflate.COMMENT_STATE;
      }
    }
    if (s.status === _zlib_deflate.COMMENT_STATE) {
      if (s.gzhead.comment) {
        beg = s.pending;
        do {
          if (s.pending === s.pending_buf_size) {
            if (s.gzhead.hcrc && s.pending > beg) {
              strm.adler = zlib_crc32.crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
            }
            _zlib_deflate.flush_pending(strm);
            beg = s.pending;
            if (s.pending === s.pending_buf_size) {
              val = 1;
              break;
            }
          }
          if (s.gzindex < s.gzhead.comment.length) {
            val = s.gzhead.comment.charCodeAt(s.gzindex++) & 255;
          } else {
            val = 0;
          }
          _zlib_deflate.put_byte(s, val);
        } while (val !== 0);
        if (s.gzhead.hcrc && s.pending > beg) {
          strm.adler = zlib_crc32.crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
        }
        if (val === 0) {
          s.status = _zlib_deflate.HCRC_STATE;
        }
      } else {
        s.status = _zlib_deflate.HCRC_STATE;
      }
    }
    if (s.status === _zlib_deflate.HCRC_STATE) {
      if (s.gzhead.hcrc) {
        if (s.pending + 2 > s.pending_buf_size) {
          _zlib_deflate.flush_pending(strm);
        }
        if (s.pending + 2 <= s.pending_buf_size) {
          _zlib_deflate.put_byte(s, strm.adler & 255);
          _zlib_deflate.put_byte(s, strm.adler >> 8 & 255);
          strm.adler = 0;
          s.status = _zlib_deflate.BUSY_STATE;
        }
      } else {
        s.status = _zlib_deflate.BUSY_STATE;
      }
    }
    if (s.pending !== 0) {
      _zlib_deflate.flush_pending(strm);
      if (strm.avail_out === 0) {
        s.last_flush = -1;
        return zlib_constants.Z_OK;
      }
    } else if (strm.avail_in === 0 && _zlib_deflate.rank(flush) <= _zlib_deflate.rank(old_flush) && flush !== zlib_constants.Z_FINISH) {
      return _zlib_deflate.err(strm, zlib_constants.Z_BUF_ERROR);
    }
    if (s.status === _zlib_deflate.FINISH_STATE && strm.avail_in !== 0) {
      return _zlib_deflate.err(strm, zlib_constants.Z_BUF_ERROR);
    }
    if (strm.avail_in !== 0 || s.lookahead !== 0 || flush !== zlib_constants.Z_NO_FLUSH && s.status !== _zlib_deflate.FINISH_STATE) {
      let bstate = s.strategy === zlib_constants.Z_HUFFMAN_ONLY ? _zlib_deflate.deflate_huff(s, flush) : s.strategy === zlib_constants.Z_RLE ? _zlib_deflate.deflate_rle(s, flush) : _zlib_deflate.configuration_table[s.level].func(s, flush);
      if (bstate === _zlib_deflate.BS_FINISH_STARTED || bstate === _zlib_deflate.BS_FINISH_DONE) {
        s.status = _zlib_deflate.FINISH_STATE;
      }
      if (bstate === _zlib_deflate.BS_NEED_MORE || bstate === _zlib_deflate.BS_FINISH_STARTED) {
        if (strm.avail_out === 0) {
          s.last_flush = -1;
        }
        return zlib_constants.Z_OK;
      }
      if (bstate === _zlib_deflate.BS_BLOCK_DONE) {
        if (flush === zlib_constants.Z_PARTIAL_FLUSH) {
          zlib_Pako_trees._tr_align(s);
        } else if (flush !== zlib_constants.Z_BLOCK) {
          zlib_Pako_trees._tr_stored_block(s, 0, 0, false);
          if (flush === zlib_constants.Z_FULL_FLUSH) {
            zlib_Pako_trees.zero(s.head);
            if (s.lookahead === 0) {
              s.strstart = 0;
              s.block_start = 0;
              s.insert = 0;
            }
          }
        }
        _zlib_deflate.flush_pending(strm);
        if (strm.avail_out === 0) {
          s.last_flush = -1;
          return zlib_constants.Z_OK;
        }
      }
    }
    if (flush !== zlib_constants.Z_FINISH) {
      return zlib_constants.Z_OK;
    }
    if (s.wrap <= 0) {
      return zlib_constants.Z_STREAM_END;
    }
    if (s.wrap === 2) {
      _zlib_deflate.put_byte(s, strm.adler & 255);
      _zlib_deflate.put_byte(s, strm.adler >> 8 & 255);
      _zlib_deflate.put_byte(s, strm.adler >> 16 & 255);
      _zlib_deflate.put_byte(s, strm.adler >> 24 & 255);
      _zlib_deflate.put_byte(s, strm.total_in & 255);
      _zlib_deflate.put_byte(s, strm.total_in >> 8 & 255);
      _zlib_deflate.put_byte(s, strm.total_in >> 16 & 255);
      _zlib_deflate.put_byte(s, strm.total_in >> 24 & 255);
    } else {
      _zlib_deflate.putShortMSB(s, strm.adler >>> 16);
      _zlib_deflate.putShortMSB(s, strm.adler & 65535);
    }
    _zlib_deflate.flush_pending(strm);
    if (s.wrap > 0) {
      s.wrap = -s.wrap;
    }
    return s.pending !== 0 ? zlib_constants.Z_OK : zlib_constants.Z_STREAM_END;
  }
  static deflateEnd(strm) {
    if (!strm || !strm.state) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    let status = strm.state.status;
    if (status !== _zlib_deflate.INIT_STATE && status !== _zlib_deflate.EXTRA_STATE && status !== _zlib_deflate.NAME_STATE && status !== _zlib_deflate.COMMENT_STATE && status !== _zlib_deflate.HCRC_STATE && status !== _zlib_deflate.BUSY_STATE && status !== _zlib_deflate.FINISH_STATE) {
      return _zlib_deflate.err(strm, zlib_constants.Z_STREAM_ERROR);
    }
    strm.state = null;
    return status === _zlib_deflate.BUSY_STATE ? _zlib_deflate.err(strm, zlib_constants.Z_DATA_ERROR) : zlib_constants.Z_OK;
  }
  /* =========================================================================
   * Initializes the compression dictionary from the given byte
   * sequence without producing any compressed output.
   */
  static deflateSetDictionary(strm, dictionary) {
    let dictLength = dictionary.length;
    if (!strm || !strm.state) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    const s = strm.state;
    const wrap = s.wrap;
    if (wrap === 2 || wrap === 1 && s.status !== _zlib_deflate.INIT_STATE || s.lookahead) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    if (wrap === 1) {
      strm.adler = zlib_adler32.adler32(strm.adler, dictionary, dictLength, 0);
    }
    s.wrap = 0;
    if (dictLength >= s.w_size) {
      if (wrap === 0) {
        zlib_Pako_trees.zero(s.head);
        s.strstart = 0;
        s.block_start = 0;
        s.insert = 0;
      }
      let tmpDict = new Uint8Array(s.w_size);
      tmpDict.set(dictionary.subarray(dictLength - s.w_size, dictLength), 0);
      dictionary = tmpDict;
      dictLength = s.w_size;
    }
    const avail = strm.avail_in;
    const next = strm.next_in;
    const input = strm.input;
    strm.avail_in = dictLength;
    strm.next_in = 0;
    strm.input = dictionary;
    _zlib_deflate.fill_window(s);
    while (s.lookahead >= zlib_Pako_trees.MIN_MATCH) {
      let str = s.strstart;
      let n = s.lookahead - (zlib_Pako_trees.MIN_MATCH - 1);
      do {
        s.ins_h = _zlib_deflate.HASH(s, s.ins_h, s.window[str + zlib_Pako_trees.MIN_MATCH - 1]);
        s.prev[str & s.w_mask] = s.head[s.ins_h];
        s.head[s.ins_h] = str;
        str++;
      } while (--n);
      s.strstart = str;
      s.lookahead = zlib_Pako_trees.MIN_MATCH - 1;
      _zlib_deflate.fill_window(s);
    }
    s.strstart += s.lookahead;
    s.block_start = s.strstart;
    s.insert = s.lookahead;
    s.lookahead = 0;
    s.match_length = s.prev_length = zlib_Pako_trees.MIN_MATCH - 1;
    s.match_available = 0;
    strm.next_in = next;
    strm.input = input;
    strm.avail_in = avail;
    s.wrap = wrap;
    return zlib_constants.Z_OK;
  }
};
zlib_deflate.MAX_MEM_LEVEL = 9;
zlib_deflate.MAX_WBITS = 15;
zlib_deflate.DEF_MEM_LEVEL = 8;
zlib_deflate.LENGTH_CODES = 29;
zlib_deflate.LITERALS = 256;
zlib_deflate.L_CODES = zlib_deflate.LITERALS + 1 + zlib_deflate.LENGTH_CODES;
zlib_deflate.D_CODES = 30;
zlib_deflate.BL_CODES = 19;
zlib_deflate.HEAP_SIZE = 2 * zlib_deflate.L_CODES + 1;
zlib_deflate.MAX_BITS = 15;
zlib_deflate.MIN_MATCH = 3;
zlib_deflate.MAX_MATCH = 258;
zlib_deflate.MIN_LOOKAHEAD = zlib_deflate.MAX_MATCH + zlib_deflate.MIN_MATCH + 1;
zlib_deflate.PRESET_DICT = 32;
zlib_deflate.INIT_STATE = 42;
zlib_deflate.EXTRA_STATE = 69;
zlib_deflate.NAME_STATE = 73;
zlib_deflate.COMMENT_STATE = 91;
zlib_deflate.HCRC_STATE = 103;
zlib_deflate.BUSY_STATE = 113;
zlib_deflate.FINISH_STATE = 666;
zlib_deflate.BS_NEED_MORE = 1;
zlib_deflate.BS_BLOCK_DONE = 2;
zlib_deflate.BS_FINISH_STARTED = 3;
zlib_deflate.BS_FINISH_DONE = 4;
zlib_deflate.OS_CODE = 3;
zlib_deflate.deflate_fast = (s, flush) => {
  let hash_head;
  let bflush;
  for (; ; ) {
    if (s.lookahead < zlib_deflate.MIN_LOOKAHEAD) {
      zlib_deflate.fill_window(s);
      if (s.lookahead < zlib_deflate.MIN_LOOKAHEAD && flush === zlib_constants.Z_NO_FLUSH) {
        return zlib_deflate.BS_NEED_MORE;
      }
      if (s.lookahead === 0) {
        break;
      }
    }
    hash_head = 0;
    if (s.lookahead >= zlib_Pako_trees.MIN_MATCH) {
      s.ins_h = zlib_deflate.HASH(s, s.ins_h, s.window[s.strstart + zlib_Pako_trees.MIN_MATCH - 1]);
      hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
      s.head[s.ins_h] = s.strstart;
    }
    if (hash_head !== 0 && s.strstart - hash_head <= s.w_size - zlib_deflate.MIN_LOOKAHEAD) {
      s.match_length = zlib_deflate.longest_match(s, hash_head);
    }
    if (s.match_length >= zlib_Pako_trees.MIN_MATCH) {
      bflush = zlib_Pako_trees._tr_tally(s, s.strstart - s.match_start, s.match_length - zlib_Pako_trees.MIN_MATCH);
      s.lookahead -= s.match_length;
      if (s.match_length <= s.max_lazy_match && s.lookahead >= zlib_Pako_trees.MIN_MATCH) {
        s.match_length--;
        do {
          s.strstart++;
          s.ins_h = zlib_deflate.HASH(s, s.ins_h, s.window[s.strstart + zlib_Pako_trees.MIN_MATCH - 1]);
          hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
          s.head[s.ins_h] = s.strstart;
        } while (--s.match_length !== 0);
        s.strstart++;
      } else {
        s.strstart += s.match_length;
        s.match_length = 0;
        s.ins_h = s.window[s.strstart];
        s.ins_h = zlib_deflate.HASH(s, s.ins_h, s.window[s.strstart + 1]);
      }
    } else {
      bflush = zlib_Pako_trees._tr_tally(s, 0, s.window[s.strstart]);
      s.lookahead--;
      s.strstart++;
    }
    if (bflush) {
      zlib_deflate.flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return zlib_deflate.BS_NEED_MORE;
      }
    }
  }
  s.insert = s.strstart < zlib_Pako_trees.MIN_MATCH - 1 ? s.strstart : zlib_Pako_trees.MIN_MATCH - 1;
  if (flush === zlib_constants.Z_FINISH) {
    zlib_deflate.flush_block_only(s, true);
    if (s.strm.avail_out === 0) {
      return zlib_deflate.BS_FINISH_STARTED;
    }
    return zlib_deflate.BS_FINISH_DONE;
  }
  if (s.last_lit) {
    zlib_deflate.flush_block_only(s, false);
    if (s.strm.avail_out === 0) {
      return zlib_deflate.BS_NEED_MORE;
    }
  }
  return zlib_deflate.BS_BLOCK_DONE;
};
zlib_deflate.configuration_table = [
  /*      good lazy nice chain */
  new zlib_config(0, 0, 0, 0, zlib_deflate.deflate_stored),
  new zlib_config(4, 4, 8, 4, zlib_deflate.deflate_fast),
  new zlib_config(4, 5, 16, 8, zlib_deflate.deflate_fast),
  new zlib_config(4, 6, 32, 32, zlib_deflate.deflate_fast),
  new zlib_config(4, 4, 16, 16, zlib_deflate.deflate_slow),
  new zlib_config(8, 16, 32, 32, zlib_deflate.deflate_slow),
  new zlib_config(8, 16, 128, 128, zlib_deflate.deflate_slow),
  new zlib_config(8, 32, 128, 256, zlib_deflate.deflate_slow),
  new zlib_config(32, 128, 258, 1024, zlib_deflate.deflate_slow),
  new zlib_config(32, 258, 258, 4096, zlib_deflate.deflate_slow)
  /* 9 max compression */
];

// obj/full/Pako/zlib/trees.js
var zlib_StaticTreeDesc = class {
  constructor(static_tree, extra_bits, extra_base, elems, max_length) {
    this.static_tree = static_tree;
    this.extra_bits = extra_bits;
    this.extra_base = extra_base;
    this.elems = elems;
    this.max_length = max_length;
    this.has_stree = static_tree != null && static_tree.length > 0;
  }
};
var zlib_TreeDesc = class {
  constructor(dyn_tree, stat_desc) {
    this.max_code = 0;
    this.dyn_tree = dyn_tree;
    this.max_code = 0;
    this.stat_desc = stat_desc;
  }
};
var zlib_Pako_trees = class _zlib_Pako_trees {
  /*============================================================================*/
  static zero(buf) {
    let len = buf.length;
    while (--len >= 0) {
      buf[len] = 0;
    }
  }
  /* First normalized distance for each code (0 = distance of 1) */
  static Zeroinit() {
    _zlib_Pako_trees.zero(_zlib_Pako_trees.static_ltree);
    _zlib_Pako_trees.zero(_zlib_Pako_trees.base_dist);
    _zlib_Pako_trees.zero(_zlib_Pako_trees.base_length);
    _zlib_Pako_trees.zero(_zlib_Pako_trees._length_code);
    _zlib_Pako_trees.zero(_zlib_Pako_trees._dist_code);
    _zlib_Pako_trees.zero(_zlib_Pako_trees.static_dtree);
    return true;
  }
  static d_code(dist) {
    return dist < 256 ? _zlib_Pako_trees._dist_code[dist] : _zlib_Pako_trees._dist_code[256 + (dist >>> 7)];
  }
  /* ===========================================================================
   * Output a short LSB first on the stream.
   * IN assertion: there is enough room in pendingBuf.
   */
  static put_short(s, w) {
    s.pending_buf[s.pending++] = w & 255;
    s.pending_buf[s.pending++] = w >>> 8 & 255;
  }
  static send_code(s, c, tree) {
    _zlib_Pako_trees.send_bits(
      s,
      tree[c * 2],
      tree[c * 2 + 1]
      /*.Len*/
    );
  }
  /* ===========================================================================
   * Reverse the first len bits of a code, using straightforward code (a faster
   * method would use a table)
   * IN assertion: 1 <= len <= 15
   */
  static bi_reverse(code, len) {
    let res = 0;
    do {
      res |= code & 1;
      code >>>= 1;
      res <<= 1;
    } while (--len > 0);
    return res >>> 1;
  }
  /* ===========================================================================
   * Flush the bit buffer, keeping at most 7 bits in it.
   */
  static bi_flush(s) {
    if (s.bi_valid === 16) {
      _zlib_Pako_trees.put_short(s, s.bi_buf);
      s.bi_buf = 0;
      s.bi_valid = 0;
    } else if (s.bi_valid >= 8) {
      s.pending_buf[s.pending++] = s.bi_buf & 255;
      s.bi_buf >>= 8;
      s.bi_valid -= 8;
    }
  }
  /* ===========================================================================
   * Generate the codes for a given tree and bit counts (which need not be
   * optimal).
   * IN assertion: the array bl_count contains the bit length statistics for
   * the given tree and the field len is set for all tree elements.
   * OUT assertion: the field code is set for all tree elements of non
   *     zero code length.
   */
  static gen_codes(tree, max_code, bl_count) {
    const next_code = new Array(_zlib_Pako_trees.MAX_BITS + 1);
    let code = 0;
    let bits;
    let n;
    for (bits = 1; bits <= _zlib_Pako_trees.MAX_BITS; bits++) {
      next_code[bits] = code = code + bl_count[bits - 1] << 1;
    }
    for (n = 0; n <= max_code; n++) {
      let len = tree[n * 2 + 1];
      if (len === 0) {
        continue;
      }
      tree[n * 2] = _zlib_Pako_trees.bi_reverse(next_code[len]++, len);
    }
  }
  /* ===========================================================================
   * Initialize the various 'constant' tables.
   */
  static tr_static_init() {
    let n;
    let bits;
    let length;
    let code;
    let dist;
    const bl_count = new Uint16Array(_zlib_Pako_trees.MAX_BITS + 1);
    length = 0;
    for (code = 0; code < _zlib_Pako_trees.LENGTH_CODES - 1; code++) {
      _zlib_Pako_trees.base_length[code] = length;
      for (n = 0; n < 1 << _zlib_Pako_trees.extra_lbits[code]; n++) {
        _zlib_Pako_trees._length_code[length++] = code;
      }
    }
    _zlib_Pako_trees._length_code[length - 1] = code;
    dist = 0;
    for (code = 0; code < 16; code++) {
      _zlib_Pako_trees.base_dist[code] = dist;
      for (n = 0; n < 1 << _zlib_Pako_trees.extra_dbits[code]; n++) {
        _zlib_Pako_trees._dist_code[dist++] = code;
      }
    }
    dist >>= 7;
    for (; code < _zlib_Pako_trees.D_CODES; code++) {
      _zlib_Pako_trees.base_dist[code] = dist << 7;
      for (n = 0; n < 1 << _zlib_Pako_trees.extra_dbits[code] - 7; n++) {
        _zlib_Pako_trees._dist_code[256 + dist++] = code;
      }
    }
    for (bits = 0; bits <= _zlib_Pako_trees.MAX_BITS; bits++) {
      bl_count[bits] = 0;
    }
    n = 0;
    while (n <= 143) {
      _zlib_Pako_trees.static_ltree[n * 2 + 1] = 8;
      n++;
      bl_count[8]++;
    }
    while (n <= 255) {
      _zlib_Pako_trees.static_ltree[n * 2 + 1] = 9;
      n++;
      bl_count[9]++;
    }
    while (n <= 279) {
      _zlib_Pako_trees.static_ltree[n * 2 + 1] = 7;
      n++;
      bl_count[7]++;
    }
    while (n <= 287) {
      _zlib_Pako_trees.static_ltree[n * 2 + 1] = 8;
      n++;
      bl_count[8]++;
    }
    _zlib_Pako_trees.gen_codes(_zlib_Pako_trees.static_ltree, _zlib_Pako_trees.L_CODES + 1, bl_count);
    for (n = 0; n < _zlib_Pako_trees.D_CODES; n++) {
      _zlib_Pako_trees.static_dtree[n * 2 + 1] = 5;
      _zlib_Pako_trees.static_dtree[n * 2] = _zlib_Pako_trees.bi_reverse(n, 5);
    }
    _zlib_Pako_trees.static_l_desc = new zlib_StaticTreeDesc(_zlib_Pako_trees.static_ltree, _zlib_Pako_trees.extra_lbits, _zlib_Pako_trees.LITERALS + 1, _zlib_Pako_trees.L_CODES, _zlib_Pako_trees.MAX_BITS);
    _zlib_Pako_trees.static_d_desc = new zlib_StaticTreeDesc(_zlib_Pako_trees.static_dtree, _zlib_Pako_trees.extra_dbits, 0, _zlib_Pako_trees.D_CODES, _zlib_Pako_trees.MAX_BITS);
    _zlib_Pako_trees.static_bl_desc = new zlib_StaticTreeDesc(new Uint16Array(0), _zlib_Pako_trees.extra_blbits, 0, _zlib_Pako_trees.BL_CODES, _zlib_Pako_trees.MAX_BL_BITS);
  }
  /* ===========================================================================
   * Initialize a new block.
   */
  static init_block(s) {
    let n;
    for (n = 0; n < _zlib_Pako_trees.L_CODES; n++) {
      s.dyn_ltree[n * 2] = 0;
    }
    for (n = 0; n < _zlib_Pako_trees.D_CODES; n++) {
      s.dyn_dtree[n * 2] = 0;
    }
    for (n = 0; n < _zlib_Pako_trees.BL_CODES; n++) {
      s.bl_tree[n * 2] = 0;
    }
    s.dyn_ltree[_zlib_Pako_trees.END_BLOCK * 2] = 1;
    s.opt_len = s.static_len = 0;
    s.last_lit = s.matches = 0;
  }
  /* ===========================================================================
   * Flush the bit buffer and align the output on a byte boundary
   */
  static bi_windup(s) {
    if (s.bi_valid > 8) {
      _zlib_Pako_trees.put_short(s, s.bi_buf);
    } else if (s.bi_valid > 0) {
      s.pending_buf[s.pending++] = s.bi_buf;
    }
    s.bi_buf = 0;
    s.bi_valid = 0;
  }
  /* ===========================================================================
   * Copy a stored block, storing first the length and its
   * one's complement if requested.
   */
  static copy_block(s, buf, len, header) {
    _zlib_Pako_trees.bi_windup(s);
    if (header) {
      _zlib_Pako_trees.put_short(s, len);
      _zlib_Pako_trees.put_short(s, ~len);
    }
    s.pending_buf.set(s.window.subarray(buf, buf + len), s.pending);
    s.pending += len;
  }
  /* ===========================================================================
   * Compares to subtrees, using the tree depth as tie breaker when
   * the subtrees have equal frequency. This minimizes the worst case length.
   */
  static smaller(tree, n, m, depth) {
    const _n2 = n * 2;
    const _m2 = m * 2;
    return tree[_n2] < tree[_m2] || tree[_n2] === tree[_m2] && depth[n] <= depth[m];
  }
  /* ===========================================================================
   * Restore the heap property by moving down the tree starting at node k,
   * exchanging a node with the smallest of its two sons if necessary, stopping
   * when the heap property is re-established (each father smaller than its
   * two sons).
   */
  static pqdownheap(s, tree, k) {
    const v = s.heap[k];
    let j = k << 1;
    while (j <= s.heap_len) {
      if (j < s.heap_len && _zlib_Pako_trees.smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) {
        j++;
      }
      if (_zlib_Pako_trees.smaller(tree, v, s.heap[j], s.depth)) {
        break;
      }
      s.heap[k] = s.heap[j];
      k = j;
      j <<= 1;
    }
    s.heap[k] = v;
  }
  // inlined manually
  // const SMALLEST = 1;
  /* ===========================================================================
   * Send the block data compressed using the given Huffman trees
   */
  static compress_block(s, ltree, dtree) {
    let dist;
    let lc;
    let lx = 0;
    let code;
    let extra;
    if (s.last_lit !== 0) {
      do {
        dist = s.pending_buf[s.d_buf + lx * 2] << 8 | s.pending_buf[s.d_buf + lx * 2 + 1];
        lc = s.pending_buf[s.l_buf + lx];
        lx++;
        if (dist === 0) {
          _zlib_Pako_trees.send_code(s, lc, ltree);
        } else {
          code = _zlib_Pako_trees._length_code[lc];
          _zlib_Pako_trees.send_code(s, code + _zlib_Pako_trees.LITERALS + 1, ltree);
          extra = _zlib_Pako_trees.extra_lbits[code];
          if (extra !== 0) {
            lc -= _zlib_Pako_trees.base_length[code];
            _zlib_Pako_trees.send_bits(s, lc, extra);
          }
          dist--;
          code = _zlib_Pako_trees.d_code(dist);
          _zlib_Pako_trees.send_code(s, code, dtree);
          extra = _zlib_Pako_trees.extra_dbits[code];
          if (extra !== 0) {
            dist -= _zlib_Pako_trees.base_dist[code];
            _zlib_Pako_trees.send_bits(s, dist, extra);
          }
        }
      } while (lx < s.last_lit);
    }
    _zlib_Pako_trees.send_code(s, _zlib_Pako_trees.END_BLOCK, ltree);
  }
  /* ===========================================================================
   * Construct one Huffman tree and assigns the code bit strings and lengths.
   * Update the total bit length for the current block.
   * IN assertion: the field freq is set for all tree elements.
   * OUT assertions: the fields len and code are set to the optimal bit length
   *     and corresponding code. The length opt_len is updated; static_len is
   *     also updated if stree is not null. The field max_code is set.
   */
  static build_tree(s, desc) {
    const tree = desc.dyn_tree;
    const stree = desc.stat_desc.static_tree;
    const has_stree = desc.stat_desc.has_stree;
    const elems = desc.stat_desc.elems;
    let n, m;
    let max_code = -1;
    let node;
    s.heap_len = 0;
    s.heap_max = _zlib_Pako_trees.HEAP_SIZE;
    for (n = 0; n < elems; n++) {
      if (tree[n * 2] !== 0) {
        s.heap[++s.heap_len] = max_code = n;
        s.depth[n] = 0;
      } else {
        tree[n * 2 + 1] = 0;
      }
    }
    while (s.heap_len < 2) {
      node = s.heap[++s.heap_len] = max_code < 2 ? ++max_code : 0;
      tree[node * 2] = 1;
      s.depth[node] = 0;
      s.opt_len--;
      if (has_stree) {
        s.static_len -= stree[node * 2 + 1];
      }
    }
    desc.max_code = max_code;
    for (n = s.heap_len >> 1; n >= 1; n--) {
      _zlib_Pako_trees.pqdownheap(s, tree, n);
    }
    node = elems;
    do {
      n = s.heap[
        1
        /*SMALLEST*/
      ];
      s.heap[
        1
        /*SMALLEST*/
      ] = s.heap[s.heap_len--];
      _zlib_Pako_trees.pqdownheap(
        s,
        tree,
        1
        /*SMALLEST*/
      );
      m = s.heap[
        1
        /*SMALLEST*/
      ];
      s.heap[--s.heap_max] = n;
      s.heap[--s.heap_max] = m;
      tree[node * 2] = tree[n * 2] + tree[m * 2];
      s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1;
      tree[n * 2 + 1] = tree[m * 2 + 1] = node;
      s.heap[
        1
        /*SMALLEST*/
      ] = node++;
      _zlib_Pako_trees.pqdownheap(
        s,
        tree,
        1
        /*SMALLEST*/
      );
    } while (s.heap_len >= 2);
    s.heap[--s.heap_max] = s.heap[
      1
      /*SMALLEST*/
    ];
    _zlib_Pako_trees.gen_bitlen(s, desc);
    _zlib_Pako_trees.gen_codes(tree, max_code, s.bl_count);
  }
  /* ===========================================================================
   * Scan a literal or distance tree to determine the frequencies of the codes
   * in the bit length tree.
   */
  static scan_tree(s, tree, max_code) {
    let n;
    let prevlen = -1;
    let curlen;
    let nextlen = tree[0 * 2 + 1];
    let count = 0;
    let max_count = 7;
    let min_count = 4;
    if (nextlen === 0) {
      max_count = 138;
      min_count = 3;
    }
    tree[(max_code + 1) * 2 + 1] = 65535;
    for (n = 0; n <= max_code; n++) {
      curlen = nextlen;
      nextlen = tree[(n + 1) * 2 + 1];
      if (++count < max_count && curlen === nextlen) {
        continue;
      } else if (count < min_count) {
        s.bl_tree[curlen * 2] += count;
      } else if (curlen !== 0) {
        if (curlen !== prevlen) {
          s.bl_tree[curlen * 2]++;
        }
        s.bl_tree[_zlib_Pako_trees.REP_3_6 * 2]++;
      } else if (count <= 10) {
        s.bl_tree[_zlib_Pako_trees.REPZ_3_10 * 2]++;
      } else {
        s.bl_tree[_zlib_Pako_trees.REPZ_11_138 * 2]++;
      }
      count = 0;
      prevlen = curlen;
      if (nextlen === 0) {
        max_count = 138;
        min_count = 3;
      } else if (curlen === nextlen) {
        max_count = 6;
        min_count = 3;
      } else {
        max_count = 7;
        min_count = 4;
      }
    }
  }
  /* ===========================================================================
   * Send a literal or distance tree in compressed form, using the codes in
   * bl_tree.
   */
  static send_tree(s, tree, max_code) {
    let n;
    let prevlen = -1;
    let curlen;
    let nextlen = tree[0 * 2 + 1];
    let count = 0;
    let max_count = 7;
    let min_count = 4;
    if (nextlen === 0) {
      max_count = 138;
      min_count = 3;
    }
    for (n = 0; n <= max_code; n++) {
      curlen = nextlen;
      nextlen = tree[(n + 1) * 2 + 1];
      if (++count < max_count && curlen === nextlen) {
        continue;
      } else if (count < min_count) {
        do {
          _zlib_Pako_trees.send_code(s, curlen, s.bl_tree);
        } while (--count !== 0);
      } else if (curlen !== 0) {
        if (curlen !== prevlen) {
          _zlib_Pako_trees.send_code(s, curlen, s.bl_tree);
          count--;
        }
        _zlib_Pako_trees.send_code(s, _zlib_Pako_trees.REP_3_6, s.bl_tree);
        _zlib_Pako_trees.send_bits(s, count - 3, 2);
      } else if (count <= 10) {
        _zlib_Pako_trees.send_code(s, _zlib_Pako_trees.REPZ_3_10, s.bl_tree);
        _zlib_Pako_trees.send_bits(s, count - 3, 3);
      } else {
        _zlib_Pako_trees.send_code(s, _zlib_Pako_trees.REPZ_11_138, s.bl_tree);
        _zlib_Pako_trees.send_bits(s, count - 11, 7);
      }
      count = 0;
      prevlen = curlen;
      if (nextlen === 0) {
        max_count = 138;
        min_count = 3;
      } else if (curlen === nextlen) {
        max_count = 6;
        min_count = 3;
      } else {
        max_count = 7;
        min_count = 4;
      }
    }
  }
  /* ===========================================================================
   * Construct the Huffman tree for the bit lengths and return the index in
   * bl_order of the last bit length code to send.
   */
  static build_bl_tree(s) {
    let max_blindex;
    _zlib_Pako_trees.scan_tree(s, s.dyn_ltree, s.l_desc.max_code);
    _zlib_Pako_trees.scan_tree(s, s.dyn_dtree, s.d_desc.max_code);
    _zlib_Pako_trees.build_tree(s, s.bl_desc);
    for (max_blindex = _zlib_Pako_trees.BL_CODES - 1; max_blindex >= 3; max_blindex--) {
      if (s.bl_tree[_zlib_Pako_trees.bl_order[max_blindex] * 2 + 1] !== 0) {
        break;
      }
    }
    s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;
    return max_blindex;
  }
  /* ===========================================================================
   * Send the header for a block using dynamic Huffman trees: the counts, the
   * lengths of the bit length codes, the literal tree and the distance tree.
   * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
   */
  static send_all_trees(s, lcodes, dcodes, blcodes) {
    let rank;
    _zlib_Pako_trees.send_bits(s, lcodes - 257, 5);
    _zlib_Pako_trees.send_bits(s, dcodes - 1, 5);
    _zlib_Pako_trees.send_bits(s, blcodes - 4, 4);
    for (rank = 0; rank < blcodes; rank++) {
      _zlib_Pako_trees.send_bits(s, s.bl_tree[_zlib_Pako_trees.bl_order[rank] * 2 + 1], 3);
    }
    _zlib_Pako_trees.send_tree(s, s.dyn_ltree, lcodes - 1);
    _zlib_Pako_trees.send_tree(s, s.dyn_dtree, dcodes - 1);
  }
  /* ===========================================================================
   * Check if the data type is TEXT or BINARY, using the following algorithm:
   * - TEXT if the two conditions below are satisfied:
   *    a) There are no non-portable control characters belonging to the
   *       "black list" (0..6, 14..25, 28..31).
   *    b) There is at least one printable character belonging to the
   *       "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
   * - BINARY otherwise.
   * - The following partially-portable control characters form a
   *   "gray list" that is ignored in this detection algorithm:
   *   (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
   * IN assertion: the fields Freq of dyn_ltree are set.
   */
  static detect_data_type(s) {
    let black_mask = 4093624447;
    let n;
    for (n = 0; n <= 31; n++, black_mask >>>= 1) {
      if (black_mask & 1 && s.dyn_ltree[n * 2] !== 0) {
        return zlib_constants.Z_BINARY;
      }
    }
    if (s.dyn_ltree[9 * 2] !== 0 || s.dyn_ltree[10 * 2] !== 0 || s.dyn_ltree[13 * 2] !== 0) {
      return zlib_constants.Z_TEXT;
    }
    for (n = 32; n < _zlib_Pako_trees.LITERALS; n++) {
      if (s.dyn_ltree[n * 2] !== 0) {
        return zlib_constants.Z_TEXT;
      }
    }
    return zlib_constants.Z_BINARY;
  }
  /* ===========================================================================
   * Initialize the tree data structures for a new zlib stream.
   */
  static _tr_init(s) {
    if (!_zlib_Pako_trees.static_init_done) {
      _zlib_Pako_trees.tr_static_init();
      _zlib_Pako_trees.static_init_done = true;
    }
    s.l_desc = new zlib_TreeDesc(s.dyn_ltree, _zlib_Pako_trees.static_l_desc);
    s.d_desc = new zlib_TreeDesc(s.dyn_dtree, _zlib_Pako_trees.static_d_desc);
    s.bl_desc = new zlib_TreeDesc(s.bl_tree, _zlib_Pako_trees.static_bl_desc);
    s.bi_buf = 0;
    s.bi_valid = 0;
    _zlib_Pako_trees.init_block(s);
  }
  /* ===========================================================================
   * Send a stored block
   */
  static _tr_stored_block(s, buf, stored_len, last) {
    _zlib_Pako_trees.send_bits(s, (_zlib_Pako_trees.STORED_BLOCK << 1) + (last ? 1 : 0), 3);
    _zlib_Pako_trees.copy_block(s, buf, stored_len, true);
  }
  /* ===========================================================================
   * Send one empty static block to give enough lookahead for inflate.
   * This takes 10 bits, of which 7 may remain in the bit buffer.
   */
  static _tr_align(s) {
    _zlib_Pako_trees.send_bits(s, _zlib_Pako_trees.STATIC_TREES << 1, 3);
    _zlib_Pako_trees.send_code(s, _zlib_Pako_trees.END_BLOCK, _zlib_Pako_trees.static_ltree);
    _zlib_Pako_trees.bi_flush(s);
  }
  /* ===========================================================================
   * Determine the best encoding for the current block: dynamic trees, static
   * trees or store, and output the encoded block to the zip file.
   */
  static _tr_flush_block(s, buf, stored_len, last) {
    let opt_lenb;
    let static_lenb;
    let max_blindex = 0;
    if (s.level > 0) {
      if (s.strm.data_type === zlib_constants.Z_UNKNOWN) {
        s.strm.data_type = _zlib_Pako_trees.detect_data_type(s);
      }
      _zlib_Pako_trees.build_tree(s, s.l_desc);
      _zlib_Pako_trees.build_tree(s, s.d_desc);
      max_blindex = _zlib_Pako_trees.build_bl_tree(s);
      opt_lenb = s.opt_len + 3 + 7 >>> 3;
      static_lenb = s.static_len + 3 + 7 >>> 3;
      if (static_lenb <= opt_lenb) {
        opt_lenb = static_lenb;
      }
    } else {
      opt_lenb = static_lenb = stored_len + 5;
    }
    if (stored_len + 4 <= opt_lenb && buf !== -1) {
      _zlib_Pako_trees._tr_stored_block(s, buf, stored_len, last);
    } else if (s.strategy === zlib_constants.Z_FIXED || static_lenb === opt_lenb) {
      _zlib_Pako_trees.send_bits(s, (_zlib_Pako_trees.STATIC_TREES << 1) + (last ? 1 : 0), 3);
      _zlib_Pako_trees.compress_block(s, _zlib_Pako_trees.static_ltree, _zlib_Pako_trees.static_dtree);
    } else {
      _zlib_Pako_trees.send_bits(s, (_zlib_Pako_trees.DYN_TREES << 1) + (last ? 1 : 0), 3);
      _zlib_Pako_trees.send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1);
      _zlib_Pako_trees.compress_block(s, s.dyn_ltree, s.dyn_dtree);
    }
    _zlib_Pako_trees.init_block(s);
    if (last) {
      _zlib_Pako_trees.bi_windup(s);
    }
  }
  /* ===========================================================================
   * Save the match info and tally the frequency counts. Return true if
   * the current block must be flushed.
   */
  static _tr_tally(s, dist, lc) {
    s.pending_buf[s.d_buf + s.last_lit * 2] = dist >>> 8 & 255;
    s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 255;
    s.pending_buf[s.l_buf + s.last_lit] = lc & 255;
    s.last_lit++;
    if (dist === 0) {
      s.dyn_ltree[lc * 2]++;
    } else {
      s.matches++;
      dist--;
      s.dyn_ltree[(_zlib_Pako_trees._length_code[lc] + _zlib_Pako_trees.LITERALS + 1) * 2]++;
      s.dyn_dtree[_zlib_Pako_trees.d_code(dist) * 2]++;
    }
    return s.last_lit === s.lit_bufsize - 1;
  }
};
zlib_Pako_trees.Z_FIXED = 4;
zlib_Pako_trees.Z_BINARY = 0;
zlib_Pako_trees.Z_TEXT = 1;
zlib_Pako_trees.Z_UNKNOWN = 2;
zlib_Pako_trees.STORED_BLOCK = 0;
zlib_Pako_trees.STATIC_TREES = 1;
zlib_Pako_trees.DYN_TREES = 2;
zlib_Pako_trees.MIN_MATCH = 3;
zlib_Pako_trees.MAX_MATCH = 258;
zlib_Pako_trees.LENGTH_CODES = 29;
zlib_Pako_trees.LITERALS = 256;
zlib_Pako_trees.L_CODES = zlib_Pako_trees.LITERALS + 1 + zlib_Pako_trees.LENGTH_CODES;
zlib_Pako_trees.D_CODES = 30;
zlib_Pako_trees.BL_CODES = 19;
zlib_Pako_trees.HEAP_SIZE = 2 * zlib_Pako_trees.L_CODES + 1;
zlib_Pako_trees.MAX_BITS = 15;
zlib_Pako_trees.Buf_size = 16;
zlib_Pako_trees.MAX_BL_BITS = 7;
zlib_Pako_trees.END_BLOCK = 256;
zlib_Pako_trees.REP_3_6 = 16;
zlib_Pako_trees.REPZ_3_10 = 17;
zlib_Pako_trees.REPZ_11_138 = 18;
zlib_Pako_trees.extra_lbits = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0]);
zlib_Pako_trees.extra_dbits = new Uint8Array([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13]);
zlib_Pako_trees.extra_blbits = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7]);
zlib_Pako_trees.bl_order = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
zlib_Pako_trees.DIST_CODE_LEN = 512;
zlib_Pako_trees.static_ltree = new Uint16Array((zlib_Pako_trees.L_CODES + 2) * 2);
zlib_Pako_trees.static_dtree = new Uint16Array(zlib_Pako_trees.D_CODES * 2);
zlib_Pako_trees._dist_code = new Uint16Array(zlib_Pako_trees.DIST_CODE_LEN);
zlib_Pako_trees._length_code = new Uint16Array(zlib_Pako_trees.MAX_MATCH - zlib_Pako_trees.MIN_MATCH + 1);
zlib_Pako_trees.base_length = new Uint16Array(zlib_Pako_trees.LENGTH_CODES);
zlib_Pako_trees.base_dist = new Uint16Array(zlib_Pako_trees.D_CODES);
zlib_Pako_trees.send_bits = (s, value, length) => {
  if (s.bi_valid > zlib_Pako_trees.Buf_size - length) {
    s.bi_buf |= value << s.bi_valid & 65535;
    zlib_Pako_trees.put_short(s, s.bi_buf);
    s.bi_buf = value >> zlib_Pako_trees.Buf_size - s.bi_valid;
    s.bi_valid += length - zlib_Pako_trees.Buf_size;
  } else {
    s.bi_buf |= value << s.bi_valid & 65535;
    s.bi_valid += length;
  }
};
zlib_Pako_trees.gen_bitlen = (s, desc) => {
  const tree = desc.dyn_tree;
  const max_code = desc.max_code;
  const stree = desc.stat_desc.static_tree;
  const has_stree = desc.stat_desc.has_stree;
  const extra = desc.stat_desc.extra_bits;
  const base = desc.stat_desc.extra_base;
  const max_length = desc.stat_desc.max_length;
  let h;
  let n, m;
  let bits;
  let xbits;
  let f;
  let overflow = 0;
  for (bits = 0; bits <= zlib_Pako_trees.MAX_BITS; bits++) {
    s.bl_count[bits] = 0;
  }
  tree[s.heap[s.heap_max] * 2 + 1] = 0;
  for (h = s.heap_max + 1; h < zlib_Pako_trees.HEAP_SIZE; h++) {
    n = s.heap[h];
    bits = tree[tree[n * 2 + 1] * 2 + 1] + 1;
    if (bits > max_length) {
      bits = max_length;
      overflow++;
    }
    tree[n * 2 + 1] = bits;
    if (n > max_code) {
      continue;
    }
    s.bl_count[bits]++;
    xbits = 0;
    if (n >= base) {
      xbits = extra[n - base];
    }
    f = tree[n * 2];
    s.opt_len += f * (bits + xbits);
    if (has_stree) {
      s.static_len += f * (stree[n * 2 + 1] + xbits);
    }
  }
  if (overflow === 0) {
    return;
  }
  do {
    bits = max_length - 1;
    while (s.bl_count[bits] === 0) {
      bits--;
    }
    s.bl_count[bits]--;
    s.bl_count[bits + 1] += 2;
    s.bl_count[max_length]--;
    overflow -= 2;
  } while (overflow > 0);
  for (bits = max_length; bits !== 0; bits--) {
    n = s.bl_count[bits];
    while (n !== 0) {
      m = s.heap[--h];
      if (m > max_code) {
        continue;
      }
      if (tree[m * 2 + 1] !== bits) {
        s.opt_len += (bits - tree[m * 2 + 1]) * tree[m * 2];
        tree[m * 2 + 1] = bits;
      }
      n--;
    }
  }
};
zlib_Pako_trees.static_init_done = false;
zlib_Pako_trees.initDone = zlib_Pako_trees.Zeroinit();

// obj/full/Pako/zlib/inffast.js
var zlib_inffast = class _zlib_inffast {
  /*
         Decode literal, length, and distance codes and write out the resulting
         literal and match bytes until either not enough input or output is
         available, an end-of-block is encountered, or a data error is encountered.
         When large enough input and output buffers are supplied to inflate(), for
         example, a 16K input buffer and a 64K output buffer, more than 95% of the
         inflate execution time is spent in this routine.
  
         Entry assumptions:
  
              state.mode === LEN
              strm.avail_in >= 6
              strm.avail_out >= 258
              start >= strm.avail_out
              state.bits < 8
  
         On return, state.mode is one of:
  
              LEN -- ran out of enough output space or enough available input
              TYPE -- reached end of block code, inflate() to interpret next block
              BAD -- error in block data
  
         Notes:
  
          - The maximum input bits used by a length/distance pair is 15 bits for the
            length code, 5 bits for the length extra, 15 bits for the distance code,
            and 13 bits for the distance extra.  This totals 48 bits, or six bytes.
            Therefore if strm.avail_in >= 6, then there is enough input to avoid
            checking for available input while decoding.
  
          - The maximum bytes that a single length/distance pair can output is 258
            bytes, which is the maximum length that can be coded.  inflate_fast()
            requires strm.avail_out >= 258 for each loop to avoid checking for
            output space.
       */
  static inflate_fast(strm, start) {
    let _in;
    let last;
    let _out;
    let beg;
    let end;
    let wsize;
    let whave;
    let wnext;
    let s_window;
    let hold;
    let bits;
    let lcode;
    let dcode;
    let lmask;
    let dmask;
    let here;
    let op;
    let len;
    let dist;
    let from;
    let from_source;
    let input;
    let output;
    let state = strm.state;
    _in = strm.next_in;
    input = strm.input;
    last = _in + (strm.avail_in - 5);
    _out = strm.next_out;
    output = strm.output;
    beg = _out - (start - strm.avail_out);
    end = _out + (strm.avail_out - 257);
    wsize = state.wsize;
    whave = state.whave;
    wnext = state.wnext;
    s_window = state.window;
    hold = state.hold;
    bits = state.bits;
    lcode = state.lencode;
    dcode = state.distcode;
    lmask = (1 << state.lenbits) - 1;
    dmask = (1 << state.distbits) - 1;
    top: do {
      if (bits < 15) {
        hold += input[_in++] << bits;
        bits += 8;
        hold += input[_in++] << bits;
        bits += 8;
      }
      here = lcode[hold & lmask];
      dolen: for (; ; ) {
        op = here >>> 24;
        hold >>>= op;
        bits -= op;
        op = here >>> 16 & 255;
        if (op === 0) {
          output[_out++] = here & 65535;
        } else if (op & 16) {
          len = here & 65535;
          op &= 15;
          if (op) {
            if (bits < op) {
              hold += input[_in++] << bits;
              bits += 8;
            }
            len += hold & (1 << op) - 1;
            hold >>>= op;
            bits -= op;
          }
          if (bits < 15) {
            hold += input[_in++] << bits;
            bits += 8;
            hold += input[_in++] << bits;
            bits += 8;
          }
          here = dcode[hold & dmask];
          dodist: for (; ; ) {
            op = here >>> 24;
            hold >>>= op;
            bits -= op;
            op = here >>> 16 & 255;
            if (op & 16) {
              dist = here & 65535;
              op &= 15;
              if (bits < op) {
                hold += input[_in++] << bits;
                bits += 8;
                if (bits < op) {
                  hold += input[_in++] << bits;
                  bits += 8;
                }
              }
              dist += hold & (1 << op) - 1;
              hold >>>= op;
              bits -= op;
              op = _out - beg;
              if (dist > op) {
                op = dist - op;
                if (op > whave) {
                  if (state.sane) {
                    strm.msg = "invalid distance too far back";
                    state.mode = _zlib_inffast.BAD;
                    break top;
                  }
                }
                from = 0;
                from_source = s_window;
                if (wnext === 0) {
                  from += wsize - op;
                  if (op < len) {
                    len -= op;
                    do {
                      output[_out++] = s_window[from++];
                    } while (--op);
                    from = _out - dist;
                    from_source = output;
                  }
                } else if (wnext < op) {
                  from += wsize + wnext - op;
                  op -= wnext;
                  if (op < len) {
                    len -= op;
                    do {
                      output[_out++] = s_window[from++];
                    } while (--op);
                    from = 0;
                    if (wnext < len) {
                      op = wnext;
                      len -= op;
                      do {
                        output[_out++] = s_window[from++];
                      } while (--op);
                      from = _out - dist;
                      from_source = output;
                    }
                  }
                } else {
                  from += wnext - op;
                  if (op < len) {
                    len -= op;
                    do {
                      output[_out++] = s_window[from++];
                    } while (--op);
                    from = _out - dist;
                    from_source = output;
                  }
                }
                while (len > 2) {
                  output[_out++] = from_source[from++];
                  output[_out++] = from_source[from++];
                  output[_out++] = from_source[from++];
                  len -= 3;
                }
                if (len) {
                  output[_out++] = from_source[from++];
                  if (len > 1) {
                    output[_out++] = from_source[from++];
                  }
                }
              } else {
                from = _out - dist;
                do {
                  output[_out++] = output[from++];
                  output[_out++] = output[from++];
                  output[_out++] = output[from++];
                  len -= 3;
                } while (len > 2);
                if (len) {
                  output[_out++] = output[from++];
                  if (len > 1) {
                    output[_out++] = output[from++];
                  }
                }
              }
            } else if ((op & 64) === 0) {
              here = dcode[(here & 65535) + (hold & (1 << op) - 1)];
              continue dodist;
            } else {
              strm.msg = "invalid distance code";
              state.mode = _zlib_inffast.BAD;
              break top;
            }
            break;
          }
        } else if ((op & 64) === 0) {
          here = lcode[(here & 65535) + (hold & (1 << op) - 1)];
          continue dolen;
        } else if (op & 32) {
          state.mode = _zlib_inffast.TYPE;
          break top;
        } else {
          strm.msg = "invalid literal/length code";
          state.mode = _zlib_inffast.BAD;
          break top;
        }
        break;
      }
    } while (_in < last && _out < end);
    len = bits >> 3;
    _in -= len;
    bits -= len << 3;
    hold &= (1 << bits) - 1;
    strm.next_in = _in;
    strm.next_out = _out;
    strm.avail_in = _in < last ? 5 + (last - _in) : 5 - (_in - last);
    strm.avail_out = _out < end ? 257 + (end - _out) : 257 - (_out - end);
    state.hold = hold;
    state.bits = bits;
    return;
  }
};
zlib_inffast.BAD = 30;
zlib_inffast.TYPE = 12;

// obj/full/Pako/zlib/inflate.js
var zlib_InflateState = class {
  constructor() {
    this.mode = 0;
    this.last = 0;
    this.wrap = 0;
    this.havedict = 0;
    this.flags = 0;
    this.dmax = 0;
    this.check = 0;
    this.total = 0;
    this.head = null;
    this.wbits = 0;
    this.wsize = 0;
    this.whave = 0;
    this.wnext = 0;
    this.window = null;
    this.hold = 0;
    this.bits = 0;
    this.length = 0;
    this.offset = 0;
    this.extra = 0;
    this.lencode = null;
    this.distcode = null;
    this.lenbits = 0;
    this.distbits = 0;
    this.ncode = 0;
    this.nlen = 0;
    this.ndist = 0;
    this.have = 0;
    this.next = null;
    this.lens = new Uint16Array(320);
    this.work = new Uint16Array(288);
    this.lendyn = null;
    this.distdyn = null;
    this.sane = 0;
    this.back = 0;
    this.was = 0;
  }
};
var zlib_inflate = class _zlib_inflate {
  static zswap32(q) {
    return (q >>> 24 & 255) + (q >>> 8 & 65280) + ((q & 65280) << 8) + ((q & 255) << 24);
  }
  static inflateResetKeep(strm) {
    if (!strm || !strm.state) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    let state = strm.state;
    strm.total_in = strm.total_out = state.total = 0;
    strm.msg = "";
    if (state.wrap) {
      strm.adler = state.wrap & 1;
    }
    state.mode = _zlib_inflate.HEAD;
    state.last = 0;
    state.havedict = 0;
    state.dmax = 32768;
    state.head = null;
    state.hold = 0;
    state.bits = 0;
    state.lencode = state.lendyn = new Int32Array(_zlib_inflate.ENOUGH_LENS);
    state.distcode = state.distdyn = new Int32Array(_zlib_inflate.ENOUGH_DISTS);
    state.sane = 1;
    state.back = -1;
    return zlib_constants.Z_OK;
  }
  static inflateReset(strm) {
    if (!strm || !strm.state) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    const state = strm.state;
    state.wsize = 0;
    state.whave = 0;
    state.wnext = 0;
    return _zlib_inflate.inflateResetKeep(strm);
  }
  static inflateReset2(strm, windowBits) {
    let wrap;
    if (!strm || !strm.state) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    let state = strm.state;
    if (windowBits < 0) {
      wrap = 0;
      windowBits = -windowBits;
    } else {
      wrap = (windowBits >> 4) + 1;
      if (windowBits < 48) {
        windowBits &= 15;
      }
    }
    if (windowBits && (windowBits < 8 || windowBits > 15)) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    if (state.window !== null && state.wbits !== windowBits) {
      state.window = null;
    }
    state.wrap = wrap;
    state.wbits = windowBits;
    return _zlib_inflate.inflateReset(strm);
  }
  static inflateInit2(strm, windowBits) {
    if (!strm) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    let state = new zlib_InflateState();
    strm.state = state;
    state.window = null;
    const ret = _zlib_inflate.inflateReset2(strm, windowBits);
    if (ret !== zlib_constants.Z_OK) {
      strm.state = null;
    }
    return ret;
  }
  static inflateInit(strm) {
    return _zlib_inflate.inflateInit2(strm, _zlib_inflate.DEF_WBITS);
  }
  static fixedtables(state) {
    if (_zlib_inflate.virgin) {
      _zlib_inflate.lenfix = new Int32Array(512);
      _zlib_inflate.distfix = new Int32Array(32);
      let sym = 0;
      while (sym < 144) {
        state.lens[sym++] = 8;
      }
      while (sym < 256) {
        state.lens[sym++] = 9;
      }
      while (sym < 280) {
        state.lens[sym++] = 7;
      }
      while (sym < 288) {
        state.lens[sym++] = 8;
      }
      zlib_inftrees.inflate_table(_zlib_inflate.LENS, state.lens, 0, 288, _zlib_inflate.lenfix, 0, state.work, { bits: 9 });
      sym = 0;
      while (sym < 32) {
        state.lens[sym++] = 5;
      }
      zlib_inftrees.inflate_table(_zlib_inflate.DISTS, state.lens, 0, 32, _zlib_inflate.distfix, 0, state.work, { bits: 5 });
      _zlib_inflate.virgin = false;
    }
    state.lencode = _zlib_inflate.lenfix;
    state.lenbits = 9;
    state.distcode = _zlib_inflate.distfix;
    state.distbits = 5;
  }
  /*
       Update the window with the last wsize (normally 32K) bytes written before
       returning.  If window does not exist yet, create it.  This is only called
       when a window is already in use, or when output has been written during this
       inflate call, but the end of the deflate stream has not been reached yet.
       It is also called to create a window for dictionary data when a dictionary
       is loaded.
  
       Providing output buffers larger than 32K to inflate() should provide a speed
       advantage, since only the last 32K of output is copied to the sliding window
       upon return from inflate(), and since all distances after the first 32K of
       output will fall in the output data, making match copies simpler and faster.
       The advantage may be dependent on the size of the processor's data caches.
       */
  static updatewindow(strm, src, end, copy) {
    let dist;
    const state = strm.state;
    if (state.window === null) {
      state.wsize = 1 << state.wbits;
      state.wnext = 0;
      state.whave = 0;
      state.window = new Uint8Array(state.wsize);
    }
    if (copy >= state.wsize) {
      state.window.set(src.subarray(end - state.wsize, end), 0);
      state.wnext = 0;
      state.whave = state.wsize;
    } else {
      dist = state.wsize - state.wnext;
      if (dist > copy) {
        dist = copy;
      }
      state.window.set(src.subarray(end - copy, end - copy + dist), state.wnext);
      copy -= dist;
      if (copy) {
        state.window.set(src.subarray(end - copy, end), 0);
        state.wnext = copy;
        state.whave = state.wsize;
      } else {
        state.wnext += dist;
        if (state.wnext === state.wsize) {
          state.wnext = 0;
        }
        if (state.whave < state.wsize) {
          state.whave += dist;
        }
      }
    }
    return 0;
  }
  static inflate(strm, flush) {
    let state;
    let input, output;
    let next;
    let put;
    let have, left;
    let hold;
    let bits;
    let _in;
    let _out;
    let copy;
    let from;
    let from_source;
    let here = 0;
    let here_bits;
    let here_op;
    let here_val;
    let last_bits;
    let last_op;
    let last_val;
    let len;
    let ret;
    const hbuf = new Uint8Array(4);
    let opts;
    let n;
    let order = (
      /* permutation of code lengths */
      new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15])
    );
    if (!strm || !strm.state || !strm.output || !strm.input && strm.avail_in !== 0) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    state = strm.state;
    if (state.mode === _zlib_inflate.TYPE) {
      state.mode = _zlib_inflate.TYPEDO;
    }
    put = strm.next_out;
    output = strm.output;
    left = strm.avail_out;
    next = strm.next_in;
    input = strm.input;
    have = strm.avail_in;
    hold = state.hold;
    bits = state.bits;
    _in = have;
    _out = left;
    ret = zlib_constants.Z_OK;
    inf_leave:
      for (; ; ) {
        switch (state.mode) {
          case _zlib_inflate.HEAD:
            if (state.wrap === 0) {
              state.mode = _zlib_inflate.TYPEDO;
              break;
            }
            while (bits < 16) {
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            if (state.wrap & 2 && hold === 35615) {
              state.check = 0;
              hbuf[0] = hold & 255;
              hbuf[1] = hold >>> 8 & 255;
              state.check = zlib_crc32.crc32(state.check, hbuf, 2, 0);
              hold = 0;
              bits = 0;
              state.mode = _zlib_inflate.FLAGS;
              break;
            }
            state.flags = 0;
            if (state.head) {
              state.head.done = false;
            }
            if (!(state.wrap & 1) || /* check if zlib header allowed */
            (((hold & 255) << 8) + (hold >> 8)) % 31) {
              strm.msg = "incorrect header check";
              state.mode = _zlib_inflate.BAD;
              break;
            }
            if ((hold & 15) !== zlib_constants.Z_DEFLATED) {
              strm.msg = "unknown compression method";
              state.mode = _zlib_inflate.BAD;
              break;
            }
            hold >>>= 4;
            bits -= 4;
            len = (hold & 15) + 8;
            if (state.wbits === 0) {
              state.wbits = len;
            } else if (len > state.wbits) {
              strm.msg = "invalid window size";
              state.mode = _zlib_inflate.BAD;
              break;
            }
            state.dmax = 1 << state.wbits;
            strm.adler = state.check = 1;
            state.mode = hold & 512 ? _zlib_inflate.DICTID : _zlib_inflate.TYPE;
            hold = 0;
            bits = 0;
            break;
          case _zlib_inflate.FLAGS:
            while (bits < 16) {
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            state.flags = hold;
            if ((state.flags & 255) !== zlib_constants.Z_DEFLATED) {
              strm.msg = "unknown compression method";
              state.mode = _zlib_inflate.BAD;
              break;
            }
            if (state.flags & 57344) {
              strm.msg = "unknown header flags set";
              state.mode = _zlib_inflate.BAD;
              break;
            }
            if (state.head) {
              state.head.text = hold >> 8 & 1;
            }
            if (state.flags & 512) {
              hbuf[0] = hold & 255;
              hbuf[1] = hold >>> 8 & 255;
              state.check = zlib_crc32.crc32(state.check, hbuf, 2, 0);
            }
            hold = 0;
            bits = 0;
            state.mode = _zlib_inflate.TIME;
          /* falls through */
          case _zlib_inflate.TIME:
            while (bits < 32) {
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            if (state.head) {
              state.head.time = hold;
            }
            if (state.flags & 512) {
              hbuf[0] = hold & 255;
              hbuf[1] = hold >>> 8 & 255;
              hbuf[2] = hold >>> 16 & 255;
              hbuf[3] = hold >>> 24 & 255;
              state.check = zlib_crc32.crc32(state.check, hbuf, 4, 0);
            }
            hold = 0;
            bits = 0;
            state.mode = _zlib_inflate.OS;
          /* falls through */
          case _zlib_inflate.OS:
            while (bits < 16) {
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            if (state.head) {
              state.head.xflags = hold & 255;
              state.head.os = hold >> 8;
            }
            if (state.flags & 512) {
              hbuf[0] = hold & 255;
              hbuf[1] = hold >>> 8 & 255;
              state.check = zlib_crc32.crc32(state.check, hbuf, 2, 0);
            }
            hold = 0;
            bits = 0;
            state.mode = _zlib_inflate.EXLEN;
          /* falls through */
          case _zlib_inflate.EXLEN:
            if (state.flags & 1024) {
              while (bits < 16) {
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              state.length = hold;
              if (state.head) {
                state.head.extra_len = hold;
              }
              if (state.flags & 512) {
                hbuf[0] = hold & 255;
                hbuf[1] = hold >>> 8 & 255;
                state.check = zlib_crc32.crc32(state.check, hbuf, 2, 0);
              }
              hold = 0;
              bits = 0;
            } else if (state.head) {
              state.head.extra = null;
            }
            state.mode = _zlib_inflate.EXTRA;
          /* falls through */
          case _zlib_inflate.EXTRA:
            if (state.flags & 1024) {
              copy = state.length;
              if (copy > have) {
                copy = have;
              }
              if (copy) {
                if (state.head) {
                  len = state.head.extra_len - state.length;
                  if (!state.head.extra) {
                    state.head.extra = new Uint8Array(state.head.extra_len);
                  }
                  state.head.extra.set(
                    input.subarray(
                      next,
                      // extra field is limited to 65536 bytes
                      // - no need for additional size check
                      next + copy
                    ),
                    /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/
                    len
                  );
                }
                if (state.flags & 512) {
                  state.check = zlib_crc32.crc32(state.check, input, copy, next);
                }
                have -= copy;
                next += copy;
                state.length -= copy;
              }
              if (state.length) {
                break inf_leave;
              }
            }
            state.length = 0;
            state.mode = _zlib_inflate.NAME;
          /* falls through */
          case _zlib_inflate.NAME:
            if (state.flags & 2048) {
              if (have === 0) {
                break inf_leave;
              }
              copy = 0;
              do {
                len = input[next + copy++];
                if (state.head && len && state.length < 65536) {
                  state.head.name += String.fromCharCode(len);
                }
              } while (len && copy < have);
              if (state.flags & 512) {
                state.check = zlib_crc32.crc32(state.check, input, copy, next);
              }
              have -= copy;
              next += copy;
              if (len) {
                break inf_leave;
              }
            } else if (state.head) {
              state.head.name = null;
            }
            state.length = 0;
            state.mode = _zlib_inflate.COMMENT;
          /* falls through */
          case _zlib_inflate.COMMENT:
            if (state.flags & 4096) {
              if (have === 0) {
                break inf_leave;
              }
              copy = 0;
              do {
                len = input[next + copy++];
                if (state.head && len && state.length < 65536) {
                  state.head.comment += String.fromCharCode(len);
                }
              } while (len && copy < have);
              if (state.flags & 512) {
                state.check = zlib_crc32.crc32(state.check, input, copy, next);
              }
              have -= copy;
              next += copy;
              if (len) {
                break inf_leave;
              }
            } else if (state.head) {
              state.head.comment = null;
            }
            state.mode = _zlib_inflate.HCRC;
          /* falls through */
          case _zlib_inflate.HCRC:
            if (state.flags & 512) {
              while (bits < 16) {
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              if (hold !== (state.check & 65535)) {
                strm.msg = "header crc mismatch";
                state.mode = _zlib_inflate.BAD;
                break;
              }
              hold = 0;
              bits = 0;
            }
            if (state.head) {
              state.head.hcrc = state.flags >> 9 & 1;
              state.head.done = true;
            }
            strm.adler = state.check = 0;
            state.mode = _zlib_inflate.TYPE;
            break;
          case _zlib_inflate.DICTID:
            while (bits < 32) {
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            strm.adler = state.check = _zlib_inflate.zswap32(hold);
            hold = 0;
            bits = 0;
            state.mode = _zlib_inflate.DICT;
          /* falls through */
          case _zlib_inflate.DICT:
            if (!state.havedict) {
              strm.next_out = put;
              strm.avail_out = left;
              strm.next_in = next;
              strm.avail_in = have;
              state.hold = hold;
              state.bits = bits;
              return zlib_constants.Z_NEED_DICT;
            }
            strm.adler = state.check = 1;
            state.mode = _zlib_inflate.TYPE;
          /* falls through */
          case _zlib_inflate.TYPE:
            if (flush === zlib_constants.Z_BLOCK || flush === zlib_constants.Z_TREES) {
              break inf_leave;
            }
          /* falls through */
          case _zlib_inflate.TYPEDO:
            if (state.last) {
              hold >>>= bits & 7;
              bits -= bits & 7;
              state.mode = _zlib_inflate.CHECK;
              break;
            }
            while (bits < 3) {
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            state.last = hold & 1;
            hold >>>= 1;
            bits -= 1;
            switch (hold & 3) {
              case 0:
                state.mode = _zlib_inflate.STORED;
                break;
              case 1:
                _zlib_inflate.fixedtables(state);
                state.mode = _zlib_inflate.LEN_;
                if (flush === zlib_constants.Z_TREES) {
                  hold >>>= 2;
                  bits -= 2;
                  break inf_leave;
                }
                break;
              case 2:
                state.mode = _zlib_inflate.TABLE;
                break;
              case 3:
                strm.msg = "invalid block type";
                state.mode = _zlib_inflate.BAD;
            }
            hold >>>= 2;
            bits -= 2;
            break;
          case _zlib_inflate.STORED:
            hold >>>= bits & 7;
            bits -= bits & 7;
            while (bits < 32) {
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            if ((hold & 65535) !== (hold >>> 16 ^ 65535)) {
              strm.msg = "invalid stored block lengths";
              state.mode = _zlib_inflate.BAD;
              break;
            }
            state.length = hold & 65535;
            hold = 0;
            bits = 0;
            state.mode = _zlib_inflate.COPY_;
            if (flush === zlib_constants.Z_TREES) {
              break inf_leave;
            }
          /* falls through */
          case _zlib_inflate.COPY_:
            state.mode = _zlib_inflate.COPY;
          /* falls through */
          case _zlib_inflate.COPY:
            copy = state.length;
            if (copy) {
              if (copy > have) {
                copy = have;
              }
              if (copy > left) {
                copy = left;
              }
              if (copy === 0) {
                break inf_leave;
              }
              output.set(input.subarray(next, next + copy), put);
              have -= copy;
              next += copy;
              left -= copy;
              put += copy;
              state.length -= copy;
              break;
            }
            state.mode = _zlib_inflate.TYPE;
            break;
          case _zlib_inflate.TABLE:
            while (bits < 14) {
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            state.nlen = (hold & 31) + 257;
            hold >>>= 5;
            bits -= 5;
            state.ndist = (hold & 31) + 1;
            hold >>>= 5;
            bits -= 5;
            state.ncode = (hold & 15) + 4;
            hold >>>= 4;
            bits -= 4;
            if (state.nlen > 286 || state.ndist > 30) {
              strm.msg = "too many length or distance symbols";
              state.mode = _zlib_inflate.BAD;
              break;
            }
            state.have = 0;
            state.mode = _zlib_inflate.LENLENS;
          /* falls through */
          case _zlib_inflate.LENLENS:
            while (state.have < state.ncode) {
              while (bits < 3) {
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              state.lens[order[state.have++]] = hold & 7;
              hold >>>= 3;
              bits -= 3;
            }
            while (state.have < 19) {
              state.lens[order[state.have++]] = 0;
            }
            state.lencode = state.lendyn;
            state.lenbits = 7;
            opts = { bits: state.lenbits };
            ret = zlib_inftrees.inflate_table(_zlib_inflate.CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts);
            state.lenbits = opts.bits;
            if (ret) {
              strm.msg = "invalid code lengths set";
              state.mode = _zlib_inflate.BAD;
              break;
            }
            state.have = 0;
            state.mode = _zlib_inflate.CODELENS;
          /* falls through */
          case _zlib_inflate.CODELENS:
            while (state.have < state.nlen + state.ndist) {
              for (; ; ) {
                here = state.lencode[hold & (1 << state.lenbits) - 1];
                here_bits = here >>> 24;
                here_op = here >>> 16 & 255;
                here_val = here & 65535;
                if (here_bits <= bits) {
                  break;
                }
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              if (here_val < 16) {
                hold >>>= here_bits;
                bits -= here_bits;
                state.lens[state.have++] = here_val;
              } else {
                if (here_val === 16) {
                  n = here_bits + 2;
                  while (bits < n) {
                    if (have === 0) {
                      break inf_leave;
                    }
                    have--;
                    hold += input[next++] << bits;
                    bits += 8;
                  }
                  hold >>>= here_bits;
                  bits -= here_bits;
                  if (state.have === 0) {
                    strm.msg = "invalid bit length repeat";
                    state.mode = _zlib_inflate.BAD;
                    break;
                  }
                  len = state.lens[state.have - 1];
                  copy = 3 + (hold & 3);
                  hold >>>= 2;
                  bits -= 2;
                } else if (here_val === 17) {
                  n = here_bits + 3;
                  while (bits < n) {
                    if (have === 0) {
                      break inf_leave;
                    }
                    have--;
                    hold += input[next++] << bits;
                    bits += 8;
                  }
                  hold >>>= here_bits;
                  bits -= here_bits;
                  len = 0;
                  copy = 3 + (hold & 7);
                  hold >>>= 3;
                  bits -= 3;
                } else {
                  n = here_bits + 7;
                  while (bits < n) {
                    if (have === 0) {
                      break inf_leave;
                    }
                    have--;
                    hold += input[next++] << bits;
                    bits += 8;
                  }
                  hold >>>= here_bits;
                  bits -= here_bits;
                  len = 0;
                  copy = 11 + (hold & 127);
                  hold >>>= 7;
                  bits -= 7;
                }
                if (state.have + copy > state.nlen + state.ndist) {
                  strm.msg = "invalid bit length repeat";
                  state.mode = _zlib_inflate.BAD;
                  break;
                }
                while (copy--) {
                  state.lens[state.have++] = len;
                }
              }
            }
            if (state.mode === _zlib_inflate.BAD) {
              break;
            }
            if (state.lens[256] === 0) {
              strm.msg = "invalid code -- missing end-of-block";
              state.mode = _zlib_inflate.BAD;
              break;
            }
            state.lenbits = 9;
            opts = { bits: state.lenbits };
            ret = zlib_inftrees.inflate_table(_zlib_inflate.LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts);
            state.lenbits = opts.bits;
            if (ret) {
              strm.msg = "invalid literal/lengths set";
              state.mode = _zlib_inflate.BAD;
              break;
            }
            state.distbits = 6;
            state.distcode = state.distdyn;
            opts = { bits: state.distbits };
            ret = zlib_inftrees.inflate_table(_zlib_inflate.DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts);
            state.distbits = opts.bits;
            if (ret) {
              strm.msg = "invalid distances set";
              state.mode = _zlib_inflate.BAD;
              break;
            }
            state.mode = _zlib_inflate.LEN_;
            if (flush === zlib_constants.Z_TREES) {
              break inf_leave;
            }
          /* falls through */
          case _zlib_inflate.LEN_:
            state.mode = _zlib_inflate.LEN;
          /* falls through */
          case _zlib_inflate.LEN:
            if (have >= 6 && left >= 258) {
              strm.next_out = put;
              strm.avail_out = left;
              strm.next_in = next;
              strm.avail_in = have;
              state.hold = hold;
              state.bits = bits;
              zlib_inffast.inflate_fast(strm, _out);
              put = strm.next_out;
              output = strm.output;
              left = strm.avail_out;
              next = strm.next_in;
              input = strm.input;
              have = strm.avail_in;
              hold = state.hold;
              bits = state.bits;
              if (state.mode === _zlib_inflate.TYPE) {
                state.back = -1;
              }
              break;
            }
            state.back = 0;
            for (; ; ) {
              here = state.lencode[hold & (1 << state.lenbits) - 1];
              here_bits = here >>> 24;
              here_op = here >>> 16 & 255;
              here_val = here & 65535;
              if (here_bits <= bits) {
                break;
              }
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            if (here_op && (here_op & 240) === 0) {
              last_bits = here_bits;
              last_op = here_op;
              last_val = here_val;
              for (; ; ) {
                here = state.lencode[last_val + ((hold & (1 << last_bits + last_op) - 1) >> last_bits)];
                here_bits = here >>> 24;
                here_op = here >>> 16 & 255;
                here_val = here & 65535;
                if (last_bits + here_bits <= bits) {
                  break;
                }
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              hold >>>= last_bits;
              bits -= last_bits;
              state.back += last_bits;
            }
            hold >>>= here_bits;
            bits -= here_bits;
            state.back += here_bits;
            state.length = here_val;
            if (here_op === 0) {
              state.mode = _zlib_inflate.LIT;
              break;
            }
            if (here_op & 32) {
              state.back = -1;
              state.mode = _zlib_inflate.TYPE;
              break;
            }
            if (here_op & 64) {
              strm.msg = "invalid literal/length code";
              state.mode = _zlib_inflate.BAD;
              break;
            }
            state.extra = here_op & 15;
            state.mode = _zlib_inflate.LENEXT;
          /* falls through */
          case _zlib_inflate.LENEXT:
            if (state.extra) {
              n = state.extra;
              while (bits < n) {
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              state.length += hold & (1 << state.extra) - 1;
              hold >>>= state.extra;
              bits -= state.extra;
              state.back += state.extra;
            }
            state.was = state.length;
            state.mode = _zlib_inflate.DIST;
          /* falls through */
          case _zlib_inflate.DIST:
            for (; ; ) {
              here = state.distcode[hold & (1 << state.distbits) - 1];
              here_bits = here >>> 24;
              here_op = here >>> 16 & 255;
              here_val = here & 65535;
              if (here_bits <= bits) {
                break;
              }
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            if ((here_op & 240) === 0) {
              last_bits = here_bits;
              last_op = here_op;
              last_val = here_val;
              for (; ; ) {
                here = state.distcode[last_val + ((hold & (1 << last_bits + last_op) - 1) >> last_bits)];
                here_bits = here >>> 24;
                here_op = here >>> 16 & 255;
                here_val = here & 65535;
                if (last_bits + here_bits <= bits) {
                  break;
                }
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              hold >>>= last_bits;
              bits -= last_bits;
              state.back += last_bits;
            }
            hold >>>= here_bits;
            bits -= here_bits;
            state.back += here_bits;
            if (here_op & 64) {
              strm.msg = "invalid distance code";
              state.mode = _zlib_inflate.BAD;
              break;
            }
            state.offset = here_val;
            state.extra = here_op & 15;
            state.mode = _zlib_inflate.DISTEXT;
          /* falls through */
          case _zlib_inflate.DISTEXT:
            if (state.extra) {
              n = state.extra;
              while (bits < n) {
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              state.offset += hold & (1 << state.extra) - 1;
              hold >>>= state.extra;
              bits -= state.extra;
              state.back += state.extra;
            }
            state.mode = _zlib_inflate.MATCH;
          /* falls through */
          case _zlib_inflate.MATCH:
            if (left === 0) {
              break inf_leave;
            }
            copy = _out - left;
            if (state.offset > copy) {
              copy = state.offset - copy;
              if (copy > state.whave) {
                if (state.sane) {
                  strm.msg = "invalid distance too far back";
                  state.mode = _zlib_inflate.BAD;
                  break;
                }
              }
              if (copy > state.wnext) {
                copy -= state.wnext;
                from = state.wsize - copy;
              } else {
                from = state.wnext - copy;
              }
              if (copy > state.length) {
                copy = state.length;
              }
              from_source = state.window;
            } else {
              from_source = output;
              from = put - state.offset;
              copy = state.length;
            }
            if (copy > left) {
              copy = left;
            }
            left -= copy;
            state.length -= copy;
            do {
              output[put++] = from_source[from++];
            } while (--copy);
            if (state.length === 0) {
              state.mode = _zlib_inflate.LEN;
            }
            break;
          case _zlib_inflate.LIT:
            if (left === 0) {
              break inf_leave;
            }
            output[put++] = state.length;
            left--;
            state.mode = _zlib_inflate.LEN;
            break;
          case _zlib_inflate.CHECK:
            if (state.wrap) {
              while (bits < 32) {
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold |= input[next++] << bits;
                bits += 8;
              }
              _out -= left;
              strm.total_out += _out;
              state.total += _out;
              if (_out) {
                strm.adler = state.check = /*UPDATE(state.check, put - _out, _out);*/
                state.flags ? zlib_crc32.crc32(state.check, output, _out, put - _out) : zlib_adler32.adler32(state.check, output, _out, put - _out);
              }
              _out = left;
              if ((state.flags ? hold : _zlib_inflate.zswap32(hold)) !== state.check) {
                strm.msg = "incorrect data check";
                state.mode = _zlib_inflate.BAD;
                break;
              }
              hold = 0;
              bits = 0;
            }
            state.mode = _zlib_inflate.LENGTH;
          /* falls through */
          case _zlib_inflate.LENGTH:
            if (state.wrap && state.flags) {
              while (bits < 32) {
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              if (hold !== (state.total & 4294967295)) {
                strm.msg = "incorrect length check";
                state.mode = _zlib_inflate.BAD;
                break;
              }
              hold = 0;
              bits = 0;
            }
            state.mode = _zlib_inflate.DONE;
          /* falls through */
          case _zlib_inflate.DONE:
            ret = zlib_constants.Z_STREAM_END;
            break inf_leave;
          case _zlib_inflate.BAD:
            ret = zlib_constants.Z_DATA_ERROR;
            break inf_leave;
          case _zlib_inflate.MEM:
            return zlib_constants.Z_MEM_ERROR;
          case _zlib_inflate.SYNC:
          /* falls through */
          default:
            return zlib_constants.Z_STREAM_ERROR;
        }
      }
    strm.next_out = put;
    strm.avail_out = left;
    strm.next_in = next;
    strm.avail_in = have;
    state.hold = hold;
    state.bits = bits;
    if (state.wsize || _out !== strm.avail_out && state.mode < _zlib_inflate.BAD && (state.mode < _zlib_inflate.CHECK || flush !== zlib_constants.Z_FINISH)) {
      if (_zlib_inflate.updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) {
        state.mode = _zlib_inflate.MEM;
        return zlib_constants.Z_MEM_ERROR;
      }
    }
    _in -= strm.avail_in;
    _out -= strm.avail_out;
    strm.total_in += _in;
    strm.total_out += _out;
    state.total += _out;
    if (state.wrap && _out) {
      strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/
      state.flags ? zlib_crc32.crc32(state.check, output, _out, strm.next_out - _out) : zlib_adler32.adler32(state.check, output, _out, strm.next_out - _out);
    }
    strm.data_type = state.bits + (state.last ? 64 : 0) + (state.mode === _zlib_inflate.TYPE ? 128 : 0) + (state.mode === _zlib_inflate.LEN_ || state.mode === _zlib_inflate.COPY_ ? 256 : 0);
    if ((_in === 0 && _out === 0 || flush === zlib_constants.Z_FINISH) && ret === zlib_constants.Z_OK) {
      ret = zlib_constants.Z_BUF_ERROR;
    }
    return ret;
  }
  static inflateEnd(strm) {
    if (!strm || !strm.state) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    let state = strm.state;
    if (state.window) {
      state.window = null;
    }
    strm.state = null;
    return zlib_constants.Z_OK;
  }
  static inflateGetHeader(strm, head) {
    if (!strm || !strm.state) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    let state = strm.state;
    if ((state.wrap & 2) === 0) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    state.head = head;
    head.done = false;
    return zlib_constants.Z_OK;
  }
  static inflateSetDictionary(strm, dictionary) {
    const dictLength = dictionary.length;
    let state;
    let dictid;
    let ret;
    if (!strm || !strm.state) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    state = strm.state;
    if (state.wrap !== 0 && state.mode !== _zlib_inflate.DICT) {
      return zlib_constants.Z_STREAM_ERROR;
    }
    if (state.mode === _zlib_inflate.DICT) {
      dictid = 1;
      dictid = zlib_adler32.adler32(dictid, dictionary, dictLength, 0);
      if (dictid !== state.check) {
        return zlib_constants.Z_DATA_ERROR;
      }
    }
    ret = _zlib_inflate.updatewindow(strm, dictionary, dictLength, dictLength);
    if (ret) {
      state.mode = _zlib_inflate.MEM;
      return zlib_constants.Z_MEM_ERROR;
    }
    state.havedict = 1;
    return zlib_constants.Z_OK;
  }
};
zlib_inflate.CODES = 0;
zlib_inflate.LENS = 1;
zlib_inflate.DISTS = 2;
zlib_inflate.HEAD = 1;
zlib_inflate.FLAGS = 2;
zlib_inflate.TIME = 3;
zlib_inflate.OS = 4;
zlib_inflate.EXLEN = 5;
zlib_inflate.EXTRA = 6;
zlib_inflate.NAME = 7;
zlib_inflate.COMMENT = 8;
zlib_inflate.HCRC = 9;
zlib_inflate.DICTID = 10;
zlib_inflate.DICT = 11;
zlib_inflate.TYPE = 12;
zlib_inflate.TYPEDO = 13;
zlib_inflate.STORED = 14;
zlib_inflate.COPY_ = 15;
zlib_inflate.COPY = 16;
zlib_inflate.TABLE = 17;
zlib_inflate.LENLENS = 18;
zlib_inflate.CODELENS = 19;
zlib_inflate.LEN_ = 20;
zlib_inflate.LEN = 21;
zlib_inflate.LENEXT = 22;
zlib_inflate.DIST = 23;
zlib_inflate.DISTEXT = 24;
zlib_inflate.MATCH = 25;
zlib_inflate.LIT = 26;
zlib_inflate.CHECK = 27;
zlib_inflate.LENGTH = 28;
zlib_inflate.DONE = 29;
zlib_inflate.BAD = 30;
zlib_inflate.MEM = 31;
zlib_inflate.SYNC = 32;
zlib_inflate.ENOUGH_LENS = 852;
zlib_inflate.ENOUGH_DISTS = 592;
zlib_inflate.MAX_WBITS = 15;
zlib_inflate.DEF_WBITS = zlib_inflate.MAX_WBITS;
zlib_inflate.virgin = true;

// obj/full/Pako/zlib/messages.js
var zlib_messages = class {
  static msg(index) {
    switch (index) {
      case 2:
        return "need dictionary";
      /* Z_NEED_DICT       2  */
      case 1:
        return "stream end";
      /* Z_STREAM_END      1  */
      case 0:
        return "";
      /* Z_OK              0  */
      case -1:
        return "file error";
      /* Z_ERRNO         (-1) */
      case -2:
        return "stream error";
      /* Z_STREAM_ERROR  (-2) */
      case -3:
        return "data error";
      /* Z_DATA_ERROR    (-3) */
      case -4:
        return "insufficient memory";
      /* Z_MEM_ERROR     (-4) */
      case -5:
        return "buffer error";
      /* Z_BUF_ERROR     (-5) */
      case -6:
        return "incompatible version";
      /* Z_VERSION_ERROR (-6) */
      default:
        return "unknowwn error";
    }
  }
};

// obj/full/Pako/zlib/inftrees.js
var zlib_inftrees = class _zlib_inftrees {
  static inflate_table(type, lens, lens_index, codes, table, table_index, work, opts) {
    const bits = opts.bits;
    let len;
    let sym;
    let min = 0;
    let max;
    let root;
    let curr = 0;
    let drop = 0;
    let left = 0;
    let used = 0;
    let huff = 0;
    let incr;
    let fill;
    let low;
    let mask;
    let next;
    let base;
    let base_index = 0;
    let end;
    const count = new Uint16Array(_zlib_inftrees.MAXBITS + 1);
    const offs = new Uint16Array(_zlib_inftrees.MAXBITS + 1);
    let extra;
    let extra_index = 0;
    let here_bits;
    let here_op;
    let here_val;
    for (len = 0; len <= _zlib_inftrees.MAXBITS; len++) {
      count[len] = 0;
    }
    for (sym = 0; sym < codes; sym++) {
      count[lens[lens_index + sym]]++;
    }
    root = bits;
    for (max = _zlib_inftrees.MAXBITS; max >= 1; max--) {
      if (count[max] !== 0) {
        break;
      }
    }
    if (root > max) {
      root = max;
    }
    if (max === 0) {
      table[table_index++] = 1 << 24 | 64 << 16 | 0;
      table[table_index++] = 1 << 24 | 64 << 16 | 0;
      opts.bits = 1;
      return 0;
    }
    for (min = 1; min < max; min++) {
      if (count[min] !== 0) {
        break;
      }
    }
    if (root < min) {
      root = min;
    }
    left = 1;
    for (len = 1; len <= _zlib_inftrees.MAXBITS; len++) {
      left <<= 1;
      left -= count[len];
      if (left < 0) {
        return -1;
      }
    }
    if (left > 0 && (type === _zlib_inftrees.CODES || max !== 1)) {
      return -1;
    }
    offs[1] = 0;
    for (len = 1; len < _zlib_inftrees.MAXBITS; len++) {
      offs[len + 1] = offs[len] + count[len];
    }
    for (sym = 0; sym < codes; sym++) {
      if (lens[lens_index + sym] !== 0) {
        work[offs[lens[lens_index + sym]]++] = sym;
      }
    }
    if (type === _zlib_inftrees.CODES) {
      base = work;
      extra = new Uint8Array();
      end = 19;
    } else if (type === _zlib_inftrees.LENS) {
      base = _zlib_inftrees.lbase;
      base_index -= 257;
      extra = _zlib_inftrees.lext;
      extra_index -= 257;
      end = 256;
    } else {
      base = _zlib_inftrees.dbase;
      extra = _zlib_inftrees.dext;
      end = -1;
    }
    huff = 0;
    sym = 0;
    len = min;
    next = table_index;
    curr = root;
    drop = 0;
    low = -1;
    used = 1 << root;
    mask = used - 1;
    if (type === _zlib_inftrees.LENS && used > _zlib_inftrees.ENOUGH_LENS || type === _zlib_inftrees.DISTS && used > _zlib_inftrees.ENOUGH_DISTS) {
      return 1;
    }
    for (; ; ) {
      here_bits = len - drop;
      if (work[sym] < end) {
        here_op = 0;
        here_val = work[sym];
      } else if (work[sym] > end) {
        here_op = extra[extra_index + work[sym]];
        here_val = base[base_index + work[sym]];
      } else {
        here_op = 32 + 64;
        here_val = 0;
      }
      incr = 1 << len - drop;
      fill = 1 << curr;
      min = fill;
      do {
        fill -= incr;
        table[next + (huff >> drop) + fill] = here_bits << 24 | here_op << 16 | here_val | 0;
      } while (fill !== 0);
      incr = 1 << len - 1;
      while (huff & incr) {
        incr >>= 1;
      }
      if (incr !== 0) {
        huff &= incr - 1;
        huff += incr;
      } else {
        huff = 0;
      }
      sym++;
      if (--count[len] === 0) {
        if (len === max) {
          break;
        }
        len = lens[lens_index + work[sym]];
      }
      if (len > root && (huff & mask) !== low) {
        if (drop === 0) {
          drop = root;
        }
        next += min;
        curr = len - drop;
        left = 1 << curr;
        while (curr + drop < max) {
          left -= count[curr + drop];
          if (left <= 0) {
            break;
          }
          curr++;
          left <<= 1;
        }
        used += 1 << curr;
        if (type === _zlib_inftrees.LENS && used > _zlib_inftrees.ENOUGH_LENS || type === _zlib_inftrees.DISTS && used > _zlib_inftrees.ENOUGH_DISTS) {
          return 1;
        }
        low = huff & mask;
        table[low] = root << 24 | curr << 16 | next - table_index | 0;
      }
    }
    if (huff !== 0) {
      table[next + huff] = len - drop << 24 | 64 << 16 | 0;
    }
    opts.bits = root;
    return 0;
  }
};
zlib_inftrees.MAXBITS = 15;
zlib_inftrees.ENOUGH_LENS = 852;
zlib_inftrees.ENOUGH_DISTS = 592;
zlib_inftrees.CODES = 0;
zlib_inftrees.LENS = 1;
zlib_inftrees.DISTS = 2;
zlib_inftrees.lbase = new Uint16Array([
  3,
  4,
  5,
  6,
  7,
  8,
  9,
  10,
  11,
  13,
  15,
  17,
  19,
  23,
  27,
  31,
  35,
  43,
  51,
  59,
  67,
  83,
  99,
  115,
  131,
  163,
  195,
  227,
  258,
  0,
  0
]);
zlib_inftrees.lext = new Uint8Array([
  16,
  16,
  16,
  16,
  16,
  16,
  16,
  16,
  17,
  17,
  17,
  17,
  18,
  18,
  18,
  18,
  19,
  19,
  19,
  19,
  20,
  20,
  20,
  20,
  21,
  21,
  21,
  21,
  16,
  72,
  78
]);
zlib_inftrees.dbase = new Uint16Array([
  1,
  2,
  3,
  4,
  5,
  7,
  9,
  13,
  17,
  25,
  33,
  49,
  65,
  97,
  129,
  193,
  257,
  385,
  513,
  769,
  1025,
  1537,
  2049,
  3073,
  4097,
  6145,
  8193,
  12289,
  16385,
  24577,
  0,
  0
]);
zlib_inftrees.dext = new Uint8Array([
  16,
  16,
  16,
  16,
  17,
  17,
  18,
  18,
  19,
  19,
  20,
  20,
  21,
  21,
  22,
  22,
  23,
  23,
  24,
  24,
  25,
  25,
  26,
  26,
  27,
  27,
  28,
  28,
  29,
  29,
  64,
  64
]);

// obj/full/Pako/zlib/zstream.js
var zlib_ZStream = class {
  constructor() {
    this.input = null;
    this.next_in = 0;
    this.avail_in = 0;
    this.total_in = 0;
    this.output = null;
    this.next_out = 0;
    this.avail_out = 0;
    this.total_out = 0;
    this.msg = "";
    this.state = null;
    this.data_type = 2;
    this.adler = 0;
  }
};

// obj/full/webPage.js
var YWebPage = class _YWebPage {
  edit(widgetIndex) {
    _YWebPage.widgets[widgetIndex].edit();
  }
  static get editMenuItem() {
    return _YWebPage._editMenuItem;
  }
  static get deleteMenuItem() {
    return _YWebPage._deleteMenuItem;
  }
  static get disableMarkerMenuItem() {
    return _YWebPage._disableMarkerMenuItem;
  }
  static get clearDataloggerMenuItem() {
    return _YWebPage._clearDataloggerMenuItem;
  }
  static get addMarkerMenuItem() {
    return _YWebPage._addMarkerMenuItem;
  }
  static get markersSubMenuItems() {
    return _YWebPage._markersSubMenuItems;
  }
  static get contextMarkerSubMenu() {
    return _YWebPage._contextMarkerSubMenu;
  }
  static get contextWidgetsSubMenu() {
    return _YWebPage._contextWidgetsSubMenu;
  }
  static get resetDataViewMenuItem() {
    return _YWebPage._resetDataViewMenuItem;
  }
  static get snapshotMenuItem() {
    return _YWebPage._snapshotMenuItem;
  }
  static applicationGlobalinit() {
    if (_YWebPage._MarkerCountPerXaxis == 0) {
      let xAxisProp = new XaxisDescription();
      let propDesc = GenericProperties.getAllProperties(xAxisProp);
      for (let propname in propDesc.byName) {
        if (propname.startsWith("markers"))
          _YWebPage._MarkerCountPerXaxis++;
      }
    }
    if (_YWebPage.manager == null)
      _YWebPage.manager = new sensorsManager();
    if (!_YWebPage.readonly && _YWebPage._editor == null)
      _YWebPage._editor = new PropertiesForm2();
    if (!_YWebPage.readonly && _YWebPage._mainform == null)
      _YWebPage._mainform = new mainForm();
    if (_YWebPage._contextMenu == null) {
      _YWebPage._contextMenu = new ContextMenu(_YWebPage.baseSize, (mouseX, mouseY) => {
        _YWebPage.menuIsOpening(mouseX, mouseY);
      }, constants.generalFontFamily);
      _YWebPage._contextMarkerSubMenu = new ContextSubMenu(_YWebPage.baseSize, _YWebPage._contextMenu, null, constants.generalFontFamily);
      for (let i = 0; i < _YWebPage._MarkerCountPerXaxis; i++) {
        _YWebPage._markersSubMenuItems.push(_YWebPage._contextMarkerSubMenu.addMenuItem(ressources.AddMarkerIcon(_YWebPage.baseSize.toString(), i + 1), "Place marker " + (i + 1).toString(), () => {
          let target = _YWebPage._markersSubMenuItems[i].userdata;
          target.startMarkerCapture(i);
        }));
      }
      if (!_YWebPage.readonly) {
        _YWebPage._contextWidgetsSubMenu = new ContextSubMenu(_YWebPage.baseSize, _YWebPage._contextMenu, (mouseX, mouseY) => {
          _YWebPage.WidgetsSubMenuIsOpening(mouseX, mouseY);
        }, constants.generalFontFamily);
        _YWebPage._widgetsMenuItem = _YWebPage._contextMenu.addSubMenuItem(ressources.AllIcon(_YWebPage.baseSize.toString(), false, false, false, false), "All widgets", _YWebPage._contextWidgetsSubMenu);
        _YWebPage._editMenuItem = _YWebPage._contextMenu.addMenuItem(ressources.EditIcon(_YWebPage.baseSize.toString(), false, false, false, false), "Configure this widget", () => {
          _YWebPage.editWidget();
        });
        _YWebPage._deleteMenuItem = _YWebPage._contextMenu.addMenuItem(ressources.TrashIcon(_YWebPage.baseSize.toString(), false, false, false, false), "Delete this widget", () => {
          _YWebPage.deleteWidget();
        });
        _YWebPage._editMenuItem.visible = false;
        _YWebPage._deleteMenuItem.visible = false;
      }
      _YWebPage._resetDataViewMenuItem = _YWebPage._contextMenu.addMenuItem(ressources.resetViewPort(_YWebPage.baseSize.toString(), false, false, false, false), "Reset dataview", () => {
        _YWebPage.resetDataView();
      });
      _YWebPage._resetDataViewMenuItem.visible = false;
      if (!_YWebPage.readonly) {
        _YWebPage._clearDataloggerMenuItem = _YWebPage._contextMenu.addMenuItem(ressources.EraseDatalogger(_YWebPage.baseSize.toString(), false, false, false, false), "Clear datalogger", () => {
          _YWebPage.clearDatalogger();
        });
        _YWebPage._resetDataViewMenuItem.visible = false;
      }
      _YWebPage._contextMenu.AddSeparator();
      if (!_YWebPage.readonly) {
        _YWebPage._addMarkerMenuItem = _YWebPage._contextMenu.addSubMenuItem(ressources.AddMarker(_YWebPage.baseSize.toString(), false, true, true, false), "Place markers", _YWebPage._contextMarkerSubMenu);
        _YWebPage._disableMarkerMenuItem = _YWebPage._contextMenu.addMenuItem(ressources.DeleteMarker(_YWebPage.baseSize.toString(), false, true, false, true), "Disable Allmarkers", () => {
          _YWebPage.disableAllMarker();
        });
        _YWebPage._addMarkerMenuItem.visible = false;
        _YWebPage._disableMarkerMenuItem.visible = false;
        _YWebPage._contextMenu.AddSeparator();
        _YWebPage._contextMenu.addMenuItem(ressources.SolidGaugeIcon(_YWebPage.baseSize.toString(), false, true, true, false), "Add a new Solid gauge", () => {
          _YWebPage.newGaugeWidget();
        });
        _YWebPage._contextMenu.addMenuItem(ressources.AngularGaugeIcon(_YWebPage.baseSize.toString(), false, true, true, false), "Add a new Angular gauge", () => {
          _YWebPage.newAngularGaugeWidget();
        });
        _YWebPage._contextMenu.addMenuItem(ressources.DigitalDisplayIcon(_YWebPage.baseSize.toString(), false, true, true, false), "Add a new Digital Display", () => {
          _YWebPage.newDigitalDisplayWidget();
        });
        _YWebPage._contextMenu.addMenuItem(ressources.GraphIcon(_YWebPage.baseSize.toString(), false, true, true, false), "Add a new Graph", () => {
          _YWebPage.newGraphWidget();
        });
      }
      _YWebPage._contextMenu.addMenuItem(ressources.RawDataIcon(_YWebPage.baseSize.toString(), false, false, false, false), "Show raw data", () => {
        rawDataForm.show();
      });
      _YWebPage._snapshotMenuItem = _YWebPage._contextMenu.addMenuItem(ressources.snapshotIcon(_YWebPage.baseSize.toString(), false, false, false, false), "Snapshot", () => {
        _YWebPage.snapShot();
      });
      if (!_YWebPage.readonly) {
        _YWebPage._contextMenu.AddSeparator();
        _YWebPage._contextMenu.addMenuItem(ressources.ConfigureIcon(_YWebPage.baseSize.toString(), false, false, false, false), "Global configuration", () => {
          configForm.show();
        });
        _YWebPage._contextMenu.addMenuItem(ressources.SaveIcon(_YWebPage.baseSize.toString(), false, false, false, false), "Save now", () => {
          _YWebPage.save(true);
        });
      }
      _YWebPage._contextMenu.addMenuItem(ressources.LogFileIcon(_YWebPage.baseSize.toString(), false, false, false, false), "Show logs", () => {
        _YWebPage.showLogsWindow();
      });
    }
  }
  static RegisterDeviceArrivalCallback(arrivalCallback) {
    sensorsManager.RegisterDeviceArrivalCallback(arrivalCallback);
  }
  static RegisterDeviceRemovalCallback(removalCallback) {
    sensorsManager.RegisterDeviceRemovalCallback(removalCallback);
  }
  //#ifndef READONLY
  static WidgetsSubMenuIsOpening(mouseX, mouseY) {
    _YWebPage._contextWidgetsSubMenu.clearAllContents();
    for (let i = 0; i < _YWebPage.widgets.length; i++) {
      let icon = "";
      let subMenu = new ContextSubMenu(_YWebPage.baseSize, _YWebPage._contextWidgetsSubMenu, (mouseX2, mouseY2) => {
        _YWebPage.specificWidgetSubMenuIsOpening(subMenu, mouseX2, mouseY2);
      }, constants.generalFontFamily);
      subMenu.userdata = _YWebPage.widgets[i];
      if (_YWebPage.widgets[i] instanceof graphWidget)
        icon = ressources.GraphIcon(_YWebPage.baseSize.toString(), false, false, false, false);
      if (_YWebPage.widgets[i] instanceof digitalDisplayWidget)
        icon = ressources.DigitalDisplayIcon(_YWebPage.baseSize.toString(), false, false, false, false);
      if (_YWebPage.widgets[i] instanceof gaugeWidget)
        icon = ressources.SolidGaugeIcon(_YWebPage.baseSize.toString(), false, false, false, false);
      if (_YWebPage.widgets[i] instanceof angularGaugeWidget)
        icon = ressources.AngularGaugeIcon(_YWebPage.baseSize.toString(), false, false, false, false);
      _YWebPage._contextWidgetsSubMenu.addSubMenuItem(icon, _YWebPage.widgets[i].Text, subMenu);
    }
  }
  static specificWidgetSubMenuIsOpening(source, mouseX, mouseY) {
    if (source.menuItemsCount > 0)
      return;
    let w = source.userdata;
    source.addMenuItem(ressources.EditIcon(_YWebPage.baseSize.toString(), false, false, false, false), "Configure", () => {
      w.edit();
    });
    source.addMenuItem(ressources.TrashIcon(_YWebPage.baseSize.toString(), false, false, false, false), "Delete", () => {
      _YWebPage.deleteWidget(w);
    });
    if (w instanceof graphWidget) {
      source.addMenuItem(ressources.resetViewPort(_YWebPage.baseSize.toString(), false, false, false, false), "Reset dataview", () => {
        w.resetDataView();
      });
      source.addMenuItem(ressources.EraseDatalogger(_YWebPage.baseSize.toString(), false, false, false, false), "Clear datalogger", () => {
        w.clearDataLogger();
      });
    }
  }
  //#endif
  static showLogsWindow() {
    logForm.show();
  }
  static snapShot() {
    let w = _YWebPage._snapshotMenuItem.userdata;
    w.snapshot();
  }
  static menuIsOpening(mouseX, mouseY) {
    if (_YWebPage.disableMarkerMenuItem != null)
      _YWebPage.disableMarkerMenuItem.visible = false;
    if (_YWebPage.addMarkerMenuItem != null)
      _YWebPage.addMarkerMenuItem.visible = false;
    if (_YWebPage.clearDataloggerMenuItem != null)
      _YWebPage.clearDataloggerMenuItem.visible = false;
    if (_YWebPage.editMenuItem != null)
      _YWebPage.editMenuItem.visible = false;
    if (_YWebPage.deleteMenuItem != null)
      _YWebPage.deleteMenuItem.visible = false;
    _YWebPage.resetDataViewMenuItem.visible = false;
    _YWebPage.snapshotMenuItem.visible = false;
    for (let i = 0; i < _YWebPage.widgets.length; i++) {
      _YWebPage.widgets[i].contextMenuCallBack(mouseX, mouseY);
    }
    _YWebPage._widgetsMenuItem.visible = !_YWebPage.readonly && _YWebPage.widgets.length > 0 && !_YWebPage._editMenuItem.visible;
    _YWebPage._contextMenu.refresh();
  }
  static resetDataView() {
    _YWebPage._resetDataViewMenuItem.userdata.resetDataView();
  }
  //#ifndef READONLY
  static editWidget() {
    let w = _YWebPage._editMenuItem.userdata;
    w.edit();
  }
  static disableAllMarker() {
    _YWebPage._disableMarkerMenuItem.userdata.disableAllMarkers();
  }
  static clearDatalogger() {
    _YWebPage._clearDataloggerMenuItem.userdata.clearDataLogger();
  }
  static deleteWidget(widget) {
    let w = typeof widget == "undefined" ? _YWebPage._deleteMenuItem.userdata : widget;
    confirm.ask(w.confirmDeleteString, () => {
      if (!_YWebPage.readonly && w.isBeingEdited && _YWebPage._editor != null)
        _YWebPage._editor.hide();
      w.delete();
      constants.edited = true;
      for (let i = _YWebPage.widgets.length - 1; i >= 0; i--) {
        if (_YWebPage.widgets[i] == w) {
          _YWebPage.widgets.splice(i, 1);
        }
      }
      if (_YWebPage.widgets.length <= 0 && _YWebPage._mainform != null)
        _YWebPage._mainform.show();
    }, null, null);
  }
  static FindUniqueNewName(prefix) {
    let n = 1;
    for (let i = 0; i < _YWebPage.widgets.length; i++) {
      if (_YWebPage.widgets[i].Text.startsWith(prefix))
        n++;
    }
    if (n > 1)
      prefix = prefix + " " + n.toString();
    return prefix;
  }
  static newGraphWidget() {
    let w = window.innerWidth / 2 >> 0;
    let h = window.innerHeight / 2 >> 0;
    if (w < 350)
      w = 350;
    if (h < 200)
      w = 200;
    let x = (window.innerWidth - w) / 2 >> 0;
    let y = (window.innerHeight - h) / 2 >> 0;
    let index = _YWebPage.widgets.length;
    if (index > 0) {
      x = index * 25;
      y = index * 25;
    }
    let wdg = new graphWidget(null, _YWebPage._editor, x, y, 765, 470);
    wdg.set_name(_YWebPage.FindUniqueNewName("New Graph"));
    _YWebPage.widgets.push(wdg);
    wdg.resize(w, h);
    if (this._mainform != null)
      this._mainform.hide();
    if (!_YWebPage.readonly) {
      if (_YWebPage._editor != null && _YWebPage._editor.visible)
        wdg.edit();
    }
  }
  static newAngularGaugeWidget() {
    let s = Math.min(window.innerWidth, window.innerHeight) / 2;
    if (s < 350)
      s = 350;
    let x = (window.innerWidth - s) / 2 >> 0;
    let y = (window.innerHeight - s) / 2 >> 0;
    let w = s >> 0;
    let h = s >> 0;
    let index = _YWebPage.widgets.length;
    if (index > 0) {
      x = index * 25;
      y = index * 25;
    }
    let wdg = new angularGaugeWidget(null, _YWebPage._editor, x, y, 375, 300);
    wdg.set_name(_YWebPage.FindUniqueNewName("New Angular Gauge"));
    _YWebPage.widgets.push(wdg);
    wdg.resize(w, h);
    if (this._mainform != null)
      this._mainform.hide();
    if (!_YWebPage.readonly) {
      if (_YWebPage._editor != null && _YWebPage._editor.visible)
        wdg.edit();
    }
  }
  static newGaugeWidget() {
    let s = Math.min(window.innerWidth, window.innerHeight) / 2;
    if (s < 350)
      s = 350;
    let x = (window.innerWidth - s) / 2 >> 0;
    let y = (window.innerHeight - s) / 2 >> 0;
    let w = s >> 0;
    let h = s >> 0;
    let index = _YWebPage.widgets.length;
    if (index > 0) {
      x = index * 25;
      y = index * 25;
    }
    let wdg = new gaugeWidget(null, _YWebPage._editor, x, y, 355, 201);
    wdg.set_name(_YWebPage.FindUniqueNewName("New Solid Gauge"));
    _YWebPage.widgets.push(wdg);
    wdg.resize(w, h);
    if (this._mainform != null)
      this._mainform.hide();
    if (!_YWebPage.readonly) {
      if (_YWebPage._editor != null && _YWebPage._editor.visible)
        wdg.edit();
    }
  }
  static newDigitalDisplayWidget() {
    let s = Math.min(window.innerWidth, window.innerHeight) / 8;
    if (s < 90)
      s = 90;
    let x = (window.innerWidth - 4 * s) / 2 >> 0;
    let y = (window.innerHeight - s) / 2 >> 0;
    let w = 4 * s >> 0;
    let h = s >> 0;
    let index = _YWebPage.widgets.length;
    if (index > 0) {
      x = index * 25;
      y = index * 25;
    }
    let wdg = new digitalDisplayWidget(null, _YWebPage._editor, x, y, 410, 125);
    wdg.set_name(_YWebPage.FindUniqueNewName("New Digital Display"));
    let n = 1;
    for (let i = 0; i < _YWebPage.widgets.length; i++) {
      if (_YWebPage.widgets[i].Text.startsWith(wdg.Text))
        n++;
    }
    if (n > 1)
      wdg.Text = wdg.Text + " " + n.toString();
    _YWebPage.widgets.push(wdg);
    wdg.resize(w, h);
    if (this._mainform != null)
      this._mainform.hide();
    if (!_YWebPage.readonly) {
      if (_YWebPage._editor != null && _YWebPage._editor.visible)
        wdg.edit();
    }
  }
  static refreshPropertiesForm() {
    if (!_YWebPage.readonly && _YWebPage._editor != null)
      _YWebPage._editor.refresh();
  }
  //#endif
  static sensorListHaschanged() {
    _YWebPage.refreshPropertiesForm();
  }
  static EmptyReadonlyConfig() {
    alert2.show("Yocto-Visualization Error\n\nConfiguration is both empty and read only, check configuration file.");
  }
  static async ForceReloadConfig(newXMLdata) {
    if (!_YWebPage.readonly) {
      if (_YWebPage._editor != null)
        _YWebPage._editor.visible = false;
      configForm.hide();
      rawDataForm.hide();
      HubEdit.hide();
    }
    logForm.log("Reloading the whole configuration.");
    await sensorsManager.clearHublist();
    for (let i = _YWebPage.widgets.length - 1; i >= 0; i--) {
      _YWebPage.widgets[i].destroy();
      _YWebPage.widgets.splice(i, 1);
    }
    _YWebPage.loadFromXML(newXMLdata);
    constants.edited = false;
  }
  static ConfigChanged(newXMLdata) {
    if (_YWebPage.ConfigChangedConfirmWindows != null) {
      _YWebPage.ConfigChangedConfirmWindows.hide();
    }
    let newCRC = constants.crc32(newXMLdata);
    if (newCRC == _YWebPage.xmlDataCRC)
      return;
    logForm.log("Config file changed behind our back!");
    if (!this.readonly) {
      _YWebPage.ConfigChangedConfirmWindows = confirm.ask("Configuration has changed on server\nWould you like to reload it?", () => {
        _YWebPage.ForceReloadConfig(newXMLdata);
        _YWebPage.ConfigChangedConfirmWindows = null;
      }, () => {
        _YWebPage.ConfigChangedConfirmWindows = null;
      }, null);
    } else {
      _YWebPage.ForceReloadConfig(newXMLdata);
    }
    _YWebPage.xmlDataCRC = newCRC;
    constants.edited = false;
  }
  static ShowSaveReminder(visible) {
    if (_YWebPage.saveIntDiv == null) {
      _YWebPage.saveIntDiv = document.createElement("DIV");
      _YWebPage.saveIntDiv.style.position = "fixed";
      _YWebPage.saveIntDiv.style.left = "5px";
      _YWebPage.saveIntDiv.style.top = "5px";
      _YWebPage.saveIntDiv.style.zIndex = "200000";
      _YWebPage.saveIntDiv.style.cursor = "pointer";
      _YWebPage.saveIntDiv.innerHTML = ressources.SaveNeededIcon((48 * constants.guiDPIFactor).toString(), false, false, false, false);
      _YWebPage.saveIntDiv.addEventListener("click", () => {
        _YWebPage.saveIntDiv.style.display = "none";
        _YWebPage.save(true);
      });
      document.body.appendChild(_YWebPage.saveIntDiv);
    }
    _YWebPage.saveIntDiv.style.display = visible ? "" : "none";
  }
  //#endif
  static loadFromXML(xmlData) {
    if (xmlData == "") {
      if (!_YWebPage.readonly && _YWebPage._mainform != null) {
        _YWebPage._mainform.show();
      } else {
        _YWebPage.EmptyReadonlyConfig();
      }
      return;
    }
    let parser = new DOMParser();
    let doc = parser.parseFromString(xmlData.replace("<!-->", "<!-- "), "application/xml");
    let root = null;
    doc.childNodes.forEach((node) => {
      if (node.nodeName.toUpperCase() == "ROOT" && root == null)
        root = node;
    });
    if (root == null)
      throw new Error("No ROOT top node in XML data");
    root.childNodes.forEach((node) => {
      if (node.nodeName.toUpperCase() == "SENSORS") {
        sensorsManager.setKnownSensors(new YXmlNode(node));
      }
    });
    root.childNodes.forEach((node) => {
      let editor = null;
      editor = _YWebPage._editor;
      switch (node.nodeName.toUpperCase()) {
        case "CONFIG":
          constants.Init(new YXmlNode(node));
          break;
        case "GRAPHFORM":
          _YWebPage.widgets.push(new graphWidget(new YXmlNode(node), editor));
          break;
        case "ANGULARGAUGEFORM":
          _YWebPage.widgets.push(new angularGaugeWidget(new YXmlNode(node), editor));
          break;
        case "GAUGEFORM":
          _YWebPage.widgets.push(new gaugeWidget(new YXmlNode(node), editor));
          break;
        case "DIGITALDISPLAYFORM":
          _YWebPage.widgets.push(new digitalDisplayWidget(new YXmlNode(node), editor));
          break;
      }
      sensorsManager.registerChangeCallback(() => {
        _YWebPage.sensorListHaschanged();
      });
      if (node.nodeName.toUpperCase() == "ROOT" && root == null)
        root = node;
    });
    if (_YWebPage.widgets.length <= 0) {
      constants.edited = false;
      if (!_YWebPage.readonly && _YWebPage._mainform != null) {
        _YWebPage._mainform.show();
      } else {
        _YWebPage.EmptyReadonlyConfig();
      }
    } else if (!_YWebPage.readonly && _YWebPage._mainform != null)
      _YWebPage._mainform.hide();
  }
  static AnimateNewVerwsionMessage(step) {
    _YWebPage.NewVersionmessageDiv.style.left = Math.round(100 - step) + "%";
    if (step < 100) {
      step += 2;
      if (step > 100)
        step = 100;
      setTimeout(() => {
        _YWebPage.AnimateNewVerwsionMessage(step);
      });
    }
  }
  static StartInstaller(roothub) {
    let installerDiv = document.createElement("DIV");
    installerDiv.style.position = "absolute";
    installerDiv.style.top = "50%";
    installerDiv.style.left = "50%";
    installerDiv.style.transform = "translate(-50%,-50%)";
    installerDiv.style.width = "550px";
    installerDiv.style.height = "380px";
    installerDiv.style.zIndex = _YWebPage.getTopZIndex().toString();
    installerDiv.style.border = "1px solid black";
    installerDiv.style.boxShadow = "rgba(0, 0, 0, 0.05) 4px 4px 4px 1px";
    document.body.appendChild(installerDiv);
    let options = {};
    if (roothub != null)
      options = {
        "protocol": roothub.protocol,
        "addr": roothub.addr,
        "port": parseInt(roothub.port),
        "path": roothub.path,
        "cancelable": true
      };
    window.startYV4W_installer(installerDiv, options);
  }
  static DownloadAndStartInstaller(installerURL, roothub, deleteTimer) {
    clearTimeout(deleteTimer);
    console.log("Downloading installer (" + installerURL + ")...");
    let P = document.createElement("P");
    P.style.textAlign = "center";
    P.style.fontFamily = constants.generalFontFamily;
    P.style.fontSize = "16px";
    P.innerHTML = "Downloading Installer<br>Please wait...";
    _YWebPage.NewVersionmessageDiv.removeChild(_YWebPage.NewVersionmessageDiv.firstChild);
    _YWebPage.NewVersionmessageDiv.appendChild(P);
    let script = document.createElement("script");
    script.async = false;
    script.type = "module";
    script.onerror = (err) => {
      console.log("Failed to donwload installer (" + Event.toString() + ")");
      P.innerHTML = "Failed to download installer,<br>Check conole logs";
    };
    script.onload = () => {
      P.innerHTML = "Starting installer...";
      _YWebPage.DeleteNewVersionMessage(100);
      _YWebPage.StartInstaller(roothub);
    };
    document.body.appendChild(script);
    script.src = installerURL;
  }
  static DeleteNewVersionMessage(opacity) {
    opacity -= 5;
    if (opacity > 0) {
      _YWebPage.NewVersionmessageDiv.style.opacity = (opacity / 100).toString();
      setTimeout(() => {
        _YWebPage.DeleteNewVersionMessage(opacity);
      }, 100);
      return;
    }
    document.body.removeChild(_YWebPage.NewVersionmessageDiv.parentNode);
  }
  static getTopZIndex() {
    var elems = document.getElementsByTagName("DIV");
    var highestZ = 0;
    for (var i = 0; i < elems.length; i++) {
      var z = Number.parseInt(window.getComputedStyle(elems[i], null).getPropertyValue("z-index"));
      if (z > highestZ)
        highestZ = z;
    }
    return highestZ + 1;
  }
  static async CheckForNewVersion(force) {
    if (!constants.mustCheckForUpdate && !force)
      return;
    let checkurl = location.protocol + "//www.yoctopuce.com/FR/common/getLastFirmwareLink.php?app=yoctovisualization4web.installer&version=" + constants.buildVersion + "&platform=_";
    let response;
    try {
      response = await fetch(checkurl);
    } catch (e) {
      return;
    }
    if (!response.ok)
      return;
    let json;
    try {
      json = await response.json();
    } catch (e) {
      return;
    }
    if (!(json instanceof Array))
      return;
    if (json.length <= 0)
      return;
    if (!("version" in json[0]))
      return;
    if (!("link" in json[0]))
      return;
    let newVersionInstallerURL = json[0]["link"];
    let newVersionNumber = parseInt(json[0]["version"]);
    let currentVersionStr = constants.trueBuild;
    let currentVersionNumber = 0;
    if (currentVersionStr.indexOf("-") < 0) {
      let n = currentVersionStr.lastIndexOf(".");
      if (n >= 0)
        currentVersionStr = currentVersionStr.substr(n + 1);
      currentVersionNumber = parseInt(currentVersionStr);
    }
    if (currentVersionNumber >= newVersionNumber)
      return;
    let generalSize = constants.generalFontSize;
    let NewVersionSizeX = generalSize * 24;
    let NewVersionSizeY = generalSize * 5;
    let containerDiv = document.createElement("DIV");
    containerDiv.style.position = "absolute";
    containerDiv.style.width = NewVersionSizeX + "px";
    containerDiv.style.height = NewVersionSizeY + "px";
    containerDiv.style.zIndex = _YWebPage.getTopZIndex().toString();
    containerDiv.style.top = "5px";
    containerDiv.style.right = "0px";
    containerDiv.style.overflow = "hidden";
    document.body.appendChild(containerDiv);
    _YWebPage.NewVersionmessageDiv = document.createElement("DIV");
    _YWebPage.NewVersionmessageDiv.style.position = "absolute";
    _YWebPage.NewVersionmessageDiv.style.width = NewVersionSizeX - 7 + "px";
    _YWebPage.NewVersionmessageDiv.style.height = NewVersionSizeY - 2 + "px";
    _YWebPage.NewVersionmessageDiv.style.backgroundColor = "GhostWhite";
    _YWebPage.NewVersionmessageDiv.style.border = "1px solid dimgray";
    _YWebPage.NewVersionmessageDiv.style.top = "0px";
    _YWebPage.NewVersionmessageDiv.style.left = NewVersionSizeX + "px";
    let table = document.createElement("TABLE");
    table.style.fontFamily = constants.generalFontFamily;
    table.style.fontSize = generalSize + "px";
    let TR = document.createElement("TR");
    let TD1 = document.createElement("TD");
    TD1.style.paddingLeft = "5px";
    TD1.style.paddingRight = "10px";
    TD1.rowSpan = 2;
    TD1.style.verticalAlign = "middle";
    TD1.innerHTML = ressources.infoIcon((generalSize * 4).toString(), false, false, false, false, "information");
    TR.appendChild(TD1);
    let TD2 = document.createElement("TD");
    TD2.innerHTML = "A new Yocto-Visualization (for web)<br>version is available";
    TR.appendChild(TD2);
    table.appendChild(TR);
    TR = document.createElement("TR");
    TD2 = document.createElement("TD");
    TD2.style.textAlign = "right";
    TD2.style.verticalAlign = "bottom";
    let hubList = sensorsManager.hubList;
    let rootHub = null;
    for (let i = 0; i < hubList.length; i++) {
      if (!hubList[i].removable)
        rootHub = hubList[i];
    }
    let a = document.createElement("A");
    let deleteTimer = setTimeout(() => {
      _YWebPage.DeleteNewVersionMessage(100);
    }, 5e3);
    a.style.fontSize = "12px";
    a.style.color = " #0000EE";
    a.style.textDecoration = "underline";
    a.style.cursor = "pointer";
    a.style.fontFamily = constants.generalFontFamily;
    a.style.fontSize = generalSize + "px";
    if (rootHub != null) {
      a.innerText = "Download and start installer..";
      a.addEventListener("click", () => {
        _YWebPage.DownloadAndStartInstaller(newVersionInstallerURL, rootHub, deleteTimer);
      });
    } else {
      a.innerText = "www.yoctopuce.com";
      a.addEventListener("click", () => {
        window.location.assign("http://www.yoctopuce.com/EN/tools.php");
      });
    }
    TD2.appendChild(a);
    TR.appendChild(TD2);
    table.appendChild(TR);
    _YWebPage.NewVersionmessageDiv.appendChild(table);
    containerDiv.appendChild(_YWebPage.NewVersionmessageDiv);
    _YWebPage.AnimateNewVerwsionMessage(0);
  }
  //#endif
  // YWebPage.run is the application entry point.
  //
  // params:
  // xmldata  : XML  configuration data string
  // ExternalConfigChange:  will be called if a module config changes , can be used to track configuration file changes,
  // ExternalConfigChange should return the new  XML  configuration data string or null. If you don't want
  // to use that feature, set ExternalConfigChange to null
  // saveFunction will be called at save time and is supposed to sve the configuration data the way it sees fit
  // One can use the internal  YWebPage.Dummysave to test.
  static run(xmlData, ExternalConfigChange, saveFunction) {
    if (typeof ExternalConfigChange !== "undefined") {
      if (ExternalConfigChange != null) {
        sensorsManager.registerChangeExternalCallback(ExternalConfigChange);
      }
    }
    _YWebPage.xmlDataCRC = constants.crc32(xmlData);
    if (typeof saveFunction !== "undefined")
      _YWebPage._saveFunction = saveFunction;
    logForm.log("Application start, Welcome to Yocto-Visualization (for web).");
    logForm.log("This application contains code ported from the Pako library (www.github.com/nodeca/pako).");
    logForm.log("Yocto-Visualization (for web) version is " + constants.buildVersion);
    logForm.log("Yoctopuce API version is " + YAPI.imm_GetAPIVersion());
    if (constants.RunningOnAndroid)
      logForm.log("Browser is running on Android");
    let widthCm = Math.round(2.54 * constants.deviceScreenWidth / constants.screenDPI);
    let heightCm = Math.round(2.54 * constants.deviceScreenHeight / constants.screenDPI);
    logForm.log("Reported screen size is " + constants.deviceScreenWidth.toString() + "x" + constants.deviceScreenHeight.toString() + " at " + constants.screenDPI + " dpi, (~" + widthCm.toString() + "x" + heightCm.toString() + "cm) ");
    logForm.log("Reported pixel ratio is " + window.devicePixelRatio.toString());
    logForm.log(constants.isPhoneOrTablet ? "Probably running on a tablet or a phone." : "Probably running on a desktop.");
    logForm.log((constants.guiDPIFactorWasOverriden ? "DPI based zoom overridden  to " : "DPI based zoom factor set to ") + constants.guiDPIFactor.toFixed(1));
    logForm.log("Reported window inner size =" + window.innerWidth.toString() + "x" + window.innerHeight.toString() + "px");
    _YWebPage.applicationGlobalinit();
    sensorsManager.run();
    _YWebPage.loadFromXML(xmlData);
    constants.edited = false;
    _YWebPage.CheckForNewVersion(false);
  }
  static async pageIsleaving(e) {
    if (!e.persistent) {
      if (constants.edited) {
        await _YWebPage.save(false);
        await YAPI.FreeAPI();
      }
    }
  }
  //#ifndef READONLY
  static refreshEditor() {
    if (_YWebPage.readonly)
      return;
    if (_YWebPage._editor == null)
      return;
    _YWebPage._editor.refresh();
  }
  static EditObject(widget, structData, setvalueCallBack, getvalueCallBack, EditStoppedCallBack) {
    if (_YWebPage.readonly)
      return;
    if (_YWebPage._editor == null)
      return;
    _YWebPage._editor.showWindow(widget, structData, setvalueCallBack, getvalueCallBack, EditStoppedCallBack);
  }
  static get_XmlData() {
    let XmlConfigFile = `<?xml version="1.0" ?>
<!--
   **************************************************
   Yocto-Visualization (for web)  configuration file
   **************************************************
-->
<ROOT version='2.1'>
<!-- in 2.1 version, floating point values are saved in CultureInfo.InvariantCulture form -->
`;
    XmlConfigFile += constants.GetXMLConfiguration();
    for (let i = 0; i < _YWebPage.widgets.length; i++) {
      if (_YWebPage.widgets[i] instanceof graphWidget)
        XmlConfigFile += _YWebPage.widgets[i].getConfigData();
      if (_YWebPage.widgets[i] instanceof angularGaugeWidget)
        XmlConfigFile += _YWebPage.widgets[i].getConfigData();
      if (_YWebPage.widgets[i] instanceof gaugeWidget)
        XmlConfigFile += _YWebPage.widgets[i].getConfigData();
      if (_YWebPage.widgets[i] instanceof digitalDisplayWidget)
        XmlConfigFile += _YWebPage.widgets[i].getConfigData();
    }
    XmlConfigFile += sensorsManager.getXMLSensorsConfig();
    XmlConfigFile += "</ROOT>\n";
    return XmlConfigFile;
  }
  //#endif
  static get readonly() {
    let res = true;
    res = _YWebPage._saveFunction == null;
    return res;
  }
  //#ifndef READONLY
  // stub for dev tests
  static async runInternalTest() {
  }
  //#endif
  static async save(force) {
    if (_YWebPage.readonly)
      return true;
    let xml = this.get_XmlData();
    let newCRC = constants.crc32(xml);
    if (!force && newCRC == _YWebPage.xmlDataCRC)
      return true;
    let saveDoneCallback = (ok, message) => {
      let div = document.createElement("DIV");
      div.style.position = "Fixed";
      div.style.left = "5px";
      div.style.top = "5px";
      div.style.padding = "2px 5px 2px 5px";
      div.style.margin = "2px 2px 2px 2px";
      div.style.border = "1px solid darkgreen";
      div.style.fontFamily = "Arial";
      div.style.fontSize = (12 * constants.guiDPIFactor).toString();
      div.style.whiteSpace = "nowrap";
      div.style.zIndex = "200000";
      div.style.textAlign = "center";
      div.style.opacity = "1";
      let fgcolor = ok ? "darkgreen" : "darkred";
      let bgcolor = ok ? "#b9eea9" : "#eea9a9";
      div.style.opacity = "1";
      div.style.backgroundColor = bgcolor;
      div.style.border = "1px solid " + fgcolor;
      div.style.color = fgcolor;
      div.innerText = message;
      document.body.appendChild(div);
      setTimeout(() => {
        _YWebPage.handleSavedNotification(div);
      }, 100);
    };
    try {
      if (_YWebPage._saveFunction != null) {
        return await _YWebPage._saveFunction(this.get_XmlData(), saveDoneCallback);
      }
    } catch (e) {
      saveDoneCallback(false, "Save failed");
      alert2.show("Configuration save failed, sorry\n" + e.message);
      return false;
    }
    _YWebPage.xmlDataCRC = newCRC;
    logForm.log("Configuration saved.");
    constants.edited = false;
    return true;
  }
  static handleSavedNotification(div) {
    let opacity = parseFloat(div.style.opacity) - 0.05;
    if (opacity < 0) {
      document.body.removeChild(div);
      return;
    }
    div.style.opacity = opacity.toString();
    setTimeout(() => {
      _YWebPage.handleSavedNotification(div);
    }, 100);
  }
  //#ifndef READONLY
  // Will just print the XML  config  file on the page
  static Dummysave(xml) {
    let contents = document.createElement("PRE");
    contents.innerText = xml;
    document.body.appendChild(contents);
    return true;
  }
};
YWebPage.baseSize = 18 * constants.generalSizeCoef;
YWebPage.xmlDataCRC = 0;
YWebPage.widgets = [];
YWebPage._markersSubMenuItems = [];
YWebPage._editor = null;
YWebPage._mainform = null;
YWebPage._saveFunction = null;
YWebPage._MarkerCountPerXaxis = 0;
YWebPage.ConfigChangedConfirmWindows = null;
YWebPage.saveIntDiv = null;
var HTTPrequestResult = class {
  constructor(data, status, statusText) {
    this._data = data;
    this._status = status;
    this._statusText = statusText;
  }
  get data() {
    return this._data;
  }
  get status() {
    return this._status;
  }
  get statusText() {
    return this._statusText;
  }
};
var HubInfo = class {
  get protocol() {
    return this._protocol;
  }
  get addr() {
    return this._addr;
  }
  get port() {
    return this._port;
  }
  get path() {
    return this._path;
  }
  get userPassword() {
    return this._userPassword;
  }
  get adminPassword() {
    return this._adminPassword;
  }
  get serial() {
    return this._serial;
  }
  get srvUsername() {
    return this._srvusername != null ? this._srvusername : "";
  }
  get srvPassword() {
    return this._srvpassword != null ? this._srvpassword : "";
  }
  constructor(protocol, addr, port, path, srvusername, srvpassword) {
    this._userPassword = false;
    this._adminPassword = false;
    this._cred = "";
    this._serial = "";
    this._srvusername = null;
    this._srvpassword = null;
    if (protocol.slice(-1) == ":")
      protocol = protocol.slice(0, -1);
    this._protocol = protocol;
    this._addr = addr;
    this._port = port;
    this._protocol = protocol;
    this._path = path;
    if (srvusername !== void 0)
      this._srvusername = srvusername;
    if (srvpassword !== void 0)
      this._srvpassword = srvpassword;
    if (this._path != "") {
      if (this._path.charAt(0) == "/") {
        this._path = this._path.slice(1);
      }
    }
    if (this._path != "") {
      if (this._path.slice(-1) == "/") {
        this._path = this._path.slice(0, -1);
      }
    }
  }
  get_hubUrl(user, password) {
    let url = this.protocol + "://";
    if (typeof user != "undefined" && user != "") {
      if (user != "") {
        url += user + ":" + password + "@";
      }
    }
    url += this.addr + ":" + this.port.toString();
    if (this.path != "")
      url += "/" + this.path;
    return url;
  }
  async makeRequest() {
    let url = this._protocol + "://" + this._addr + ":" + this._port.toString();
    if (this._path != "")
      url += "/" + this._path;
    url += "/info.json";
    let response;
    try {
      response = await fetch(url, { mode: "cors" });
    } catch (e) {
      return -3;
    }
    if (response.ok) {
      let HubData;
      try {
        HubData = await response.json();
      } catch (e) {
        return -1;
      }
      if (HubData.port && HubData.port.length > 0) {
        let proto_port = HubData.port[0].split(":");
        this._protocol = proto_port[0];
        this._port = parseInt(proto_port[1]);
      }
      this._serial = HubData.serialNumber;
      this._path = HubData.dir;
      this._adminPassword = HubData.adminPassword.toUpperCase() == "TRUE";
      this._userPassword = HubData.userPassword.toUpperCase() == "TRUE";
      return true;
    }
    if (response.status == 404 && this._path != "")
      return -2;
    if (response.status == 0 && response.statusText == null)
      return -3;
    return response.status;
  }
  findChild(parent, name) {
    let res = null;
    parent.childNodes.forEach((node) => {
      if (node.nodeName == name) {
        res = node;
        return;
      }
    });
    return res;
  }
  findOutCredentialsFromConfigFile(xmlData) {
    if (xmlData == null)
      return;
    let parser = new DOMParser();
    let doc = parser.parseFromString(xmlData, "application/xml");
    let rootNode = this.findChild(doc, "ROOT");
    if (rootNode == null)
      return;
    let configNode = this.findChild(rootNode, "Config");
    if (configNode == null)
      return;
    let Hubs = this.findChild(configNode, "Hubs");
    if (Hubs == null)
      return;
    let HubSection;
    let list = Hubs.childNodes;
    let sourceHubFound = false;
    for (let i = 0; i < list.length; i++) {
      if (list[i].nodeName == "Hub") {
        let node = new YXmlNode(list[i]);
        let attributes = node.Attributes;
        let addr = attributes.hasOwnProperty("addr") ? attributes["addr"] : "";
        let port = attributes.hasOwnProperty("port") ? attributes["port"] : "80";
        let protocol = attributes.hasOwnProperty("protocol") ? attributes["protocol"] : "";
        let path = attributes.hasOwnProperty("path") ? attributes["path"] : "";
        let username = attributes.hasOwnProperty("user") ? attributes["user"] : "";
        let password = attributes.hasOwnProperty("password") ? attributes["password"] : "";
        let a1 = addr.toUpperCase();
        if (a1.substring(0, 4) == "WWW.")
          a1 = a1.substring(4);
        let a2 = this._addr.toUpperCase();
        if (a2.substring(0, 4) == "WWW.")
          a2 = a2.substring(4);
        if (a1 == a2 && path == this._path) {
          this._srvusername = username;
          this._srvpassword = Hub.Decrypt(password, Hub.loginCypherPassword);
        }
      }
    }
  }
};
var YoctoHubFileHandler = class _YoctoHubFileHandler {
  //#endif
  constructor(hubInfo) {
    this.filename = "";
    this.xmldata = "";
    this.htmlSrcCode = "";
    this.FileSystemAccessDenied = false;
    this._XMLConfigFileChanged = null;
    this.fileSystem = null;
    this.fileSystemReady = false;
    this.configFileCRC = -1;
    this.info = hubInfo;
  }
  async init(defaultXmlConfigFileContents) {
    let url = location.href;
    this.filename = url.substring(url.lastIndexOf("/") + 1);
    let p = this.filename.indexOf("?");
    if (p > 0)
      this.filename = this.filename.substring(0, p);
    this.htmlSrcCode = document.documentElement.outerHTML;
    if (typeof defaultXmlConfigFileContents === "undefined" || defaultXmlConfigFileContents == "") {
      this.xmldata = '<?xml version="1.0" ?>\n<ROOT version="2.1">\n<Config>\n<Hubs>\n<Hub protocol="' + this.info.protocol + '" addr="' + this.info.addr + '" port="' + this.info.port.toString() + '"' + (this.info.path != "" ? ' path="' + this.info.path + '"' : "") + ' removable="FALSE"/>\n</Hubs>\n</Config>\n</ROOT>\n';
    } else {
      this.xmldata = defaultXmlConfigFileContents;
    }
    this.fileSystem = null;
    this.fileSystemReady = false;
    await this.findSourceFileSystem(defaultXmlConfigFileContents);
  }
  async disconnect() {
    if (this.fileSystem == null)
      return;
    let m = await this.fileSystem.get_module();
    let url = await m.get_url();
    await YAPI.UnregisterHub(url);
  }
  static async start(defaultXmlConfigFileContents) {
    let dirpath = location.pathname;
    let defport = "80";
    let slashpos = dirpath.lastIndexOf("/");
    if (slashpos > 0) {
      dirpath = dirpath.slice(0, slashpos);
    } else {
      dirpath = "";
    }
    if (location.protocol == "https:") {
      defport = "443";
    }
    let hubinfo = new HubInfo(location.protocol, location.hostname, parseInt(location.port ? location.port : defport), dirpath);
    await hubinfo.makeRequest();
    let filehandler = new _YoctoHubFileHandler(hubinfo);
    await filehandler.init(defaultXmlConfigFileContents);
    await filehandler.disconnect();
    return filehandler;
  }
  get xmlConfigData() {
    return this.xmldata;
  }
  static makeRequest(method, url, username, password) {
    return new Promise(function(resolve, reject) {
      let xhr = new XMLHttpRequest();
      if (username !== void 0 && password !== void 0 && username != null && password !== null) {
        xhr.open(method, url, true, username, password);
        xhr.withCredentials = true;
      } else {
        xhr.open(method, url);
      }
      xhr.overrideMimeType("text/plain; charset=x-user-defined");
      xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
          if (this.status >= 200 && this.status < 300) {
            resolve(new HTTPrequestResult(YAPI.imm_str2bin(xhr.response), this.status, xhr.statusText));
          } else {
            resolve(new HTTPrequestResult(null, this.status, xhr.statusText));
          }
        }
      };
      xhr.onerror = function() {
        resolve(new HTTPrequestResult(null, this.status, xhr.statusText));
      };
      xhr.send();
    });
  }
  //#ifndef READONLY
  async findSourceFileSystem(xmldata) {
    this.fileSystem = null;
    let errmsg = new YErrorMsg();
    this.info.findOutCredentialsFromConfigFile(xmldata);
    let url = this.info.get_hubUrl(this.info.srvUsername, this.info.srvPassword);
    let errorNo = YAPI.SUCCESS;
    let errorMsg = "";
    try {
      errorNo = await YAPI.RegisterHub(url, errmsg);
      errorMsg = errmsg.msg;
    } catch (e) {
      errorNo = e.errorType;
      errorMsg = e.message;
    }
    if (errorNo != YAPI.SUCCESS) {
      this.fileSystemReady = false;
      if (errorNo == YAPI.UNAUTHORIZED) {
        console.log("Access to hub " + url + " is denied, waiting for YV callback to save the situation.");
        this.FileSystemAccessDenied = true;
        return;
      }
      throw new Error("YV4F ERROR: Unable to register Hub " + this.info.get_hubUrl() + "(" + errorMsg + "), save will not work.");
    }
    let fs = YFiles.FirstFiles();
    if (fs == null) {
      this.fileSystemReady = true;
      throw new Error("YV4F ERROR: No file system found on Hub " + this.info.get_hubUrl() + ", save will not work.");
    }
    while (fs != null) {
      if (await fs.get_serialNumber() == this.info.serial) {
        await this.registerSourceFileSystem(fs);
        return;
      }
      fs = fs.nextFiles();
    }
    fs = YFiles.FirstFiles();
    while (fs != null) {
      let m = await fs.get_module();
      let url2 = await m.get_url();
      let p = url2.indexOf("://");
      if (p >= 0)
        url2 = url2.slice(p + 3);
      p = url2.indexOf(":");
      if (p > 0)
        url2 = url2.slice(0, p);
      {
        if (url2 == this.info.addr) {
          await this.registerSourceFileSystem(fs);
          return;
        }
      }
      fs = fs.nextFiles();
    }
    this.fileSystemReady = true;
    throw new Error("YV4F ERROR: No file system matching Hub " + this.info.get_hubUrl() + " found, save will not work.");
  }
  async registerSourceFileSystem(fs) {
    this.fileSystem = fs;
    this.fileSystemReady = true;
    this.FileSystemAccessDenied = false;
    let entries = await this.fileSystem.get_list(this.filename);
    if (entries.length > 0)
      this.configFileCRC = await entries[0].get_crc();
  }
  // called from Yocto-visualization when a Module had a config change and
  // will find out if the config change source is the web page host and if
  // the original file has changed. This is called from a string of callbacks
  // because there can be only one change call back per module, and it is
  // already used by the Sensor manager
  async configChangeCallback(m) {
    if (this.fileSystem == null) {
      let urlSignature = this.info.addr + ":" + this.info.port.toString() + "/" + this.info.path + (this.info.path != "" ? "/" : "");
      if (urlSignature.startsWith("www."))
        urlSignature = urlSignature.substring(4);
      let sourceUrl = await m.get_url();
      if (sourceUrl.length > urlSignature.length && sourceUrl.substring(sourceUrl.length - urlSignature.length) == urlSignature) {
        let fs = YFiles.FindFiles(await m.get_serialNumber() + ".files");
        if (await fs.isOnline())
          await this.registerSourceFileSystem(fs);
      }
    }
    if (this.fileSystem == null)
      return null;
    if (await this.fileSystem.get_serialNumber() != await m.get_serialNumber())
      return null;
    let entries = await this.fileSystem.get_list(this.filename);
    if (entries.length <= 0)
      return null;
    if (this.configFileCRC == await entries[0].get_crc())
      return null;
    let data = await this.fileSystem.download(this.filename);
    let file = new TextDecoder("utf-8").decode(data);
    let indexStart = file.indexOf(_YoctoHubFileHandler.signatureStart);
    if (indexStart < 0)
      return null;
    this.configFileCRC = await entries[0].get_crc();
    indexStart += _YoctoHubFileHandler.signatureStart.length;
    let indexEnd = file.indexOf(_YoctoHubFileHandler.signatureEnd);
    if (indexEnd < indexStart)
      return "";
    indexEnd += _YoctoHubFileHandler.signatureEnd.length - 2;
    let configdata = file.substring(indexStart, indexEnd);
    try {
      return JSON.parse(configdata);
    } catch (e) {
    }
    return null;
  }
  get configChangeCallbackFct() {
    return async (module) => {
      return await this.configChangeCallback(module);
    };
  }
  get saveFct() {
    return async (xml, callback) => {
      return await this.saveFctinternal(xml, callback);
    };
  }
  async retryWithNewCredentials(xml, username, password, callback) {
    if (this.fileSystem == null) {
      debugger;
      return;
    }
    let m = await this.fileSystem.get_module();
    let url = await m.get_url();
    let err = new YErrorMsg();
    let p1 = url.indexOf("://");
    let p2 = url.indexOf("@", p1 + 3);
    let newurl = url.substring(0, p1 + 3) + username + ":" + password + "@";
    if (p2 < 0)
      newurl += url.substring(p1 + 3);
    else
      newurl += url.substring(p2 + 1);
    await YAPI.UnregisterHub(url);
    if (await YAPI.RegisterHub(newurl, err) != YAPI.SUCCESS) {
      await YAPI.RegisterHub(url, err);
    }
    try {
      if (!await this.saveFctinternal(xml, callback))
        return;
    } catch (e) {
      if (callback != null)
        callback(false, "Save failed (" + e.message) + ")";
      return;
    }
  }
  async saveFctinternal(xml, callback) {
    if (this.FileSystemAccessDenied) {
      throw "YV4F ERROR: Access to file system on " + this.info.get_hubUrl() + " is denied, it is probably password protected. Check network connections in the global configuration.";
    }
    if (this.fileSystem == null) {
      throw "YV4F ERROR: No file system, Save aborted";
    }
    if (!this.fileSystemReady) {
      throw "YV4F ERROR: File system is not ready yet, retry in a few seconds.";
    }
    if (!this.fileSystem.isOnline()) {
      throw "YV4F ERROR: File system is off line, Save aborted";
    }
    let tosave = "";
    let index = this.htmlSrcCode.indexOf(_YoctoHubFileHandler.signatureStart + ")");
    if (index > 0) {
      tosave = this.htmlSrcCode.substring(0, index + _YoctoHubFileHandler.signatureStart.length) + JSON.stringify(xml) + this.htmlSrcCode.substring(index + _YoctoHubFileHandler.signatureStart.length);
    } else {
      let index1 = this.htmlSrcCode.indexOf(_YoctoHubFileHandler.signatureStart);
      let index2 = this.htmlSrcCode.indexOf(_YoctoHubFileHandler.signatureEnd);
      if (index1 > 0 && index2 && index2 > index1) {
        tosave = this.htmlSrcCode.substring(0, index1 + _YoctoHubFileHandler.signatureStart.length) + JSON.stringify(xml) + ");" + this.htmlSrcCode.substring(index2 + _YoctoHubFileHandler.signatureEnd.length);
      } else {
        throw "Unable to locate YoctoHubFileHandler constructor call in original HTML file.";
      }
    }
    tosave = "<!DOCTYPE html>\n" + tosave;
    let GZdata = Pako_Deflate.gzip(tosave, { level: 9 });
    let askForRwCredentials = false;
    try {
      await this.fileSystem.upload(this.filename + ".gz", GZdata);
    } catch (e) {
      if (this.fileSystem.get_errorType() == YAPI_UNAUTHORIZED)
        askForRwCredentials = true;
      else
        throw e;
    }
    if (askForRwCredentials) {
      let m = await this.fileSystem.get_module();
      let url = await m.get_url();
      let err = new YErrorMsg();
      let p1 = url.indexOf("://");
      let p2 = url.indexOf("@", p1 + 3);
      if (p2 >= 0)
        url = url.substring(p2 + 1);
      else if (p1 >= 0)
        url = url.substring(p1 + 3);
      await CredentialsPrompt.show(url, async () => {
        await this.retryWithNewCredentials(xml, CredentialsPrompt.username, CredentialsPrompt.password, callback);
      });
      if (callback != null)
        callback(false, "Save failed, credentials required");
      return false;
    }
    let entries = await this.fileSystem.get_list(this.filename);
    if (entries.length > 0)
      this.configFileCRC = await entries[0].get_crc();
    if (callback != null)
      callback(true, "Save sucessfull");
    return true;
  }
};
YoctoHubFileHandler.signatureStart = "YoctoHubFileHandler.start(";
YoctoHubFileHandler.signatureEnd = '</ROOT>\\n");';
export {
  AlarmSection,
  AlarmSettings,
  AlarmTestTypeDescription,
  AngularGaugeFormProperties,
  AngularZoneDescription,
  AnnotationPanel,
  AnnotationPanelDescription,
  AnnotationPanelDescriptionGraph,
  ChartSerie,
  ColorEditorSroll,
  ColorSampler,
  ContextMenu,
  ContextMenuItem,
  ContextMenuItemBase,
  ContextMenuItemSubMenuEntry,
  ContextMenuSeparator,
  ContextSubMenu,
  CredentialsPrompt,
  CustomYSensor,
  DataPanel,
  DataSegment,
  DataSerie,
  DataSummarySegment,
  DataTracker,
  DataTrackerDescription,
  EditorBaseDef,
  FontDescription,
  GaugeFormProperties,
  GenericAxis,
  GenericPanel,
  GenericProperties,
  GraphFormProperties,
  HTTPrequestResult,
  Hub,
  HubEdit,
  HubInfo,
  InputFieldManager,
  Legend,
  LegendDescription,
  LegendPanel,
  LegendPanelDescription,
  Marker,
  MarkerDescription,
  Matrix3x3,
  MessagePanel,
  MinMax,
  MinMaxHandler,
  Navigator,
  NavigatorDescription,
  NullYSensor,
  Point,
  PointF,
  PropPathinfo,
  PropertiesForm2,
  PropertiesList,
  PropertyDescriptor,
  Proportional,
  StartStopStep,
  Summary,
  TimeConverter,
  TimeConverterParseResult,
  TimedSensorValue,
  TypeDescription,
  UIElement,
  UIElementBaseParams,
  UIElementBoolean,
  UIElementColor,
  UIElementDoubleNan,
  UIElementEnum,
  UIElementGeneric,
  UIElementList,
  UIElementMarkerPos,
  UIElementNumber,
  UIElementString,
  Vector3,
  ViewPortSettings,
  XAxis,
  XaxisDescription,
  YAPI,
  YAPIContext,
  YAPI_BUFFER_TOO_SMALL,
  YAPI_DEVICE_BUSY,
  YAPI_DEVICE_NOT_FOUND,
  YAPI_DNS_ERROR,
  YAPI_DOUBLE_ACCES,
  YAPI_EXHAUSTED,
  YAPI_FILE_NOT_FOUND,
  YAPI_INVALID_ARGUMENT,
  YAPI_INVALID_DOUBLE,
  YAPI_INVALID_INT,
  YAPI_INVALID_LONG,
  YAPI_INVALID_STRING,
  YAPI_INVALID_UINT,
  YAPI_IO_ERROR,
  YAPI_LEGACY,
  YAPI_MAX_DOUBLE,
  YAPI_MIN_DOUBLE,
  YAPI_NOT_INITIALIZED,
  YAPI_NOT_SUPPORTED,
  YAPI_NO_EXPIRATION_CHECK,
  YAPI_NO_HOSTNAME_CHECK,
  YAPI_NO_MORE_DATA,
  YAPI_NO_TRUSTED_CA_CHECK,
  YAPI_RFID_HARD_ERROR,
  YAPI_RFID_SOFT_ERROR,
  YAPI_RTC_NOT_READY,
  YAPI_SSL_ERROR,
  YAPI_SSL_UNK_CERT,
  YAPI_SUCCESS,
  YAPI_TIMEOUT,
  YAPI_UNAUTHORIZED,
  YAPI_UNCONFIGURED,
  YAPI_VERSION_MISMATCH,
  YAngularGauge,
  YAngularZone,
  YAxis,
  YBrush,
  YColor,
  YConsolidatedDataSet,
  YDataLogger,
  YDataRenderer,
  YDataSet,
  YDataStream,
  YDate,
  YDigitalDisplay,
  YEnum,
  YEnumItem,
  YErrorMsg,
  YFileRecord,
  YFiles,
  YFirmwareFile,
  YFirmwareUpdate,
  YFont,
  YFunction,
  YGenericHub,
  YGenericSSDPManager,
  YGraph,
  YGraphics,
  YGraphicsSVG,
  YHTTPBody,
  YHTTPRequest,
  YHttpEngine,
  YHub,
  YHubEngine,
  YLinearGradientBrush,
  YMeasure,
  YModule,
  YNetwork,
  YPen,
  YRectangle,
  YSensor,
  YSizeF,
  YSmoothingMode,
  YSolidBrush,
  YSolidGauge,
  YStringFormat,
  YSystemEnv,
  YSystemEnvHtml,
  YTextRenderingHint,
  YWebPage,
  YWebSocketEngine,
  YWidget,
  YWindow,
  YXmlNode,
  Y_DETECT_ALL,
  Y_DETECT_NET,
  Y_DETECT_NONE,
  Y_DETECT_USB,
  Y_FUNCTIONDESCRIPTOR_INVALID,
  YaxisDescription,
  YoctoError,
  YoctoHubFileHandler,
  Zone,
  ZoneDescription,
  _YY_UrlInfo,
  alert2 as alert,
  angularGaugeWidget,
  button,
  captureParametersSet,
  colorEditor,
  configForm,
  confirm,
  constants,
  digitalDisplayFormProperties,
  digitalDisplayWidget,
  doubleNan,
  fontNameTypeDescription,
  gaugeWidget,
  graphWidget,
  info,
  logForm,
  mainForm,
  minMaxPoint,
  newWindowParam,
  pointXY,
  pointsSummary,
  rawDataForm,
  ressources,
  sensorDataTypeDescription,
  sensorFreqTypeDescription,
  sensorPrecisionTypeDescription,
  sensorsManager,
  xAxisPosition,
  yAxisDescription
};
//# sourceMappingURL=yv4web-full.js.map
