Added slices and instances to tree layout
diff --git a/gui/ngXosViews/serviceTopology/src/css/serviceTopology.css b/gui/ngXosViews/serviceTopology/src/css/serviceTopology.css
index 2afcf08..b241361 100644
--- a/gui/ngXosViews/serviceTopology/src/css/serviceTopology.css
+++ b/gui/ngXosViews/serviceTopology/src/css/serviceTopology.css
@@ -4,6 +4,11 @@
     display: block;
 }
 
+service-canvas svg {
+    position: absolute;
+    top: 0;
+}
+
 .node {
     cursor: pointer;
 }
@@ -19,6 +24,14 @@
     stroke: #ffdb07;
 }
 
+.node.slice circle {
+    stroke: #b01dff;
+}
+
+.node.instance circle {
+    stroke: #ea25ff;
+}
+
 .node rect.slice-detail{
     fill: #fff;
     stroke: steelblue;
diff --git a/gui/ngXosViews/serviceTopology/src/js/config.js b/gui/ngXosViews/serviceTopology/src/js/config.js
index b5c0db0..3ef8748 100644
--- a/gui/ngXosViews/serviceTopology/src/js/config.js
+++ b/gui/ngXosViews/serviceTopology/src/js/config.js
@@ -4,7 +4,8 @@
   angular.module('xos.serviceTopology')
   .constant('serviceTopologyConfig', {
     widthMargin: 100,
-    heightMargin: 30
+    heightMargin: 30,
+    duration: 750
   })
 
 }());
\ No newline at end of file
diff --git a/gui/ngXosViews/serviceTopology/src/js/d3.js b/gui/ngXosViews/serviceTopology/src/js/d3.js
index 52127a4..9452e5f 100644
--- a/gui/ngXosViews/serviceTopology/src/js/d3.js
+++ b/gui/ngXosViews/serviceTopology/src/js/d3.js
@@ -5,10 +5,214 @@
     .factory('d3', function($window){
       return $window.d3;
     })
