Last active
August 7, 2016 16:38
-
-
Save binary10ve/1eef742d6c821a8f1d1920263ec1b18a to your computer and use it in GitHub Desktop.
Network graph
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<title>Orbit Layout Modes</title> | |
<meta charset="utf-8" /> | |
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> | |
<style> | |
body, html { | |
width: 100%; | |
margin: 0; | |
font-family: "Helvetica Neue", Helvetica, sans-serif; | |
font-size: 12px; | |
-webkit-backface-visibility: hidden; | |
} | |
body{ | |
padding: 40px; | |
height: 1000px; | |
} | |
.node { | |
cursor: pointer; } | |
.node.leaf { | |
cursor: default; } | |
.node circle { | |
fill: lightsteelblue; | |
stroke: steelblue; | |
stroke-width: 1.5px; } | |
.node.leaf circle { | |
fill: #fff; } | |
.node text { | |
font-size: 11px; } | |
.link { | |
fill: none; | |
stroke: #ccc; | |
stroke-width: 1.5px; } | |
@-webkit-keyframes bounceIn { | |
0% { | |
opacity: 0; | |
-webkit-transform: scale(.3); | |
} | |
50% { | |
opacity: 1; | |
-webkit-transform: scale(1.05); | |
} | |
70% { | |
-webkit-transform: scale(.9); | |
} | |
100% { | |
-webkit-transform: scale(1); | |
} | |
} | |
@keyframes bounceIn { | |
0% { | |
opacity: 0; | |
transform: scale(.3); | |
} | |
50% { | |
opacity: 1; | |
transform: scale(1.05); | |
} | |
70% { | |
transform: scale(.9); | |
} | |
100% { | |
transform: scale(1); | |
} | |
} | |
.d3-tip.animate { | |
animation: bounceIn 0.2s ease-out; | |
-webkit-animation: bounceIn 0.2s ease-out; | |
} | |
.d3-tip span { | |
color: #ff00c7; | |
} | |
.domain { | |
display: none; | |
} | |
.axis line { | |
stroke-width: 1px; | |
stroke: #eee; | |
shape-rendering: crispedges; | |
} | |
.axis text { | |
fill: #888; | |
} | |
rect { | |
fill: #339cff; | |
fill-opacity: 0.7; | |
} | |
rect:hover { | |
fill-opacity: 1; | |
} | |
.user-panel { | |
padding: 10px; | |
background-color: white; | |
border:1px solid #E0E0E0; | |
border-radius: 8px; | |
} | |
.user-panel:before, | |
.user-panel:after { | |
display: table; | |
content: " "; | |
} | |
.user-panel:after { | |
clear: both; | |
} | |
.user-panel > .image > img { | |
width: 60px; | |
height: 60px; | |
} | |
.user-panel > .info { | |
padding: 5px 5px 5px 15px; | |
font-size: 14px; | |
line-height: 1; | |
} | |
.user-panel > .info > p { | |
margin-bottom: 9px; | |
} | |
.user-panel > .info > a { | |
text-decoration: none; | |
padding-right: 5px; | |
margin-top: 3px; | |
font-size: 11px; | |
font-weight: normal; | |
} | |
.user-panel > .info > a > .fa, | |
.user-panel > .info > a > .ion, | |
.user-panel > .info > a > .glyphicon { | |
margin-right: 3px; | |
} | |
.user-panel .info .title{ | |
font-style: 16px; | |
} | |
.user-panel .info .description{ | |
font-size: 12px; | |
color: #999; | |
} | |
.typeahead, | |
.tt-query, | |
.tt-hint { | |
width: 300px; | |
height: 30px; | |
padding: 8px 12px; | |
font-size: 16px; | |
border: 2px solid #ccc; | |
-webkit-border-radius: 8px; | |
-moz-border-radius: 8px; | |
border-radius: 8px; | |
outline: none; | |
} | |
.typeahead { | |
background-color: #fff; | |
} | |
.typeahead:focus { | |
border: 2px solid #0097cf; | |
} | |
.tt-query { | |
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); | |
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); | |
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); | |
} | |
.tt-hint { | |
color: #999 | |
} | |
.tt-menu { | |
width: 300px; | |
margin: 12px 0; | |
padding: 8px 0; | |
background-color: #fff; | |
border: 1px solid #ccc; | |
border: 1px solid rgba(0, 0, 0, 0.2); | |
-webkit-border-radius: 8px; | |
-moz-border-radius: 8px; | |
border-radius: 8px; | |
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2); | |
-moz-box-shadow: 0 5px 10px rgba(0,0,0,.2); | |
box-shadow: 0 5px 10px rgba(0,0,0,.2); | |
} | |
.tt-suggestion { | |
padding: 3px 20px; | |
font-size: 18px; | |
line-height: 24px; | |
} | |
.tt-suggestion:hover { | |
cursor: pointer; | |
color: #fff; | |
background-color: #0097cf; | |
} | |
.tt-suggestion.tt-cursor { | |
color: #fff; | |
background-color: #0097cf; | |
} | |
.tt-suggestion p { | |
margin: 0; | |
} | |
svg .n1, svg .n2, svg .n3{ | |
cursor: pointer; | |
} | |
.n2 text{ | |
font-size: 0.8em; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="network-grap-container" style="width:800px;margin: 0 auto;"> | |
<div id="the-basics"> | |
<input class="typeahead" type="text" placeholder="Search"> | |
</div> | |
</div> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.2.0/d3.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js"></script> | |
<script type="text/javascript"> | |
var NetworkGraph = (function () { | |
var NG = { | |
canvas: null, | |
store: [], | |
init: init | |
}; | |
var dimension = 650,zoom; | |
function setupCanvas() { | |
zoom = d3.behavior.zoom() | |
.scaleExtent([1, 8]) | |
.on("zoom", zoomed); | |
NG.canvas = d3.select("#network-grap-container") | |
.append("svg") | |
.attr("width", "100%") | |
.attr("height", "100%") | |
.call(zoom) | |
.append("g") | |
.attr("class", ".canvas") | |
} | |
function setUpPathHighlighter() { | |
NG.highlighter = NG.canvas.append("path") | |
.attr("stroke", "#9467bd") | |
.attr("stroke-width", "6") | |
.attr("fill", "none") | |
.style("visibility", "hidden"); | |
} | |
function zoomed() { | |
var t = d3.event.translate, | |
s = d3.event.scale; | |
t[0] = Math.min(dimension / 2 * (s - 1), Math.max(dimension / 2 * (1 - s), t[0])); | |
t[1] = Math.min(dimension / 2 * (s - 1) + 230 * s, Math.max(dimension / 2 * (1 - s) - 230 * s, t[1])); | |
zoom.translate(t); | |
NG.canvas.attr("transform", "translate(" + t + ")scale(" + s + ")"); | |
} | |
function hexagonPath() { | |
return d3.svg.line() | |
.x(function (d) { | |
return d.x; | |
}) | |
.y(function (d) { | |
return d.y; | |
}) | |
.interpolate("cardinal-closed") | |
.tension(1); | |
} | |
function drawHexagon(hexagonCorners) { | |
NG.canvas.append("path") | |
.attr("d", hexagonPath()(hexagonCorners)) | |
.attr("stroke", "#FFC107") | |
.attr("stroke-width", 2) | |
.attr("fill", "none"); | |
hexagonCorners.forEach(function (d) { | |
NG.canvas.append("path") | |
.attr("d", "M " + d.x + " " + d.y + " L " + dimension / 2 + " " + dimension / 2) | |
.attr("stroke", "#FFC107") | |
.attr("stroke-dasharray", "5,5") | |
.attr("stroke-width", 1) | |
.attr("class", "hexagon-path") | |
}) | |
} | |
function setUpN1Container() { | |
var hexagonCorners = getHexagonPath(); | |
this.attr("class", "n1-container") | |
.attr("x", function (d, i) { | |
var corner = hexagonCorners[i]; | |
return corner.x; | |
}) | |
.attr("y", function (d, i) { | |
var corner = hexagonCorners[i]; | |
return corner.y; | |
}) | |
.attr("transform", function (d, i) { | |
var corner = hexagonCorners[i]; | |
d.path = [dimension / 2, dimension / 2, corner.x, corner.y]; | |
NG.store.push({ | |
name: d.name, | |
path: [dimension / 2, dimension / 2, corner.x, corner.y].join(',') | |
}); | |
return "translate(" + corner.x + "," + corner.y + ")"; | |
}) | |
.attr("pathXY", function (d, i) { | |
return d.path.join(','); | |
}) | |
} | |
function drawNetwork(_data) { | |
var hexagonCorners = getHexagonPath(); | |
drawHexagon(hexagonCorners); | |
var n1Container = NG.canvas | |
.selectAll(".n1-container") | |
.data(_data.children) | |
.enter() | |
.append("g") | |
.call(setUpN1Container); | |
n1Container.call(drawN1); | |
var n2N3Container = n1Container | |
.selectAll(".n2-n3-container") | |
.data(function (d, i) { | |
return d.children.map(function (c) { | |
c.l = d.children.length; | |
return c; | |
}); | |
}) | |
.enter() | |
.append("g") | |
n2N3Container.call(setUpN2N3Container) | |
n2N3Container.each(drawN3); | |
n2N3Container.call(drawN2); | |
drawRootNode(_data); | |
NG.events.onLoad(); | |
} | |
function setUpN2N3Container(){ | |
this.attr("class", "n2-n3-container") | |
.attr("pathXY", function (d, i) { | |
var parent = d3.select(this.parentNode); | |
d.dx = NG.n1LoopScale(dimension) * Math.cos(2 * Math.PI * i / d.l); | |
d.dy = NG.n1LoopScale(dimension) * Math.sin(2 * Math.PI * i / d.l); | |
d.path = [parent.attr("pathXY"), | |
parseInt(parent.attr("x")) + parseInt(d.dx), | |
parseInt(parent.attr("y")) + parseInt(d.dy)].join(','); | |
NG.store.push({ | |
name: d.name, | |
path: d.path | |
}); | |
return d.path; | |
}) | |
.attr("x", function (d, i) { | |
return d.dx; | |
}) | |
.attr("y", function (d, i) { | |
return d.dy; | |
}) | |
.transition() | |
.duration(1000) | |
.attr("transform", function (d, i) { | |
var parent = d3.select(this.parentNode); | |
d.path = [parent.attr("pathXY"), parseInt(parent.attr("x")) + d.dx, parseInt(parent.attr("y")) + d.dy].join(','); | |
NG.store.push({ | |
name: d.name, | |
path: d.path | |
}); | |
return "translate(" + d.dx + "," + d.dy + ")"; | |
}) | |
} | |
function drawN3(d) { | |
var _that = this; | |
if (d.children) { | |
var spaceScale = d3.scale.linear() | |
.range([0, 70]) | |
.domain([1, 6]), | |
l = d.children.length, | |
theta = Math.atan2(d.dy, d.dx) * 180 / Math.PI, | |
degree = theta > 0 ? 360 - theta : Math.abs(theta), | |
degreeScale = d3.scale.linear() | |
.range([degree + spaceScale(l), degree - spaceScale(l)]) | |
.domain([0, l - 1]); | |
d.children.forEach(function (d, i) { | |
d.cx = 35 * Math.cos(degreeScale(i) * (Math.PI / 180)) | |
d.cy = -(35 * Math.sin(degreeScale(i) * (Math.PI / 180))) | |
}); | |
d3.select(_that) | |
.selectAll(".n3-branch") | |
.data(d.children) | |
.enter() | |
.append("path") | |
.attr("class", "n3-branch") | |
.attr("stroke", "#F64F22") | |
.attr("stroke-width", 1) | |
.attr("d", function (d, i) { | |
return "M 0 0 L " + d.cx + " " + d.cy | |
}) | |
var n3 = d3.select(_that) | |
.selectAll(".n3") | |
.data(d.children) | |
.enter() | |
.append("g") | |
.attr("class", "n3") | |
.attr("transform", function (b, i) { | |
var parent = d3.select(this.parentNode); | |
var grandParent = d3.select(this.parentNode.parentNode); | |
b.path = [parent.attr("pathXY"), parseInt(grandParent.attr("x")) + parseInt(parent.attr("x")) + b.cx, parseInt(grandParent.attr("y")) + parseInt(parent.attr("y")) + b.cy].join(','); | |
NG.store.push({ | |
name: b.name, | |
path: b.path | |
}); | |
return "translate(" + b.cx + "," + b.cy + ")"; | |
}) | |
.attr("pathXY", function (d, i) { | |
return d.path; | |
}) | |
.attr("x", function (d, i) { | |
return d.cx; | |
}) | |
.attr("y", function (d, i) { | |
return d.cy; | |
}) | |
n3.append("circle") | |
.attr('fill', '#AFD097') | |
.attr('r', 8) | |
n3.append('text') | |
.attr("text-anchor", "middle") | |
.attr("fill", "white") | |
.attr("dy", ".3em") | |
.attr("font-size", "5") | |
.text(function (d, i) { | |
return initials(d.name) | |
}); | |
} | |
} | |
function drawN2() { | |
var n2 = this | |
.append("g") | |
.attr("class", "n2"); | |
n2.append("circle") | |
.attr('r', NG.n2RadiusScale(dimension)) | |
.attr('fill', '#F64F22') | |
n2.append('text') | |
.attr("text-anchor", "middle") | |
.attr("fill", "white") | |
.attr("dy", ".3em") | |
.text(function (d, i) { | |
return initials(d.name) | |
}); | |
n2.on('click', function () { | |
var parent = d3.select(this.parentNode), | |
path = parent.selectAll('path'), | |
lastNode = parent.selectAll('.n3'); | |
lastNode | |
.style('visibility', lastNode.style('visibility') == 'hidden' ? 'visible' : 'hidden'); | |
path | |
.style('visibility', path.style('visibility') == 'hidden' ? 'visible' : 'hidden'); | |
}) | |
} | |
function drawN1() { | |
var n1Container = this; | |
var n1 = n1Container | |
.append("g") | |
.attr("class", "n1") | |
.attr("expanded", 'true') | |
.on('click', function () { | |
var sn = d3.select(this.parentNode) | |
.selectAll('.n2-n3-container,.n1-loop,.n3-branch,.n3'); | |
var expanded = d3.select(this).attr('expanded') == 'true' | |
sn.style('visibility', expanded ? 'hidden' : 'visible') | |
d3.select(this).attr('expanded', !expanded) | |
}) | |
n1.append("circle") | |
.attr("r", NG.n1RadiusScale(dimension)) | |
.attr("fill", "#23B3F4") | |
n1.append("text") | |
.attr("text-anchor", "middle") | |
.attr("fill", "white") | |
.attr("dy", ".3em") | |
.text(function (d, i) { | |
return initials(d.name) | |
}) | |
n1Container | |
.append("circle") | |
.attr("class", "n1-loop") | |
.attr("fill", "none") | |
.attr("stroke", "#23B3F4") | |
.attr("stroke-dasharray", "3,3") | |
.transition() | |
.delay(700) | |
.attr('r', NG.n1LoopScale(dimension)) | |
} | |
function drawRootNode(_data) { | |
var rootNode = NG.canvas.selectAll(".root-node") | |
.data([_data]) | |
.enter() | |
.append("g") | |
.attr("class", "root-node") | |
.attr("transform", function (d) { | |
return "translate(" + dimension / 2 + "," + dimension / 2 + ")"; | |
}) | |
rootNode.append("circle") | |
.attr("r", " 30") | |
.attr("fill", "#FFC107") | |
rootNode.append("text") | |
.attr("text-anchor", "middle") | |
.attr("fill", "white") | |
.attr("dy", ".3em") | |
.text(initials(_data.name)) | |
} | |
function getHexagonPath(config) { | |
var radius = dimension / 3, | |
xp = dimension / 2, | |
yp = xp, | |
h = (Math.sqrt(3) / 2); | |
return [ | |
{"x": radius + xp, "y": yp}, | |
{"x": radius / 2 + xp, "y": radius * h + yp}, | |
{"x": -radius / 2 + xp, "y": radius * h + yp}, | |
{"x": -radius + xp, "y": yp}, | |
{"x": -radius / 2 + xp, "y": -radius * h + yp}, | |
{"x": radius / 2 + xp, "y": -radius * h + yp}]; | |
} | |
function highlight(node) { | |
if (node) { | |
NG.highlighter | |
.attr("d", "M " + node.path) | |
.style("visibility", "visible") | |
} else { | |
NG.highlighter | |
.style("visibility", "hidden") | |
} | |
} | |
function setDispatcher(config) { | |
if (config.events) { | |
NG.events = d3.dispatch(Object.keys(config.events).join(',')); | |
Object.keys(config.events).forEach(function (event) { | |
NG.events.on(event, config.events[event]); | |
}); | |
} | |
} | |
function setUpScales(config) { | |
var svgWidth = parseInt(d3.select(NG.canvas.node().parentNode).style('width')) | |
NG.n1LoopScale = d3.scale.linear().domain([400, Math.max(config.dimension || svgWidth, svgWidth)]).range([30, 70]) | |
NG.n1RadiusScale = d3.scale.linear().domain([400, svgWidth]).range([15, 20]); | |
NG.n2RadiusScale = d3.scale.linear().domain([400, svgWidth]).range([10, 15]); | |
} | |
function init(config) { | |
dimension = config.dimension || dimension; | |
setDispatcher(config) | |
setupCanvas(); | |
setUpScales(config) | |
setUpPathHighlighter(); | |
drawNetwork(config.data); | |
} | |
function initials(name) { | |
var regex = /(\b[a-zA-Z0-9])[a-zA-Z0-9]* ?/g, | |
matches, | |
output = []; | |
while (matches = regex.exec(name)) { | |
output.push(matches[1]); | |
} | |
return output.join(''); | |
} | |
return { | |
init: init, | |
highlight: highlight, | |
getStore: function () { | |
return NG.store; | |
} | |
}; | |
}()); | |
var SearchSuggestions = {}; | |
(function(search){ | |
search.init = init; | |
function init(){ | |
var substringMatcher = function(strs) { | |
return function findMatches(q, cb) { | |
var matches, substringRegex,substrRegex; | |
// an array that will be populated with substring matches | |
matches = []; | |
// regex used to determine if a string contains the substring `q` | |
substrRegex = new RegExp(q, 'i'); | |
// iterate through the pool of strings and for any string that | |
// contains the substring `q`, add it to the `matches` array | |
$.each(strs, function(i, str) { | |
if (substrRegex.test(str.name)) { | |
matches.push(str); | |
} | |
}); | |
cb(matches); | |
}; | |
}; | |
$('#the-basics .typeahead').typeahead({ hint: true, | |
highlight: true, | |
minLength: 1}, | |
{ | |
source: substringMatcher(NetworkGraph.getStore()), | |
display : 'name' | |
}) | |
.bind('typeahead:select', function(ev, suggestion) { | |
NetworkGraph.highlight(suggestion); | |
console.log( suggestion.name,'Selection: ' + suggestion.path); | |
}) | |
.bind('keyup', function(){ | |
if($(this).val() == ''){ | |
NetworkGraph.highlight(false); | |
} | |
}) | |
} | |
}(SearchSuggestions)); | |
var Tooltip = {}; | |
(function(tooltip){ | |
tooltip.init = init; | |
tooltip.supplant = supplant; | |
function supplant(template, o) { | |
return template.replace(/{([^{}]*)}/g, | |
function (a, b) { | |
var r = o[b]; | |
return typeof r === 'string' || typeof r === 'number' ? r : a; | |
} | |
); | |
} | |
function init(){ | |
var tooltipMarkUp = d3.select("body") | |
.append("div") | |
.attr("class","tip") | |
.style("position", "absolute") | |
.style("z-index", "10") | |
.style("visibility", "hidden"); | |
var template = document.getElementById('tooltip-template').innerHTML; | |
d3.selectAll(".root-node,.n1,.n2,.n3") | |
.on("mouseover", function (d) { | |
tooltipMarkUp.html(tooltip.supplant(template, d)); | |
return tooltipMarkUp.style("visibility", "visible"); | |
}) | |
.on("mousemove", function () { | |
return tooltipMarkUp.style("top", (d3.event.pageY - 10) + "px").style("left", (d3.event.pageX + 10) + "px"); | |
}) | |
.on("mouseout", function () { | |
return tooltipMarkUp.style("visibility", "hidden"); | |
}); | |
} | |
}(Tooltip)); | |
d3.json("https://gist.github.com/binary10ve/1eef742d6c821a8f1d1920263ec1b18a", function (data) { | |
NetworkGraph.init({ | |
data : data, | |
dimension : 650, | |
events : { | |
onLoad : function(){ | |
Tooltip.init(); | |
SearchSuggestions.init(); | |
} | |
} | |
}); | |
}); | |
</script> | |
<script type="text/x-template" id="tooltip-template"> | |
<div class="user-panel"> | |
<div class="pull-left image"> | |
<img src="../../img/avatar3.png" class="img-circle" alt="User Image"> | |
</div> | |
<div class="pull-left info"> | |
<p class="title">{name}</p> | |
<div class="description"> | |
<p>ceaser.umbre@xyz.com</p> | |
<p>8805670639</p> | |
<p>Commission Earned : $100</p> | |
<p>Expected Commission : $100</p> | |
</div> | |
</div> | |
</div> | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment