constructor(body, images, groups, layoutEngine) {
    this.body = body;
    this.images = images;
    this.groups = groups;
    this.layoutEngine = layoutEngine;

    // create the node API in the body container
    this.body.functions.createNode = this.create.bind(this);

    this.nodesListeners = {
      add: (event, params) => {
        this.add(params.items);
      },
      update: (event, params) => {
        this.update(params.items, params.data, params.oldData);
      },
      remove: (event, params) => {
        this.remove(params.items);
      },
    };

    this.defaultOptions = {
      borderWidth: 1,
      borderWidthSelected: undefined,
      brokenImage: undefined,
      color: {
        border: "#2B7CE9",
        background: "#97C2FC",
        highlight: {
          border: "#2B7CE9",
          background: "#D2E5FF",
        },
        hover: {
          border: "#2B7CE9",
          background: "#D2E5FF",
        },
      },
      opacity: undefined, // number between 0 and 1
      fixed: {
        x: false,
        y: false,
      },
      font: {
        color: "#343434",
        size: 14, // px
        face: "arial",
        background: "none",
        strokeWidth: 0, // px
        strokeColor: "#ffffff",
        align: "center",
        vadjust: 0,
        multi: false,
        bold: {
          mod: "bold",
        },
        boldital: {
          mod: "bold italic",
        },
        ital: {
          mod: "italic",
        },
        mono: {
          mod: "",
          size: 15, // px
          face: "monospace",
          vadjust: 2,
        },
      },
      group: undefined,
      hidden: false,
      icon: {
        face: "FontAwesome", //'FontAwesome',
        code: undefined, //'\uf007',
        size: 50, //50,
        color: "#2B7CE9", //'#aa00ff'
      },
      image: undefined, // --> URL
      imagePadding: {
        // only for image shape
        top: 0,
        right: 0,
        bottom: 0,
        left: 0,
      },
      label: undefined,
      labelHighlightBold: true,
      level: undefined,
      margin: {
        top: 5,
        right: 5,
        bottom: 5,
        left: 5,
      },
      mass: 1,
      physics: true,
      scaling: {
        min: 10,
        max: 30,
        label: {
          enabled: false,
          min: 14,
          max: 30,
          maxVisible: 30,
          drawThreshold: 5,
        },
        customScalingFunction: function (min, max, total, value) {
          if (max === min) {
            return 0.5;
          } else {
            const scale = 1 / (max - min);
            return Math.max(0, (value - min) * scale);
          }
        },
      },
      shadow: {
        enabled: false,
        color: "rgba(0,0,0,0.5)",
        size: 10,
        x: 5,
        y: 5,
      },
      shape: "ellipse",
      shapeProperties: {
        borderDashes: false, // only for borders
        borderRadius: 6, // only for box shape
        interpolation: true, // only for image and circularImage shapes
        useImageSize: false, // only for image and circularImage shapes
        useBorderWithImage: false, // only for image shape
        coordinateOrigin: "center", // only for image and circularImage shapes
      },
      size: 25,
      title: undefined,
      value: undefined,
      x: undefined,
      y: undefined,
    };

    // Protect from idiocy
    if (this.defaultOptions.mass <= 0) {
      throw "Internal error: mass in defaultOptions of NodesHandler may not be zero or negative";
    }

    this.options = bridgeObject(this.defaultOptions);

    this.bindEventListeners();
  }
@param {object} body
@param {Images} images
@param {Array.<Group>} groups
@param {LayoutEngine} layoutEngine