-  .service('TreeLayout', function(){
-    this.updateTree = (source) => {
+  .service('TreeLayout', function($window, lodash, ServiceRelation, serviceTopologyConfig, Slice, Instances){
 
-    }
+    var _svg, _layout, _source;
+
+    var i = 0;
+
+    // given a canvas, a layout and a data source, draw a tree layout
+    const updateTree = (svg, layout, source) => {
+
+      //cache data
+      _svg = svg;
+      _layout = layout;
+      _source = source;
+
+      const maxDepth = ServiceRelation.depthOf(source);
+
+      const diagonal = d3.svg.diagonal()
+        .projection(d => [d.y, d.x]);
+
+      // Compute the new tree layout.
+      var nodes = layout.nodes(source).reverse(),
+        links = layout.links(nodes);
+
+      // Normalize for fixed-depth.
+      nodes.forEach(function(d) {
+        // position the child node horizontally
+        d.y = d.depth * (($window.innerWidth - (serviceTopologyConfig.widthMargin * 2)) / maxDepth);
+        console.log(d.id);
+        if(d.type == 'slice'){
+          console.info('slice found!', d);
+        }
+      });
+
+      // Update the nodes…
+      var node = svg.selectAll('g.node')
+        .data(nodes, function(d) { return d.id || (d.id = ++i); });
+
+      // Enter any new nodes at the parent's previous position.
+      var nodeEnter = node.enter().append('g')
+        .attr({
+          class: d => `node ${d.type}`,
+          transform: d => `translate(${source.y0},${source.x0})` // this is the starting position
+        });
+
+      nodeEnter.append('circle')
+        .attr('r', 1e-6)
+        .style('fill', d => d._children ? 'lightsteelblue' : '#fff')
+        .on('click', serviceClick);
+
+      nodeEnter.append('text')
+        .attr('x', function(d) { return d.children || d._children ? -13 : 13; })
+        .attr('transform', function(d) {
+          if((d.children || d._children) && d.parent || d._parent){
+            return 'rotate(30)';
+          }
+        })
+        .attr('dy', '.35em')
+        .attr('text-anchor', function(d) { return d.children || d._children ? 'end' : 'start'; })
+        .text(function(d) { return d.name; })
+        .style('fill-opacity', 1e-6);
+
+      // Transition nodes to their new position.
+      var nodeUpdate = node.transition()
+        .duration(serviceTopologyConfig.duration)
+        .attr('transform', function(d) {
+          return 'translate(' + d.y + ',' + d.x + ')';
+        });
+
+      nodeUpdate.select('circle')
+        .attr('r', d => d.selected ? 15 : 10)
+        .style('fill', function(d) { return d._children ? 'lightsteelblue' : '#fff'; });
+
+      nodeUpdate.select('text')
+        .style('fill-opacity', 1);
+
+      // Transition exiting nodes to the parent's new position.
+      var nodeExit = node.exit().transition()
+        .duration(serviceTopologyConfig.duration)
+        .attr('transform', function(d) { return 'translate(' + source.y + ',' + source.x + ')'; })
+        .remove();
+
+      nodeExit.select('circle')
+        .attr('r', 1e-6);
+
+      nodeExit.select('text')
+        .style('fill-opacity', 1e-6);
+
+      // Update the links…
+      var link = svg.selectAll('path.link')
+        .data(links, function(d) { return d.target.id; });
+
+      // Enter any new links at the parent's previous position.
+      link.enter().insert('path', 'g')
+        .attr('class', 'link')
+        .attr('d', function(d) {
+          var o = {x: source.x0, y: source.y0};
+          return diagonal({source: o, target: o});
+        });
+
+      // Transition links to their new position.
+      link.transition()
+        .duration(serviceTopologyConfig.duration)
+        .attr('d', diagonal);
+
+      // Transition exiting nodes to the parent's new position.
+      link.exit().transition()
+        .duration(serviceTopologyConfig.duration)
+        .attr('d', function(d) {
+          var o = {x: source.x, y: source.y};
+          return diagonal({source: o, target: o});
+        })
+        .remove();
+
+      // Stash the old positions for transition.
+      nodes.forEach(function(d) {
+        d.x0 = d.x;
+        d.y0 = d.y;
+      });
+    };
+
+    const serviceClick = function(d) {
+
+      // empty panel
+      //_this.slices = [];
+      //_this.instances = [];
+
+      // reset all the nodes to default radius
+      var nodes = d3.selectAll('circle')
+        .transition()
+        .duration(serviceTopologyConfig.duration)
+        .attr('r', 10);
+
+      // remove slices details
+      d3.selectAll('rect.slice-detail')
+        .remove();
+      d3.selectAll('text.slice-name')
+        .remove();
+
+      var selectedNode = d3.select(this);
+
+      selectedNode
+        .transition()
+        .duration(serviceTopologyConfig.duration)
+        .attr('r', 15);
+
+      if(!d.service){
+        return;
+      }
+
+      //_this.selectedService = {
+      //  id: d.id,
+      //  name: d.name
+      //};
+
+      ServiceRelation.getServiceInterfaces(d.service.id)
+        .then(interfaceTree => {
+
+          const isDetailed = lodash.find(d.children, {type: 'slice'});
+          if(isDetailed){
+            d.selected = false;
+            lodash.remove(d.children, {type: 'slice'});
+          }
+          else {
+            d.selected = true;
+
+            d.children = d.children.concat(interfaceTree);
+          }
+
+          updateTree(_svg, _layout, _source);
+            // draw a rect with slice names
+            //const parentNode = d3.select(this.parentNode);
+            //parentNode
+            //  .append('rect')
+            //  .style('opacity', 0)
+            //  .attr({
+            //    width: 150,
+            //    height: 50,
+            //    y: 35,
+            //    x: -75,
+            //    class: 'slice-detail'
+            //  })
+            //  .transition()
+            //  .duration(serviceTopologyConfig.duration)
+            //  .style('opacity', 1);
+            // TODO attach a click listener to draw instances and networks
+
+            //parentNode
+            //  .append('text')
+            //  .style('opacity', 0)
+            //  .attr({
+            //    y: 65,
+            //    x: -60,
+            //    class: 'slice-name'
+            //  })
+            //  .text(() => {
+            //    if(slices[0]){
+            //      return slices[0].humanReadableName;
+            //    }
+            //
+            //    return '';
+            //  })
+            //  .transition()
+            //  .duration(serviceTopologyConfig.duration)
+            //  .style('opacity', 1);
+        });
+    };
+
+    this.updateTree = updateTree;
   });
 
 }());
\ No newline at end of file
diff --git a/gui/ngXosViews/serviceTopology/src/js/main.js b/gui/ngXosViews/serviceTopology/src/js/main.js
index 5fe1896..4e6239a 100644
--- a/gui/ngXosViews/serviceTopology/src/js/main.js
+++ b/gui/ngXosViews/serviceTopology/src/js/main.js
@@ -17,7 +17,4 @@
 })
 .config(function($httpProvider){
   $httpProvider.interceptors.push('NoHyperlinks');
-})
-.factory('_', function($window){
-  return window._;
 });
