nvd3-fork
Version:
FORK! of NVD3, a reusable charting library written in d3.js
227 lines (190 loc) • 10.2 kB
JavaScript
nv.models.candlestickBar = function() {
"use strict";
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = {top: 0, right: 0, bottom: 0, left: 0}
, width = null
, height = null
, id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
, container
, x = d3.scale.linear()
, y = d3.scale.linear()
, getX = function(d) { return d.x }
, getY = function(d) { return d.y }
, getOpen = function(d) { return d.open }
, getClose = function(d) { return d.close }
, getHigh = function(d) { return d.high }
, getLow = function(d) { return d.low }
, forceX = []
, forceY = []
, padData = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart
, clipEdge = true
, color = nv.utils.defaultColor()
, interactive = false
, xDomain
, yDomain
, xRange
, yRange
, dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd', 'chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove')
;
//============================================================
// Private Variables
//------------------------------------------------------------
function chart(selection) {
selection.each(function(data) {
container = d3.select(this);
var availableWidth = nv.utils.availableWidth(width, container, margin),
availableHeight = nv.utils.availableHeight(height, container, margin);
nv.utils.initSVG(container);
// Width of the candlestick bars.
var barWidth = (availableWidth / data[0].values.length) * .45;
// Setup Scales
x.domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) ));
if (padData)
x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
else
x.range(xRange || [5 + barWidth / 2, availableWidth - barWidth / 2 - 5]);
y.domain(yDomain || [
d3.min(data[0].values.map(getLow).concat(forceY)),
d3.max(data[0].values.map(getHigh).concat(forceY))
]
).range(yRange || [availableHeight, 0]);
// If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
if (x.domain()[0] === x.domain()[1])
x.domain()[0] ?
x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
: x.domain([-1,1]);
if (y.domain()[0] === y.domain()[1])
y.domain()[0] ?
y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
: y.domain([-1,1]);
// Setup containers and skeleton of chart
var wrap = d3.select(this).selectAll('g.nv-wrap.nv-candlestickBar').data([data[0].values]);
var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-candlestickBar');
var defsEnter = wrapEnter.append('defs');
var gEnter = wrapEnter.append('g');
var g = wrap.select('g');
gEnter.append('g').attr('class', 'nv-ticks');
wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
container
.on('click', function(d,i) {
dispatch.chartClick({
data: d,
index: i,
pos: d3.event,
id: id
});
});
defsEnter.append('clipPath')
.attr('id', 'nv-chart-clip-path-' + id)
.append('rect');
wrap.select('#nv-chart-clip-path-' + id + ' rect')
.attr('width', availableWidth)
.attr('height', availableHeight);
g .attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : '');
var ticks = wrap.select('.nv-ticks').selectAll('.nv-tick')
.data(function(d) { return d });
ticks.exit().remove();
var tickGroups = ticks.enter().append('g');
// The colors are currently controlled by CSS.
ticks
.attr('class', function(d, i, j) { return (getOpen(d, i) > getClose(d, i) ? 'nv-tick negative' : 'nv-tick positive') + ' nv-tick-' + j + '-' + i});
var lines = tickGroups.append('line')
.attr('class', 'nv-candlestick-lines')
.attr('transform', function(d, i) { return 'translate(' + x(getX(d, i)) + ',0)'; })
.attr('x1', 0)
.attr('y1', function(d, i) { return y(getHigh(d, i)); })
.attr('x2', 0)
.attr('y2', function(d, i) { return y(getLow(d, i)); });
var rects = tickGroups.append('rect')
.attr('class', 'nv-candlestick-rects nv-bars')
.attr('transform', function(d, i) {
return 'translate(' + (x(getX(d, i)) - barWidth/2) + ','
+ (y(getY(d, i)) - (getOpen(d, i) > getClose(d, i) ? (y(getClose(d, i)) - y(getOpen(d, i))) : 0))
+ ')';
})
.attr('x', 0)
.attr('y', 0)
.attr('width', barWidth)
.attr('height', function(d, i) {
var open = getOpen(d, i);
var close = getClose(d, i);
return open > close ? y(close) - y(open) : y(open) - y(close);
});
ticks.select('.nv-candlestick-lines').transition()
.attr('transform', function(d, i) { return 'translate(' + x(getX(d, i)) + ',0)'; })
.attr('x1', 0)
.attr('y1', function(d, i) { return y(getHigh(d, i)); })
.attr('x2', 0)
.attr('y2', function(d, i) { return y(getLow(d, i)); });
ticks.select('.nv-candlestick-rects').transition()
.attr('transform', function(d, i) {
return 'translate(' + (x(getX(d, i)) - barWidth/2) + ','
+ (y(getY(d, i)) - (getOpen(d, i) > getClose(d, i) ? (y(getClose(d, i)) - y(getOpen(d, i))) : 0))
+ ')';
})
.attr('x', 0)
.attr('y', 0)
.attr('width', barWidth)
.attr('height', function(d, i) {
var open = getOpen(d, i);
var close = getClose(d, i);
return open > close ? y(close) - y(open) : y(open) - y(close);
});
});
return chart;
}
//Create methods to allow outside functions to highlight a specific bar.
chart.highlightPoint = function(pointIndex, isHoverOver) {
chart.clearHighlights();
container.select(".nv-candlestickBar .nv-tick-0-" + pointIndex)
.classed("hover", isHoverOver)
;
};
chart.clearHighlights = function() {
container.select(".nv-candlestickBar .nv-tick.hover")
.classed("hover", false)
;
};
//============================================================
// Expose Public Variables
//------------------------------------------------------------
chart.dispatch = dispatch;
chart.options = nv.utils.optionsFunc.bind(chart);
chart._options = Object.create({}, {
// simple options, just get/set the necessary values
width: {get: function(){return width;}, set: function(_){width=_;}},
height: {get: function(){return height;}, set: function(_){height=_;}},
xScale: {get: function(){return x;}, set: function(_){x=_;}},
yScale: {get: function(){return y;}, set: function(_){y=_;}},
xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}},
forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
padData: {get: function(){return padData;}, set: function(_){padData=_;}},
clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
id: {get: function(){return id;}, set: function(_){id=_;}},
interactive: {get: function(){return interactive;}, set: function(_){interactive=_;}},
x: {get: function(){return getX;}, set: function(_){getX=_;}},
y: {get: function(){return getY;}, set: function(_){getY=_;}},
open: {get: function(){return getOpen();}, set: function(_){getOpen=_;}},
close: {get: function(){return getClose();}, set: function(_){getClose=_;}},
high: {get: function(){return getHigh;}, set: function(_){getHigh=_;}},
low: {get: function(){return getLow;}, set: function(_){getLow=_;}},
// options that require extra logic in the setter
margin: {get: function(){return margin;}, set: function(_){
margin.top = _.top != undefined ? _.top : margin.top;
margin.right = _.right != undefined ? _.right : margin.right;
margin.bottom = _.bottom != undefined ? _.bottom : margin.bottom;
margin.left = _.left != undefined ? _.left : margin.left;
}},
color: {get: function(){return color;}, set: function(_){
color = nv.utils.getColor(_);
}}
});
nv.utils.initOptions(chart);
return chart;
};