use linkchain class to get linkchains
authorgithub138 <m.ehinger@ltur.de>
Wed, 13 Jan 2016 09:23:38 +0000 (10:23 +0100)
committergithub138 <m.ehinger@ltur.de>
Wed, 13 Jan 2016 09:23:38 +0000 (10:23 +0100)
add cytoscape map

LinkManagement/plugins/linkmgmt.php

index f12bf2c..59d243f 100644 (file)
@@ -1,4 +1,8 @@
 <?php
+// TODO linkchain cytoscape create libs?
+//     linkchain all objects graph cytoscape takes ages
+//     avoid create own linkchain for every port on global map
+//     highlight port cytoscape maps
 /*
  * Link Management for RT >= 0.20.9
  *
  *             7. To get a Graphviz Map of a single port click the port name on the left
  *
  *
+ * Changes:
+ *     switch to new linkchain class
+ *             - nicer looking graphviz maps
+ *             - better multilink support
+ *
+ *     add cytoscape.js maps
+ *             - path higlighting
+ *             - zooming
+ *
  * Requirements:
  *     PHP 5 (http://php.net/)
  *     GraphViz_Image 1.3.0 or newer (http://pear.php.net/package/Image_GraphViz)
  *             GraphViz (http://www.graphviz.org/)
  *
+ *     to user cytoscape js map the following is required:
+ *
+ *     Cytoscape.js (http://js.cytoscape.org/)
+ *             Racktables wwwroot/js directory:
+ *             https://cytoscape.github.io/cytoscape.js/api/cytoscape.js-latest/cytoscape.min.js
+ *     dagre.js
+ *             Racktables wwwroot/js directory:
+ *             https://raw.githubusercontent.com/cpettitt/dagre/master/dist/dagre.min.js
+ *             https://raw.githubusercontent.com/cytoscape/cytoscape.js-dagre/master/cytoscape-dagre.js
+ *     qtip
+ *             Racktables wwwroot/css directory:
+ *             https://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.1/jquery.qtip.min.css
+ *
+ *             Racktables wwwroot/js directory:
+ *             https://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.1/jquery.qtip.min.js
+ *             https://raw.githubusercontent.com/cytoscape/cytoscape.js-qtip/master/cytoscape-qtip.js
+ *
+ *     jquery >= 1.10.0 (qtip requirement)
+ *             Racktables wwwroot/js directory:
+ *             https://code.jquery.com/jquery-1.11.3.min.js
+ *
  * INSTALL:
  *
  *     1. create LinkBackend Table in your RackTables database
@@ -86,7 +120,7 @@ ALTER TABLE LinkBackend CONVERT to CHARACTER SET utf8 COLLATE utf8_unicode_ci;
 /**
  * The newest version of this plugin can be found at:
  *
- * https://github.com/github138/myRT-contribs/tree/develop-0.20.9
+ * https://github.com/github138/myRT-contribs/tree/develop-0.20.x
  *
  */
 
@@ -96,12 +130,6 @@ ALTER TABLE LinkBackend CONVERT to CHARACTER SET utf8 COLLATE utf8_unicode_ci;
  * - code cleanups
  * - bug fixing
  *
- * - fix loopdectect for multiport
- *     MAX_LOOP_COUNT
- *     loop highlight gv map
- *
- * - fix column alignment with multilinks
- *
  * - put selected object/port top left of graph
  * - multlink count for Graphviz maps empty or full dot
  *
@@ -124,6 +152,8 @@ $ophandler['object']['linkmgmt']['Help'] = 'linkmgmt_opHelp';
 $ophandler['object']['linkmgmt']['map'] = 'linkmgmt_opmap';
 $ajaxhandler['lm_mapinfo'] = 'linkmgmt_ajax_mapinfo';
 
+$ophandler['object']['linkmgmt']['cytoscapemap'] = 'linkmgmt_cytoscapemap';
+
 /* ------------------------------------------------- */
 
 define('LM_MULTILINK',TRUE);
