Refactored topology to fit diagnostic
diff --git a/views/ngXosViews/diagnostic/src/js/d3.js b/views/ngXosViews/diagnostic/src/js/d3.js
new file mode 100644
index 0000000..027f08d
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/d3.js
@@ -0,0 +1,261 @@
+(function () {
+ 'use strict';
+
+ angular.module('xos.serviceTopology')
+ .factory('d3', function($window){
+ return $window.d3;
+ })
+ .service('TreeLayout', function($window, $log, lodash, ServiceRelation, serviceTopologyConfig){
+
+ const drawLegend = (svg) => {
+ const legendContainer = svg.append('g')
+ .attr({
+ class: 'legend'
+ });
+
+ legendContainer.append('rect')
+ .attr({
+ transform: d => `translate(10, 80)`,
+ width: 100,
+ height: 100
+ });
+
+ // service
+ const service = legendContainer.append('g')
+ .attr({
+ class: 'node service'
+ });
+
+ service.append('circle')
+ .attr({
+ r: serviceTopologyConfig.circle.radius,
+ transform: d => `translate(30, 100)`
+ });
+
+ service.append('text')
+ .attr({
+ transform: d => `translate(45, 100)`,
+ dy: '.35em'
+ })
+ .text('Service')
+ .style('fill-opacity', 1);
+
+ // slice
+ const slice = legendContainer.append('g')
+ .attr({
+ class: 'node slice'
+ });
+
+ slice.append('rect')
+ .attr({
+ width: 20,
+ height: 20,
+ x: -10,
+ y: -10,
+ transform: d => `translate(30, 130)`
+ });
+
+ slice.append('text')
+ .attr({
+ transform: d => `translate(45, 130)`,
+ dy: '.35em'
+ })
+ .text('Slices')
+ .style('fill-opacity', 1);
+
+ // instance
+ const instance = legendContainer.append('g')
+ .attr({
+ class: 'node instance'
+ });
+
+ instance.append('rect')
+ .attr({
+ width: 20,
+ height: 20,
+ x: -10,
+ y: -10,
+ transform: d => `translate(30, 160)`
+ });
+
+ instance.append('text')
+ .attr({
+ transform: d => `translate(45, 160)`,
+ dy: '.35em'
+ })
+ .text('Instances')
+ .style('fill-opacity', 1);
+ };
+
+ 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
+ const step = (($window.innerWidth - (serviceTopologyConfig.widthMargin * 2)) / maxDepth);
+ d.y = d.depth * step;
+ });
+
+ // 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: `translate(${source.y0}, ${source.x0})`
+ });
+
+ const subscriberNodes = nodeEnter.filter('.subscriber');
+ const internetNodes = nodeEnter.filter('.internet');
+ const serviceNodes = nodeEnter.filter('.service');
+ const instanceNodes = nodeEnter.filter('.instance');
+ const sliceNodes = nodeEnter.filter('.slice');
+
+ subscriberNodes.append('rect')
+ .attr(serviceTopologyConfig.square);
+
+ internetNodes.append('rect')
+ .attr(serviceTopologyConfig.square);
+
+ serviceNodes.append('circle')
+ .attr('r', 1e-6)
+ .style('fill', d => d._children ? 'lightsteelblue' : '#fff')
+ .on('click', serviceClick);
+
+ sliceNodes.append('rect')
+ .attr({
+ width: 20,
+ height: 20,
+ x: -10,
+ y: -10
+ });
+
+ instanceNodes.append('rect')
+ .attr({
+ width: 20,
+ height: 20,
+ x: -10,
+ y: -10,
+ class: d => d.active ?'' : 'active'
+ });
+
+ nodeEnter.append('text')
+ .attr({
+ x: d => d.children ? -serviceTopologyConfig.circle.selectedRadius -3 : serviceTopologyConfig.circle.selectedRadius + 3,
+ dy: '.35em',
+ transform: d => {
+ if (d.children && d.parent){
+ if(d.parent.x < d.x){
+ return 'rotate(-30)';
+ }
+ return 'rotate(30)';
+ }
+ },
+ 'text-anchor': d => d.children ? 'end' : 'start'
+ })
+ .text(d => d.name)
+ .style('fill-opacity', 1e-6);
+
+ // Transition nodes to their new position.
+ var nodeUpdate = node.transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ 'transform': d => `translate(${d.y},${d.x})`
+ });
+
+ nodeUpdate.select('circle')
+ .attr('r', d => d.selected ? serviceTopologyConfig.circle.selectedRadius : serviceTopologyConfig.circle.radius)
+ .style('fill', d => d.selected ? '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)
+ .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', d => `link ${d.target.type} ${d.target.active ? '' : 'active'}`)
+ .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) {
+
+ $log.info('TODO emit an event to highlight VMs');
+
+ if(!d.service){
+ return;
+ }
+
+ // toggling selected status
+ d.selected = !d.selected;
+
+ var selectedNode = d3.select(this);
+
+ selectedNode
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr('r', serviceTopologyConfig.circle.selectedRadius);
+ };
+
+ this.updateTree = updateTree;
+ this.drawLegend = drawLegend;
+ });
+
+}());
\ No newline at end of file