diff --git a/gui/ngXosViews/serviceTopology/src/js/services.js b/gui/ngXosViews/serviceTopology/src/js/services.js
index e7c6e3f..55eac9c 100644
--- a/gui/ngXosViews/serviceTopology/src/js/services.js
+++ b/gui/ngXosViews/serviceTopology/src/js/services.js
@@ -17,7 +17,7 @@
   .service('Subscribers', function($resource){
     return $resource('/xos/subscribers', {id: '@id'});
   })
-  .service('ServiceRelation', function($q, _, lodash, Services, Tenant){
+  .service('ServiceRelation', function($q, lodash, Services, Tenant, Slice, Instances){
 
     // count the mas depth of an object
     const depthOf = (obj) => {
@@ -128,6 +128,49 @@
       return deferred.promise;
     };
 
+    const getServiceInterfaces = (serviceId) => {
+      var deferred = $q.defer();
+
+      var _slices;
+
+      Slice.query({service: serviceId}).$promise
+      .then((slices) => {
+        _slices = slices;
+        const promisesArr = slices.reduce((promises, slice) => {
+          promises.push(Instances.query({slice: slice.id}).$promise);
+          return promises;
+        }, []);
+
+        return $q.all(promisesArr);
+      })
+      .then((instances) => {
+        console.log(instances);
+        // parse data to build a tree (2 level only)
+        var interfaceTree = [];
+        lodash.forEach(_slices, (slice, i) => {
+          let current = {
+            name: slice.name,
+            slice: slice,
+            type: 'slice',
+            children: instances[i].map((instance) => {
+              return {
+                name: instance.humanReadableName,
+                children: [],
+                type: 'instance',
+                instance: instance
+              };
+
+            })
+          };
+          interfaceTree.push(current);
+        });
+        console.log(interfaceTree)
+        deferred.resolve(interfaceTree);
+      });
+
+      return deferred.promise;
+    };
+
     // export APIs
     this.get = get;
     this.buildLevel = buildLevel;
@@ -135,6 +178,7 @@
     this.findLevelRelation = findLevelRelation;
     this.findLevelServices = findLevelServices;
     this.depthOf = depthOf;
+    this.getServiceInterfaces = getServiceInterfaces;
   });
 
 }());
\ No newline at end of file
diff --git a/gui/ngXosViews/serviceTopology/src/js/topologyCanvas.js b/gui/ngXosViews/serviceTopology/src/js/topologyCanvas.js
index 008eaa3..b53d29f 100644
--- a/gui/ngXosViews/serviceTopology/src/js/topologyCanvas.js
+++ b/gui/ngXosViews/serviceTopology/src/js/topologyCanvas.js
@@ -9,7 +9,7 @@
       bindToController: true,
       controllerAs: 'vm',
       templateUrl: 'templates/topology_canvas.tpl.html',
