title: Apply onmouseover on each node - GOjs library - swimlane
body: <p>I've done the <a href="https://stackoverflow.com/questions/22676411/add-css-or-style-to-each-node-in-swimlane-gojs-library">first part</a>, adding colors on each node. The next step would be how to add onmouseover on each node then highlight/apply style on the subgraph (including the links/lines connecting it) belonging to the hovered node.</p>
  
  <p>Anyone knows?</p>
  
  <p>This is my code so far:</p>
  
  <pre><code>        &lt;?php
      $prospectus = array(
      'subjects' =&gt; array(
          'First Year' =&gt; array(
              array('course_no' =&gt; 'CS 110'       , 'color' =&gt; '#21ff1c'),
              array('course_no' =&gt; 'CS 111'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'MATH 1'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'MATH 2'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'ENGL 1'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'REED 1'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'PE 1'         , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'CWTS/ROTC 11' , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'GUIDANCE 1'   , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'CS 120'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'CS 121'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'CS 122'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'MATH 4'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'ENGL 2'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'REED 2'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'PE 2'         , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'CWTS/ROTC 12' , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'GUIDANCE 2'   , 'color' =&gt; 'white')
          ),
          'Second Year' =&gt; array(
              array('course_no' =&gt; 'CS 211'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'CS 212'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'CS 213'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'ENGL 3'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'MATH 6'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'REED 3'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'PE 3'         , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'CS 221'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'CS 222'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'CS 223'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'CS 224'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'ENGL 4'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'REED 4'       , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'PE 4'         , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'HUMANITIES 1' , 'color' =&gt; 'white'),
              array('course_no' =&gt; 'MATH 7'       , 'color' =&gt; 'white')
          )
      ),
      'preqs'    =&gt; array(
          array('subject' =&gt; 'CS 120'      , 'preq' =&gt; 'CS 110'),
          array('subject' =&gt; 'CS 121'      , 'preq' =&gt; 'CS 111'),
          array('subject' =&gt; 'CS 122'      , 'preq' =&gt; 'MATH 1'),
          array('subject' =&gt; 'MATH 4'      , 'preq' =&gt; 'MATH 1'),
          array('subject' =&gt; 'MATH 4'      , 'preq' =&gt; 'MATH 2'),
          array('subject' =&gt; 'ENGL 2'      , 'preq' =&gt; 'ENGL 1'),
          array('subject' =&gt; 'REED 2'      , 'preq' =&gt; 'REED 1'),
          array('subject' =&gt; 'PE 2'        , 'preq' =&gt; 'PE 1'  ),
          array('subject' =&gt; 'CWTS/ROTC 12', 'preq' =&gt; 'CWTS/ROTC 11'),
          array('subject' =&gt; 'GUIDANCE 2'  , 'preq' =&gt; 'GUIDANCE 1'),
  
          array('subject' =&gt; 'CS 211'        , 'preq' =&gt; 'CS 121'),
          array('subject' =&gt; 'CS 212'        , 'preq' =&gt; 'CS 110'),
          array('subject' =&gt; 'CS 213'        , 'preq' =&gt; 'CS 122'),
          array('subject' =&gt; 'ENGL 3'        , 'preq' =&gt; 'ENGL 2'),
          array('subject' =&gt; 'MATH 6'        , 'preq' =&gt; 'MATH 4'),
          array('subject' =&gt; 'REED 3'        , 'preq' =&gt; 'REED 2'),
          array('subject' =&gt; 'PE 3'          , 'preq' =&gt; 'PE 2'),
  
          array('subject' =&gt; 'CS 221'        , 'preq' =&gt; 'CS 122'),
          array('subject' =&gt; 'CS 221'        , 'preq' =&gt; 'CS 211'),
          array('subject' =&gt; 'CS 222'        , 'preq' =&gt; 'CS 211'),
          array('subject' =&gt; 'CS 223'        , 'preq' =&gt; 'CS 211'),
          array('subject' =&gt; 'CS 224'        , 'preq' =&gt; 'CS 121'),
          array('subject' =&gt; 'ENGL 4'        , 'preq' =&gt; 'ENGL 2'),
          array('subject' =&gt; 'REED 4'        , 'preq' =&gt; 'REED 3'),
          array('subject' =&gt; 'PE 4'          , 'preq' =&gt; 'PE 3'),
      )
  );
  ?&gt;
  
  &lt;!DOCTYPE html&gt;
  &lt;html&gt;
  &lt;head&gt;
  &lt;title&gt;Swimlane&lt;/title&gt;
  &lt;!-- Copyright 1998-2014 by Northwoods Software Corporation. --&gt;
  &lt;link href="goSamples.css" rel="stylesheet" type="text/css"/&gt;
  &lt;script type="text/javascript" src="go.js"&gt;&lt;/script&gt;
  &lt;script id="code"&gt;
  
  function highlightNode(node, color) {
      if (node === null) return;
      var shape = node.findMainElement();
      if (shape === null) return;
      if (color !== undefined) {
          if (!shape.previousStroke) shape.previousStroke = shape.stroke;
          shape.stroke = color;
      } else {  // restore previous color
          shape.stroke = shape.previousStroke;
      }
  }
  
  function highlightLink(link, color) {
      if (link === null) return;
      var shape = link.findMainElement();
      if (shape === null) return;
      if (color !== undefined) {
          if (!shape.previousStroke) shape.previousStroke = shape.stroke;
          shape.stroke = color;
      } else {  // restore previous color
          shape.stroke = shape.previousStroke;
      }
  }
  
  function highlightConnectedNodes(node, color) {
      if (node === null) return;
  
      var lit = node.findLinksOutOf();
  
      while (lit.next()) {
          highlightLink(lit.value, color);
      }
  
      var nit = node.findNodesOutOf();
  
      while (nit.next()) {
          highlightNode(nit.value, color);
          highlightConnectedNodes(nit.value, color);
      }
  }
  
  // These parameters need to be set before defining the templates.
  // this controls whether the swimlanes are horizontal stacked vertically, or the other way:
  var HORIZONTAL = false;
  // this controls the minimum length of any swimlane
  var MINLENGTH = 500;
  // this controls the minimum breadth of any swimlane
  var MINBREADTH = 360;
  
  
  // compute the minimum length needed to hold all of the subgraphs
  function computeMinPlaceholderSize(diagram) {
  
      var len = MINLENGTH;
  
      for (var it = diagram.nodes; it.next(); ) {
          var group = it.value;
  
          if (!(group instanceof go.Group))
              continue;
  
          var holder = group.placeholder;
  
          if (holder !== null) {
              var sz = holder.actualBounds;
              len = Math.max(len, (HORIZONTAL ? sz.width : sz.height));
          }
      }
  
      return (HORIZONTAL ? new go.Size(len, NaN) : new go.Size(NaN, len));
  }
  
  // get the minimum placeholder size for a particular Group;
  // when group is null, return the minimum size
  function computePlaceholderSize(group) {
  
      if (group instanceof go.Group) {
          var holder = group.placeholder;
  
          if (holder !== null) {
              return holder.actualBounds.size;
          }
      }
  
      return (HORIZONTAL ? new go.Size(MINLENGTH, MINBREADTH) : new go.Size(MINBREADTH, MINLENGTH));
  }
  
  // define a custom grid layout that makes sure the length of each lane is the same
  // and that each lane is broad enough to hold its subgraph
  function StackLayout() {
      go.GridLayout.call(this);
  }
  go.Diagram.inherit(StackLayout, go.GridLayout);
  
  StackLayout.prototype.doLayout = function(coll) {
      var diagram = this.diagram;
  
      if (diagram === null)
          return;
  
      diagram.startTransaction("StackLayout");
  
      // make sure all of the Group Shapes are big enough
      var minsize = computeMinPlaceholderSize(diagram);
  
      for (var it = diagram.nodes; it.next(); ) {
          var group = it.value;
  
          if (!(group instanceof go.Group))
              continue;
  
          var shape = group.findObject("SHAPE");
  
          if (shape !== null) {  // change the desiredSize to be big enough in both directions
              var sz = computePlaceholderSize(group);
  
              if (HORIZONTAL) {
                  shape.width = (isNaN(shape.width) ? minsize.width : Math.max(shape.width, minsize.width));
  
                  if (!isNaN(shape.height))
                      shape.height = Math.max(shape.height, sz.height);
              } else {
                  if (!isNaN(shape.width))
                      shape.width = Math.max(shape.width, sz.width);
                  shape.height = (isNaN(shape.height) ? minsize.height : Math.max(shape.height, minsize.height));
              }
  
              var cell = group.resizeCellSize;
  
              if (!isNaN(shape.width) &amp;&amp; !isNaN(cell.width) &amp;&amp; cell.width &gt; 0)
                  shape.width = Math.ceil(shape.width / cell.width) * cell.width;
              if (!isNaN(shape.height) &amp;&amp; !isNaN(cell.height) &amp;&amp; cell.height &gt; 0)
                  shape.height = Math.ceil(shape.height / cell.height) * cell.height;
          }
      }
  
      // now do all of the usual stuff, according to whatever properties have been set on this GridLayout
      go.GridLayout.prototype.doLayout.call(this, coll);
      diagram.commitTransaction("StackLayout");
  };
  // end StackLayout class
  
  function init() {
  
      var $ = go.GraphObject.make;
      myDiagram =
          $(go.Diagram, "myDiagram",
              {
                  initialContentAlignment: go.Spot.Center,
                  layout:
                      $(StackLayout,
                          {
                              cellSize: new go.Size(1, 1),
                              spacing: new go.Size(0, 0),
                              wrappingColumn: (HORIZONTAL ? 1 : Infinity),
                              wrappingWidth: Infinity,
                              isViewportSized: false
                          })
              });
  
      myDiagram.nodeTemplate =
          $(go.Node, "Auto",
              $(go.Shape, "Rectangle",
                  { fill: "lightblue", portId: "", cursor: "pointer", fromLinkable: true, toLinkable: true },
                  new go.Binding("fill", "color")),
              $(go.TextBlock, { margin: 5 },
                  new go.Binding("text", "key")),
              // limit dragging of Nodes to stay within the containing Group, defined above
              {
                  mouseEnter: function(e, obj, prev) {
                      var shape = obj.findMainElement();
                      if (shape){
                          shape.previousFill = shape.fill || "lightblue";
                          shape.fill = "white";
                      }
                      highlightConnectedNodes(obj, "red");
                  },
                  mouseLeave: function(e, obj, next) {
                      var shape = obj.findMainElement();
                      var original_color = obj.wl.color;
  
                      if (shape) shape.fill = shape.previousFill;
  
                      highlightConnectedNodes(obj);
                  }
  
              }
          );
  
      // each Group is a "swimlane" with a header on the left and a resizable lane on the right
      myDiagram.groupTemplate =
          $(go.Group, HORIZONTAL ? "Horizontal" : "Vertical",
              {
                  movable: false, copyable: false, deletable: false,  // can't move or copy or delete lanes
                  avoidable: false,
                  selectionObjectName: "SHAPE",  // selecting a lane causes the body of the lane to be highlit, not the label
                  resizable: true, resizeObjectName: "SHAPE",  // allow lanes to be resized, but the custom resizeAdornmentTemplate only permits one kind of resizing
                  layout: $(go.LayeredDigraphLayout,  // automatically lay out the lane's subgraph
                      { direction: HORIZONTAL ? 90 : 0, columnSpacing: 10, layeringOption: go.LayeredDigraphLayout.LayerLongestPathSource }),
                  computesBoundsAfterDrag: false,  // needed to prevent recomputing Group.placeholder bounds too soon
                  computesBoundsIncludingLinks: false,
                  computesBoundsIncludingLocation: true
              },
  
              // the lane header consisting of a Shape and a TextBlock
              $(go.Panel, "Horizontal",
                  {
                      angle: HORIZONTAL ? 270 : 0,  // maybe rotate the header to read sideways going up
                      alignment: go.Spot.Center
                  },
  
                  $(go.Shape, "Diamond",
                      { width: 0, height: 0 },
                      new go.Binding("fill", "color")),
  
                  $(go.TextBlock,  // the lane label
                      { font: "bold 14pt Century Gothic" },
                      new go.Binding("text", "key"))
              ),
              // end Horizontal Panel
  
              $(go.Panel, "Auto",  // the lane consisting of a background Shape and a Placeholder representing the subgraph
                  $(go.Shape, "Rectangle",
                      { name: "SHAPE", fill: "white", minSize: computePlaceholderSize(null) },
                      new go.Binding("fill", "color")),
                  $(go.Placeholder,
                      { padding: 10, alignment: go.Spot.TopLeft })
              )
              // end Auto Panel
          );
      // end Group
  
      // define a custom resize adornment that only has a single resize handle
      myDiagram.groupTemplate.resizeAdornmentTemplate =
          $(go.Adornment, "Spot",
              $(go.Placeholder),
              $(go.Shape,  // for changing the length of a lane
                  {
                      alignment: HORIZONTAL ? go.Spot.Right: go.Spot.Bottom,
                      desiredSize: HORIZONTAL ? new go.Size(7, 50) : new go.Size(50, 7),
                      fill: "lightblue", stroke: "dodgerblue",
                      cursor: HORIZONTAL ? "col-resize" : "row-resize"
                  }),
              $(go.Shape,  // for changing the breadth of a lane
                  {
                      alignment: HORIZONTAL ? go.Spot.Bottom : go.Spot.Right,
                      desiredSize: HORIZONTAL ? new go.Size(50, 7) : new go.Size(7, 50),
                      fill: "lightblue", stroke: "dodgerblue",
                      cursor: HORIZONTAL ? "row-resize" : "col-resize"
                  })
          );
  
      myDiagram.linkTemplate =
          $(go.Link,
              { routing: go.Link.AvoidsNodes, corner: 5 },
              { relinkableFrom: true, relinkableTo: true },
              $(go.Shape),
              $(go.Shape, { toArrow: "Standard" }),
              {
                  layoutConditions: go.Part.LayoutAdded
              }
          );
  
      // define some sample graphs in some of the lanes
      myDiagram.model = new go.GraphLinksModel(
          [ // node data
              { key: "First Year"   , isGroup: true, color: "#84ff9e" },
              { key: "Second Year"  , isGroup: true, color: "#dafb69" },
              { key: "Third Year"   , isGroup: true, color: "#fd5c91" },
              { key: "Fourth Year"  , isGroup: true, color: "#6f5cfd" },
  
              &lt;?php
                  if(isset($prospectus['subjects'])){
                      if(!empty($prospectus['subjects'])){
                          foreach($prospectus['subjects'] as $year =&gt; $subjects){
                              foreach($subjects as $subject){
                                  echo '{key: "' . $subject['course_no'] . '", group: "' . $year . '", color: "' . $subject['color'] . '"},' ;
                              }
                          }
                      }
                  }
              ?&gt;
          ],
          [ // link data
              &lt;?php
                  if(isset($prospectus['preqs'])){
                      if(!empty($prospectus['preqs'])){
                          foreach($prospectus['preqs'] as $preq){
                              echo "{from: '" . $preq['preq'] . "', to: '" . $preq['subject'] . "'}," ;
                          }
                      }
                  }
              ?&gt;
          ]);
  
      myDiagram.isReadOnly = true;
  }
  &lt;/script&gt;
  &lt;/head&gt;
  &lt;body onload="init()" style="font-family: Century Gothic"&gt;
  &lt;div id="sample" style="margin-left: 0px"&gt;
      &lt;div id="myDiagram" style="border: solid 1px blue; width:100%; height:750px;"&gt;
      &lt;/div&gt;
  &lt;/div&gt;
  &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>