import CLASS from './class'; import { ChartInternal } from './core'; import { isFunction } from './util'; ChartInternal.prototype.initBrush = function (scale) { var $$ = this, d3 = $$.d3; // TODO: dynamically change brushY/brushX according to axis_rotated. $$.brush = ($$.config.axis_rotated ? d3.brushY() : d3.brushX()).on("brush", function () { var event = d3.event.sourceEvent; if (event && event.type === "zoom") { return; } $$.redrawForBrush(); }).on("end", function () { var event = d3.event.sourceEvent; if (event && event.type === "zoom") { return; } if ($$.brush.empty() && event && event.type !== 'end') { $$.brush.clear(); } }); $$.brush.updateExtent = function () { var range = this.scale.range(), extent; if ($$.config.axis_rotated) { extent = [[0, range[0]], [$$.width2, range[1]]]; } else { extent = [[range[0], 0], [range[1], $$.height2]]; } this.extent(extent); return this; }; $$.brush.updateScale = function (scale) { this.scale = scale; return this; }; $$.brush.update = function (scale) { this.updateScale(scale || $$.subX).updateExtent(); $$.context.select('.' + CLASS.brush).call(this); }; $$.brush.clear = function () { $$.context.select('.' + CLASS.brush).call($$.brush.move, null); }; $$.brush.selection = function () { return d3.brushSelection($$.context.select('.' + CLASS.brush).node()); }; $$.brush.selectionAsValue = function (selectionAsValue, withTransition) { var selection, brush; if (selectionAsValue) { if ($$.context) { selection = [this.scale(selectionAsValue[0]), this.scale(selectionAsValue[1])]; brush = $$.context.select('.' + CLASS.brush); if (withTransition) { brush = brush.transition(); } $$.brush.move(brush, selection); } return []; } selection = $$.brush.selection() || [0,0]; return [this.scale.invert(selection[0]), this.scale.invert(selection[1])]; }; $$.brush.empty = function () { var selection = $$.brush.selection(); return !selection || selection[0] === selection[1]; }; return $$.brush.updateScale(scale); }; ChartInternal.prototype.initSubchart = function () { var $$ = this, config = $$.config, context = $$.context = $$.svg.append("g").attr("transform", $$.getTranslate('context')), visibility = config.subchart_show ? 'visible' : 'hidden'; // set style context.style('visibility', visibility); // Define g for chart area context.append('g') .attr("clip-path", $$.clipPathForSubchart) .attr('class', CLASS.chart); // Define g for bar chart area context.select('.' + CLASS.chart).append("g") .attr("class", CLASS.chartBars); // Define g for line chart area context.select('.' + CLASS.chart).append("g") .attr("class", CLASS.chartLines); // Add extent rect for Brush context.append("g") .attr("clip-path", $$.clipPath) .attr("class", CLASS.brush); // ATTENTION: This must be called AFTER chart added // Add Axis $$.axes.subx = context.append("g") .attr("class", CLASS.axisX) .attr("transform", $$.getTranslate('subx')) .attr("clip-path", config.axis_rotated ? "" : $$.clipPathForXAxis); }; ChartInternal.prototype.initSubchartBrush = function () { var $$ = this; // Add extent rect for Brush $$.initBrush($$.subX).updateExtent(); $$.context.select('.' + CLASS.brush).call($$.brush); }; ChartInternal.prototype.updateTargetsForSubchart = function (targets) { var $$ = this, context = $$.context, config = $$.config, contextLineEnter, contextLine, contextBarEnter, contextBar, classChartBar = $$.classChartBar.bind($$), classBars = $$.classBars.bind($$), classChartLine = $$.classChartLine.bind($$), classLines = $$.classLines.bind($$), classAreas = $$.classAreas.bind($$); if (config.subchart_show) { //-- Bar --// contextBar = context.select('.' + CLASS.chartBars).selectAll('.' + CLASS.chartBar) .data(targets); contextBarEnter = contextBar.enter().append('g') .style('opacity', 0); contextBarEnter.merge(contextBar) .attr('class', classChartBar); // Bars for each data contextBarEnter.append('g') .attr("class", classBars); //-- Line --// contextLine = context.select('.' + CLASS.chartLines).selectAll('.' + CLASS.chartLine) .data(targets); contextLineEnter = contextLine.enter().append('g') .style('opacity', 0); contextLineEnter.merge(contextLine) .attr('class', classChartLine); // Lines for each data contextLineEnter.append("g") .attr("class", classLines); // Area contextLineEnter.append("g") .attr("class", classAreas); //-- Brush --// context.selectAll('.' + CLASS.brush + ' rect') .attr(config.axis_rotated ? "width" : "height", config.axis_rotated ? $$.width2 : $$.height2); } }; ChartInternal.prototype.updateBarForSubchart = function (durationForExit) { var $$ = this; var contextBar = $$.context.selectAll('.' + CLASS.bars).selectAll('.' + CLASS.bar) .data($$.barData.bind($$)); var contextBarEnter = contextBar.enter().append('path') .attr("class", $$.classBar.bind($$)) .style("stroke", 'none') .style("fill", $$.color); contextBar.exit().transition().duration(durationForExit) .style('opacity', 0) .remove(); $$.contextBar = contextBarEnter.merge(contextBar) .style("opacity", $$.initialOpacity.bind($$)); }; ChartInternal.prototype.redrawBarForSubchart = function (drawBarOnSub, withTransition, duration) { (withTransition ? this.contextBar.transition(Math.random().toString()).duration(duration) : this.contextBar) .attr('d', drawBarOnSub) .style('opacity', 1); }; ChartInternal.prototype.updateLineForSubchart = function (durationForExit) { var $$ = this; var contextLine = $$.context.selectAll('.' + CLASS.lines).selectAll('.' + CLASS.line) .data($$.lineData.bind($$)); var contextLineEnter = contextLine.enter().append('path') .attr('class', $$.classLine.bind($$)) .style('stroke', $$.color); contextLine.exit().transition().duration(durationForExit) .style('opacity', 0) .remove(); $$.contextLine = contextLineEnter.merge(contextLine) .style("opacity", $$.initialOpacity.bind($$)); }; ChartInternal.prototype.redrawLineForSubchart = function (drawLineOnSub, withTransition, duration) { (withTransition ? this.contextLine.transition(Math.random().toString()).duration(duration) : this.contextLine) .attr("d", drawLineOnSub) .style('opacity', 1); }; ChartInternal.prototype.updateAreaForSubchart = function (durationForExit) { var $$ = this, d3 = $$.d3; var contextArea = $$.context.selectAll('.' + CLASS.areas).selectAll('.' + CLASS.area) .data($$.lineData.bind($$)); var contextAreaEnter = contextArea.enter().append('path') .attr("class", $$.classArea.bind($$)) .style("fill", $$.color) .style("opacity", function () { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0; }); contextArea.exit().transition().duration(durationForExit) .style('opacity', 0) .remove(); $$.contextArea = contextAreaEnter.merge(contextArea) .style("opacity", 0); }; ChartInternal.prototype.redrawAreaForSubchart = function (drawAreaOnSub, withTransition, duration) { (withTransition ? this.contextArea.transition(Math.random().toString()).duration(duration) : this.contextArea) .attr("d", drawAreaOnSub) .style("fill", this.color) .style("opacity", this.orgAreaOpacity); }; ChartInternal.prototype.redrawSubchart = function (withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices) { var $$ = this, d3 = $$.d3, config = $$.config, drawAreaOnSub, drawBarOnSub, drawLineOnSub; $$.context.style('visibility', config.subchart_show ? 'visible' : 'hidden'); // subchart if (config.subchart_show) { // reflect main chart to extent on subchart if zoomed if (d3.event && d3.event.type === 'zoom') { $$.brush.selectionAsValue($$.x.orgDomain()); } // update subchart elements if needed if (withSubchart) { // extent rect if (!$$.brush.empty()) { $$.brush.selectionAsValue($$.x.orgDomain()); } // setup drawer - MEMO: this must be called after axis updated drawAreaOnSub = $$.generateDrawArea(areaIndices, true); drawBarOnSub = $$.generateDrawBar(barIndices, true); drawLineOnSub = $$.generateDrawLine(lineIndices, true); $$.updateBarForSubchart(duration); $$.updateLineForSubchart(duration); $$.updateAreaForSubchart(duration); $$.redrawBarForSubchart(drawBarOnSub, duration, duration); $$.redrawLineForSubchart(drawLineOnSub, duration, duration); $$.redrawAreaForSubchart(drawAreaOnSub, duration, duration); } } }; ChartInternal.prototype.redrawForBrush = function () { var $$ = this, x = $$.x, d3 = $$.d3, s; $$.redraw({ withTransition: false, withY: $$.config.zoom_rescale, withSubchart: false, withUpdateXDomain: true, withEventRect: false, withDimension: false }); // update zoom transation binded to event rect s = d3.event.selection || $$.brush.scale.range(); $$.main.select('.' + CLASS.eventRect).call($$.zoom.transform, d3.zoomIdentity .scale($$.width / (s[1] - s[0])) .translate(-s[0], 0)); $$.config.subchart_onbrush.call($$.api, x.orgDomain()); }; ChartInternal.prototype.transformContext = function (withTransition, transitions) { var $$ = this, subXAxis; if (transitions && transitions.axisSubX) { subXAxis = transitions.axisSubX; } else { subXAxis = $$.context.select('.' + CLASS.axisX); if (withTransition) { subXAxis = subXAxis.transition(); } } $$.context.attr("transform", $$.getTranslate('context')); subXAxis.attr("transform", $$.getTranslate('subx')); }; ChartInternal.prototype.getDefaultSelection = function () { var $$ = this, config = $$.config, selection = isFunction(config.axis_x_selection) ? config.axis_x_selection($$.getXDomain($$.data.targets)) : config.axis_x_selection; if ($$.isTimeSeries()) { selection = [$$.parseDate(selection[0]), $$.parseDate(selection[1])]; } return selection; };