diff --git a/01_Code/physical_computing_interface/demos/app1.js b/01_Code/physical_computing_interface/demos/app1.js
index 94f09b4bf31175a79dabab558209d1fc53146a68..364847f026eb6217a0833d931c6144e0b84847fd 100644
--- a/01_Code/physical_computing_interface/demos/app1.js
+++ b/01_Code/physical_computing_interface/demos/app1.js
@@ -17,51 +17,67 @@ initEditor();// todo enclose into class
 var assembler= new Assembler(three,GLOBALS,1,50,[new THREE.Vector3(0,0,0)],[new THREE.Vector3(GLOBALS.gridSize/2.0*GLOBALS.voxelSpacing,0,0)]);
 assembler.run();
 
-var info={
-    name:"MNIST",
-    imageSize:"(28,28)",
-    numDatasets:65000,
-    numTraining:55000,
-    numTest:65000-55000,
+info={
+    name:"prediction",
+    parent: 'CNN',
+    classes: 'output',
+    outputNeigh:-1 //-1:none,0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,6:y+1&&z-1
 };
-GLOBALS.addNode(0,8,0 ,false,info);
+GLOBALS.addNode(0,12,0,false,info);
 info={
-    name:"conv2d_Conv2D1",
-    inputShape:"(batch,28,28,1)",
-    kernelSize:5,
-    filters:8,
-    strides:1,
-    activation: 'relu',
+    name:"categoricalCrossentropy",
+    metrics: ['accuracy'],
+    parent: 'CNN',
+    classes: 'loss',
+    outputNeigh:6 //-1:none,0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,6:y+1&&z-1
+
+};
+GLOBALS.addNode(0,11,1,false,info);
+info={
+    name:"dense",
+    inputShape:"(batch,256)",
     kernelInitializer: 'varianceScaling',
-    outputShape:"(batch,24,24,8)",
-    numParams:208,
-    Trainable:true
+    activation: 'softmax',
+    outputShape:"(batch,10)",
+    numParams:2570,
+    Trainable:true,
+    parent: 'CNN',
+    classes: 'layers',
+    outputNeigh:4 //-1:none,0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,6:y+1&&z-1
 
 };
-GLOBALS.addNode(0,8,1 ,false,info);
+GLOBALS.addNode(0,11,0,false,info);
 
 info={
-    name:"testImage",
-    imageSize:"(1,28,28)"
-};
-GLOBALS.addNode(0,8,2 ,false,info);
+    name:"flatten",
+    inputShape:"(batch,4,4,16)",
+    outputShape:"(batch,256)",
+    numParams:0,
+    Trainable:true,
+    parent: 'CNN',
+    classes: 'layers',
+    outputNeigh:6 //-1:none,0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,6:y+1&&z-1
 
+};
+GLOBALS.addNode(0,10,1,false,info);
 
 info={
-    name:"max_pooling2d_MaxPooling2D1",
-    inputShape:"(batch,24,24,8)",
+    name:"maxPooling2d",
+    inputShape:"(batch,12,12,8)",
     poolSize:"[2,2]",
     strides:"[2,2]",
-    outputShape:"(batch,12,12,8)",
+    outputShape:"(batch,4,4,16)",
     numParams:0,
-    Trainable:true
+    Trainable:true,
+    parent: 'CNN',
+    classes: 'layers',
+    outputNeigh:4 //-1:none,0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,6:y+1&&z-1
 
 };