-      controller: function($element, $window, d3, serviceTopologyConfig, ServiceRelation, Slice, Instances, Subscribers){
+      controller: function($element, $window, d3, serviceTopologyConfig, ServiceRelation, Slice, Instances, Subscribers, TreeLayout){
 
         this.instances = [];
         this.slices = [];
@@ -17,18 +17,15 @@
         const width = $window.innerWidth - serviceTopologyConfig.widthMargin;
         const height = $window.innerHeight - serviceTopologyConfig.heightMargin;
 
-        const tree = d3.layout.tree()
+        const treeLayout = d3.layout.tree()
           .size([height, width]);
 
-        const diagonal = d3.svg.diagonal()
-          .projection(d => [d.y, d.x]);
-
         const svg = d3.select($element[0])
           .append('svg')
           .style('width', `${$window.innerWidth}px`)
           .style('height', `${$window.innerHeight}px`)
           .append('g')
-          .attr('transform', 'translate(' + serviceTopologyConfig.widthMargin+ '','' + serviceTopologyConfig.heightMargin + '')'');;
+          .attr('transform', 'translate(' + serviceTopologyConfig.widthMargin+ ',' + serviceTopologyConfig.heightMargin + ')');
 
         //const resizeCanvas = () => {
         //  var targetSize = svg.node().getBoundingClientRect();
@@ -47,199 +44,16 @@
         //    update(root);
         //  });
         var root;
-        var i = 0;
-        var duration = 750;
 
         const draw = (tree) => {
           root = tree;
           root.x0 = $window.innerHeight / 2;
           root.y0 = 0;
 
-          update(root);
+          TreeLayout.updateTree(svg, treeLayout, root);
         };
 
-        function update(source) {
-
-          const maxDepth = depthOf(source);
-
-          // Compute the new tree layout.
-          var nodes = tree.nodes(root).reverse(),
-            links = tree.links(nodes);
-
-          // Normalize for fixed-depth.
-          nodes.forEach(function(d) {
-            // position the child node horizontally
-            d.y = d.depth * (($window.innerWidth - (serviceTopologyConfig.widthMargin * 2)) / maxDepth);
-          });
-
-          // Update the nodes…
-          var node = svg.selectAll('g.node')
-            .data(nodes, function(d) { return d.id || (d.id = ++i); });
-
-          // Enter any new nodes at the parent's previous position.
-          var nodeEnter = node.enter().append('g')
-            .attr({
-              class: d => `node ${d.type}`
-            })
-            .attr('transform', function(d) {
-              // this is the starting position
-              return 'translate(' + source.y0 + ',' + source.x0 + ')';
-            });
-
-          nodeEnter.append('circle')
-            .attr('r', 1e-6)
-            .style('fill', function(d) { return d._children ? 'lightsteelblue' : '#fff'; })
-            .on('click', click);
-
-          nodeEnter.append('text')
-            .attr('x', function(d) { return d.children || d._children ? -13 : 13; })
-            .attr('transform', function(d) {
-              if((d.children || d._children) && d.parent || d._parent){
-                return 'rotate(30)';
-              }
-            })
-            .attr('dy', '.35em')
-            .attr('text-anchor', function(d) { return d.children || d._children ? 'end' : 'start'; })
-            .text(function(d) { return d.name; })
-            .style('fill-opacity', 1e-6);
-
-          // Transition nodes to their new position.
-          var nodeUpdate = node.transition()
-            .duration(duration)
-            .attr('transform', function(d) {
-              return 'translate(' + d.y + ',' + d.x + ')';
-            });
-
-          nodeUpdate.select('circle')
-            .attr('r', 10)
-            .style('fill', function(d) { return d._children ? 'lightsteelblue' : '#fff'; });
-
-          nodeUpdate.select('text')
-            .style('fill-opacity', 1);
-
-          // Transition exiting nodes to the parent's new position.
-          var nodeExit = node.exit().transition()
-            .duration(duration)
-            .attr('transform', function(d) { return 'translate(' + source.y + ',' + source.x + ')'; })
-            .remove();
-
-          nodeExit.select('circle')
-            .attr('r', 1e-6);
-
-          nodeExit.select('text')
-            .style('fill-opacity', 1e-6);
-
-          // Update the links…
-          var link = svg.selectAll('path.link')
-            .data(links, function(d) { return d.target.id; });
-
-          // Enter any new links at the parent's previous position.
-          link.enter().insert('path', 'g')
-            .attr('class', 'link')
-            .attr('d', function(d) {
-              var o = {x: source.x0, y: source.y0};
-              return diagonal({source: o, target: o});
-            });
-
-          // Transition links to their new position.
-          link.transition()
-            .duration(duration)
-            .attr('d', diagonal);
-
-          // Transition exiting nodes to the parent's new position.
-          link.exit().transition()
-            .duration(duration)
-            .attr('d', function(d) {
-              var o = {x: source.x, y: source.y};
-              return diagonal({source: o, target: o});
-            })
-            .remove();
-
-          // Stash the old positions for transition.
-          nodes.forEach(function(d) {
-            d.x0 = d.x;
-            d.y0 = d.y;
-          });
-        }
-
         var _this = this;
-        const click = function(d) {
-
-          // empty panel
-          _this.slices = [];
-          _this.instances = [];
-
-          var nodes = d3.selectAll('circle')
-            .transition()
-            .duration(duration)
-            .attr('r', 10);
-
-          d3.selectAll('rect.slice-detail')
-            .remove();
-
-          d3.selectAll('text.slice-name')
-            .remove();
-
-          var selectedNode = d3.select(this);
-
-          selectedNode
-            .transition()
-            .duration(duration)
-            .attr('r', 15);
-
-          if(!d.service){
-            return;
-          }
-
-          _this.selectedService = {
-            id: d.id,
-            name: d.name
-          };
-
-          Slice.query({service: d.service.id}).$promise
-          .then(slices => {
-            _this.slices = slices;
-
-            if(slices.length > 0){
-              // TODO slice can be more than 1 create a for loop
-              const parentNode = d3.select(this.parentNode);
-
-              parentNode
-                .append('rect')
-                .style('opacity', 0)
-                .attr({
-                  width: 150,
-                  height: 50,
-                  y: 35,
-                  x: -75,
-                  class: 'slice-detail'
-                })
-                .transition()
-                .duration(duration)
-                .style('opacity', 1);
-                // TODO attach a click listener to draw instances and networks
-
-              parentNode
-                .append('text')
-                .style('opacity', 0)
-                .attr({
-                  y: 65,
-                  x: -60,
-                  class: 'slice-name'
-                })
-                .text(() => {
-                  if(slices[0]){
-                    return slices[0].humanReadableName;
-                  }
-
-                  return '';
-                })
-                .transition()
-                .duration(duration)
-                .style('opacity', 1);
-            }
-          })
-        };
 
         Subscribers.query().$promise
         .then((subscribers) => {
@@ -258,6 +72,7 @@
           })
         };
 
