@ -42,8 +42,9 @@ class ClusterEngine {  
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    for  ( let  i  =  0 ;  i  <  nodesToCluster . length ;  i ++ )  {   
			
		
	
		
			
				
					      this . clusterByConnection ( nodesToCluster [ i ] , options , fals e) ;   
			
		
	
		
			
				
					      this . clusterByConnection ( nodesToCluster [ i ] , options , tru e) ;   
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    this . body . emitter . emit ( '_dataChanged' ) ;   
			
		
	
		
			
				
					  }   
			
		
	
		
			
				
					
  
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -73,7 +74,9 @@ class ClusterEngine {  
			
		
	
		
			
				
					        // collect the nodes that will be in the cluster
   
			
		
	
		
			
				
					        for  ( let  i  =  0 ;  i  <  node . edges . length ;  i ++ )  {   
			
		
	
		
			
				
					          let  edge  =  node . edges [ i ] ;   
			
		
	
		
			
				
					          childEdgesObj [ edge . id ]  =  edge ;   
			
		
	
		
			
				
					          if  ( edge . hiddenByCluster  !==  true )  {   
			
		
	
		
			
				
					            childEdgesObj [ edge . id ]  =  edge ;   
			
		
	
		
			
				
					          }   
			
		
	
		
			
				
					        }   
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					    }   
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -83,53 +86,72 @@ class ClusterEngine {  
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					  / * *   
			
		
	
		
			
				
					  *  Cluster  all  nodes  in  the  network  that  have  only  1  edge   
			
		
	
		
			
				
					  *  @ param  options   
			
		
	
		
			
				
					  *  @ param  refreshData   
			
		
	
		
			
				
					  * /   
			
		
	
		
			
				
					  clusterOutliers ( options ,  refreshData  =  true )  {   
			
		
	
		
			
				
					   *  Cluster  all  nodes  in  the  network  that  have  only  X  edges   
			
		
	
		
			
				
					   *  @ param  edgeCount   
			
		
	
		
			
				
					   *  @ param  options   
			
		
	
		
			
				
					   *  @ param  refreshData   
			
		
	
		
			
				
					   * /   
			
		
	
		
			
				
					  clusterByEdgeCount ( edgeCount ,  options ,  refreshData  =  true )  {   
			
		
	
		
			
				
					    options  =  this . _checkOptions ( options ) ;   
			
		
	
		
			
				
					    let  clusters  =  [ ] ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    let  usedNodes  =  { } ;   
			
		
	
		
			
				
					    let  edge ,  edges ,  node ,  nodeId ,  visibleEdges ;   
			
		
	
		
			
				
					    // collect the nodes that will be in the cluster
   
			
		
	
		
			
				
					    for  ( let  i  =  0 ;  i  <  this . body . nodeIndices . length ;  i ++ )  {   
			
		
	
		
			
				
					      let  childNodesObj  =  { } ;   
			
		
	
		
			
				
					      let  childEdgesObj  =  { } ;   
			
		
	
		
			
				
					      let  nodeId  =  this . body . nodeIndices [ i ] ;   
			
		
	
		
			
				
					      let  visibleEdges  =  0 ;   
			
		
	
		
			
				
					      let  edge ;   
			
		
	
		
			
				
					      for  ( let  j  =  0 ;  j  <  this . body . nodes [ nodeId ] . edges . length ;  j ++ )  {   
			
		
	
		
			
				
					        if  ( this . body . nodes [ nodeId ] . edges [ j ] . options . hidden  ===  false )  {   
			
		
	
		
			
				
					          visibleEdges ++ ;   
			
		
	
		
			
				
					          edge  =  this . body . nodes [ nodeId ] . edges [ j ] ;   
			
		
	
		
			
				
					      nodeId  =  this . body . nodeIndices [ i ] ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					      // if this node is already used in another cluster this session, we do not have to re-evaluate it.
   
			
		
	
		
			
				
					      if  ( usedNodes [ nodeId ]  ===  undefined )  {   
			
		
	
		
			
				
					        visibleEdges  =  0 ;   
			
		
	
		
			
				
					        node  =  this . body . nodes [ nodeId ] ;   
			
		
	
		
			
				
					        edges  =  [ ] ;   
			
		
	
		
			
				
					        for  ( let  j  =  0 ;  j  <  node . edges . length ;  j ++ )  {   
			
		
	
		
			
				
					          edge  =  node . edges [ j ] ;   
			
		
	
		
			
				
					          if  ( edge . hiddenByCluster  !==  true )  {   
			
		
	
		
			
				
					            edges . push ( edge ) ;   
			
		
	
		
			
				
					          }   
			
		
	
		
			
				
					        }   
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					      if  ( visibleEdges  ===  1 )  {   
			
		
	
		
			
				
					        // this is an outlier
   
			
		
	
		
			
				
					        let  childNodeId  =  this . _getConnectedId ( edge ,  nodeId ) ;   
			
		
	
		
			
				
					        if  ( childNodeId  !==  nodeId )  {   
			
		
	
		
			
				
					          if  ( options . joinCondition  ===  undefined )  {   
			
		
	
		
			
				
					            if  ( this . _checkIfUsed ( clusters , nodeId , edge . id )  ===  false  &&  this . _checkIfUsed ( clusters , childNodeId , edge . id )  ===  false )  {   
			
		
	
		
			
				
					              childEdgesObj [ edge . id ]  =  edge ;   
			
		
	
		
			
				
					              childNodesObj [ nodeId ]  =  this . body . nodes [ nodeId ] ;   
			
		
	
		
			
				
					              childNodesObj [ childNodeId ]  =  this . body . nodes [ childNodeId ] ;   
			
		
	
		
			
				
					            }   
			
		
	
		
			
				
					          }   
			
		
	
		
			
				
					          else  {   
			
		
	
		
			
				
					            let  clonedOptions  =  this . _cloneOptions ( this . body . nodes [ nodeId ] ) ;   
			
		
	
		
			
				
					            if  ( options . joinCondition ( clonedOptions )  ===  true  &&  this . _checkIfUsed ( clusters , nodeId , edge . id )  ===  false )  {   
			
		
	
		
			
				
					              childEdgesObj [ edge . id ]  =  edge ;   
			
		
	
		
			
				
					              childNodesObj [ nodeId ]  =  this . body . nodes [ nodeId ] ;   
			
		
	
		
			
				
					        // this node qualifies, we collect its neighbours to start the clustering process.
   
			
		
	
		
			
				
					        if  ( edges . length  ===  edgeCount )  {   
			
		
	
		
			
				
					          let  gatheringSuccessful  =  true ;   
			
		
	
		
			
				
					          for  ( let  j  =  0 ;  j  <  edges . length ;  j ++ )  {   
			
		
	
		
			
				
					            edge  =  edges [ j ] ;   
			
		
	
		
			
				
					            let  childNodeId  =  this . _getConnectedId ( edge ,  nodeId ) ;   
			
		
	
		
			
				
					            // if unused and if not referencing itself
   
			
		
	
		
			
				
					            if  ( childNodeId  !==  nodeId  &&  usedNodes [ nodeId ]  ===  undefined )  {   
			
		
	
		
			
				
					              // add the nodes to the list by the join condition.
   
			
		
	
		
			
				
					              if  ( options . joinCondition  ===  undefined )  {   
			
		
	
		
			
				
					                childEdgesObj [ edge . id ]  =  edge ;   
			
		
	
		
			
				
					                childNodesObj [ nodeId ]  =  this . body . nodes [ nodeId ] ;   
			
		
	
		
			
				
					                childNodesObj [ childNodeId ]  =  this . body . nodes [ childNodeId ] ;   
			
		
	
		
			
				
					                usedNodes [ nodeId ]  =  true ;   
			
		
	
		
			
				
					              }   
			
		
	
		
			
				
					              else  {   
			
		
	
		
			
				
					                let  clonedOptions  =  this . _cloneOptions ( this . body . nodes [ nodeId ] ) ;   
			
		
	
		
			
				
					                if  ( options . joinCondition ( clonedOptions )  ===  true )  {   
			
		
	
		
			
				
					                  childEdgesObj [ edge . id ]  =  edge ;   
			
		
	
		
			
				
					                  childNodesObj [ nodeId ]  =  this . body . nodes [ nodeId ] ;   
			
		
	
		
			
				
					                  usedNodes [ nodeId ]  =  true ;   
			
		
	
		
			
				
					                }   
			
		
	
		
			
				
					                else  {   
			
		
	
		
			
				
					                  // this node does not qualify after all.
   
			
		
	
		
			
				
					                  gatheringSuccessful  =  false ;   
			
		
	
		
			
				
					                  break ;   
			
		
	
		
			
				
					                }   
			
		
	
		
			
				
					              }   
			
		
	
		
			
				
					            }   
			
		
	
		
			
				
					            clonedOptions  =  this . _cloneOptions ( this . body . nodes [ childNodeId ] ) ;   
			
		
	
		
			
				
					            if  ( options . joinCondition ( clonedOptions )  ===  true  &&  this . _checkIfUsed ( clusters , nodeId , edge . id )  ===  false )  {   
			
		
	
		
			
				
					              childEdgesObj [ edge . id ]  =  edge ;   
			
		
	
		
			
				
					              childNodesObj [ childNodeId ]  =  this . body . nodes [ childNodeId ] ;   
			
		
	
		
			
				
					            else  {   
			
		
	
		
			
				
					              // this node does not qualify after all.
    
			
		
	
		
			
				
					              gatheringSuccessful  =  fals e;   
			
		
	
		
			
				
					              break ;   
			
		
	
		
			
				
					            }   
			
		
	
		
			
				
					          }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					          if  ( Object . keys ( childNodesObj ) . length  >  0  &&  Object . keys ( childEdgesObj ) . length  >  0 )  {   
			
		
	
		
			
				
					          // add to the cluster queue
   
			
		
	
		
			
				
					          if  ( Object . keys ( childNodesObj ) . length  >  0  &&  Object . keys ( childEdgesObj ) . length  >  0  &&  gatheringSuccessful  ===  true )  {   
			
		
	
		
			
				
					            clusters . push ( { nodes :  childNodesObj ,  edges :  childEdgesObj } )   
			
		
	
		
			
				
					          }   
			
		
	
		
			
				
					        }   
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -145,17 +167,26 @@ class ClusterEngine {  
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					  }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					  / * *   
			
		
	
		
			
				
					  *  Cluster  all  nodes  in  the  network  that  have  only  1  edge   
			
		
	
		
			
				
					  *  @ param  options   
			
		
	
		
			
				
					  *  @ param  refreshData   
			
		
	
		
			
				
					  * /   
			
		
	
		
			
				
					  clusterOutliers ( options ,  refreshData  =  true )  {   
			
		
	
		
			
				
					    this . clusterByEdgeCount ( 1 , options , refreshData ) ;   
			
		
	
		
			
				
					  }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					  _checkIfUsed ( clusters ,  nodeId ,  edgeId )  {   
			
		
	
		
			
				
					    for  ( let  i  =  0 ;  i  <  clusters . length ;  i ++ )  {   
			
		
	
		
			
				
					      let  cluster  =  clusters [ i ] ;   
			
		
	
		
			
				
					      if  ( cluster . nodes [ nodeId ]  !==  undefined  ||  cluster . edges [ edgeId ]  !==  undefined )  {   
			
		
	
		
			
				
					        return  true ;   
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					    return  false ;   
			
		
	
		
			
				
					  / * *   
			
		
	
		
			
				
					   *  Cluster  all  nodes  in  the  network  that  have  only  2  edge   
			
		
	
		
			
				
					   *  @ param  options   
			
		
	
		
			
				
					   *  @ param  refreshData   
			
		
	
		
			
				
					   * /   
			
		
	
		
			
				
					  clusterBridges ( options ,  refreshData  =  true )  {   
			
		
	
		
			
				
					    this . clusterByEdgeCount ( 2 , options , refreshData ) ;   
			
		
	
		
			
				
					  }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					  / * *   
			
		
	
		
			
				
					  *  suck  all  connected  nodes  of  a  node  into  the  node .   
			
		
	
		
			
				
					  *  @ param  nodeId   
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -187,25 +218,31 @@ class ClusterEngine {  
			
		
	
		
			
				
					    // collect the nodes that will be in the cluster
   
			
		
	
		
			
				
					    for  ( let  i  =  0 ;  i  <  node . edges . length ;  i ++ )  {   
			
		
	
		
			
				
					      let  edge  =  node . edges [ i ] ;   
			
		
	
		
			
				
					      let  childNodeId  =  this . _getConnectedId ( edge ,  parentNodeId ) ;   
			
		
	
		
			
				
					      if  ( edge . hiddenByCluster  !==  true )  {   
			
		
	
		
			
				
					        let  childNodeId  =  this . _getConnectedId ( edge ,  parentNodeId ) ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					      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.
   
			
		
	
		
			
				
					          let  childClonedOptions  =  this . _cloneOptions ( this . body . nodes [ childNodeId ] ) ;   
			
		
	
		
			
				
					          if  ( options . joinCondition ( parentClonedOptions ,  childClonedOptions )  ===  true )  {   
			
		
	
		
			
				
					        // if the child node is not in a cluster (may not be needed now with the edge.hiddenByCluster check)
   
			
		
	
		
			
				
					        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.
   
			
		
	
		
			
				
					              let  childClonedOptions  =  this . _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 ;   
			
		
	
		
			
				
					            childNodesObj [ childNodeId ]  =  this . body . nodes [ childNodeId ] ;   
			
		
	
		
			
				
					          }   
			
		
	
		
			
				
					        }   
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					      else  {   
			
		
	
		
			
				
					        childEdgesObj [ edge . id ]  =  edge ;   
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    this . _cluster ( childNodesObj ,  childEdgesObj ,  options ,  refreshData ) ;   
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -235,18 +272,21 @@ class ClusterEngine {  
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					  / * *   
			
		
	
		
			
				
					  *  This  function  creates  the  edges  that  will  be  attached  to  the  cluster .   
			
		
	
		
			
				
					  *  This  function  creates  the  edges  that  will  be  attached  to  the  cluster   
			
		
	
		
			
				
					  *  It  looks  for  edges  that  are  connected  to  the  nodes  from  the  " outside '  of  the  cluster .   
			
		
	
		
			
				
					  *   
			
		
	
		
			
				
					  *  @ param  childNodesObj   
			
		
	
		
			
				
					  *  @ param  childEdgesObj   
			
		
	
		
			
				
					  *  @ param  newEdges   
			
		
	
		
			
				
					  *  @ param  options   
			
		
	
		
			
				
					  *  @ private   
			
		
	
		
			
				
					  * /   
			
		
	
		
			
				
					  _createClusterEdges  ( childNodesObj ,  childEdgesObj ,  newEdges ,  c lusterNodeProperties ,  clusterEdgeProperties )  {   
			
		
	
		
			
				
					  _createClusterEdges  ( childNodesObj ,  clusterNodeProperties ,  clusterEdgeProperties )  {   
			
		
	
		
			
				
					    let  edge ,  childNodeId ,  childNode ,  toId ,  fromId ,  otherNodeId ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    // loop over all child nodes and their edges to find edges going out of the cluster
   
			
		
	
		
			
				
					    // these edges will be replaced by clusterEdges.
   
			
		
	
		
			
				
					    let  childKeys  =  Object . keys ( childNodesObj ) ;   
			
		
	
		
			
				
					    let  createEdges  =  [ ] ;   
			
		
	
		
			
				
					    for  ( let  i  =  0 ;  i  <  childKeys . length ;  i ++ )  {   
			
		
	
		
			
				
					      childNodeId  =  childKeys [ i ] ;   
			
		
	
		
			
				
					      childNode  =  childNodesObj [ childNodeId ] ;   
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -254,31 +294,56 @@ class ClusterEngine {  
			
		
	
		
			
				
					      // construct new edges from the cluster to others
   
			
		
	
		
			
				
					      for  ( let  j  =  0 ;  j  <  childNode . edges . length ;  j ++ )  {   
			
		
	
		
			
				
					        edge  =  childNode . edges [ j ] ;   
			
		
	
		
			
				
					        childEdgesObj [ edge . id ]  =  edge ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					        // childNodeId position will be replaced by the cluster .
  
			
		
	
		
			
				
					        if  ( edge . toId  ==  childNodeId )  {  // this is a double equals because ints and strings can be interchanged here.
   
			
		
	
		
			
				
					          toId  =  clusterNodeProperties . id ;   
			
		
	
		
			
				
					          fromId  =  edge . fromId ;   
			
		
	
		
			
				
					          otherNodeId  =  fromId ;   
			
		
	
		
			
				
					        }   
			
		
	
		
			
				
					        else  {   
			
		
	
		
			
				
					          toId  =  edge . toId ;   
			
		
	
		
			
				
					          fromId  =  clusterNodeProperties . id ;   
			
		
	
		
			
				
					          otherNodeId  =  toId ;   
			
		
	
		
			
				
					        }   
			
		
	
		
			
				
					        // we only handle edges that are visible to the system, not the disabled ones from the clustering process.
   
			
		
	
		
			
				
					        if  ( edge . hiddenByCluster  !==  true )  {   
			
		
	
		
			
				
					          // set up the from and to .
   
			
		
	
		
			
				
					           if  ( edge . toId  ==  childNodeId )  {  // this is a double equals because ints and strings can be interchanged here.
   
			
		
	
		
			
				
					             toId  =  clusterNodeProperties . id ;   
			
		
	
		
			
				
					             fromId  =  edge . fromId ;   
			
		
	
		
			
				
					             otherNodeId  =  fromId ;   
			
		
	
		
			
				
					           }   
			
		
	
		
			
				
					           else  {   
			
		
	
		
			
				
					             toId  =  edge . toId ;   
			
		
	
		
			
				
					             fromId  =  clusterNodeProperties . id ;   
			
		
	
		
			
				
					             otherNodeId  =  toId ;   
			
		
	
		
			
				
					           }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					        // if the node connected to the cluster is also in the cluster we do not need a new edge.
   
			
		
	
		
			
				
					        if  ( childNodesObj [ otherNodeId ]  ===  undefined )  {   
			
		
	
		
			
				
					          let  clonedOptions  =  this . _cloneOptions ( edge ,  'edge' ) ;   
			
		
	
		
			
				
					          util . deepExtend ( clonedOptions ,  clusterEdgeProperties ) ;   
			
		
	
		
			
				
					          clonedOptions . from  =  fromId ;   
			
		
	
		
			
				
					          clonedOptions . to  =  toId ;   
			
		
	
		
			
				
					          clonedOptions . id  =  'clusterEdge:'  +  util . randomUUID ( ) ;   
			
		
	
		
			
				
					          newEdges . push ( this . body . functions . createEdge ( clonedOptions ) ) ;   
			
		
	
		
			
				
					          // Only edges from the cluster outwards are being replaced.
   
			
		
	
		
			
				
					          if  ( childNodesObj [ otherNodeId ]  ===  undefined )  {   
			
		
	
		
			
				
					            createEdges . push ( { edge :  edge ,  fromId :  fromId ,  toId :  toId } ) ;   
			
		
	
		
			
				
					          }   
			
		
	
		
			
				
					        }   
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    // here we actually create the replacement edges. We could not do this in the loop above as the creation process
   
			
		
	
		
			
				
					    // would add an edge to the edges array we are iterating over.
   
			
		
	
		
			
				
					    for  ( let  j  =  0 ;  j  <  createEdges . length ;  j ++ )  {   
			
		
	
		
			
				
					      let  edge  =  createEdges [ j ] . edge ;   
			
		
	
		
			
				
					      // copy the options of the edge we will replace
   
			
		
	
		
			
				
					      let  clonedOptions  =  this . _cloneOptions ( edge ,  'edge' ) ;   
			
		
	
		
			
				
					      // make sure the properties of clusterEdges are superimposed on it
   
			
		
	
		
			
				
					      util . deepExtend ( clonedOptions ,  clusterEdgeProperties ) ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					      // set up the edge
   
			
		
	
		
			
				
					      clonedOptions . from  =  createEdges [ j ] . fromId ;   
			
		
	
		
			
				
					      clonedOptions . to  =  createEdges [ j ] . toId ;   
			
		
	
		
			
				
					      clonedOptions . id  =  'clusterEdge:'  +  util . randomUUID ( ) ;   
			
		
	
		
			
				
					      //clonedOptions.id = '(cf: ' + createEdges[j].fromId + " to: " + createEdges[j].toId + ")" + Math.random();
   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					      // create the edge and give a reference to the one it replaced.
   
			
		
	
		
			
				
					      let  newEdge  =  this . body . functions . createEdge ( clonedOptions ) ;   
			
		
	
		
			
				
					      newEdge . clusteringEdgeReplacingId  =  edge . id ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					      // connect the edge.
   
			
		
	
		
			
				
					      this . body . edges [ newEdge . id ]  =  newEdge ;   
			
		
	
		
			
				
					      newEdge . connect ( ) ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					      // hide the replaced edge
   
			
		
	
		
			
				
					      edge . setOptions ( { physics : false ,  hidden : true } ) ;   
			
		
	
		
			
				
					      edge . hiddenByCluster  =  true ;   
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					  }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					  / * *   
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -304,8 +369,17 @@ class ClusterEngine {  
			
		
	
		
			
				
					  *  @ private   
			
		
	
		
			
				
					  * /   
			
		
	
		
			
				
					  _cluster ( childNodesObj ,  childEdgesObj ,  options ,  refreshData  =  true )  {   
			
		
	
		
			
				
					    // kill condition: no children so cant cluster
   
			
		
	
		
			
				
					    if  ( Object . keys ( childNodesObj ) . length  ===  0 )  { return ; }   
			
		
	
		
			
				
					    // kill condition: no children so can't cluster or only one node in the cluster, dont bother
   
			
		
	
		
			
				
					    if  ( Object . keys ( childNodesObj ) . length  <  2 )  { return ; }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    // check if this cluster call is not trying to cluster anything that is in another cluster.
   
			
		
	
		
			
				
					    for  ( let  nodeId  in  childNodesObj )  {   
			
		
	
		
			
				
					      if  ( childNodesObj . hasOwnProperty ( nodeId ) )  {   
			
		
	
		
			
				
					        if  ( this . clusteredNodes [ nodeId ]  !==  undefined )  {   
			
		
	
		
			
				
					          return ;   
			
		
	
		
			
				
					        }   
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    let  clusterNodeProperties  =  util . deepExtend ( { } , options . clusterNodeProperties ) ;   
			
		
	
		
			
				
					
  
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -314,17 +388,21 @@ class ClusterEngine {  
			
		
	
		
			
				
					      // get the childNode options
   
			
		
	
		
			
				
					      let  childNodesOptions  =  [ ] ;   
			
		
	
		
			
				
					      for  ( let  nodeId  in  childNodesObj )  {   
			
		
	
		
			
				
					        let  clonedOptions  =  this . _cloneOptions ( childNodesObj [ nodeId ] ) ;   
			
		
	
		
			
				
					        childNodesOptions . push ( clonedOptions ) ;   
			
		
	
		
			
				
					        if  ( childNodesObj . hasOwnProperty ( nodeId ) )  {   
			
		
	
		
			
				
					          let  clonedOptions  =  this . _cloneOptions ( childNodesObj [ nodeId ] ) ;   
			
		
	
		
			
				
					          childNodesOptions . push ( clonedOptions ) ;   
			
		
	
		
			
				
					        }   
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					      // get clusterproperties based on childNodes
   
			
		
	
		
			
				
					      let  childEdgesOptions  =  [ ] ;   
			
		
	
		
			
				
					      for  ( let  edgeId  in  childEdgesObj )  {   
			
		
	
		
			
				
					        // these cluster edges will be removed on creation of the cluster.
   
			
		
	
		
			
				
					        if  ( edgeId . substr ( 0 , 12 )  !==  "clusterEdge:" )  {   
			
		
	
		
			
				
					          let  clonedOptions  =  this . _cloneOptions ( childEdgesObj [ edgeId ] ,  'edge' ) ;   
			
		
	
		
			
				
					          childEdgesOptions . push ( clonedOptions ) ;   
			
		
	
		
			
				
					        if  ( childEdgesObj . hasOwnProperty ( edgeId ) )  {   
			
		
	
		
			
				
					          // these cluster edges will be removed on creation of the cluster.
   
			
		
	
		
			
				
					          if  ( edgeId . substr ( 0 ,  12 )  !==  "clusterEdge:" )  {   
			
		
	
		
			
				
					            let  clonedOptions  =  this . _cloneOptions ( childEdgesObj [ edgeId ] ,  'edge' ) ;   
			
		
	
		
			
				
					            childEdgesOptions . push ( clonedOptions ) ;   
			
		
	
		
			
				
					          }   
			
		
	
		
			
				
					        }   
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					
  
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -350,9 +428,7 @@ class ClusterEngine {  
			
		
	
		
			
				
					      clusterNodeProperties . x  =  pos . x ;   
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					    if  ( clusterNodeProperties . y  ===  undefined )  {   
			
		
	
		
			
				
					      if  ( pos  ===  undefined )  {   
			
		
	
		
			
				
					        pos  =  this . _getClusterPosition ( childNodesObj ) ;   
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					      if  ( pos  ===  undefined )  { pos  =  this . _getClusterPosition ( childNodesObj ) ; }   
			
		
	
		
			
				
					      clusterNodeProperties . y  =  pos . y ;   
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					
  
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -371,28 +447,15 @@ class ClusterEngine {  
			
		
	
		
			
				
					    this . body . nodes [ clusterNodeProperties . id ]  =  clusterNode ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    // create the new edges that will connect to the cluster
   
			
		
	
		
			
				
					    let  newEdges  =  [ ] ;   
			
		
	
		
			
				
					    this . _createClusterEdges ( childNodesObj ,  childEdgesObj ,  newEdges ,  clusterNodeProperties ,  options . clusterEdgeProperties ) ;   
			
		
	
		
			
				
					    this . _createClusterEdges ( childNodesObj ,  clusterNodeProperties ,  options . clusterEdgeProperties ) ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    // disable the childEdges
   
			
		
	
		
			
				
					    for  ( let  edgeId  in  childEdgesObj )  {   
			
		
	
		
			
				
					      if  ( childEdgesObj . hasOwnProperty ( edgeId ) )  {   
			
		
	
		
			
				
					        if  ( this . body . edges [ edgeId ]  !==  undefined )  {   
			
		
	
		
			
				
					          let  edge  =  this . body . edges [ edgeId ] ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					          // if this is a cluster edge that is fully encompassed in the cluster, we want to delete it
   
			
		
	
		
			
				
					          // this check verifies that both of the connected nodes are in this cluster
   
			
		
	
		
			
				
					          if  ( edgeId . substr ( 0 , 12 )  ===  "clusterEdge:"  &&  childNodesObj [ edge . fromId ]  !==  undefined  &&  childNodesObj [ edge . toId ]  !==  undefined )  {   
			
		
	
		
			
				
					            edge . cleanup ( ) ;   
			
		
	
		
			
				
					            // this removes the edge from node.edges, which is why edgeIds is formed
   
			
		
	
		
			
				
					            edge . disconnect ( ) ;   
			
		
	
		
			
				
					            delete  childEdgesObj [ edgeId ] ;   
			
		
	
		
			
				
					            delete  this . body . edges [ edgeId ] ;   
			
		
	
		
			
				
					          }   
			
		
	
		
			
				
					          else  {   
			
		
	
		
			
				
					            edge . setOptions ( { physics : false ,  hidden : true } ) ;   
			
		
	
		
			
				
					            //edge.options.hidden = true;
   
			
		
	
		
			
				
					          }   
			
		
	
		
			
				
					          edge . setOptions ( { physics : false ,  hidden : true } ) ;   
			
		
	
		
			
				
					          edge . hiddenByCluster  =  true ;   
			
		
	
		
			
				
					        }   
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					    }   
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -405,12 +468,6 @@ class ClusterEngine {  
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    // push new edges to global
   
			
		
	
		
			
				
					    for  ( let  i  =  0 ;  i  <  newEdges . length ;  i ++ )  {   
			
		
	
		
			
				
					      this . body . edges [ newEdges [ i ] . id ]  =  newEdges [ i ] ;   
			
		
	
		
			
				
					      this . body . edges [ newEdges [ i ] . id ] . connect ( ) ;   
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    // set ID to undefined so no duplicates arise
   
			
		
	
		
			
				
					    clusterNodeProperties . id  =  undefined ;   
			
		
	
		
			
				
					
  
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -496,8 +553,8 @@ class ClusterEngine {  
			
		
	
		
			
				
					        if  ( containedNodes . hasOwnProperty ( nodeId ) )  {   
			
		
	
		
			
				
					          let  containedNode  =  this . body . nodes [ nodeId ] ;   
			
		
	
		
			
				
					          if  ( newPositions [ nodeId ]  !==  undefined )  {   
			
		
	
		
			
				
					            containedNode . x  =  newPositions [ nodeId ] . x  || clusterNode . x ;   
			
		
	
		
			
				
					            containedNode . y  =  newPositions [ nodeId ] . y  || clusterNode . y ;   
			
		
	
		
			
				
					            containedNode . x  =  ( newPositions [ nodeId ] . x  ===  undefined  ? clusterNode . x  :  newPositions [ nodeId ] . x )  ;   
			
		
	
		
			
				
					            containedNode . y  =  ( newPositions [ nodeId ] . y  ===  undefined  ? clusterNode . y  :  newPositions [ nodeId ] . y )  ;   
			
		
	
		
			
				
					          }   
			
		
	
		
			
				
					        }   
			
		
	
		
			
				
					      }   
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -525,78 +582,79 @@ class ClusterEngine {  
			
		
	
		
			
				
					        containedNode . vy  =  clusterNode . vy ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					        // we use these methods to avoid reinstantiating the shape, which happens with setOptions.
   
			
		
	
		
			
				
					        //containedNode.toggleHidden(false);
   
			
		
	
		
			
				
					        //containedNode.togglePhysics(true);
   
			
		
	
		
			
				
					        containedNode . setOptions ( { hidden : false ,  physics : true } ) ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					        delete  this . clusteredNodes [ nodeId ] ;   
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    // release edges
   
			
		
	
		
			
				
					    for  ( let  edgeId  in  containedEdges )  {   
			
		
	
		
			
				
					      if  ( containedEdges . hasOwnProperty ( edgeId ) )  {   
			
		
	
		
			
				
					        let  edge  =  containedEdges [ edgeId ] ;   
			
		
	
		
			
				
					        // if this edge was a temporary edge and it's connected nodes do not exist anymore, we remove it from the data
   
			
		
	
		
			
				
					        if  ( this . body . nodes [ edge . fromId ]  ===  undefined  ||  this . body . nodes [ edge . toId ]  ===  undefined  ||  edge . toId  ==  clusterNodeId  ||  edge . fromId  ==  clusterNodeId )  {   
			
		
	
		
			
				
					          edge . cleanup ( ) ;   
			
		
	
		
			
				
					          // this removes the edge from node.edges, which is why edgeIds is formed
   
			
		
	
		
			
				
					          edge . disconnect ( ) ;   
			
		
	
		
			
				
					          delete  this . body . edges [ edgeId ] ;   
			
		
	
		
			
				
					        }   
			
		
	
		
			
				
					        else  {   
			
		
	
		
			
				
					          // one of the nodes connected to this edge is in a cluster. We give the edge to that cluster so it will be released when that cluster is opened.
   
			
		
	
		
			
				
					          if  ( this . clusteredNodes [ edge . fromId ]  !==  undefined  ||  this . clusteredNodes [ edge . toId ]  !==  undefined )  {   
			
		
	
		
			
				
					            let  fromId ,  toId ;   
			
		
	
		
			
				
					            let  clusteredNode  =  this . clusteredNodes [ edge . fromId ]  ||  this . clusteredNodes [ edge . toId ] ;   
			
		
	
		
			
				
					            let  clusterId  =  clusteredNode . clusterId ;   
			
		
	
		
			
				
					            let  clusterNode  =  this . body . nodes [ clusterId ] ;   
			
		
	
		
			
				
					            clusterNode . containedEdges [ edgeId ]  =  edge ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					            if  ( this . clusteredNodes [ edge . fromId ]  !==  undefined )  {   
			
		
	
		
			
				
					              fromId  =  clusterId ;   
			
		
	
		
			
				
					              toId  =  edge . toId ;   
			
		
	
		
			
				
					            }   
			
		
	
		
			
				
					            else  {   
			
		
	
		
			
				
					              fromId  =  edge . fromId ;   
			
		
	
		
			
				
					              toId  =  clusterId ;   
			
		
	
		
			
				
					            }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					            // if both from and to nodes are visible, we create a new temporary edge
   
			
		
	
		
			
				
					            if  ( this . body . nodes [ fromId ] . options . hidden  !==  true  &&  this . body . nodes [ toId ] . options . hidden  !==  true )  {   
			
		
	
		
			
				
					              let  clonedOptions  =  this . _cloneOptions ( edge ,  'edge' ) ;   
			
		
	
		
			
				
					              let  id  =  'clusterEdge:'  +  util . randomUUID ( ) ;   
			
		
	
		
			
				
					              util . deepExtend ( clonedOptions ,  clusterNode . clusterEdgeProperties ) ;   
			
		
	
		
			
				
					              util . deepExtend ( clonedOptions ,  { from :  fromId ,  to :  toId ,  hidden :  false ,  physics :  true ,  id :  id } ) ;   
			
		
	
		
			
				
					              let  newEdge  =  this . body . functions . createEdge ( clonedOptions ) ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					              this . body . edges [ id ]  =  newEdge ;   
			
		
	
		
			
				
					              this . body . edges [ id ] . connect ( ) ;   
			
		
	
		
			
				
					            }   
			
		
	
		
			
				
					    // copy the clusterNode edges because we cannot iterate over an object that we add or remove from.
   
			
		
	
		
			
				
					    let  edgesToBeDeleted  =  [ ] ;   
			
		
	
		
			
				
					    for  ( let  i  =  0 ;  i  <  clusterNode . edges . length ;  i ++ )  {   
			
		
	
		
			
				
					      edgesToBeDeleted . push ( clusterNode . edges [ i ] ) ;   
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    // actually handling the deleting.
   
			
		
	
		
			
				
					    for  ( let  i  =  0 ;  i  <  edgesToBeDeleted . length ;  i ++ )  {   
			
		
	
		
			
				
					      let  edge  =  edgesToBeDeleted [ i ] ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					      let  otherNodeId  =  this . _getConnectedId ( edge ,  clusterNodeId ) ;   
			
		
	
		
			
				
					      // if the other node is in another cluster, we transfer ownership of this edge to the other cluster
   
			
		
	
		
			
				
					      if  ( this . clusteredNodes [ otherNodeId ]  !==  undefined )  {   
			
		
	
		
			
				
					        // transfer ownership:
   
			
		
	
		
			
				
					        let  otherCluster  =  this . body . nodes [ this . clusteredNodes [ otherNodeId ] . clusterId ] ;   
			
		
	
		
			
				
					        let  transferEdge  =  this . body . edges [ edge . clusteringEdgeReplacingId ] ;   
			
		
	
		
			
				
					        if  ( transferEdge  !==  undefined )  {   
			
		
	
		
			
				
					          otherCluster . containedEdges [ transferEdge . id ]  =  transferEdge ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					          // delete local reference
   
			
		
	
		
			
				
					          delete  containedEdges [ transferEdge . id ] ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					          // create new cluster edge from the otherCluster:
   
			
		
	
		
			
				
					          // get to and from
   
			
		
	
		
			
				
					          let  fromId  =  transferEdge . fromId ;   
			
		
	
		
			
				
					          let  toId  =  transferEdge . toId ;   
			
		
	
		
			
				
					          if  ( transferEdge . toId  ==  otherNodeId )  {   
			
		
	
		
			
				
					            toId  =  this . clusteredNodes [ otherNodeId ] . clusterId ;   
			
		
	
		
			
				
					          }   
			
		
	
		
			
				
					          else  {   
			
		
	
		
			
				
					            edge . setOptions ( { physics : true ,  hidden : false } ) ;   
			
		
	
		
			
				
					            //edge.options.hidden = false;
   
			
		
	
		
			
				
					            //edge.togglePhysics(true);
   
			
		
	
		
			
				
					            fromId  =  this . clusteredNodes [ otherNodeId ] . clusterId ;   
			
		
	
		
			
				
					          }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					          // clone the options and apply the cluster options to them
   
			
		
	
		
			
				
					          let  clonedOptions  =  this . _cloneOptions ( transferEdge ,  'edge' ) ;   
			
		
	
		
			
				
					          util . deepExtend ( clonedOptions ,  otherCluster . clusterEdgeProperties ) ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					          // apply the edge specific options to it.
   
			
		
	
		
			
				
					          let  id  =  'clusterEdge:'  +  util . randomUUID ( ) ;   
			
		
	
		
			
				
					          util . deepExtend ( clonedOptions ,  { from :  fromId ,  to :  toId ,  hidden :  false ,  physics :  true ,  id :  id } ) ;   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					          // create it
   
			
		
	
		
			
				
					          let  newEdge  =  this . body . functions . createEdge ( clonedOptions ) ;   
			
		
	
		
			
				
					          newEdge . clusteringEdgeReplacingId  =  transferEdge . id ;   
			
		
	
		
			
				
					          this . body . edges [ id ]  =  newEdge ;   
			
		
	
		
			
				
					          this . body . edges [ id ] . connect ( ) ;   
			
		
	
		
			
				
					        }   
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					      else  {   
			
		
	
		
			
				
					        let  replacedEdge  =  this . body . edges [ edge . clusteringEdgeReplacingId ] ;   
			
		
	
		
			
				
					        if  ( replacedEdge  !==  undefined )  {   
			
		
	
		
			
				
					          replacedEdge . setOptions ( { physics :  true ,  hidden :  false } ) ;   
			
		
	
		
			
				
					          replacedEdge . hiddenByCluster  =  false ;   
			
		
	
		
			
				
					        }   
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					      edge . cleanup ( ) ;   
			
		
	
		
			
				
					      // this removes the edge from node.edges, which is why edgeIds is formed
   
			
		
	
		
			
				
					      edge . disconnect ( ) ;   
			
		
	
		
			
				
					      delete  this . body . edges [ edge . id ] ;   
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    // remove all temporary edges, make an array of ids so we don't remove from the list we're iterating over.
   
			
		
	
		
			
				
					    let  removeIds  =  [ ] ;   
			
		
	
		
			
				
					    for  ( let  i  =  0 ;  i  <  clusterNode . edges . length ;  i ++ )  {   
			
		
	
		
			
				
					      let  edgeId  =  clusterNode . edges [ i ] . id ;   
			
		
	
		
			
				
					      removeIds . push ( edgeId ) ;   
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    // actually removing the edges
   
			
		
	
		
			
				
					    for  ( let  i  =  0 ;  i  <  removeIds . length ;  i ++ )  {   
			
		
	
		
			
				
					      let  edgeId  =  removeIds [ i ] ;   
			
		
	
		
			
				
					      this . body . edges [ edgeId ] . cleanup ( ) ;   
			
		
	
		
			
				
					      // this removes the edge from node.edges, which is why edgeIds is formed
   
			
		
	
		
			
				
					      this . body . edges [ edgeId ] . disconnect ( ) ;   
			
		
	
		
			
				
					      delete  this . body . edges [ edgeId ] ;   
			
		
	
		
			
				
					    // handle the releasing of the edges
   
			
		
	
		
			
				
					    for  ( let  edgeId  in  containedEdges )  {   
			
		
	
		
			
				
					      if  ( containedEdges . hasOwnProperty ( edgeId ) )  {   
			
		
	
		
			
				
					        let  edge  =  containedEdges [ edgeId ] ;   
			
		
	
		
			
				
					        edge . setOptions ( { physics :  true ,  hidden :  false } ) ;   
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					    }   
			
		
	
		
			
				
					
  
			
		
	
		
			
				
					    // remove clusterNode