Skip to content

Instantly share code, notes, and snippets.

@csaladenes
Last active April 9, 2021 23:38
Show Gist options
  • Save csaladenes/38d92466c2ae4d68793e to your computer and use it in GitHub Desktop.
Save csaladenes/38d92466c2ae4d68793e to your computer and use it in GitHub Desktop.
Sankey Diagram Generator
(function(e,t){if(typeof define==="function"&&define.amd){define(t)}else{e.Dragdealer=t()}})(this,function(){function f(e){var t="Webkit Moz ms O".split(" "),n=document.documentElement.style;if(n[e]!==undefined)return e;e=e.charAt(0).toUpperCase()+e.substr(1);for(var r=0;r<t.length;r++){if(n[t[r]+e]!==undefined){return t[r]+e}}}function l(e){if(a.backfaceVisibility&&a.perspective){e.style[a.perspective]="1000px";e.style[a.backfaceVisibility]="hidden"}}var e=function(e,t){this.bindMethods();this.options=this.applyDefaults(t||{});this.wrapper=this.getWrapperElement(e);if(!this.wrapper){return}this.handle=this.getHandleElement(this.wrapper,this.options.handleClass);if(!this.handle){return}this.init();this.bindEventListeners()};e.prototype={defaults:{disabled:false,horizontal:true,vertical:false,slide:true,steps:0,snap:false,loose:false,speed:.1,xPrecision:0,yPrecision:0,handleClass:"handle",css3:true,activeClass:"active"},init:function(){if(this.options.css3){l(this.handle)}this.value={prev:[-1,-1],current:[this.options.x||0,this.options.y||0],target:[this.options.x||0,this.options.y||0]};this.offset={wrapper:[0,0],mouse:[0,0],prev:[-999999,-999999],current:[0,0],target:[0,0]};this.change=[0,0];this.stepRatios=this.calculateStepRatios();this.activity=false;this.dragging=false;this.tapping=false;this.reflow();if(this.options.disabled){this.disable()}},applyDefaults:function(e){for(var t in this.defaults){if(!e.hasOwnProperty(t)){e[t]=this.defaults[t]}}return e},getWrapperElement:function(e){if(typeof e=="string"){return document.getElementById(e)}else{return e}},getHandleElement:function(e,t){var n,r,i;if(e.getElementsByClassName){n=e.getElementsByClassName(t);if(n.length>0){return n[0]}}else{r=new RegExp("(^|\\s)"+t+"(\\s|$)");n=e.getElementsByTagName("*");for(i=0;i<n.length;i++){if(r.test(n[i].className)){return n[i]}}}},calculateStepRatios:function(){var e=[];if(this.options.steps>1){for(var t=0;t<=this.options.steps-1;t++){e[t]=t/(this.options.steps-1)}}return e},setWrapperOffset:function(){this.offset.wrapper=u.get(this.wrapper)},calculateBounds:function(){var e={top:this.options.top||0,bottom:-(this.options.bottom||0)+this.wrapper.offsetHeight,left:this.options.left||0,right:-(this.options.right||0)+this.wrapper.offsetWidth};e.availWidth=e.right-e.left-this.handle.offsetWidth;e.availHeight=e.bottom-e.top-this.handle.offsetHeight;return e},calculateValuePrecision:function(){var e=this.options.xPrecision||Math.abs(this.bounds.availWidth),t=this.options.yPrecision||Math.abs(this.bounds.availHeight);return[e?1/e:0,t?1/t:0]},bindMethods:function(){this.onHandleMouseDown=t(this.onHandleMouseDown,this);this.onHandleTouchStart=t(this.onHandleTouchStart,this);this.onDocumentMouseMove=t(this.onDocumentMouseMove,this);this.onWrapperTouchMove=t(this.onWrapperTouchMove,this);this.onWrapperMouseDown=t(this.onWrapperMouseDown,this);this.onWrapperTouchStart=t(this.onWrapperTouchStart,this);this.onDocumentMouseUp=t(this.onDocumentMouseUp,this);this.onDocumentTouchEnd=t(this.onDocumentTouchEnd,this);this.onHandleClick=t(this.onHandleClick,this);this.onWindowResize=t(this.onWindowResize,this)},bindEventListeners:function(){n(this.handle,"mousedown",this.onHandleMouseDown);n(this.handle,"touchstart",this.onHandleTouchStart);n(document,"mousemove",this.onDocumentMouseMove);n(this.wrapper,"touchmove",this.onWrapperTouchMove);n(this.wrapper,"mousedown",this.onWrapperMouseDown);n(this.wrapper,"touchstart",this.onWrapperTouchStart);n(document,"mouseup",this.onDocumentMouseUp);n(document,"touchend",this.onDocumentTouchEnd);n(this.handle,"click",this.onHandleClick);n(window,"resize",this.onWindowResize);var e=this;this.interval=setInterval(function(){e.animate()},25);this.animate(false,true)},unbindEventListeners:function(){r(this.handle,"mousedown",this.onHandleMouseDown);r(this.handle,"touchstart",this.onHandleTouchStart);r(document,"mousemove",this.onDocumentMouseMove);r(this.wrapper,"touchmove",this.onWrapperTouchMove);r(this.wrapper,"mousedown",this.onWrapperMouseDown);r(this.wrapper,"touchstart",this.onWrapperTouchStart);r(document,"mouseup",this.onDocumentMouseUp);r(document,"touchend",this.onDocumentTouchEnd);r(this.handle,"click",this.onHandleClick);r(window,"resize",this.onWindowResize);clearInterval(this.interval)},onHandleMouseDown:function(e){o.refresh(e);i(e);s(e);this.activity=false;this.startDrag()},onHandleTouchStart:function(e){o.refresh(e);s(e);this.activity=false;this.startDrag()},onDocumentMouseMove:function(e){o.refresh(e);if(this.dragging){this.activity=true}},onWrapperTouchMove:function(e){o.refresh(e);if(!this.activity&&this.draggingOnDisabledAxis()){if(this.dragging){this.stopDrag()}return}i(e);this.activity=true},onWrapperMouseDown:function(e){o.refresh(e);i(e);this.startTap()},onWrapperTouchStart:function(e){o.refresh(e);i(e);this.startTap()},onDocumentMouseUp:function(e){this.stopDrag();this.stopTap()},onDocumentTouchEnd:function(e){this.stopDrag();this.stopTap()},onHandleClick:function(e){if(this.activity){i(e);s(e)}},onWindowResize:function(e){this.reflow()},enable:function(){this.disabled=false;this.handle.className=this.handle.className.replace(/\s?disabled/g,"")},disable:function(){this.disabled=true;this.handle.className+=" disabled"},reflow:function(){this.setWrapperOffset();this.bounds=this.calculateBounds();this.valuePrecision=this.calculateValuePrecision();this.updateOffsetFromValue()},getStep:function(){return[this.getStepNumber(this.value.target[0]),this.getStepNumber(this.value.target[1])]},getValue:function(){return this.value.target},setStep:function(e,t,n){this.setValue(this.options.steps&&e>1?(e-1)/(this.options.steps-1):0,this.options.steps&&t>1?(t-1)/(this.options.steps-1):0,n)},setValue:function(e,t,n){this.setTargetValue([e,t||0]);if(n){this.groupCopy(this.value.current,this.value.target);this.updateOffsetFromValue();this.callAnimationCallback()}},startTap:function(){if(this.disabled){return}this.tapping=true;this.setWrapperOffset();this.setTargetValueByOffset([o.x-this.offset.wrapper[0]-this.handle.offsetWidth/2,o.y-this.offset.wrapper[1]-this.handle.offsetHeight/2])},stopTap:function(){if(this.disabled||!this.tapping){return}this.tapping=false;this.setTargetValue(this.value.current)},startDrag:function(){if(this.disabled){return}this.dragging=true;this.setWrapperOffset();this.offset.mouse=[o.x-u.get(this.handle)[0],o.y-u.get(this.handle)[1]];if(!this.wrapper.className.match(this.options.activeClass)){this.wrapper.className+=" "+this.options.activeClass}},stopDrag:function(){if(this.disabled||!this.dragging){return}this.dragging=false;var e=this.groupClone(this.value.current);if(this.options.slide){var t=this.change;e[0]+=t[0]*4;e[1]+=t[1]*4}this.setTargetValue(e);this.wrapper.className=this.wrapper.className.replace(" "+this.options.activeClass,"")},callAnimationCallback:function(){var e=this.value.current;if(this.options.snap&&this.options.steps>1){e=this.getClosestSteps(e)}if(!this.groupCompare(e,this.value.prev)){if(typeof this.options.animationCallback=="function"){this.options.animationCallback.call(this,e[0],e[1])}this.groupCopy(this.value.prev,e)}},callTargetCallback:function(){if(typeof this.options.callback=="function"){this.options.callback.call(this,this.value.target[0],this.value.target[1])}},animate:function(e,t){if(e&&!this.dragging){return}if(this.dragging){var n=this.groupClone(this.value.target);var r=[o.x-this.offset.wrapper[0]-this.offset.mouse[0],o.y-this.offset.wrapper[1]-this.offset.mouse[1]];this.setTargetValueByOffset(r,this.options.loose);this.change=[this.value.target[0]-n[0],this.value.target[1]-n[1]]}if(this.dragging||t){this.groupCopy(this.value.current,this.value.target)}if(this.dragging||this.glide()||t){this.updateOffsetFromValue();this.callAnimationCallback()}},glide:function(){var e=[this.value.target[0]-this.value.current[0],this.value.target[1]-this.value.current[1]];if(!e[0]&&!e[1]){return false}if(Math.abs(e[0])>this.valuePrecision[0]||Math.abs(e[1])>this.valuePrecision[1]){this.value.current[0]+=e[0]*this.options.speed;this.value.current[1]+=e[1]*this.options.speed}else{this.groupCopy(this.value.current,this.value.target)}return true},updateOffsetFromValue:function(){if(!this.options.snap){this.offset.current=this.getOffsetsByRatios(this.value.current)}else{this.offset.current=this.getOffsetsByRatios(this.getClosestSteps(this.value.current))}if(!this.groupCompare(this.offset.current,this.offset.prev)){this.renderHandlePosition();this.groupCopy(this.offset.prev,this.offset.current)}},renderHandlePosition:function(){var e="";if(this.options.css3&&a.transform){if(this.options.horizontal){e+="translateX("+this.offset.current[0]+"px)"}if(this.options.vertical){e+=" translateY("+this.offset.current[1]+"px)"}this.handle.style[a.transform]=e;return}if(this.options.horizontal){this.handle.style.left=this.offset.current[0]+"px"}if(this.options.vertical){this.handle.style.top=this.offset.current[1]+"px"}},setTargetValue:function(e,t){var n=t?this.getLooseValue(e):this.getProperValue(e);this.groupCopy(this.value.target,n);this.offset.target=this.getOffsetsByRatios(n);this.callTargetCallback()},setTargetValueByOffset:function(e,t){var n=this.getRatiosByOffsets(e);var r=t?this.getLooseValue(n):this.getProperValue(n);this.groupCopy(this.value.target,r);this.offset.target=this.getOffsetsByRatios(r)},getLooseValue:function(e){var t=this.getProperValue(e);return[t[0]+(e[0]-t[0])/4,t[1]+(e[1]-t[1])/4]},getProperValue:function(e){var t=this.groupClone(e);t[0]=Math.max(t[0],0);t[1]=Math.max(t[1],0);t[0]=Math.min(t[0],1);t[1]=Math.min(t[1],1);if(!this.dragging&&!this.tapping||this.options.snap){if(this.options.steps>1){t=this.getClosestSteps(t)}}return t},getRatiosByOffsets:function(e){return[this.getRatioByOffset(e[0],this.bounds.availWidth,this.bounds.left),this.getRatioByOffset(e[1],this.bounds.availHeight,this.bounds.top)]},getRatioByOffset:function(e,t,n){return t?(e-n)/t:0},getOffsetsByRatios:function(e){return[this.getOffsetByRatio(e[0],this.bounds.availWidth,this.bounds.left),this.getOffsetByRatio(e[1],this.bounds.availHeight,this.bounds.top)]},getOffsetByRatio:function(e,t,n){return Math.round(e*t)+n},getStepNumber:function(e){return this.getClosestStep(e)*(this.options.steps-1)+1},getClosestSteps:function(e){return[this.getClosestStep(e[0]),this.getClosestStep(e[1])]},getClosestStep:function(e){var t=0;var n=1;for(var r=0;r<=this.options.steps-1;r++){if(Math.abs(this.stepRatios[r]-e)<n){n=Math.abs(this.stepRatios[r]-e);t=r}}return this.stepRatios[t]},groupCompare:function(e,t){return e[0]==t[0]&&e[1]==t[1]},groupCopy:function(e,t){e[0]=t[0];e[1]=t[1]},groupClone:function(e){return[e[0],e[1]]},draggingOnDisabledAxis:function(){return!this.options.horizontal&&o.xDiff>o.yDiff||!this.options.vertical&&o.yDiff>o.xDiff}};var t=function(e,t){return function(){return e.apply(t,arguments)}};var n=function(e,t,n){if(e.addEventListener){e.addEventListener(t,n,false)}else if(e.attachEvent){e.attachEvent("on"+t,n)}};var r=function(e,t,n){if(e.removeEventListener){e.removeEventListener(t,n,false)}else if(e.detachEvent){e.detachEvent("on"+t,n)}};var i=function(e){if(!e){e=window.event}if(e.preventDefault){e.preventDefault()}e.returnValue=false};var s=function(e){if(!e){e=window.event}if(e.stopPropagation){e.stopPropagation()}e.cancelBubble=true};var o={x:0,y:0,xDiff:0,yDiff:0,refresh:function(e){if(!e){e=window.event}if(e.type=="mousemove"){this.set(e)}else if(e.touches){this.set(e.touches[0])}},set:function(e){var t=this.x,n=this.y;if(e.clientX||e.clientY){this.x=e.clientX;this.y=e.clientY}else if(e.pageX||e.pageY){this.x=e.pageX-document.body.scrollLeft-document.documentElement.scrollLeft;this.y=e.pageY-document.body.scrollTop-document.documentElement.scrollTop}this.xDiff=Math.abs(this.x-t);this.yDiff=Math.abs(this.y-n)}};var u={get:function(e){var t={left:0,top:0};if(e.getBoundingClientRect!==undefined){t=e.getBoundingClientRect()}return[t.left,t.top]}};var a={transform:f("transform"),perspective:f("perspective"),backfaceVisibility:f("backfaceVisibility")};return e})
/*This software is released under the MIT License
Copyright (C) 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 (heavily modified) by Mike Bostock's,
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() {
nodesform.append("div").append("input").attr("value","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","0,1,0.52");
}
function removelink() {
linksform[0][0].children[linksform[0][0].children.length-1].remove("div")
}
function draw() {
data={"nodes": [], "links": []}
for (i = 0; i < nodesform[0][0].children.length; i++) {
data.nodes.push({"name": nodesform[0][0].children[i].children[0].value});
}
for (i = 0; i < linksform[0][0].children.length; i++) {
var array = linksform[0][0].children[i].children[0].value.split(',');
data.links.push({"source":parseInt(array[0]),"target":parseInt(array[1]),"value":parseFloat(array[2])});
}
change(data);
}
function save(){
d3.select('#save').style('z-index',100).transition().style('opacity',0.9);
st='{"nodes":['
for (i = 0; i < nodesform[0][0].children.length; i++) {
st=st+'{"name":"'+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++) {
var array = linksform[0][0].children[i].children[0].value.split(',');
st=st+'{"source":'+parseInt(array[0])+',"target":'+parseInt(array[1])+',"value":'+parseFloat(array[2])+'},';
}
st = st.substring(0, st.length - 1)+']}';
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 loadtext=d3.select('#load')[0][0].children[1].value;
if (loadtext!="") {
//redraw
var newdata=JSON.parse(loadtext);
change(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
for (i = 0; i < newdata.nodes.length; i++) {
nodesform.append("div").append("input").attr("value",newdata.nodes[i].name);
}
//add new link entry boxes
var newdata=JSON.parse(loadtext.substring(loadtext.indexOf('"links":[')+8, loadtext.length - 1))
for (i = 0; i < newdata.length; i++) {
linksform.append("div").append("input").attr("value",newdata[i].source+","+newdata[i].target+","+newdata[i].value);
}
}
}
//<!--SANKEY DIAGRAM-->
var padding = 28;
var paddingmultiplier = 50;
var lowopacity = 0.3;
var highopacity = 0.7;
var format2Number = d3.format(",.2f"),
formatNumber = d3.format(",.0f"),
format = function(a) {
return formatNumber(a)
},
format2 = function(a) {
return format2Number(a)
},
color = d3.scale.category20();
d3.select("#chart").style("width", document.getElementById("chart").offsetWidth - sizecorrection)
d3.select("#titlebar").style("width", document.getElementById("titlebar").offsetWidth - sizecorrection)
var margin = {
top: 70,
right: 10,
bottom: 30,
left: 40
},
width = document.getElementById("chart").offsetWidth - margin.left - margin.right,
height = document.getElementById("chart").offsetHeight - margin.bottom - 90;
var svg = d3.select("#chart").append("svg").attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom).append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var sankey = d3.sankey().nodeWidth(30).nodePadding(padding).size([width, height]);
var path = sankey.reversibleLink();
var change = function() {};
change = function(d) {
padding = paddingmultiplier * (1 - densityslider.getValue()[0]) + 3
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 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) {
return i.source.color = color(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") //link
.text(function(i) {
return i.source.name + " → " + i.target.name + "\n" + format2(i.value)
});
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", b));
c.append("rect") //node
.attr("height", function(i) {
return i.dy
}).attr("width", sankey.nodeWidth()).style("fill", function(i) {
return i.color = color(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(function(i) {
return i.name + "\n" + format2(i.value)
});
c.append("text") //node
.attr("x", -6).attr("y", function(i) {
return i.dy / 2
}).attr("dy", ".35em").attr("text-anchor", "end").attr("transform", null).text(function(i) {
return i.name
}).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 + 6})
.attr("transform", "rotate(270)").attr("text-anchor", "middle").text(function(i) {
if (i.dy>50){
return format(i.value);
}
}).attr("fill","white").attr("stroke","black");
function b(i) { //dragmove
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 {
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));
h.attr("d", path(0));
e.attr("d", path(2))
};
};
draw();
<!DOCTYPE xhtml>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-Type" content="text/html; charset=utf-8" />
<meta name="description" content="Sankey Diagram Generator #sankey #d3js @csaladenes" />
<meta name="keywords" content="csaladenes, sankey diagram, d3.js, d3js" />
<!--<meta property="og:image" content="http://food.csaladen.es/favicon.png" />-->
<meta property="og:image" content="http://food.csaladen.es/food_energy_flows_sankey.jpg" />
<meta property="og:description" content="Sankey Diagram Generator #sankey #d3js @csaladenes" />
<meta property="og:title" content="Sankey Diagram" />
<meta property="og:type" content="article" />
<meta property="og:site_name" content="food.csaladen.es" />
<!--<meta property="og:url" content="http://food.csaladen.es" />-->
<meta property="fb:admins" content="100943737036023614165" />
<title>Sankey Diagram</title>
<link rel="shortcut icon" href="http://food.csaladen.es/favicon.png" />
<style>
html,
body {
height: 100%;
font-family: "Trebuchet MS", "Open Sans", Segoe UI light, Verdana, Tahoma, Helvetica, sans-serif;
margin: 0;
padding: 0;
background: #fff;
}
#chart {
height: 99%;
float: left;
position: absolute;
width: -moz-calc(80% - 45px);
width: -webkit-calc(80% - 45px);
width: calc(80% - 45px);
}
a {
text-decoration: none;
}
.pagetitle {
height: 30px;
color: #F60;
font-size: 20px;
line-height: 30px;
text-align: center;
font-weight: bold;
font-style: oblique
}
.node rect {
cursor: move;
fill-opacity: .9;
shape-rendering: crispEdges
}
.node text {
pointer-events: none;
text-shadow: 0 1px 0 #fff
}
.link {
stroke: #000;
stroke-opacity: .3;
}
.link:hover {
stroke-opacity: .7;
}
#titlebar {
width: -moz-calc(80% - 90px);
width: -webkit-calc(80% - 90px);
width: calc(80% - 90px);
height: 30px;
border-bottom: solid;
border-color: #EEE;
padding-bottom: 5px;
margin-left: 40px;
margin-top: 5px;
position: fixed;
z-index: 50;
}
#titlebar-right {
right: -moz-calc(20% + 50px);
right: -webkit-calc(20% + 50px);
right: calc(20% + 50px);
height: 30px;
padding-bottom: 5px;
margin-left: 40px;
margin-top: 5px;
position: fixed;
z-index: 50;
}
.dragdealer {
height: 30px;
background: #EEE;
z-index: 50;
}
.dragdealer .handle {
top: 0;
left: 0;
cursor: pointer;
}
.dragdealer .red-bar {
width: 100px;
height: 30px;
background: #F80;
color: #FFF;
font-size: 20px;
line-height: 30px;
text-align: center;
font-weight: bold;
font-style: oblique
}
.dragdealer .red-bar:hover {
background: #F60
}
.dragdealer .orange-bar {
width: 25px;
height: 18px;
background: #F80;
color: #FFF;
font-size: 18px;
line-height: 16px;
text-align: center;
font-weight: bold;
}
.dragdealer .orange-bar:hover {
background: #F60
}
.dragdealer .disabled {
background: #898989
}
#social {
position: fixed;
bottom: 5;
right: 0;
text-decoration: none;
padding: 3px;
padding-top:1px;
z-index: 800;
white-space:nowrap;
overflow:hidden;
}
#social a{
margin-left:10px;
margin-right:5px;
color: #000;
font-size: 10px;
font-family: "Trebuchet MS";
font-weight: lighter;
text-decoration: none;
}
.likeform input {
width:100%;
min-width:220px;
}
</style>
<script src="dragdealer.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var sizecorrection = Math.max(0, 220 - parseInt(window.innerWidth * 0.2));
function checksize() {
if ((window.innerWidth < 600) || (window.innerHeight < 300)) {
alert("The recommended minimum resolution is 600 x 300.\n Yours is " + window.innerWidth + " x " + window.innerHeight + ".");
}
setTimeout(function(){
d3.select("#social").transition().style("opacity", 1);
},3000);
}
window.onresize = function() {
window.location.reload();
}
</script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-40713687-2', 'auto');
ga('send', 'pageview');
</script>
<script type="text/javascript">var switchTo5x=true;</script>
<script type="text/javascript" src="http://w.sharethis.com/button/buttons.js"></script>
<script type="text/javascript">stLight.options({publisher: "505a26ad-d820-47bd-a500-7f49d04a30f5", doNotHash: true, doNotCopy: false, hashAddressBar: false});</script>
</head>
<body onload="checksize();">
<!--TITLELBAR-->
<div id="titlebar">
<span id="titletext" class="pagetitle">Sankey Diagram</span>
</div>
<!--PAGE WRAPPER-->
<div id="content" style="opacity:1;">
<!--SANKEY DIAGRAM CONTROLS-->
<div id="sourcecontrol" style="float:right; margin-right:10px; width:20%; margin-top:10px; min-width:220px; border-left:solid; border-color:#EEE; height:98%; padding-left:20px;line-height:26px;z-index:600000;">
<!--LOAD-->
<div id="load" style="position:fixed;float:left;width:inherit;height:100%;background:#fff;color:#000;opacity:0;margin-top:0px;margin-left:0px;z-index:-1;">
<div>Please paste the Sankey string below:</div>
<textarea id="loadtext" style="margin-top:20px;margin-bottom:20px;width:97%;height:50%;"/></textarea>
<div><button onclick="loadsubmit();">Done</button></div>
</div>
<!--SAVE-->
<div id="save" style="position:fixed;float:left;width:inherit;height:100%;background:#fff;color:#000;opacity:0;margin-top:0px;margin-left:0px;z-index:-1;">
<div>Please copy and save the text below to load later:</div>
<textarea id="savetext" style="margin-top:20px;margin-bottom:20px;width:97%;height:50%;"/></textarea>
<div><button onclick="d3.select('#save').transition().style('opacity',0).style('z-index',-1)">Done</button></div>
</div>
<!--INCREMENTAL-->
<div style="display:block;overflow: hidden;white-space: nowrap;"><span style="float:left;">Move nodes:</span>
<div style="float:right; padding-right:0; margin-right:0;">
<div style="font-size:20px;font-weight:bold;line-height:10px;">
<input title="Enable horizontal node dragging" type="checkbox" id="xmove">
<label for="xmove">↔</label>
<img alt="" style="width:5px;height:0px;">
<input title="Enable vertical node dragging" type="checkbox" id="ymove" checked>
<label for="ymove">↕</label>
</div>
</div>
</div>
<div style="display:block;overflow: hidden;white-space: nowrap;"><span style="float:left;">Density:</span>
<div style="float:right;">
<div id="pslider" title="Drag slider to set diagram node padding" class="dragdealer" style="margin-top:2px;margin-left:-130px;width:130px; height:18px; position:absolute;">
<div class="handle orange-bar">↔
<script>
var densityslider = new Dragdealer("pslider", {
x: 0.5,
steps: 5,
snap: true,
callback: function(a, b) {
padding = paddingmultiplier * (1 - a) + 3;
draw()
}
});
</script>
</div>
</div>
</div>
</div>
<div style="display:block;overflow: hidden;white-space: nowrap;"><span style="float:left;">Opacity:</span>
<div style="float:right;">
<div id="oslider" title="Drag slider to set diagram flow opacities" class="dragdealer" style="margin-top:2px;margin-left:-130px;width:130px; height:18px; position:absolute;">
<div class="handle orange-bar">↔
<script>
new Dragdealer("oslider", {
x: 0.25,
steps: 5,
snap: true,
callback: function(a, b) {
lowopacity = 0.1 + 0.8 * a;
draw()
}
});
</script>
</div>
</div>
</div>
</div>
<div style="display:block;overflow: hidden;white-space: nowrap;">
<div>
<button onclick="draw()" style="width:100%; min-width:220px; margin-top:20px;margin-bottom:20px;font-size:20px;">Draw Sankey</button>
</div>
<div style="margin-bottom:20px;display:block;overflow: hidden;white-space: nowrap;">
<span><button onclick="load()" style="width:45%; min-width:100px; float:left;">Load</button></span>
<span><button onclick="save()" style="width:45%; min-width:100px; float:right;">Save</button></span>
</div>
<div style="display:block;overflow: hidden;white-space: nowrap;">
<span><button onclick="addnode()" style="width:45%; min-width:100px; float:left;">Add new node</button></span>
<span><button onclick="removenode()" style="width:45%; min-width:100px; float:right;">Remove node</button></span>
</div>
<div class="likeform" id="nodes-form">
<div><input type="text" value="Oil"/></div>
<div><input type="text" value="Natural Gas" /></div>
<div><input type="text" value="Coal"/></div>
<div><input type="text" value="Fossil Fuels"/></div>
<div><input type="text" value="Electricity"/></div>
<div><input type="text" value="Energy"/></div>
</div>
</div>
<div style="display:block;overflow: hidden;white-space: nowrap;">
<div style="display:block;overflow: hidden;white-space: nowrap;">
<span><button onclick="addlink()" style="width:45%; min-width:100px; float:left;">Add new link</button></span>
<span><button onclick="removelink()" style="width:45%; min-width:100px; float:right;">Remove link</button></span>
</div>
<div class="likeform" id="links-form">
<div><input type="text" value="0,3,15"/></div>
<div><input type="text" value="1,3,20"/></div>
<div><input type="text" value="2,3,25"/></div>
<div><input type="text" value="2,4,25"/></div>
<div><input type="text" value="3,5,60"/></div>
<div><input type="text" value="4,5,25"/></div>
<div><input type="text" value="4,4,5"/></div>
</div>
</div>
</div>
<!--SANKEY DIAGRAM-->
<div id="chart" style="z-index:5;"></div>
<!---------SCRIPTS----------->
<script src="js/sankey.js"></script>
<script src="js/food.js"></script>
</div>
<!--SOCIAL-->
<div id="social" style="opacity:0; z-index:800;">
<span class='st_facebook'></span>
<span class='st_twitter'></span>
<span class='st_googleplus'></span>
<span class='st_linkedin'></span>
<span class='st_tumblr'></span>
<span class='st_reddit'></span>
<span class='st_email'></span>
<span><a href="http://www.csaladen.es" title="created by" alt="created by">© D.C. 2014</a></span>
</div>
</body>
</html>
http://sankey.csaladen.es
d3.sankey=function(){function u(){i.forEach(function(e){e.sourceLinks=[];e.targetLinks=[]});s.forEach(function(e){var t=e.source,n=e.target;if(typeof t==="number")t=e.source=i[e.source];if(typeof n==="number")n=e.target=i[e.target];t.sourceLinks.push(e);n.targetLinks.push(e)})}function a(){i.forEach(function(e){e.value=Math.max(d3.sum(e.sourceLinks,g),d3.sum(e.targetLinks,g))})}function f(){function n(r){r.index=t++;r.lowIndex=r.index;r.onStack=true;e.push(r);if(r.sourceLinks){r.sourceLinks.forEach(function(e){var t=e.target;if(!t.hasOwnProperty("index")){n(t);r.lowIndex=Math.min(r.lowIndex,t.lowIndex)}else if(t.onStack){r.lowIndex=Math.min(r.lowIndex,t.index)}});if(r.lowIndex===r.index){var i=[],s;do{s=e.pop();s.onStack=false;i.push(s)}while(s!=r);o.push({root:r,scc:i})}}}var e=[],t=0;i.forEach(function(e){if(!e.index){n(e)}});o.forEach(function(e,t){e.index=t;e.scc.forEach(function(e){e.component=t})})}function l(){function u(e){return[].concat.apply([],e)}function a(){var e=o,t,n,r=0;while(e.length){t=[];n={};e.forEach(function(e){e.x=r;e.scc.forEach(function(r){r.sourceLinks.forEach(function(r){if(!n.hasOwnProperty(r.target.component)&&r.target.component!=e.index){t.push(o[r.target.component]);n[r.target.component]=true}})})});e=t;++r}}function f(e,n){var r=[e],i=1,s=0;var o=0;while(i>0){var u=r.shift();i--;if(!u.hasOwnProperty("x")){u.x=o;u.dx=t;var a=n(u);r=r.concat(a);s+=a.length}if(i==0){o++;i=s;s=0}}}a();o.forEach(function(e,t){f(e.root,function(e){var n=e.sourceLinks.filter(function(e){return e.target.component==t}).map(function(e){return e.target});return n})});var e=0;var n=d3.nest().key(function(e){return e.x}).sortKeys(d3.ascending).entries(o).map(function(e){return e.values});var e=-1,s=-1;n.forEach(function(t){t.forEach(function(t){t.x=e+1;t.scc.forEach(function(e){e.x=t.x+e.x;s=Math.max(s,e.x)})});e=s});i.filter(function(e){var t=e.sourceLinks.filter(function(e){return e.source.name!=e.target.name});return t.length==0}).forEach(function(t){t.x=e});p((r[0]-t)/Math.max(e,1))}function c(){i.forEach(function(e){if(!e.targetLinks.length){e.x=d3.min(e.sourceLinks,function(e){return e.target.x})-1}})}function h(e){i.forEach(function(t){if(!t.sourceLinks.length){t.x=e-1}})}function p(e){i.forEach(function(t){t.x*=e})}function d(e){function u(){var e=d3.min(t,function(e){return(r[1]-(e.length-1)*n)/d3.sum(e,g)});t.forEach(function(t){t.forEach(function(t,n){t.y=n;t.dy=t.value*e})});s.forEach(function(t){t.dy=t.value*e})}function a(e){function n(e){return m(e.source)*e.value}t.forEach(function(t,r){t.forEach(function(t){if(t.targetLinks.length){var r=d3.sum(t.targetLinks,n)/d3.sum(t.targetLinks,g);t.y+=(r-m(t))*e}})})}function f(e){function n(e){return m(e.target)*e.value}t.slice().reverse().forEach(function(t){t.forEach(function(t){if(t.sourceLinks.length){var r=d3.sum(t.sourceLinks,n)/d3.sum(t.sourceLinks,g);t.y+=(r-m(t))*e}})})}function l(){t.forEach(function(e){var t,i,s=0,o=e.length,u;e.sort(c);for(u=0;u<o;++u){t=e[u];i=s-t.y;if(i>0)t.y+=i;s=t.y+t.dy+n}i=s-n-r[1];if(i>0){s=t.y-=i;for(u=o-2;u>=0;--u){t=e[u];i=t.y+t.dy+n-s;if(i>0)t.y-=i;s=t.y}}})}function c(e,t){return e.y-t.y}var t=d3.nest().key(function(e){return e.x}).sortKeys(d3.ascending).entries(i).map(function(e){return e.values});u();l();for(var o=1;e>0;--e){f(o*=.99);l();a(o);l()}}function v(){function e(e,t){return e.source.y-t.source.y}function t(e,t){return e.target.y-t.target.y}i.forEach(function(n){n.sourceLinks.sort(t);n.targetLinks.sort(e)});i.forEach(function(e){var t=0,n=0;e.sourceLinks.forEach(function(e){e.sy=t;t+=e.dy});e.targetLinks.forEach(function(e){e.ty=n;n+=e.dy})})}function m(e){return e.y+e.dy/2}function g(e){return e.value}var e={},t=24,n=8,r=[1,1],i=[],s=[],o=[];e.nodeWidth=function(n){if(!arguments.length)return t;t=+n;return e};e.nodePadding=function(t){if(!arguments.length)return n;n=+t;return e};e.nodes=function(t){if(!arguments.length)return i;i=t;return e};e.links=function(t){if(!arguments.length)return s;s=t;return e};e.size=function(t){if(!arguments.length)return r;r=t;return e};e.layout=function(t){u();a();f();l();d(t);v();return e};e.relayout=function(){v();return e};e.reversibleLink=function(){function t(t,n){var r=n.source.x+n.source.dx,i=n.target.x,s=d3.interpolateNumber(r,i),o=s(e),u=s(1-e),a=n.source.y+n.sy,f=n.target.y+n.ty,l=n.source.y+n.sy+n.dy,c=n.target.y+n.ty+n.dy;switch(t){case 0:return"M"+r+","+a+"L"+r+","+(a+n.dy);case 1:return"M"+r+","+a+"C"+o+","+a+" "+u+","+f+" "+i+","+f+"L"+i+","+c+"C"+u+","+c+" "+o+","+l+" "+r+","+l+"Z";case 2:return"M"+i+","+f+"L"+i+","+(f+n.dy)}}function n(e,t){function i(e){return e.source.y+e.sy>e.target.y+e.ty?-1:1}function s(e,t){return e+","+t+" "}var n=30;var r=15;var o=i(t)*r,u=t.source.x+t.source.dx,a=t.source.y+t.sy,f=t.target.x,l=t.target.y+t.ty;switch(e){case 0:return"M"+s(u,a)+"C"+s(u,a)+s(u+n,a)+s(u+n,a+o)+"L"+s(u+n,a+o+t.dy)+"C"+s(u+n,a+t.dy)+s(u,a+t.dy)+s(u,a+t.dy)+"Z";case 1:return"M"+s(u+n,a+o)+"C"+s(u+n,a+3*o)+s(f-n,l-3*o)+s(f-n,l-o)+"L"+s(f-n,l-o+t.dy)+"C"+s(f-n,l-3*o+t.dy)+s(u+n,a+3*o+t.dy)+s(u+n,a+o+t.dy)+"Z";case 2:return"M"+s(f-n,l-o)+"C"+s(f-n,l)+s(f,l)+s(f,l)+"L"+s(f,l+t.dy)+"C"+s(f,l+t.dy)+s(f-n,l+t.dy)+s(f-n,l+t.dy-o)+"Z"}}var e=.5;return function(e){return function(r){if(r.source.x<r.target.x){return t(e,r)}else{return n(e,r)}}}};e.link=function(){function t(t){var n=t.source.x+t.source.dx,r=t.target.x,i=d3.interpolateNumber(n,r),s=i(e),o=i(1-e),u=t.source.y+t.sy+t.dy/2,a=t.target.y+t.ty+t.dy/2;return"M"+n+","+u+"C"+s+","+u+" "+o+","+a+" "+r+","+a}var e=.5;t.curvature=function(n){if(!arguments.length)return e;e=+n;return t};return t};return e}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment