//Settings:
//width, height and the default radius of the circles
var w = 1024,
h = 768,
r = 10;
//test data - usually this is recieved via ajax
//Initial Data:
var hosts = eval({
"ITEM003": {
"name": "ITEM003",
"parents": [],
"status": 0,
"hostgroup": "Secure"
},
"ITEM004": {
"name": "ITEM004",
"parents": [],
"status": 0,
"hostgroup": "Secure"
},
"CORE": {
"name": "CORE",
"parents": ["ITEM004", "ITEM003"],
"status": 0,
"hostgroup": "DMZ"
}
});
var mylinks = eval({
"0": ["CORE", "ITEM004"],
"1": ["CORE", "ITEM003"]
});
//Data after update
var updated_hosts = eval({
"ITEM003": {
"name": "ITEM003",
"parents": [],
"status": 0,
"hostgroup": "Secure"
},
"ITEM004": {
"name": "ITEM004",
"parents": [],
"status": 0,
"hostgroup": "Secure"
},
"CORE": {
"name": "CORE",
"parents": ["ITEM004", "ITEM003"],
"status": 0,
"hostgroup": "DMZ"
},
"REMOTE": {
"name": "REMOTE",
"parents": [],
"status": 0,
"hostgroup": ""
}
});
var updated_mylinks = eval({
"0": ["CORE", "ITEM004"],
"1": ["CORE", "ITEM003"],
"2": ["CORE", "REMOTE"]
});
//I define these here so they carry between functions - not really necessary in this jsfiddle probably
window.link = undefined;
window.node = undefined;
//make up my node object
window.nodeArray = [];
window.node_hash = [];
for (var key in hosts) {
var a = {
id: "node_" + hosts[key].name,
labelText: hosts[key].name,
status: hosts[key].status,
hostgroup: hosts[key].hostgroup,
class: "node realnode",
iconimage: hosts[key].iconimage,
added: true
};
nodeArray.push(a);
node_hash[key] = a;
}
//make up my link object
window.linkArray = [];
for (var key in mylinks) {
var linkcolor = "#47CC60";
var a = {
source: node_hash[mylinks[key][0]],
target: node_hash[mylinks[key][1]],
color: linkcolor,
class: "link reallink"
};
linkArray.push(a);
}
//make up my node text objects
//these are just more nodes with a different class
//we will append text to them later
//we also add the links to the linkArray now to bind them to their real nodes
window.text_hash = [];
for (var key in hosts) {
var a = {
id: "label_" + hosts[key].name,
text: hosts[key].name,
color: "#ffffff",
size: "6",
class: "node label",
added: true
};
nodeArray.push(a);
text_hash[key] = a;
}
//because the text labels are in the same order as the
//original nodes we know that node_hash[0] has label text_hash[0]
//it doesn't matter which we iterate through here
for (var key in text_hash) {
var a = {
source: node_hash[key],
target: text_hash[key],
class: "link label"
};
linkArray.push(a);
}
//set up the environment in a div called graph using the settings baove
window.vis = d3.select("body")
.append("svg:svg")
.attr("height", 500)
.attr("width", 500)
.attr("pointer-events", "all")
.append('svg:g')
//object to interact with the force libraries in d3
//the settings here set how the nodes interact
//seems a bit overcomplicated but it stops the diagram going nuts!
window.force = d3.layout.force()
.friction("0.7")
.gravity(function(d, i) {
if (d.class == "link reallink") {
return "0.95";
} else {
return "0.1";
}
})
.charge(function(d, i) {
if (d.class == "link reallink") {
return "-1500";
} else {
return "-300";
}
})
.linkDistance(function(d) {
if (d.class == "link reallink") {
return "120";
} else {
return "35";
}
})
.linkStrength(function(d) {
if (d.class == "link reallink") {
return "8";
} else {
return "6";
}
})
.nodes(nodeArray)
.links(linkArray)
.on("tick", tick)
node = vis.selectAll(".node");
link = vis.selectAll(".link");
//create the objects and run it
draw();
for (key in nodeArray) {
nodeArray[key].added = false;
}
//wait 5 seconds then update the diagram TO ADD A NODE
setTimeout(function() {
//update the objects
//vis.selectAll("g.node").data(nodeArray).exit().transition().ease("elastic").remove();
//vis.selectAll("line").data(linkArray).exit().transition().ease("elastic").remove();
var a = {
id: "node_REMOTE",
labelText: "REMOTE",
status: "0",
hostgroup: "",
class: "node realnode",
iconimage: "",
added: true
};
nodeArray.push(a);
node_hash["REMOTE"] = a;
var linkcolor = "#47CC60";
var a = {
source: node_hash["CORE"],
target: node_hash["REMOTE"],
color: linkcolor,
class: "link reallink"
};
linkArray.push(a);
//make up my node text objects
var a = {
id: "label_REMOTE",
text: "REMOTE",
color: "#000000",
size: "6",
class: "node label",
added: true
};
nodeArray.push(a);
text_hash["REMOTE"] = a;
var a = {
source: node_hash["REMOTE"],
target: text_hash["REMOTE"],
class: "link label"
};
linkArray.push(a);
//redraw it
draw();
}, 5000);
//----- functions for drawing and tick below
function draw() {
link = link.data(force.links(), function(d) {
return d.source.id + "-" + d.target.id;
});
node = node.data(force.nodes(), function(d) {
return d.id;
});
//create the link object using the links object in the json
//link = vis.selectAll("line").data(linkArray);
link.enter().insert("line", ".node")
.attr("stroke-width", '0')
.transition()
.duration(1000)
.ease("bounce")
.attr("stroke-width", function(d, i) {
if (d.class == 'link reallink') {
return '3';
} else {
return '0';
};
})
.style("stroke", function(d, i) {
return d.color;
})
.attr("class", function(d, i) {
return d.class;
});
//node = vis.selectAll("g").data(nodeArray);
node.enter().append("svg:g")
.attr("class", function(d) {
return d.class
})
.attr("id", function(d) {
return d.id
})
.call(force.drag);
//append to each node an svg circle element
vis.selectAll(".realnode").filter(function(d) {
return d.added;
})
.append("svg:circle")
.attr("r", "0")
.transition()
.duration(1000)
.ease("bounce")
.attr("r", "6")
.style("fill", "#000000")
.style("stroke", function(d) {
return d.color;
})
.style("stroke-width", "4");
//append to each node the attached text desc
vis.selectAll(".label").filter(function(d) {
return d.added;
})
.append("svg:text")
.attr("text-anchor", "middle")
.attr("fill", "black")
.style("pointer-events", "none")
.attr("font-size", "9px")
.attr("font-weight", "100")
.text(function(d) {
return d.text;
})
.attr("transform", "rotate(180)")
.transition()
.duration(1000)
.ease("bounce")
.attr("transform", "rotate(0)");
node.exit().transition().ease("elastic").remove();
link.exit().transition().ease("elastic").remove();
//activate it all - initiate the nodes and links
force.start();
}
function tick() {
node.attr("cx", function(d) {
return d.x = Math.max(r + 15, Math.min(w - r - 15, d.x));
})
.attr("cy", function(d) {
return d.y = Math.max(r + 15, Math.min(h - r - 15, d.y));
})
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
link.data(linkArray).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;
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>