-GLOBALS.addNode(0,9,0 ,false,info);
-
+GLOBALS.addNode(0,10,0,false,info);
 
 info={
-    name:"conv2d_Conv2D2",
+    name:"conv2d",
     inputShape:"(batch,12,12,8)",
     kernelSize:5,
     filters:16,
@@ -70,57 +86,64 @@ info={
     kernelInitializer: 'varianceScaling',
     outputShape:"(batch,8,8,16)",
     numParams:3216,
-    Trainable:true
+    Trainable:true,
+    parent: 'CNN',
+    classes: 'layers',
+    outputNeigh:6 //-1:none,0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,6:y+1&&z-1
 
 };
 GLOBALS.addNode(0,9,1 ,false,info);
 
 info={
-    name:"max_pooling2d_MaxPooling2D2",
-    inputShape:"(batch,12,12,8)",
+    name:"maxPooling2d",
+    inputShape:"(batch,24,24,8)",
     poolSize:"[2,2]",
     strides:"[2,2]",
-    outputShape:"(batch,4,4,16)",
-    numParams:0,
-    Trainable:true
-
-};
-GLOBALS.addNode(0,10,0,false,info);
-
-info={
-    name:"flatten_Flatten1",
-    inputShape:"(batch,4,4,16)",
-    outputShape:"(batch,256)",
+    outputShape:"(batch,12,12,8)",
     numParams:0,
-    Trainable:true
+    Trainable:true,
+    parent: 'CNN',
+    classes: 'layers',
+    outputNeigh:4 //-1:none,0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,6:y+1&&z-1
 
 };
-GLOBALS.addNode(0,10,1,false,info);
+GLOBALS.addNode(0,9,0 ,false,info);
 
 info={
-    name:"dense_Dense1",
-    inputShape:"(batch,256)",
+    name:"conv2d",
+    inputShape:"(batch,28,28,1)",
+    kernelSize:5,
+    filters:8,
+    strides:1,
+    activation: 'relu',
     kernelInitializer: 'varianceScaling',
-    activation: 'softmax',
-    outputShape:"(batch,10)",
-    numParams:2570,
-    Trainable:true
+    outputShape:"(batch,24,24,8)",
+    numParams:208,
+    Trainable:true,
+    parent: 'CNN',
+    classes: 'layers',
+    outputNeigh:6 //-1:none,0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,6:y+1&&z-1
 
 };
-GLOBALS.addNode(0,11,0,false,info);
-
-
-info={
-    name:"loss_categoricalCrossentropy",
-    metrics: ['accuracy'],
+GLOBALS.addNode(0,8,1 ,false,info);
 
+var info={
+    name:"MNIST",
+    imageSize:"(28,28)",
+    numDatasets:65000,
+    numTraining:55000,
+    numTest:65000-55000,
+    parent: 'CNN',
+    classes: 'input',
+    outputNeigh:4 //-1:none,0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,6:y+1&&z-1
 };
-GLOBALS.addNode(0,11,1,false,info);
+GLOBALS.addNode(0,8,0 ,false,info);
 
 info={
-    name:"prediction"
+    name:"Test",
+    imageSize:"(1,28,28)",
+    parent: 'CNN',
+    classes: 'viz',
+    outputNeigh:5 //-1:none,0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,6:y+1&&z-1
 };
-GLOBALS.addNode(0,12,0,false,info);
-
-
-
+GLOBALS.addNode(0,8,2 ,false,info);
\ No newline at end of file
diff --git a/01_Code/physical_computing_interface/demos/indexDNN.html b/01_Code/physical_computing_interface/demos/indexDNN.html
index edfaa104c9efd64614eebe81e867a0d970881c67..696213e4787b3afb8d9359e26df86a41fc289463 100644
--- a/01_Code/physical_computing_interface/demos/indexDNN.html
+++ b/01_Code/physical_computing_interface/demos/indexDNN.html
@@ -99,7 +99,6 @@
 <!-- TODO: 
             Clean structure to modules?
             Add another footer
-             
 -->
 
 
@@ -120,6 +119,9 @@
 <script src="https://unpkg.com/cytoscape-cose-bilkent/cytoscape-cose-bilkent.js"></script>
 <script src="../lib/cytoscape-expand-collapse.js"></script>
 
+<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script><!-- edge handling-->
+<script src="../lib/cytoscape-edgehandles.js"></script><!-- edge handling-->
+
 
 <script src="../lib/jsoneditor/jsoneditor.js"></script>
 
@@ -140,17 +142,13 @@
 <script src="../assembly/replay.js"></script><!-- assembly and timestep handling -->
 
 <script src="../graph/graph.js"></script><!--graph flow visualization-->
-<script src="../dnn/data.js" type="module"></script><!-- graph flow visualization-->
-<script src="../dnn/graph.js"></script><!-- graph flow visualization-->
-
-<script src="../dnn/script.js" type="module"></script><!-- graph flow visualization-->
-
-
+<script src="../dnn/data.js" type="module"></script><!-- dnn-->
+<script src="../dnn/graph.js"></script><!-- dnn graph-->
 
+<script src="../dnn/script.js" type="module"></script><!-- dnn -->
 
 <script src="../json/json.js"></script><!-- json -->
 
-
 <script src="./app1.js"></script><!-- threejs visualization -->
 
 
diff --git a/01_Code/physical_computing_interface/dnn/graph.js b/01_Code/physical_computing_interface/dnn/graph.js
index 1acf35f5e494b807bd5aefbebeea66c893f7f379..ddc4ec216bce9e7690f177a4bbf0f90155ee4c1a 100644
--- a/01_Code/physical_computing_interface/dnn/graph.js
+++ b/01_Code/physical_computing_interface/dnn/graph.js
@@ -33,19 +33,26 @@ var cyy = cytoscape({
         'color': 'white',
         'font-family': "consolas",
         // 'font-family':"Times New Roman",
-        'width': 80,
-        'height': 80
+        'width': 120,
+        'height': 120
       })
     .selector('edge')
       .css({
         'content': 'data(name)',
         'width': 8,
-        'line-color': '#888',
-        'target-arrow-color': '#888',
-        'source-arrow-color': '#888',
-        'target-arrow-shape': 'triangle'
+        'line-color': color6,
+        'target-arrow-color': color6,
+        'source-arrow-color': color6,
+        'target-arrow-shape': 'vee',
+        'curve-style': 'bezier', //straight
       })
     .selector(':selected')
+      .css({
+        "border-width": 4,
+        'line-color': color4,
+        'target-arrow-color': color4,
+        "border-color": color4
+      })
     .selector('$node > node')
       .css({
         'shape': 'roundrectangle',
@@ -147,6 +154,41 @@ var cyy = cytoscape({
         'padding-left': 40,
         'padding-bottom': 40,
         'padding-right': 40
+      })
+      .selector('.eh-handle')
+      .css({
+        'background-color': color4,
+        'width': 12,
+        'height': 12,
+        'shape': 'ellipse',
+        'overlay-opacity': 0,
+        'border-width': 12, // makes the handle easier to hit
+        'border-opacity': 0
+      })
+      .selector('.eh-hover')
+        .css({
+          'background-color': color4
+      })
+      .selector('.eh-source')
+        .css({
+          'border-width': 2,
+          'border-color': color4
+      })
+      .selector('.eh-target')
+        .css({
+          'border-width': 2,
+          'border-color': color4
+      })
+      .selector('.eh-preview, .eh-ghost-edge')
+        .css({
+          'background-color': color4,
+          'line-color': color4,
+          'target-arrow-color': color4,
+          'source-arrow-color': color4
+      })
+      .selector('.eh-ghost-edge.eh-preview-active')
+        .css({
+          'opacity': 0
       }),
 
   elements: {
@@ -186,60 +228,60 @@ var cyy = cytoscape({
       
       
       
-      {
-        data: { id: 'pred', name: 'prediction', parent: 'CNN' },
-        position: { x: 0, y: 0 },classes: 'output',
-      },
-      {
-        data: { id: '7', name: 'categoricalCrossentropy', parent: 'CNN' },
-        position: { x: 0, y: 0 },classes: 'loss',
-      },
-      {
-        data: { id: '6', name: 'dense', parent: 'CNN' },
-        position: { x: 0, y: 0 },classes: 'layers',
-      },
-      {
-        data: { id: '5', name: 'flatten', parent: 'CNN' },
-        position: { x: 0, y: 0 },classes: 'layers',
-      },
-      {
-        data: { id: '4', name: 'maxPooling2d', parent: 'CNN' },
-        position: { x: 0, y: 0 },classes: 'layers',
-      },
-      {
-        data: { id: '3', name: 'conv2d', parent: 'CNN' },
-        position: { x: 0, y: 0 },classes: 'layers',
-      },
-      {
-        data: { id: '2', name: 'maxPooling2d', parent: 'CNN' },
-        position: { x: 0, y: 0 },classes: 'layers',
-      },
-      {
-        data: { id: '1', name: 'conv2d', parent: 'CNN' },
-        position: { x: 0, y: 0 },classes: 'layers',
-      },
-      {
-        data: { id: '0', name: 'MNIST',parent:'CNN' },
-        position: { x: 0, y: 0 },classes: 'input',
-      },
-      {
-        data: { id: 'ex', name: 'Test',parent:'CNN' },
-        position: { x: 0, y: 0 },classes: 'viz',
-      },
+      // {
+      //   data: { id: '[0,12,0]', name: 'prediction', parent: 'CNN' },
+      //   position: { x: 0, y: 0 },classes: 'output',
+      // },
+      // {
+      //   data: { id: '[0,11,1]', name: 'categoricalCrossentropy', parent: 'CNN' },
+      //   position: { x: 0, y: 0 },classes: 'loss',
+      // },
+      // {
+      //   data: { id: '[0,11,0]', name: 'dense', parent: 'CNN' },
+      //   position: { x: 0, y: 0 },classes: 'layers',
+      // },
+      // {
+      //   data: { id: '[0,10,1]', name: 'flatten', parent: 'CNN' },
+      //   position: { x: 0, y: 0 },classes: 'layers',
+      // },
+      // {
+      //   data: { id: '[0,10,0]', name: 'maxPooling2d', parent: 'CNN' },
+      //   position: { x: 0, y: 0 },classes: 'layers',
+      // },
+      // {
+      //   data: { id: '[0,9,1]', name: 'conv2d', parent: 'CNN' },
+      //   position: { x: 0, y: 0 },classes: 'layers',
+      // },
+      // {
+      //   data: { id: '[0,9,0]', name: 'maxPooling2d', parent: 'CNN' },
+      //   position: { x: 0, y: 0 },classes: 'layers',
+      // },
+      // {
+      //   data: { id: '[0,8,1]', name: 'conv2d', parent: 'CNN' },
+      //   position: { x: 0, y: 0 },classes: 'layers',
+      // },
+      // {
+      //   data: { id: '[0,8,0]', name: 'MNIST',parent:'CNN' },
+      //   position: { x: 0, y: 0 },classes: 'input',
+      // },
+      // {
+      //   data: { id: '[0,8,2]', name: 'Test',parent:'CNN' },
+      //   position: { x: 0, y: 0 },classes: 'viz',
+      // },
 
 
     ],
     edges: [
 
-      { data: { source: '0', target: '1' , name: ''},classes: 'exte', },
-      { data: { source: '1', target: '2' , name: ''},classes: 'exte', },
-      { data: { source: '2', target: '3' , name: ''},classes: 'exte', },
-      { data: { source: '3', target: '4' , name: ''},classes: 'exte', },
-      { data: { source: '4', target: '5' , name: ''},classes: 'exte', },
-      { data: { source: '5', target: '6' , name: ''},classes: 'exte', },
-      { data: { source: '6', target: '7' , name: ''},classes: 'exte', },
-      { data: { source: '7', target: 'pred' , name: ''},classes: 'exte', },
-      { data: { source: 'ex', target: '1' , name: ''},classes: 'exte', },
+      // { data: { source: '[0,8,0]', target: '[0,8,1]' , name: ''},classes: 'exte', },
+      // { data: { source: '[0,8,1]', target: '[0,9,0]' , name: ''},classes: 'exte', },
+      // { data: { source: '[0,9,0]', target: '[0,9,1]' , name: ''},classes: 'exte', },
+      // { data: { source: '[0,9,1]', target: '[0,10,0]' , name: ''},classes: 'exte', },
+      // { data: { source: '[0,10,0]', target: '[0,10,1]' , name: ''},classes: 'exte', },
+      // { data: { source: '[0,10,1]', target: '[0,11,0]' , name: ''},classes: 'exte', },
+      // { data: { source: '[0,11,0]', target: '[0,11,1]' , name: ''},classes: 'exte', },
+      // { data: { source: '[0,11,1]', target: '[0,12,0]' , name: ''},classes: 'exte', },
+      // { data: { source: '[0,8,2]', target: '[0,8,1]' , name: ''},classes: 'exte', },
 
 
 
@@ -258,7 +300,7 @@ api.expandAll();
 // cy.$('#vizs').style('background-image', 'https://farm6.staticflickr.com/5109/5817854163_eaccd688f5_b.jpg');
 // cy.$('#vizs').style('background-image', dataUri);
 
-console.log(cyy.$id("vizs"))
+// console.log(cyy.$id("vizs"))
 
 
 function createImage(width,height){
@@ -295,3 +337,246 @@ function createImage(width,height){
     
 }
 
+document.addEventListener('selectNode', function (e) { 
+        
+  var tgt=cyy.$id(e.detail.id);
+  cyy.nodes().unselect();//unselect the rest
+  tgt.select();
+  
+}, false);
+
+cyy.on('tap', 'node', function(){
+
+  if (this.selected()) {
+    //console.log('double clicked ' + this.id());
+    if(this.id()=="[0,8,2]") //example node
+    {
+      GLOBALS.runExamplePrediction();
+
+    }
+  }
+
+  // var nodes = this;
+  // GLOBALS.selectedjson=this._private.data.data;
+  var pos=utils.getXYZfromName(this.data('id'));
+  GLOBALS.selectNode(pos.x,pos.y,pos.z);
+
+  
+  // this.trigger('blaa');
+});
+
+document.addEventListener('addNode', function (e) { 
+  cyy.edges().unselect();//unselect the rest
+
+  var neighborhood=0;
+  
+  cyy.add({
+      classes: ''+e.detail.data.classes,
+      data: { 
+          id: e.detail.id,
+          name: e.detail.data.name,
+          parent: ''+e.detail.data.parent,
+          
+          isNeighborhood :0,
+          data:{
+              id: e.detail.id,
+              name: '[' +e.detail.x +"," +e.detail.y+"," +e.detail.z+']',
+              neighbors:[],
+              dnn:e.detail.data
+          }
+      },
+      position: {
+          x: e.detail.posX,
+          y: e.detail.posY,
+      }
+  });
+
+  x=parseInt(e.detail.x);
+  y=parseInt(e.detail.y);
+  z=parseInt(e.detail.z);
+  var i1,j1,k1;
+
+  //-1:none,0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,6:y+1&&z-1
+  if(parseInt(e.detail.data.outputNeigh)==0){
+    i1=x+1;
+    j1=y+0;
+    k1=z+0;
+
+  }else if(parseInt(e.detail.data.outputNeigh)==1){
+    i1=x-1;
+    j1=y+0;
+    k1=z+0;
+
+  }else if(parseInt(e.detail.data.outputNeigh)==2){
+    i1=x+0;
+    j1=y+1;
+    k1=z+0;
+
+  }else if(parseInt(e.detail.data.outputNeigh)==3){
+    i1=x+0;
+    j1=y-1;
+    k1=z+0;
+
+  }else if(parseInt(e.detail.data.outputNeigh)==4){
+    i1=x+0;
+    j1=y+0;
+    k1=z+1;
+
+  }else if(parseInt(e.detail.data.outputNeigh)==5){
+    i1=x+0;
+    j1=y+0;
+    k1=z-1;
+
+  }else if(parseInt(e.detail.data.outputNeigh)==6){
+    i1=x+0;
+    j1=y+1;
+    k1=z-1;
+
+  }
+
+  if(parseInt(e.detail.data.outputNeigh)>=0){
+    cyy.add([
+      { group: "edges",data: { name:'', source: '[' +x +"," +y+","+z+']', target: '[' +i1 +"," +j1+","+k1+']'}}
+    ]);
+
+  }
+  
+
+  
+
+  // addEdges(e.detail.x,e.detail.y,e.detail.z);
+
+  // var tgt=cy.$id('[' +e.detail.x +"," +e.detail.y+","+e.detail.z+']');
+  // updateName(tgt);
+
+  // neighborhood=findNeighborhood(tgt);
+
+  // tgt.move({parent: ''+neighborhood});
+  // tgt._private.data.data.parent= neighborhood;
+
+  // api.expandAll();
+  
+}, false);
+
+document.addEventListener('updateNode', function (e) { 
+  cyy.edges().unselect();//unselect the rest
+
+  var tgt=cyy.$id(''+e.detail.id);
+  tgt.remove();
+  console.log(tgt._private.position.x)
+
+  // tgt._private.data.name=e.detail.dnn.name;
+  // tgt._private.data.classes=e.detail.dnn.classes;
+  // tgt._private.data.parent=''+e.detail.dnn.parent;
+  // tgt._private.data.data.dnn=''+e.detail.dnn;
+  
+  cyy.add({
+      classes: ''+e.detail.dnn.classes,
+      data: { 
+          id: e.detail.id,
+          name: e.detail.dnn.name,
+          parent: ''+e.detail.dnn.parent,
+          
+          isNeighborhood :0,
+          data:{
+              id: e.detail.id,
+              name: '[' +e.detail.x +"," +e.detail.y+"," +e.detail.z+']',
+              neighbors:[],
+              dnn:e.detail.dnn
+          }
+      },
+      position: {
+          x: e.detail.posX,
+          y: e.detail.posY,
+      }
+  });
+
+  x=parseInt(e.detail.x);
+  y=parseInt(e.detail.y);
+  z=parseInt(e.detail.z);
+  var i1,j1,k1;
+
+  //-1:none,0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,6:y+1&&z-1
+  if(parseInt(e.detail.dnn.outputNeigh)==0){
+    i1=x+1;
+    j1=y+0;
+    k1=z+0;
+
+  }else if(parseInt(e.detail.dnn.outputNeigh)==1){
+    i1=x-1;
+    j1=y+0;
+    k1=z+0;
+
+  }else if(parseInt(e.detail.dnn.outputNeigh)==2){
+    i1=x+0;
+    j1=y+1;
+    k1=z+0;
+
+  }else if(parseInt(e.detail.dnn.outputNeigh)==3){
+    i1=x+0;
+    j1=y-1;
+    k1=z+0;
+
+  }else if(parseInt(e.detail.dnn.outputNeigh)==4){
+    i1=x+0;
+    j1=y+0;
+    k1=z+1;
+
+  }else if(parseInt(e.detail.dnn.outputNeigh)==5){
+    i1=x+0;
+    j1=y+0;
+    k1=z-1;
+
+  }else if(parseInt(e.detail.dnn.outputNeigh)==6){
+    i1=x+0;
+    j1=y+1;
+    k1=z-1;
+
+  }
+
+  if(parseInt(e.detail.dnn.outputNeigh)>=0){
+    cyy.add([
+      { group: "edges",data: { name:'', source: '[' +x +"," +y+","+z+']', target: '[' +i1 +"," +j1+","+k1+']'}}
+    ]);
+
+  }
+  
+
+  
+
+  // addEdges(e.detail.x,e.detail.y,e.detail.z);
+
+  // var tgt=cy.$id('[' +e.detail.x +"," +e.detail.y+","+e.detail.z+']');
+  // updateName(tgt);
+
+  // neighborhood=findNeighborhood(tgt);
+
+  // tgt.move({parent: ''+neighborhood});
+  // tgt._private.data.data.parent= neighborhood;
+
+  // api.expandAll();
+  
+}, false);
+
+document.addEventListener('removeNode', function (e) { 
+  var tgt=cyy.$id(e.detail.id);
+  tgt.remove();
+
+}, false);
+
+
+var eh = cyy.edgehandles();
+
+document.querySelector('#draw-on').addEventListener('click', function() {
+  eh.enableDrawMode();
+});
+
+document.querySelector('#draw-off').addEventListener('click', function() {
+  eh.disableDrawMode();
+});
+
+document.querySelector('#start').addEventListener('click', function() {
+  eh.start( cyy.$('node:selected') );
+});
+
+
diff --git a/01_Code/physical_computing_interface/dnn/script.js b/01_Code/physical_computing_interface/dnn/script.js
index 8418b4077cde2c33c7b8270eff93936dd318bd30..8af7c3826333fab820213ce175dbdaa34f727c42 100644
--- a/01_Code/physical_computing_interface/dnn/script.js
+++ b/01_Code/physical_computing_interface/dnn/script.js
@@ -43,8 +43,10 @@ async function vizExample(data){
     });
     const imageArray=imageTensor.arraySync();
     var dataUri= createImage(imageArray);
-    cyy.$('#ex').style('background-image', dataUri);
-    updateVariable('pred', "????");
+    // cyy.$('#ex').style('background-image', dataUri); //?? todo
+
+    cyy.$id("[0,8,2]").style('background-image', dataUri); //?? todo
+    updateVariable('[0,12,0]', "????");
 
     
 }
@@ -115,6 +117,11 @@ async function run() {
 
 }
 
+async function runExample() {
+
+    await doPrediction1(model, data,  1);
+}
+
 function getModel() {
     const model = tf.sequential();
 
@@ -228,7 +235,7 @@ function doPrediction1(model, data, testDataSize = 1) {
     });
     const imageArray=imageTensor.arraySync();
     var dataUri= createImage(imageArray);
-    cyy.$('#ex').style('background-image', dataUri);
+    cyy.$id("[0,8,2]").style('background-image', dataUri);
     
 
 
@@ -240,7 +247,7 @@ function doPrediction1(model, data, testDataSize = 1) {
     testxs.dispose();
     console.log(preds.arraySync());
 
-    updateVariable('pred', ""+preds.arraySync());
+    updateVariable('[0,12,0]', ""+preds.arraySync());
     return [preds, labels];
 }
 
@@ -280,3 +287,4 @@ async function showConfusion(model, data) {
 
 document.addEventListener('DOMContentLoaded', load);
 document.addEventListener('runNode', run);
+document.addEventListener('runExamplePrediction', runExample);
diff --git a/01_Code/physical_computing_interface/globals.js b/01_Code/physical_computing_interface/globals.js
index cba7f0e88ff374e13221cbda63310cf9bd88b766..0559d46b32ec766da6828e8449df69504bcd8c03 100644
--- a/01_Code/physical_computing_interface/globals.js
+++ b/01_Code/physical_computing_interface/globals.js
@@ -330,6 +330,8 @@ globals.prototype.removeNode=function (x,y,z,replay=false){
 };
 
 globals.prototype.updateNode=function (data){
+    var pos=utils.getXYZfromName(data.id);
+    [p_x ,p_y ,p_z ,s_x ,s_y,s_z,r_y]=this.utils.getTransforms(this.grid,pos.x,pos.y,pos.z);
     var updateNodeEvent = new CustomEvent('updateNode', { 
         detail: 
         {
@@ -342,7 +344,15 @@ globals.prototype.updateNode=function (data){
             inputs:data.inputs,
             outputs:data.outputs,
             locked:data.locked,
-            numRuns:data.numRuns
+            numRuns:data.numRuns,
+            dnn:data.cnn,
+            x:pos.x,
+            y:pos.y,
+            z:pos.z,
+            posX:p_x,
+            posY:p_y,
+            posZ:p_z,
+            rotY:r_y
         }
     });
     document.dispatchEvent(updateNodeEvent);
@@ -419,6 +429,22 @@ globals.prototype.selectEdge=function (){
     });
     document.dispatchEvent(selectEdgeEvent);
 };
+
+globals.prototype.runExamplePrediction=function (){
+    var runExamplePredictionEvent = new CustomEvent('runExamplePrediction', { 
+        detail: 
+        {
+            // x:x,
+            // y:y,
+            // z:z,
+            // id:'[' +x +"," +y+","+z+']',
+            // posX:p_x,
+            // posY:p_y,
+            // posZ:p_z
+        }
+    });
+    document.dispatchEvent(runExamplePredictionEvent);
+};
 //////////////////////////////////////////////
 
 //////////////////////utils//////////////////
diff --git a/01_Code/physical_computing_interface/graph/graph.js b/01_Code/physical_computing_interface/graph/graph.js
index f28995447389f1463028487c26639c3785d11aad..41f4a69d629325d5936268765cf59bb37b85671a 100644
--- a/01_Code/physical_computing_interface/graph/graph.js
+++ b/01_Code/physical_computing_interface/graph/graph.js
@@ -394,7 +394,6 @@ function initGraph(){
     //////////////////////////////////////////////////
     
     //select node
-    //TODO: HIGHLIGHT NODE IN THREEJS
     cy.on('tap', 'node', function(){
         // var nodes = this;
         // GLOBALS.selectedjson=this._private.data.data;
@@ -484,6 +483,7 @@ function initGraph(){
                 y: e.detail.posY,
             }
         });
+        
 
         api.expandAll();
 
@@ -593,6 +593,7 @@ function initGraph(){
         tgt._private.data.data.code=data.code;
         tgt._private.data.data.locked=false;
         tgt._private.data.data.numRuns=0;
+        tgt._private.data.data.dnn=data.dnn;
 
         runNode(tgt); //todo: call event instead??
     
diff --git a/01_Code/physical_computing_interface/json/json.js b/01_Code/physical_computing_interface/json/json.js
index ebc2aebdfe12e5f83b1bafee60fe7623bc99c195..207e6cd0aef7f8ae5ae84f79dae7afaa0695fead 100644
--- a/01_Code/physical_computing_interface/json/json.js
+++ b/01_Code/physical_computing_interface/json/json.js
@@ -1,7 +1,201 @@
 function initEditor(){
+
+    // todo ?? move template creation to separate files
+    var templates = [
+        {
+            text:"DEM simulation node",
+            title: 'Insert prediction viewer',
+            className: 'jsoneditor-type-object',
+            field: 'voxel',
+            value: {
+                name:"prediction",
+                size:5,
+                E:20000,
+                area:10.0
+            }
+        },
+        {
+            text: 'MNIST',
+            title: 'Insert a MNIST dataset',
+            className: 'jsoneditor-type-object',
+            field: 'cnn',
+            value: {
+                name:"MNIST",
+                imageSize:"(28,28)",
+                numDatasets:65000,
+                numTraining:55000,
+                numTest:65000-55000,
+                parent: 'CNN',
+                classes: 'input',
+                outputNeigh:-1 //0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,
+            }
+        },
+        {
+            text:"conv2d",
+            title: 'Insert a 3d convolutionsl layer',
+            className: 'jsoneditor-type-object',
+            field: 'cnn',
+            value: {
+            name:"conv2d_Conv2D1",
+                inputShape:"(batch,28,28,1)",
+                kernelSize:5,
+                filters:8,
+                strides:1,
+                activation: 'relu',
+                kernelInitializer: 'varianceScaling',
+                outputShape:"(batch,24,24,8)",
+                numParams:208,
+                Trainable:true,
+                parent: 'CNN',
+                classes: 'layers',
+                outputNeigh:-1 //0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,
+            }
+        },
+        {
+            text:"testImage",
+            title: 'Insert a test Image',
+            className: 'jsoneditor-type-object',
+            field: 'cnn',
+            value: {
+                name:"testImage",
+                imageSize:"(1,28,28)",
+                parent: 'CNN',
+                classes: 'viz',
+                outputNeigh:-1 //0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,
+            }
+        },
+        {
+            text:"max_pooling2d",
+            title: 'Insert a 2d max pooling layer',
+            className: 'jsoneditor-type-object',
+            field: 'cnn',
+            value: {
+                name:"max_pooling2d_MaxPooling2D1",
+                inputShape:"(batch,24,24,8)",
+                poolSize:"[2,2]",
+                strides:"[2,2]",
+                outputShape:"(batch,12,12,8)",
+                numParams:0,
+                Trainable:true,
+                parent: 'CNN',
+                classes: 'layers',
+                outputNeigh:-1 //0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,
+            }
+        
+        },
+        {
+            text:"flatten",
+            title: 'flattern layer',
+            className: 'jsoneditor-type-object',
+            field: 'cnn',
+            value: {
+                name:"flatten_Flatten1",
+                inputShape:"(batch,4,4,16)",
+                outputShape:"(batch,256)",
+                numParams:0,
+                Trainable:true,
+                parent: 'CNN',
+                classes: 'layers',
+                outputNeigh:-1 //0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,
+            }
+        
+        },
+        {
+            text:"dense",
+            title: 'Insert a fully connected layer',
+            className: 'jsoneditor-type-object',
+            field: 'cnn',
+            value: {
+                name:"dense_Dense1",
+                inputShape:"(batch,256)",
+                kernelInitializer: 'varianceScaling',
+                activation: 'softmax',
+                outputShape:"(batch,10)",
+                numParams:2570,
+                Trainable:true,
+                parent: 'CNN',
+                classes: 'layers',
+                outputNeigh:-1 //0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,
+            }
+        
+        },
+        {
+            text:"loss_categoricalCrossentropy",
+            title: 'Insert a categoricalCrossentropy loss layer',
+            className: 'jsoneditor-type-object',
+            field: 'cnn',
+            value: {
+                name:"loss_categoricalCrossentropy",
+                metrics: ['accuracy'],
+                parent: 'CNN',
+                classes: 'loss',
+                outputNeigh:-1 //0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,
+            }
+        
+        },
+        {
+            text:"prediction",
+            title: 'Insert prediction viewer',
+            className: 'jsoneditor-type-object',
+            field: 'cnn',
+            value: {
+                name:"prediction",
+                parent: 'CNN',
+                classes: 'output',
+                outputNeigh:-1 //0:x+1,1:x-1,2:y+1,3:y-1,4:z+1,5:z-1,
+            }
+        }
+
+    ];
+
+    var schema = {
+        "title": "Example Schema",
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "firstName": {
+              "type": "string"
+            },
+            "lastName": {
+              "type": "string"
+            },
+            "gender": {
+              "enum": ["male", "female"]
+            },
+            "age": {
+              "description": "Age in years",
+              "type": "integer",
+              "minimum": 0
+            },
+            "job": {
+              "$ref": "job"
+            }
+          },
+          "required": ["firstName", "lastName"]
+        }
+    };
+  
+    var job = {
+        "title": "Job description",
+        "type": "object",
+        "properties": {
+            "company": {
+            "type": "string"
+            },
+            "role": {
+            "type": "string"
+            }
+        }
+    };
+
+
     // create the editor
     var container = document.getElementById('jsoneditor');
     var options = {
+        // schema: schema,
+        // schemaRefs: {"job": job},
+        templates: templates,
         onChangeText: function (jsonString) {
             // console.log(jsonString);
             // const json = editor.get();
@@ -11,19 +205,6 @@ function initEditor(){
     };
     var editor = new JSONEditor(container, options);
 
-    // set json //get info
-    // document.getElementById('setJSON').onclick = function () {
-    //     const json = {
-    //         'array': [1, 2, 3],
-    //         'boolean': true,
-    //         'color': '#82b92c',
-    //         'null': null,
-    //         'number': 123,
-    //         'object': {'a': 'b', 'c': 'd'},
-    //         'string': 'Hello World'
-    //     };
-    //     editor.set(GLOBALS.selectedjson);
-    // };
 
     document.addEventListener('selectNode', function (e) { 
         editor.set(GLOBALS.selectedjson);
diff --git a/01_Code/physical_computing_interface/lib/cytoscape-edgehandles.js b/01_Code/physical_computing_interface/lib/cytoscape-edgehandles.js
new file mode 100644
index 0000000000000000000000000000000000000000..1d64377ed8ad642f748ae6b94676d6172b3d6380
--- /dev/null
+++ b/01_Code/physical_computing_interface/lib/cytoscape-edgehandles.js
@@ -0,0 +1,1404 @@
+(function webpackUniversalModuleDefinition(root, factory) {
+	if(typeof exports === 'object' && typeof module === 'object')
+		module.exports = factory(require("lodash.memoize"), require("lodash.throttle"));
+	else if(typeof define === 'function' && define.amd)
+		define(["lodash.memoize", "lodash.throttle"], factory);
+	else if(typeof exports === 'object')
+		exports["cytoscapeEdgehandles"] = factory(require("lodash.memoize"), require("lodash.throttle"));
+	else
+		root["cytoscapeEdgehandles"] = factory(root["_"]["memoize"], root["_"]["throttle"]);
+})(this, function(__WEBPACK_EXTERNAL_MODULE_13__, __WEBPACK_EXTERNAL_MODULE_14__) {
+return /******/ (function(modules) { // webpackBootstrap
+/******/ 	// The module cache
+/******/ 	var installedModules = {};
+/******/
+/******/ 	// The require function
+/******/ 	function __webpack_require__(moduleId) {
+/******/
+/******/ 		// Check if module is in cache
+/******/ 		if(installedModules[moduleId]) {
+/******/ 			return installedModules[moduleId].exports;
+/******/ 		}
+/******/ 		// Create a new module (and put it into the cache)
+/******/ 		var module = installedModules[moduleId] = {
+/******/ 			i: moduleId,
+/******/ 			l: false,
+/******/ 			exports: {}
+/******/ 		};
+/******/
+/******/ 		// Execute the module function
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ 		// Flag the module as loaded
+/******/ 		module.l = true;
+/******/
+/******/ 		// Return the exports of the module
+/******/ 		return module.exports;
+/******/ 	}
+/******/
+/******/
+/******/ 	// expose the modules object (__webpack_modules__)
+/******/ 	__webpack_require__.m = modules;
+/******/
+/******/ 	// expose the module cache
+/******/ 	__webpack_require__.c = installedModules;
+/******/
+/******/ 	// identity function for calling harmony imports with the correct context
+/******/ 	__webpack_require__.i = function(value) { return value; };
+/******/
+/******/ 	// define getter function for harmony exports
+/******/ 	__webpack_require__.d = function(exports, name, getter) {
+/******/ 		if(!__webpack_require__.o(exports, name)) {
+/******/ 			Object.defineProperty(exports, name, {
+/******/ 				configurable: false,
+/******/ 				enumerable: true,
+/******/ 				get: getter
+/******/ 			});
+/******/ 		}
+/******/ 	};
+/******/
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
+/******/ 	__webpack_require__.n = function(module) {
+/******/ 		var getter = module && module.__esModule ?
+/******/ 			function getDefault() { return module['default']; } :
+/******/ 			function getModuleExports() { return module; };
+/******/ 		__webpack_require__.d(getter, 'a', getter);
+/******/ 		return getter;
+/******/ 	};
+/******/
+/******/ 	// Object.prototype.hasOwnProperty.call
+/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ 	// __webpack_public_path__
+/******/ 	__webpack_require__.p = "";
+/******/
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(__webpack_require__.s = 12);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+// Simple, internal Object.assign() polyfill for options objects etc.
+
+module.exports = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
+  for (var _len = arguments.length, srcs = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+    srcs[_key - 1] = arguments[_key];
+  }
+
+  srcs.filter(function (src) {
+    return src != null;
+  }).forEach(function (src) {
+    Object.keys(src).forEach(function (k) {
+      return tgt[k] = src[k];
+    });
+  });
+
+  return tgt;
+};
+
+/***/ }),
+/* 1 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var Edgehandles = __webpack_require__(10);
+var assign = __webpack_require__(0);
+
+module.exports = function (options) {
+  var cy = this;
+
+  return new Edgehandles(assign({ cy: cy }, options));
+};
+
+/***/ }),
+/* 2 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+function disableGestures() {
+  this.saveGestureState();
+
+  this.cy.zoomingEnabled(false).panningEnabled(false).boxSelectionEnabled(false);
+
+  if (this.options.disableBrowserGestures) {
+    var wlOpts = this.windowListenerOptions;
+
+    window.addEventListener('touchstart', this.preventDefault, wlOpts);
+    window.addEventListener('touchmove', this.preventDefault, wlOpts);
+    window.addEventListener('wheel', this.preventDefault, wlOpts);
+  }
+
+  return this;
+}
+
+function resetGestures() {
+  this.cy.zoomingEnabled(this.lastZoomingEnabled).panningEnabled(this.lastPanningEnabled).boxSelectionEnabled(this.lastBoxSelectionEnabled);
+
+  if (this.options.disableBrowserGestures) {
+    var wlOpts = this.windowListenerOptions;
+
+    window.removeEventListener('touchstart', this.preventDefault, wlOpts);
+    window.removeEventListener('touchmove', this.preventDefault, wlOpts);
+    window.removeEventListener('wheel', this.preventDefault, wlOpts);
+  }
+
+  return this;
+}
+
+function saveGestureState() {
+  var cy = this.cy;
+
+
+  this.lastPanningEnabled = cy.panningEnabled();
+  this.lastZoomingEnabled = cy.zoomingEnabled();
+  this.lastBoxSelectionEnabled = cy.boxSelectionEnabled();
+
+  return this;
+}
+
+module.exports = { disableGestures: disableGestures, resetGestures: resetGestures, saveGestureState: saveGestureState };
+
+/***/ }),
+/* 3 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+function addCytoscapeListeners() {
+  var _this = this;
+
+  var cy = this.cy,
+      options = this.options;
+
+  // grabbing nodes
+
+  this.addListener(cy, 'drag', function () {
+    return _this.grabbingNode = true;
+  });
+  this.addListener(cy, 'free', function () {
+    return _this.grabbingNode = false;
+  });
+
+  // show handle on hover
+  this.addListener(cy, 'mouseover', 'node', function (e) {
+    _this.show(e.target);
+  });
+
+  // hide handle on tap handle
+  this.addListener(cy, 'tap', 'node', function (e) {
+    var node = e.target;
+
+    if (!node.same(_this.handleNode)) {
+      _this.show(node);
+    }
+  });
+
+  // hide handle when source node moved
+  this.addListener(cy, 'position', 'node', function (e) {
+    if (e.target.same(_this.sourceNode)) {
+      _this.hide();
+    }
+  });
+
+  // start on tapstart handle
+  // start on tapstart node (draw mode)
+  // toggle on source node
+  this.addListener(cy, 'tapstart', 'node', function (e) {
+    var node = e.target;
+
+    if (node.same(_this.handleNode)) {
+      _this.start(_this.sourceNode);
+    } else if (_this.drawMode) {
+      _this.start(node);
+    } else if (node.same(_this.sourceNode)) {
+      _this.hide();
+    }
+  });
+
+  // update line on drag
+  this.addListener(cy, 'tapdrag', function (e) {
+    _this.update(e.position);
+  });
+
+  // hover over preview
+  this.addListener(cy, 'tapdragover', 'node', function (e) {
+    if (options.snap) {
+      // then ignore events like mouseover
+    } else {
+      _this.preview(e.target);
+    }
+  });
+
+  // hover out unpreview
+  this.addListener(cy, 'tapdragout', 'node', function (e) {
+    if (options.snap) {
+      // then keep the preview
+    } else {
+      _this.unpreview(e.target);
+    }
+  });
+
+  // stop gesture on tapend
+  this.addListener(cy, 'tapend', function () {
+    _this.stop();
+  });
+
+  // hide handle if source node is removed
+  this.addListener(cy, 'remove', function (e) {
+    if (e.target.same(_this.sourceNode)) {
+      _this.hide();
+    }
+  });
+
+  return this;
+}
+
+module.exports = { addCytoscapeListeners: addCytoscapeListeners };
+
+/***/ }),
+/* 4 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+/* eslint-disable no-unused-vars */
+var defaults = {
+  preview: true, // whether to show added edges preview before releasing selection
+  hoverDelay: 150, // time spent hovering over a target node before it is considered selected
+  handleNodes: 'node', // selector/filter function for whether edges can be made from a given node
+  snap: false, // when enabled, the edge can be drawn by just moving close to a target node (can be confusing on compound graphs)
+  snapThreshold: 50, // the target node must be less than or equal to this many pixels away from the cursor/finger
+  snapFrequency: 15, // the number of times per second (Hz) that snap checks done (lower is less expensive)
+  noEdgeEventsInDraw: false, // set events:no to edges during draws, prevents mouseouts on compounds
+  disableBrowserGestures: true, // during an edge drawing gesture, disable browser gestures such as two-finger trackpad swipe and pinch-to-zoom
+  handlePosition: function handlePosition(node) {
+    return 'middle top'; // sets the position of the handle in the format of "X-AXIS Y-AXIS" such as "left top", "middle top"
+  },
+  handleInDrawMode: false, // whether to show the handle in draw mode
+  edgeType: function edgeType(sourceNode, targetNode) {
+    // can return 'flat' for flat edges between nodes or 'node' for intermediate node between them
+    // returning null/undefined means an edge can't be added between the two nodes
+    return 'flat';
+  },
+  loopAllowed: function loopAllowed(node) {
+    // for the specified node, return whether edges from itself to itself are allowed
+    return false;
+  },
+  nodeLoopOffset: -50, // offset for edgeType: 'node' loops
+  nodeParams: function nodeParams(sourceNode, targetNode) {
+    // for edges between the specified source and target
+    // return element object to be passed to cy.add() for intermediary node
+    return {};
+  },
+  edgeParams: function edgeParams(sourceNode, targetNode, i) {
+    // for edges between the specified source and target
+    // return element object to be passed to cy.add() for edge
+    // NB: i indicates edge index in case of edgeType: 'node'
+    return {};
+  },
+  ghostEdgeParams: function ghostEdgeParams() {
+    // return element object to be passed to cy.add() for the ghost edge
+    // (default classes are always added for you)
+    return {};
+  },
+  show: function show(sourceNode) {
+    // fired when handle is shown
+  },
+  hide: function hide(sourceNode) {
+    // fired when the handle is hidden
+  },
+  start: function start(sourceNode) {
+    // fired when edgehandles interaction starts (drag on handle)
+  },
+  complete: function complete(sourceNode, targetNode, addedEles) {
+    // fired when edgehandles is done and elements are added
+  },
+  stop: function stop(sourceNode) {
+    // fired when edgehandles interaction is stopped (either complete with added edges or incomplete)
+  },
+  cancel: function cancel(sourceNode, cancelledTargets) {
+    // fired when edgehandles are cancelled (incomplete gesture)
+  },
+  hoverover: function hoverover(sourceNode, targetNode) {
+    // fired when a target is hovered
+  },
+  hoverout: function hoverout(sourceNode, targetNode) {
+    // fired when a target isn't hovered anymore
+  },
+  previewon: function previewon(sourceNode, targetNode, previewEles) {
+    // fired when preview is shown
+  },
+  previewoff: function previewoff(sourceNode, targetNode, previewEles) {
+    // fired when preview is hidden
+  },
+  drawon: function drawon() {
+    // fired when draw mode enabled
+  },
+  drawoff: function drawoff() {
+    // fired when draw mode disabled
+  }
+};
+/* eslint-enable */
+
+module.exports = defaults;
+
+/***/ }),
+/* 5 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+function toggleDrawMode(bool) {
+  var cy = this.cy,
+      options = this.options;
+
+
+  this.drawMode = bool != null ? bool : !this.drawMode;
+
+  if (this.drawMode) {
+    this.prevUngrabifyState = cy.autoungrabify();
+
+    cy.autoungrabify(true);
+
+    if (!options.handleInDrawMode && this.handleShown()) {
+      this.hide();
+    }
+
+    this.emit('drawon');
+  } else {
+    cy.autoungrabify(this.prevUngrabifyState);
+
+    this.emit('drawoff');
+  }
+
+  return this;
+}
+
+function enableDrawMode() {
+  return this.toggleDrawMode(true);
+}
+
+function disableDrawMode() {
+  return this.toggleDrawMode(false);
+}
+
+module.exports = { toggleDrawMode: toggleDrawMode, enableDrawMode: enableDrawMode, disableDrawMode: disableDrawMode };
+
+/***/ }),
+/* 6 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
+
+var assign = __webpack_require__(0);
+var isString = function isString(x) {
+  return (typeof x === 'undefined' ? 'undefined' : _typeof(x)) === _typeof('');
+};
+var isArray = function isArray(x) {
+  return (typeof x === 'undefined' ? 'undefined' : _typeof(x)) === _typeof([]) && x.length != null;
+};
+
+function getEleJson(overrides, params, addedClasses) {
+  var json = {};
+
+  // basic values
+  assign(json, params, overrides);
+
+  // make sure params can specify data but that overrides take precedence
+  assign(json.data, params.data, overrides.data);
+
+  if (isString(params.classes)) {
+    json.classes = params.classes + ' ' + addedClasses;
+  } else if (isArray(params.classes)) {
+    json.classes = params.classes.join(' ') + ' ' + addedClasses;
+  } else {
+    json.classes = addedClasses;
+  }
+
+  return json;
+}
+
+function makeEdges() {
+  var preview = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
+  var cy = this.cy,
+      options = this.options,
+      presumptiveTargets = this.presumptiveTargets,
+      previewEles = this.previewEles,
+      active = this.active;
+
+
+  var source = this.sourceNode;
+  var target = this.targetNode;
+  var classes = preview ? 'eh-preview' : '';
+  var added = cy.collection();
+  var edgeType = options.edgeType(source, target);
+
+  // can't make edges outside of regular gesture lifecycle
+  if (!active) {
+    return;
+  }
+
+  // must have a non-empty edge type
+  if (!edgeType) {
+    return;
+  }
+
+  // can't make preview if disabled
+  if (preview && !options.preview) {
+    return;
+  }
+
+  // detect cancel
+  if (!target || target.size() === 0) {
+    previewEles.remove();
+
+    this.emit('cancel', this.mp(), source, presumptiveTargets);
+
+    return;
+  }
+
+  // just remove preview class if we already have the edges
+  if (!preview && options.preview) {
+    previewEles.removeClass('eh-preview').style('events', '');
+
+    this.emit('complete', this.mp(), source, target, previewEles);
+
+    return;
+  }
+
+  var p1 = source.position();
+  var p2 = target.position();
+
+  var p = void 0;
+  if (source.same(target)) {
+    p = {
+      x: p1.x + options.nodeLoopOffset,
+      y: p1.y + options.nodeLoopOffset
+    };
+  } else {
+    p = {
+      x: (p1.x + p2.x) / 2,
+      y: (p1.y + p2.y) / 2
+    };
+  }
+
+  if (edgeType === 'node') {
+    var interNode = cy.add(getEleJson({
+      group: 'nodes',
+      position: p
+    }, options.nodeParams(source, target), classes));
+
+    var source2inter = cy.add(getEleJson({
+      group: 'edges',
+      data: {
+        source: source.id(),
+        target: interNode.id()
+      }
+    }, options.edgeParams(source, target, 0), classes));
+
+    var inter2target = cy.add(getEleJson({
+      group: 'edges',
+      data: {
+        source: interNode.id(),
+        target: target.id()
+      }
+    }, options.edgeParams(source, target, 1), classes));
+
+    added = added.merge(interNode).merge(source2inter).merge(inter2target);
+  } else {
+    // flat
+    var source2target = cy.add(getEleJson({
+      group: 'edges',
+      data: {
+        source: source.id(),
+        target: target.id()
+      }
+    }, options.edgeParams(source, target, 0), classes));
+
+    added = added.merge(source2target);
+  }
+
+  if (preview) {
+    this.previewEles = added;
+
+    added.style('events', 'no');
+  } else {
+    added.style('events', '');
+
+    this.emit('complete', this.mp(), source, target, added);
+  }
+
+  return this;
+}
+
+function makePreview() {
+  this.makeEdges(true);
+
+  return this;
+}
+
+function previewShown() {
+  return this.previewEles.nonempty() && this.previewEles.inside();
+}
+
+function removePreview() {
+  if (this.previewShown()) {
+    this.previewEles.remove();
+  }
+
+  return this;
+}
+
+function handleShown() {
+  return this.handleNode.nonempty() && this.handleNode.inside();
+}
+
+function removeHandle() {
+  if (this.handleShown()) {
+    this.handleNode.remove();
+  }
+
+  return this;
+}
+
+function setHandleFor(node) {
+  var _this = this;
+
+  var options = this.options,
+      cy = this.cy;
+
+
+  var handlePosition = _typeof(options.handlePosition) === _typeof('') ? function () {
+    return options.handlePosition;
+  } : options.handlePosition;
+
+  var p = node.position();
+  var h = node.outerHeight();
+  var w = node.outerWidth();
+
+  // store how much we should move the handle from origin(p.x, p.y)
+  var moveX = 0;
+  var moveY = 0;
+
+  // grab axes
+  var axes = handlePosition(node).toLowerCase().split(/\s+/);
+  var axisX = axes[0];
+  var axisY = axes[1];
+
+  // based on handlePosition move left/right/top/bottom. Middle/middle will just be normal
+  if (axisX === 'left') {
+    moveX = -(w / 2);
+  } else if (axisX === 'right') {
+    moveX = w / 2;
+  }if (axisY === 'top') {
+    moveY = -(h / 2);
+  } else if (axisY === 'bottom') {
+    moveY = h / 2;
+  }
+
+  // set handle x and y based on adjusted positions
+  var hx = this.hx = p.x + moveX;
+  var hy = this.hy = p.y + moveY;
+  var pos = { x: hx, y: hy };
+
+  if (this.handleShown()) {
+    this.handleNode.position(pos);
+  } else {
+    cy.batch(function () {
+      _this.handleNode = cy.add({
+        classes: 'eh-handle',
+        position: pos,
+        grabbable: false,
+        selectable: false
+      });
+
+      _this.handleNode.style('z-index', 9007199254740991);
+    });
+  }
+
+  return this;
+}
+
+function updateEdge() {
+  var _this2 = this;
+
+  var sourceNode = this.sourceNode,
+      ghostNode = this.ghostNode,
+      cy = this.cy,
+      mx = this.mx,
+      my = this.my,
+      options = this.options;
+
+  var x = mx;
+  var y = my;
+  var ghostEdge = void 0,
+      ghostEles = void 0;
+
+  // can't draw a line without having the starting node
+  if (!sourceNode) {
+    return;
+  }
+
+  if (!ghostNode || ghostNode.length === 0 || ghostNode.removed()) {
+    ghostEles = this.ghostEles = cy.collection();
+
+    cy.batch(function () {
+      ghostNode = _this2.ghostNode = cy.add({
+        group: 'nodes',
+        classes: 'eh-ghost eh-ghost-node',
+        position: {
+          x: 0,
+          y: 0
+        }
+      });
+
+      ghostNode.style({
+        'background-color': 'blue',
+        'width': 0.0001,
+        'height': 0.0001,
+        'opacity': 0,
+        'events': 'no'
+      });
+
+      var ghostEdgeParams = options.ghostEdgeParams();
+
+      ghostEdge = cy.add(assign({}, ghostEdgeParams, {
+        group: 'edges',
+        data: assign({}, ghostEdgeParams.data, {
+          source: sourceNode.id(),
+          target: ghostNode.id()
+        }),
+        classes: 'eh-ghost eh-ghost-edge'
+      }));
+
+      ghostEdge.style({
+        'events': 'no'
+      });
+    });
+
+    ghostEles.merge(ghostNode).merge(ghostEdge);
+  }
+
+  ghostNode.position({ x: x, y: y });
+
+  return this;
+}
+
+module.exports = {
+  makeEdges: makeEdges, makePreview: makePreview, removePreview: removePreview, previewShown: previewShown,
+  updateEdge: updateEdge,
+  handleShown: handleShown, setHandleFor: setHandleFor, removeHandle: removeHandle
+};
+
+/***/ }),
+/* 7 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+function disableEdgeEvents() {
+  if (this.options.noEdgeEventsInDraw) {
+    this.cy.edges().style('events', 'no');
+  }
+
+  return this;
+}
+
+function enableEdgeEvents() {
+  if (this.options.noEdgeEventsInDraw) {
+    this.cy.edges().style('events', '');
+  }
+
+  return this;
+}
+
+module.exports = { disableEdgeEvents: disableEdgeEvents, enableEdgeEvents: enableEdgeEvents };
+
+/***/ }),
+/* 8 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+function enable() {
+  this.enabled = true;
+
+  this.emit('enable');
+
+  return this;
+}
+
+function disable() {
+  this.enabled = false;
+
+  this.emit('disable');
+
+  return this;
+}
+
+module.exports = { enable: enable, disable: disable };
+
+/***/ }),
+/* 9 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var memoize = __webpack_require__(13);
+var sqrt2 = Math.sqrt(2);
+
+function canStartOn(node) {
+  var options = this.options,
+      previewEles = this.previewEles,
+      ghostEles = this.ghostEles,
+      handleNode = this.handleNode;
+
+  var isPreview = function isPreview(el) {
+    return previewEles.anySame(el);
+  };
+  var isGhost = function isGhost(el) {
+    return ghostEles.anySame(el);
+  };
+  var userFilter = function userFilter(el) {
+    return el.filter(options.handleNodes).length > 0;
+  };
+  var isHandle = function isHandle(el) {
+    return handleNode.same(el);
+  };
+  var isTemp = function isTemp(el) {
+    return isPreview(el) || isHandle(el) || isGhost(el);
+  };
+
+  var enabled = this.enabled,
+      active = this.active,
+      grabbingNode = this.grabbingNode;
+
+
+  return enabled && !active && !grabbingNode && (node == null || !isTemp(node) && userFilter(node));
+}
+
+function canStartDrawModeOn(node) {
+  return this.canStartOn(node) && this.drawMode;
+}
+
+function canStartNonDrawModeOn(node) {
+  return this.canStartOn(node) && !this.drawMode;
+}
+
+function show(node) {
+  var options = this.options,
+      drawMode = this.drawMode;
+
+
+  if (!this.canStartOn(node) || drawMode && !options.handleInDrawMode) {
+    return;
+  }
+
+  this.sourceNode = node;
+
+  this.setHandleFor(node);
+
+  this.emit('show', this.hp(), this.sourceNode);
+
+  return this;
+}
+
+function hide() {
+  this.removeHandle();
+
+  this.emit('hide', this.hp(), this.sourceNode);
+
+  return this;
+}
+
+function start(node) {
+  if (!this.canStartOn(node)) {
+    return;
+  }
+
+  this.active = true;
+
+  this.sourceNode = node;
+  this.sourceNode.addClass('eh-source');
+
+  this.disableGestures();
+  this.disableEdgeEvents();
+
+  this.emit('start', this.hp(), node);
+}
+
+function update(pos) {
+  if (!this.active) {
+    return;
+  }
+
+  var p = pos;
+
+  this.mx = p.x;
+  this.my = p.y;
+
+  this.updateEdge();
+  this.throttledSnap();
+
+  return this;
+}
+
+function snap() {
+  if (!this.active || !this.options.snap) {
+    return false;
+  }
+
+  var cy = this.cy;
+  var tgt = this.targetNode;
+  var threshold = this.options.snapThreshold;
+  var mousePos = this.mp();
+  var handleNode = this.handleNode,
+      previewEles = this.previewEles,
+      ghostNode = this.ghostNode;
+
+
+  var radius = function radius(n) {
+    return sqrt2 * Math.max(n.outerWidth(), n.outerHeight()) / 2;
+  }; // worst-case enclosure of bb by circle
+  var sqDist = function sqDist(x1, y1, x2, y2) {
+    var dx = x2 - x1;var dy = y2 - y1;return dx * dx + dy * dy;
+  };
+  var sqDistByPt = function sqDistByPt(p1, p2) {
+    return sqDist(p1.x, p1.y, p2.x, p2.y);
+  };
+  var nodeSqDist = function nodeSqDist(n) {
+    return sqDistByPt(n.position(), mousePos);
+  };
+
+  var sqThreshold = function sqThreshold(n) {
+    var r = radius(n);var t = r + threshold;return t * t;
+  };
+  var isWithinTheshold = function isWithinTheshold(n) {
+    return nodeSqDist(n) <= sqThreshold(n);
+  };
+
+  var bbSqDist = function bbSqDist(n) {
+    var p = n.position();
+    var halfW = n.outerWidth() / 2;
+    var halfH = n.outerHeight() / 2;
+
+    // node and mouse positions, line is formed from node to mouse
+    var nx = p.x;
+    var ny = p.y;
+    var mx = mousePos.x;
+    var my = mousePos.y;
+
+    // bounding box
+    var x1 = nx - halfW;
+    var x2 = nx + halfW;
+    var y1 = ny - halfH;
+    var y2 = ny + halfH;
+
+    var insideXBounds = x1 <= mx && mx <= x2;
+    var insideYBounds = y1 <= my && my <= y2;
+
+    if (insideXBounds && insideYBounds) {
+      // inside box
+      return 0;
+    } else if (insideXBounds) {
+      // perpendicular distance to box, top or bottom
+      var dy1 = my - y1;
+      var dy2 = my - y2;
+
+      return Math.min(dy1 * dy1, dy2 * dy2);
+    } else if (insideYBounds) {
+      // perpendicular distance to box, left or right
+      var dx1 = mx - x1;
+      var dx2 = mx - x2;
+
+      return Math.min(dx1 * dx1, dx2 * dx2);
+    } else if (mx < x1 && my < y1) {
+      // top-left corner distance
+      return sqDist(mx, my, x1, y1);
+    } else if (mx > x2 && my < y1) {
+      // top-right corner distance
+      return sqDist(mx, my, x2, y1);
+    } else if (mx < x1 && my > y2) {
+      // bottom-left corner distance
+      return sqDist(mx, my, x1, y2);
+    } else {
+      // bottom-right corner distance
+      return sqDist(mx, my, x2, y2);
+    }
+  };
+
+  var cmpBbSqDist = function cmpBbSqDist(n1, n2) {
+    return bbSqDist(n1) - bbSqDist(n2);
+  };
+
+  var cmp = cmpBbSqDist;
+
+  var allowHoverDelay = false;
+
+  var mouseIsInside = function mouseIsInside(n) {
+    var mp = mousePos;
+    var w = n.outerWidth();
+    var halfW = w / 2;
+    var h = n.outerHeight();
+    var halfH = h / 2;
+    var p = n.position();
+    var x1 = p.x - halfW;
+    var x2 = p.x + halfW;
+    var y1 = p.y - halfH;
+    var y2 = p.y + halfH;
+
+    return x1 <= mp.x && mp.x <= x2 && y1 <= mp.y && mp.y <= y2;
+  };
+
+  var isEhEle = function isEhEle(n) {
+    return n.same(handleNode) || n.same(previewEles) || n.same(ghostNode);
+  };
+
+  var nodesByDist = cy.nodes(function (n) {
+    return !isEhEle(n) && isWithinTheshold(n);
+  }).sort(cmp);
+  var snapped = false;
+
+  if (tgt.nonempty() && !isWithinTheshold(tgt)) {
+    this.unpreview(tgt);
+  }
+
+  for (var i = 0; i < nodesByDist.length; i++) {
+    var n = nodesByDist[i];
+
+    // skip a parent node when the mouse is inside it
+    if (n.isParent() && mouseIsInside(n)) {
+      continue;
+    }
+
+    // skip a child node when the mouse is not inside the parent
+    if (n.isChild() && !mouseIsInside(n.parent())) {
+      continue;
+    }
+
+    if (n.same(tgt) || this.preview(n, allowHoverDelay)) {
+      snapped = true;
+      break;
+    }
+  }
+
+  return snapped;
+}
+
+function preview(target) {
+  var _this = this;
+
+  var allowHoverDelay = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
+  var options = this.options,
+      sourceNode = this.sourceNode,
+      ghostNode = this.ghostNode,
+      ghostEles = this.ghostEles,
+      presumptiveTargets = this.presumptiveTargets,
+      previewEles = this.previewEles,
+      active = this.active;
+
+  var source = sourceNode;
+  var isLoop = target.same(source);
+  var loopAllowed = options.loopAllowed(target);
+  var isGhost = target.same(ghostNode);
+  var noEdge = !options.edgeType(source, target);
+  var isHandle = target.same(this.handleNode);
+  var isExistingTgt = target.same(this.targetNode);
+
+  if (!active || isHandle || isGhost || noEdge || isExistingTgt || isLoop && !loopAllowed
+  // || (target.isParent())
+  ) {
+      return false;
+    }
+
+  if (this.targetNode.nonempty()) {
+    this.unpreview(this.targetNode);
+  }
+
+  clearTimeout(this.previewTimeout);
+
+  var applyPreview = function applyPreview() {
+    _this.targetNode = target;
+
+    presumptiveTargets.merge(target);
+
+    target.addClass('eh-presumptive-target');
+    target.addClass('eh-target');
+
+    _this.emit('hoverover', _this.mp(), source, target);
+
+    if (options.preview) {
+      target.addClass('eh-preview');
+
+      ghostEles.addClass('eh-preview-active');
+      sourceNode.addClass('eh-preview-active');
+      target.addClass('eh-preview-active');
+
+      _this.makePreview();
+
+      _this.emit('previewon', _this.mp(), source, target, previewEles);
+    }
+  };
+
+  if (allowHoverDelay && options.hoverDelay > 0) {
+    this.previewTimeout = setTimeout(applyPreview, options.hoverDelay);
+  } else {
+    applyPreview();
+  }
+
+  return true;
+}
+
+function unpreview(target) {
+  if (!this.active || target.same(this.handleNode)) {
+    return;
+  }
+
+  var previewTimeout = this.previewTimeout,
+      sourceNode = this.sourceNode,
+      previewEles = this.previewEles,
+      ghostEles = this.ghostEles,
+      cy = this.cy;
+
+  clearTimeout(previewTimeout);
+  this.previewTimeout = null;
+
+  var source = sourceNode;
+
+  target.removeClass('eh-preview eh-target eh-presumptive-target eh-preview-active');
+  ghostEles.removeClass('eh-preview-active');
+  sourceNode.removeClass('eh-preview-active');
+
+  this.targetNode = cy.collection();
+
+  this.removePreview(source, target);
+
+  this.emit('hoverout', this.mp(), source, target);
+  this.emit('previewoff', this.mp(), source, target, previewEles);
+
+  return this;
+}
+
+function stop() {
+  if (!this.active) {
+    return;
+  }
+
+  var sourceNode = this.sourceNode,
+      targetNode = this.targetNode,
+      ghostEles = this.ghostEles,
+      presumptiveTargets = this.presumptiveTargets;
+
+
+  clearTimeout(this.previewTimeout);
+
+  sourceNode.removeClass('eh-source');
+  targetNode.removeClass('eh-target eh-preview eh-hover');
+  presumptiveTargets.removeClass('eh-presumptive-target');
+
+  this.makeEdges();
+
+  this.removeHandle();
+
+  ghostEles.remove();
+
+  this.clearCollections();
+
+  this.resetGestures();
+  this.enableEdgeEvents();
+
+  this.active = false;
+
+  this.emit('stop', this.mp(), sourceNode);
+
+  return this;
+}
+
+module.exports = {
+  show: show, hide: hide, start: start, update: update, preview: preview, unpreview: unpreview, stop: stop, snap: snap,
+  canStartOn: canStartOn, canStartDrawModeOn: canStartDrawModeOn, canStartNonDrawModeOn: canStartNonDrawModeOn
+};
+
+/***/ }),
+/* 10 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var defaults = __webpack_require__(4);
+var assign = __webpack_require__(0);
+var throttle = __webpack_require__(14);
+
+var cyGesturesToggle = __webpack_require__(2);
+var cyListeners = __webpack_require__(3);
+var drawMode = __webpack_require__(5);
+var drawing = __webpack_require__(6);
+var enabling = __webpack_require__(8);
+var gestureLifecycle = __webpack_require__(9);
+var listeners = __webpack_require__(11);
+var edgeEvents = __webpack_require__(7);
+
+function Edgehandles(options) {
+  var cy = options.cy;
+
+  this.cy = cy;
+  this.listeners = [];
+
+  // edgehandles gesture state
+  this.enabled = true;
+  this.drawMode = false;
+  this.active = false;
+  this.grabbingNode = false;
+
+  // edgehandles elements
+  this.handleNode = cy.collection();
+  this.clearCollections();
+
+  // handle
+  this.hx = 0;
+  this.hy = 0;
+  this.hr = 0;
+
+  // mouse position
+  this.mx = 0;
+  this.my = 0;
+
+  this.options = assign({}, defaults, options);
+
+  this.saveGestureState();
+  this.addListeners();
+
+  this.throttledSnap = throttle(this.snap.bind(this), 1000 / options.snapFrequency);
+
+  this.preventDefault = function (e) {
+    return e.preventDefault();
+  };
+
+  var supportsPassive = false;
+  try {
+    var opts = Object.defineProperty({}, 'passive', {
+      get: function get() {
+        supportsPassive = true;
+      }
+    });
+
+    window.addEventListener('test', null, opts);
+  } catch (err) {}
+
+  if (supportsPassive) {
+    this.windowListenerOptions = { capture: true, passive: false };
+  } else {
+    this.windowListenerOptions = true;
+  }
+}
+
+var proto = Edgehandles.prototype = {};
+var extend = function extend(obj) {
+  return assign(proto, obj);
+};
+
+proto.destroy = function () {
+  this.removeListeners();
+};
+
+proto.setOptions = function (options) {
+  assign(this.options, options);
+};
+
+proto.mp = function () {
+  return { x: this.mx, y: this.my };
+};
+
+proto.hp = function () {
+  return { x: this.hx, y: this.hy };
+};
+
+proto.clearCollections = function () {
+  var cy = this.cy;
+
+
+  this.previewEles = cy.collection();
+  this.ghostEles = cy.collection();
+  this.ghostNode = cy.collection();
+  this.sourceNode = cy.collection();
+  this.targetNode = cy.collection();
+  this.presumptiveTargets = cy.collection();
+};
+
+[cyGesturesToggle, cyListeners, drawMode, drawing, enabling, gestureLifecycle, listeners, edgeEvents].forEach(extend);
+
+module.exports = Edgehandles;
+
+/***/ }),
+/* 11 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
+
+function addListeners() {
+  var _this = this;
+
+  this.addCytoscapeListeners();
+
+  this.addListener(this.cy, 'destroy', function () {
+    return _this.destroy();
+  });
+
+  return this;
+}
+
+function removeListeners() {
+  for (var i = this.listeners.length - 1; i >= 0; i--) {
+    var l = this.listeners[i];
+
+    this.removeListener(l.target, l.event, l.selector, l.callback, l.options);
+  }
+
+  return this;
+}
+
+function getListener(target, event, selector, callback, options) {
+  if ((typeof selector === 'undefined' ? 'undefined' : _typeof(selector)) !== _typeof('')) {
+    callback = selector;
+    options = callback;
+    selector = null;
+  }
+
+  if (options == null) {
+    options = false;
+  }
+
+  return { target: target, event: event, selector: selector, callback: callback, options: options };
+}
+
+function isDom(target) {
+  return target instanceof Element;
+}
+
+function addListener(target, event, selector, callback, options) {
+  var l = getListener(target, event, selector, callback, options);
+
+  this.listeners.push(l);
+
+  if (isDom(l.target)) {
+    l.target.addEventListener(l.event, l.callback, l.options);
+  } else {
+    if (l.selector) {
+      l.target.addListener(l.event, l.selector, l.callback, l.options);
+    } else {
+      l.target.addListener(l.event, l.callback, l.options);
+    }
+  }
+
+  return this;
+}
+
+function removeListener(target, event, selector, callback, options) {
+  var l = getListener(target, event, selector, callback, options);
+
+  for (var i = this.listeners.length - 1; i >= 0; i--) {
+    var l2 = this.listeners[i];
+
+    if (l.target === l2.target && l.event === l2.event && (l.selector == null || l.selector === l2.selector) && (l.callback == null || l.callback === l2.callback)) {
+      this.listeners.splice(i, 1);
+
+      if (isDom(l.target)) {
+        l.target.removeEventListener(l.event, l.callback, l.options);
+      } else {
+        if (l.selector) {
+          l.target.removeListener(l.event, l.selector, l.callback, l.options);
+        } else {
+          l.target.removeListener(l.event, l.callback, l.options);
+        }
+      }
+
+      break;
+    }
+  }
+
+  return this;
+}
+
+function emit(type, position) {
+  var options = this.options,
+      cy = this.cy;
+
+  for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
+    args[_key - 2] = arguments[_key];
+  }
+
+  cy.emit({ type: 'eh' + type, position: position }, args);
+
+  var handler = options[type];
+
+  if (handler != null) {
+    handler.apply(undefined, args);
+  }
+
+  return this;
+}
+
+module.exports = { addListener: addListener, addListeners: addListeners, removeListener: removeListener, removeListeners: removeListeners, emit: emit };
+
+/***/ }),
+/* 12 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var impl = __webpack_require__(1);
+
+// registers the extension on a cytoscape lib ref
+var register = function register(cytoscape) {
+  if (!cytoscape) {
+    return;
+  } // can't register if cytoscape unspecified
+
+  cytoscape('core', 'edgehandles', impl); // register with cytoscape.js
+};
+
+if (typeof cytoscape !== 'undefined') {
+  // expose to global cytoscape (i.e. window.cytoscape)
+  register(cytoscape); // eslint-disable-line no-undef
+}
+
+module.exports = register;
+
+/***/ }),
+/* 13 */
+/***/ (function(module, exports) {
+
+module.exports = __WEBPACK_EXTERNAL_MODULE_13__;
+
+/***/ }),
+/* 14 */
+/***/ (function(module, exports) {
+
+module.exports = __WEBPACK_EXTERNAL_MODULE_14__;
+
+/***/ })
+/******/ ]);
+});
\ No newline at end of file
diff --git a/01_Code/physical_computing_interface/threejs/grid.js b/01_Code/physical_computing_interface/threejs/grid.js
index 055fd44dfad3e0b07b3e9a01fc9a0b95bfaffbb0..8c7bd1144a9583624f28100d788520fc766b322f 100644
--- a/01_Code/physical_computing_interface/threejs/grid.js
+++ b/01_Code/physical_computing_interface/threejs/grid.js
@@ -468,6 +468,7 @@ function onDocumentMouseDownThree( event ) {
             var obj=utils.getXYZfromName(intersect.object.name);
             obj=utils.getXYZfromName(three.rollOverMesh.name);
             GLOBALS.addNode (obj.x, obj.y,obj.z);
+            GLOBALS.selectNode (obj.x, obj.y,obj.z);
         }
         if ( intersects1.length > 0 && event.which==3){//right click
                 var intersect = intersects1[ 0 ];