index.js
var d3 = require('d3')
, mousetrap = require('mousetrap')
, debounce = require('debounce')
, resize = require('./resize')
var root = document.getElementById('colony')
, colony = window.colony
var nodes = colony.nodes
, links = colony.links
, scale = colony.scale
, focus
var width = 600
, height = 400
, link
, node
, text
, textTarget = false
var colors = {
links: 'FAFAFA'
, text: {
subtitle: 'FAFAFA'
}
, nodes: {
method: function(d) {
return groups[d.group].color
}
, hover: 'FAFAFA'
, dep: '252929'
}
}
var readme = document.getElementById('readme-contents').innerHTML
links.forEach(function(link) {
var source = nodes[link.source]
, target = nodes[link.target]
source.children = source.children || []
source.children.push(link.target)
target.parents = target.parents || []
target.parents.push(link.source)
})
var groups = nodes.reduce(function(groups, file) {
var group = file.mgroup || 'none'
, index = groups.indexOf(group)
if (index === -1) {
index = groups.length
groups.push(group)
}
file.group = index
return groups
}, [])
groups = groups.map(function(name, n) {
var color = d3.hsl(n / groups.length * 300, 0.7, 0.725)
return {
name: name
, color: color.toString()
};
})
var force = d3.layout.force()
.size([width, height])
.charge(-50 * colony.scale)
.linkDistance(20 * colony.scale)
.on('tick', function() {
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; })
node.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; })
if (textTarget) {
text.attr('transform'
, 'translate(' + textTarget.x + ',' + textTarget.y + ')')
}
})
var vis = d3.select(root)
.append('svg')
.attr('width', width)
.attr('height', height)
force.nodes(nodes)
.links(links)
.start()
link = vis.selectAll('line.link').data(links)
node = vis.selectAll('circle.node')
.data(nodes, function(d) { return d.filename })
link.enter()
.insert('line', '.node')
.attr('class', '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; })
.style('stroke', colors.links)
.style('opacity', function(d) {
return d.target.module ? 0.2 : 0.3
})
node.enter()
.append('circle')
.attr('class', 'node')
.attr('cx', function(d) { return d.x })
.attr('cy', function(d) { return d.y })
.attr('r', function(d) {
return scale * (d.root ? 8 : d.module && !d.native ? 5 : 3)
})
.style('fill', colors.nodes.method)
.call(force.drag)
.on('mouseover', function(d) {
textTarget = d
text.attr('transform', 'translate(' + d.x + ',' + d.y + ')')
.text(d.id)
.style('display', null)
d3.select(this)
.style('fill', colors.nodes.hover)
d3.selectAll(childNodes(d))
.style('fill', colors.nodes.hover)
.style('stroke', colors.nodes.method)
.style('stroke-width', 2)
d3.selectAll(parentNodes(d))
.style('fill', colors.nodes.dep)
.style('stroke', colors.nodes.method)
.style('stroke-width', 2)
})
.on('mouseout', function(d) {
textTarget = false
text.style('display', 'none')
d3.select(this)
.style('fill', colors.nodes.method)
d3.selectAll(childNodes(d))
.style('fill', colors.nodes.method)
.style('stroke', null)
d3.selectAll(parentNodes(d))
.style('fill', colors.nodes.method)
.style('stroke', null)
})
.on('click', function(d) {
if (focus === d) {
force.charge(-50 * colony.scale)
.linkDistance(20 * colony.scale)
.linkStrength(1)
.start()
node.style('opacity', 1)
link.style('opacity', function(d) {
return d.target.module ? 0.2 : 0.3
})
focus = false
d3.select('#readme-contents')
.html(readme)
.classed('showing-code', false)
return
}
focus = d
d3.xhr('./files/' + d.filename + '.html', function(res) {
if (!res) return
d3.select('#readme-contents')
.html(res.responseText)
.classed('showing-code', true)
document.getElementById('readme')
.scrollTop = 0
})
node.style('opacity', function(o) {
o.active = connected(d, o)
return o.active ? 1 : 0.2
})
force.charge(function(o) {
return (o.active ? -100 : -5) * colony.scale
}).linkDistance(function(l) {
return (l.source.active && l.target.active ? 100 : 20) * colony.scale
}).linkStrength(function(l) {
return (l.source === d || l.target === d ? 1 : 0) * colony.scale
}).start()
link.style('opacity', function(l, i) {
return l.source.active && l.target.active ? 0.2 : 0.02
})
})
text = vis.selectAll('.nodetext')
.data([
[-1.5, 1.5, 1]
, [ 1.5, 1.5, 1]
, [-1.5, -1.5, 1]
, [ 1.5, -1.5, 1]
, [ 0.0, 0.0, 0]
])
.enter()
.insert('text', ':last-child')
.attr('class', 'nodetext')
.classed('shadow', function(d) { return d[2] })
.attr('dy', function(d) { return d[0] - 10 })
.attr('dx', function(d) { return d[1] })
.attr('text-anchor', 'middle')
function refresh(e) {
width = Math.max(window.innerWidth * 0.5, 500)
height = window.innerHeight
force.size([width, height])
.resume()
vis.attr('width', window.innerWidth)
.attr('height', height)
};
function childNodes(d) {
if (!d.children) return []
return d.children
.map(function(child) {
return node[0][child]
}).filter(function(child) {
return child
})
};
function parentNodes(d) {
if (!d.parents) return []
return d.parents
.map(function(parent) {
return node[0][parent]
}).filter(function(parent) {
return parent
})
};
function connected(d, o) {
return o.index === d.index ||
(d.children && d.children.indexOf(o.index) !== -1) ||
(o.children && o.children.indexOf(d.index) !== -1) ||
(o.parents && o.parents.indexOf(d.index) !== -1) ||
(d.parents && d.parents.indexOf(o.index) !== -1)
};
function restartForce() {
var theta = force.theta()
force.start()
.theta(theta)
};
resize(debounce(refresh, 500))
refresh()
mousetrap.bind(['~', '`'], function() {
var readme = d3.select('#readme')
readme.classed('enlarged', !readme.classed('enlarged'))
})