| /** | 
|  * Highcharts Drilldown plugin | 
|  *  | 
|  * Author: Torstein Honsi | 
|  * Last revision: 2013-02-18 | 
|  * License: MIT License | 
|  * | 
|  * Demo: http://jsfiddle.net/highcharts/Vf3yT/ | 
|  */ | 
|   | 
| /*global HighchartsAdapter*/ | 
| (function (H) { | 
|   | 
|     "use strict"; | 
|   | 
|     var noop = function () {}, | 
|         defaultOptions = H.getOptions(), | 
|         each = H.each, | 
|         extend = H.extend, | 
|         wrap = H.wrap, | 
|         Chart = H.Chart, | 
|         seriesTypes = H.seriesTypes, | 
|         PieSeries = seriesTypes.pie, | 
|         ColumnSeries = seriesTypes.column, | 
|         fireEvent = HighchartsAdapter.fireEvent; | 
|   | 
|     // 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(',') + ')'; | 
|     } | 
|   | 
|     // Add language | 
|     extend(defaultOptions.lang, { | 
|         drillUpText: '◁ Back to {series.name}' | 
|     }); | 
|     defaultOptions.drilldown = { | 
|         activeAxisLabelStyle: { | 
|             cursor: 'pointer', | 
|             color: '#039', | 
|             fontWeight: 'bold', | 
|             textDecoration: 'underline'             | 
|         }, | 
|         activeDataLabelStyle: { | 
|             cursor: 'pointer', | 
|             color: '#039', | 
|             fontWeight: 'bold', | 
|             textDecoration: 'underline'             | 
|         }, | 
|         animation: { | 
|             duration: 500 | 
|         }, | 
|         drillUpButton: { | 
|             position: {  | 
|                 align: 'right', | 
|                 x: -10, | 
|                 y: 10 | 
|             } | 
|             // relativeTo: 'plotBox' | 
|             // theme | 
|         } | 
|     };     | 
|   | 
|     /** | 
|      * A general fadeIn method | 
|      */ | 
|     H.SVGRenderer.prototype.Element.prototype.fadeIn = function () { | 
|         this | 
|         .attr({ | 
|             opacity: 0.1, | 
|             visibility: 'visible' | 
|         }) | 
|         .animate({ | 
|             opacity: 1 | 
|         }, { | 
|             duration: 250 | 
|         }); | 
|     }; | 
|   | 
|     // Extend the Chart prototype | 
|     Chart.prototype.drilldownLevels = []; | 
|   | 
|     Chart.prototype.addSeriesAsDrilldown = function (point, ddOptions) { | 
|         var oldSeries = point.series, | 
|             xAxis = oldSeries.xAxis, | 
|             yAxis = oldSeries.yAxis, | 
|             newSeries, | 
|             color = point.color || oldSeries.color, | 
|             pointIndex, | 
|             level; | 
|              | 
|         ddOptions = extend({ | 
|             color: color | 
|         }, ddOptions); | 
|         pointIndex = HighchartsAdapter.inArray(this, oldSeries.points); | 
|         level = { | 
|             seriesOptions: oldSeries.userOptions, | 
|             shapeArgs: point.shapeArgs, | 
|             bBox: point.graphic.getBBox(), | 
|             color: color, | 
|             newSeries: ddOptions, | 
|             pointOptions: oldSeries.options.data[pointIndex], | 
|             pointIndex: pointIndex, | 
|             oldExtremes: { | 
|                 xMin: xAxis && xAxis.userMin, | 
|                 xMax: xAxis && xAxis.userMax, | 
|                 yMin: yAxis && yAxis.userMin, | 
|                 yMax: yAxis && yAxis.userMax | 
|             } | 
|         }; | 
|   | 
|         this.drilldownLevels.push(level); | 
|   | 
|         newSeries = this.addSeries(ddOptions, false); | 
|         if (xAxis) { | 
|             xAxis.oldPos = xAxis.pos; | 
|             xAxis.userMin = xAxis.userMax = null; | 
|             yAxis.userMin = yAxis.userMax = null; | 
|         } | 
|   | 
|         // Run fancy cross-animation on supported and equal types | 
|         if (oldSeries.type === newSeries.type) { | 
|             newSeries.animate = newSeries.animateDrilldown || noop; | 
|             newSeries.options.animation = true; | 
|         } | 
|          | 
|         oldSeries.remove(false); | 
|          | 
|         this.redraw(); | 
|         this.showDrillUpButton(); | 
|     }; | 
|   | 
|     Chart.prototype.getDrilldownBackText = function () { | 
|         var lastLevel = this.drilldownLevels[this.drilldownLevels.length - 1]; | 
|   | 
|         return this.options.lang.drillUpText.replace('{series.name}', lastLevel.seriesOptions.name); | 
|   | 
|     }; | 
|   | 
|     Chart.prototype.showDrillUpButton = function () { | 
|         var chart = this, | 
|             backText = this.getDrilldownBackText(), | 
|             buttonOptions = chart.options.drilldown.drillUpButton; | 
|              | 
|   | 
|         if (!this.drillUpButton) { | 
|             this.drillUpButton = this.renderer.button( | 
|                 backText, | 
|                 null, | 
|                 null, | 
|                 function () { | 
|                     chart.drillUp();  | 
|                 } | 
|             ) | 
|             .attr(extend({ | 
|                 align: buttonOptions.position.align, | 
|                 zIndex: 9 | 
|             }, buttonOptions.theme)) | 
|             .add() | 
|             .align(buttonOptions.position, false, buttonOptions.relativeTo || 'plotBox'); | 
|         } else { | 
|             this.drillUpButton.attr({ | 
|                 text: backText | 
|             }) | 
|             .align(); | 
|         } | 
|     }; | 
|   | 
|     Chart.prototype.drillUp = function () { | 
|         var chart = this, | 
|             level = chart.drilldownLevels.pop(), | 
|             oldSeries = chart.series[0], | 
|             oldExtremes = level.oldExtremes, | 
|             newSeries = chart.addSeries(level.seriesOptions, false); | 
|          | 
|         fireEvent(chart, 'drillup', { seriesOptions: level.seriesOptions }); | 
|   | 
|         if (newSeries.type === oldSeries.type) { | 
|             newSeries.drilldownLevel = level; | 
|             newSeries.animate = newSeries.animateDrillupTo || noop; | 
|             newSeries.options.animation = true; | 
|   | 
|             if (oldSeries.animateDrillupFrom) { | 
|                 oldSeries.animateDrillupFrom(level); | 
|             } | 
|         } | 
|   | 
|         oldSeries.remove(false); | 
|   | 
|         // Reset the zoom level of the upper series | 
|         if (newSeries.xAxis) { | 
|             newSeries.xAxis.setExtremes(oldExtremes.xMin, oldExtremes.xMax, false); | 
|             newSeries.yAxis.setExtremes(oldExtremes.yMin, oldExtremes.yMax, false); | 
|         } | 
|   | 
|   | 
|         this.redraw(); | 
|   | 
|         if (this.drilldownLevels.length === 0) { | 
|             this.drillUpButton = this.drillUpButton.destroy(); | 
|         } else { | 
|             this.drillUpButton.attr({ | 
|                 text: this.getDrilldownBackText() | 
|             }) | 
|             .align(); | 
|         } | 
|     }; | 
|   | 
|     PieSeries.prototype.animateDrilldown = function (init) { | 
|         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; | 
|   | 
|         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 | 
|                     })) | 
|                     .animate(point.shapeArgs, H.merge(animationOptions, { | 
|                         step: function (val, fx) { | 
|                             if (fx.prop === 'start') { | 
|                                 this.attr({ | 
|                                     fill: tweenColors(startColor, endColor, fx.pos) | 
|                                 }); | 
|                             } | 
|                         } | 
|                     })); | 
|                 /*jslint unparam: false*/ | 
|             }); | 
|         } | 
|     }; | 
|   | 
|   | 
|     /** | 
|      * When drilling up, keep the upper series invisible until the lower series has | 
|      * moved into place | 
|      */ | 
|     PieSeries.prototype.animateDrillupTo =  | 
|             ColumnSeries.prototype.animateDrillupTo = function (init) { | 
|         if (!init) { | 
|             var newSeries = this, | 
|                 level = newSeries.drilldownLevel; | 
|   | 
|             each(this.points, function (point) { | 
|                 point.graphic.hide(); | 
|                 if (point.dataLabel) { | 
|                     point.dataLabel.hide(); | 
|                 } | 
|                 if (point.connector) { | 
|                     point.connector.hide(); | 
|                 } | 
|             }); | 
|   | 
|   | 
|             // 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.pointIndex ? 'show' : 'fadeIn'; | 
|                     point.graphic[verb](); | 
|                     if (point.dataLabel) { | 
|                         point.dataLabel[verb](); | 
|                     } | 
|                     if (point.connector) { | 
|                         point.connector[verb](); | 
|                     } | 
|                 }); | 
|             }, Math.max(this.chart.options.drilldown.animation.duration - 50, 0)); | 
|   | 
|             // Reset | 
|             this.animate = noop; | 
|         } | 
|   | 
|     }; | 
|      | 
|     ColumnSeries.prototype.animateDrilldown = function (init) { | 
|         var animateFrom = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1].shapeArgs, | 
|             animationOptions = this.chart.options.drilldown.animation; | 
|              | 
|         if (!init) { | 
|   | 
|             animateFrom.x += (this.xAxis.oldPos - this.xAxis.pos); | 
|      | 
|             each(this.points, function (point) { | 
|                 point.graphic | 
|                     .attr(animateFrom) | 
|                     .animate(point.shapeArgs, animationOptions); | 
|             }); | 
|         } | 
|          | 
|     }; | 
|   | 
|     /** | 
|      * When drilling up, pull out the individual point graphics from the lower series | 
|      * and animate them into the origin point in the upper series. | 
|      */ | 
|     ColumnSeries.prototype.animateDrillupFrom =  | 
|         PieSeries.prototype.animateDrillupFrom = | 
|     function (level) { | 
|         var animationOptions = this.chart.options.drilldown.animation, | 
|             group = this.group; | 
|   | 
|         delete this.group; | 
|         each(this.points, function (point) { | 
|             var graphic = point.graphic, | 
|                 startColor = H.Color(point.color).rgba; | 
|   | 
|             delete point.graphic; | 
|   | 
|             /*jslint unparam: true*/ | 
|             graphic.animate(level.shapeArgs, H.merge(animationOptions, { | 
|   | 
|                 step: function (val, fx) { | 
|                     if (fx.prop === 'start') { | 
|                         this.attr({ | 
|                             fill: tweenColors(startColor, H.Color(level.color).rgba, fx.pos) | 
|                         }); | 
|                     } | 
|                 }, | 
|                 complete: function () { | 
|                     graphic.destroy(); | 
|                     if (group) { | 
|                         group = group.destroy(); | 
|                     } | 
|                 } | 
|             })); | 
|             /*jslint unparam: false*/ | 
|         }); | 
|     }; | 
|      | 
|     H.Point.prototype.doDrilldown = function () { | 
|         var series = this.series, | 
|             chart = series.chart, | 
|             drilldown = chart.options.drilldown, | 
|             i = drilldown.series.length, | 
|             seriesOptions; | 
|          | 
|         while (i-- && !seriesOptions) { | 
|             if (drilldown.series[i].id === this.drilldown) { | 
|                 seriesOptions = drilldown.series[i]; | 
|             } | 
|         } | 
|   | 
|         // 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 | 
|         }); | 
|          | 
|         if (seriesOptions) { | 
|             chart.addSeriesAsDrilldown(this, seriesOptions); | 
|         } | 
|   | 
|     }; | 
|      | 
|     wrap(H.Point.prototype, 'init', function (proceed, series, options, x) { | 
|         var point = proceed.call(this, series, options, x), | 
|             chart = series.chart, | 
|             tick = series.xAxis && series.xAxis.ticks[x], | 
|             tickLabel = tick && tick.label; | 
|          | 
|         if (point.drilldown) { | 
|              | 
|             // Add the click event to the point label | 
|             H.addEvent(point, 'click', function () { | 
|                 point.doDrilldown(); | 
|             }); | 
|              | 
|             // Make axis labels clickable | 
|             if (tickLabel) { | 
|                 if (!tickLabel._basicStyle) { | 
|                     tickLabel._basicStyle = tickLabel.element.getAttribute('style'); | 
|                 } | 
|                 tickLabel | 
|                     .addClass('highcharts-drilldown-axis-label') | 
|                     .css(chart.options.drilldown.activeAxisLabelStyle) | 
|                     .on('click', function () { | 
|                         if (point.doDrilldown) { | 
|                             point.doDrilldown(); | 
|                         } | 
|                     }); | 
|                      | 
|             } | 
|         } else if (tickLabel && tickLabel._basicStyle) { | 
|             tickLabel.element.setAttribute('style', tickLabel._basicStyle); | 
|         } | 
|          | 
|         return point; | 
|     }); | 
|   | 
|     wrap(H.Series.prototype, 'drawDataLabels', function (proceed) { | 
|         var css = this.chart.options.drilldown.activeDataLabelStyle; | 
|   | 
|         proceed.call(this); | 
|   | 
|         each(this.points, function (point) { | 
|             if (point.drilldown && point.dataLabel) { | 
|                 point.dataLabel | 
|                     .attr({ | 
|                         'class': 'highcharts-drilldown-data-label' | 
|                     }) | 
|                     .css(css) | 
|                     .on('click', function () { | 
|                         point.doDrilldown(); | 
|                     }); | 
|             } | 
|         }); | 
|     }); | 
|   | 
|     // Mark the trackers with a pointer  | 
|     ColumnSeries.prototype.supportsDrilldown = true; | 
|     PieSeries.prototype.supportsDrilldown = true; | 
|     var type,  | 
|         drawTrackerWrapper = function (proceed) { | 
|             proceed.call(this); | 
|             each(this.points, function (point) { | 
|                 if (point.drilldown && point.graphic) { | 
|                     point.graphic | 
|                         .attr({ | 
|                             'class': 'highcharts-drilldown-point' | 
|                         }) | 
|                         .css({ cursor: 'pointer' }); | 
|                 } | 
|             }); | 
|         }; | 
|     for (type in seriesTypes) { | 
|         if (seriesTypes[type].prototype.supportsDrilldown) { | 
|             wrap(seriesTypes[type].prototype, 'drawTracker', drawTrackerWrapper); | 
|         } | 
|     } | 
|          | 
| }(Highcharts)); |