@@ -151,730 +181,2535 @@ $lm_cache = array(
 //     return 'std';
 //} /* linkmgmt_tabtrigger */
 
-/* -------------------------------------------------- */
+/* -------------------linkchain stuff------------------------------- */
+class linkchain_cache
+{
+       public $cache = array();
 
-function linkmgmt_opHelp() {
-?>
-       <table cellspacing=10><tr><th>Help</th><tr>
-               <tr><td width=150></td><td width=150 style="font-weight:bold;color:<?php echo portlist::CURRENT_OBJECT_BGCOLOR; ?>">Current Object</td></tr>
-               <tr><td></td><td bgcolor=<?php echo portlist::CURRENT_PORT_BGCOLOR; ?>>[current port]</td></tr>
-               <tr><td>front link</td><td>[port]<(Object)</td><td>back link</td></tr>
-               <tr><td>back link</td><td>(Object)>[port]</td><td>front link</td></tr>
-               <tr><td></td><td><pre>----></pre></td><td>Front link</td></tr>
-               <tr><td></td><td><pre>====></pre></td><td>Backend link</td></tr>
-               <tr><td></td><td>Link Symbol</td><td>Create new link</td></tr>
-               <tr><td></td><td>Cut Symbol</td><td>Delete link</td></tr>
+       function getobject($object_id , &$rack = null)
+       {
+               if(!$object_id)
+                       return null;
 
-       </table>
+               if(!isset($this->cache['o'.$object_id]))
+               {
+                       $object = spotEntity('object', $object_id);
+                       $object['IPV4OBJ'] = considerConfiguredConstraint ($object, 'IPV4OBJ_LISTSRC');
 
-<?php
-       exit;
-} /* opHelp */
+                       /* get more object info */
 
-/* -------------------------------------------------- */
+                       if($object['IPV4OBJ'])
+                       {
+                               // ip addresses
+                               //amplifyCell($object); /* get ports, ipv4, ipv6, nat4 and files */
+                               $object['ipv4'] = getObjectIPv4Allocations ($object_id);
+                               $object['portip'] = array();
+                               foreach($object['ipv4'] as $ipv4)
+                               {
+                                       $object['portip'][$ipv4['osif']] = $ipv4['addrinfo']['ip'];
+                               }
+                       }
 
-function linkmgmt_ajax_mapinfo() {
+                       if($object['container_id'])
+                       {
+                               $container = $this->getobject($object['container_id']);
+                       }
 
-       $object_id = NULL;
-       $port_id = NULL;
-       $remote_id = NULL;
-       $linktype = NULL;
+                       $this->cache['o'.$object_id] = $object;
+               }
+               else
+                       $object = $this->cache['o'.$object_id];
 
-       if(isset($_REQUEST['object_id']))
-               $object_id = $_REQUEST['object_id'];
+               $rack = $this->getrack($object['rack_id']);
 
-       if(isset($_REQUEST['port_id']))
-               $port_id = $_REQUEST['port_id'];
+               return $object;
+       }
 
-       if(isset($_REQUEST['remote_id']))
-               $remote_id = $_REQUEST['remote_id'];
+       function getrack($rack_id)
+       {
+               // rack
+               if($rack_id)
+               {
+                       if(!isset($this->cache['r'.$rack_id]))
+                       {
+                               $rack = spotEntity('rack', $rack_id);
+                               $this->cache['r'.$rack_id] = $rack;
+                       }
+                       else
+                               $rack = $this->cache['r'.$rack_id];
+               }
+               else
+                       $rack = null;
 
-       if(isset($_REQUEST['linktype']))
-               $linktype = $_REQUEST['linktype'];
+               return $rack;
 
-       $debug = NULL;
-       if(isset($_REQUEST['debug']))
-               $debug['value'] = $_REQUEST['debug'];
+       }
+}
 
-       $info = array();
+$lc_cache = new linkchain_cache();
 
-       echo "<table style=\"font-size:12;\"><tr>";
+/* recursive class */
+class lm_linkchain implements Iterator {
 
-       if($port_id != NULL)
+       const B2B_LINK_BGCOLOR = '#d8d8d8';
+       const CURRENT_PORT_BGCOLOR = '#ffff99';
+       const CURRENT_OBJECT_BGCOLOR = '#ff0000';
+       const HL_PORT_BGCOLOR = '#A0FFA0';
+       const ALTERNATE_ROW_BGCOLOR = '#f0f0f0';
+
+       public $first = null;
+       public $last = null;
+       public $init = null;
+       public $linked = null;
+       public $linkcount = 0;
+
+       public $loop = false;
+       private $lastipobjport = null;
+
+       public $ports = array();
+
+       private $currentid = null;
+       private $back = null;
+       public $initback = null;
+
+       //public $cache = null;
+       private $object_id = null;
+
+       private $icount = 0;
+       public $exceed = false;
+
+       public $initalign = true;
+
+       public $portmulti = 0;
+       public $prevportmulti = 0;
+
+       private $initport = false;
+
+       private $lids = array(); // link ids porta portb linktype
+
+       public $pids = null; // port ids
+       public $oids = null; // object ids
+
+       /* $back = null follow front and back
+        *              true follow back only
+        *              false follow front only
+        * $prevport first port
+        */
+       function __construct($port_id, $back = null, $prevport = null, $reverse = false, &$pids = null, &$oids = null)
        {
-               $port = new linkmgmt_RTport($port_id);
+               global $lc_cache;
 
-               echo "<td>";
-               $port->printtable('both');
-               echo "</td>";
+               $this->init = $port_id;
 
-               if($debug)
-                       $debug['port'] = &$port;
+               $this->initback = $back;
 
-               if($remote_id != NULL)
+               if($pids === null)
+                       $this->pids = array();
+               else
+                       $this->pids = &$pids;
+
+               if($oids === null)
+                       $this->oids = array();
+               else
+                       $this->oids = &$oids;
+
+               if($back !== null)
                {
+                       $prevport_id = $prevport['id'];
+                       $prevlinktype =  $this->getlinktype(!$back);
+                       //if($this->setlinkid($port_id, $prevport_id, $prevlinktype))
+                       {
+                               $this->last = $this->_getlinks($port_id, $back, null, $reverse);
+                       }
+                       //else
+                       {
+                       //      $this->loop = true;
+                       //      echo "SUBCHAIN LID exists $port_id $prevlinktype $prevport_id<br>";
+                       }
 
-                       $remote_port = new linkmgmt_RTport($remote_id);
+                       // TODO set previous port ..use _set... function()
+                       $this->ports[$port_id][$this->getlinktype(!$back)]['portcount'] = 1;
+                       $this->ports[$port_id][$this->getlinktype(!$back)]['linked'] = 1;
+                       $this->ports[$port_id][$this->getlinktype(!$back)]['remote_id'] = $prevport['id'];
+                       $this->ports[$port_id][$this->getlinktype(!$back)]['remote_object_id'] = $prevport['object_id'];
+                       $this->ports[$port_id][$this->getlinktype(!$back)]['remote_name'] = $prevport['name'];
+                       $this->ports[$port_id][$this->getlinktype(!$back)]['remote_object_name'] = $prevport['object_name'];
+                       $this->ports[$port_id][$this->getlinktype(!$back)]['cableid'] = $prevport['cableid'];
 
-                       echo "<td><table align=\"center\">";
 
-                       // TODO cableid
-                       echo "<tr><td><pre>".($linktype == 'back' ? ' ===> ' : ' ---> ')."</pre></td></tr>";
+                       $prevport = $this->_setportlink($prevport, $this->getlinktype(!$back));
+                       $prevport[$this->getlinktype($back)]['portcount'] = null; 
+                       $prevport[$this->getlinktype($back)]['remote_id'] = null; 
+                       $prevport[$this->getlinktype($back)]['remote_object_id'] = null; 
 
-                       $port->printunlinktr($linktype, $remote_port);
+                       $this->ports[$prevport_id] = $prevport;
+                       $this->first = $prevport_id;
+                       $this->linkcount++;
 
-                       echo "</table></td>";
+               //      self::var_dump_html($this->ports[$port_id], "PORT");
+               //      self::var_dump_html($prevport, "PREVPORT");
+                       $this->object_id = $_GET['object_id'];
+               }
+               else
+               {
+                       // Link
+                       $this->last = $this->_getlinks($port_id, false);
 
+                       if(!$this->loop)
+                               $this->first = $this->_getlinks($port_id, true, null, true);
 
-                       echo "<td>";
-                       $remote_port->printtable('both');
-                       echo "</td>";
+                       $this->object_id = $this->ports[$this->init]['object_id'];
+               }
 
-                       if($debug)
-                               $debug['remote_port'] = &$remote_port;
+               if(0)
+               if($this->loop)
+               {
+                       $linktype = $this->getlinktype($back);
+                       $prevlinktype = $this->getlinktype(!$back);
+
+                       /* set first object */
+                       $object_id = $this->ports[$port_id]['object_id'];
+                       $object = $lc_cache->getobject($object_id);
 
+                       if($object['IPV4OBJ'])
+                               $this->lastipobjport = $port_id;
+
+                       $this->first = $this->lastipobjport;
                }
-               else
-                       $port->printunlinktr();
 
+               $this->linked = ($this->linkcount > 0);
+
+               if($reverse)
+                       $this->reverse();
 
        }
-       echo "</tr><tr>";
 
-       echo "<td>";
-       $object = linkmgmt_RTport::printobjecttable($object_id);
-       echo "</td>";
+       function setlinkid($porta, $portb, $linktype)
+       {
+               if($porta > $portb)
+                       $lid = "${portb}_${porta}";
+               else
+                       $lid = "${porta}_${portb}";
 
-       if($debug)
-               $debug['object'] = &$object;
+               $lid .= $linktype;
 
-       if($remote_id != NULL)
-       {
+               if(isset($this->lids[$lid]))
+                       return false;
 
-               echo "<td></td>"; /* link */
-               echo "<td>";
-               $remote_object = linkmgmt_RTport::printobjecttable($remote_port->port['object_id']);
-               echo "</td>";
+               $this->lids[$lid] = true;
 
-               if($debug)
-                       $debug['remote_object'] = &$remote_object;
+               return true;
        }
 
-       echo "</tr></table>";
-
-       if($debug)
+       function hasport_id($port_id)
        {
-               echo "<pre>--- Debug ---";
-               var_dump($debug);
-               echo "</pre>";
+               if(isset($this->pids[$port_id]))
+                       return true;
+               else
+                       return false;
        }
 
-       exit;
-}
+       function hasobject_id($object_id)
+       {
+               if(isset($this->oids[$object_id]))
+                       return true;
+               else
+                       return false;
+       }
 
-/* -------------------------------------- */
-function lm_renderObjectCell ($cell)
-{
-       echo "<table class='slbcell vscell'><tr><td rowspan=2 width='5%'>";
-       printImageHREF ('OBJECT');
-       echo '</td><td>';
-       echo mkA ('<strong>' . niftyString ($cell['dname']) . '</strong>', 'object', $cell['id']);
-       echo '</td></tr><tr><td>';
-       echo count ($cell['etags']) ? ("<small>" . serializeTags ($cell['etags']) . "</small>") : '&nbsp;';
-       echo "</td></tr></table>";
-}
-/* -------------------------------------- */
+       function _setportprevlink($port, $linktype, $prevport)
+       {
+               $port[$linktype] = array(
+                                       'cableid' => $prevport[$linktype]['cableid'],
+                                       'linked' => $prevport[$linktype]['linked'],
+                                       'portcount' => null, //$prevport[$linktype]['linked'],
+                                       'remote_id' => $prevport['id'],
+                                       'remote_name' => $prevport['name'],
+                                       'remote_object_id' => $prevport['object_id'],
+                                       'remote_object_name' => $prevport['object_name'],
+                               );
 
-class linkmgmt_RTport {
+               return $port;
+       }
 
-       private $port_id = NULL;
+       function _setportlink($port, $linktype)
+       {
+               $port[$linktype] = array(
+                                       'cableid' => $port['cableid'],
+                                       'linked' => $port['linked'],
+                                       'portcount' => $port['portcount'],
+                                       'remote_id' => $port['remote_id'],
+                                       'remote_name' => $port['remote_name'],
+                                       'remote_object_id' => $port['remote_object_id'],
+                                       'remote_object_name' => $port['remote_object_name'],
+                               );
 
-       public $port = false;
+               unset($port['cableid']);
+               unset($port['linked']);
+               unset($port['portcount']);
+               unset($port['remote_id']);
+               unset($port['remote_name']);
+               unset($port['remote_object_id']);
+               unset($port['remote_object_name']);
 
-       function __construct($port_id) {
+               return $port;
+       }
 
-               $this->port = getPortInfo($port_id);
+       function _getportlink($port)
+       {
+               $linktype = $this->getlinktype($this->back);
+               return array_merge($port, $port[$linktype], array('linktype' => $linktype));
+       }
 
-               if($this->port === false)
-                       return;
+       function isback($linktable)
+       {
+               if($linktable == 'back')
+                       return true;
+               else
+                       return false;
+       }
 
-               /* successfully get port info */
-               $this->port_id = $port_id;
+       function getlinktype($back)
+       {
+               return ($back ? 'back' : 'front' );
+       }
 
-       } /* __construct */
+       function reverse()
+       {
+               $tmp = $this->first;
+               $this->first = $this->last;
+               $this->last = $tmp;
 
-       function isvalid() {
-               return ($port_id !== NULL);
        }
 
-       function getlinks($type = 'front') {
-       } /* getlinks */
-
-       function printtable($linktype = 'front') {
 
-               if($this->port_id == NULL)
-                       return;
+       //recursive
+       function _getlinks($port_id, $back = false, $prevport_id = null, $reverse = false)
+       {
+               global $lc_cache;
+               $linktype = $this->getlinktype($back);
 
-               echo "<table>";
+               if($port_id == $this->init)
+                       $this->initport = true;
 
-               $urlparams = array(
-                                       'module' => 'redirect',
-                                       'page' => 'object',
-                                       'tab' => 'linkmgmt',
-                                       'op' => 'map',
-                                       'object_id' => $this->port['object_id'],
-                                       'port_id' => $this->port_id,
-                                       'usemap' => 1,
-                               );
+               $ports = lm_getPortInfo($port_id, $back);
 
-               echo '<tr><td><a title="don\'t highlight port" href="?'.http_build_query($urlparams).'">-phl</a></td>';
+               $portcount = count($ports);
 
-               $urlparams['hl'] = 'p';
-               echo '<td><a title="highlight port" href="?'.http_build_query($urlparams).'">+phl</a></td></tr>';
+               $port = $ports[0];
 
-               $this->_printinforow($this->port,
-                                       array(
-                                               'id' => 'Port ID',
-                                               'name' => 'Port Name',
-                                               'oif_name' => 'Port Type',
-                                               'l2address' => 'MAC',
-                                               'reservation_comment' => 'comment',
-                                       )
-               ); /* printinforow */
+               // check for loops on multilinked ports
+               // set main port to looping one
+               if($portcount > 1)
+                       foreach($ports as $mport)
+                       {
+                               if(isset($this->ports[$mport['remote_id']]))
+                               {
+                                       $port = $mport;
+                                       break;
+                               }
+                       }
 
-               $this->printlinktr($linktype);
+               $remote_id = $port['remote_id'];
 
-               echo "</table>";
-       } /* printtable */
+               $port['portcount'] = $portcount;
 
-       function printlinktr($linktype = 'front') {
-               if($this->port_id === NULL)
-                       return;
+               $object_id =  $port['object_id'];
 
-                $urlparams = array(
-                               'tab' => 'linkmgmt',
-                               'page' => 'object',
-                                'op'=>'PortLinkDialog',
-                                'port'=>$this->port_id,
-                                'object_id'=>$this->port['object_id'],
-                               'linktype' => $linktype,
-                               );
+               $rack = null;
+               $object = $lc_cache->getobject($object_id, $rack);
 
-               echo "<tr><td align=\"center\"><a href='".
-                                makeHrefProcess($urlparams).
-                        "'>";
-                        printImageHREF ('plug', 'Link this port');
-                        echo "</a></td></tr>";
-       } /* link */
+               $this->oids[$object_id] = true;
 
-       function printunlinktr($linktype = 'front', $remote_port = NULL) {
-               if($this->port_id === NULL)
-                       return;
+               if($object['IPV4OBJ'])
+                       $this->lastipobjport = $port_id;
 
-               $urlparams = array(
-                                       'tab' => 'linkmgmt',
-                                        'op'=>'unlinkPort',
-                                        'port_id'=>$this->port_id,
-                                        'object_id'=>$this->port['object_id'],
-                                       'linktype' => $linktype,
-                               );
+               if($object)
+                       if(isset($object['portip'][$port['name']]))
+                               $port['portip'] = $object['portip'][$port['name']];
 
-               $confirmmsg = "unlink port ".$this->port['name'];
+               $port = $this->_setportlink($port, $linktype);
 
-               if($remote_port !== NULL)
+               if($prevport_id)
                {
-                       $urlparams['remote_id'] = $remote_port->port['id'];
-                       $confirmmsg .= ' -> '.$remote_port->port['name'];
+                       $prevlinktype =  $this->getlinktype(!$back);
+                       $port = $this->_setportprevlink($port, $prevlinktype, $this->ports[$prevport_id]);
                }
 
-               $confirmmsg .= " ($linktype)"; // TODO cableid
+               //if(!$back)
+               {
+                       if($prevport_id)
+                       {
+                               /* mutlilink: multiple previous links */
+                               $prevports = lm_getPortInfo($port_id, !$back);
 
-               echo "<tr><td align=\"center\"><a href='".makeHrefProcess($urlparams).
-               "' onclick=\"return confirm('$confirmmsg');\">";
-               printImageHREF ('cut', 'Unlink this port');
-               echo "</a></td></tr>";
+                               $prevportcount = count($prevports);
 
-       } /* unlink */
+                               if($prevportcount > 1)
+                               {
+                                       if($this->initport)
+                                               $this->initalign = false;
 
-       /* TODO move to object class */
-       static function printobjecttable($object_id = NULL) {
+                                       $port[$this->getlinktype(!$back)]['portcount'] = $prevportcount;
 
-               if($object_id === NULL)
-                       return;
+                                       $lcs = array();
+                                       foreach($prevports as $mport)
+                                       {
+                                               if($prevport_id != $mport['remote_id'])
+                                               {
+                                                       if(isset($this->pids[$mport['remote_id']]))
+                                                               continue;
 
-               $object = spotEntity ('object', $object_id);
+                                                       $mport['portcount'] = 1;
+                                                       $lc = new lm_linkchain($mport['remote_id'], $back, $mport, !$reverse, $this->pids, $this->oids);
+                                                       $lcs[$mport['remote_id']] = $lc;
+                                                       $this->linkcount += $lc->linkcount;
+                                               }
+                                       }
 
-               if($object === false)
-                       return;
+                                       $port[$this->getlinktype(!$back)]['chains'] = $lcs;
+                               }
+                       }
+               }
 
-               if($object['rack_id'])
+               if($portcount > 1)
                {
-                       $rack = spotEntity('rack', $object['rack_id']);
+                       /* mutlilink: multiple links */
+
+                       $lcs = array();
+                       foreach($ports as $mport)
+                       {
+                               if($remote_id != $mport['remote_id'])
+                               {
+                                       if(isset($this->pids[$mport['remote_id']]))
+                                               continue;
+
+                                       $mport['portcount'] = 1;
+                                       $lc = new lm_linkchain($mport['remote_id'], !$back, $mport, $reverse, $this->pids, $this->oids);
+                                       $lcs[$mport['remote_id']] = $lc; 
+                                       $this->linkcount += $lc->linkcount;
+                               }
+                       }
+
+                       $port[$linktype]['chains'] = $lcs;
 
-                       $object['row_name'] = $rack['row_name'];
-                       $object['rack_name'] = $rack['name'];
                }
 
-               echo "<table><tr><td>";
-               lm_renderObjectCell($object);
-               echo "</td></tr><tr><td><table>";
+               if(isset($this->ports[$port_id]))
+               {
+                       if(!isset($this->ports[$port_id][$linktype]))
+                       {
+                               $this->ports[$port_id][$linktype] = $port[$linktype];
+                       }
+               }
+               else
+                       $this->ports[$port_id] = $port;
 
-               self::_printinforow($object,
-                               array(
-                                       'id' => 'ID',
-                                       'dname' => 'Name',
-                                       'label' => 'Label',
-                                       'rack_name' => 'Rack',
-                                       'row_name' => 'Row',
-                               )
+               $this->pids[$port_id] = true;
 
-               ); /* printinforow */
+               if($remote_id)
+               {
+                       if($this->setlinkid($port_id, $remote_id, $linktype))
+                       {
+                               $this->linkcount++;
+                               return $this->_getlinks($remote_id, !$back, $port_id, $reverse);
+                       }
+                       else
+                       {
+                               $prevlinktype =  $this->getlinktype(!$back);
+                               $this->loop = true;
+                               if(isset($port[$prevlinktype]))
+                                       $this->ports[$port_id][$prevlinktype] = $port[$prevlinktype];
+
+                               $this->first = $remote_id;
+                       }
+               }
+
+               return $port_id;
+       }
+
+       // TODO
+       function getchaintext()
+       {
+               //$this::var_dump_html($this->ports);
+               $chain = "";
+               foreach($this as $id => $port)
+               {
+                       $linktype = $port['linktype']; //$this->getlinktype();
+                       if($linktype == 'front')
+                               $arrow = ' --> ';
+                       else
+                               $arrow = ' => ';
+
+                       $text = $port['object_name']." [".$port['name']."]";
+
+                       if($id == $this->init)
+                               $chain .= "*$text*";
+                       else
+                               $chain .= "$text";
+
+                       $remote_id = $port['remote_id'];
+
+                       if($remote_id)
+                               $chain .= $arrow;
+
+                       if($this->loop && $remote_id == $this->first)
+                       {
+                               $chain .= "LOOP!";
+                               break;
+                       }
+               }
+               return $chain;
+       }
+
+       function getchainlabeltrstart($rowbgcolor = '#ffffff')
+       {
+
+               $port_id = $this->init;
+
+               $initport = $this->ports[$port_id];
 
                $urlparams = array(
-                                       'module' => 'redirect',
-                                       'page' => 'object',
-                                       'tab' => 'linkmgmt',
-                                       'op' => 'map',
-                                       'object_id' => $object_id,
-                                       'usemap' => 1,
+                               'module' => 'redirect',
+                               'page' => 'object',
+                               'tab' => 'linkmgmt',
+                               'op' => 'map',
+                               'usemap' => 1,
+                               'object_id' => $initport['object_id'],
                                );
 
-               echo '<tr><td><a title="don\'t highlight object" href="?'.http_build_query($urlparams).'">-ohl</a></td>';
+               $hl_port_id = NULL;
+               if($hl_port_id !== NULL)
+                       $urlparams['hl_port_id'] = $hl_port_id;
+               else
+                       $urlparams['port_id'] = $port_id;
 
-               $urlparams['hl'] = 'o';
-               echo '<td><a title="highlight object" href="?'.http_build_query($urlparams).'">+ohl</a></td></tr>';
+               $title = "linkcount: ".$this->linkcount."\nTypeID: ${initport['oif_id']}\nPortID: $port_id";
 
-               echo "</table></td></tr></table>";
+               $onclick = 'onclick=window.open("'.makeHrefProcess(array_merge($_GET, $urlparams)).'","Map","height=500,width=800,scrollbars=yes");';
 
-               return $object;
+               if($hl_port_id == $port_id)
+               {
+                       $hlbgcolor = "bgcolor=".self::HL_PORT_BGCOLOR;
+               }
+               else
+                       $hlbgcolor = "bgcolor=$rowbgcolor";
 
-       } /* printobjecttable */
+               if($rowbgcolor == '#ffffff')
+                       $troutlinecolor = 'grey';
+               else
+                       $troutlinecolor = 'white';
 
-       static function _printinforow(&$data, $config) {
+               /* Current Port */
+               $chainlabel = '<tr '.$hlbgcolor.' style="outline: thin solid '.$troutlinecolor.';"><td nowrap="nowrap" bgcolor='.($this->loop ? '#ff9966' : self::CURRENT_PORT_BGCOLOR).' title="'.$title.
+                       '"><a '.$onclick.'>'.
+                       $initport['name'].': </a></td>';
 
-               foreach($config as $key => $name)
+               return $chainlabel;
+       }
+
+       function getchainrow($allback = false, $rowbgcolor = '#ffffff', $isprev = false)
+       {
+               $port_id = $this->init;
+
+               $initport = $this->ports[$port_id];
+               $portmulti = 0;
+               $prevportmulti = 0;
+
+               $chain = "";
+
+               $remote_id = null;
+               $portalign = false;
+               $portmultis = array();
+               $i=0;
+               foreach($this as $id => $port)
                {
-                       if(isset($data[$key]))
+                       if($this->loop && (($isprev && $id == $this->last) || ($this->initback  === null && !$isprev && $id == $this->first)))
+                               $chain .= '<td bgcolor=#ff9966>LOOP</td>';
+
+                       $object_text = $this->getprintobject($port);
+                       $port_text = $this->getprintport($port);
+
+                       if($this->initback !== null && (($isprev && $id == $this->last) || (!$isprev && $id == $this->first)))
                        {
-                               $value = $data[$key];
-                               if(!empty($value))
-                                       echo "<tr><td align=\"right\" nowrap=\"nowrap\" style=\"font-size:10;\">$name:</td><td nowrap=\"nowrap\">$value</td></tr>";
+                               $object_text = "";
+                               $port_text = "";
                        }
-               }
 
-       } /* _printinforow */
-} /* class RTport */
+                       $linktype = $port['linktype'];
+                       $prevlinktype = ($linktype == 'front' ? 'back' : 'front');
 
-/* -------------------------------------------------- */
+                       if($port[$prevlinktype]['portcount'] > 1)
+                       {
+                               $prevportmulti++;
+                               /* mutlilink: multiple previous links */
 
-function linkmgmt_opmap() {
+                               $notrowbgcolor = ($rowbgcolor == lm_linkchain::ALTERNATE_ROW_BGCOLOR ? '#ffffff' : lm_linkchain::ALTERNATE_ROW_BGCOLOR );
+                               if($this->prevportmulti % 2)
+                               {
+                                       $oddbgcolor = $rowbgcolor;
+                                       $evenbgcolor = $notrowbgcolor;
+                               }
+                               else
+                               {
+                                       $oddbgcolor = $notrowbgcolor;
+                                       $evenbgcolor = $rowbgcolor;
+                               }
 
-       /* display require errors  "white screen of death" */
-       $errorlevel = error_reporting();
-       error_reporting(E_ALL);
+                               $chain = "<td><table id=pmp frame=box><tr id=main><td><table id=pmmain align=right><tr>$chain</tr></table></td></tr><!-- main tr--><tr><td><table id=plcs align=right width=100%>"; // end table pmmain; end main tr
 
-       require_once 'Image/GraphViz.php';
+                               $mi = 0;
+                               foreach($port[$prevlinktype]['chains'] as $mlc)
+                               {
+                                       $mbgcolor = ($mi % 2 ? $evenbgcolor : $oddbgcolor);
+                                       $chain .= "<tr bgcolor=$mbgcolor align=right><td><table id=plc><tr>".$mlc->getchainrow($allback,$mbgcolor, true)."</tr></table><!-- end plc --></td></tr>"; // close table plc
+                                       $mi++;
+                               }
+                               $chain .= "</table><!--plcs--></td></tr></table><!--pmp--></td><td bgcolor=#ff0000></td>"; // clode tables plcs; pmp
 
-/*
- *
- */
-class lm_Image_GraphViz extends Image_GraphViz {
+                       }
 
-       /* extend renderDotFile with additional output file
-        */
-    function renderDotFile($dotfile, $outputfile, $format = 'svg',
-                           $command = null, $outputfile2 = null, $format2 = null)
-    {
-        if (!file_exists($dotfile)) {
-            if ($this->_returnFalseOnError) {
-                return false;
-            }
-            $error = PEAR::raiseError('Could not find dot file');
-            return $error;
-        }
+                       if($port_text && !$this->loop && $id == $this->first)
+                       {
+                               $chain .= $this->_printlinkportsymbol($id, $prevlinktype);
 
-        $oldmtime = file_exists($outputfile) ? filemtime($outputfile) : 0;
+                               if($prevlinktype == 'front')
+                                       $chain .= $this->printcomment($port);
+                       }
 
-        switch ($command) {
-        case 'dot':
-        case 'neato':
-            break;
-        default:
-            $command = $this->graph['directed'] ? 'dot' : 'neato';
-        }
-        $command_orig = $command;
+                       // current port align   
+                       if($this->initback === null && $port_text && !$portmulti && !$prevportmulti && $this->initalign && $id == $port_id)
+                       {
+                               $portalign = true;
+                               $port_text = "</tr></table><!--t1 current--></td><!--end 1st td--><td id=2nd width=100%><table id=t2><tr>$port_text"; // close table t1 current; clode 1st td
+                       }
 
-        $command = $this->binPath.(($command == 'dot') ? $this->dotCommand
-                                                       : $this->neatoCommand);
+                       $object_id = $port['object_id'];
 
-        $command .= ' -T'.escapeshellarg($format)
-                    .' -o'.escapeshellarg($outputfile)
-                    .($format2 !== null && $outputfile2 !== null ? ' -T'.escapeshellarg($format2).' -o'.escapeshellarg($outputfile2) : '')
-                    .' '.escapeshellarg($dotfile)
-                    .' 2>&1';
+                       $prevobject_id = $port[$prevlinktype]['remote_object_id'];
+                       $remote_object_id = $port['remote_object_id'];
 
-        exec($command, $msg, $return_val);
+                       if($linktype == 'front')
+                       {
+                       //      $arrow = ' ---> ';
+                               if($object_text)
+                               {
+                                       if($prevobject_id != $object_id || $allback)
+                                               $chain .= $object_text."<td>></td>";
 
-        clearstatcache();
-        if (file_exists($outputfile) && filemtime($outputfile) > $oldmtime
-            && $return_val == 0) {
-            return true;
-        } elseif ($this->_returnFalseOnError) {
-            return false;
-        }
-        $error = PEAR::raiseError($command_orig.' command failed: '
-                                  .implode("\n", $msg));
-        return $error;
-    }
-    // renderDotFile
+                                       $chain .= $port_text;
+                               }
 
+                       }
+                       else
+                       {
+                       //      $arrow = ' ===> ';
+                               if($object_text)
+                                       $chain .= $port_text."<td><</td>".$object_text;
+                       }
 
-       /*
-        */
-    function fetch($format = 'svg', $command = null, $format2 = null, &$data2 = null)
-    {
+                       if($port['portcount'] > 1)
+                       {
+                               
+                               /* mutlilink: multiple links */
 
-        $file = $this->saveParsedGraph();
-        if (!$file || PEAR::isError($file)) {
-            return $file;
-        }
+                               $notrowbgcolor = ($rowbgcolor == lm_linkchain::ALTERNATE_ROW_BGCOLOR ? '#ffffff' : lm_linkchain::ALTERNATE_ROW_BGCOLOR );
+                               if($this->portmulti % 2)
+                               {
+                                       $oddbgcolor = $rowbgcolor;
+                                       $evenbgcolor = $notrowbgcolor;
+                               }
+                               else
+                               {
+                                       $oddbgcolor = $notrowbgcolor;
+                                       $evenbgcolor = $rowbgcolor;
+                               }
+       
+                               $chain .= "<td bgcolor=#ff0000></td><td><table id=mp frame=box>";
 
-        $outputfile = $file . '.' . $format;
+                               $multichain = "<tr><td><table id=mlcs width=100%>";
 
-       if($format2 != null && $data2 !== null)
-               $outputfile2 = $file . '.' . $format2;
-       else
-               $outputfile2 = null;
+                               $mi = 0;
+                               foreach($port['chains'] as $mlc)
+                               {
 
-        $rendered = $this->renderDotFile($file, $outputfile, $format,
-                                         $command, $outputfile2, $format2);
-        if ($rendered !== true) {
-            return $rendered;
-        }
+                                       $mbgcolor = ($mi % 2 ? $evenbgcolor : $oddbgcolor);
+                                       $multichain .= "<tr bgcolor=$mbgcolor><td><table id=t9><tr>".$mlc->getchainrow(false, $mbgcolor)."</tr></table></td></tr>";
+                                       $mi++;
+                               }
 
-        @unlink($file);
+                               $multichain .= "</table><!-- mlcs --></td></tr>"; // close table mlcs
 
-       if($format2 !== null && $data2 !== null) {
-               $fp = fopen($outputfile2, 'rb');
+                               $portmultis[] = $multichain;
+                               
+                               // main
+                               $chain .= "<tr id=mm><td><table id=pmm><tr>";
 
-               if ($fp) {
-                       $data = fread($fp, filesize($outputfile2));
-                       fclose($fp);
-                       @unlink($outputfile2);
+                               $portmulti++;
 
-                       $data2 = $data;
-               } else {
-                       return $error;
+                       }
+
+                       $remote_id = $port['remote_id'];
+
+                       if($remote_id)
+                               if($object_id != $remote_object_id || $allback || $linktype == 'front')
+                                       $chain .= $this->printlink($port, $linktype);
+                               else
+                                       $chain .= "<td>></td>";
+
+                       if($port_text && !($remote_id && $this->loop) && $id == $this->last)
+                       {
+                               $chain .= $this->_printlinkportsymbol($id, $linktype);
+
+                               if($linktype == 'front')
+                                       $chain .= $this->printcomment($port);
+                       }
+
+                       $i++;
+               } // foreach port
+
+               if($this->loop && $remote_id)
+               {
+                       $chain .= '<td bgcolor=#ff9966>LOOP</td>';
+                       showWarning("Possible Loop on Port ($linktype) ".$initport['name']);
                }
-       }
 
+               if($this->exceed)
+               {
+                       $chain .= '<td bgcolor=#ff9966>LINKCOUNT EXCEEDED</td>';
+                       $linktype = $this->getlinktype($this->back);
+                       showWarning("Possible Loop linkcount(".$this->linkcount.") exceeded on Port ($linktype) ".$initport['name']);
+               }
 
-        $fp = fopen($outputfile, 'rb');
+               foreach(array_reverse($portmultis) as $multitr)
+               {
+                       $chain .= "</tr><!-- tr mm --></table><!-- pmm --></td></tr>$multitr</table><!-- mp--></td>"; // close table pmm; mp
+               }       
 
-        if (!$fp) {
-            if ($this->_returnFalseOnError) {
-                return false;
+               if($this->initback === null)
+               {
+                       // TODO width..
+                       $chain = "<td id=1st".(!$portalign ? " colspan=2" : " width=1%")."><table id=t1".(!$portalign ? "" : " align=right")."><tr>$chain";
+                       $chain .= "</tr></table><!-- end t1/t2 --></td><!--1st/2nd td-->"; // close table t1/t2; close 1st/2nd td
+               }
+
+               $this->portmulti += $portmulti;
+               $this->prevportmulti += $prevportmulti;
+
+               return $chain;
+       }
+
+       /*
+        */
+       function getprintobject($port) {
+               global $lc_cache;
+
+               $object_id = $port['object_id'];
+
+               if($object_id == $this->object_id) {
+                        $color='color: '.self::CURRENT_OBJECT_BGCOLOR;
+                } else {
+                        $color='';
+                }
+
+               $style = "font-size: 80%;";
+
+               $rack = null;
+               $object = $lc_cache->getobject($object_id, $rack);
+
+                if(!$rack)
+                       $rackinfo = '<span style="'.$style.'">Unmounted</span>';
+                else
+               {
+                        $rackinfo = '<a style="'.$style.'" href='.makeHref(array('page'=>'row', 'row_id'=>$rack['row_id'])).'>'.$rack['row_name']
+                                .'</a>/<a style="'.$style.'" href='.makeHref(array('page'=>'rack', 'rack_id'=>$rack['id'])).'>'
+                                .$rack['name'].'</a>';
+               }
+
+                return '<td><table frame=box align=center cellpadding=5 cellspacing=0><tr><td align=center><a style="font-weight:bold;'
+                        .$color.'" href="'.makeHref(array('page'=>'object', 'tab' => 'linkmgmt', 'object_id' => $object_id))
+                        .'"><pre>'.$object['name'].($object['container_name'] ? " (".$object['container_name'].")" : "").'</pre></a><pre>'.$rackinfo
+                        .'</pre></td></tr></table></td>';
+
+       } /* printobject */
+
+       function getprintport($port, $multilink = false) {
+               global $lm_cache, $lm_multilink_port_types;
+
+               /* multilink port */
+               $multilink = in_array($port['oif_id'], $lm_multilink_port_types);
+
+               /* set bgcolor for current port */
+               if($this->initback === null && $port['id'] == $this->init) {
+                       $bgcolor = 'bgcolor='.self::CURRENT_PORT_BGCOLOR;
+                       $idtag = ' id='.$port['id'];
+               } else {
+                       $bgcolor = 'bgcolor=#e0e0f8';
+                       $idtag = '';
+               }
+
+               $mac = trim(preg_replace('/(..)/','$1:',$port['l2address']),':');
+
+               $title = "Label: ${port['label']}\nMAC: $mac\nTypeID: ${port['oif_id']}\nPortID: ${port['id']}";
+
+               if(isset($port['portip']))
+                       $ip = "<br><p style=\"font-size: 80%\">".$port['portip']."</p>";
+               else
+                       $ip = "";
+
+               return '<td><table><tr><td'.$idtag.' align=center '.$bgcolor.' title="'.$title.'"><pre>[<a href="'
+                       .makeHref(array('page'=>'object', 'tab' => 'linkmgmt', 'object_id' => $port['object_id'], 'hl_port_id' => $port['id']))
+                       .'#'.$port['id']
+                       .'">'.$port['name'].'</a>]'.$ip.'</pre>'.($multilink && $lm_cache['allowbacklink'] ? $this->_getlinkportsymbol($port['id'], 'back') : '' ).'</td></tr></table></td>';
+
+       } /* printport */
+       
+       /*
+        */
+       function printlink($port, $linktype) {
+
+               $link_id = $port['id']."_".$port['remote_id'];
+
+               if($linktype == 'back')
+               {
+                       $arrow = '====>';
+                       $link_id .= '_back';
+               }
+               else
+                       $arrow = '---->';
+
+               $port_id = $port['id'];
+
+               /* link */
+               return '<td align=center>'
+                       .'<pre><a class="editcable" id='.$link_id.'>'.$port['cableid']
+                       ."</a></pre><pre>$arrow</pre>"
+                       .$this->_printUnLinkPort($port, $linktype)
+                       .'</td>';
+       } /* printlink */
+
+       /*
+        * return link symbol
+        */
+       function _getlinkportsymbol($port_id, $linktype) {
+               $retval = '<span onclick=window.open("'.makeHrefProcess(array_merge($_GET,
+                       array('op' => 'PortLinkDialog','port' => $port_id,'linktype' => $linktype ))).'","name","height=800,width=800");'
+                       .'>';
+
+                $img = getImageHREF ('plug', $linktype.' Link this port');
+
+               if($linktype == 'back')
+                       $img = str_replace('<img',
+                               '<img style="transform:rotate(180deg);-o-transform:rotate(180deg);-ms-transform:rotate(180deg);-moz-transform:rotate(180deg);-webkit-transform:rotate(180deg);"',
+                               $img);
+
+               $retval .= $img;
+               $retval .= "</span>";
+               return $retval;
+
+       } /* _getlinkportsymbol */
+
+       /*
+        * print link symbol
+        *
+        */
+       function _printlinkportsymbol($port_id, $linktype = 'front') {
+               global $lm_cache;
+
+               if($linktype == 'front' && !$lm_cache['allowlink'])
+                       return;
+
+               if($linktype != 'front' && !$lm_cache['allowbacklink'])
+                       return;
+
+                       return "<td align=center>"
+                       .$this->_getlinkportsymbol($port_id, $linktype)
+                       ."</td>";
+
+        } /* _printlinkportsymbol */
+
+       /*
+        */
+       function printcomment($port) {
+
+               if(!empty($port['reservation_comment'])) {
+                       $prefix = '<b>Reserved: </b>';
+               } else
+                       $prefix = '';
+
+               return '<td>'.$prefix.'<i><a class="editcmt" id='.$port['id'].'>'.$port['reservation_comment'].'</a></i></td>';
+
+       } /* printComment */
+
+       /*
+        * return link cut symbol
+        *
+         * TODO $opspec_list
+        */
+       function _printUnLinkPort($src_port, $linktype) {
+               global $lm_cache;
+
+               if($linktype == 'front' && !$lm_cache['allowlink'])
+                       return;
+
+               if($linktype != 'front' && !$lm_cache['allowbacklink'])
+                       return;
+
+               $dst_port = $this->ports[$src_port['remote_id']];
+
+               return '<a href='.
+                               makeHrefProcess(array(
+                                       'op'=>'unlinkPort',
+                                       'port_id'=>$src_port['id'],
+                                       'remote_id' => $dst_port['id'],
+                                       'object_id'=> $this->object_id, //$this->ports[$this->init]['object_id'],
+                                       'tab' => 'linkmgmt',
+                                       'linktype' => $linktype)).
+                       ' onclick="return confirm(\'unlink ports '.$src_port['name']. ' -> '.$dst_port['name']
+                                       .' ('.$linktype.') with cable ID: '.$src_port['cableid'].'?\');">'.
+                       getImageHREF ('cut', $linktype.' Unlink this port').'</a>';
+
+       } /* _printUnLinkPort */
+
+       // TODO
+       // html table
+       function getchainhtml()
+       {
+               $remote_id = $this->first;
+
+               // if not Link use LinkBackend
+               $back = $this->ports[$remote_id]['front']['remote_id'];
+
+               $chain = "<table>";
+
+               for(;$remote_id;)
+               {
+                       $back = !$back;
+                       $linktype = $port['linktype'];
+
+                       if($back)
+                       {
+                       //      $linktable = 'LinkBackend';
+                               $arrow = ' => ';
+                       }
+                       else
+                       {
+                       //      $linktable = 'Link';
+                               $arrow = ' --> ';
+                       }
+
+                       $port = $this->ports[$remote_id][$linktype];
+
+                       if($this->init == $remote_id)
+                               $chain .= "<tr><td><b>".$port['object_name']."</b></td><td><b> [".$port['name']."]</b></td>";
+                       else
+                               $chain .= "<tr><td>".$port['object_name']."</td><td> [".$port['name']."]</td>";
+
+                       if($remote_id == $this->first || $remote_id == $this->last)
+                               $chain .= "<td><div name=\"port${remote_id}-status\"></div></td>";
+                       else
+                               $chain .= "<td></td>";
+
+                       $remote_id = $port['remote_id'];
+
+                       if($remote_id)
+                               $chain .= "<td>$arrow</td></tr>";
+                       else
+                               $chain .= "<td></td></tr>";
+
+                       if($this->loop && $remote_id == $this->first)
+                       {
+                               $chain .= "LOOP!<br>";
+                               break;
+                       }
+
+               }
+
+               $chain .= "</table>";
+
+               return $chain;
+       }
+
+       function getport($id)
+       {
+               return $this->ports[$id];
+       }
+
+       /* Iterator */
+       function rewind() {
+               $this->currentid = $this->first;
+
+               $this->back = isset($this->ports[$this->currentid]['back']['remote_id']);
+
+               $this->icount = 0;
+       }
+
+       function current() {
+               return $this->_getportlink($this->ports[$this->currentid]);
+       }
+
+       function key() {
+               return $this->currentid;
+       }
+
+       function next() {
+               $port = $this->current();
+               $remote_id = $port['remote_id'];
+       
+               if($this->loop && $remote_id == $this->first)
+                       $this->currentid = false;
+               else
+                       $this->currentid = $remote_id;
+
+               $this->back = !$this->back;
+
+               $this->icount++;
+       }
+
+       function valid() {
+
+               /* linkcout exceeded */
+               if($this->icount > $this->linkcount+1)
+               {
+                       $this->exceed = true;
+                       return false;
+               }
+
+               return $this->currentid;
+       }
+
+       /* for debugging only */
+       function var_dump_html(&$var, $msg = "") {
+               echo "<pre>------------------Start Var Dump -------------$msg------------\n";
+               var_dump($var);
+               echo "\n---------------------END Var Dump -----------$msg-------------</pre>";
+       }
+} // lm_linkchain
+
+/*
+ *   from RT database.php fetchPortList()
+ *     with Link table selection
+ *     and multilink changes
+ */
+function lm_fetchPortList ($sql_where_clause, $query_params = array(), $linktable = 'Link')
+{
+       $query = <<<END
+SELECT
+       Port.id,
+       Port.name,
+       Port.object_id,
+       Object.name AS object_name,
+       Port.l2address,
+       Port.label,
+       Port.reservation_comment,
+       Port.iif_id,
+       Port.type AS oif_id,
+       (SELECT PortInnerInterface.iif_name FROM PortInnerInterface WHERE PortInnerInterface.id = Port.iif_id) AS iif_name,
+       (SELECT PortOuterInterface.oif_name FROM PortOuterInterface WHERE PortOuterInterface.id = Port.type) AS oif_name,
+
+       lk.cable AS cableid,
+       IF(lk.porta = Port.id, pb.id, pa.id) AS remote_id,
+       IF(lk.porta = Port.id, pb.name, pa.name) AS remote_name,
+       IF(lk.porta = Port.id, pb.object_id, pa.object_id) AS remote_object_id,
+       IF(lk.porta = Port.id, ob.name, oa.name) AS remote_object_name,
+
+       (SELECT COUNT(*) FROM PortLog WHERE PortLog.port_id = Port.id) AS log_count,
+       PortLog.user,
+       UNIX_TIMESTAMP(PortLog.date) as time
+FROM
+       Port
+       INNER JOIN Object ON Port.object_id = Object.id
+
+       LEFT JOIN $linktable AS lk ON lk.porta = Port.id or lk.portb = Port.id
+       LEFT JOIN Port AS pa ON pa.id = lk.porta
+       LEFT JOIN Object AS oa ON pa.object_id = oa.id
+       LEFT JOIN Port AS pb ON pb.id = lk.portb
+       LEFT JOIN Object AS ob ON pb.object_id = ob.id
+
+       LEFT JOIN PortLog ON PortLog.id = (SELECT id FROM PortLog WHERE PortLog.port_id = Port.id ORDER BY date DESC LIMIT 1)
+WHERE
+       $sql_where_clause
+END;
+
+       $result = usePreparedSelectBlade ($query, $query_params);
+
+       $ret = array();
+       while ($row = $result->fetch (PDO::FETCH_ASSOC))
+       {
+               $row['l2address'] = l2addressFromDatabase ($row['l2address']);
+               $row['linked'] = isset ($row['remote_id']) ? 1 : 0;
+
+               // last changed log
+               $row['last_log'] = array();
+               if ($row['log_count'])
+               {
+                       $row['last_log']['user'] = $row['user'];
+                       $row['last_log']['time'] = $row['time'];
+               }
+               unset ($row['user']);
+               unset ($row['time']);
+
+               $ret[] = $row;
+       }
+       return $ret;
+} /* lm_fetchPortList */
+
+function lm_getPortInfo ($port_id, $back = false)
+{
+       $linktable = ($back ? 'LinkBackend' : 'Link');
+       if($back)
+               $result = lm_fetchPortList ('Port.id = ?', array ($port_id), $linktable);
+       else
+               $result = fetchPortList ('Port.id = ?', array ($port_id));
+
+        //return empty ($result) ? NULL : $result[0];
+        return $result;
+} /* lm_getPortInfo */
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------- */
+
+function linkmgmt_opHelp() {
+?>
+       <table cellspacing=10><tr><th>Help</th><tr>
+               <tr><td width=150></td><td width=150 style="font-weight:bold;color:<?php echo portlist::CURRENT_OBJECT_BGCOLOR; ?>">Current Object</td></tr>
+               <tr><td></td><td bgcolor=<?php echo portlist::CURRENT_PORT_BGCOLOR; ?>>[current port]</td></tr>
+               <tr><td>front link</td><td>[port]<(Object)</td><td>back link</td></tr>
+               <tr><td>back link</td><td>(Object)>[port]</td><td>front link</td></tr>
+               <tr><td></td><td><pre>----></pre></td><td>Front link</td></tr>
+               <tr><td></td><td><pre>====></pre></td><td>Backend link</td></tr>
+               <tr><td></td><td>Link Symbol</td><td>Create new link</td></tr>
+               <tr><td></td><td>Cut Symbol</td><td>Delete link</td></tr>
+
+       </table>
+
+<?php
+       exit;
+} /* opHelp */
+
+/* -------------------------------------------------- */
+
+function linkmgmt_ajax_mapinfo() {
+
+       $object_id = NULL;
+       $port_id = NULL;
+       $remote_id = NULL;
+       $linktype = NULL;
+
+       if(isset($_REQUEST['object_id']))
+               $object_id = $_REQUEST['object_id'];
+
+       if(isset($_REQUEST['port_id']))
+               $port_id = $_REQUEST['port_id'];
+
+       if(isset($_REQUEST['remote_id']))
+               $remote_id = $_REQUEST['remote_id'];
+
+       if(isset($_REQUEST['linktype']))
+               $linktype = $_REQUEST['linktype'];
+
+       $debug = NULL;
+       if(isset($_REQUEST['debug']))
+               $debug['value'] = $_REQUEST['debug'];
+
+       $info = array();
+
+       echo "<table style=\"font-size:12;\"><tr>";
+
+       if($port_id != NULL)
+       {
+               $port = new linkmgmt_RTport($port_id);
+
+               echo "<td>";
+               $port->printtable('both');
+               echo "</td>";
+
+               if($debug)
+                       $debug['port'] = &$port;
+
+               if($remote_id != NULL)
+               {
+
+                       $remote_port = new linkmgmt_RTport($remote_id);
+
+                       echo "<td><table align=\"center\">";
+
+                       // TODO cableid
+                       echo "<tr><td><pre>".($linktype == 'back' ? ' ===> ' : ' ---> ')."</pre></td></tr>";
+
+                       $port->printunlinktr($linktype, $remote_port);
+
+                       echo "</table></td>";
+
+
+                       echo "<td>";
+                       $remote_port->printtable('both');
+                       echo "</td>";
+
+                       if($debug)
+                               $debug['remote_port'] = &$remote_port;
+
+               }
+               else
+                       $port->printunlinktr();
+
+
+       }
+       echo "</tr><tr>";
+
+       echo "<td>";
+       $object = linkmgmt_RTport::printobjecttable($object_id);
+       echo "</td>";
+
+       if($debug)
+               $debug['object'] = &$object;
+
+       if($remote_id != NULL)
+       {
+
+               echo "<td></td>"; /* link */
+               echo "<td>";
+               $remote_object = linkmgmt_RTport::printobjecttable($remote_port->port['object_id']);
+               echo "</td>";
+
+               if($debug)
+                       $debug['remote_object'] = &$remote_object;
+       }
+
+       echo "</tr></table>";
+
+       if($debug)
+       {
+               echo "<pre>--- Debug ---";
+               var_dump($debug);
+               echo "</pre>";
+       }
+
+       exit;
+}
+
+/* -------------------------------------- */
+function lm_renderObjectCell ($cell)
+{
+       echo "<table class='slbcell vscell'><tr><td rowspan=2 width='5%'>";
+       printImageHREF ('OBJECT');
+       echo '</td><td>';
+       echo mkA ('<strong>' . niftyString ($cell['dname']) . '</strong>', 'object', $cell['id']);
+       echo '</td></tr><tr><td>';
+       echo count ($cell['etags']) ? ("<small>" . serializeTags ($cell['etags']) . "</small>") : '&nbsp;';
+       echo "</td></tr></table>";
+}
+/* -------------------------------------- */
+
+// TODO replace..
+class linkmgmt_RTport {
+
+       private $port_id = NULL;
+
+       public $port = false;
+
+       function __construct($port_id) {
+
+               $this->port = getPortInfo($port_id);
+
+               if($this->port === false)
+                       return;
+
+               /* successfully get port info */
+               $this->port_id = $port_id;
+
+       } /* __construct */
+
+       function isvalid() {
+               return ($port_id !== NULL);
+       }
+
+       function getlinks($type = 'front') {
+       } /* getlinks */
+
+       function printtable($linktype = 'front') {
+
+               if($this->port_id == NULL)
+                       return;
+
+               echo "<table>";
+
+               $urlparams = array(
+                                       'module' => 'redirect',
+                                       'page' => 'object',
+                                       'tab' => 'linkmgmt',
+                                       'op' => 'map',
+                                       'object_id' => $this->port['object_id'],
+                                       'port_id' => $this->port_id,
+                                       'usemap' => 1,
+                               );
+
+               echo '<tr><td><a title="don\'t highlight port" href="?'.http_build_query($urlparams).'">-phl</a></td>';
+
+               $urlparams['hl'] = 'p';
+               echo '<td><a title="highlight port" href="?'.http_build_query($urlparams).'">+phl</a></td></tr>';
+
+               $this->_printinforow($this->port,
+                                       array(
+                                               'id' => 'Port ID',
+                                               'name' => 'Port Name',
+                                               'oif_name' => 'Port Type',
+                                               'l2address' => 'MAC',
+                                               'reservation_comment' => 'comment',
+                                       )
+               ); /* printinforow */
+
+               $this->printlinktr($linktype);
+
+               echo "</table>";
+       } /* printtable */
+
+       function printlinktr($linktype = 'front') {
+               if($this->port_id === NULL)
+                       return;
+
+                $urlparams = array(
+                               'tab' => 'linkmgmt',
+                               'page' => 'object',
+                                'op'=>'PortLinkDialog',
+                                'port'=>$this->port_id,
+                                'object_id'=>$this->port['object_id'],
+                               'linktype' => $linktype,
+                               );
+
+               echo "<tr><td align=\"center\"><a href='".
+                                makeHrefProcess($urlparams).
+                        "'>";
+                        printImageHREF ('plug', 'Link this port');
+                        echo "</a></td></tr>";
+       } /* link */
+
+       function printunlinktr($linktype = 'front', $remote_port = NULL) {
+               if($this->port_id === NULL)
+                       return;
+
+               $urlparams = array(
+                                       'tab' => 'linkmgmt',
+                                        'op'=>'unlinkPort',
+                                        'port_id'=>$this->port_id,
+                                        'object_id'=>$this->port['object_id'],
+                                       'linktype' => $linktype,
+                               );
+
+               $confirmmsg = "unlink port ".$this->port['name'];
+
+               if($remote_port !== NULL)
+               {
+                       $urlparams['remote_id'] = $remote_port->port['id'];
+                       $confirmmsg .= ' -> '.$remote_port->port['name'];
+               }
+
+               $confirmmsg .= " ($linktype)"; // TODO cableid
+
+               echo "<tr><td align=\"center\"><a href='".makeHrefProcess($urlparams).
+               "' onclick=\"return confirm('$confirmmsg');\">";
+               printImageHREF ('cut', 'Unlink this port');
+               echo "</a></td></tr>";
+
+       } /* unlink */
+
+       /* TODO move to object class */
+       static function printobjecttable($object_id = NULL) {
+
+               if($object_id === NULL)
+                       return;
+
+               $object = spotEntity ('object', $object_id);
+
+               if($object === false)
+                       return;
+
+               if($object['rack_id'])
+               {
+                       $rack = spotEntity('rack', $object['rack_id']);
+
+                       $object['row_name'] = $rack['row_name'];
+                       $object['rack_name'] = $rack['name'];
+               }
+
+               echo "<table><tr><td>";
+               lm_renderObjectCell($object);
+               echo "</td></tr><tr><td><table>";
+
+               self::_printinforow($object,
+                               array(
+                                       'id' => 'ID',
+                                       'dname' => 'Name',
+                                       'label' => 'Label',
+                                       'rack_name' => 'Rack',
+                                       'row_name' => 'Row',
+                               )
+
+               ); /* printinforow */
+
+               $urlparams = array(
+                                       'module' => 'redirect',
+                                       'page' => 'object',
+                                       'tab' => 'linkmgmt',
+                                       'op' => 'map',
+                                       'object_id' => $object_id,
+                                       'usemap' => 1,
+                               );
+
+               echo '<tr><td><a title="don\'t highlight object" href="?'.http_build_query($urlparams).'">-ohl</a></td>';
+
+               $urlparams['hl'] = 'o';
+               echo '<td><a title="highlight object" href="?'.http_build_query($urlparams).'">+ohl</a></td></tr>';
+
+               echo "</table></td></tr></table>";
+
+               return $object;
+
+       } /* printobjecttable */
+
+       static function _printinforow(&$data, $config) {
+
+               foreach($config as $key => $name)
+               {
+                       if(isset($data[$key]))
+                       {
+                               $value = $data[$key];
+                               if(!empty($value))
+                                       echo "<tr><td align=\"right\" nowrap=\"nowrap\" style=\"font-size:10;\">$name:</td><td nowrap=\"nowrap\">$value</td></tr>";
+                       }
+               }
+
+       } /* _printinforow */
+} /* class RTport */
+
+/* -------------------------------------------------- */
+
+function linkmgmt_opmap() {
+
+       /* display require errors  "white screen of death" */
+       $errorlevel = error_reporting();
+       error_reporting(E_ALL);
+
+       require_once 'Image/GraphViz.php';
+
+/*
+ *
+ */
+class lm_Image_GraphViz extends Image_GraphViz {
+
+       /* extend renderDotFile with additional output file
+        */
+    function renderDotFile($dotfile, $outputfile, $format = 'svg',
+                           $command = null, $outputfile2 = null, $format2 = null)
+    {
+        if (!file_exists($dotfile)) {
+            if ($this->_returnFalseOnError) {
+                return false;
+            }
+            $error = PEAR::raiseError('Could not find dot file');
+            return $error;
+        }
+
+        $oldmtime = file_exists($outputfile) ? filemtime($outputfile) : 0;
+
+        switch ($command) {
+        case 'dot':
+        case 'neato':
+            break;
+        default:
+            $command = $this->graph['directed'] ? 'dot' : 'neato';
+        }
+        $command_orig = $command;
+
+        $command = $this->binPath.(($command == 'dot') ? $this->dotCommand
+                                                       : $this->neatoCommand);
+
+        $command .= ' -T'.escapeshellarg($format)
+                    .' -o'.escapeshellarg($outputfile)
+                    .($format2 !== null && $outputfile2 !== null ? ' -T'.escapeshellarg($format2).' -o'.escapeshellarg($outputfile2) : '')
+                    .' '.escapeshellarg($dotfile)
+                    .' 2>&1';
+
+        exec($command, $msg, $return_val);
+
+        clearstatcache();
+        if (file_exists($outputfile) && filemtime($outputfile) > $oldmtime
+            && $return_val == 0) {
+            return true;
+        } elseif ($this->_returnFalseOnError) {
+            return false;
+        }
+        $error = PEAR::raiseError($command_orig.' command failed: '
+                                  .implode("\n", $msg));
+        return $error;
+    }
+    // renderDotFile
+
+
+       /*
+        */
+    function fetch($format = 'svg', $command = null, $format2 = null, &$data2 = null)
+    {
+
+        $file = $this->saveParsedGraph();
+        if (!$file || PEAR::isError($file)) {
+            return $file;
+        }
+
+        $outputfile = $file . '.' . $format;
+
+       if($format2 != null && $data2 !== null)
+               $outputfile2 = $file . '.' . $format2;
+       else
+               $outputfile2 = null;
+
+        $rendered = $this->renderDotFile($file, $outputfile, $format,
+                                         $command, $outputfile2, $format2);
+        if ($rendered !== true) {
+            return $rendered;
+        }
+
+        @unlink($file);
+
+       if($format2 !== null && $data2 !== null) {
+               $fp = fopen($outputfile2, 'rb');
+
+               if ($fp) {
+                       $data = fread($fp, filesize($outputfile2));
+                       fclose($fp);
+                       @unlink($outputfile2);
+
+                       $data2 = $data;
+               } else {
+                       return $error;
+               }
+       }
+
+
+        $fp = fopen($outputfile, 'rb');
+
+        if (!$fp) {
+            if ($this->_returnFalseOnError) {
+                return false;
             }
             $error = PEAR::raiseError('Could not read rendered file');
             return $error;
         }
 
-        $data = fread($fp, filesize($outputfile));
-        fclose($fp);
-        @unlink($outputfile);
+        $data = fread($fp, filesize($outputfile));
+        fclose($fp);
+        @unlink($outputfile);
+
+        return $data;
+    }
+    // fetch
+
+
+
+} /* class lm_Image_GraphViz */
+
+       error_reporting($errorlevel);
+
+       $object_id = NULL;
+       $port_id = NULL;
+       $remote_id = NULL;
+       $allports = false;
+       $usemap = false;
+       $command = NULL;
+
+       /* highlight object */
+       $hl = NULL;
+       if(isset($_REQUEST['hl']))
+       {
+               $hl = $_REQUEST['hl'];
+               unset($_REQUEST['hl_object_id']);
+               unset($_REQUEST['hl_port_id']);
+
+               if($hl == 'o')
+               {
+                       unset($_GET['port_id']);
+                       unset($_GET['remote_id']);
+               }
+
+       }
+
+       if(!$hl && isset($_REQUEST['hl_object_id']))
+       {
+               $hl = 'o';
+               $object_id = $_REQUEST['hl_object_id'];
+               $_REQUEST['object_id'] = $object_id;
+               unset($_REQUEST['hl_object_id']);
+               unset($_REQUEST['hl_port_id']);
+               unset($_REQUEST['port_id']);
+       }
+
+       if(isset($_REQUEST['object_id']))
+               $object_id = $_REQUEST['object_id'];
+
+       if(isset($_REQUEST['type']))
+       {
+               $type = $_REQUEST['type'];
+       }
+       else
+               $type = 'gif';
+
+       /* highlight port */
+       if(!$hl && isset($_REQUEST['hl_port_id']))
+       {
+               $hl = 'p';
+               $port_id = $_REQUEST['hl_port_id'];
+               $_REQUEST['port_id'] = $port_id;
+               unset($_REQUEST['hl_port_id']);
+       }
+
+       if(isset($_REQUEST['allports']))
+       {
+               $allports = $_REQUEST['allports'];
+       }
+
+       if(isset($_REQUEST['port_id']))
+       {
+               $port_id = $_REQUEST['port_id'];
+       }
+
+       if(isset($_REQUEST['usemap']))
+               $usemap = $_REQUEST['usemap'];
+
+       if($hl == 'p' && $port_id === NULL)
+       {
+               unset($_GET['hl']);
+               unset($_GET['port_id']);
+               unset($_GET['remote_id']);
+       }
+
+       if($hl == 'o')
+               unset($_GET['remote_id']);
+
+       if(isset($_REQUEST['remote_id']))
+               $remote_id = $_REQUEST['remote_id'];
+
+       /* show all objects */
+       if(isset($_REQUEST['all']))
+       {
+               $object_id = NULL;
+               $port_id = NULL;
+               $hl = NULL;
+               unset($_GET['hl']);
+       }
+
+       if(isset($_REQUEST['cmd']))
+               $command = $_REQUEST['cmd'];
+
+       if(isset($_REQUEST['debug']))
+               $debug = $_REQUEST['debug'];
+       else
+               $debug = False;
+
+       if($debug) echo "-- DEBUG --<br>";
+
+
+       switch($type) {
+               case 'gif':
+               case 'png':
+               case 'bmp':
+               case 'jpeg':
+               case 'tif':
+               case 'wbmp':
+                       $ctype = "image/$type";
+                       break;
+               case 'jpg':
+                       $ctype = "image/jpeg";
+                       break;
+               case 'svg':
+                       $ctype = 'image/svg+xml';
+                       break;
+               case 'pdf':
+                       $ctype = 'application/pdf';
+                       break;
+               case 'cmapx':
+                       $ctype = 'text/plain';
+                       break;
+       }
+
+       $start = microtime(true);
+       $gvmap = new linkmgmt_gvmap($object_id, $port_id, $allports, $hl, $remote_id);
+       $stop = microtime(true);
+
+       if($debug)
+               echo "gvmap Time: ".( $stop - $start )."<br>";
+
+       if($debug) echo "-- after gvmap --<br>";
+
+       if($usemap)
+       {
+
+               if($debug) echo "-- usemap --<br>";
+
+               /* add context menu to Ports, Objects, Links, ...
+                */
+
+               echo "<script>
+                       function initcontextmenu() {
+                               var maps = document.getElementsByTagName('map');
+                                for(var i=0;i<maps.length;i++) {
+                                       var areas = maps[i].childNodes;
+
+                                       for(j=0;j<areas.length;j++) {
+                                               if(areas[j].nodeType == 1)
+                                               {
+                                               //      console.log(areas[j].id);
+                                               //      attr = document.createAttribute('onmouseover','ahh');
+                                               //      areas[j].setAttribute(attr);
+                                               //      areas[j].onmouseover = 'menu(this);';
+
+                                                       areas[j].addEventListener('contextmenu',menu,false);
+                                               //      areas[j].oncontextmenu = 'menu(this, event);';
+                                               //      console.log(areas[j].oncontextmenu);
+                                               }
+                                       }
+
+                                }
+
+                       };
+
+                       function menu(event) {
+
+                       //      console.log('Menu');
+
+                               if(!event)
+                                       event = window.event;
+
+                               var parent = event.target;
+
+                       //      console.log('--' + parent);
+
+                               var ids = parent.id.split('-');
+
+                               if(ids[0] == 'graph1')
+                                       return false;
+
+                               var object_id = ids[0];
+
+                               var url ='?module=ajax&ac=lm_mapinfo&object_id=' + object_id;
+
+                       //      links ='<li><a href=' + object_id + '>Object</a></li>';
+
+                               if(ids[1] != '')
+                               {
+                                       var port_id = ids[1];
+                                       url += '&port_id=' + port_id;
+                               //      links += '<li><a href=' + port_id + '>Port</a></li>';
+
+                                       if(ids[2] != '')
+                                       {
+                                               var remote_id = ids[2];
+
+                                               if(ids[3] != '')
+                                               {
+                                                       var linktype = ids[3];
+                                                       url += '&remote_id=' + remote_id + '&linktype=' + linktype;
+                                               //      links += '<li><a href=' + port_id + '_' + remote_id + '_' + linktype + '>Unlink</a></li>';
+                                               }
+                                       }
+
+                               }
+
+
+                               var xmlHttp = new XMLHttpRequest();
+                               xmlHttp.open('GET', url, false);
+                               xmlHttp.send(null);
+
+                               var infodiv = document.getElementById('info');
+                               infodiv.innerHTML = xmlHttp.responseText;
+
+               //              linkdiv = document.getElementById('link');
+               //              linkdiv.innerHTML = links;
+
+                               var menudiv = document.getElementById('menu');
+                               menudiv.style.position  = 'absolute';
+                               menudiv.style.top  = (event.clientY + document.body.scrollTop) + 'px';
+                               menudiv.style.left  = (event.clientX + document.body.scrollLeft) + 'px';
+                               menudiv.style.display  = '';
+
+                               return false;
+                       };
+
+                       function mousedown(event) {
+                               //      console.log('mouse down');
+
+                               if(!event)
+                                       event = window.event;
+
+                               if(event.button != 2)
+                                       return true;
+
+                               var menudiv = document.getElementById('menu');
+
+                               menudiv.style.display = 'none';
+
+                               return false;
+                       };
+
+                       </script>";
+
+               echo "<body oncontextmenu=\"return false\" onmousedown=\"mousedown(event);\" onload=\"initcontextmenu();\">";
+
+               echo "<div id=\"menu\" style=\"display:none; background-color:#ffff90\">
+                               <div id=\"info\"></div>
+                               <ul id=\"link\" style=\"list-style-type:none\"></ul>
+                       </div>";
+
+               if($debug)
+                       $gvmap->setFalseOnError(False);
+
+               $data2 = '';
+
+               $start = microtime(true);
+               $data = $gvmap->fetch($type, $command, 'cmapx', $data2);
+               $stop = microtime(true);
+
+               if($debug)
+                       echo "DOT time: ".( $stop - $start )."<br>";
+
+               if($data === false)
+                       echo "ERROR Fetching image data!<br>";
+
+               if(PEAR::isError($data))
+                       echo $data->getMessage();
+
+               //echo $gvmap->fetch('cmapx', $command);
+               echo $data2;
+
+               if($debug) echo "-- after map gvmap --<br>";
+
+               echo "<img src=\"data:$ctype;base64,".
+                       base64_encode($data).
+                       "\" usemap=#map$object_id />";
+
+               if($debug)
+               {
+                       echo "<pre>";
+                       echo $gvmap->export();
+                       echo "</pre>";
+
+                       echo "<pre>".$gvmap->parse()."</pre>";
+               }
+       }
+       else
+       {
+               $gvmap->image($type);
+       }
+
+       exit;
+
+} /* linkmgmt_opmap */
+
+class cytoscapedata
+{
+       public $objects = array();
+
+       public $pids = array();
+
+       public $parents = array();
+       public $nodes = array();
+
+       public $edges = NULL;
+
+       public $debug = null;
+
+       function __construct()
+       {
+
+               $this->edges['parents'] = array();
+               $this->edges['nodes'] = array();
+       }
+
+       function addnode($id, $values = NULL, &$arr = NULL)
+       {
+               $data = array( 'id' => $id );
+
+               if($values != NULL)
+                       $data = $data + $values;
+
+               $node['data'] = $data;
+
+               $this->objects[] = array('group' => 'nodes')  + $node;
+
+               switch($values['type'])
+               {
+                       case 'port':
+                               $this->nodes[$id] = array('group' => 'nodes')  + $node;
+                               break;
+                       case 'object':
+                       case 'container':
+                               $this->parents[$id] = array('group' => 'nodes')  + $node;
+                               break;
+               }
+
+               if($arr !== NULL)
+                       $arr[] = array('group' => 'nodes')  + $node;
+       }
+
+       function addedge($id, $source, $target, $values = NULL, &$arr = NULL)
+       {
+               $data = array(
+                               'id' => $id,
+                               'source' => $source,
+                               'target' => $target
+                        );
+
+               if($values != NULL)
+                       $data = $data + $values;
+
+               $edge['data'] = $data;
+
+               //$this->elements['edges'][] = $edge;
+
+               if($arr !== NULL)
+               {
+                       $arr[$id] = array('group' => 'edges') + $edge;
+               }
+               else
+                       $this->objects[] = array('group' => 'edges') + $edge;
+
+               //$this->edges[] = array('group' => 'edges') + $edge;
+       }
+
+       function _addobjectnode($object_id, $type = 'object')
+       {
+                       global $lc_cache;
+
+                       if(!isset($this->parents["o$object_id"]))
+                       {
+                               $rack = null;
+                               $object = $lc_cache->getobject($object_id, $rack);
+
+                               $clustertitle = "${object['dname']}";
+
+                               //has_problems
+                               //if($object['has_problems'] != 'no')
+
+                               $rack_text = "";
+                               if(!empty($rack['row_name']) || !empty($rack['name']))
+                               {
+                                       $rack_text = "${rack['row_name']} / ${rack['name']}";
+                               }
+
+                               $data = array('label' => $object['name'], 'text' => $rack_text, 'type' => $type, 'has_problems' => $object['has_problems']);
+
+                               $container_id = $object['container_id'];
+                               if($container_id)
+                               {
+                                       $data['parent'] = "o$container_id";
+                                       $this->_addobjectnode($container_id, 'container');
+                               }
+
+                               $this->addnode('o'.$object_id, $data);
+                       }
+
+       }
+
+       // cytoscape
+       function addlinkchain($linkchain, $index) {
+
+               foreach($linkchain as $id => $port)
+               {
+
+                       if(!$linkchain->linked)
+                               continue;
+
+                       $this->_addobjectnode($port['object_id']);
+
+                       $text = (isset($port['portip']) ? $port['portip'] : "" );
+                       $nodedata = array('label' => $port['name'], 'parent' => 'o'.$port['object_id'], 'text' => $text, 'type' => 'port', 'index' => $index , 'loop' => ($linkchain->loop ? '1' : '0'));
+
+                       //$this->addnode('l_'.$port['id'], array( 'label' => $port['name'], 'parent' => 'p'.$port['id'], 'text' => $text ));
+
+                       if($port['portcount'] > 1)
+                               foreach($port['chains'] as $mlc)
+                               {
+                                       $this->addlinkchain($mlc, 0); // TODO index
+                               }
+
+                       $prevlinktype = ($port['linktype'] == 'front' ? 'back' : 'front');
+                       if($port[$prevlinktype]['portcount'] > 1)
+                       {
+                               foreach($port[$prevlinktype]['chains'] as $mlc)
+                               {
+                                       $this->addlinkchain($mlc, 0, false); // TODO index
+                               }
+                       }
 
-        return $data;
-    }
-    // fetch
+                       if($port['remote_id'])
+                       {
 
+                               $this->_addobjectnode($port['remote_object_id']);
 
+                               $linktype = $port['linktype'];
+                               $edgedata = array('label' => $port['cableid'], 'type' => $linktype, 'loop' => ($linkchain->loop ? '1' : '0'));
 
-} /* class lm_Image_GraphViz */
+                               if($linkchain->loop && $port['remote_id'] == $linkchain->first)
+                               {
+                                       $nodedata['loopedge'] = array('group' => 'edges', 'data' => array( 'id' => 'le'.$port['id']."_".$port['remote_id'], 'source' => 'p'.$port['id'], 'target' => 'p'.$port['remote_id']) +  $edgedata);
+                               }
+                               else
+                               {
 
-       error_reporting($errorlevel);
+                                       $this->addedge('e'.$port['id']."_".$port['remote_id'], 'p'.$port['id'], 'p'.$port['remote_id'], $edgedata);
+                                       $this->addedge('e'.$port['id']."_".$port['remote_id'], 'p'.$port['id'], 'p'.$port['remote_id'], $edgedata, $this->edges['nodes']);
+                                       $id1 = $port['object_id'];
+                                       if($id1 > $port['remote_object_id'])
+                                       {
+                                               $id1 = $port['remote_object_id'];
+                                               $id2 = $port['object_id'];
+                                       }
+                                       else
+                                               $id2 = $port['remote_object_id'];
 
-       $object_id = NULL;
-       $port_id = NULL;
-       $remote_id = NULL;
-       $allports = false;
-       $usemap = false;
-       $command = NULL;
+                                       $peid = "pe".$id1."_".$id2;
 
-       /* highlight object */
-       $hl = NULL;
-       if(isset($_REQUEST['hl']))
-       {
-               $hl = $_REQUEST['hl'];
-               unset($_REQUEST['hl_object_id']);
-               unset($_REQUEST['hl_port_id']);
+                                       if(isset($this->edges['parents'][$peid]))
+                                               $this->edges['parents'][$peid]['data']['linkcount']++;
+                                       else
+                                               $this->addedge($peid, 'o'.$id1, 'o'.$id2, array('type' => $linktype, 'linkcount' => 1), $this->edges['parents']);
+                               }
+                       }
 
-               if($hl == 'o')
+                       $this->addnode('p'.$port['id'], $nodedata);
+               }
+
+               if(0)
+               if($linkchain->first != $linkchain->last )
                {
-                       unset($_GET['port_id']);
-                       unset($_GET['remote_id']);
+                               $first = $linkchain->first;
+                               $last = $linkchain->last;
+                               $this->addedge("l${first}_${last}",'p'.$first, 'p'.$last, array('type' => 'logical', 'label' => "logical"));
                }
 
+               //portlist::var_dump_html($this->parents);
        }
 
-       if(!$hl && isset($_REQUEST['hl_object_id']))
-       {
-               $hl = 'o';
-               $object_id = $_REQUEST['hl_object_id'];
-               $_REQUEST['object_id'] = $object_id;
-               unset($_REQUEST['hl_object_id']);
-               unset($_REQUEST['hl_port_id']);
-               unset($_REQUEST['port_id']);
-       }
+       function getlinkchains($object_id) {
 
-       if(isset($_REQUEST['object_id']))
-               $object_id = $_REQUEST['object_id'];
+               $this->objects = array();
+               $this->parents = array();
+               $this->nodes = array();
+               $this->edges['parents'] = array();
+               $this->edges['nodes'] = array();
 
-       if(isset($_REQUEST['type']))
-       {
-               $type = $_REQUEST['type'];
+               $this->_getlinkchains($object_id);
        }
-       else
-               $type = 'gif';
 
-       /* highlight port */
-       if(!$hl && isset($_REQUEST['hl_port_id']))
+       function getelements()
        {
-               $hl = 'p';
-               $port_id = $_REQUEST['hl_port_id'];
-               $_REQUEST['port_id'] = $port_id;
-               unset($_REQUEST['hl_port_id']);
-       }
+               //lm_linkchain::var_dump_html($this);
+               return array('parents' => array_values($this->parents),
+                       'nodes' =>  array_values($this->nodes),
+                       'edges' =>  array(
+                                       'parents' => array_values($this->edges['parents']),
+                                       'nodes' => array_values($this->edges['nodes'])
+                                       ),
+                       'debug' => $this->debug
+                       );
 
-       if(isset($_REQUEST['allports']))
-       {
-               $allports = $_REQUEST['allports'];
        }
 
-       if(isset($_REQUEST['port_id']))
+       function gettest()
        {
-               $port_id = $_REQUEST['port_id'];
+               return array_merge(array_values($this->parents), array_values($this->edges['parents']));
+               //return array_merge(array_values($this->parents), array_values($this->edges['parents']), array_values($this->nodes), array_values($this->edges['nodes']));
        }
 
-       if(isset($_REQUEST['usemap']))
-               $usemap = $_REQUEST['usemap'];
+       function _getlinkchains($object_id) {
 
-       if($hl == 'p' && $port_id === NULL)
-       {
-               unset($_GET['hl']);
-               unset($_GET['port_id']);
-               unset($_GET['remote_id']);
-       }
 
-       if($hl == 'o')
-               unset($_GET['remote_id']);
+               // container
+       //      $object = spotEntity('object', $object_id);
+               $object['ports'] = getObjectPortsAndLinks ($object_id);
 
-       if(isset($_REQUEST['remote_id']))
-               $remote_id = $_REQUEST['remote_id'];
+               $i = 0;
+               foreach($object['ports'] as $key => $port)
+               {
 
-       /* show all objects */
-       if(isset($_REQUEST['all']))
+                       if(isset($this->pids[$port['id']]))
+                               continue;
+
+                       $i++;
+                       $lc = new lm_linkchain($port['id']);
+
+                       $this->pids += $lc->pids;
+
+                       $this->addlinkchain($lc, $i);
+
+               }
+
+               $children = getEntityRelatives ('children', 'object', $object_id);
+
+               foreach($children as $child)
+                       $this->_getlinkchains($child['entity_id']);
+       }
+
+       function allobjects()
        {
-               $object_id = NULL;
-               $port_id = NULL;
-               $hl = NULL;
-               unset($_GET['hl']);
+
+               $this->objects = array();
+               $this->parents = array();
+               $this->nodes = array();
+               $this->edges['parents'] = array();
+               $this->edges['nodes'] = array();
+
+               $objects = listCells('object');
+
+               $i = 0;
+               foreach($objects as $object)
+               {
+                       //echo $object['id']."<br>";
+                       $this->_getlinkchains($object['id']);
+                       $i++;
+                       if($i > 20 ) break;
+               }
        }
+}
 
-       if(isset($_REQUEST['cmd']))
-               $command = $_REQUEST['cmd'];
+function linkmgmt_cytoscapemap() {
 
-       if(isset($_REQUEST['debug']))
-               $debug = $_REQUEST['debug'];
-       else
-               $debug = False;
 
-       if($debug) echo "-- DEBUG --<br>";
+       $object_id = $_GET['object_id'];
 
-       $gvmap = new linkmgmt_gvmap($object_id, $port_id, $allports, $hl, $remote_id);
+       if(isset($_GET['json']))
+       {
+               ob_start();
+               $data = new cytoscapedata();
+               $data->getlinkchains($object_id);
+               //$data->allobjects(); // ugly graph;
+               //echo json_encode($data->objects);
 
-       if($debug) echo "-- after gvmap --<br>";
+               if(ob_get_length())
+                       $data->debug = ob_get_contents();
 
-       switch($type) {
-               case 'gif':
-               case 'png':
-               case 'bmp':
-               case 'jpeg':
-               case 'tif':
-               case 'wbmp':
-                       $ctype = "image/$type";
-                       break;
-               case 'jpg':
-                       $ctype = "image/jpeg";
-                       break;
-               case 'svg':
-                       $ctype = 'image/svg+xml';
-                       break;
-               case 'pdf':
-                       $ctype = 'application/pdf';
-                       break;
-               case 'cmapx':
-                       $ctype = 'text/plain';
-                       break;
+               ob_end_clean();
+
+               echo json_encode($data->getelements());
 
+               exit;
        }
 
-       if($usemap)
-       {
+       echo (<<<HTMLEND
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+body {
+  font: 14px helvetica neue, helvetica, arial, sans-serif;
+}
 
-               if($debug) echo "-- usemap --<br>";
+#cy {
+  height: 100%;
+  width: 100%;
+  position: absolute;
+  left: 0;
+  top: 0;
+}
+</style>
+<meta charset=utf-8 />
+<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
+<title>Compound nodes</title>
+<!--<script src="js/jquery-1.4.4.min.js"></script>-->
+
+<script src="js/jquery-1.11.3.min.js"></script>
+<script src="js/cytoscape.min.js"></script>
+<script src="js/dagre.min.js"></script>
+<script src="js/cytoscape-dagre.js"></script>
+<link rel="stylesheet" type="text/css" href="css/jquery.qtip.min.css">
+<script src="js/jquery.qtip.min.js"></script>
+<script src="js/cytoscape-qtip.js"></script>
+
+<!--<script src="js/cytoscape-css-renderer_mod.js"></script>-->
+<!--<script src="js/cytoscape.js-navigator.js_mod"></script>-->
+<script>
+var cy = null;
+var cy2 = null;
+var layout = null;
+var data = null;
+
+$(function(){ // on dom ready
+  var cystyle = [
+    {
+      selector: 'node',
+      css: {
+        'content': 'data(id)',
+       'text-wrap': 'wrap'
+      },
+      style: {
+        'background-color': '#666',
+        'label': 'data(label)',
+       'width': 'label',
+       'min-zoomed-font-size' : 8,
+        'text-valign': 'center',
+        'text-halign': 'center',
+       'text-wrap': 'wrap',
+       'shape': function(ele) {
+                       if(ele.data('type') != 'port')
+                               return 'rectangle';
+                       else
+                               return 'ellipse';
+               },
 
-               /* add context menu to Ports, Objects, Links, ...
-                */
+      }
+    },
+    {
+      selector: '\$node > node',
+      css: {
+        'padding-top': '10px',
+        'padding-left': '10px',
+        'padding-bottom': '10px',
+        'padding-right': '10px',
+        'text-valign': 'top',
+        'text-halign': 'center',
+        'background-color': '#bbb',
+       'min-zoomed-font-size' : 6,
+      }
+    },
+    {
+      selector: 'edge',
+      css: {
+        'target-arrow-shape': 'triangle',
+       'line-color': function(ele){
+                               if(ele.data('type') == 'front')
+                                       return 'black';
+                               else
+                                       return 'grey';
+                        },
+       'line-style': function(ele){
+                               if(ele.data('type') == 'front')
+                                       return 'solid';
+                               else
+                                       return 'dashed';
+                        },
+       'width': function(ele){
+                               var ret = 1;
+                               if(ele.data('type') == 'front')
+                                       ret = 3;
+                               else
+                                       ret = 5;
 
-               echo "<script>
-                       function initcontextmenu() {
-                               var maps = document.getElementsByTagName('map');
-                                for(var i=0;i<maps.length;i++) {
-                                       var areas = maps[i].childNodes;
+                               if(1)
+                               if(ele.data('linkcount'))
+                               {
+                                       ret = (ele.data('linkcount') * ret);
+                               }
 
-                                       for(j=0;j<areas.length;j++) {
-                                               if(areas[j].nodeType == 1)
-                                               {
-                                               //      console.log(areas[j].id);
-                                               //      attr = document.createAttribute('onmouseover','ahh');
-                                               //      areas[j].setAttribute(attr);
-                                               //      areas[j].onmouseover = 'menu(this);';
+                               //console.log(ele.data('id') + ret);
+                               return ret;
+                        },
+//     'curve-style': 'segments',
+       'font-size': '8',
+       'min-zoomed-font-size' : 8,
+        'label': 'data(label)',
+       'edge-text-rotation': 'autorotate',
+       'source-arrow-shape': 'none',
+       'target-arrow-shape': 'none'
+      }
+    },
+       {
+               selector: '.logical',
+               css: {
+                       'line-color': '#0000ff',
+                       'width': 5,
+                       'curve-style': 'segments',
+                       'z-index': 0
+               }
+       },
+    {
+      selector: ':selected',
+      css: {
+        'background-color': 'black',
+        'line-color': 'black',
+        'target-arrow-color': 'black',
+        'source-arrow-color': 'black'
+      }
+    },
+    {
+       selector: '.highlighted',
+       css: {
+        'line-color': '#ff0000',
+        'background-color': '#ff0000'
+      }
+       },
+    {
+       selector: '.clhighlighted',
+       css: {
+        'line-color': '#00ff00',
+        'background-color': '#00ff00'
+       }
+   }
+  ];
 
-                                                       areas[j].addEventListener('contextmenu',menu,false);
-                                               //      areas[j].oncontextmenu = 'menu(this, event);';
-                                               //      console.log(areas[j].oncontextmenu);
-                                               }
-                                       }
+var cylayout = { name: 'dagre', nodeSep: 3, ready: layoutready, stop: layoutstop };
 
-                                }
+function highlight(evt) {
 
-                       };
+       //cy = evt.cy;
 
-                       function menu(event) {
+       var hlclass = evt.data.hlclass;
+       var ele = evt.cyTarget;
 
-                       //      console.log('Menu');
+       if(!ele.data)
+               return;
 
-                               if(!event)
-                                       event = window.event;
+       if(ele.data('source'))
+               ele = cy.$( '#' + ele.data('source'));
 
-                               var parent = event.target;
+       var id = ele.data('id');
 
-                       //      console.log('--' + parent);
+       if(ele.isParent())
+       {
+       //      var childs = cy.$('#' + id).children()
+       //      childs.layout({name:'grid', cols: 2, condense: true});
+               return;
+       }
 
-                               var ids = parent.id.split('-');
 
-                               if(ids[0] == 'graph1')
-                                       return false;
+//     console.log('Event ' + hlclass + ' ' + id );
 
-                               var object_id = ids[0];
+       // remove existing highlights
+       cy.$('.' + hlclass).removeClass(hlclass);
 
-                               var url ='?module=ajax&ac=lm_mapinfo&object_id=' + object_id;
+       var hleles = ele.closedNeighborhood();
 
-                       //      links ='<li><a href=' + object_id + '>Object</a></li>';
+       var hlcount = hleles.length;
 
-                               if(ids[1] != '')
-                               {
-                                       var port_id = ids[1];
-                                       url += '&port_id=' + port_id;
-                               //      links += '<li><a href=' + port_id + '>Port</a></li>';
+       // max 100
+       for(i=0;i<100;i++)
+       {
+               hleles = hleles.closedNeighborhood();
 
-                                       if(ids[2] != '')
-                                       {
-                                               var remote_id = ids[2];
+               if(hlcount == hleles.length)
+                       break;
 
-                                               if(ids[3] != '')
-                                               {
-                                                       var linktype = ids[3];
-                                                       url += '&remote_id=' + remote_id + '&linktype=' + linktype;
-                                               //      links += '<li><a href=' + port_id + '_' + remote_id + '_' + linktype + '>Unlink</a></li>';
-                                               }
-                                       }
+               hlcount = hleles.length;
 
-                               }
+       }
+       //console.log('End Loop ' + i );
+       hleles.addClass(hlclass);
 
+//     console.log('Event Type: ' + evt.originalEvent.type)
+//     console.log('Event Type: ' + evt.type)
 
-                               var xmlHttp = new XMLHttpRequest();
-                               xmlHttp.open('GET', url, false);
-                               xmlHttp.send(null);
+       if(evt.type != 'click')
+               return;
 
-                               var infodiv = document.getElementById('info');
-                               infodiv.innerHTML = xmlHttp.responseText;
+       var hleles2 = hleles.clone();
+       hleles2 = hleles2.add(hleles.parents().clone());
 
-               //              linkdiv = document.getElementById('link');
-               //              linkdiv.innerHTML = links;
+       //var cy2 = evt.data.cy2;
 
-                               var menudiv = document.getElementById('menu');
-                               menudiv.style.position  = 'absolute';
-                               menudiv.style.top  = (event.clientY + document.body.scrollTop) + 'px';
-                               menudiv.style.left  = (event.clientX + document.body.scrollLeft) + 'px';
-                               menudiv.style.display  = '';
+       //var ret = evt.data.ret;
 
-                               return false;
-                       };
+       //var j = ret.parents.concat(ret.nodes).concat(ret.edges.nodes);
 
-                       function mousedown(event) {
-                               //      console.log('mouse down');
+       cy2.remove(cy2.elements());
+       cy2.add(hleles2);
+       //cy2.add(j);
+       cy2.layout({name: 'dagre', rankDir: 'LR', ready: layoutready});
+}
 
-                               if(!event)
-                                       event = window.event;
+function cytoscapeswitch(evt)
+{
+       var b = evt.target;
 
-                               if(event.button != 2)
-                                       return true;
+       var j = null;
+       if(b.value == "1")
+       {
+               j = data.parents.concat(data.edges.parents);
+               b.value = "0";
+               b.innerHTML = "Ports";
+       }
+       else
+       {
+               j = data.parents.concat(data.nodes).concat(data.edges.nodes);
+               b.value = "1";
+               b.innerHTML = "Objects";
+       }
 
-                               var menudiv = document.getElementById('menu');
+       //layout.stop();
+       cy.remove(cy.elements());
 
-                               menudiv.style.display = 'none';
+       cy.add(j);
 
-                               return false;
-                       };
+       cy.layout(cylayout);
+       //layout.run();
+}
 
-                       </script>";
+$.ajax({
+       type: "GET",
+       url: "{$_SERVER['PHP_SELF']}",
+       data: { module: 'redirect',
+               page: 'object',
+               tab: 'linkmgmt',
+               object_id: $object_id,
+               op: 'cytoscapemap',
+               json: 'json'
+               },
+       dataTye: 'json',
+       error: function(){ alert("Error loading"); },
+       success: function(jdata) {
+
+                       data = JSON.parse(jdata);
+
+                       if(data.debug)
+                               $('#debug').html(data.debug);
+
+                       //j = data.parents.concat(data.edges.parents);
+                       j = data.parents.concat(data.nodes).concat(data.edges.nodes);
+
+                       if(j.length == 0)
+                       {
+                               alert("No Links to display. Closing Window");
+                               window.close();
+                               return;
+                       }
 
-               echo "<body oncontextmenu=\"return false\" onmousedown=\"mousedown(event);\" onload=\"initcontextmenu();\">";
+                       cy2 = cytoscape({
+                               container: document.getElementById('cy2'),
+                               //renderer: { name: 'css' },
+
+                               boxSelectionEnabled: false,
+                               autounselectify: true,
+                               style: cystyle,
+                               wheelSensitivity: 0.1,
+                       });
+                       cy2.style().selector('node').style('label', function(node) { return node.data('label') + '\\n' + node.data('text'); });
+
+                       cystyle.push({
+                               selector: '.loopedge',
+                               css: {
+                                       'curve-style': 'segments',
+                               }
+                       });
+
+                       cy = cytoscape({
+                               container: document.getElementById('cy'),
+
+                               boxSelectionEnabled: false,
+                               autounselectify: true,
+                               style: cystyle,
+                               wheelSensitivity: 0.1,
+                               elements: j,
+                               layout: cylayout,
+                       });
+
+                       cy.on('mouseover', { hlclass: 'highlighted' }, highlight );
+                       cy.on('click', { hlclass: 'clhighlighted' }, highlight );
+
+                       $('#switch').click(cytoscapeswitch);
+
+                       /*
+                               TODO: node ranking
+                       */
+
+                       if(0)
+                       cy.layout({
+                               name: 'dagre',
+                               /*
+                               nodeSep: 5,
+                               rankDir: 'TB',
+                               */
+                               //edgeSep: 10,
+                               /*
+                               edgeWeight: function(edge) {
+                                               if(edge.data('type') == 'front')
+                                                       return 10;
+                                               else
+                                                       return 1;
+                                               
+                                       },
+                               */
+                               ready: layoutready,
+                               stop: layoutstop
+                               });
+
+                       if(1)
+                       cy.$(':child').qtip({
+
+                               content: function() {
+
+                                               return this.id() + '<br>' 
+                                               + 'Index: ' + this.data('index') + '<br>'
+                                               + 'Name: ' + this.data('label') + '<br>'
+                                               + 'Text: ' + this.data('text');
+                                       },
+                               position: {
+                                       my: 'top center',
+                                       at: 'bottom center'
+                                       },
+                               style: {
+                                       classes: 'qtip-bootstrap',
+                                       tip: {
+                                               width: 16,
+                                               height: 8
+                                               },
+                                       }
+                       }); // qtip
+               } // success function
+       });
+
+
+}); // on dom ready
+
+function layoutready(evt) {
+       var _cy = evt.cy;
 
-               echo "<div id=\"menu\" style=\"display:none; background-color:#ffff90\">
-                               <div id=\"info\"></div>
-                               <ul id=\"link\" style=\"list-style-type:none\"></ul>
-                       </div>";
+       // highlight current object
+       var object = _cy.$('#o$object_id');
+       if(object.data('has_problems') == 'no')
+               object.style('background-color','#ffcccc');
 
-               if($debug)
-                       $gvmap->setFalseOnError(False);
+       // highlight object with problems
+       _cy.$('node[type = "object"][has_problems != "no"]').style('background-color','#ff0000');
 
-               $data2 = '';
-               $data = $gvmap->fetch($type, $command, 'cmapx', $data2);
+       var e = _cy.$('node[loop = "1"]').style('background-color','#ff6666');
 
-               if($data === false)
-                       echo "ERROR Fetching image data!<br>";
+//     console.log(e[0].data('loop'));
 
-               if(PEAR::isError($data))
-                       echo $data->getMessage();
+//     cy.add({group: 'edges', data: {id:'l691_2991', source: 'p691', target: 'p2991', label: 'test'}, classes: 'logical'});
 
-               //echo $gvmap->fetch('cmapx', $command);
-               echo $data2;
+}
 
-               if($debug) echo "-- after map gvmap --<br>";
+function layoutstop(evt) {
+       var _cy = evt.cy;
+//     cy.elements().locked = true;
+//     cy.add({group: 'nodes', data: {id:'l2493', parent:'p2943', label: 'test'}});
 
-               echo "<img src=\"data:$ctype;base64,".
-                       base64_encode($data).
-                       "\" usemap=#map$object_id />";
 
-               if($debug)
-               {
-                       echo "<pre>";
-                       echo $gvmap->export();
-                       echo "</pre>";
+       var les = _cy.$('[loopedge]');
 
-                       echo "<pre>".$gvmap->parse()."</pre>";
-               }
-       }
-       else
+       if(les)
        {
-               $gvmap->image($type);
+               _cy.batch( function() {
+                       les.each(function(i, ele) {
+                               var le = ele.data('loopedge');
+                               var edge = _cy.add(le);
+                               edge.addClass('loopedge');
+                               //edge.style('line-color', '#ffffff'); // TypeError text-transform undefined
+                       });     
+               });
+               _cy.$('edge[loop = "1"]').style('line-color','#ff6666');
        }
-
+}
+</script>
+</head>
+<body>
+<div id="cy" style="position: absolute; height: 80%; width: 100%; left: 0; top: 20%;"></div>
+<div id="cy2" style="position: absolute; height: 20%; width: 100%; left: 0; top: 0%;"></div>
+<div id="debug"></div>
+<button type="button" id="switch" style="position: absolute;" value="1">Object</button>
+</body>
+</html>
+HTMLEND
+); // echo
        exit;
-
-} /* linkmgmt_opmap */
+}
 
 /* ------------------------------------- */
 class linkmgmt_gvmap {
@@ -882,6 +2717,7 @@ class linkmgmt_gvmap {
        private $object_id = NULL;
        private $port_id = NULL;
        private $remote_id = NULL;
+       private $hl = NULL;
 
        private $gv = NULL;
 
@@ -894,12 +2730,57 @@ class linkmgmt_gvmap {
 
        private $errorlevel = NULL;
 
+       public $data = NULL;
+
+       private $pids = array();
+
+       function addlinkchainsobject($object_id)
+       {
+
+               $object['ports'] = getObjectPortsAndLinks ($object_id);
+
+               if(empty($object['ports']))
+               {
+
+                       $hl = false;
+                       $alpha = $this->alpha;
+                       if($this->hl == 'o' && $this->object_id == $object_id)
+                       {
+                               $hl = true;
+                               $this->alpha = 'ff';
+                       }
+
+                       $this->_addCluster($object_id, $hl, empty($object['ports']));
+
+                       $this->alpha = $alpha;
+                       return;
+               }
+
+               $i = 0;
+               foreach($object['ports'] as $key => $port)
+               {
+
+                       if(isset($this->pids[$port['id']]))
+                               continue;
+
+                       $i++;
+                       $lc = new lm_linkchain($port['id']);
+
+                       $this->pids += $lc->pids;
+
+                       if($this->allports ||($lc->linkcount > 0))
+                               $this->addlinkchain($lc, $i);
+               }
+
+       }
+
        function __construct($object_id = NULL, $port_id = NULL, $allports = false, $hl = NULL, $remote_id = NULL) {
                $this->allports = $allports;
 
                $this->object_id = $object_id;
                $this->port_id = $port_id;
                $this->remote_id = $remote_id;
+               $this->hl = $hl;
 
                $hllabel = "";
 
@@ -910,7 +2791,7 @@ class linkmgmt_gvmap {
                error_reporting($this->errorlevel & ~E_STRICT);
 
                $graphattr = array(
-                                       'rankdir' => 'RL',
+                                       'rankdir' => 'LR',
                                //      'ranksep' => '0',
                                        'nodesep' => '0',
                                //      'overlay' => false,
@@ -924,9 +2805,18 @@ class linkmgmt_gvmap {
 
                unset($_GET['all']);
 
+               switch($hl)
+               {
+                       case 'o':
+                       case 'p':
+                               $this->alpha = '30';
+                               break;
+               }
+
                //$this->gv = new Image_GraphViz(true, $graphattr, "map".$object_id);
                $this->gv = new lm_Image_GraphViz(true, $graphattr, "map".$object_id);
 
+               /* --------------------------- */
                if($object_id === NULL)
                {
                        /* all objects ! */
@@ -942,7 +2832,8 @@ class linkmgmt_gvmap {
                        $objects = listCells('object');
 
                        foreach($objects as $obj)
-                               $this->_add($this->gv, $obj['id'], NULL);
+                               //$this->addlinkchainsobject($obj['id']); // longer runtimes !!
+                               $this->_add($this->gv, $obj['id'], NULL); // for all still faster and nicer looking graph
 
                        return;
                }
@@ -956,34 +2847,14 @@ class linkmgmt_gvmap {
                                                )
                                );
 
-                       $this->_add($this->gv, $object_id, $port_id);
+                       $this->addlinkchainsobject($object_id);
+                       //$this->_add($this->gv, $object_id, $port_id);
 
                        $children = getEntityRelatives ('children', 'object', $object_id); //'entity_id'
 
                        foreach($children as $child)
-                               $this->_add($this->gv, $child['entity_id'], NULL);
-               }
-
-               switch($hl)
-               {
-                       case 'p':
-                       case 'port':
-                               $hllabel = " (Port highlight)";
-                               $this->alpha = '30';
-                               $this->_add($this->gv, $object_id, NULL);
-                               break;
-                       case 'o':
-                       case 'object':
-                               $hllabel = " (Object highlight)";
-                               $this->alpha = '30';
-                               /* all objects */
-                               $objects = listCells('object');
-
-                               foreach($objects as $obj)
-                                       $this->_add($this->gv, $obj['id'], NULL);
-
-                               break;
-
+                               $this->addlinkchainsobject($child['entity_id']);
+                       //      $this->_add($this->gv, $child['entity_id'], NULL);
                }
 
                /* add hl label */
@@ -992,6 +2863,9 @@ class linkmgmt_gvmap {
                                ));
 
        //      portlist::var_dump_html($this->gv);
+//             portlist::var_dump_html($this->data);
+
+//             echo json_encode($this->data);
 
        //      $this->gv->saveParsedGraph('/tmp/graph.txt');
        //      error_reporting( E_ALL ^ E_NOTICE);
@@ -1001,6 +2875,280 @@ class linkmgmt_gvmap {
                error_reporting($this->errorlevel);
        }
 
+       function _addCluster($object_id, $hl = false, $adddummy = false)
+       {
+                       global $lc_cache;
+
+                       $cluster_id = "c".$object_id;
+
+                       if(
+                               !isset($this->gv->graph['clusters'][$cluster_id]) &&
+                               !isset($this->gv->graph['subgraphs'][$cluster_id])
+                               || $hl
+                       ) {
+                               $rack = null;
+                               $object = $lc_cache->getobject($object_id, $rack);
+
+                       //      $object['attr'] = getAttrValues($object_id);
+
+                               $clusterattr = array();
+
+                               $this->_getcolor('cluster', 'default', $this->alpha, $clusterattr, 'color');
+                               $this->_getcolor('cluster', 'default', $this->alpha, $clusterattr, 'fontcolor');
+
+                               if($this->object_id == $object_id)
+                               {
+                                       $clusterattr['rank'] = 'source';
+
+                                       $this->_getcolor('cluster', 'current', $this->alpha, $clusterattr, 'color');
+                                       $this->_getcolor('cluster', 'current', $this->alpha, $clusterattr, 'fontcolor');
+                               }
+
+                               $clustertitle = htmlspecialchars($object['dname']);
+                               $clusterattr['tooltip'] = $clustertitle;
+
+                               unset($_GET['module']); // makeHrefProcess adds this
+                               unset($_GET['port_id']);
+                               unset($_GET['remote_id']);
+                               $_GET['object_id'] = $object_id;
+                               //$_GET['hl'] = 'o';
+
+                               $clusterattr['URL'] = $this->_makeHrefProcess($_GET);
+
+                               //has_problems
+                               if($object['has_problems'] != 'no')
+                               {
+                                       $clusterattr['style'] = 'filled';
+                                       $this->_getcolor('cluster', 'problem', $this->alpha, $clusterattr, 'fillcolor');
+                               }
+
+                               if(!empty($object['container_name']))
+                                       $clustertitle .= "<BR/>(${object['container_name']})";
+
+                               if(!empty($rack['row_name']) || !empty($rack['name']))
+                                       $clustertitle .= "<BR/><FONT point-size=\"10\">{$rack['row_name']} / {$rack['name']}</FONT>";
+
+                               $embedin = $object['container_id'];
+                               if(empty($embedin))
+                                       $embedin = 'default';
+                               else
+                               {
+                                       $embedin = "c$embedin"; /* see cluster_id */
+
+                                       // TODO
+                                       /* add container / cluster if not already exists */
+                                       $this->addlinkchainsobject($object['container_id']);
+                               }
+
+                               $clusterattr['id'] = "$object_id----"; /* used for js context menu */
+
+                               $this->gv->addCluster($cluster_id, $clustertitle, $clusterattr, $embedin);
+
+                               /* needed because of  gv_image empty cluster bug (invalid foreach argument) */
+                               if($adddummy)
+                                       $this->gv->addNode("dummy$cluster_id", array(
+                                       //      'label' =>'No Ports found/connected',
+                                               'label' =>'',
+                                               'fontsize' => 0,
+                                               'size' => 0,
+                                               'width' => 0,
+                                               'height' => 0,
+                                               'shape' => 'point',
+                                               'style' => 'invis',
+                                               ), $cluster_id);
+
+
+                       }
+        } // _addCluster
+
+       function _addEdge($port, $linkchain, $loopedge = false)
+       {
+               global $lm_multilink_port_types;
+               if(
+                       !isset($this->gv->graph['edgesFrom'][$port['id']][$port['remote_id']]) &&
+                       !isset($this->gv->graph['edgesFrom'][$port['remote_id']][$port['id']])
+                       || $loopedge
+               ) {
+                       $remote_id = $port['remote_id'];
+
+                       $linktype = $port['linktype'];
+
+                       $edgetooltip = $port['object_name'].':'.$port['name'].
+                                       ' - '.$port['cableid'].' -> '.
+                                       $port['remote_name'].':'.$port['remote_object_name'];
+
+                       $edgeattr = array(
+                                       'fontsize' => 8,
+                                       'label' => htmlspecialchars($port['cableid']),
+                                       'tooltip' => $edgetooltip,
+                                       'sametail' => $linktype,
+                                       'samehead' => $linktype,
+                                       'arrowhead' => 'none',
+                                       'arrowtail' => 'none',
+                               );
+
+                       $this->_getcolor('edge', ($linkchain->loop ? 'loop' : 'default'), $this->alpha, $edgeattr, 'color');
+                       $this->_getcolor('edge', 'default', $this->alpha, $edgeattr, 'fontcolor');
+
+                       if($linktype == 'back' )
+                       {
+                               $edgeattr['style'] =  'dashed';
+
+                               /* multilink ports */
+                               if(in_array($port['oif_id'], $lm_multilink_port_types))
+                               {
+                                       $edgeattr['dir'] = 'both';
+                                       $edgeattr['arrowtail'] = 'dot';
+                               }
+
+                               if(in_array($linkchain->ports[$remote_id]['oif_id'], $lm_multilink_port_types))
+                               {
+                                       $edgeattr['dir'] = 'both';
+                                       $edgeattr['arrowhead'] = 'dot';
+                               }
+                       }
+
+                       if(
+                               ($port['id'] == $this->port_id && $port['remote_id'] == $this->remote_id) ||
+                               ($port['id'] == $this->remote_id && $port['remote_id'] == $this->port_id)
+                       )
+                       {
+                               $this->_getcolor('edge', 'highlight', 'ff', $edgeattr, 'color');
+                               $edgeattr['penwidth'] = 2; /* bold */
+                       }
+
+                       unset($_GET['module']);
+                       $_GET['object_id'] = $port['object_id'];
+                       $_GET['port_id'] = $port['id'];
+                       $_GET['remote_id'] = $port['remote_id'];
+
+                       $edgeattr['URL'] = $this->_makeHrefProcess($_GET);
+
+                       $edgeattr['id'] = $port['object_id']."-".$port['id']."-".$port['remote_id']."-".$linktype; /* for js context menu  */
+
+                       if($loopedge)
+                       {
+                                       $edgeattr = array_merge($edgeattr, array(
+                                       'sametail' => 'loop',
+                                       'samehead' => 'loop',
+                                       'dir' => 'both',
+                                       'arrowhead' => 'invodot',
+                                       'arrowtail' => 'invodot',
+                                       ));
+                       }
+
+                       $this->gv->addEdge(array($port['id'] => $port['remote_id']),
+                                               $edgeattr,
+                                               array(
+                                                       $port['id'] => $linktype,
+                                                       $port['remote_id'] => $linktype,
+                                               )
+                                       );
+
+               }
+       } // _addEdge
+
+       function addlinkchain($linkchain, $index)
+       {
+               global $lm_multilink_port_types;
+
+               $hl = false;
+               $alpha = $this->alpha;
+               if(
+                       ($this->hl == 'p' && $linkchain->hasport_id($this->port_id))
+                       || ($this->hl == 'o' && $linkchain->hasobject_id($this->object_id))
+               )
+               {
+                       $hl = true;
+                       $this->alpha = 'ff';
+               }
+
+               $remote_id = null;
+               foreach($linkchain as $id => $port)
+               {
+                       $this->_addCluster($port['object_id'], $hl);
+
+                       $nodelabel = htmlspecialchars("${port['name']}");
+                       $text = $nodelabel;
+
+                       if($port['iif_id'] != '1' )
+                       {
+                               $nodelabel .= "<BR/><FONT POINT-SIZE=\"8\">${port['iif_name']}</FONT>";
+                               $text .= "\n".$port['iif_name'];
+                       }
+
+                       $nodelabel .= "<BR/><FONT POINT-SIZE=\"8\">${port['oif_name']}</FONT>";
+                       $text .= "\n".$port['oif_name'];
+
+                       // add ip address
+                       if(isset($port['portip']))
+                               $nodelabel .= "<BR/><FONT POINT-SIZE=\"8\">".$port['portip']."</FONT>";
+
+                       $nodeattr = array(
+                                       'label' => $nodelabel,
+                                       );
+
+                       $this->_getcolor('port', ($linkchain->loop ? 'loop' : 'default'),$this->alpha, $nodeattr, 'fontcolor');
+                       $this->_getcolor('oif_id', $port['oif_id'],$this->alpha, $nodeattr, 'color');
+
+                       if($this->port_id == $port['id']) {
+                               $nodeattr['style'] = 'filled';
+                               $nodeattr['fillcolor'] = $this->_getcolor('port', 'current', $this->alpha);
+                       }
+
+                       if($this->remote_id == $port['id']) {
+                               $nodeattr['style'] = 'filled';
+                               $nodeattr['fillcolor'] = $this->_getcolor('port', 'remote', $this->alpha);
+                       }
+
+                       $nodeattr['tooltip'] = htmlspecialchars("${port['name']}");
+
+                       unset($_GET['module']);
+                       unset($_GET['remote_id']);
+                       $_GET['object_id'] = $port['object_id'];
+                       $_GET['port_id'] = $port['id'];
+                       $_GET['hl'] = 'p';
+
+                       $nodeattr['URL'] = $this->_makeHrefProcess($_GET);
+                       $nodeattr['id'] = "${port['object_id']}-${port['id']}--"; /* for js context menu */
+
+                       $this->gv->addNode($port['id'],
+                                       $nodeattr,
+                                       "c${port['object_id']}"); /* see cluster_id */
+
+                       $remote_id = $port['remote_id'];
+
+                       if($port['portcount'] > 1)
+                               foreach($port['chains'] as $mlc)
+                               {
+                                       $this->addlinkchain($mlc, 0); // TODO index
+                               }
+
+                       $prevlinktype = ($port['linktype'] == 'front' ? 'back' : 'front');
+                       if($port[$prevlinktype]['portcount'] > 1)
+                       {
+                               foreach($port[$prevlinktype]['chains'] as $mlc)
+                               {
+                                       $this->addlinkchain($mlc, 0, false); // TODO index
+                               }
+                       }
+
+                       if($remote_id)
+                               $this->_addEdge($port, $linkchain);
+
+               } //foreach
+
+               if($linkchain->loop && $remote_id)
+               {
+                       // TODO separate loop link
+                       // add loop edge
+                       $this->_addEdge($port, $linkchain, true);
+               }
+
+               // reset alpha to start value
+               $this->alpha = $alpha;
+       }
+
        function setFalseOnError($newvalue)
        {
                $this->gv->_returnFalseOnError = $newvalue;
@@ -1057,6 +3205,7 @@ class linkmgmt_gvmap {
                                return;
                }
 
+               $object = NULL;
                if($object_id !== NULL) {
                        if(
                                !isset($gv->graph['clusters'][$cluster_id]) &&
@@ -1064,6 +3213,15 @@ class linkmgmt_gvmap {
                        ) {
 
                                $object = spotEntity ('object', $object_id);
+
+                               // ip addresses
+                               amplifyCell($object);
+                               $object['portip'] = array();
+                               foreach($object['ipv4'] as $ipv4)
+                               {
+                                       $object['portip'][$ipv4['osif']] = $ipv4['addrinfo']['ip'];
+                               }
+
                        //      $object['attr'] = getAttrValues($object_id);
 
                                $clusterattr = array();
@@ -1080,6 +3238,7 @@ class linkmgmt_gvmap {
                                }
 
                                $clustertitle = "${object['dname']}";
+                               $text = "${object['dname']}";
                                $clusterattr['tooltip'] = $clustertitle;
 
                                unset($_GET['module']); // makeHrefProcess adds this
@@ -1098,14 +3257,20 @@ class linkmgmt_gvmap {
                                }
 
                                if(!empty($object['container_name']))
+                               {
                                        $clustertitle .= "<BR/>${object['container_name']}";
+                                       $text .= "\n${object['container_name']}";
+                               }
 
                                if($object['rack_id'])
                                {
                                        $rack = spotEntity('rack', $object['rack_id']);
 
                                        if(!empty($rack['row_name']) || !empty($rack['name']))
+                                       {
                                                $clustertitle .= "<BR/>${rack['row_name']} / ${rack['name']}";
+                                               $text .= "\n${rack['row_name']} / ${rack['name']}";
+                                       }
                                }
 
                                $embedin = $object['container_id'];
@@ -1134,11 +3299,24 @@ class linkmgmt_gvmap {
 
 
                                $nodelabel = htmlspecialchars("${port['name']}");
+                               $text = $nodelabel;
 
                                if($port['iif_id'] != '1' )
+                               {
                                        $nodelabel .= "<BR/><FONT POINT-SIZE=\"8\">${port['iif_name']}</FONT>";
+                                       $text .= "\n".$port['iif_name'];
+                               }
 
                                $nodelabel .= "<BR/><FONT POINT-SIZE=\"8\">${port['oif_name']}</FONT>";
+                               $text .= "\n".$port['oif_name'];
+
+                               // add ip address
+                               if($object)
+                                       if(isset($object['portip'][$port['name']]))
+                                       {
+                                               $nodelabel .= "<BR/><FONT POINT-SIZE=\"8\">".$object['portip'][$port['name']]."</FONT>";
+                                               $text .= "\n".$object['portip'][$port['name']];
+                                       }
 
                                $nodeattr = array(
                                                        'label' => $nodelabel,
@@ -1248,6 +3426,7 @@ class linkmgmt_gvmap {
                                                                        $port['remote_id'] => $linktype,
                                                                )
                                                        );
+
                                }
                        }
 
@@ -1362,6 +3541,7 @@ class linkmgmt_gvmap {
                $port = array(
                                'current' => '#ffff90',
                                'remote' => '#ffffD0',
+                               'loop' => '#ff6666',
                                );
 
                $cluster = array(
@@ -1371,6 +3551,7 @@ class linkmgmt_gvmap {
 
                $edge = array (
                                'highlight' => '#ff0000',
+                               'loop' => '#ff6666',
                                );
 
                $oif_id = array(
@@ -1475,8 +3656,6 @@ function linkmgmt_opunlinkPort() {
        $port_id = $_REQUEST['port_id'];
        $linktype = $_REQUEST['linktype'];
 
-       portlist::var_dump_html($_REQUEST);
-
        /* check permissions */
        if(!permitted(NULL, NULL, 'set_link')) {
                exit;
@@ -1494,26 +3673,18 @@ function linkmgmt_opunlinkPort() {
                                        $port_id, $remote_id,
                                        $port_id, $remote_id)
                        );
+
+               if($retval == 0)
+                       showWarning("Link not found");
+               else
+                       showSuccess("Backend Link deleted");
+
        }
        else
        {
-               $table = 'Link';
-
-               $retval = usePreparedDeleteBlade ($table, array('porta' => $port_id, 'portb' => $port_id), 'OR');
+               // RT function for normal links
+               unlinkPort();
        }
-
-       if($retval == 0)
-               echo " Link not found";
-       else
-               echo " $retval Links deleted";
-
-
-       unset($_GET['module']);
-       unset($_GET['op']);
-
-       header('Location: ?'.http_build_query($_GET));
-       //header('Location: ?page='.$_REQUEST['page'].'&tab='.$_REQUEST['tab'].'&object_id='.$_REQUEST['object_id']);
-       exit;
 } /* opunlinkPort */
 
 /* -------------------------------------------------- */
@@ -2175,11 +4346,12 @@ function linkmgmt_tabhandler($object_id) {
        //$parents = getEntityRelatives ('parents', 'object', $object_id);
        $children = getEntityRelatives ('children', 'object', $object_id); //'entity_id'
 
-       //portlist::var_dump_html($children);
-
        foreach($children as $child) {
-               echo '<h1>Links for Child: '.$child['name'].'</h1>';
+               $childobj = spotEntity($child['entity_type'], $child['entity_id']);
+
+               echo '<h1>Links for Child: '.$childobj['name'].'</h1>';
                linkmgmt_renderObjectLinks($child['entity_id']);
+               unset($childobj);
        }
 
        return;
@@ -2238,6 +4410,10 @@ function linkmgmt_renderObjectLinks($object_id) {
        echo '<td width=100><span onclick=window.open("'.makeHrefProcess(portlist::urlparamsarray(
                                 array('op' => 'map','usemap' => 1))).'","name","height=800,width=800,scrollbars=yes");><a>Object Map</a></span></td>';
 
+       /* cytoscape map */
+       echo '<td width=100><span onclick=window.open("'.makeHrefProcess(portlist::urlparamsarray(
+                                array('op' => 'cytoscapemap'))).'","name","height=800,width=800,scrollbars=yes");><a>Cytoscape Object Map</a></span></td>';
+
        /* Help */
        echo '<td width=200><span onclick=window.open("'.makeHrefProcess(portlist::urlparamsarray(
                                 array('op' => 'Help'))).'","name","height=400,width=500");><a>Help</a></span></td>';
@@ -2255,16 +4431,23 @@ function linkmgmt_renderObjectLinks($object_id) {
        /*  switch display order depending on backend links */
        $first = portlist::hasbackend($object_id);
 
+
        $rowcount = 0;
+
        foreach($ports as $key => $port) {
 
-               $plist = new portlist($port, $object_id, $allports, $allback);
+               $lc = new lm_linkchain($port['id']);
 
-               //echo "<td><img src=\"index.php?module=redirect&page=object&tab=linkmgmt&op=map&object_id=$object_id&port_id=${port['id']}&allports=$allports\" ></td>";
+               if($allports || $lc->linkcount > 0)
+               {
+                       if($port['id'] == $hl_port_id)
+                               $rowbgcolor = lm_linkchain::HL_PORT_BGCOLOR;
+                       else
+                               $rowbgcolor = ($rowcount % 2 ? lm_linkchain::ALTERNATE_ROW_BGCOLOR : "#ffffff");
 
-               if($plist->printportlistrow($first, $hl_port_id, ($rowcount % 2 ? portlist::ALTERNATE_ROW_BGCOLOR : "#ffffff")) )
+                       echo $lc->getchainlabeltrstart($rowbgcolor).$lc->getchainrow($allback, $rowbgcolor)."</tr>";
                        $rowcount++;
-
+               }
        }
 
        echo "</table>";
@@ -2490,7 +4673,9 @@ class portlist {
        //      $this->var_dump_html($retval);
 
                /* return reference */
-               return ($this->list[$port_id] = &$retval);
+               $this->list[$port_id] = &$retval;
+
+               return $retval;
 
        } /* _getportdata */
 
@@ -2878,7 +5063,7 @@ class portlist {
                if($linktype != 'front' && !$lm_cache['allowbacklink'])
                        return;
 
-                echo "<td align=center>";
+               echo "<td align=center>";
 
                echo $this->_getlinkportsymbol($port_id, $linktype);