<!DOCTYPE html>
<html>
<head>
<script type=
"text/javascript"
src=
"http://mbostock.github.com/d3/d3.v2.js?2.9.1"
></script>
<style type=
"text/css"
>
.link { stroke: green; stroke-linejoin:bevel;}
.link_error{
stroke:red;
stroke-linejoin:bevel;
}
.nodetext {
font: 12px sans-serif;
-webkit-user-select:none;
-moze-user-select:none;
stroke-linejoin:bevel;
}
#container{
width:800px;
height:600px;
border:1px solid gray;
border-radius:5px;
position:relative;
margin:20px;
}
</style>
</head>
<body>
<div id=
'container'
></div>
<script type=
"text/javascript"
>
function Topology(ele){
typeof(ele)==
'string'
&& (ele=document.getElementById(ele));
var w=ele.clientWidth,
h=ele.clientHeight,
self=
this
;
this
.force = d3.layout.force().gravity(.
05
).distance(
200
).charge(-
800
).size([w, h]);
this
.nodes=
this
.force.nodes();
this
.links=
this
.force.links();
this
.clickFn=function(){};
this
.vis = d3.select(ele).append(
"svg:svg"
)
.attr(
"width"
, w).attr(
"height"
, h).attr(
"pointer-events"
,
"all"
);
this
.force.on(
"tick"
, function(x) {
self.vis.selectAll(
"g.node"
)
.attr(
"transform"
, function(d) {
return
"translate("
+ d.x +
","
+ d.y +
")"
; });
self.vis.selectAll(
"line.link"
)
.attr(
"x1"
, function(d) {
return
d.source.x; })
.attr(
"y1"
, function(d) {
return
d.source.y; })
.attr(
"x2"
, function(d) {
return
d.target.x; })
.attr(
"y2"
, function(d) {
return
d.target.y; });
});
}
Topology.prototype.doZoom=function(){
d3.select(
this
).select(
'g'
).attr(
"transform"
,
"translate("
+ d3.event.translate +
")"
+
" scale("
+ d3.event.scale +
")"
);
}
Topology.prototype.addNode=function(node){
this
.nodes.push(node);
}
Topology.prototype.addNodes=function(nodes){
if
(Object.prototype.toString.call(nodes)==
'[object Array]'
){
var self=
this
;
nodes.forEach(function(node){
self.addNode(node);
});
}
}
Topology.prototype.addLink=function(source,target){
this
.links.push({source:
this
.findNode(source),target:
this
.findNode(target)});
}
Topology.prototype.addLinks=function(links){
if
(Object.prototype.toString.call(links)==
'[object Array]'
){
var self=
this
;
links.forEach(function(link){
self.addLink(link[
'source'
],link[
'target'
]);
});
}
}
Topology.prototype.removeNode=function(id){
var i=
0
,
n=
this
.findNode(id),
links=
this
.links;
while
( i < links.length){
links[i][
'source'
]==n || links[i][
'target'
] ==n ? links.splice(i,
1
) : ++i;
}
this
.nodes.splice(
this
.findNodeIndex(id),
1
);
}
Topology.prototype.removeChildNodes=function(id){
var node=
this
.findNode(id),
nodes=
this
.nodes;
links=
this
.links,
self=
this
;
var linksToDelete=[],
childNodes=[];
links.forEach(function(link,index){
link[
'source'
]==node
&& linksToDelete.push(index)
&& childNodes.push(link[
'target'
]);
});
linksToDelete.reverse().forEach(function(index){
links.splice(index,
1
);
});
var remove=function(node){
var length=links.length;
for
(var i=length-
1
;i>=
0
;i--){
if
(links[i][
'source'
] == node ){
var target=links[i][
'target'
];
links.splice(i,
1
);
nodes.splice(self.findNodeIndex(node.id),
1
);
remove(target);
}
}
}
childNodes.forEach(function(node){
remove(node);
});
for
(var i=nodes.length-
1
;i>=
0
;i--){
var haveFoundNode=
false
;
for
(var j=
0
,l=links.length;j<l;j++){
( links[j][
'source'
]==nodes[i] || links[j][
'target'
]==nodes[i] ) && (haveFoundNode=
true
)
}
!haveFoundNode && nodes.splice(i,
1
);
}
}
Topology.prototype.findNode=function(id){
var nodes=
this
.nodes;
for
(var i in nodes){
if
(nodes[i][
'id'
]==id )
return
nodes[i];
}
return
null
;
}
Topology.prototype.findNodeIndex=function(id){
var nodes=
this
.nodes;
for
(var i in nodes){
if
(nodes[i][
'id'
]==id )
return
i;
}
return
-
1
;
}
Topology.prototype.setNodeClickFn=function(callback){
this
.clickFn=callback;
}
Topology.prototype.update=function(){
var link =
this
.vis.selectAll(
"line.link"
)
.data(
this
.links, function(d) {
return
d.source.id +
"-"
+ d.target.id; })
.attr(
"class"
, function(d){
return
d[
'source'
][
'status'
] && d[
'target'
][
'status'
] ?
'link'
:
'link link_error'
;
});
link.enter().insert(
"svg:line"
,
"g.node"
)
.attr(
"class"
, function(d){
return
d[
'source'
][
'status'
] && d[
'target'
][
'status'
] ?
'link'
:
'link link_error'
;
});
link.exit().remove();
var node =
this
.vis.selectAll(
"g.node"
)
.data(
this
.nodes, function(d) {
return
d.id;});
var nodeEnter = node.enter().append(
"svg:g"
)
.attr(
"class"
,
"node"
)
.call(
this
.force.drag);
var self=
this
;
nodeEnter.append(
"svg:image"
)
.attr(
"class"
,
"circle"
)
.attr(
"xlink:href"
, function(d){
return
d.expand ?
"http://ww2.sinaimg.cn/large/412e82dbjw1dsbny7igx2j.jpg"
:
"http://ww4.sinaimg.cn/large/412e82dbjw1dsbnxezrrpj.jpg"
;
})
.attr(
"x"
,
"-32px"
)
.attr(
"y"
,
"-32px"
)
.attr(
"width"
,
"64px"
)
.attr(
"height"
,
"64px"
)
.on(
'click'
,function(d){ d.expand && self.clickFn(d);})
nodeEnter.append(
"svg:text"
)
.attr(
"class"
,
"nodetext"
)
.attr(
"dx"
,
15
)
.attr(
"dy"
, -
35
)
.text(function(d) {
return
d.id });
node.exit().remove();
this
.force.start();
}
var topology=
new
Topology(
'container'
);
var nodes=[
{id:
'10.4.42.1'
,type:
'router'
,status:
1
},
{id:
'10.4.43.1'
,type:
'switch'
,status:
1
,expand:
true
},
{id:
'10.4.44.1'
,type:
'switch'
,status:
1
},
{id:
'10.4.45.1'
,type:
'switch'
,status:
0
}
];
var childNodes=[
{id:
'10.4.43.2'
,type:
'switch'
,status:
1
},
{id:
'10.4.43.3'
,type:
'switch'
,status:
1
}
];
var links=[
{source:
'10.4.42.1'
,target:
'10.4.43.1'
},
{source:
'10.4.42.1'
,target:
'10.4.44.1'
},
{source:
'10.4.42.1'
,target:
'10.4.45.1'
}
];
var childLinks=[
{source:
'10.4.43.1'
,target:
'10.4.43.2'
},
{source:
'10.4.43.1'
,target:
'10.4.43.3'
},
{source:
'10.4.43.2'
,target:
'10.4.43.3'
}
]
topology.addNodes(nodes);
topology.addLinks(links);
topology.setNodeClickFn(function(node){
if
(!node[
'_expanded'
]){
expandNode(node.id);
node[
'_expanded'
]=
true
;
}
else
{
collapseNode(node.id);
node[
'_expanded'
]=
false
;
}
});
topology.update();
function expandNode(id){
topology.addNodes(childNodes);
topology.addLinks(childLinks);
topology.update();
}
function collapseNode(id){
topology.removeChildNodes(id);
topology.update();
}
</script>
</body>
</html>