Initial public commit, merging files from private repo
commit
cc7aedcbe2
@ -0,0 +1,35 @@
|
||||
JS_FILES = \
|
||||
src/nvtooltip.js \
|
||||
src/intro.js \
|
||||
src/core.js \
|
||||
src/models/legend.js \
|
||||
src/models/xaxis.js \
|
||||
src/models/yaxis.js \
|
||||
src/models/bar.js \
|
||||
src/models/line.js \
|
||||
src/models/lineWithFocus.js \
|
||||
src/models/lineWithLegend.js \
|
||||
src/models/scatter.js \
|
||||
src/models/scatterWithLegend.js \
|
||||
src/models/sparkline.js \
|
||||
src/outro.js
|
||||
|
||||
JS_COMPILER = \
|
||||
uglifyjs
|
||||
|
||||
all: nv.d3.js nv.d3.min.js
|
||||
nv.d3.js: $(JS_FILES)
|
||||
nv.d3.min.js: $(JS_FILES)
|
||||
|
||||
nv.d3.js: Makefile
|
||||
rm -f $@
|
||||
cat $(filter %.js,$^) >> $@
|
||||
|
||||
%.min.js:: Makefile
|
||||
rm -f $@
|
||||
cat $(filter %.js,$^) | $(JS_COMPILER) >> $@
|
||||
|
||||
clean:
|
||||
rm -rf nv.d3.js nv.d3.min.js
|
||||
|
||||
|
@ -0,0 +1,3 @@
|
||||
# nv.d3 - v0.0.1
|
||||
|
||||
A reusable chart library for d3 by Bob Monteverde of Novus Partners.
|
@ -0,0 +1,111 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<link href="../src/d3.css" rel="stylesheet" type="text/css">
|
||||
<style>
|
||||
|
||||
body {
|
||||
overflow-y:scroll;
|
||||
}
|
||||
|
||||
text {
|
||||
font: 12px sans-serif;
|
||||
}
|
||||
|
||||
</style>
|
||||
<body>
|
||||
|
||||
<svg id="test1"></svg>
|
||||
|
||||
<script src="../lib/d3.v2.js"></script>
|
||||
<script src="../lib/jquery.min.js"></script>
|
||||
<script src="../nv.d3.js"></script>
|
||||
<script src="../src/models/bar.js"></script>
|
||||
<script>
|
||||
|
||||
var testdata = [
|
||||
{
|
||||
label: "One",
|
||||
y: 5
|
||||
},
|
||||
{
|
||||
label: "Two",
|
||||
y: 2
|
||||
},
|
||||
{
|
||||
label: "Three",
|
||||
y: 9
|
||||
},
|
||||
{
|
||||
label: "Four",
|
||||
y: 7
|
||||
},
|
||||
{
|
||||
label: "Five",
|
||||
y: 4
|
||||
}
|
||||
];
|
||||
|
||||
//Format A
|
||||
nv.addGraph({
|
||||
generate: function() {
|
||||
var width = $(window).width() - 40,
|
||||
height = $(window).height() - 40;
|
||||
|
||||
var chart = nv.models.bar()
|
||||
.width(width)
|
||||
.height(height);
|
||||
|
||||
d3.select("#test1")
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.datum(testdata)
|
||||
.call(chart);
|
||||
|
||||
return chart;
|
||||
},
|
||||
callback: function(graph) {
|
||||
$(window).resize(function() {
|
||||
var width = $(window).width() - 40,
|
||||
height = $(window).height() - 40;
|
||||
|
||||
d3.select("#test1")
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.call(
|
||||
graph
|
||||
.width($(window).width() - 40)
|
||||
.height($(window).height() - 40)
|
||||
)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
//Format B
|
||||
nv.addGraph(function() {
|
||||
var selection = d3.select("body")
|
||||
.datum(irwinHallDistribution(10000, 10));
|
||||
|
||||
var chart = nv.models.histogram()
|
||||
.bins(d3.scale.linear().ticks(20))
|
||||
.tickFormat(d3.format(".02f"));
|
||||
|
||||
chart(selection);
|
||||
|
||||
return chart;
|
||||
}, function(g) { console.log(g.width(), g.height()) })
|
||||
|
||||
//Format C
|
||||
nv.addGraph(function() {
|
||||
return nv.models.histogram()
|
||||
.bins(d3.scale.linear().ticks(20))
|
||||
.tickFormat(d3.format(".02f"))(
|
||||
d3.select("body")
|
||||
.datum(irwinHallDistribution(10000, 10))
|
||||
);
|
||||
}, function(g) { console.log(g.width(), g.height()) })
|
||||
*/
|
||||
|
||||
|
||||
|
||||
</script>
|
@ -0,0 +1,76 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<link href="../src/d3.css" rel="stylesheet" type="text/css">
|
||||
<style>
|
||||
|
||||
</style>
|
||||
<body>
|
||||
|
||||
<svg id="test1"></svg>
|
||||
|
||||
<script src="../lib/d3.v2.js"></script>
|
||||
<script src="../lib/jquery.min.js"></script>
|
||||
<script src="../nv.d3.js"></script>
|
||||
<script src="../src/models/legend.js"></script>
|
||||
<script>
|
||||
|
||||
|
||||
//Format A
|
||||
nv.addGraph({
|
||||
generate: function() {
|
||||
var width = 500,
|
||||
height = 20;
|
||||
|
||||
var chart = nv.models.legend()
|
||||
.width(width)
|
||||
.height(height);
|
||||
|
||||
chart.dispatch.on('legendClick', function(d,i) { log(d,i) });
|
||||
|
||||
//chart.xaxis.tickFormat(d3.format(".02f"))
|
||||
|
||||
d3.select('#test1')
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.datum(sinAndCos())
|
||||
.call(chart);
|
||||
|
||||
return chart;
|
||||
},
|
||||
callback: function(graph) {
|
||||
var chart = graph,
|
||||
height = chart.height();
|
||||
|
||||
d3.select('#test1')
|
||||
.attr('height', height)
|
||||
.call(chart)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
function sinAndCos() {
|
||||
return [
|
||||
{
|
||||
key: "Sine Wave"
|
||||
},
|
||||
{
|
||||
key: "A Very Long Series Label"
|
||||
},
|
||||
{
|
||||
key: "A Very Long Series Label"
|
||||
},
|
||||
{
|
||||
key: "A Very Long Series Label"
|
||||
},
|
||||
{
|
||||
key: "Cosine Wave"
|
||||
},
|
||||
{
|
||||
key: "Another test label"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
</script>
|
@ -0,0 +1,97 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<link href="../src/d3.css" rel="stylesheet" type="text/css">
|
||||
<style>
|
||||
|
||||
body {
|
||||
overflow-y:scroll;
|
||||
}
|
||||
|
||||
</style>
|
||||
<body>
|
||||
|
||||
<svg id="test1"></svg>
|
||||
|
||||
<script src="../lib/d3.v2.js"></script>
|
||||
<script src="../lib/jquery.min.js"></script>
|
||||
<script src="../nv.d3.js"></script>
|
||||
<script src="../src/models/legend.js"></script>
|
||||
<script src="../src/models/line.js"></script>
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
//Format A
|
||||
nv.addGraph({
|
||||
generate: function() {
|
||||
var width = $(window).width() - 40,
|
||||
height = $(window).height() - 40;
|
||||
|
||||
var chart = nv.models.line()
|
||||
.width(width)
|
||||
.height(height)
|
||||
.margin({top: 20, right: 20, bottom: 20, left: 20})
|
||||
|
||||
|
||||
d3.select('#test1')
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.datum(sinAndCos())
|
||||
.call(chart);
|
||||
|
||||
return chart;
|
||||
},
|
||||
callback: function(graph) {
|
||||
$(window).resize(function() {
|
||||
var width = $(window).width() - 40,
|
||||
height = $(window).height() - 40,
|
||||
margin = graph.margin(),
|
||||
animate = graph.animate();
|
||||
|
||||
|
||||
if (width < margin.left + margin.right + 20)
|
||||
width = margin.left + margin.right + 20;
|
||||
|
||||
if (height < margin.top + margin.bottom + 20)
|
||||
height = margin.top + margin.bottom + 20;
|
||||
|
||||
|
||||
graph
|
||||
.width(width)
|
||||
.height(height);
|
||||
|
||||
d3.select('#test1')
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.call(graph);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
function sinAndCos() {
|
||||
var sin = [],
|
||||
cos = [];
|
||||
|
||||
for (var i = 0; i < 100; i++) {
|
||||
sin.push({x: i, y: Math.sin(i/10)});
|
||||
cos.push({x: i, y: .5 * Math.cos(i/10)});
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
values: sin,
|
||||
key: "Sine Wave",
|
||||
color: "#ff7f0e"
|
||||
},
|
||||
{
|
||||
values: cos,
|
||||
key: "Cosine Wave",
|
||||
color: "#2ca02c"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
</script>
|
@ -0,0 +1,131 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<link href="../src/d3.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<style>
|
||||
|
||||
|
||||
body {
|
||||
overflow-y:scroll;
|
||||
}
|
||||
|
||||
text {
|
||||
font: 12px sans-serif;
|
||||
}
|
||||
|
||||
</style>
|
||||
<body>
|
||||
|
||||
<div id="test1">
|
||||
<svg></svg>
|
||||
</div>
|
||||
|
||||
<script src="../lib/d3.v2.js"></script>
|
||||
<script src="../lib/jquery.min.js"></script>
|
||||
<script src="../nv.d3.js"></script>
|
||||
<script src="../src/nvtooltip.js"></script>
|
||||
<script src="../src/models/legend.js"></script>
|
||||
<script src="../src/models/xaxis.js"></script>
|
||||
<script src="../src/models/yaxis.js"></script>
|
||||
<script src="../src/models/line.js"></script>
|
||||
<script src="../src/models/lineWithFocus.js"></script>
|
||||
<script>
|
||||
|
||||
|
||||
nv.addGraph({
|
||||
generate: function() {
|
||||
var width = $(window).width() - 40,
|
||||
height = $(window).height() - 40;
|
||||
|
||||
var chart = nv.models.lineWithLegend()
|
||||
.width(width)
|
||||
.height(height)
|
||||
.yTickFormat(d3.format('.2r'))
|
||||
.xTickFormat(d3.format('.2r'))
|
||||
|
||||
|
||||
var svg = d3.select('#test1 svg')
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.datum(sinAndCos());
|
||||
|
||||
svg.transition().duration(500).call(chart);
|
||||
|
||||
return chart;
|
||||
},
|
||||
callback: function(graph) {
|
||||
|
||||
graph.dispatch.on('tooltipShow', function(e) {
|
||||
var offset = $('#test1').offset(),
|
||||
left = e.pos[0] + offset.left,
|
||||
top = e.pos[1] + offset.top,
|
||||
formatter = d3.format('.2r');
|
||||
|
||||
var content = '<h3>' + e.series.key + '</h3>' +
|
||||
'<p>' +
|
||||
formatter(graph.y()(e.point)) + ', ' + formatter(graph.x()(e.point)) +
|
||||
'</p>';
|
||||
|
||||
nvtooltip.show([left, top], content);
|
||||
});
|
||||
|
||||
graph.dispatch.on('tooltipHide', function(e) {
|
||||
nvtooltip.cleanup();
|
||||
});
|
||||
|
||||
|
||||
|
||||
$(window).resize(function() {
|
||||
var width = $(window).width() - 40,
|
||||
height = $(window).height() - 40,
|
||||
margin = graph.margin();
|
||||
|
||||
|
||||
if (width < margin.left + margin.right + 20)
|
||||
width = margin.left + margin.right + 20;
|
||||
|
||||
if (height < margin.top + margin.bottom + 20)
|
||||
height = margin.top + margin.bottom + 20;
|
||||
|
||||
|
||||
graph
|
||||
.width(width)
|
||||
.height(height);
|
||||
|
||||
d3.select('#test1 svg')
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.call(graph);
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
function sinAndCos() {
|
||||
var sin = [],
|
||||
cos = [];
|
||||
|
||||
for (var i = 0; i < 100; i++) {
|
||||
sin.push({x: i, y: Math.sin(i/10)});
|
||||
cos.push({x: i, y: .5 * Math.cos(i/10)});
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
values: sin,
|
||||
key: "Sine Wave",
|
||||
color: "#ff7f0e"
|
||||
},
|
||||
{
|
||||
values: cos,
|
||||
key: "Cosine Wave",
|
||||
color: "#2ca02c"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
</script>
|
@ -0,0 +1,144 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<link href="../src/d3.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<style>
|
||||
|
||||
body {
|
||||
overflow-y:scroll;
|
||||
}
|
||||
|
||||
text {
|
||||
font: 12px sans-serif;
|
||||
}
|
||||
|
||||
</style>
|
||||
<body>
|
||||
|
||||
<div id="test1">
|
||||
<svg></svg>
|
||||
</div>
|
||||
|
||||
<script src="../lib/d3.v2.js"></script>
|
||||
<script src="../lib/jquery.min.js"></script>
|
||||
<script src="../nv.d3.js"></script>
|
||||
<script src="../src/nvtooltip.js"></script>
|
||||
<script src="../src/models/legend.js"></script>
|
||||
<script src="../src/models/xaxis.js"></script>
|
||||
<script src="../src/models/yaxis.js"></script>
|
||||
<script src="../src/models/line.js"></script>
|
||||
<script src="../src/models/lineWithLegend.js"></script>
|
||||
<script>
|
||||
|
||||
|
||||
//Format A
|
||||
nv.addGraph({
|
||||
generate: function() {
|
||||
var width = $(window).width() - 40,
|
||||
height = $(window).height() - 40;
|
||||
|
||||
var chart = nv.models.lineWithLegend()
|
||||
.width(width)
|
||||
.height(height)
|
||||
//.margin({top: 20, right: 10, bottom: 50, left: 80})
|
||||
|
||||
//chart.yAxis.axisLabel('Cumulative');
|
||||
//chart.xAxis.axisLabel('Date');
|
||||
|
||||
|
||||
chart.yAxis.tickFormat(d3.format(',%'))
|
||||
chart.xAxis.tickFormat(function(d) {
|
||||
return d3.time.format('%x')(new Date(d))
|
||||
})
|
||||
|
||||
//chart.xaxis.tickFormat(d3.format(".02f"))
|
||||
|
||||
var svg = d3.select('#test1 svg')
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.datum(sinAndCos())
|
||||
|
||||
svg.transition().duration(500).call(chart);
|
||||
|
||||
return chart;
|
||||
},
|
||||
callback: function(graph) {
|
||||
|
||||
graph.dispatch.on('tooltipShow', function(e) {
|
||||
var offset = $('#test1').offset(),
|
||||
left = e.pos[0] + offset.left,
|
||||
top = e.pos[1] + offset.top,
|
||||
formatterY = d3.format(",.2%"),
|
||||
formatterX = function(d) {
|
||||
return d3.time.format('%x')(new Date(d))
|
||||
};
|
||||
|
||||
var content = '<h3>' + e.series.key + '</h3>' +
|
||||
'<p>' +
|
||||
formatterY(graph.y()(e.point)) + ' on ' + formatterX(graph.x()(e.point)) +
|
||||
'</p>';
|
||||
|
||||
//$('#positionTest').css({'left': left, 'top': top});
|
||||
nvtooltip.show([left, top], content);
|
||||
});
|
||||
|
||||
graph.dispatch.on('tooltipHide', function(e) {
|
||||
nvtooltip.cleanup();
|
||||
});
|
||||
|
||||
|
||||
|
||||
$(window).resize(function() {
|
||||
var width = $(window).width() - 40,
|
||||
height = $(window).height() - 40,
|
||||
margin = graph.margin();
|
||||
|
||||
|
||||
if (width < margin.left + margin.right + 20)
|
||||
width = margin.left + margin.right + 20;
|
||||
|
||||
if (height < margin.top + margin.bottom + 20)
|
||||
height = margin.top + margin.bottom + 20;
|
||||
|
||||
|
||||
graph
|
||||
.width(width)
|
||||
.height(height);
|
||||
|
||||
d3.select('#test1 svg')
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.call(graph);
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
function sinAndCos() {
|
||||
var sin = [],
|
||||
cos = [];
|
||||
|
||||
for (var i = 0; i < 100; i++) {
|
||||
sin.push({x: i, y: Math.sin(i/10)});
|
||||
cos.push({x: i, y: .5 * Math.cos(i/10)});
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
values: sin,
|
||||
key: "Sine Wave",
|
||||
color: "#ff7f0e"
|
||||
},
|
||||
{
|
||||
values: cos,
|
||||
key: "Cosine Wave",
|
||||
color: "#2ca02c"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
</script>
|
@ -0,0 +1,100 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<link href="../src/d3.css" rel="stylesheet" type="text/css">
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<script src="../lib/d3.v2.js"></script>
|
||||
<script src="../lib/jquery.min.js"></script>
|
||||
<script src="../nv.d3.js"></script>
|
||||
<script>
|
||||
|
||||
function ilog(text) {
|
||||
console.log(text);
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
function daysInMonth(month,year) {
|
||||
var m = [31,28,31,30,31,30,31,31,30,31,30,31];
|
||||
if (month != 2) return m[month - 1];
|
||||
if (year%4 != 0) return m[1];
|
||||
if (year%100 == 0 && year%400 != 0) return m[1];
|
||||
return m[1] + 1;
|
||||
}
|
||||
|
||||
|
||||
function d3_time_range(floor, step, number) {
|
||||
return function(t0, t1, dt) {
|
||||
var time = floor(t0), times = [];
|
||||
if (time < t0) step(time);
|
||||
if (dt > 1) {
|
||||
while (time < t1) {
|
||||
var date = new Date(+time);
|
||||
if (!(number(date) % dt)) times.push(date);
|
||||
step(time);
|
||||
}
|
||||
} else {
|
||||
while (time < t1) times.push(new Date(+time)), step(time);
|
||||
}
|
||||
return times;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
d3.time.monthEnd = function(date) {
|
||||
return new Date(date.getFullYear(), date.getMonth(), 0);
|
||||
};
|
||||
|
||||
|
||||
d3.time.monthEnds = d3_time_range(d3.time.monthEnd, function(date) {
|
||||
date.setUTCDate(date.getUTCDate() + 1);
|
||||
date.setDate(daysInMonth(date.getMonth() + 1, date.getFullYear()));
|
||||
}, function(date) {
|
||||
return date.getMonth();
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
var margin = {top: 10, right: 40, bottom: 40, left: 40},
|
||||
width = 960 - margin.left - margin.right,
|
||||
height = 80 - margin.top - margin.bottom;
|
||||
|
||||
var x = d3.time.scale()
|
||||
.domain([new Date(2010, 0, 1), new Date(2011, 0, 1)])
|
||||
.range([0, width]);
|
||||
|
||||
var xAxis = d3.svg.axis()
|
||||
.scale(x)
|
||||
.orient("bottom")
|
||||
//.ticks(d3.time.months)
|
||||
.ticks(d3.time.monthEnds)
|
||||
//.tickSubdivide(3)
|
||||
.tickSize(8, 4, 0)
|
||||
.tickFormat(d3.time.format("%x"));
|
||||
//.tickFormat(d3.time.format("%B"));
|
||||
|
||||
var svg = d3.select("body").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 + ")");
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "x axis")
|
||||
.attr("transform", "translate(0," + height + ")")
|
||||
.call(xAxis)
|
||||
|
||||
.selectAll("text")
|
||||
.attr("text-anchor", "middle")
|
||||
//.attr("text-anchor", "start")
|
||||
.attr("x", 0)
|
||||
.attr("y", 12);
|
||||
|
||||
</script>
|
@ -0,0 +1,115 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<link href="../src/d3.css" rel="stylesheet" type="text/css">
|
||||
<style>
|
||||
|
||||
body {
|
||||
overflow-y:scroll;
|
||||
}
|
||||
|
||||
</style>
|
||||
<body>
|
||||
|
||||
<svg id="test1"></svg>
|
||||
|
||||
<script src="../lib/d3.v2.js"></script>
|
||||
<script src="../lib/jquery.min.js"></script>
|
||||
<script src="../nv.d3.js"></script>
|
||||
<script src="../src/models/legend.js"></script>
|
||||
<script src="../src/models/scatter.js"></script>
|
||||
<script>
|
||||
|
||||
|
||||
//Format A
|
||||
nv.addGraph({
|
||||
generate: function() {
|
||||
var width = $(window).width() - 40,
|
||||
height = $(window).height() - 40;
|
||||
|
||||
var chart = nv.models.scatter()
|
||||
.width(width)
|
||||
.height(height)
|
||||
.margin({top: 20, right: 20, bottom: 20, left: 20})
|
||||
.size(10)
|
||||
|
||||
|
||||
d3.select('#test1')
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.datum(randomData())
|
||||
.call(chart);
|
||||
|
||||
return chart;
|
||||
},
|
||||
callback: function(graph) {
|
||||
$(window).resize(function() {
|
||||
var width = $(window).width() - 40,
|
||||
height = $(window).height() - 40,
|
||||
margin = graph.margin();
|
||||
|
||||
|
||||
if (width < margin.left + margin.right + 20)
|
||||
width = margin.left + margin.right + 20;
|
||||
|
||||
if (height < margin.top + margin.bottom + 20)
|
||||
height = margin.top + margin.bottom + 20;
|
||||
|
||||
|
||||
graph
|
||||
.width(width)
|
||||
.height(height);
|
||||
|
||||
d3.select('#test1')
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.call(graph);
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
function sinAndCos() {
|
||||
var sin = [],
|
||||
cos = [];
|
||||
|
||||
for (var i = 0; i < 100; i++) {
|
||||
sin.push({x: i, y: Math.sin(i/10)});
|
||||
cos.push({x: i, y: .5 * Math.cos(i/10)});
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
values: sin,
|
||||
key: "Sine Wave",
|
||||
color: "#ff7f0e"
|
||||
},
|
||||
{
|
||||
values: cos,
|
||||
key: "Cosine Wave",
|
||||
color: "#2ca02c"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
function randomData() {
|
||||
var data = [];
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
data.push({
|
||||
key: 'Group ' + i,
|
||||
values: []
|
||||
});
|
||||
|
||||
for (j = 0; j < 100; j++) {
|
||||
data[i].values.push({x: Math.random(), y: Math.random()});
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
</script>
|
@ -0,0 +1,165 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<link href="../src/d3.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<style>
|
||||
|
||||
body {
|
||||
overflow-y:scroll;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
svg {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
#offsetDiv {
|
||||
}
|
||||
|
||||
|
||||
#test1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="offsetDiv">
|
||||
<div id="test1" class="chartWrap">
|
||||
<svg></svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../lib/d3.v2.js"></script>
|
||||
<script src="../lib/jquery.min.js"></script>
|
||||
<script src="../src/nvtooltip.js"></script>
|
||||
<script src="../nv.d3.js"></script>
|
||||
<script src="../src/models/legend.js"></script>
|
||||
<script src="../src/models/xaxis.js"></script>
|
||||
<script src="../src/models/yaxis.js"></script>
|
||||
<script src="../src/models/scatter.js"></script>
|
||||
<script src="../src/models/scatterWithLegend.js"></script>
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
//Format A
|
||||
nv.addGraph({
|
||||
generate: function() {
|
||||
var width = $(window).width() - 20,
|
||||
height = $(window).height() - 20;
|
||||
|
||||
var chart = nv.models.scatterWithLegend()
|
||||
.width(width)
|
||||
.height(height)
|
||||
//.width(width)
|
||||
//.height(height)
|
||||
//.forceX([-4,4])
|
||||
//.forceY([-4,4])
|
||||
//.margin({top: 20, right: 10, bottom: 50, left: 80})
|
||||
|
||||
|
||||
//chart.xaxis.tickFormat(d3.format(".02f"))
|
||||
|
||||
var svg =d3.select('#test1 svg')
|
||||
.datum(randomData());
|
||||
|
||||
|
||||
svg.transition().duration(500)
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.call(chart)
|
||||
//.attr('width', wid0th)
|
||||
//.attr('height', height)
|
||||
|
||||
return chart;
|
||||
},
|
||||
callback: function(graph) {
|
||||
|
||||
graph.dispatch.on('tooltipShow', function(e) {
|
||||
var offset = $('#test1').offset(),
|
||||
left = e.pos[0] + offset.left,
|
||||
top = e.pos[1] + offset.top,
|
||||
formatter = d3.format(".2f");
|
||||
|
||||
|
||||
/*
|
||||
var content = '<h3>' + e.series.key + '</h3>' +
|
||||
'<p>' +
|
||||
'<span class="value"><span class="label">x:</span><span class="val">' + formatter(e.point.x) + '</span></span><br>' +
|
||||
'<s0pan class="value"><span class="label">y:</span><span class="val">' + formatter(e.point.y) + '</span></span>' +
|
||||
'</p>';
|
||||
|
||||
nvtooltip.show([left, top], content);
|
||||
*/
|
||||
|
||||
var contentX = '<strong>' + formatter(e.point.x) + '</strong>';
|
||||
var contentY = '<strong>' + formatter(e.point.y) + '</strong>';
|
||||
nvtooltip.show([left, $(window).height() - 30], contentX, 'n', 1);
|
||||
nvtooltip.show([5, top], contentY, 'w', 1);
|
||||
});
|
||||
|
||||
|
||||
graph.dispatch.on('tooltipHide', function(e) {
|
||||
nvtooltip.cleanup();
|
||||
});
|
||||
|
||||
$(window).resize(function() {
|
||||
var width = $(window).width() - 20,
|
||||
height = $(window).height() - 20,
|
||||
margin = graph.margin(),
|
||||
animate = graph.animate();
|
||||
|
||||
|
||||
if (width < margin.left + margin.right + 20)
|
||||
width = margin.left + margin.right + 20;
|
||||
|
||||
if (height < margin.top + margin.bottom + 20)
|
||||
height = margin.top + margin.bottom + 20;
|
||||
|
||||
|
||||
graph
|
||||
.width(width)
|
||||
.height(height);
|
||||
|
||||
d3.select('#test1 svg')
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.call(graph);
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function randomData() {
|
||||
var data = [],
|
||||
random = d3.random.normal();
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
data.push({
|
||||
key: 'Group ' + i,
|
||||
values: []
|
||||
});
|
||||
|
||||
for (j = 0; j < 100; j++) {
|
||||
data[i].values.push({x: random(), y: random(), size: Math.random()});
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
@ -0,0 +1,57 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<link href="../src/d3.css" rel="stylesheet" type="text/css">
|
||||
<style>
|
||||
|
||||
.sparkline path {
|
||||
fill: none;
|
||||
stroke: #444;
|
||||
}
|
||||
|
||||
text {
|
||||
font: 10px sans-serif;
|
||||
}
|
||||
|
||||
</style>
|
||||
<body>
|
||||
|
||||
<h2>Sparkline: <span id="test1" class="sparkline"></span></h2>
|
||||
|
||||
<script src="../lib/d3.v2.js"></script>
|
||||
<script src="../lib/jquery.min.js"></script>
|
||||
<script src="../nv.d3.js"></script>
|
||||
<script src="../src/models/sparkline.js"></script>
|
||||
<script>
|
||||
|
||||
//Format A
|
||||
nv.addGraph({
|
||||
generate: function() {
|
||||
var chart = nv.models.sparkline()
|
||||
.width(200)
|
||||
.height(20)
|
||||
|
||||
d3.select("#test1")
|
||||
.datum(sine())
|
||||
.call(chart);
|
||||
|
||||
return chart;
|
||||
},
|
||||
callback: function(graph) {
|
||||
//log("Sparkline rendered");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
function sine() {
|
||||
var sin = [];
|
||||
|
||||
for (var i = 0; i < 100; i++) {
|
||||
sin.push({x: i, y: Math.sin(i/10)});
|
||||
}
|
||||
|
||||
return sin;
|
||||
}
|
||||
|
||||
|
||||
</script>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
jquery-1.7.1.min.js
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,85 @@
|
||||
var nv = {version: "0.0.1"};
|
||||
|
||||
window.nv = nv;
|
||||
|
||||
nv.models = {};
|
||||
nv.graphs = [];
|
||||
nv.log = {};
|
||||
|
||||
nv.dispatch = d3.dispatch("render_start", "render_end");
|
||||
|
||||
|
||||
// ********************************************
|
||||
// Public Helper functions, not part of NV
|
||||
|
||||
window.log = function(obj) {
|
||||
if ((typeof(window.console) === "object")
|
||||
&& (typeof(window.console.log) === "function"))
|
||||
console.log.apply(console, arguments);
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// ********************************************
|
||||
// Public Core NV functions
|
||||
|
||||
nv.dispatch.on("render_start", function(e) {
|
||||
nv.log.startTime = +new Date;
|
||||
//log('start', nv.log.startTime);
|
||||
});
|
||||
|
||||
nv.dispatch.on("render_end", function(e) {
|
||||
nv.log.endTime = +new Date;
|
||||
nv.log.totalTime = nv.log.endTime - nv.log.startTime;
|
||||
//log('end', nv.log.endTime);
|
||||
log('total', nv.log.totalTime);
|
||||
});
|
||||
|
||||
|
||||
// ********************************************
|
||||
// Public Core NV functions
|
||||
|
||||
nv.render = function render(stepSize) {
|
||||
var step = stepSize || 1; // number of graphs to generate in each timout loop
|
||||
|
||||
render.active = true;
|
||||
nv.dispatch.render_start();
|
||||
|
||||
setTimeout(function(){
|
||||
var chart;
|
||||
|
||||
for (var i = 0; i < step && (graph = render.queue[i]); i++) {
|
||||
chart = graph.generate();
|
||||
if (typeof graph.callback === 'function') graph.callback(chart);
|
||||
nv.graphs.push(chart);
|
||||
}
|
||||
|
||||
render.queue.splice(0, i);
|
||||
|
||||
if (render.queue.length > 0) setTimeout(arguments.callee, 0);
|
||||
else {
|
||||
nv.render.active = false;
|
||||
nv.dispatch.render_end();
|
||||
}
|
||||
}, 0);
|
||||
};
|
||||
nv.render.queue = [];
|
||||
|
||||
|
||||
nv.addGraph = function(obj) {
|
||||
if (typeof arguments[0] === "function")
|
||||
obj = {generate: arguments[0], callback: arguments[1]};
|
||||
|
||||
nv.render.queue.push(obj);
|
||||
|
||||
if (!nv.render.active) nv.render();
|
||||
};
|
||||
|
||||
|
||||
nv.strip = function(s) {
|
||||
return s.replace(/(\s|&)/g,'');
|
||||
}
|
||||
|
@ -0,0 +1,214 @@
|
||||
|
||||
/********************
|
||||
* HTML CSS
|
||||
*/
|
||||
|
||||
|
||||
.chartWrap {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
/********************
|
||||
* TOOLTIP CSS
|
||||
*/
|
||||
|
||||
.nvtooltip {
|
||||
position: absolute;
|
||||
background-color: rgba(255,255,255,1);
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
|
||||
font-family: Arial;
|
||||
font-size: 13px;
|
||||
|
||||
transition: opacity 500ms linear;
|
||||
-moz-transition: opacity 500ms linear;
|
||||
-webkit-transition: opacity 500ms linear;
|
||||
|
||||
transition-delay: 500ms
|
||||
-moz-transition-delay: 500ms;
|
||||
-webkit-transition-delay: 500ms;
|
||||
|
||||
-moz-box-shadow: 4px 4px 12px rgba(0,0,0,.5);
|
||||
-webkit-box-shadow: 4px 4px 12px rgba(0,0,0,.5);
|
||||
box-shadow: 4px 4px 12px rgba(0,0,0,.5);
|
||||
|
||||
-moz-border-radius: 15px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.nvtooltip h3 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nvtooltip p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nvtooltip span {
|
||||
display: inline-block;
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
|
||||
/********************
|
||||
* SVG CSS
|
||||
*/
|
||||
|
||||
|
||||
text {
|
||||
font: 12px sans-serif;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Brush
|
||||
*/
|
||||
|
||||
.brush .extent {
|
||||
stroke: #666;
|
||||
fill-opacity: .125;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Legend
|
||||
*/
|
||||
|
||||
.legend .series {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.legend .disabled circle {
|
||||
fill-opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Axes
|
||||
*/
|
||||
|
||||
.axis path {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
stroke-opacity: .75;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.axis path.domain {
|
||||
stroke-opacity: .75;
|
||||
}
|
||||
|
||||
.axis line {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
stroke-opacity: .25;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.axis line.zero {
|
||||
stroke-opacity: .75;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Bars
|
||||
*/
|
||||
|
||||
.bars rect {
|
||||
fill: steelblue;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.bars .hover rect {
|
||||
fill: lightblue;
|
||||
}
|
||||
|
||||
.bars text {
|
||||
fill: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
.bars .hover text {
|
||||
fill: rgba(0,0,0,1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Lines
|
||||
*/
|
||||
|
||||
.lines path {
|
||||
fill: none;
|
||||
stroke-width: 1.5px;
|
||||
stroke-linecap: round;
|
||||
shape-rendering: geometricPrecision;
|
||||
|
||||
transition: stroke-width 250ms linear;
|
||||
-moz-transition: stroke-width 250ms linear;
|
||||
-webkit-transition: stroke-width 250ms linear;
|
||||
|
||||
transition-delay: 250ms
|
||||
-moz-transition-delay: 250ms;
|
||||
-webkit-transition-delay: 250ms;
|
||||
}
|
||||
|
||||
.line.hover path {
|
||||
stroke-width: 6px;
|
||||
}
|
||||
|
||||
.lines .point {
|
||||
transition: all 250ms linear;
|
||||
-moz-transition: all 250ms linear;
|
||||
-webkit-transition: all 250ms linear;
|
||||
}
|
||||
|
||||
.lines .point.hover {
|
||||
stroke-width: 20px;
|
||||
stroke-opacity: .5;
|
||||
}
|
||||
|
||||
|
||||
.point-paths path {
|
||||
stroke: #aaa;
|
||||
stroke-opacity: 0;
|
||||
fill: #eee;
|
||||
fill-opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
/**********
|
||||
* Scatter
|
||||
*/
|
||||
|
||||
.groups .point {
|
||||
transition: all 250ms linear;
|
||||
-moz-transition: all 250ms linear;
|
||||
-webkit-transition: all 250ms linear;
|
||||
}
|
||||
|
||||
.groups .point.hover {
|
||||
stroke-width: 20px;
|
||||
stroke-opacity: .5;
|
||||
}
|
||||
|
||||
.d3scatter .point.hover {
|
||||
fill-opacity: 1;
|
||||
}
|
||||
|
||||
/*
|
||||
.group.hover .point {
|
||||
fill-opacity: 1;
|
||||
}
|
||||
*/
|
@ -0,0 +1 @@
|
||||
(function(d3, window){
|
@ -0,0 +1,126 @@
|
||||
|
||||
nv.models.bar = function() {
|
||||
var margin = {top: 20, right: 10, bottom: 20, left: 60},
|
||||
width = 960,
|
||||
height = 500,
|
||||
animate = 500;
|
||||
|
||||
var x = d3.scale.ordinal(),
|
||||
y = d3.scale.linear(),
|
||||
xAxis = d3.svg.axis().scale(x).orient('bottom').ticks(5),
|
||||
yAxis = d3.svg.axis().scale(y).orient('left');
|
||||
|
||||
function chart(selection) {
|
||||
selection.each(function(data) {
|
||||
|
||||
//x .domain(data.map(function(d,i) { return d.label }))
|
||||
x .domain(["One", "Two", "Three", "Four", "Five"])
|
||||
.rangeRoundBands([0, width - margin.left - margin.right], .1);
|
||||
|
||||
y .domain([0, d3.max(data, function(d) { return d.y; })])
|
||||
.range([height - margin.top - margin.bottom, 0]);
|
||||
|
||||
xAxis.ticks( width / 100 );
|
||||
yAxis.ticks( height / 36 ).tickSize(-(width - margin.right - margin.left), 0);
|
||||
|
||||
yAxis.tickSize(-(width - margin.right - margin.left), 0);
|
||||
|
||||
var wrap = d3.select(this).selectAll('g.wrap').data([data]);
|
||||
var gEnter = wrap.enter().append('g').attr('class', 'wrap').append('g');
|
||||
|
||||
gEnter.append('g').attr('class', 'x axis');
|
||||
gEnter.append('g').attr('class', 'y axis');
|
||||
gEnter.append('g').attr('class', 'bars');
|
||||
|
||||
|
||||
wrap.attr('width', width)
|
||||
.attr('height', height);
|
||||
|
||||
var g = wrap.select('g')
|
||||
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
|
||||
|
||||
|
||||
var bars = wrap.select('.bars').selectAll('.bar')
|
||||
.data(function(d) { return d });
|
||||
bars.exit().remove();
|
||||
|
||||
|
||||
var barsEnter = bars.enter().append('g')
|
||||
.attr('class', 'bar')
|
||||
.on('mouseover', function(d,i){ d3.select(this).classed('hover', true) })
|
||||
.on('mouseout', function(d,i){ d3.select(this).classed('hover', false) });
|
||||
barsEnter.append('rect')
|
||||
.attr('y', function(d) { return y(0) });
|
||||
barsEnter.append('text')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('dy', '-4px');
|
||||
|
||||
|
||||
bars
|
||||
.attr('transform', function(d,i) { return 'translate(' + x(d.label) + ',0)' })
|
||||
bars.selectAll('rect')
|
||||
.order()
|
||||
.attr('width', x.rangeBand )
|
||||
.transition()
|
||||
.duration(animate)
|
||||
.attr('x', 0 )
|
||||
.attr('y', function(d) { return y(d.y) })
|
||||
.attr('height', function(d) { return y.range()[0] - y(d.y) });
|
||||
bars.selectAll('text')
|
||||
.attr('x', 0 )
|
||||
.attr('y', function(d) { return y(d.y) })
|
||||
.attr('dx', x.rangeBand() / 2)
|
||||
.text(function(d) { return d.y });
|
||||
|
||||
|
||||
g.select('.x.axis')
|
||||
.attr('transform', 'translate(0,' + y.range()[0] + ')')
|
||||
.call(xAxis);
|
||||
|
||||
g.select('.y.axis')
|
||||
.call(yAxis);
|
||||
});
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
chart.margin = function(_) {
|
||||
if (!arguments.length) return margin;
|
||||
margin = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.width = function(_) {
|
||||
if (!arguments.length) return width;
|
||||
if (margin.left + margin.right + 20 > _)
|
||||
width = margin.left + margin.right + 20 // Min width
|
||||
else
|
||||
width = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.height = function(_) {
|
||||
if (!arguments.length) return height;
|
||||
if (margin.top + margin.bottom + 20 > _)
|
||||
height = margin.top + margin.bottom + 20 // Min height
|
||||
else
|
||||
height = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.animate = function(_) {
|
||||
if (!arguments.length) return animate;
|
||||
animate = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.xaxis = {};
|
||||
// Expose the x-axis' tickFormat method.
|
||||
d3.rebind(chart.xaxis, xAxis, 'tickFormat');
|
||||
|
||||
chart.yaxis = {};
|
||||
// Expose the y-axis' tickFormat method.
|
||||
d3.rebind(chart.yaxis, yAxis, 'tickFormat');
|
||||
|
||||
return chart;
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
|
||||
nv.models.legend = function() {
|
||||
var margin = {top: 5, right: 0, bottom: 5, left: 10},
|
||||
width = 400,
|
||||
height = 20,
|
||||
color = d3.scale.category10().range(),
|
||||
dispatch = d3.dispatch('legendClick', 'legendMouseover', 'legendMouseout');
|
||||
|
||||
function chart(selection) {
|
||||
selection.each(function(data) {
|
||||
|
||||
var wrap = d3.select(this).selectAll('g.legend').data([data]);
|
||||
var gEnter = wrap.enter().append('g').attr('class', 'legend').append('g');
|
||||
|
||||
|
||||
var g = wrap.select('g')
|
||||
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
|
||||
|
||||
|
||||
var series = g.selectAll('.series')
|
||||
.data(function(d) { return d });
|
||||
var seriesEnter = series.enter().append('g').attr('class', 'series')
|
||||
.on('mouseover', function(d,i) {
|
||||
dispatch.legendMouseover(d,i);
|
||||
})
|
||||
.on('mouseout', function(d,i) {
|
||||
dispatch.legendMouseout(d,i);
|
||||
})
|
||||
.on('click', function(d,i) {
|
||||
dispatch.legendClick(d,i);
|
||||
});
|
||||
seriesEnter.append('circle')
|
||||
.style('fill', function(d,i) { return d.color || color[i % 20] })
|
||||
.style('stroke', function(d,i) { return d.color || color[i % 20] })
|
||||
.style('stroke-width', 2)
|
||||
.attr('r', 5);
|
||||
seriesEnter.append('text')
|
||||
.text(function(d) { return d.key })
|
||||
.attr('text-anchor', 'start')
|
||||
.attr('dy', '.32em')
|
||||
.attr('dx', '8');
|
||||
series.classed('disabled', function(d) { return d.disabled });
|
||||
series.exit().remove();
|
||||
|
||||
|
||||
var ypos = 5,
|
||||
newxpos = 5,
|
||||
maxwidth = 0,
|
||||
xpos;
|
||||
series
|
||||
.attr('transform', function(d, i) {
|
||||
var length = d3.select(this).select('text').node().getComputedTextLength() + 28;
|
||||
xpos = newxpos;
|
||||
|
||||
if (width < margin.left + margin.right + xpos + length) {
|
||||
newxpos = xpos = 5;
|
||||
ypos += 20;
|
||||
}
|
||||
|
||||
newxpos += length;
|
||||
if (newxpos > maxwidth) maxwidth = newxpos;
|
||||
|
||||
return 'translate(' + xpos + ',' + ypos + ')';
|
||||
});
|
||||
|
||||
//position legend as far right as possible within the total width
|
||||
g.attr('transform', 'translate(' + (width - margin.right - maxwidth) + ',' + margin.top + ')');
|
||||
|
||||
//update height value if calculated larger than current
|
||||
//Asuming legend is always horizontal for now, removing if clause because this does not let legend shrink after expanding
|
||||
//TODO: allow legend to be horizontal or vertical, instead of definign height/width define one, and maybe call it maxHeight/maxWidth
|
||||
//if (height < margin.top + margin.bottom + ypos + 15)
|
||||
height = margin.top + margin.bottom + ypos + 15;
|
||||
|
||||
});
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
chart.dispatch = dispatch;
|
||||
|
||||
chart.margin = function(_) {
|
||||
if (!arguments.length) return margin;
|
||||
margin = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.width = function(_) {
|
||||
if (!arguments.length) return width;
|
||||
width = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.height = function(_) {
|
||||
if (!arguments.length) return height;
|
||||
height = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
return chart;
|
||||
}
|
@ -0,0 +1,268 @@
|
||||
//TODO: consider adding axes
|
||||
// -How to deal with time vs generic linear, vs any other scale?
|
||||
|
||||
nv.models.line = function() {
|
||||
//Default Settings
|
||||
var margin = {top: 0, right: 0, bottom: 0, left: 0}, //consider removing margin options from here... or make margin padding inside the chart (subtract margin from range)
|
||||
width = 960,
|
||||
height = 500,
|
||||
dotRadius = function() { return 2.5 }, //consider removing this, or making similar to scatter
|
||||
color = d3.scale.category10().range(),
|
||||
id = Math.floor(Math.random() * 10000), //Create semi-unique ID incase user doesn't select one
|
||||
getX = function(d) { return d.x },
|
||||
getY = function(d) { return d.y },
|
||||
interactive = true,
|
||||
xDomain, yDomain;
|
||||
|
||||
var x = d3.scale.linear(),
|
||||
y = d3.scale.linear(),
|
||||
dispatch = d3.dispatch('pointMouseover', 'pointMouseout'),
|
||||
x0, y0;
|
||||
|
||||
|
||||
function chart(selection) {
|
||||
selection.each(function(data) {
|
||||
var seriesData = data.map(function(d) { return d.values });
|
||||
|
||||
x0 = x0 || x;
|
||||
y0 = y0 || y;
|
||||
|
||||
//TODO: consider reusing the parent's scales (almost always making duplicates of the same scale)
|
||||
x .domain(xDomain || d3.extent(d3.merge(seriesData), getX ))
|
||||
.range([0, width - margin.left - margin.right]);
|
||||
|
||||
y .domain(yDomain || d3.extent(d3.merge(seriesData), getY ))
|
||||
.range([height - margin.top - margin.bottom, 0]);
|
||||
|
||||
|
||||
var wrap = d3.select(this).selectAll('g.d3line').data([data]);
|
||||
var wrapEnter = wrap.enter().append('g').attr('class', 'd3line');
|
||||
var gEnter = wrapEnter.append('g');
|
||||
|
||||
gEnter.append('g').attr('class', 'lines');
|
||||
|
||||
var g = wrap.select('g')
|
||||
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
|
||||
|
||||
|
||||
|
||||
wrapEnter.append('defs').append('clipPath')
|
||||
.attr('id', 'chart-clip-path-' + id)
|
||||
.append('rect');
|
||||
wrap.select('#chart-clip-path-' + id + ' rect')
|
||||
.attr('width', width - margin.left - margin.right)
|
||||
.attr('height', height - margin.top - margin.bottom);
|
||||
|
||||
gEnter
|
||||
.attr('clip-path', 'url(#chart-clip-path-' + id + ')');
|
||||
|
||||
var shiftWrap = gEnter.append('g').attr('class', 'shiftWrap');
|
||||
|
||||
|
||||
//TODO: currently doesnt remove if user renders, then turns off interactions... currently must turn off before the first render (will need to fix)
|
||||
if (interactive) {
|
||||
shiftWrap.append('g').attr('class', 'point-clips');
|
||||
shiftWrap.append('g').attr('class', 'point-paths');
|
||||
|
||||
|
||||
var vertices = d3.merge(data.map(function(line, lineIndex) {
|
||||
return line.values.map(function(point, pointIndex) {
|
||||
return [x(getX(point)), y(getY(point)), lineIndex, pointIndex]; //inject series and point index for reference into voronoi
|
||||
})
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
|
||||
//var pointClips = wrap.select('.point-clips').selectAll('clipPath') // **BROWSER BUG** can't reselect camel cased elements
|
||||
var pointClips = wrap.select('.point-clips').selectAll('.clip-path')
|
||||
.data(vertices);
|
||||
pointClips.enter().append('clipPath').attr('class', 'clip-path')
|
||||
.append('circle')
|
||||
.attr('r', 25);
|
||||
pointClips.exit().remove();
|
||||
pointClips
|
||||
.attr('id', function(d, i) { return 'clip-' + id + '-' + d[2] + '-' + d[3] })
|
||||
.attr('transform', function(d) { return 'translate(' + d[0] + ',' + d[1] + ')' })
|
||||
|
||||
|
||||
//inject series and point index for reference into voronoi
|
||||
var voronoi = d3.geom.voronoi(vertices).map(function(d, i) { return { 'data': d, 'series': vertices[i][2], 'point': vertices[i][3] } });
|
||||
|
||||
|
||||
//TODO: Add small amount noise to prevent duplicates
|
||||
var pointPaths = wrap.select('.point-paths').selectAll('path')
|
||||
.data(voronoi);
|
||||
|
||||
pointPaths.enter().append('path')
|
||||
.attr('class', function(d,i) { return 'path-'+i; })
|
||||
//.style('fill', d3.rgb(230, 230, 230))
|
||||
//.style('stroke', d3.rgb(200, 200, 200))
|
||||
.style('fill-opacity', 0);
|
||||
|
||||
pointPaths.exit().remove();
|
||||
|
||||
pointPaths
|
||||
.attr('clip-path', function(d,i) { return 'url(#clip-' + id + '-' + d.series + '-' + d.point +')' })
|
||||
.attr('d', function(d) { return 'M' + d.data.join(',') + 'Z'; })
|
||||
.on('mouseover', function(d) {
|
||||
var series = data[d.series],
|
||||
point = series.values[d.point];
|
||||
|
||||
dispatch.pointMouseover({
|
||||
point: point,
|
||||
series:series,
|
||||
pos: [x(getX(point)) + margin.left, y(getY(point)) + margin.top],
|
||||
seriesIndex: d.series,
|
||||
pointIndex: d.point
|
||||
});
|
||||
})
|
||||
.on('mouseout', function(d, i) {
|
||||
dispatch.pointMouseout({
|
||||
point: data[d.series].values[d.point],
|
||||
series: data[d.series],
|
||||
seriesIndex: d.series,
|
||||
pointIndex: d.point
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
dispatch.on('pointMouseover.point', function(d) {
|
||||
wrap.select('.series-' + d.seriesIndex + ' .point-' + d.pointIndex)
|
||||
.classed('hover', true);
|
||||
});
|
||||
dispatch.on('pointMouseout.point', function(d) {
|
||||
wrap.select('.series-' + d.seriesIndex + ' circle.point-' + d.pointIndex)
|
||||
.classed('hover', false);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
var lines = wrap.select('.lines').selectAll('.line')
|
||||
.data(function(d) { return d }, function(d) { return d.key });
|
||||
lines.enter().append('g')
|
||||
.style('stroke-opacity', 1e-6)
|
||||
.style('fill-opacity', 1e-6);
|
||||
d3.transition(lines.exit())
|
||||
.style('stroke-opacity', 1e-6)
|
||||
.style('fill-opacity', 1e-6)
|
||||
.remove();
|
||||
lines
|
||||
.attr('class', function(d,i) { return 'line series-' + i })
|
||||
.classed('hover', function(d) { return d.hover })
|
||||
.style('fill', function(d,i){ return color[i % 20] })
|
||||
.style('stroke', function(d,i){ return color[i % 20] })
|
||||
d3.transition(lines)
|
||||
.style('stroke-opacity', 1)
|
||||
.style('fill-opacity', .5);
|
||||
|
||||
|
||||
var paths = lines.selectAll('path')
|
||||
.data(function(d, i) { return [d.values] });
|
||||
paths.enter().append('path')
|
||||
.attr('d', d3.svg.line()
|
||||
.x(function(d) { return x0(getX(d)) })
|
||||
.y(function(d) { return y0(getY(d)) })
|
||||
);
|
||||
paths.exit().remove();
|
||||
d3.transition(paths)
|
||||
.attr('d', d3.svg.line()
|
||||
.x(function(d) { return x(getX(d)) })
|
||||
.y(function(d) { return y(getY(d)) })
|
||||
);
|
||||
|
||||
|
||||
var points = lines.selectAll('circle.point')
|
||||
.data(function(d) { return d.values });
|
||||
points.enter().append('circle')
|
||||
.attr('cx', function(d) { return x0(getX(d)) })
|
||||
.attr('cy', function(d) { return y0(getY(d)) });
|
||||
points.exit().remove();
|
||||
points.attr('class', function(d,i) { return 'point point-' + i });
|
||||
d3.transition(points)
|
||||
.attr('cx', function(d) { return x(getX(d)) })
|
||||
.attr('cy', function(d) { return y(getY(d)) })
|
||||
.attr('r', dotRadius);
|
||||
|
||||
|
||||
x0 = x.copy();
|
||||
y0 = y.copy();
|
||||
|
||||
});
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
chart.dispatch = dispatch;
|
||||
|
||||
chart.x = function(_) {
|
||||
if (!arguments.length) return getX;
|
||||
getX = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.y = function(_) {
|
||||
if (!arguments.length) return getY;
|
||||
getY = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.margin = function(_) {
|
||||
if (!arguments.length) return margin;
|
||||
margin = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.width = function(_) {
|
||||
if (!arguments.length) return width;
|
||||
width = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.height = function(_) {
|
||||
if (!arguments.length) return height;
|
||||
height = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.xDomain = function(_) {
|
||||
if (!arguments.length) return xDomain;
|
||||
xDomain = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.yDomain = function(_) {
|
||||
if (!arguments.length) return yDomain;
|
||||
yDomain = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.interactive = function(_) {
|
||||
if (!arguments.length) return interactive;
|
||||
interactive = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.dotRadius = function(_) {
|
||||
if (!arguments.length) return dotRadius;
|
||||
dotRadius = d3.functor(_);
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.color = function(_) {
|
||||
if (!arguments.length) return color;
|
||||
color = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.id = function(_) {
|
||||
if (!arguments.length) return id;
|
||||
id = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
|
||||
return chart;
|
||||
}
|
@ -0,0 +1,332 @@
|
||||
|
||||
nv.models.lineWithLegend = function() {
|
||||
var margin = {top: 30, right: 20, bottom: 30, left: 60},
|
||||
margin2 = {top: 0, right: 20, bottom: 20, left: 60},
|
||||
width = 960,
|
||||
height = 500,
|
||||
height1 = 400,
|
||||
height2 = 100,
|
||||
dotRadius = function() { return 2.5 },
|
||||
color = d3.scale.category10().range(),
|
||||
id = Math.floor(Math.random() * 10000), //Create semi-unique ID incase user doesn't select one
|
||||
dispatch = d3.dispatch('tooltipShow', 'tooltipHide');
|
||||
|
||||
var x = d3.scale.linear(),
|
||||
y = d3.scale.linear(),
|
||||
x2 = d3.scale.linear(),
|
||||
y2 = d3.scale.linear(),
|
||||
getX = function(d) { return d.x },
|
||||
getY = function(d) { return d.y },
|
||||
xAxis = nv.models.xaxis().scale(x),
|
||||
yAxis = nv.models.yaxis().scale(y),
|
||||
xAxis2 = nv.models.xaxis().scale(x2),
|
||||
yAxis2 = nv.models.yaxis().scale(y2),
|
||||
legend = nv.models.legend().height(30),
|
||||
focus = nv.models.line(),
|
||||
context = nv.models.line().dotRadius(.1).interactive(false);
|
||||
|
||||
var brush = d3.svg.brush()
|
||||
.x(x2)
|
||||
.on('brush', onBrush);
|
||||
|
||||
|
||||
var wrap, gEnter, g, focus, focusLines, contextWrap, focusWrap, contextLines; //brought all variables to this scope for use within function... is this a bad idea?
|
||||
|
||||
|
||||
function chart(selection) {
|
||||
selection.each(function(data) {
|
||||
var seriesData = data.filter(function(d) { return !d.disabled })
|
||||
.map(function(d) { return d.values });
|
||||
|
||||
x2 .domain(d3.extent(d3.merge(seriesData), getX ))
|
||||
.range([0, width - margin.left - margin.right]);
|
||||
y2 .domain(d3.extent(d3.merge(seriesData), getY ))
|
||||
.range([height2 - margin2.top - margin2.bottom, 0]);
|
||||
|
||||
x .domain(brush.empty() ? x2.domain() : brush.extent())
|
||||
.range([0, width - margin.left - margin.right]);
|
||||
y .domain(y2.domain())
|
||||
.range([height1 - margin.top - margin.bottom, 0]);
|
||||
|
||||
|
||||
focus
|
||||
.width(width - margin.left - margin.right)
|
||||
.height(height1 - margin.top - margin.bottom)
|
||||
.color(data.map(function(d,i) {
|
||||
return d.color || color[i % 10];
|
||||
}).filter(function(d,i) { return !data[i].disabled }))
|
||||
|
||||
context
|
||||
.width(width - margin.left - margin.right)
|
||||
.height(height2 - margin2.top - margin2.bottom)
|
||||
.color(data.map(function(d,i) {
|
||||
return d.color || color[i % 10];
|
||||
}).filter(function(d,i) { return !data[i].disabled }))
|
||||
|
||||
|
||||
|
||||
|
||||
wrap = d3.select(this).selectAll('g.wrap').data([data]);
|
||||
gEnter = wrap.enter().append('g').attr('class', 'wrap d3lineWithFocus').append('g');
|
||||
|
||||
gEnter.append('g').attr('class', 'focus');
|
||||
gEnter.append('g').attr('class', 'context');
|
||||
gEnter.append('g').attr('class', 'legendWrap');
|
||||
|
||||
|
||||
|
||||
g = wrap.select('g')
|
||||
//.attr('transform', 'translate(0,0)');
|
||||
|
||||
|
||||
|
||||
|
||||
// ********** LEGEND **********
|
||||
|
||||
legend.width(width/2 - margin.right);
|
||||
|
||||
g.select('.legendWrap')
|
||||
.datum(data)
|
||||
.attr('transform', 'translate(' + (width/2 - margin.left) + ',0)')
|
||||
.call(legend);
|
||||
|
||||
|
||||
//TODO: margins should be adjusted based on what components are used: axes, axis labels, legend
|
||||
margin.top = legend.height();
|
||||
|
||||
|
||||
|
||||
|
||||
// ********** FOCUS **********
|
||||
|
||||
focusWrap = g.select('.focus')
|
||||
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
|
||||
|
||||
gEnter.select('.focus').append('g').attr('class', 'x axis');
|
||||
gEnter.select('.focus').append('g').attr('class', 'y axis');
|
||||
gEnter.select('.focus').append('g').attr('class', 'focusLines');
|
||||
|
||||
|
||||
focusLines = g.select('.focusLines')
|
||||
.datum(data.filter(function(d) { return !d.disabled }))
|
||||
|
||||
d3.transition(focusLines).call(focus);
|
||||
|
||||
|
||||
xAxis
|
||||
.domain(x.domain())
|
||||
.range(x.range())
|
||||
.ticks( width / 100 )
|
||||
.tickSize(-(height1 - margin.top - margin.bottom), 0);
|
||||
|
||||
g.select('.x.axis')
|
||||
.attr('transform', 'translate(0,' + y.range()[0] + ')');
|
||||
d3.transition(g.select('.x.axis'))
|
||||
.call(xAxis);
|
||||
|
||||
yAxis
|
||||
.domain(y.domain())
|
||||
.range(y.range())
|
||||
.ticks( height / 36 )
|
||||
.tickSize(-(width - margin.right - margin.left), 0);
|
||||
|
||||
d3.transition(g.select('.y.axis'))
|
||||
.call(yAxis);
|
||||
|
||||
|
||||
|
||||
|
||||
// ********** CONTEXT **********
|
||||
|
||||
contextWrap = g.select('.context')
|
||||
.attr('transform', 'translate(' + margin2.left + ',' + height1 + ')');
|
||||
|
||||
gEnter.select('.context').append('g').attr('class', 'x2 axis');
|
||||
gEnter.select('.context').append('g').attr('class', 'y2 axis');
|
||||
gEnter.select('.context').append('g').attr('class', 'contextLines');
|
||||
gEnter.select('.context').append('g').attr('class', 'x brush')
|
||||
.attr('class', 'x brush')
|
||||
.call(brush)
|
||||
.selectAll('rect')
|
||||
.attr('y', -5)
|
||||
.attr('height', height2 + 4);
|
||||
|
||||
contextLines = contextWrap.select('.contextLines')
|
||||
.datum(data.filter(function(d) { return !d.disabled }))
|
||||
|
||||
d3.transition(contextLines).call(context);
|
||||
|
||||
|
||||
xAxis2
|
||||
.domain(x2.domain())
|
||||
.range(x2.range())
|
||||
.ticks( width / 100 )
|
||||
.tickSize(-(height2 - margin2.top - margin2.bottom), 0);
|
||||
|
||||
contextWrap.select('.x2.axis')
|
||||
.attr('transform', 'translate(0,' + y2.range()[0] + ')');
|
||||
d3.transition(contextWrap.select('.x2.axis'))
|
||||
.call(xAxis2);
|
||||
|
||||
|
||||
yAxis2
|
||||
.domain(y2.domain())
|
||||
.range(y2.range())
|
||||
.ticks( (height2 - margin2.top - margin2.bottom) / 24 )
|
||||
.tickSize(-(width - margin2.right - margin2.left), 0);
|
||||
|
||||
contextWrap.select('.y2.axis');
|
||||
|
||||
d3.transition(contextWrap.select('.y2.axis'))
|
||||
.call(yAxis2);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ********** EVENT LISTENERS **********
|
||||
|
||||
legend.dispatch.on('legendClick', function(d,i) {
|
||||
d.disabled = !d.disabled;
|
||||
|
||||
if (!data.filter(function(d) { return !d.disabled }).length) {
|
||||
data.map(function(d) {
|
||||
d.disabled = false;
|
||||
wrap.selectAll('.series').classed('disabled', false);
|
||||
return d;
|
||||
});
|
||||
}
|
||||
|
||||
selection.transition().call(chart);
|
||||
});
|
||||
|
||||
legend.dispatch.on('legendMouseover', function(d, i) {
|
||||
d.hover = true;
|
||||
selection.transition().call(chart)
|
||||
});
|
||||
legend.dispatch.on('legendMouseout', function(d, i) {
|
||||
d.hover = false;
|
||||
selection.transition().call(chart)
|
||||
});
|
||||
|
||||
|
||||
focus.dispatch.on('pointMouseover.tooltip', function(e) {
|
||||
dispatch.tooltipShow({
|
||||
point: e.point,
|
||||
series: e.series,
|
||||
pos: [e.pos[0] + margin.left, e.pos[1] + margin.top],
|
||||
seriesIndex: e.seriesIndex,
|
||||
pointIndex: e.pointIndex
|
||||
});
|
||||
});
|
||||
focus.dispatch.on('pointMouseout.tooltip', function(e) {
|
||||
dispatch.tooltipHide(e);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ********** FUNCTIONS **********
|
||||
|
||||
function onBrush() {
|
||||
x.domain(brush.empty() ? x2.domain() : brush.extent());
|
||||
|
||||
focus.xDomain(x.domain());
|
||||
|
||||
focusLines.call(focus)
|
||||
|
||||
wrap.select('.x.axis').call(xAxis);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ********** PUBLIC ACCESSORS **********
|
||||
|
||||
chart.dispatch = dispatch;
|
||||
|
||||
chart.x = function(_) {
|
||||
if (!arguments.length) return getX;
|
||||
getX = _;
|
||||
focus.x(_);
|
||||
context.x(_);
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.y = function(_) {
|
||||
if (!arguments.length) return getY;
|
||||
getY = _;
|
||||
focus.y(_);
|
||||
context.y(_);
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.margin = function(_) {
|
||||
if (!arguments.length) return margin;
|
||||
margin = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.width = function(_) {
|
||||
if (!arguments.length) return width;
|
||||
width = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.height = function(_) {
|
||||
if (!arguments.length) return height;
|
||||
height = _;
|
||||
height1 = _ - height2;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.contextHeight = function(_) {
|
||||
if (!arguments.length) return height2;
|
||||
height2 = _;
|
||||
height1 = height - _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.dotRadius = function(_) {
|
||||
if (!arguments.length) return dotRadius;
|
||||
dotRadius = d3.functor(_);
|
||||
focus.dotRadius = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.id = function(_) {
|
||||
if (!arguments.length) return id;
|
||||
id = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
|
||||
// Chart has multiple similar Axes, to prevent code duplication, probably need to link all axis functions manually like below
|
||||
chart.xTickFormat = function(_) {
|
||||
if (!arguments.length) return x.tickFormat();
|
||||
xAxis.tickFormat(_);
|
||||
xAxis2.tickFormat(_);
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.yTickFormat = function(_) {
|
||||
if (!arguments.length) return y.tickFormat();
|
||||
yAxis.tickFormat(_);
|
||||
yAxis2.tickFormat(_);
|
||||
return chart;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//TODO: allow for both focus and context axes to be linked
|
||||
chart.xAxis = xAxis;
|
||||
chart.yAxis = yAxis;
|
||||
|
||||
|
||||
return chart;
|
||||
}
|
@ -0,0 +1,188 @@
|
||||
|
||||
nv.models.lineWithLegend = function() {
|
||||
var margin = {top: 30, right: 20, bottom: 50, left: 60},
|
||||
width = 960,
|
||||
height = 500,
|
||||
dotRadius = function() { return 2.5 },
|
||||
color = d3.scale.category10().range(),
|
||||
dispatch = d3.dispatch('tooltipShow', 'tooltipHide');
|
||||
|
||||
var x = d3.scale.linear(),
|
||||
y = d3.scale.linear(),
|
||||
getX = function(d) { return d.x },
|
||||
getY = function(d) { return d.y },
|
||||
xAxis = nv.models.xaxis().scale(x),
|
||||
yAxis = nv.models.yaxis().scale(y),
|
||||
legend = nv.models.legend().height(30),
|
||||
lines = nv.models.line();
|
||||
|
||||
|
||||
function chart(selection) {
|
||||
selection.each(function(data) {
|
||||
var series = data.filter(function(d) { return !d.disabled })
|
||||
.map(function(d) { return d.values });
|
||||
|
||||
x .domain(d3.extent(d3.merge(series), getX ))
|
||||
.range([0, width - margin.left - margin.right]);
|
||||
|
||||
y .domain(d3.extent(d3.merge(series), getY ))
|
||||
.range([height - margin.top - margin.bottom, 0]);
|
||||
|
||||
lines
|
||||
.width(width - margin.left - margin.right)
|
||||
.height(height - margin.top - margin.bottom)
|
||||
.color(data.map(function(d,i) {
|
||||
return d.color || color[i % 10];
|
||||
}).filter(function(d,i) { return !data[i].disabled }))
|
||||
|
||||
|
||||
var wrap = d3.select(this).selectAll('g.wrap').data([data]);
|
||||
var gEnter = wrap.enter().append('g').attr('class', 'wrap d3lineWithLegend').append('g');
|
||||
|
||||
gEnter.append('g').attr('class', 'x axis');
|
||||
gEnter.append('g').attr('class', 'y axis');
|
||||
gEnter.append('g').attr('class', 'linesWrap');
|
||||
gEnter.append('g').attr('class', 'legendWrap');
|
||||
|
||||
|
||||
legend.dispatch.on('legendClick', function(d,i) {
|
||||
d.disabled = !d.disabled;
|
||||
|
||||
if (!data.filter(function(d) { return !d.disabled }).length) {
|
||||
data.map(function(d) {
|
||||
d.disabled = false;
|
||||
wrap.selectAll('.series').classed('disabled', false);
|
||||
return d;
|
||||
});
|
||||
}
|
||||
|
||||
selection.transition().call(chart);
|
||||
});
|
||||
|
||||
legend.dispatch.on('legendMouseover', function(d, i) {
|
||||
d.hover = true;
|
||||
selection.transition().call(chart)
|
||||
});
|
||||
|
||||
legend.dispatch.on('legendMouseout', function(d, i) {
|
||||
d.hover = false;
|
||||
selection.transition().call(chart)
|
||||
});
|
||||
|
||||
lines.dispatch.on('pointMouseover.tooltip', function(e) {
|
||||
dispatch.tooltipShow({
|
||||
point: e.point,
|
||||
series: e.series,
|
||||
pos: [e.pos[0] + margin.left, e.pos[1] + margin.top],
|
||||
seriesIndex: e.seriesIndex,
|
||||
pointIndex: e.pointIndex
|
||||
});
|
||||
});
|
||||
|
||||
lines.dispatch.on('pointMouseout.tooltip', function(e) {
|
||||
dispatch.tooltipHide(e);
|
||||
});
|
||||
|
||||
|
||||
//TODO: margins should be adjusted based on what components are used: axes, axis labels, legend
|
||||
margin.top = legend.height();
|
||||
|
||||
var g = wrap.select('g')
|
||||
.attr('transform', 'translate(' + margin.left + ',' + legend.height() + ')');
|
||||
|
||||
|
||||
legend.width(width/2 - margin.right);
|
||||
|
||||
g.select('.legendWrap')
|
||||
.datum(data)
|
||||
.attr('transform', 'translate(' + (width/2 - margin.left) + ',' + (-legend.height()) +')')
|
||||
.call(legend);
|
||||
|
||||
|
||||
var linesWrap = g.select('.linesWrap')
|
||||
.datum(data.filter(function(d) { return !d.disabled }))
|
||||
|
||||
|
||||
d3.transition(linesWrap).call(lines);
|
||||
|
||||
|
||||
xAxis
|
||||
.domain(x.domain())
|
||||
.range(x.range())
|
||||
.ticks( width / 100 )
|
||||
.tickSize(-(height - margin.top - margin.bottom), 0);
|
||||
|
||||
g.select('.x.axis')
|
||||
.attr('transform', 'translate(0,' + y.range()[0] + ')');
|
||||
d3.transition(g.select('.x.axis'))
|
||||
.call(xAxis);
|
||||
|
||||
yAxis
|
||||
.domain(y.domain())
|
||||
.range(y.range())
|
||||
.ticks( height / 36 )
|
||||
.tickSize(-(width - margin.right - margin.left), 0);
|
||||
|
||||
d3.transition(g.select('.y.axis'))
|
||||
.call(yAxis);
|
||||
|
||||
});
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
chart.dispatch = dispatch;
|
||||
|
||||
chart.x = function(_) {
|
||||
if (!arguments.length) return getX;
|
||||
getX = _;
|
||||
lines.x(_);
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.y = function(_) {
|
||||
if (!arguments.length) return getY;
|
||||
getY = _;
|
||||
lines.y(_);
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.margin = function(_) {
|
||||
if (!arguments.length) return margin;
|
||||
margin = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.width = function(_) {
|
||||
if (!arguments.length) return width;
|
||||
width = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.height = function(_) {
|
||||
if (!arguments.length) return height;
|
||||
height = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.dotRadius = function(_) {
|
||||
if (!arguments.length) return dotRadius;
|
||||
dotRadius = d3.functor(_);
|
||||
lines.dotRadius = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
|
||||
// Expose the x-axis' tickFormat method.
|
||||
//chart.xAxis = {};
|
||||
//d3.rebind(chart.xAxis, xAxis, 'tickFormat');
|
||||
chart.xAxis = xAxis;
|
||||
|
||||
// Expose the y-axis' tickFormat method.
|
||||
//chart.yAxis = {};
|
||||
//d3.rebind(chart.yAxis, yAxis, 'tickFormat');
|
||||
chart.yAxis = yAxis;
|
||||
|
||||
|
||||
return chart;
|
||||
}
|
@ -0,0 +1,289 @@
|
||||
|
||||
nv.models.scatter = function() {
|
||||
var margin = {top: 0, right: 0, bottom: 0, left: 0},
|
||||
width = 960,
|
||||
height = 500,
|
||||
color = d3.scale.category10().range(),
|
||||
id = Math.floor(Math.random() * 100000), //Create semi-unique ID incase user doesn't selet one
|
||||
x = d3.scale.linear(),
|
||||
y = d3.scale.linear(),
|
||||
z = d3.scale.sqrt(), //sqrt because point size is done by area, not radius
|
||||
getX = function(d) { return d.x }, // or d[0]
|
||||
getY = function(d) { return d.y }, // or d[1]
|
||||
getSize = function(d) { return d.size }, // or d[2]
|
||||
forceX = [],
|
||||
forceY = [],
|
||||
x0, y0, z0,
|
||||
dispatch = d3.dispatch('pointMouseover', 'pointMouseout');
|
||||
|
||||
|
||||
function chart(selection) {
|
||||
selection.each(function(data) {
|
||||
var seriesData = data.map(function(d) { return d.values });
|
||||
|
||||
x0 = x0 || x;
|
||||
y0 = y0 || y;
|
||||
z0 = z0 || z;
|
||||
|
||||
//TODO: reconsider points {x: #, y: #} instead of [x,y]
|
||||
//add series data to each point for future ease of use
|
||||
data = data.map(function(series, i) {
|
||||
series.values = series.values.map(function(point) {
|
||||
//point.label = series.label;
|
||||
//point.color = series.color;
|
||||
point.series = i;
|
||||
return point;
|
||||
});
|
||||
return series;
|
||||
});
|
||||
|
||||
|
||||
//TODO: figure out the best way to deal with scales with equal MIN and MAX
|
||||
x .domain(d3.extent(d3.merge(seriesData).map( getX ).concat(forceX) ))
|
||||
.range([0, width - margin.left - margin.right]);
|
||||
|
||||
y .domain(d3.extent(d3.merge(seriesData).map( getY ).concat(forceY) ))
|
||||
.range([height - margin.top - margin.bottom, 0]);
|
||||
|
||||
z .domain(d3.extent(d3.merge(seriesData), getSize ))
|
||||
.range([2, 10]);
|
||||
|
||||
|
||||
var vertices = d3.merge(data.map(function(group, groupIndex) {
|
||||
return group.values.map(function(point, pointIndex) {
|
||||
return [x(getX(point)), y(getY(point)), groupIndex, pointIndex]; //inject series and point index for reference into voronoi
|
||||
})
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
var wrap = d3.select(this).selectAll('g.d3scatter').data([data]);
|
||||
var gEnter = wrap.enter().append('g').attr('class', 'd3scatter').append('g');
|
||||
|
||||
gEnter.append('g').attr('class', 'groups');
|
||||
gEnter.append('g').attr('class', 'point-clips');
|
||||
gEnter.append('g').attr('class', 'point-paths');
|
||||
gEnter.append('g').attr('class', 'distribution');
|
||||
|
||||
var g = wrap.select('g')
|
||||
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
|
||||
|
||||
|
||||
var voronoiClip = gEnter.append('g').attr('class', 'voronoi-clip')
|
||||
.append('clipPath')
|
||||
.attr('id', 'voronoi-clip-path-' + id)
|
||||
.append('rect');
|
||||
wrap.select('.voronoi-clip rect')
|
||||
.attr('x', -10)
|
||||
.attr('y', -10)
|
||||
.attr('width', width - margin.left - margin.right + 20)
|
||||
.attr('height', height - margin.top - margin.bottom + 20);
|
||||
wrap.select('.point-paths')
|
||||
.attr('clip-path', 'url(#voronoi-clip-path-' + id + ')');
|
||||
|
||||
|
||||
//var pointClips = wrap.select('.point-clips').selectAll('clipPath') // **BROWSER BUG** can't reselect camel cased elements
|
||||
var pointClips = wrap.select('.point-clips').selectAll('.clip-path')
|
||||
.data(vertices);
|
||||
pointClips.enter().append('clipPath').attr('class', 'clip-path')
|
||||
.append('circle')
|
||||
.attr('r', 25);
|
||||
pointClips.exit().remove();
|
||||
pointClips
|
||||
.attr('id', function(d, i) { return 'clip-' + id + '-' + d[2] + '-' + d[3] })
|
||||
.attr('transform', function(d) { return 'translate(' + d[0] + ',' + d[1] + ')' })
|
||||
|
||||
|
||||
//inject series and point index for reference into voronoi
|
||||
var voronoi = d3.geom.voronoi(vertices).map(function(d, i) { return { 'data': d, 'series': vertices[i][2], 'point': vertices[i][3] } });
|
||||
|
||||
|
||||
//TODO: Need to deal with duplicates, maybe add small amount of noise to all
|
||||
var pointPaths = wrap.select('.point-paths').selectAll('path')
|
||||
.data(voronoi);
|
||||
pointPaths.enter().append('path')
|
||||
.attr('class', function(d,i) { return 'path-'+i; });
|
||||
pointPaths.exit().remove();
|
||||
pointPaths
|
||||
.attr('clip-path', function(d,i) { return 'url(#clip-' + id + '-' + d.series + '-' + d.point +')' })
|
||||
.attr('d', function(d) { return 'M' + d.data.join(',') + 'Z'; })
|
||||
.on('mouseover', function(d) {
|
||||
dispatch.pointMouseover({
|
||||
point: data[d.series].values[d.point],
|
||||
series: data[d.series],
|
||||
pos: [x(getX(data[d.series].values[d.point])) + margin.left, y(getY(data[d.series].values[d.point])) + margin.top],
|
||||
seriesIndex: d.series,
|
||||
pointIndex: d.point
|
||||
}
|
||||
);
|
||||
})
|
||||
.on('mouseout', function(d, i) {
|
||||
dispatch.pointMouseout({
|
||||
point: data[d.series].values[d.point],
|
||||
series: data[d.series],
|
||||
seriesIndex: d.series,
|
||||
pointIndex: d.point
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
var groups = wrap.select('.groups').selectAll('.group')
|
||||
.data(function(d) { return d }, function(d) { return d.key });
|
||||
groups.enter().append('g')
|
||||
.style('stroke-opacity', 1e-6)
|
||||
.style('fill-opacity', 1e-6);
|
||||
d3.transition(groups.exit())
|
||||
.style('stroke-opacity', 1e-6)
|
||||
.style('fill-opacity', 1e-6)
|
||||
.remove();
|
||||
groups
|
||||
.attr('class', function(d,i) { return 'group series-' + i })
|
||||
.classed('hover', function(d) { return d.hover && !d.disabled });
|
||||
d3.transition(groups)
|
||||
.style('fill', function(d,i) { return color[i % 10] })
|
||||
.style('stroke', function(d,i) { return color[i % 10] })
|
||||
.style('stroke-opacity', 1)
|
||||
.style('fill-opacity', .5);
|
||||
|
||||
|
||||
var points = groups.selectAll('circle.point')
|
||||
.data(function(d) { return d.values });
|
||||
points.enter().append('circle')
|
||||
.attr('cx', function(d) { return x0(getX(d)) })
|
||||
.attr('cy', function(d) { return y0(getY(d)) })
|
||||
.attr('r', function(d) { return z0(getSize(d)) });
|
||||
points.exit().remove();
|
||||
points.attr('class', function(d,i) { return 'point point-' + i });
|
||||
d3.transition(points)
|
||||
.attr('cx', function(d) { return x(getX(d)) })
|
||||
.attr('cy', function(d) { return y(getY(d)) })
|
||||
.attr('r', function(d) { return z(getSize(d)) });
|
||||
|
||||
|
||||
|
||||
var distX = groups.selectAll('line.distX')
|
||||
.data(function(d) { return d.values })
|
||||
distX.enter().append('line')
|
||||
.attr('x1', function(d) { return x0(getX(d)) })
|
||||
.attr('x2', function(d) { return x0(getX(d)) })
|
||||
distX
|
||||
.attr('class', function(d,i) { return 'distX distX-' + i })
|
||||
.attr('y1', y.range()[0])
|
||||
.attr('y2', y.range()[0] + 8);
|
||||
d3.transition(distX)
|
||||
.attr('x1', function(d) { return x(getX(d)) })
|
||||
.attr('x2', function(d) { return x(getX(d)) })
|
||||
distX.exit().remove();
|
||||
|
||||
var distY = groups.selectAll('line.distY')
|
||||
.data(function(d) { return d.values })
|
||||
distY.enter().append('line')
|
||||
.attr('y1', function(d) { return y0(getY(d)) })
|
||||
.attr('y2', function(d) { return y0(getY(d)) });
|
||||
distY
|
||||
.attr('class', function(d,i) { return 'distY distY-' + i })
|
||||
.attr('x1', x.range()[0])
|
||||
.attr('x2', x.range()[0] - 8)
|
||||
d3.transition(distY)
|
||||
.attr('y1', function(d) { return y(getY(d)) })
|
||||
.attr('y2', function(d) { return y(getY(d)) });
|
||||
distY.exit().remove();
|
||||
|
||||
|
||||
|
||||
dispatch.on('pointMouseover.point', function(d) {
|
||||
wrap.select('.series-' + d.seriesIndex + ' .point-' + d.pointIndex)
|
||||
.classed('hover', true);
|
||||
wrap.select('.series-' + d.seriesIndex + ' .distX-' + d.pointIndex)
|
||||
.attr('y1', d.pos[1] - margin.top);
|
||||
wrap.select('.series-' + d.seriesIndex + ' .distY-' + d.pointIndex)
|
||||
.attr('x1', d.pos[0] - margin.left);
|
||||
});
|
||||
|
||||
dispatch.on('pointMouseout.point', function(d) {
|
||||
wrap.select('.series-' + d.seriesIndex + ' circle.point-' + d.pointIndex)
|
||||
.classed('hover', false);
|
||||
wrap.select('.series-' + d.seriesIndex + ' .distX-' + d.pointIndex)
|
||||
.attr('y1', y.range()[0]);
|
||||
wrap.select('.series-' + d.seriesIndex + ' .distY-' + d.pointIndex)
|
||||
.attr('x1', x.range()[0]);
|
||||
});
|
||||
|
||||
|
||||
//store old scales for use in transitions on update
|
||||
x0 = x.copy();
|
||||
y0 = y.copy();
|
||||
z0 = z.copy();
|
||||
|
||||
});
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
chart.dispatch = dispatch;
|
||||
|
||||
chart.margin = function(_) {
|
||||
if (!arguments.length) return margin;
|
||||
margin = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.width = function(_) {
|
||||
if (!arguments.length) return width;
|
||||
width = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.height = function(_) {
|
||||
if (!arguments.length) return height;
|
||||
height = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.x = function(_) {
|
||||
if (!arguments.length) return getX;
|
||||
getX = d3.functor(_);
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.y = function(_) {
|
||||
if (!arguments.length) return getY;
|
||||
getY = d3.functor(_);
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.size = function(_) {
|
||||
if (!arguments.length) return getSize;
|
||||
getSize = d3.functor(_);
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.forceX = function(_) {
|
||||
if (!arguments.length) return forceX;
|
||||
forceX = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.forceY = function(_) {
|
||||
if (!arguments.length) return forceY;
|
||||
forceY = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.color = function(_) {
|
||||
if (!arguments.length) return color;
|
||||
color = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.id = function(_) {
|
||||
if (!arguments.length) return id;
|
||||
id = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
|
||||
return chart;
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
|
||||
nv.models.scatterWithLegend = function() {
|
||||
var margin = {top: 30, right: 20, bottom: 50, left: 60},
|
||||
width = 960,
|
||||
height = 500,
|
||||
animate = 500,
|
||||
xAxisRender = true,
|
||||
yAxisRender = true,
|
||||
xAxisLabelText = false,
|
||||
yAxisLabelText = false,
|
||||
color = d3.scale.category10().range(),
|
||||
getX = function(d) { return d.x }, // or d[0]
|
||||
getY = function(d) { return d.y }, // or d[1]
|
||||
getSize = function(d) { return d.size }, // or d[2]
|
||||
forceX = [],
|
||||
forceY = [],
|
||||
dispatch = d3.dispatch('tooltipShow', 'tooltipHide');
|
||||
|
||||
var x = d3.scale.linear(),
|
||||
y = d3.scale.linear(),
|
||||
xAxis = nv.models.xaxis().scale(x).tickPadding(10),
|
||||
yAxis = nv.models.yaxis().scale(y).tickPadding(10),
|
||||
legend = nv.models.legend().height(30),
|
||||
scatter = nv.models.scatter();
|
||||
|
||||
|
||||
function chart(selection) {
|
||||
selection.each(function(data) {
|
||||
var seriesData = data.filter(function(d) { return !d.disabled })
|
||||
.map(function(d) { return d.values });
|
||||
|
||||
x .domain(d3.extent(d3.merge(seriesData).map(getX).concat(forceX) ))
|
||||
.range([0, width - margin.left - margin.right]);
|
||||
|
||||
y .domain(d3.extent(d3.merge(seriesData).map(getY).concat(forceY) ))
|
||||
.range([height - margin.top - margin.bottom, 0]);
|
||||
|
||||
scatter
|
||||
.width(width - margin.left - margin.right)
|
||||
.height(height - margin.top - margin.bottom)
|
||||
.color(data.map(function(d,i) {
|
||||
return d.color || color[i % 20];
|
||||
}).filter(function(d,i) { return !data[i].disabled }))
|
||||
|
||||
xAxis
|
||||
.ticks( width / 100 )
|
||||
.tickSize(-(height - margin.top - margin.bottom), 0);
|
||||
yAxis
|
||||
.ticks( height / 36 )
|
||||
.tickSize(-(width - margin.right - margin.left), 0);
|
||||
|
||||
|
||||
var wrap = d3.select(this).selectAll('g.wrap').data([data]);
|
||||
var gEnter = wrap.enter().append('g').attr('class', 'wrap d3lineWithLegend').append('g');
|
||||
|
||||
gEnter.append('g').attr('class', 'legendWrap');
|
||||
gEnter.append('g').attr('class', 'x axis');
|
||||
gEnter.append('g').attr('class', 'y axis');
|
||||
gEnter.append('g').attr('class', 'scatterWrap');
|
||||
|
||||
|
||||
legend.dispatch.on('legendClick', function(d,i, that) {
|
||||
d.disabled = !d.disabled;
|
||||
|
||||
//d3.select(that).classed('disabled', d.disabled); //TODO: do this from the data, not manually
|
||||
|
||||
if (!data.filter(function(d) { return !d.disabled }).length) {
|
||||
data.map(function(d) {
|
||||
d.disabled = false;
|
||||
wrap.selectAll('.series').classed('disabled', false);
|
||||
return d;
|
||||
});
|
||||
}
|
||||
|
||||
selection.transition(animate).call(chart)
|
||||
//d3.transition(selection).call(chart);
|
||||
});
|
||||
|
||||
legend.dispatch.on('legendMouseover', function(d, i) {
|
||||
d.hover = true;
|
||||
selection.transition().call(chart)
|
||||
});
|
||||
|
||||
legend.dispatch.on('legendMouseout', function(d, i) {
|
||||
d.hover = false;
|
||||
selection.transition().call(chart)
|
||||
});
|
||||
|
||||
|
||||
|
||||
scatter.dispatch.on('pointMouseover.tooltip', function(e) {
|
||||
dispatch.tooltipShow({
|
||||
point: e.point,
|
||||
series: e.series,
|
||||
pos: [e.pos[0] + margin.left, e.pos[1] + margin.top],
|
||||
seriesIndex: e.seriesIndex,
|
||||
pointIndex: e.pointIndex
|
||||
});
|
||||
});
|
||||
|
||||
scatter.dispatch.on('pointMouseout.tooltip', function(e) {
|
||||
dispatch.tooltipHide(e);
|
||||
});
|
||||
|
||||
legend.width(width/2 - margin.right);
|
||||
|
||||
wrap.select('.legendWrap')
|
||||
.datum(data)
|
||||
.attr('transform', 'translate(' + (width/2 - margin.left) + ',' + (-legend.height()) +')')
|
||||
.call(legend);
|
||||
|
||||
|
||||
//TODO: margins should be adjusted based on what components are used: axes, axis labels, legend
|
||||
margin.top = legend.height();
|
||||
|
||||
var g = wrap.select('g')
|
||||
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
|
||||
|
||||
|
||||
var scatterWrap = wrap.select('.scatterWrap')
|
||||
.datum(data.filter(function(d) { return !d.disabled }));
|
||||
|
||||
//log(d3.transition()[0][0].duration); //get parent's duration
|
||||
|
||||
d3.transition(scatterWrap).call(scatter);
|
||||
|
||||
|
||||
|
||||
xAxis
|
||||
.domain(x.domain())
|
||||
.range(x.range())
|
||||
.ticks( width / 100 )
|
||||
.tickSize(-(height - margin.top - margin.bottom), 0);
|
||||
|
||||
g.select('.x.axis')
|
||||
.attr('transform', 'translate(0,' + y.range()[0] + ')');
|
||||
|
||||
d3.transition(g.select('.x.axis'))
|
||||
.call(xAxis);
|
||||
|
||||
|
||||
yAxis
|
||||
.domain(y.domain())
|
||||
.range(y.range())
|
||||
.ticks( height / 36 )
|
||||
.tickSize(-(width - margin.right - margin.left), 0);
|
||||
|
||||
d3.transition(g.select('.y.axis'))
|
||||
.call(yAxis);
|
||||
|
||||
});
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
chart.dispatch = dispatch;
|
||||
|
||||
chart.margin = function(_) {
|
||||
if (!arguments.length) return margin;
|
||||
margin = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.width = function(_) {
|
||||
if (!arguments.length) return width;
|
||||
width = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.height = function(_) {
|
||||
if (!arguments.length) return height;
|
||||
height = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.forceX = function(_) {
|
||||
if (!arguments.length) return forceX;
|
||||
forceX = _;
|
||||
scatter.forceX(_);
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.forceY = function(_) {
|
||||
if (!arguments.length) return forceY;
|
||||
forceY = _;
|
||||
scatter.forceY(_);
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.animate = function(_) {
|
||||
if (!arguments.length) return animate;
|
||||
animate = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.xAxis = xAxis;
|
||||
chart.yAxis = yAxis;
|
||||
|
||||
return chart;
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
|
||||
nv.models.sparkline = function() {
|
||||
var margin = {top: 3, right: 3, bottom: 3, left: 3},
|
||||
width = 200,
|
||||
height = 20,
|
||||
animate = true,
|
||||
color = d3.scale.category20().range();
|
||||
|
||||
var x = d3.scale.linear(),
|
||||
y = d3.scale.linear();
|
||||
|
||||
function chart(selection) {
|
||||
selection.each(function(data) {
|
||||
|
||||
|
||||
x .domain(d3.extent(data, function(d) { return d.x } ))
|
||||
.range([0, width - margin.left - margin.right]);
|
||||
|
||||
y .domain(d3.extent(data, function(d) { return d.y } ))
|
||||
.range([height - margin.top - margin.bottom, 0]);
|
||||
|
||||
|
||||
var svg = d3.select(this).selectAll('svg').data([data]);
|
||||
|
||||
var gEnter = svg.enter().append('svg').append('g');
|
||||
gEnter.append('g').attr('class', 'sparkline')
|
||||
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
|
||||
//.style('fill', function(d, i){ return d.color || color[i * 2 % 20] })
|
||||
.style('stroke', function(d, i){ return d.color || color[i * 2 % 20] });
|
||||
|
||||
|
||||
svg .attr('width', width)
|
||||
.attr('height', height);
|
||||
|
||||
|
||||
var paths = gEnter.select('.sparkline').selectAll('path')
|
||||
.data(function(d) { return [d] });
|
||||
paths.enter().append('path');
|
||||
paths.exit().remove();
|
||||
paths
|
||||
.attr('d', d3.svg.line()
|
||||
.x(function(d) { return x(d.x) })
|
||||
.y(function(d) { return y(d.y) })
|
||||
);
|
||||
|
||||
|
||||
var points = gEnter.select('.sparkline').selectAll('circle.point')
|
||||
.data(function(d) { return d.filter(function(p) { return y.domain().indexOf(p.y) != -1 }) });
|
||||
points.enter().append('circle').attr('class', 'point');
|
||||
points.exit().remove();
|
||||
points
|
||||
.attr('cx', function(d) { return x(d.x) })
|
||||
.attr('cy', function(d) { return y(d.y) })
|
||||
.attr('r', 2)
|
||||
.style('stroke', function(d, i){ return d.y == y.domain()[0] ? '#d62728' : '#2ca02c' })
|
||||
.style('fill', function(d, i){ return d.y == y.domain()[0] ? '#d62728' : '#2ca02c' });
|
||||
});
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
chart.margin = function(_) {
|
||||
if (!arguments.length) return margin;
|
||||
margin = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.width = function(_) {
|
||||
if (!arguments.length) return width;
|
||||
width = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.height = function(_) {
|
||||
if (!arguments.length) return height;
|
||||
height = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.animate = function(_) {
|
||||
if (!arguments.length) return animate;
|
||||
animate = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
return chart;
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
|
||||
nv.models.xaxis = function() {
|
||||
var domain = [0,1], //just to have something to start with, maybe I dont need this
|
||||
range = [0,1],
|
||||
axisLabelText = false;
|
||||
|
||||
var x = d3.scale.linear(),
|
||||
axis = d3.svg.axis().scale(x).orient('bottom');
|
||||
|
||||
function chart(selection) {
|
||||
selection.each(function(data) {
|
||||
|
||||
x .domain(domain)
|
||||
.range(range);
|
||||
|
||||
//TODO: consider calculating height based on whether or not label is added, for reference in charts using this component
|
||||
|
||||
var axisLabel = d3.select(this).selectAll('text.axislabel')
|
||||
.data([axisLabelText || null]);
|
||||
axisLabel.enter().append('text').attr('class', 'axislabel')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('x', range[1] / 2)
|
||||
.attr('y', 25);
|
||||
axisLabel.exit().remove();
|
||||
axisLabel.text(function(d) { return d });
|
||||
|
||||
|
||||
//d3.select(this)
|
||||
d3.transition(d3.select(this))
|
||||
.call(axis);
|
||||
|
||||
d3.select(this)
|
||||
.selectAll('line.tick')
|
||||
//.filter(function(d) { return !parseFloat(d) })
|
||||
.filter(function(d) { return !parseFloat(Math.round(d*100000)/1000000) })
|
||||
.classed('zero', true);
|
||||
|
||||
});
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
chart.domain = function(_) {
|
||||
if (!arguments.length) return domain;
|
||||
domain = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.range = function(_) {
|
||||
if (!arguments.length) return range;
|
||||
range = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.axisLabel = function(_) {
|
||||
if (!arguments.length) return axisLabelText;
|
||||
axisLabelText = _;
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
d3.rebind(chart, axis, 'scale', 'orient', 'ticks', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat');
|
||||
|
||||
return chart;
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
|
||||
nv.models.yaxis = function() {
|
||||
var domain = [0,1], //just to have something to start with
|
||||
range = [0,1],
|
||||
axisLabelText = false;
|
||||
|
||||
var y = d3.scale.linear(),
|
||||
axis = d3.svg.axis().scale(y).orient('left');
|
||||
|
||||
function chart(selection) {
|
||||
selection.each(function(data) {
|
||||
|
||||
y .domain(domain)
|
||||
.range(range);
|
||||
|
||||
|
||||
//TODO: consider calculating width based on whether or not label is added, for reference in charts using this component
|
||||
|
||||
var axisLabel = d3.select(this).selectAll('text.axislabel')
|
||||
.data([axisLabelText || null]);
|
||||
axisLabel.enter().append('text').attr('class', 'axislabel')
|
||||
.attr('transform', 'rotate(-90)')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('y', -40); //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
|
||||
axisLabel.exit().remove();
|
||||
axisLabel
|
||||
.attr('x', -range[0] / 2)
|
||||
.text(function(d) { return d });
|
||||
|
||||
|
||||
//d3.select(this)
|
||||
d3.transition(d3.select(this))
|
||||
.call(axis);
|
||||
|
||||
d3.select(this)
|
||||
.selectAll('line.tick')
|
||||
//.filter(function(d) { return !parseFloat(d) })
|
||||
.filter(function(d) { return !parseFloat(Math.round(d*100000)/1000000) })
|
||||
.classed('zero', true);
|
||||
|
||||
});
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
chart.domain = function(_) {
|
||||
if (!arguments.length) return domain;
|
||||
domain = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.range = function(_) {
|
||||
if (!arguments.length) return range;
|
||||
range = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.axisLabel = function(_) {
|
||||
if (!arguments.length) return axisLabelText;
|
||||
axisLabelText = _;
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
d3.rebind(chart, axis, 'scale', 'orient', 'ticks', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat');
|
||||
|
||||
return chart;
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
|
||||
/*****
|
||||
* A no frills tooltip implementation.
|
||||
*****/
|
||||
|
||||
(function($) {
|
||||
|
||||
var nvtooltip = window.nvtooltip = {};
|
||||
|
||||
nvtooltip.show = function(pos, content, gravity, dist) {
|
||||
var container = $('<div class="nvtooltip">');
|
||||
|
||||
gravity = gravity || 's';
|
||||
dist = dist || 20;
|
||||
|
||||
container
|
||||
.html(content)
|
||||
.css({left: -1000, top: -1000, opacity: 0})
|
||||
.appendTo('body'); //append the container out of view so we can get measurements
|
||||
|
||||
var height = container.height() + parseInt(container.css('padding-top')) + parseInt(container.css('padding-bottom')),
|
||||
width = container.width() + parseInt(container.css('padding-left')) + parseInt(container.css('padding-right')),
|
||||
windowWidth = $(window).width(),
|
||||
windowHeight = $(window).height(),
|
||||
scrollTop = $('body').scrollTop(),
|
||||
scrollLeft = $('body').scrollLeft(),
|
||||
left, top;
|
||||
|
||||
|
||||
switch (gravity) {
|
||||
case 'e':
|
||||
left = pos[0] - width - dist;
|
||||
top = pos[1] - (height / 2);
|
||||
if (left < scrollLeft) left = pos[0] + dist;
|
||||
if (top < scrollTop) top = scrollTop + 5;
|
||||
if (top + height > scrollTop + windowHeight) top = scrollTop - height - 5;
|
||||
break;
|
||||
case 'w':
|
||||
left = pos[0] + dist;
|
||||
top = pos[1] - (height / 2);
|
||||
if (left + width > windowWidth) left = pos[0] - width - dist;
|
||||
if (top < scrollTop) top = scrollTop + 5;
|
||||
if (top + height > scrollTop + windowHeight) top = scrollTop - height - 5;
|
||||
break;
|
||||
case 'n':
|
||||
left = pos[0] - (width / 2);
|
||||
top = pos[1] + dist;
|
||||
if (left < scrollLeft) left = scrollLeft + 5;
|
||||
if (left + width > windowWidth) left = windowWidth - width - 5;
|
||||
if (top + height > scrollTop + windowHeight) top = pos[1] - height - dist;
|
||||
break;
|
||||
case 's':
|
||||
left = pos[0] - (width / 2);
|
||||
top = pos[1] - height - dist;
|
||||
if (left < scrollLeft) left = scrollLeft + 5;
|
||||
if (left + width > windowWidth) left = windowWidth - width - 5;
|
||||
if (scrollTop > top) top = pos[1] + 20;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
container
|
||||
.css({
|
||||
left: left,
|
||||
top: top,
|
||||
opacity: 1
|
||||
});
|
||||
|
||||
return container;
|
||||
};
|
||||
|
||||
nvtooltip.cleanup = function() {
|
||||
var tooltips = $('.nvtooltip');
|
||||
|
||||
tooltips.css({
|
||||
'transition-delay': '0 !important',
|
||||
'-moz-transition-delay': '0 !important',
|
||||
'-webkit-transition-delay': '0 !important'
|
||||
});
|
||||
|
||||
tooltips.css('opacity',0);
|
||||
|
||||
setTimeout(function() {
|
||||
tooltips.remove()
|
||||
}, 500);
|
||||
};
|
||||
|
||||
})(jQuery);
|
@ -0,0 +1 @@
|
||||
})(d3, window);
|
Loading…
Reference in New Issue