Path: blob/master/webroot/rsrc/js/application/fact/Chart.js
12242 views
/**1* @provides javelin-chart2* @requires phui-chart-css3* d34* javelin-chart-curtain-view5* javelin-chart-function-label6*/7JX.install('Chart', {89construct: function(root_node) {10this._rootNode = root_node;1112JX.Stratcom.listen('resize', null, JX.bind(this, this._redraw));13},1415members: {16_rootNode: null,17_data: null,18_chartContainerNode: null,19_curtain: null,2021setData: function(blob) {22this._data = blob;23this._redraw();24},2526_redraw: function() {27if (!this._data) {28return;29}3031var hardpoint = this._rootNode;32var curtain = this._getCurtain();33var container_node = this._getChartContainerNode();3435var content = [36container_node,37curtain.getNode(),38];3940JX.DOM.setContent(hardpoint, content);4142// Remove the old chart (if one exists) before drawing the new chart.43JX.DOM.setContent(container_node, []);4445var viewport = JX.Vector.getDim(container_node);46var config = this._data;4748function css_function(n) {49return n + '(' + JX.$A(arguments).slice(1).join(', ') + ')';50}5152var padding = {};53if (JX.Device.isDesktop()) {54padding = {55top: 24,56left: 48,57bottom: 48,58right: 1259};60} else {61padding = {62top: 12,63left: 36,64bottom: 24,65right: 466};67}6869var size = {70frameWidth: viewport.x,71frameHeight: viewport.y,72};7374size.width = size.frameWidth - padding.left - padding.right;75size.height = size.frameHeight - padding.top - padding.bottom;7677var x = d3.scaleTime()78.range([0, size.width]);7980var y = d3.scaleLinear()81.range([size.height, 0]);8283var xAxis = d3.axisBottom(x);84var yAxis = d3.axisLeft(y);8586var svg = d3.select(container_node).append('svg')87.attr('width', size.frameWidth)88.attr('height', size.frameHeight)89.attr('class', 'chart');9091var g = svg.append('g')92.attr(93'transform',94css_function('translate', padding.left, padding.top));9596g.append('rect')97.attr('class', 'inner')98.attr('width', size.width)99.attr('height', size.height);100101x.domain([this._newDate(config.xMin), this._newDate(config.xMax)]);102y.domain([config.yMin, config.yMax]);103104var div = d3.select('body')105.append('div')106.attr('class', 'chart-tooltip')107.style('opacity', 0);108109curtain.reset();110111for (var idx = 0; idx < config.datasets.length; idx++) {112var dataset = config.datasets[idx];113114switch (dataset.type) {115case 'stacked-area':116this._newStackedArea(g, dataset, x, y, div, curtain);117break;118}119}120121curtain.redraw();122123g.append('g')124.attr('class', 'x axis')125.attr('transform', css_function('translate', 0, size.height))126.call(xAxis);127128g.append('g')129.attr('class', 'y axis')130.attr('transform', css_function('translate', 0, 0))131.call(yAxis);132},133134_newStackedArea: function(g, dataset, x, y, div, curtain) {135var ii;136137var to_date = JX.bind(this, this._newDate);138139var area = d3.area()140.x(function(d) { return x(to_date(d.x)); })141.y0(function(d) {142// When the area is positive, draw it above the X axis. When the area143// is negative, draw it below the X axis. We currently avoid having144// functions which cross the X axis by clever construction.145if (d.y0 >= 0 && d.y1 >= 0) {146return y(d.y0);147}148149if (d.y0 <= 0 && d.y1 <= 0) {150return y(d.y0);151}152153return y(0);154})155.y1(function(d) { return y(d.y1); });156157var line = d3.line()158.x(function(d) { return x(to_date(d.x)); })159.y(function(d) { return y(d.y1); });160161for (ii = 0; ii < dataset.data.length; ii++) {162var label = new JX.ChartFunctionLabel(dataset.labels[ii]);163164var fill_color = label.getFillColor() || label.getColor();165166g.append('path')167.style('fill', fill_color)168.attr('d', area(dataset.data[ii]));169170var stroke_color = label.getColor();171172g.append('path')173.attr('class', 'line')174.style('stroke', stroke_color)175.attr('d', line(dataset.data[ii]));176177curtain.addFunctionLabel(label);178}179180// Now that we've drawn all the areas and lines, draw the dots.181for (ii = 0; ii < dataset.data.length; ii++) {182g.selectAll('dot')183.data(dataset.events[ii])184.enter()185.append('circle')186.attr('class', 'point')187.attr('r', 3)188.attr('cx', function(d) { return x(to_date(d.x)); })189.attr('cy', function(d) { return y(d.y1); })190.on('mouseover', function(d) {191var dd = to_date(d.x);192193var d_y = dd.getFullYear();194195// NOTE: Javascript months are zero-based. See PHI1017.196var d_m = dd.getMonth() + 1;197198var d_d = dd.getDate();199200var y = parseInt(d.y1);201202var label = d.n + ' Points';203204var view =205d_y + '-' + d_m + '-' + d_d + ': ' + y + '<br />' +206label;207208div209.html(view)210.style('opacity', 0.9)211.style('left', (d3.event.pageX - 60) + 'px')212.style('top', (d3.event.pageY - 38) + 'px');213})214.on('mouseout', function() {215div.style('opacity', 0);216});217}218219},220221_newDate: function(epoch) {222return new Date(epoch * 1000);223},224225_getCurtain: function() {226if (!this._curtain) {227this._curtain = new JX.ChartCurtainView();228}229return this._curtain;230},231232_getChartContainerNode: function() {233if (!this._chartContainerNode) {234var attrs = {235className: 'chart-container'236};237238this._chartContainerNode = JX.$N('div', attrs);239}240return this._chartContainerNode;241}242243}244245});246247248