+        // redraw when subrsbiber change
         this.getServiceChain = (subscriber) => {
           ServiceRelation.get(subscriber)
             .then((tree) => {
diff --git a/gui/ngXosViews/serviceTopology/src/templates/topology_canvas.tpl.html b/gui/ngXosViews/serviceTopology/src/templates/topology_canvas.tpl.html
index 5336ddc..35857a3 100644
--- a/gui/ngXosViews/serviceTopology/src/templates/topology_canvas.tpl.html
+++ b/gui/ngXosViews/serviceTopology/src/templates/topology_canvas.tpl.html
@@ -1,30 +1,30 @@
 <select class="form-control" ng-options="s as s.name for s in vm.subscribers" ng-model="vm.selectedSubscriber" ng-change="vm.getServiceChain(vm.selectedSubscriber)">
     <option value="">Select a subscriber...</option>
 </select>
-<div class="service-details">
-    <div class="service-slices animate" ng-hide="vm.slices.length == 0">
-        <div class="panel panel-info">
-            <div class="panel-heading">
-                Slices for service:
-                <h3 class="panel-title">{{vm.selectedService.name}}</h3>
-            </div>
-            <ul class="list-group">
-                <li class="list-group-item" ng-repeat="slice in vm.slices" ng-click="vm.getInstances(slice)" ng-class="{active: slice.id === vm.selectedSlice.id}">
-                    {{slice.humanReadableName}}
-                </li>
-            </ul>
-        </div>
-    </div>
-    <div class="service-instances animate" ng-hide="vm.instances.length == 0">
-        <div class="panel panel-warning">
-            <div class="panel-heading">
-                Instances:
-            </div>
-            <ul class="list-group">
-                <li class="list-group-item" ng-repeat="instance in vm.instances">
-                    {{instance.humanReadableName}}
-                </li>
-            </ul>
-        </div>
-    </div>
-</div>
\ No newline at end of file
+<!--<div class="service-details">-->
+    <!--<div class="service-slices animate" ng-hide="vm.slices.length == 0">-->
+        <!--<div class="panel panel-info">-->
+            <!--<div class="panel-heading">-->
+                <!--Slices for service:-->
+                <!--<h3 class="panel-title">{{vm.selectedService.name}}</h3>-->
+            <!--</div>-->
+            <!--<ul class="list-group">-->
+                <!--<li class="list-group-item" ng-repeat="slice in vm.slices" ng-click="vm.getInstances(slice)" ng-class="{active: slice.id === vm.selectedSlice.id}">-->
+                    <!--{{slice.humanReadableName}}-->
+                <!--</li>-->
+            <!--</ul>-->
+        <!--</div>-->
+    <!--</div>-->
+    <!--<div class="service-instances animate" ng-hide="vm.instances.length == 0">-->
+        <!--<div class="panel panel-warning">-->
+            <!--<div class="panel-heading">-->
+                <!--Instances:-->
+            <!--</div>-->
+            <!--<ul class="list-group">-->
+                <!--<li class="list-group-item" ng-repeat="instance in vm.instances">-->
+                    <!--{{instance.humanReadableName}}-->
+                <!--</li>-->
+            <!--</ul>-->
+        <!--</div>-->
+    <!--</div>-->
+<!--</div>-->
\ No newline at end of file