Changeset View
Changeset View
Standalone View
Standalone View
webroot/rsrc/js/application/fact/Chart.js
Show All 20 Lines | members: { | ||||
}, | }, | ||||
_redraw: function() { | _redraw: function() { | ||||
if (!this._data) { | if (!this._data) { | ||||
return; | return; | ||||
} | } | ||||
var hardpoint = this._rootNode; | var hardpoint = this._rootNode; | ||||
// Remove the old chart (if one exists) before drawing the new chart. | |||||
JX.DOM.setContent(hardpoint, []); | |||||
var viewport = JX.Vector.getDim(hardpoint); | var viewport = JX.Vector.getDim(hardpoint); | ||||
var config = this._data; | var config = this._data; | ||||
function css_function(n) { | function css_function(n) { | ||||
return n + '(' + JX.$A(arguments).slice(1).join(', ') + ')'; | return n + '(' + JX.$A(arguments).slice(1).join(', ') + ')'; | ||||
} | } | ||||
var padding = { | var padding = { | ||||
top: 24, | top: 24, | ||||
left: 48, | left: 48, | ||||
bottom: 48, | bottom: 48, | ||||
right: 32 | right: 32 | ||||
}; | }; | ||||
var size = { | var size = { | ||||
frameWidth: viewport.x, | frameWidth: viewport.x, | ||||
frameHeight: viewport.y, | frameHeight: viewport.y, | ||||
}; | }; | ||||
size.width = size.frameWidth - padding.left - padding.right; | size.width = size.frameWidth - padding.left - padding.right; | ||||
size.height = size.frameHeight - padding.top - padding.bottom; | size.height = size.frameHeight - padding.top - padding.bottom; | ||||
var x = d3.time.scale() | var x = d3.scaleTime() | ||||
.range([0, size.width]); | .range([0, size.width]); | ||||
var y = d3.scale.linear() | var y = d3.scaleLinear() | ||||
.range([size.height, 0]); | .range([size.height, 0]); | ||||
var xAxis = d3.svg.axis() | var xAxis = d3.axisBottom(x); | ||||
.scale(x) | var yAxis = d3.axisLeft(y); | ||||
.orient('bottom'); | |||||
var yAxis = d3.svg.axis() | |||||
.scale(y) | |||||
.orient('left'); | |||||
// Remove the old chart (if one exists) before drawing the new chart. | |||||
JX.DOM.setContent(hardpoint, []); | |||||
amckinley: I'm just kinda assuming all these API changes are consistent with the New Hotness™. | |||||
var svg = d3.select('#' + hardpoint.id).append('svg') | var svg = d3.select('#' + hardpoint.id).append('svg') | ||||
.attr('width', size.frameWidth) | .attr('width', size.frameWidth) | ||||
.attr('height', size.frameHeight) | .attr('height', size.frameHeight) | ||||
.attr('class', 'chart'); | .attr('class', 'chart'); | ||||
var g = svg.append('g') | var g = svg.append('g') | ||||
.attr( | .attr( | ||||
'transform', | 'transform', | ||||
css_function('translate', padding.left, padding.top)); | css_function('translate', padding.left, padding.top)); | ||||
g.append('rect') | g.append('rect') | ||||
.attr('class', 'inner') | .attr('class', 'inner') | ||||
.attr('width', size.width) | .attr('width', size.width) | ||||
.attr('height', size.height); | .attr('height', size.height); | ||||
function as_date(value) { | x.domain([this._newDate(config.xMin), this._newDate(config.xMax)]); | ||||
return new Date(value * 1000); | |||||
} | |||||
x.domain([as_date(config.xMin), as_date(config.xMax)]); | |||||
y.domain([config.yMin, config.yMax]); | y.domain([config.yMin, config.yMax]); | ||||
var div = d3.select('body') | var div = d3.select('body') | ||||
.append('div') | .append('div') | ||||
.attr('class', 'chart-tooltip') | .attr('class', 'chart-tooltip') | ||||
.style('opacity', 0); | .style('opacity', 0); | ||||
for (var idx = 0; idx < config.datasets.length; idx++) { | for (var idx = 0; idx < config.datasets.length; idx++) { | ||||
var dataset = config.datasets[idx]; | var dataset = config.datasets[idx]; | ||||
var line = d3.svg.line() | switch (dataset.type) { | ||||
.x(function(d) { return x(d.xvalue); }) | case 'stacked-area': | ||||
.y(function(d) { return y(d.yvalue); }); | this._newStackedArea(g, dataset, x, y, div); | ||||
break; | |||||
var data = []; | |||||
for (var ii = 0; ii < dataset.x.length; ii++) { | |||||
data.push( | |||||
{ | |||||
xvalue: as_date(dataset.x[ii]), | |||||
yvalue: dataset.y[ii] | |||||
}); | |||||
} | } | ||||
} | |||||
g.append('g') | |||||
.attr('class', 'x axis') | |||||
.attr('transform', css_function('translate', 0, size.height)) | |||||
.call(xAxis); | |||||
g.append('g') | |||||
.attr('class', 'y axis') | |||||
.attr('transform', css_function('translate', 0, 0)) | |||||
.call(yAxis); | |||||
}, | |||||
_newStackedArea: function(g, dataset, x, y, div) { | |||||
var to_date = JX.bind(this, this._newDate); | |||||
var area = d3.area() | |||||
.x(function(d) { return x(to_date(d.x)); }) | |||||
.y0(function(d) { return y(d.y0); }) | |||||
.y1(function(d) { return y(d.y1); }); | |||||
var line = d3.line() | |||||
.x(function(d) { return x(to_date(d.x)); }) | |||||
.y(function(d) { return y(d.y1); }); | |||||
for (var ii = 0; ii < dataset.data.length; ii++) { | |||||
g.append('path') | |||||
.style('fill', dataset.color[ii % dataset.color.length]) | |||||
.style('opacity', '0.15') | |||||
.attr('d', area(dataset.data[ii])); | |||||
g.append('path') | g.append('path') | ||||
.datum(data) | |||||
.attr('class', 'line') | .attr('class', 'line') | ||||
.style('stroke', dataset.color) | .attr('d', line(dataset.data[ii])); | ||||
.attr('d', line); | |||||
g.selectAll('dot') | g.selectAll('dot') | ||||
.data(data) | .data(dataset.events[ii]) | ||||
.enter() | .enter() | ||||
.append('circle') | .append('circle') | ||||
.attr('class', 'point') | .attr('class', 'point') | ||||
.attr('r', 3) | .attr('r', 3) | ||||
.attr('cx', function(d) { return x(d.xvalue); }) | .attr('cx', function(d) { return x(to_date(d.x)); }) | ||||
.attr('cy', function(d) { return y(d.yvalue); }) | .attr('cy', function(d) { return y(d.y1); }) | ||||
.on('mouseover', function(d) { | .on('mouseover', function(d) { | ||||
var d_y = d.xvalue.getFullYear(); | var dd = to_date(d.x); | ||||
var d_y = dd.getFullYear(); | |||||
// NOTE: Javascript months are zero-based. See PHI1017. | // NOTE: Javascript months are zero-based. See PHI1017. | ||||
var d_m = d.xvalue.getMonth() + 1; | var d_m = dd.getMonth() + 1; | ||||
var d_d = d.xvalue.getDate(); | var d_d = dd.getDate(); | ||||
div | div | ||||
.html(d_y + '-' + d_m + '-' + d_d + ': ' + d.yvalue) | .html(d_y + '-' + d_m + '-' + d_d + ': ' + d.y1) | ||||
.style('opacity', 0.9) | .style('opacity', 0.9) | ||||
.style('left', (d3.event.pageX - 60) + 'px') | .style('left', (d3.event.pageX - 60) + 'px') | ||||
.style('top', (d3.event.pageY - 38) + 'px'); | .style('top', (d3.event.pageY - 38) + 'px'); | ||||
}) | }) | ||||
.on('mouseout', function() { | .on('mouseout', function() { | ||||
div.style('opacity', 0); | div.style('opacity', 0); | ||||
}); | }); | ||||
} | |||||
g.append('g') | } | ||||
.attr('class', 'x axis') | }, | ||||
.attr('transform', css_function('translate', 0, size.height)) | |||||
.call(xAxis); | |||||
g.append('g') | _newDate: function(epoch) { | ||||
.attr('class', 'y axis') | return new Date(epoch * 1000); | ||||
.attr('transform', css_function('translate', 0, 0)) | |||||
.call(yAxis); | |||||
} | } | ||||
} | } | ||||
}); | }); |
I'm just kinda assuming all these API changes are consistent with the New Hotness™.