app/assets/javascripts/highcharts/modules/drilldown.js in highcharts-rails-4.0.4.1 vs app/assets/javascripts/highcharts/modules/drilldown.js in highcharts-rails-4.1.0

- old
+ new

@@ -5,11 +5,11 @@ * License: MIT License * * Demo: http://jsfiddle.net/highcharts/Vf3yT/ */ -/*global HighchartsAdapter*/ +/*global Highcharts,HighchartsAdapter*/ (function (H) { "use strict"; var noop = function () {}, @@ -23,22 +23,43 @@ seriesTypes = H.seriesTypes, PieSeries = seriesTypes.pie, ColumnSeries = seriesTypes.column, fireEvent = HighchartsAdapter.fireEvent, inArray = HighchartsAdapter.inArray, - dupes = []; + dupes = [], + ddSeriesId = 1; // Utilities - function tweenColors(startColor, endColor, pos) { - var rgba = [ - Math.round(startColor[0] + (endColor[0] - startColor[0]) * pos), - Math.round(startColor[1] + (endColor[1] - startColor[1]) * pos), - Math.round(startColor[2] + (endColor[2] - startColor[2]) * pos), - startColor[3] + (endColor[3] - startColor[3]) * pos - ]; - return 'rgba(' + rgba.join(',') + ')'; + /* + * Return an intermediate color between two colors, according to pos where 0 + * is the from color and 1 is the to color + */ + function tweenColors(from, to, pos) { + // Check for has alpha, because rgba colors perform worse due to lack of + // support in WebKit. + var hasAlpha; + + from = from.rgba; + to = to.rgba; + hasAlpha = (to[3] !== 1 || from[3] !== 1); + if (!to.length || !from.length) { + Highcharts.error(23); + } + return (hasAlpha ? 'rgba(' : 'rgb(') + + Math.round(to[0] + (from[0] - to[0]) * (1 - pos)) + ',' + + Math.round(to[1] + (from[1] - to[1]) * (1 - pos)) + ',' + + Math.round(to[2] + (from[2] - to[2]) * (1 - pos)) + + (hasAlpha ? (',' + (to[3] + (from[3] - to[3]) * (1 - pos))) : '') + ')'; } + /** + * Handle animation of the color attributes directly + */ + each(['fill', 'stroke'], function (prop) { + HighchartsAdapter.addAnimSetter(prop, function (fx) { + fx.elem.attr(prop, tweenColors(H.Color(fx.start), H.Color(fx.end), fx.pos)); + }); + }); // Add language extend(defaultOptions.lang, { drillUpText: '◁ Back to {series.name}' }); @@ -99,34 +120,37 @@ levelSeries = [], levelSeriesOptions = [], level, levelNumber; - levelNumber = oldSeries.levelNumber || 0; + levelNumber = oldSeries.options._levelNumber || 0; ddOptions = extend({ - color: color + color: color, + _ddSeriesId: ddSeriesId++ }, ddOptions); pointIndex = inArray(point, oldSeries.points); // Record options for all current series each(oldSeries.chart.series, function (series) { if (series.xAxis === xAxis) { levelSeries.push(series); - levelSeriesOptions.push(series.userOptions); - series.levelNumber = series.levelNumber || levelNumber; // #3182 + series.options._ddSeriesId = series.options._ddSeriesId || ddSeriesId++; + series.options._colorIndex = series.userOptions._colorIndex; + levelSeriesOptions.push(series.options); + series.options._levelNumber = series.options._levelNumber || levelNumber; // #3182 } }); // Add a record of properties for each drilldown level level = { levelNumber: levelNumber, - seriesOptions: oldSeries.userOptions, + seriesOptions: oldSeries.options, levelSeriesOptions: levelSeriesOptions, levelSeries: levelSeries, shapeArgs: point.shapeArgs, - bBox: point.graphic.getBBox(), + bBox: point.graphic ? point.graphic.getBBox() : {}, // no graphic in line series with markers disabled color: color, lowerSeriesOptions: ddOptions, pointOptions: oldSeries.options.data[pointIndex], pointIndex: pointIndex, oldExtremes: { @@ -142,11 +166,11 @@ this.drilldownLevels = []; } this.drilldownLevels.push(level); newSeries = level.lowerSeries = this.addSeries(ddOptions, false); - newSeries.levelNumber = levelNumber + 1; + newSeries.options._levelNumber = levelNumber + 1; if (xAxis) { xAxis.oldPos = xAxis.pos; xAxis.userMin = xAxis.userMax = null; yAxis.userMin = yAxis.userMax = null; } @@ -165,11 +189,11 @@ if (drilldownLevels && drilldownLevels.length > 0) { // #3352, async loading levelToRemove = drilldownLevels[drilldownLevels.length - 1].levelNumber; each(this.drilldownLevels, function (level) { if (level.levelNumber === levelToRemove) { each(level.levelSeries, function (series) { - if (series.levelNumber === levelToRemove) { // Not removed, not added as part of a multi-series drilldown + if (series.options && series.options._levelNumber === levelToRemove) { // Not removed, not added as part of a multi-series drilldown series.remove(false); } }); } }); @@ -231,19 +255,19 @@ var chart = this, drilldownLevels = chart.drilldownLevels, levelNumber = drilldownLevels[drilldownLevels.length - 1].levelNumber, i = drilldownLevels.length, chartSeries = chart.series, - seriesI = chartSeries.length, + seriesI, level, oldSeries, newSeries, oldExtremes, addSeries = function (seriesOptions) { var addedSeries; each(chartSeries, function (series) { - if (series.userOptions === seriesOptions) { + if (series.options._ddSeriesId === seriesOptions._ddSeriesId) { addedSeries = series; } }); addedSeries = addedSeries || chart.addSeries(seriesOptions, false); @@ -252,20 +276,21 @@ } if (seriesOptions === level.seriesOptions) { newSeries = addedSeries; } }; - + while (i--) { level = drilldownLevels[i]; if (level.levelNumber === levelNumber) { drilldownLevels.pop(); // Get the lower series by reference or id oldSeries = level.lowerSeries; if (!oldSeries.chart) { // #2786 + seriesI = chartSeries.length; // #2919 while (seriesI--) { if (chartSeries[seriesI].options.id === level.lowerSeriesOptions.id) { oldSeries = chartSeries[seriesI]; break; } @@ -279,15 +304,15 @@ if (newSeries.type === oldSeries.type) { newSeries.drilldownLevel = level; newSeries.options.animation = chart.options.drilldown.animation; - if (oldSeries.animateDrillupFrom) { + if (oldSeries.animateDrillupFrom && oldSeries.chart) { // #2919 oldSeries.animateDrillupFrom(level); } } - newSeries.levelNumber = levelNumber; + newSeries.options._levelNumber = levelNumber; oldSeries.remove(false); // Reset the zoom level of the upper series if (newSeries.xAxis) { @@ -323,11 +348,13 @@ if (!init) { var newSeries = this, level = newSeries.drilldownLevel; each(this.points, function (point) { - point.graphic.hide(); + if (point.graphic) { // #3407 + point.graphic.hide(); + } if (point.dataLabel) { point.dataLabel.hide(); } if (point.connector) { point.connector.hide(); @@ -335,22 +362,26 @@ }); // Do dummy animation on first point to get to complete setTimeout(function () { - each(newSeries.points, function (point, i) { - // Fade in other points - var verb = i === (level && level.pointIndex) ? 'show' : 'fadeIn', - inherit = verb === 'show' ? true : undefined; - point.graphic[verb](inherit); - if (point.dataLabel) { - point.dataLabel[verb](inherit); - } - if (point.connector) { - point.connector[verb](inherit); - } - }); + if (newSeries.points) { // May be destroyed in the meantime, #3389 + each(newSeries.points, function (point, i) { + // Fade in other points + var verb = i === (level && level.pointIndex) ? 'show' : 'fadeIn', + inherit = verb === 'show' ? true : undefined; + if (point.graphic) { // #3407 + point.graphic[verb](inherit); + } + if (point.dataLabel) { + point.dataLabel[verb](inherit); + } + if (point.connector) { + point.connector[verb](inherit); + } + }); + } }, Math.max(this.chart.options.drilldown.animation.duration - 50, 0)); // Reset this.animate = noop; } @@ -358,27 +389,32 @@ }; ColumnSeries.prototype.animateDrilldown = function (init) { var series = this, drilldownLevels = this.chart.drilldownLevels, - animateFrom = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1].shapeArgs, - animationOptions = this.chart.options.drilldown.animation; + animateFrom, + animationOptions = this.chart.options.drilldown.animation, + xAxis = this.xAxis; if (!init) { each(drilldownLevels, function (level) { - if (series.userOptions === level.lowerSeriesOptions) { + if (series.options._ddSeriesId === level.lowerSeriesOptions._ddSeriesId) { animateFrom = level.shapeArgs; + animateFrom.fill = level.color; } }); - animateFrom.x += (this.xAxis.oldPos - this.xAxis.pos); - + animateFrom.x += (pick(xAxis.oldPos, xAxis.pos) - xAxis.pos); + each(this.points, function (point) { if (point.graphic) { point.graphic .attr(animateFrom) - .animate(point.shapeArgs, animationOptions); + .animate( + extend(point.shapeArgs, { fill: point.color }), + animationOptions + ); } if (point.dataLabel) { point.dataLabel.fadeIn(animationOptions); } }); @@ -405,12 +441,10 @@ delete this.group; each(this.points, function (point) { var graphic = point.graphic, - startColor = H.Color(point.color).rgba, - endColor = H.Color(level.color).rgba, complete = function () { graphic.destroy(); if (group) { group = group.destroy(); } @@ -419,22 +453,14 @@ if (graphic) { delete point.graphic; if (animationOptions) { - /*jslint unparam: true*/ - graphic.animate(level.shapeArgs, H.merge(animationOptions, { - step: function (val, fx) { - if (fx.prop === 'start' && startColor.length === 4 && endColor.length === 4) { - this.attr({ - fill: tweenColors(startColor, endColor, fx.pos) - }); - } - }, - complete: complete - })); - /*jslint unparam: false*/ + graphic.animate( + extend(level.shapeArgs, { fill: level.color }), + H.merge(animationOptions, { complete: complete }) + ); } else { graphic.attr(level.shapeArgs); complete(); } } @@ -451,40 +477,31 @@ var level = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1], animationOptions = this.chart.options.drilldown.animation, animateFrom = level.shapeArgs, start = animateFrom.start, angle = animateFrom.end - start, - startAngle = angle / this.points.length, - startColor = H.Color(level.color).rgba; + startAngle = angle / this.points.length; if (!init) { each(this.points, function (point, i) { - var endColor = H.Color(point.color).rgba; - - /*jslint unparam: true*/ point.graphic .attr(H.merge(animateFrom, { start: start + i * startAngle, - end: start + (i + 1) * startAngle - }))[animationOptions ? 'animate' : 'attr'](point.shapeArgs, H.merge(animationOptions, { - step: function (val, fx) { - if (fx.prop === 'start' && startColor.length === 4 && endColor.length === 4) { - this.attr({ - fill: tweenColors(startColor, endColor, fx.pos) - }); - } - } - })); - /*jslint unparam: false*/ + end: start + (i + 1) * startAngle, + fill: level.color + }))[animationOptions ? 'animate' : 'attr']( + extend(point.shapeArgs, { fill: point.color }), + animationOptions + ); }); this.animate = null; } } }); } - H.Point.prototype.doDrilldown = function (_holdRedraw) { + H.Point.prototype.doDrilldown = function (_holdRedraw, category) { var series = this.series, chart = series.chart, drilldown = chart.options.drilldown, i = (drilldown.series || []).length, seriesOptions; @@ -498,21 +515,33 @@ // Fire the event. If seriesOptions is undefined, the implementer can check for // seriesOptions, and call addSeriesAsDrilldown async if necessary. fireEvent(chart, 'drilldown', { point: this, - seriesOptions: seriesOptions + seriesOptions: seriesOptions, + category: category }); if (seriesOptions) { if (_holdRedraw) { chart.addSingleSeriesAsDrilldown(this, seriesOptions); } else { chart.addSeriesAsDrilldown(this, seriesOptions); } } + }; + /** + * Drill down to a given category. This is the same as clicking on an axis label. + */ + H.Axis.prototype.drilldownCategory = function (x) { + each(this.ticks[x].label.ddPoints, function (point) { + if (point.series && point.series.visible && point.doDrilldown) { // #3197 + point.doDrilldown(true, x); + } + }); + this.chart.applyDrilldown(); }; wrap(H.Point.prototype, 'init', function (proceed, series, options, x) { var point = proceed.call(this, series, options, x), chart = series.chart, @@ -541,15 +570,10 @@ } tickLabel .addClass('highcharts-drilldown-axis-label') .css(chart.options.drilldown.activeAxisLabelStyle) .on('click', function () { - each(tickLabel.ddPoints, function (point) { - if (point.doDrilldown) { - point.doDrilldown(true); - } - }); - chart.applyDrilldown(); + series.xAxis.drilldownCategory(x); }); if (!tickLabel.ddPoints) { tickLabel.ddPoints = []; } tickLabel.ddPoints.push(point);