123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- (function (global) {
- var dispatch = d3.dispatch("click");
- // ----
- /*This software is released under the MIT License
- MIT License 2014 Denes Csala http://www.csaladen.es
- The following software uses the javascript frameworks below,
- all of which are distributed under the MIT or GNU/GPL license:
- D3.js http://d3js.org/ data-oriented javascript framework.
- - Sankey plugin http://bost.ocks.org/mike/sankey/ for D3.js (modified) by Mike Bostock,
- which is based on the initial version http://tamc.github.io/Sankey/ by Thomas Counsell.
- I have incorporated the ability to render Sankey cycles, as pioneered by https://github.com/cfergus
- - Dragdealer.js href="http://skidding.github.io/dragdealer/ by Ovidiu Chereches
- */
- //<!--DATA INIT-->
- var data={"nodes": [], "links": []}
- //<!--DATA ENTRY-->
- nodesform=d3.select("#nodes-form");
- function addnode() {
- var a=nodesform.append("div");
- a.text(nodesform[0][0].children.length-1+' ');
- a.append("input").attr("value",'{"name":"New Node"}');
- }
- function removenode() {
- nodesform[0][0].children[nodesform[0][0].children.length-1].remove("div")
- }
- linksform=d3.select("#links-form");
- function addlink() {
- linksform.append("div").append("input").attr("value",'{"source":0,"target":1,"value":0.52}');
- }
- function removelink() {
- linksform[0][0].children[linksform[0][0].children.length-1].remove("div")
- }
- function draw() {
- change(data);
- }
- function save(){
- d3.select('#save').style('z-index',100).transition().style('opacity',0.9);
- st='{"sankey":{"nodes":['
- for (i = 0; i < nodesform[0][0].children.length; i++) {
- st=st+nodesform[0][0].children[i].children[0].value+',';
- }
- st=st.substring(0, st.length - 1)+'],"links":[';
- for (i = 0; i < linksform[0][0].children.length; i++) {
- st=st+linksform[0][0].children[i].children[0].value+',';
- }
- st = st.substring(0, st.length - 1)+']},"params":['+densityslider.value.current[0]+','+opacityslider.value.current[0]+','+labelformat+','+labeltextformat+','+showlinkcount+']';
- if (document.getElementById("fixedlayout").checked){
- var coords=[]
- sankey.nodes().forEach(function(d){
- coords.push([d.x,d.y])
- })
- st=st+',"fixedlayout":'+JSON.stringify(coords);
- }
- st=st+'}';
- d3.select('#savetext').text(st);
- }
- function load(){
- d3.select('#load').style('z-index',100).transition().style('opacity',0.9);
- }
- function loadsubmit(){
- d3.select('#load').transition().style('opacity',0).style('z-index',-1);
- var rawtext=d3.select('#load')[0][0].children[1].value;
- if (rawtext!="") {
- //parse data
- var rawdata=JSON.parse(rawtext);
- if ("sankey" in rawdata) {
- var newdata=rawdata.sankey;
- }
- else {
- var newdata=rawdata;
- }
- var loadtext=JSON.stringify(newdata)
- //remove existing node entry boxes
- var n=nodesform[0][0].children.length;
- for (i = 0; i < n; i++) {
- nodesform[0][0].children[0].remove("div");
- }
- //remove existing link entry boxes
- var n=linksform[0][0].children.length;
- for (i = 0; i < n; i++) {
- linksform[0][0].children[0].remove("div");
- }
- //add new node entry boxes
- var newdata2=JSON.parse(loadtext.substring(loadtext.indexOf('"nodes":[')+8, loadtext.indexOf('"links":[')-1));
- for (i = 0; i < newdata2.length; i++) {
- var a=nodesform.append("div");
- a.text(nodesform[0][0].children.length-1+' ');
- a.append("input").attr("value",JSON.stringify(newdata2[i]));
- }
- //add new link entry boxes
- var newdata2=JSON.parse(loadtext.substring(loadtext.indexOf('"links":[')+8, loadtext.length - 1))
- for (i = 0; i < newdata2.length; i++) {
- linksform.append("div").append("input").attr("value",JSON.stringify(newdata2[i]));
- }
- //set parameters
- if ("fixedlayout" in rawdata) {
- fixedlayout=document.getElementById("ignorelayout").checked?[]:rawdata.fixedlayout;
- } else {
- fixedlayout=[];
- }
- if ("params" in rawdata) {
- labelformat=rawdata.params[2];
- labeltextformat=rawdata.params[3];
- if (rawdata.params.length>4) showlinkcount=rawdata.params[4];
- else showlinkcount=0;
- document.getElementById("vlabel").checked=(labelformat==0)?true:false;
- document.getElementById("tlabel").checked=(labeltextformat==0)?true:false;
- document.getElementById("clabel").checked=(showlinkcount==1)?true:false;
- densityslider.setValue(rawdata.params[0]);
- opacityslider.setValue(rawdata.params[1]);
- }
- else {
- change(newdata);
- }
- }
- }
- //<!--SANKEY DIAGRAM-->
- var parallelrendering=false;
- var minnodewidth = 50;
- var padding = 28;
- var labelformat = 0;
- var labeltextformat = 0;
- var showlinkcount = 0;
- var paddingmultiplier = 100;
- var lowopacity = 0.3;
- var highopacity = 0.7;
- var fixedlayout=[];
- var sankeyColor = d3.scale.category20();
- var sankeyNumberFormat = d3.format(",.0f");
- // d3.select("#ndec")
- // .on("change",draw);
- // d3.select("#ldec")
- // .on("change",draw);
- var margin = {
- top: 10,
- right: 10,
- bottom: 10,
- left: 40
- };
- var width = 1000; //document.getElementById("chart").offsetWidth - margin.left - margin.right,
- var height = 500; // document.getElementById("chart").offsetHeight - margin.bottom;
- var svg = null;//d3.select("#chart").append("svg")
- //svg.append("rect").attr("x",0).attr("y",0).attr("width","100%").attr("height","100%").attr("fill","white").attr('class','background')
- //svg=svg.attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom).append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
- //set svg background color via rectangle trick
- // d3.select("#chart").select("svg")
- var sankey = null;
- // var sankey = d3.sankey().nodeWidth(30).nodePadding(padding).size([width, height]);
- // var path = sankey.reversibleLink();
- var change = function(d) {
- svg.selectAll("g").remove();
- sankey = d3.sankey().nodeWidth(30).nodePadding(padding).size([width, height]);
- sankey.nodes(d.nodes).links(d.links).layout(500);
- var g = svg.append("g") //link
- .selectAll(".link").data(d.links).enter().append("g").attr("class", "link").sort(function(j, i) {
- return i.dy - j.dy
- });
- var path = sankey.reversibleLink();
- var h = g.append("path") //path0
- .attr("d", path(0));
- var f = g.append("path") //path1
- .attr("d", path(1));
- var e = g.append("path") //path2
- .attr("d", path(2));
- g.attr("fill", function(i) {
- if (i.fill) return i.fill;
- else if (i.source.fill) return i.source.fill;
- else return i.source.color = sankeyColor(i.source.name.replace(/ .*/, ""))
- }).attr("opacity", lowopacity).on("mouseover", function(d) {
- d3.select(this).style('opacity', highopacity);
- }).on("mouseout", function(d) {
- d3.select(this).style('opacity', lowopacity);
- }).append("title")
- .text(sankeyEdgeTitle);
- var c = svg.append("g") //node
- .selectAll(".node").data(d.nodes).enter().append("g").attr("class", "node").attr("transform", function(i) {
- return "translate(" + i.x + "," + i.y + ")"
- }).call(d3.behavior.drag().origin(function(i) {
- return i
- }).on("dragstart", function() {
- this.parentNode.appendChild(this)
- }).on("drag", samkeyOnDragMove));
- c.append("rect") //node
- .attr("height", function(i) {
- return i.dy
- }).attr("width", sankey.nodeWidth()).style("fill", function(i) {
- if (i.fill) return i.color = i.fill;
- else return i.color = sankeyColor(i.name.replace(/ .*/, ""))
- }).style("stroke", function(i) {
- return d3.rgb(i.color).darker(2)
- }).on("mouseover", function(d) {
- svg.selectAll(".link").filter(function(l) {
- return l.source == d || l.target == d;
- }).transition().style('opacity', highopacity);
- }).on("mouseout", function(d) {
- svg.selectAll(".link").filter(function(l) {
- return l.source == d || l.target == d;
- }).transition().style('opacity', lowopacity);
- }).on("dblclick", function(d) {
- svg.selectAll(".link").filter(function(l) {
- return l.target == d;
- }).attr("display", function() {
- if (d3.select(this).attr("display") == "none") return "inline"
- else return "none"
- });
- }).append("title").text(sankeyNodeTitle);
- c.append("text") //node
- .attr("x", -6).attr("y", function(i) {
- return i.dy / 2
- }).attr("dy", ".35em").attr("text-anchor", "end").attr("font-size","16px")
- .text(sankeyNodeLabel)
- .filter(function(i) {
- return i.x < width / 2
- }).attr("x", 6 + sankey.nodeWidth()).attr("text-anchor", "start")
- if (showlinkcount>0) c.append("text") //node
- .attr("x", -6).attr("y", function(i) {
- return i.dy / 2 + 20
- }).attr("dy", ".35em").attr("text-anchor", "end").attr("font-size","16px")
- .text(function(i) {
- return "→ "+(i.targetLinks.length)+" | "+(i.sourceLinks.length)+" →";
- }).filter(function(i) {
- return i.x < width / 2
- }).attr("x", 6 + sankey.nodeWidth()).attr("text-anchor", "start")
- c.append("text") //node
- .attr("x", function(i) {return -i.dy / 2})
- .attr("y", function(i) {return i.dx / 2 + 9})
- .attr("transform", "rotate(270)").attr("text-anchor", "middle").attr("font-size","23px").text(function(i) {
- if ((i.dy>minnodewidth)&&(labelformat<1)){
- return sankeyNumberFormat(i.value);
- }
- }).attr("fill",function(d){
- return d3.rgb(d["color"]).brighter(2)
- }).attr("stroke",function(d){
- return d3.rgb(d["color"]).darker(2)
- }).attr("stroke-width","1px");
- svg.selectAll('rect')
- .on('mouseover', (a, b, c, d, e) => {console.log('mouseover', a, b, c, d, e)})
- .on('mouseout', (a, b, c, d, e) => {console.log('mouseout', a, b, c, d, e)})
- .on('mousedown', (a, b, c, d, e) => {console.log('mousedown', a, b, c, d, e)})
- .on('mouseup', (a) => {
- dispatch.click({
- nativeEvent: d3.event,
- node: a,
- element: d3.event.srcElement
- })
- });
- function samkeyOnDragMove(i) {
- // if (document.getElementById("ymove").checked) {
- // if (document.getElementById("xmove").checked) {
- // d3.select(this).attr("transform", "translate(" + (i.x = Math.max(0, Math.min(width - i.dx, d3.event.x))) + "," + (i.y = Math.max(0, Math.min(height - i.dy, d3.event.y))) + ")")
- // } else {
- dragged = true
- d3.select(this).attr("transform", "translate(" + i.x + "," + (i.y = Math.max(0, Math.min(height - i.dy, d3.event.y))) + ")")
- // }
- // } else {
- // if (document.getElementById("xmove").checked) {
- // d3.select(this).attr("transform", "translate(" + (i.x = Math.max(0, Math.min(width - i.dx, d3.event.x))) + "," + i.y + ")")
- // }
- // }
- sankey.relayout();
- f.attr("d", path(1)); // TODO: global var f
- h.attr("d", path(0)); // TODO: global var h
- e.attr("d", path(2)); // TODO: global var e
- }
- };
- // draw();
- function sankeyEdgeTitle(i) {
- return i.source.name + " → " + i.target.name + "\n" + sankeyNumberFormat(i.value)
- }
- function sankeyNodeTitle(i) {
- if (i.label) return i.label + "\n" + sankeyNumberFormat(i.value)
- return i.name + "\n" + sankeyNumberFormat(i.value)
- }
- function sankeyNodeLabel(i) {
- if (labeltextformat < 1) {
- if (i.label) return (i.label.length > 20) ? i.label.substr(0, 20) + '...' : i.label
- return i.name;
- } else {
- return "";
- }
- }
- var renderGraph = (htmlNode, data, opts) => {
- var opts = opts || {}
- if (!htmlNode) throw "Missing html node";
- if (!data) throw "Missing data";
- // console.log('renderGraph data', data)
- htmlNode.append("rect").attr("x",0).attr("y",0).attr("width","100%").attr("height","100%").attr("fill","white")
- margin = {
- top: 10,
- right: 20,
- bottom: 10,
- left: 40
- };
- width = (opts.width) ? opts.width - margin.left - margin.right : 1020;
- height = (opts.height) ? opts.height : 500;
- htmlNode.attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom)
- svg = htmlNode.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
- change(data);
- return dispatch
- }
- global.parallelrendering = parallelrendering
- global.fixedlayout = fixedlayout
- global.renderGraph = renderGraph
- })(window)
|