blob: 2318b48e854665f4c356e61de1a9ee4a18695c79 [file] [log] [blame]
/**
* © OpenCORD
*
* Visit http://guide.xosproject.org/devguide/addview/ for more information
*
* Created by teone on 6/28/16.
*/
(function () {
'use strict';
angular.module('xos.ecordTopology')
.directive('serviceMap', function(){
return {
restrict: 'E',
scope: {
unis: '=',
services: '='
},
bindToController: true,
controllerAs: 'vm',
template: '',
controller: function($element, $scope, $rootScope, $timeout, $log, cordIcons){
const el = $element[0];
const layout = d3.layout.tree();
let duration = 500;
let margin = 40;
// count the mas depth of an object
const depthOf = (obj) => {
var depth = 0;
if (obj.children) {
obj.children.forEach(function (d) {
var tmpDepth = depthOf(d);
if (tmpDepth > depth) {
depth = tmpDepth
}
})
}
return 1 + depth
};
// create svg elements
const svg = d3.select(el)
.append('svg')
.style('width', `${el.clientWidth}px`)
.style('height', `${el.clientHeight}px`)
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
let i = 0;
const update = (tree) => {
let maxDepth = depthOf(tree);
layout
.size([el.clientHeight, el.clientWidth]);
var nodes = layout.nodes(tree);
var links = layout.links(nodes);
const step = ((el.clientWidth - margin) / (maxDepth - 1));
// Normalize for fixed-depth.
nodes.forEach(function(d, i) {
if(i === 0){
d.y = margin;
}
else{
d.y = d.depth * step;
}
});
// Update the nodes…
var node = svg.selectAll('g.node')
.data(nodes, (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}`,
id: d => d.id
})
.attr('transform', () => `translate(${el.clientWidth / 2}, 50)`);
nodeEnter.append('rect')
.attr({
class: d => d.type,
width: 24,
height: 24,
x: -12,
y: -12
});
// unis
nodeEnter.filter('.uni')
.append('path')
.attr({
d: cordIcons.cordLogo,
transform: 'translate(-10, -10),scale(0.18)'
});
// services
nodeEnter.filter('.service')
.append('path')
.attr({
d: cordIcons.service,
transform: 'translate(-12, -12)'
});
nodeEnter.append('text')
.attr({
'text-anchor': 'middle',
x: 0,
y: 25
})
.text(d => {
return d.name
});
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.remove();
nodeExit.select('circle')
.attr('r', 1e-6);
nodeExit.select('text')
.style('fill-opacity', 1e-6);
var nodeUpdate = node.transition()
.duration(duration)
.attr('transform', (d) => `translate(${d.y},${d.x})`);
// 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: d.source.x, y: d.source.y};
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: d.source.x, y: d.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;
});
};
// format uni in the tree layout shape
this.formatUni = (unis) => {
return unis.reduce((list, item, i) => {
list.push({
name: item.scaEthFppUniN.name,
children: [],
id: `uni-${i}`
});
return list;
}, [])
return unis;
}
// add active services
this.addServices = (services, unis) => {
let list = [unis[0], ...services, unis[1]];
const addChildR = (base, list) => {
if(list.length === 0){
return [];
}
let el = list.shift();
let n = {
name: el.name || el.label,
type: el.name ? 'uni':'service',
children: addChildR(el, list),
id: el.id
};
return [n];
};
let tree = addChildR({}, list);
return tree[0];
};
$scope.$watch(() => this.services, (s) => {
if(s && this.unis){
update(this.addServices(s, this.formatUni(this.unis)))
}
}, true)
}
};
});
})();