clusterByConnection(nodeId, options, refreshData = true) {
    // kill conditions
    if (nodeId === undefined) {
      throw new Error("No nodeId supplied to clusterByConnection!");
    }
    if (this.body.nodes[nodeId] === undefined) {
      throw new Error(
        "The nodeId given to clusterByConnection does not exist!"
      );
    }

    const node = this.body.nodes[nodeId];
    options = this._checkOptions(options, node);
    if (options.clusterNodeProperties.x === undefined) {
      options.clusterNodeProperties.x = node.x;
    }
    if (options.clusterNodeProperties.y === undefined) {
      options.clusterNodeProperties.y = node.y;
    }
    if (options.clusterNodeProperties.fixed === undefined) {
      options.clusterNodeProperties.fixed = {};
      options.clusterNodeProperties.fixed.x = node.options.fixed.x;
      options.clusterNodeProperties.fixed.y = node.options.fixed.y;
    }

    const childNodesObj = {};
    const childEdgesObj = {};
    const parentNodeId = node.id;
    const parentClonedOptions = NetworkUtil.cloneOptions(node);
    childNodesObj[parentNodeId] = node;

    // collect the nodes that will be in the cluster
    for (let i = 0; i < node.edges.length; i++) {
      const edge = node.edges[i];
      if (this.clusteredEdges[edge.id] === undefined) {
        const childNodeId = this._getConnectedId(edge, parentNodeId);

        // if the child node is not in a cluster
        if (this.clusteredNodes[childNodeId] === undefined) {
          if (childNodeId !== parentNodeId) {
            if (options.joinCondition === undefined) {
              childEdgesObj[edge.id] = edge;
              childNodesObj[childNodeId] = this.body.nodes[childNodeId];
            } else {
              // clone the options and insert some additional parameters that could be interesting.
              const childClonedOptions = NetworkUtil.cloneOptions(
                this.body.nodes[childNodeId]
              );
              if (
                options.joinCondition(
                  parentClonedOptions,
                  childClonedOptions
                ) === true
              ) {
                childEdgesObj[edge.id] = edge;
                childNodesObj[childNodeId] = this.body.nodes[childNodeId];
              }
            }
          } else {
            // swallow the edge if it is self-referencing.
            childEdgesObj[edge.id] = edge;
          }
        }
      }
    }
    const childNodeIDs = Object.keys(childNodesObj).map(function (childNode) {
      return childNodesObj[childNode].id;
    });

    for (const childNodeKey in childNodesObj) {
      if (!Object.prototype.hasOwnProperty.call(childNodesObj, childNodeKey))
        continue;

      const childNode = childNodesObj[childNodeKey];
      for (let y = 0; y < childNode.edges.length; y++) {
        const childEdge = childNode.edges[y];
        if (
          childNodeIDs.indexOf(this._getConnectedId(childEdge, childNode.id)) >
          -1
        ) {
          childEdgesObj[childEdge.id] = childEdge;
        }
      }
    }
    this._cluster(childNodesObj, childEdgesObj, options, refreshData);
  }
suck all connected nodes of a node into the node.
@param {Node.id} nodeId
@param {object} options
@param {boolean} [refreshData]