// ==ClosureCompiler== 
 | 
// @compilation_level SIMPLE_OPTIMIZATIONS 
 | 
  
 | 
/** 
 | 
 * @license Highcharts JS v3.0.6 (2013-10-04) 
 | 
 * 
 | 
 * (c) 2009-2013 Torstein Hønsi 
 | 
 * 
 | 
 * License: www.highcharts.com/license 
 | 
 */ 
 | 
  
 | 
// JSLint options: 
 | 
/*global Highcharts, HighchartsAdapter, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console */ 
 | 
  
 | 
(function (Highcharts, UNDEFINED) { 
 | 
var arrayMin = Highcharts.arrayMin, 
 | 
    arrayMax = Highcharts.arrayMax, 
 | 
    each = Highcharts.each, 
 | 
    extend = Highcharts.extend, 
 | 
    merge = Highcharts.merge, 
 | 
    map = Highcharts.map, 
 | 
    pick = Highcharts.pick, 
 | 
    pInt = Highcharts.pInt, 
 | 
    defaultPlotOptions = Highcharts.getOptions().plotOptions, 
 | 
    seriesTypes = Highcharts.seriesTypes, 
 | 
    extendClass = Highcharts.extendClass, 
 | 
    splat = Highcharts.splat, 
 | 
    wrap = Highcharts.wrap, 
 | 
    Axis = Highcharts.Axis, 
 | 
    Tick = Highcharts.Tick, 
 | 
    Series = Highcharts.Series, 
 | 
    colProto = seriesTypes.column.prototype, 
 | 
    math = Math, 
 | 
    mathRound = math.round, 
 | 
    mathFloor = math.floor, 
 | 
    mathMax = math.max, 
 | 
    noop = function () {};/** 
 | 
 * The Pane object allows options that are common to a set of X and Y axes. 
 | 
 *  
 | 
 * In the future, this can be extended to basic Highcharts and Highstock. 
 | 
 */ 
 | 
function Pane(options, chart, firstAxis) { 
 | 
    this.init.call(this, options, chart, firstAxis); 
 | 
} 
 | 
  
 | 
// Extend the Pane prototype 
 | 
extend(Pane.prototype, { 
 | 
     
 | 
    /** 
 | 
     * Initiate the Pane object 
 | 
     */ 
 | 
    init: function (options, chart, firstAxis) { 
 | 
        var pane = this, 
 | 
            backgroundOption, 
 | 
            defaultOptions = pane.defaultOptions; 
 | 
         
 | 
        pane.chart = chart; 
 | 
         
 | 
        // Set options 
 | 
        if (chart.angular) { // gauges 
 | 
            defaultOptions.background = {}; // gets extended by this.defaultBackgroundOptions 
 | 
        } 
 | 
        pane.options = options = merge(defaultOptions, options); 
 | 
         
 | 
        backgroundOption = options.background; 
 | 
         
 | 
        // To avoid having weighty logic to place, update and remove the backgrounds, 
 | 
        // push them to the first axis' plot bands and borrow the existing logic there. 
 | 
        if (backgroundOption) { 
 | 
            each([].concat(splat(backgroundOption)).reverse(), function (config) { 
 | 
                var backgroundColor = config.backgroundColor; // if defined, replace the old one (specific for gradients) 
 | 
                config = merge(pane.defaultBackgroundOptions, config); 
 | 
                if (backgroundColor) { 
 | 
                    config.backgroundColor = backgroundColor; 
 | 
                } 
 | 
                config.color = config.backgroundColor; // due to naming in plotBands 
 | 
                firstAxis.options.plotBands.unshift(config); 
 | 
            }); 
 | 
        } 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * The default options object 
 | 
     */ 
 | 
    defaultOptions: { 
 | 
        // background: {conditional}, 
 | 
        center: ['50%', '50%'], 
 | 
        size: '85%', 
 | 
        startAngle: 0 
 | 
        //endAngle: startAngle + 360 
 | 
    },     
 | 
     
 | 
    /** 
 | 
     * The default background options 
 | 
     */ 
 | 
    defaultBackgroundOptions: { 
 | 
        shape: 'circle', 
 | 
        borderWidth: 1, 
 | 
        borderColor: 'silver', 
 | 
        backgroundColor: { 
 | 
            linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 
 | 
            stops: [ 
 | 
                [0, '#FFF'], 
 | 
                [1, '#DDD'] 
 | 
            ] 
 | 
        }, 
 | 
        from: Number.MIN_VALUE, // corrected to axis min 
 | 
        innerRadius: 0, 
 | 
        to: Number.MAX_VALUE, // corrected to axis max 
 | 
        outerRadius: '105%' 
 | 
    } 
 | 
     
 | 
}); 
 | 
var axisProto = Axis.prototype, 
 | 
    tickProto = Tick.prototype; 
 | 
     
 | 
/** 
 | 
 * Augmented methods for the x axis in order to hide it completely, used for the X axis in gauges 
 | 
 */ 
 | 
var hiddenAxisMixin = { 
 | 
    getOffset: noop, 
 | 
    redraw: function () { 
 | 
        this.isDirty = false; // prevent setting Y axis dirty 
 | 
    }, 
 | 
    render: function () { 
 | 
        this.isDirty = false; // prevent setting Y axis dirty 
 | 
    }, 
 | 
    setScale: noop, 
 | 
    setCategories: noop, 
 | 
    setTitle: noop 
 | 
}; 
 | 
  
 | 
/** 
 | 
 * Augmented methods for the value axis 
 | 
 */ 
 | 
/*jslint unparam: true*/ 
 | 
var radialAxisMixin = { 
 | 
    isRadial: true, 
 | 
     
 | 
    /** 
 | 
     * The default options extend defaultYAxisOptions 
 | 
     */ 
 | 
    defaultRadialGaugeOptions: { 
 | 
        labels: { 
 | 
            align: 'center', 
 | 
            x: 0, 
 | 
            y: null // auto 
 | 
        }, 
 | 
        minorGridLineWidth: 0, 
 | 
        minorTickInterval: 'auto', 
 | 
        minorTickLength: 10, 
 | 
        minorTickPosition: 'inside', 
 | 
        minorTickWidth: 1, 
 | 
        plotBands: [], 
 | 
        tickLength: 10, 
 | 
        tickPosition: 'inside', 
 | 
        tickWidth: 2, 
 | 
        title: { 
 | 
            rotation: 0 
 | 
        }, 
 | 
        zIndex: 2 // behind dials, points in the series group 
 | 
    }, 
 | 
     
 | 
    // Circular axis around the perimeter of a polar chart 
 | 
    defaultRadialXOptions: { 
 | 
        gridLineWidth: 1, // spokes 
 | 
        labels: { 
 | 
            align: null, // auto 
 | 
            distance: 15, 
 | 
            x: 0, 
 | 
            y: null // auto 
 | 
        }, 
 | 
        maxPadding: 0, 
 | 
        minPadding: 0, 
 | 
        plotBands: [], 
 | 
        showLastLabel: false,  
 | 
        tickLength: 0 
 | 
    }, 
 | 
     
 | 
    // Radial axis, like a spoke in a polar chart 
 | 
    defaultRadialYOptions: { 
 | 
        gridLineInterpolation: 'circle', 
 | 
        labels: { 
 | 
            align: 'right', 
 | 
            x: -3, 
 | 
            y: -2 
 | 
        }, 
 | 
        plotBands: [], 
 | 
        showLastLabel: false, 
 | 
        title: { 
 | 
            x: 4, 
 | 
            text: null, 
 | 
            rotation: 90 
 | 
        } 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Merge and set options 
 | 
     */ 
 | 
    setOptions: function (userOptions) { 
 | 
         
 | 
        this.options = merge( 
 | 
            this.defaultOptions, 
 | 
            this.defaultRadialOptions, 
 | 
            userOptions 
 | 
        ); 
 | 
         
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Wrap the getOffset method to return zero offset for title or labels in a radial  
 | 
     * axis 
 | 
     */ 
 | 
    getOffset: function () { 
 | 
        // Call the Axis prototype method (the method we're in now is on the instance) 
 | 
        axisProto.getOffset.call(this); 
 | 
         
 | 
        // Title or label offsets are not counted 
 | 
        this.chart.axisOffset[this.side] = 0; 
 | 
    }, 
 | 
  
 | 
  
 | 
    /** 
 | 
     * Get the path for the axis line. This method is also referenced in the getPlotLinePath 
 | 
     * method. 
 | 
     */ 
 | 
    getLinePath: function (lineWidth, radius) { 
 | 
        var center = this.center; 
 | 
        radius = pick(radius, center[2] / 2 - this.offset); 
 | 
         
 | 
        return this.chart.renderer.symbols.arc( 
 | 
            this.left + center[0], 
 | 
            this.top + center[1], 
 | 
            radius, 
 | 
            radius,  
 | 
            { 
 | 
                start: this.startAngleRad, 
 | 
                end: this.endAngleRad, 
 | 
                open: true, 
 | 
                innerR: 0 
 | 
            } 
 | 
        ); 
 | 
    }, 
 | 
  
 | 
    /** 
 | 
     * Override setAxisTranslation by setting the translation to the difference 
 | 
     * in rotation. This allows the translate method to return angle for  
 | 
     * any given value. 
 | 
     */ 
 | 
    setAxisTranslation: function () { 
 | 
         
 | 
        // Call uber method         
 | 
        axisProto.setAxisTranslation.call(this); 
 | 
             
 | 
        // Set transA and minPixelPadding 
 | 
        if (this.center) { // it's not defined the first time 
 | 
            if (this.isCircular) { 
 | 
                 
 | 
                this.transA = (this.endAngleRad - this.startAngleRad) /  
 | 
                    ((this.max - this.min) || 1); 
 | 
                     
 | 
                 
 | 
            } else {  
 | 
                this.transA = (this.center[2] / 2) / ((this.max - this.min) || 1); 
 | 
            } 
 | 
             
 | 
            if (this.isXAxis) { 
 | 
                this.minPixelPadding = this.transA * this.minPointOffset + 
 | 
                    (this.reversed ? (this.endAngleRad - this.startAngleRad) / 4 : 0); // ??? 
 | 
            } 
 | 
        } 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * In case of auto connect, add one closestPointRange to the max value right before 
 | 
     * tickPositions are computed, so that ticks will extend passed the real max. 
 | 
     */ 
 | 
    beforeSetTickPositions: function () { 
 | 
        if (this.autoConnect) { 
 | 
            this.max += (this.categories && 1) || this.pointRange || this.closestPointRange || 0; // #1197, #2260 
 | 
        } 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Override the setAxisSize method to use the arc's circumference as length. This 
 | 
     * allows tickPixelInterval to apply to pixel lengths along the perimeter 
 | 
     */ 
 | 
    setAxisSize: function () { 
 | 
         
 | 
        axisProto.setAxisSize.call(this); 
 | 
  
 | 
        if (this.isRadial) { 
 | 
  
 | 
            // Set the center array 
 | 
            this.center = this.pane.center = seriesTypes.pie.prototype.getCenter.call(this.pane); 
 | 
             
 | 
            this.len = this.width = this.height = this.isCircular ? 
 | 
                this.center[2] * (this.endAngleRad - this.startAngleRad) / 2 : 
 | 
                this.center[2] / 2; 
 | 
        } 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Returns the x, y coordinate of a point given by a value and a pixel distance 
 | 
     * from center 
 | 
     */ 
 | 
    getPosition: function (value, length) { 
 | 
        if (!this.isCircular) { 
 | 
            length = this.translate(value); 
 | 
            value = this.min;     
 | 
        } 
 | 
         
 | 
        return this.postTranslate( 
 | 
            this.translate(value), 
 | 
            pick(length, this.center[2] / 2) - this.offset 
 | 
        );         
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Translate from intermediate plotX (angle), plotY (axis.len - radius) to final chart coordinates.  
 | 
     */ 
 | 
    postTranslate: function (angle, radius) { 
 | 
         
 | 
        var chart = this.chart, 
 | 
            center = this.center; 
 | 
             
 | 
        angle = this.startAngleRad + angle; 
 | 
         
 | 
        return { 
 | 
            x: chart.plotLeft + center[0] + Math.cos(angle) * radius, 
 | 
            y: chart.plotTop + center[1] + Math.sin(angle) * radius 
 | 
        };  
 | 
         
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Find the path for plot bands along the radial axis 
 | 
     */ 
 | 
    getPlotBandPath: function (from, to, options) { 
 | 
        var center = this.center, 
 | 
            startAngleRad = this.startAngleRad, 
 | 
            fullRadius = center[2] / 2, 
 | 
            radii = [ 
 | 
                pick(options.outerRadius, '100%'), 
 | 
                options.innerRadius, 
 | 
                pick(options.thickness, 10) 
 | 
            ], 
 | 
            percentRegex = /%$/, 
 | 
            start, 
 | 
            end, 
 | 
            open, 
 | 
            isCircular = this.isCircular, // X axis in a polar chart 
 | 
            ret; 
 | 
             
 | 
        // Polygonal plot bands 
 | 
        if (this.options.gridLineInterpolation === 'polygon') { 
 | 
            ret = this.getPlotLinePath(from).concat(this.getPlotLinePath(to, true)); 
 | 
         
 | 
        // Circular grid bands 
 | 
        } else { 
 | 
             
 | 
            // Plot bands on Y axis (radial axis) - inner and outer radius depend on to and from 
 | 
            if (!isCircular) { 
 | 
                radii[0] = this.translate(from); 
 | 
                radii[1] = this.translate(to); 
 | 
            } 
 | 
             
 | 
            // Convert percentages to pixel values 
 | 
            radii = map(radii, function (radius) { 
 | 
                if (percentRegex.test(radius)) { 
 | 
                    radius = (pInt(radius, 10) * fullRadius) / 100; 
 | 
                } 
 | 
                return radius; 
 | 
            }); 
 | 
             
 | 
            // Handle full circle 
 | 
            if (options.shape === 'circle' || !isCircular) { 
 | 
                start = -Math.PI / 2; 
 | 
                end = Math.PI * 1.5; 
 | 
                open = true; 
 | 
            } else { 
 | 
                start = startAngleRad + this.translate(from); 
 | 
                end = startAngleRad + this.translate(to); 
 | 
            } 
 | 
         
 | 
         
 | 
            ret = this.chart.renderer.symbols.arc( 
 | 
                this.left + center[0], 
 | 
                this.top + center[1], 
 | 
                radii[0], 
 | 
                radii[0], 
 | 
                { 
 | 
                    start: start, 
 | 
                    end: end, 
 | 
                    innerR: pick(radii[1], radii[0] - radii[2]), 
 | 
                    open: open 
 | 
                } 
 | 
            ); 
 | 
        } 
 | 
          
 | 
        return ret; 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Find the path for plot lines perpendicular to the radial axis. 
 | 
     */ 
 | 
    getPlotLinePath: function (value, reverse) { 
 | 
        var axis = this, 
 | 
            center = axis.center, 
 | 
            chart = axis.chart, 
 | 
            end = axis.getPosition(value), 
 | 
            xAxis, 
 | 
            xy, 
 | 
            tickPositions, 
 | 
            ret; 
 | 
         
 | 
        // Spokes 
 | 
        if (axis.isCircular) { 
 | 
            ret = ['M', center[0] + chart.plotLeft, center[1] + chart.plotTop, 'L', end.x, end.y]; 
 | 
         
 | 
        // Concentric circles             
 | 
        } else if (axis.options.gridLineInterpolation === 'circle') { 
 | 
            value = axis.translate(value); 
 | 
            if (value) { // a value of 0 is in the center 
 | 
                ret = axis.getLinePath(0, value); 
 | 
            } 
 | 
        // Concentric polygons  
 | 
        } else { 
 | 
            xAxis = chart.xAxis[0]; 
 | 
            ret = []; 
 | 
            value = axis.translate(value); 
 | 
            tickPositions = xAxis.tickPositions; 
 | 
            if (xAxis.autoConnect) { 
 | 
                tickPositions = tickPositions.concat([tickPositions[0]]); 
 | 
            } 
 | 
            // Reverse the positions for concatenation of polygonal plot bands 
 | 
            if (reverse) { 
 | 
                tickPositions = [].concat(tickPositions).reverse(); 
 | 
            } 
 | 
                 
 | 
            each(tickPositions, function (pos, i) { 
 | 
                xy = xAxis.getPosition(pos, value); 
 | 
                ret.push(i ? 'L' : 'M', xy.x, xy.y); 
 | 
            }); 
 | 
             
 | 
        } 
 | 
        return ret; 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Find the position for the axis title, by default inside the gauge 
 | 
     */ 
 | 
    getTitlePosition: function () { 
 | 
        var center = this.center, 
 | 
            chart = this.chart, 
 | 
            titleOptions = this.options.title; 
 | 
         
 | 
        return {  
 | 
            x: chart.plotLeft + center[0] + (titleOptions.x || 0),  
 | 
            y: chart.plotTop + center[1] - ({ high: 0.5, middle: 0.25, low: 0 }[titleOptions.align] *  
 | 
                center[2]) + (titleOptions.y || 0)   
 | 
        }; 
 | 
    } 
 | 
     
 | 
}; 
 | 
/*jslint unparam: false*/ 
 | 
  
 | 
/** 
 | 
 * Override axisProto.init to mix in special axis instance functions and function overrides 
 | 
 */ 
 | 
wrap(axisProto, 'init', function (proceed, chart, userOptions) { 
 | 
    var axis = this, 
 | 
        angular = chart.angular, 
 | 
        polar = chart.polar, 
 | 
        isX = userOptions.isX, 
 | 
        isHidden = angular && isX, 
 | 
        isCircular, 
 | 
        startAngleRad, 
 | 
        endAngleRad, 
 | 
        options, 
 | 
        chartOptions = chart.options, 
 | 
        paneIndex = userOptions.pane || 0, 
 | 
        pane, 
 | 
        paneOptions; 
 | 
         
 | 
    // Before prototype.init 
 | 
    if (angular) { 
 | 
        extend(this, isHidden ? hiddenAxisMixin : radialAxisMixin); 
 | 
        isCircular =  !isX; 
 | 
        if (isCircular) { 
 | 
            this.defaultRadialOptions = this.defaultRadialGaugeOptions; 
 | 
        } 
 | 
         
 | 
    } else if (polar) { 
 | 
        //extend(this, userOptions.isX ? radialAxisMixin : radialAxisMixin); 
 | 
        extend(this, radialAxisMixin); 
 | 
        isCircular = isX; 
 | 
        this.defaultRadialOptions = isX ? this.defaultRadialXOptions : merge(this.defaultYAxisOptions, this.defaultRadialYOptions); 
 | 
         
 | 
    } 
 | 
     
 | 
    // Run prototype.init 
 | 
    proceed.call(this, chart, userOptions); 
 | 
     
 | 
    if (!isHidden && (angular || polar)) { 
 | 
        options = this.options; 
 | 
         
 | 
        // Create the pane and set the pane options. 
 | 
        if (!chart.panes) { 
 | 
            chart.panes = []; 
 | 
        } 
 | 
        this.pane = pane = chart.panes[paneIndex] = chart.panes[paneIndex] || new Pane( 
 | 
            splat(chartOptions.pane)[paneIndex], 
 | 
            chart, 
 | 
            axis 
 | 
        ); 
 | 
        paneOptions = pane.options; 
 | 
         
 | 
             
 | 
        // Disable certain features on angular and polar axes 
 | 
        chart.inverted = false; 
 | 
        chartOptions.chart.zoomType = null; 
 | 
         
 | 
        // Start and end angle options are 
 | 
        // given in degrees relative to top, while internal computations are 
 | 
        // in radians relative to right (like SVG). 
 | 
        this.startAngleRad = startAngleRad = (paneOptions.startAngle - 90) * Math.PI / 180; 
 | 
        this.endAngleRad = endAngleRad = (pick(paneOptions.endAngle, paneOptions.startAngle + 360)  - 90) * Math.PI / 180; 
 | 
        this.offset = options.offset || 0; 
 | 
         
 | 
        this.isCircular = isCircular; 
 | 
         
 | 
        // Automatically connect grid lines? 
 | 
        if (isCircular && userOptions.max === UNDEFINED && endAngleRad - startAngleRad === 2 * Math.PI) { 
 | 
            this.autoConnect = true; 
 | 
        } 
 | 
    } 
 | 
     
 | 
}); 
 | 
  
 | 
/** 
 | 
 * Add special cases within the Tick class' methods for radial axes. 
 | 
 */     
 | 
wrap(tickProto, 'getPosition', function (proceed, horiz, pos, tickmarkOffset, old) { 
 | 
    var axis = this.axis; 
 | 
     
 | 
    return axis.getPosition ?  
 | 
        axis.getPosition(pos) : 
 | 
        proceed.call(this, horiz, pos, tickmarkOffset, old);     
 | 
}); 
 | 
  
 | 
/** 
 | 
 * Wrap the getLabelPosition function to find the center position of the label 
 | 
 * based on the distance option 
 | 
 */     
 | 
wrap(tickProto, 'getLabelPosition', function (proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index, step) { 
 | 
    var axis = this.axis, 
 | 
        optionsY = labelOptions.y, 
 | 
        ret, 
 | 
        align = labelOptions.align, 
 | 
        angle = ((axis.translate(this.pos) + axis.startAngleRad + Math.PI / 2) / Math.PI * 180) % 360; 
 | 
     
 | 
    if (axis.isRadial) { 
 | 
        ret = axis.getPosition(this.pos, (axis.center[2] / 2) + pick(labelOptions.distance, -25)); 
 | 
         
 | 
        // Automatically rotated 
 | 
        if (labelOptions.rotation === 'auto') { 
 | 
            label.attr({  
 | 
                rotation: angle 
 | 
            }); 
 | 
         
 | 
        // Vertically centered 
 | 
        } else if (optionsY === null) { 
 | 
            optionsY = pInt(label.styles.lineHeight) * 0.9 - label.getBBox().height / 2; 
 | 
         
 | 
        } 
 | 
         
 | 
        // Automatic alignment 
 | 
        if (align === null) { 
 | 
            if (axis.isCircular) { 
 | 
                if (angle > 20 && angle < 160) { 
 | 
                    align = 'left'; // right hemisphere 
 | 
                } else if (angle > 200 && angle < 340) { 
 | 
                    align = 'right'; // left hemisphere 
 | 
                } else { 
 | 
                    align = 'center'; // top or bottom 
 | 
                } 
 | 
            } else { 
 | 
                align = 'center'; 
 | 
            } 
 | 
            label.attr({ 
 | 
                align: align 
 | 
            }); 
 | 
        } 
 | 
         
 | 
        ret.x += labelOptions.x; 
 | 
        ret.y += optionsY; 
 | 
         
 | 
    } else { 
 | 
        ret = proceed.call(this, x, y, label, horiz, labelOptions, tickmarkOffset, index, step); 
 | 
    } 
 | 
    return ret; 
 | 
}); 
 | 
  
 | 
/** 
 | 
 * Wrap the getMarkPath function to return the path of the radial marker 
 | 
 */ 
 | 
wrap(tickProto, 'getMarkPath', function (proceed, x, y, tickLength, tickWidth, horiz, renderer) { 
 | 
    var axis = this.axis, 
 | 
        endPoint, 
 | 
        ret; 
 | 
         
 | 
    if (axis.isRadial) { 
 | 
        endPoint = axis.getPosition(this.pos, axis.center[2] / 2 + tickLength); 
 | 
        ret = [ 
 | 
            'M', 
 | 
            x, 
 | 
            y, 
 | 
            'L', 
 | 
            endPoint.x, 
 | 
            endPoint.y 
 | 
        ]; 
 | 
    } else { 
 | 
        ret = proceed.call(this, x, y, tickLength, tickWidth, horiz, renderer); 
 | 
    } 
 | 
    return ret; 
 | 
});/*  
 | 
 * The AreaRangeSeries class 
 | 
 *  
 | 
 */ 
 | 
  
 | 
/** 
 | 
 * Extend the default options with map options 
 | 
 */ 
 | 
defaultPlotOptions.arearange = merge(defaultPlotOptions.area, { 
 | 
    lineWidth: 1, 
 | 
    marker: null, 
 | 
    threshold: null, 
 | 
    tooltip: { 
 | 
        pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.low}</b> - <b>{point.high}</b><br/>'  
 | 
    }, 
 | 
    trackByArea: true, 
 | 
    dataLabels: { 
 | 
        verticalAlign: null, 
 | 
        xLow: 0, 
 | 
        xHigh: 0, 
 | 
        yLow: 0, 
 | 
        yHigh: 0     
 | 
    } 
 | 
}); 
 | 
  
 | 
/** 
 | 
 * Add the series type 
 | 
 */ 
 | 
seriesTypes.arearange = Highcharts.extendClass(seriesTypes.area, { 
 | 
    type: 'arearange', 
 | 
    pointArrayMap: ['low', 'high'], 
 | 
    toYData: function (point) { 
 | 
        return [point.low, point.high]; 
 | 
    }, 
 | 
    pointValKey: 'low', 
 | 
     
 | 
    /** 
 | 
     * Extend getSegments to force null points if the higher value is null. #1703. 
 | 
     */ 
 | 
    getSegments: function () { 
 | 
        var series = this; 
 | 
  
 | 
        each(series.points, function (point) { 
 | 
            if (!series.options.connectNulls && (point.low === null || point.high === null)) { 
 | 
                point.y = null; 
 | 
            } else if (point.low === null && point.high !== null) { 
 | 
                point.y = point.high; 
 | 
            } 
 | 
        }); 
 | 
        Series.prototype.getSegments.call(this); 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Translate data points from raw values x and y to plotX and plotY 
 | 
     */ 
 | 
    translate: function () { 
 | 
        var series = this, 
 | 
            yAxis = series.yAxis; 
 | 
  
 | 
        seriesTypes.area.prototype.translate.apply(series); 
 | 
  
 | 
        // Set plotLow and plotHigh 
 | 
        each(series.points, function (point) { 
 | 
  
 | 
            var low = point.low, 
 | 
                high = point.high, 
 | 
                plotY = point.plotY; 
 | 
  
 | 
            if (high === null && low === null) { 
 | 
                point.y = null; 
 | 
            } else if (low === null) { 
 | 
                point.plotLow = point.plotY = null; 
 | 
                point.plotHigh = yAxis.translate(high, 0, 1, 0, 1); 
 | 
            } else if (high === null) { 
 | 
                point.plotLow = plotY; 
 | 
                point.plotHigh = null; 
 | 
            } else { 
 | 
                point.plotLow = plotY; 
 | 
                point.plotHigh = yAxis.translate(high, 0, 1, 0, 1); 
 | 
            } 
 | 
        }); 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Extend the line series' getSegmentPath method by applying the segment 
 | 
     * path to both lower and higher values of the range 
 | 
     */ 
 | 
    getSegmentPath: function (segment) { 
 | 
         
 | 
        var lowSegment, 
 | 
            highSegment = [], 
 | 
            i = segment.length, 
 | 
            baseGetSegmentPath = Series.prototype.getSegmentPath, 
 | 
            point, 
 | 
            linePath, 
 | 
            lowerPath, 
 | 
            options = this.options, 
 | 
            step = options.step, 
 | 
            higherPath; 
 | 
             
 | 
        // Remove nulls from low segment 
 | 
        lowSegment = HighchartsAdapter.grep(segment, function (point) { 
 | 
            return point.plotLow !== null; 
 | 
        }); 
 | 
         
 | 
        // Make a segment with plotX and plotY for the top values 
 | 
        while (i--) { 
 | 
            point = segment[i]; 
 | 
            if (point.plotHigh !== null) { 
 | 
                highSegment.push({ 
 | 
                    plotX: point.plotX, 
 | 
                    plotY: point.plotHigh 
 | 
                }); 
 | 
            } 
 | 
        } 
 | 
         
 | 
        // Get the paths 
 | 
        lowerPath = baseGetSegmentPath.call(this, lowSegment); 
 | 
        if (step) { 
 | 
            if (step === true) { 
 | 
                step = 'left'; 
 | 
            } 
 | 
            options.step = { left: 'right', center: 'center', right: 'left' }[step]; // swap for reading in getSegmentPath 
 | 
        } 
 | 
        higherPath = baseGetSegmentPath.call(this, highSegment); 
 | 
        options.step = step; 
 | 
         
 | 
        // Create a line on both top and bottom of the range 
 | 
        linePath = [].concat(lowerPath, higherPath); 
 | 
         
 | 
        // For the area path, we need to change the 'move' statement into 'lineTo' or 'curveTo' 
 | 
        higherPath[0] = 'L'; // this probably doesn't work for spline             
 | 
        this.areaPath = this.areaPath.concat(lowerPath, higherPath); 
 | 
         
 | 
        return linePath; 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Extend the basic drawDataLabels method by running it for both lower and higher 
 | 
     * values. 
 | 
     */ 
 | 
    drawDataLabels: function () { 
 | 
         
 | 
        var data = this.data, 
 | 
            length = data.length, 
 | 
            i, 
 | 
            originalDataLabels = [], 
 | 
            seriesProto = Series.prototype, 
 | 
            dataLabelOptions = this.options.dataLabels, 
 | 
            point, 
 | 
            inverted = this.chart.inverted; 
 | 
             
 | 
        if (dataLabelOptions.enabled || this._hasPointLabels) { 
 | 
             
 | 
            // Step 1: set preliminary values for plotY and dataLabel and draw the upper labels 
 | 
            i = length; 
 | 
            while (i--) { 
 | 
                point = data[i]; 
 | 
                 
 | 
                // Set preliminary values 
 | 
                point.y = point.high; 
 | 
                point.plotY = point.plotHigh; 
 | 
                 
 | 
                // Store original data labels and set preliminary label objects to be picked up  
 | 
                // in the uber method 
 | 
                originalDataLabels[i] = point.dataLabel; 
 | 
                point.dataLabel = point.dataLabelUpper; 
 | 
                 
 | 
                // Set the default offset 
 | 
                point.below = false; 
 | 
                if (inverted) { 
 | 
                    dataLabelOptions.align = 'left'; 
 | 
                    dataLabelOptions.x = dataLabelOptions.xHigh;                                 
 | 
                } else { 
 | 
                    dataLabelOptions.y = dataLabelOptions.yHigh; 
 | 
                } 
 | 
            } 
 | 
            seriesProto.drawDataLabels.apply(this, arguments); // #1209 
 | 
             
 | 
            // Step 2: reorganize and handle data labels for the lower values 
 | 
            i = length; 
 | 
            while (i--) { 
 | 
                point = data[i]; 
 | 
                 
 | 
                // Move the generated labels from step 1, and reassign the original data labels 
 | 
                point.dataLabelUpper = point.dataLabel; 
 | 
                point.dataLabel = originalDataLabels[i]; 
 | 
                 
 | 
                // Reset values 
 | 
                point.y = point.low; 
 | 
                point.plotY = point.plotLow; 
 | 
                 
 | 
                // Set the default offset 
 | 
                point.below = true; 
 | 
                if (inverted) { 
 | 
                    dataLabelOptions.align = 'right'; 
 | 
                    dataLabelOptions.x = dataLabelOptions.xLow; 
 | 
                } else { 
 | 
                    dataLabelOptions.y = dataLabelOptions.yLow; 
 | 
                } 
 | 
            } 
 | 
            seriesProto.drawDataLabels.apply(this, arguments); 
 | 
        } 
 | 
     
 | 
    }, 
 | 
     
 | 
    alignDataLabel: seriesTypes.column.prototype.alignDataLabel, 
 | 
     
 | 
    getSymbol: seriesTypes.column.prototype.getSymbol, 
 | 
     
 | 
    drawPoints: noop 
 | 
});/** 
 | 
 * The AreaSplineRangeSeries class 
 | 
 */ 
 | 
  
 | 
defaultPlotOptions.areasplinerange = merge(defaultPlotOptions.arearange); 
 | 
  
 | 
/** 
 | 
 * AreaSplineRangeSeries object 
 | 
 */ 
 | 
seriesTypes.areasplinerange = extendClass(seriesTypes.arearange, { 
 | 
    type: 'areasplinerange', 
 | 
    getPointSpline: seriesTypes.spline.prototype.getPointSpline 
 | 
});/** 
 | 
 * The ColumnRangeSeries class 
 | 
 */ 
 | 
defaultPlotOptions.columnrange = merge(defaultPlotOptions.column, defaultPlotOptions.arearange, { 
 | 
    lineWidth: 1, 
 | 
    pointRange: null 
 | 
}); 
 | 
  
 | 
/** 
 | 
 * ColumnRangeSeries object 
 | 
 */ 
 | 
seriesTypes.columnrange = extendClass(seriesTypes.arearange, { 
 | 
    type: 'columnrange', 
 | 
    /** 
 | 
     * Translate data points from raw values x and y to plotX and plotY 
 | 
     */ 
 | 
    translate: function () { 
 | 
        var series = this, 
 | 
            yAxis = series.yAxis, 
 | 
            plotHigh; 
 | 
  
 | 
        colProto.translate.apply(series); 
 | 
  
 | 
        // Set plotLow and plotHigh 
 | 
        each(series.points, function (point) { 
 | 
            var shapeArgs = point.shapeArgs, 
 | 
                minPointLength = series.options.minPointLength, 
 | 
                heightDifference, 
 | 
                height, 
 | 
                y; 
 | 
  
 | 
            point.plotHigh = plotHigh = yAxis.translate(point.high, 0, 1, 0, 1); 
 | 
            point.plotLow = point.plotY; 
 | 
  
 | 
            // adjust shape 
 | 
            y = plotHigh; 
 | 
            height = point.plotY - plotHigh; 
 | 
  
 | 
            if (height < minPointLength) { 
 | 
                heightDifference = (minPointLength - height); 
 | 
                height += heightDifference; 
 | 
                y -= heightDifference / 2; 
 | 
            } 
 | 
            shapeArgs.height = height; 
 | 
            shapeArgs.y = y; 
 | 
        }); 
 | 
    }, 
 | 
    trackerGroups: ['group', 'dataLabels'], 
 | 
    drawGraph: noop, 
 | 
    pointAttrToOptions: colProto.pointAttrToOptions, 
 | 
    drawPoints: colProto.drawPoints, 
 | 
    drawTracker: colProto.drawTracker, 
 | 
    animate: colProto.animate, 
 | 
    getColumnMetrics: colProto.getColumnMetrics 
 | 
}); 
 | 
/*  
 | 
 * The GaugeSeries class 
 | 
 */ 
 | 
  
 | 
  
 | 
  
 | 
/** 
 | 
 * Extend the default options 
 | 
 */ 
 | 
defaultPlotOptions.gauge = merge(defaultPlotOptions.line, { 
 | 
    dataLabels: { 
 | 
        enabled: true, 
 | 
        y: 15, 
 | 
        borderWidth: 1, 
 | 
        borderColor: 'silver', 
 | 
        borderRadius: 3, 
 | 
        style: { 
 | 
            fontWeight: 'bold' 
 | 
        }, 
 | 
        verticalAlign: 'top', 
 | 
        zIndex: 2 
 | 
    }, 
 | 
    dial: { 
 | 
        // radius: '80%', 
 | 
        // backgroundColor: 'black', 
 | 
        // borderColor: 'silver', 
 | 
        // borderWidth: 0, 
 | 
        // baseWidth: 3, 
 | 
        // topWidth: 1, 
 | 
        // baseLength: '70%' // of radius 
 | 
        // rearLength: '10%' 
 | 
    }, 
 | 
    pivot: { 
 | 
        //radius: 5, 
 | 
        //borderWidth: 0 
 | 
        //borderColor: 'silver', 
 | 
        //backgroundColor: 'black' 
 | 
    }, 
 | 
    tooltip: { 
 | 
        headerFormat: '' 
 | 
    }, 
 | 
    showInLegend: false 
 | 
}); 
 | 
  
 | 
/** 
 | 
 * Extend the point object 
 | 
 */ 
 | 
var GaugePoint = Highcharts.extendClass(Highcharts.Point, { 
 | 
    /** 
 | 
     * Don't do any hover colors or anything 
 | 
     */ 
 | 
    setState: function (state) { 
 | 
        this.state = state; 
 | 
    } 
 | 
}); 
 | 
  
 | 
  
 | 
/** 
 | 
 * Add the series type 
 | 
 */ 
 | 
var GaugeSeries = { 
 | 
    type: 'gauge', 
 | 
    pointClass: GaugePoint, 
 | 
     
 | 
    // chart.angular will be set to true when a gauge series is present, and this will 
 | 
    // be used on the axes 
 | 
    angular: true,  
 | 
    drawGraph: noop, 
 | 
    fixedBox: true, 
 | 
    trackerGroups: ['group', 'dataLabels'], 
 | 
     
 | 
    /** 
 | 
     * Calculate paths etc 
 | 
     */ 
 | 
    translate: function () { 
 | 
         
 | 
        var series = this, 
 | 
            yAxis = series.yAxis, 
 | 
            options = series.options, 
 | 
            center = yAxis.center; 
 | 
             
 | 
        series.generatePoints(); 
 | 
         
 | 
        each(series.points, function (point) { 
 | 
             
 | 
            var dialOptions = merge(options.dial, point.dial), 
 | 
                radius = (pInt(pick(dialOptions.radius, 80)) * center[2]) / 200, 
 | 
                baseLength = (pInt(pick(dialOptions.baseLength, 70)) * radius) / 100, 
 | 
                rearLength = (pInt(pick(dialOptions.rearLength, 10)) * radius) / 100, 
 | 
                baseWidth = dialOptions.baseWidth || 3, 
 | 
                topWidth = dialOptions.topWidth || 1, 
 | 
                rotation = yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true); 
 | 
  
 | 
            // Handle the wrap option 
 | 
            if (options.wrap === false) { 
 | 
                rotation = Math.max(yAxis.startAngleRad, Math.min(yAxis.endAngleRad, rotation)); 
 | 
            } 
 | 
            rotation = rotation * 180 / Math.PI; 
 | 
                 
 | 
            point.shapeType = 'path'; 
 | 
            point.shapeArgs = { 
 | 
                d: dialOptions.path || [ 
 | 
                    'M',  
 | 
                    -rearLength, -baseWidth / 2,  
 | 
                    'L',  
 | 
                    baseLength, -baseWidth / 2, 
 | 
                    radius, -topWidth / 2, 
 | 
                    radius, topWidth / 2, 
 | 
                    baseLength, baseWidth / 2, 
 | 
                    -rearLength, baseWidth / 2, 
 | 
                    'z' 
 | 
                ], 
 | 
                translateX: center[0], 
 | 
                translateY: center[1], 
 | 
                rotation: rotation 
 | 
            }; 
 | 
             
 | 
            // Positions for data label 
 | 
            point.plotX = center[0]; 
 | 
            point.plotY = center[1]; 
 | 
        }); 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Draw the points where each point is one needle 
 | 
     */ 
 | 
    drawPoints: function () { 
 | 
         
 | 
        var series = this, 
 | 
            center = series.yAxis.center, 
 | 
            pivot = series.pivot, 
 | 
            options = series.options, 
 | 
            pivotOptions = options.pivot, 
 | 
            renderer = series.chart.renderer; 
 | 
         
 | 
        each(series.points, function (point) { 
 | 
             
 | 
            var graphic = point.graphic, 
 | 
                shapeArgs = point.shapeArgs, 
 | 
                d = shapeArgs.d, 
 | 
                dialOptions = merge(options.dial, point.dial); // #1233 
 | 
             
 | 
            if (graphic) { 
 | 
                graphic.animate(shapeArgs); 
 | 
                shapeArgs.d = d; // animate alters it 
 | 
            } else { 
 | 
                point.graphic = renderer[point.shapeType](shapeArgs) 
 | 
                    .attr({ 
 | 
                        stroke: dialOptions.borderColor || 'none', 
 | 
                        'stroke-width': dialOptions.borderWidth || 0, 
 | 
                        fill: dialOptions.backgroundColor || 'black', 
 | 
                        rotation: shapeArgs.rotation // required by VML when animation is false 
 | 
                    }) 
 | 
                    .add(series.group); 
 | 
            } 
 | 
        }); 
 | 
         
 | 
        // Add or move the pivot 
 | 
        if (pivot) { 
 | 
            pivot.animate({ // #1235 
 | 
                translateX: center[0], 
 | 
                translateY: center[1] 
 | 
            }); 
 | 
        } else { 
 | 
            series.pivot = renderer.circle(0, 0, pick(pivotOptions.radius, 5)) 
 | 
                .attr({ 
 | 
                    'stroke-width': pivotOptions.borderWidth || 0, 
 | 
                    stroke: pivotOptions.borderColor || 'silver', 
 | 
                    fill: pivotOptions.backgroundColor || 'black' 
 | 
                }) 
 | 
                .translate(center[0], center[1]) 
 | 
                .add(series.group); 
 | 
        } 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Animate the arrow up from startAngle 
 | 
     */ 
 | 
    animate: function (init) { 
 | 
        var series = this; 
 | 
  
 | 
        if (!init) { 
 | 
            each(series.points, function (point) { 
 | 
                var graphic = point.graphic; 
 | 
  
 | 
                if (graphic) { 
 | 
                    // start value 
 | 
                    graphic.attr({ 
 | 
                        rotation: series.yAxis.startAngleRad * 180 / Math.PI 
 | 
                    }); 
 | 
  
 | 
                    // animate 
 | 
                    graphic.animate({ 
 | 
                        rotation: point.shapeArgs.rotation 
 | 
                    }, series.options.animation); 
 | 
                } 
 | 
            }); 
 | 
  
 | 
            // delete this function to allow it only once 
 | 
            series.animate = null; 
 | 
        } 
 | 
    }, 
 | 
     
 | 
    render: function () { 
 | 
        this.group = this.plotGroup( 
 | 
            'group',  
 | 
            'series',  
 | 
            this.visible ? 'visible' : 'hidden',  
 | 
            this.options.zIndex,  
 | 
            this.chart.seriesGroup 
 | 
        ); 
 | 
        seriesTypes.pie.prototype.render.call(this); 
 | 
        this.group.clip(this.chart.clipRect); 
 | 
    }, 
 | 
     
 | 
    setData: seriesTypes.pie.prototype.setData, 
 | 
    drawTracker: seriesTypes.column.prototype.drawTracker 
 | 
}; 
 | 
seriesTypes.gauge = Highcharts.extendClass(seriesTypes.line, GaugeSeries);/* **************************************************************************** 
 | 
 * Start Box plot series code                                                  * 
 | 
 *****************************************************************************/ 
 | 
  
 | 
// Set default options 
 | 
defaultPlotOptions.boxplot = merge(defaultPlotOptions.column, { 
 | 
    fillColor: '#FFFFFF', 
 | 
    lineWidth: 1, 
 | 
    //medianColor: null, 
 | 
    medianWidth: 2, 
 | 
    states: { 
 | 
        hover: { 
 | 
            brightness: -0.3 
 | 
        } 
 | 
    }, 
 | 
    //stemColor: null, 
 | 
    //stemDashStyle: 'solid' 
 | 
    //stemWidth: null, 
 | 
    threshold: null, 
 | 
    tooltip: { 
 | 
        pointFormat: '<span style="color:{series.color};font-weight:bold">{series.name}</span><br/>' + 
 | 
            'Maximum: {point.high}<br/>' + 
 | 
            'Upper quartile: {point.q3}<br/>' + 
 | 
            'Median: {point.median}<br/>' + 
 | 
            'Lower quartile: {point.q1}<br/>' + 
 | 
            'Minimum: {point.low}<br/>' 
 | 
             
 | 
    }, 
 | 
    //whiskerColor: null, 
 | 
    whiskerLength: '50%', 
 | 
    whiskerWidth: 2 
 | 
}); 
 | 
  
 | 
// Create the series object 
 | 
seriesTypes.boxplot = extendClass(seriesTypes.column, { 
 | 
    type: 'boxplot', 
 | 
    pointArrayMap: ['low', 'q1', 'median', 'q3', 'high'], // array point configs are mapped to this 
 | 
    toYData: function (point) { // return a plain array for speedy calculation 
 | 
        return [point.low, point.q1, point.median, point.q3, point.high]; 
 | 
    }, 
 | 
    pointValKey: 'high', // defines the top of the tracker 
 | 
     
 | 
    /** 
 | 
     * One-to-one mapping from options to SVG attributes 
 | 
     */ 
 | 
    pointAttrToOptions: { // mapping between SVG attributes and the corresponding options 
 | 
        fill: 'fillColor', 
 | 
        stroke: 'color', 
 | 
        'stroke-width': 'lineWidth' 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Disable data labels for box plot 
 | 
     */ 
 | 
    drawDataLabels: noop, 
 | 
  
 | 
    /** 
 | 
     * Translate data points from raw values x and y to plotX and plotY 
 | 
     */ 
 | 
    translate: function () { 
 | 
        var series = this, 
 | 
            yAxis = series.yAxis, 
 | 
            pointArrayMap = series.pointArrayMap; 
 | 
  
 | 
        seriesTypes.column.prototype.translate.apply(series); 
 | 
  
 | 
        // do the translation on each point dimension 
 | 
        each(series.points, function (point) { 
 | 
            each(pointArrayMap, function (key) { 
 | 
                if (point[key] !== null) { 
 | 
                    point[key + 'Plot'] = yAxis.translate(point[key], 0, 1, 0, 1); 
 | 
                } 
 | 
            }); 
 | 
        }); 
 | 
    }, 
 | 
  
 | 
    /** 
 | 
     * Draw the data points 
 | 
     */ 
 | 
    drawPoints: function () { 
 | 
        var series = this,  //state = series.state, 
 | 
            points = series.points, 
 | 
            options = series.options, 
 | 
            chart = series.chart, 
 | 
            renderer = chart.renderer, 
 | 
            pointAttr, 
 | 
            q1Plot, 
 | 
            q3Plot, 
 | 
            highPlot, 
 | 
            lowPlot, 
 | 
            medianPlot, 
 | 
            crispCorr, 
 | 
            crispX, 
 | 
            graphic, 
 | 
            stemPath, 
 | 
            stemAttr, 
 | 
            boxPath, 
 | 
            whiskersPath, 
 | 
            whiskersAttr, 
 | 
            medianPath, 
 | 
            medianAttr, 
 | 
            width, 
 | 
            left, 
 | 
            right, 
 | 
            halfWidth, 
 | 
            shapeArgs, 
 | 
            color, 
 | 
            doQuartiles = series.doQuartiles !== false, // error bar inherits this series type but doesn't do quartiles 
 | 
            whiskerLength = parseInt(series.options.whiskerLength, 10) / 100; 
 | 
  
 | 
  
 | 
        each(points, function (point) { 
 | 
  
 | 
            graphic = point.graphic; 
 | 
            shapeArgs = point.shapeArgs; // the box 
 | 
            stemAttr = {}; 
 | 
            whiskersAttr = {}; 
 | 
            medianAttr = {}; 
 | 
            color = point.color || series.color; 
 | 
             
 | 
            if (point.plotY !== UNDEFINED) { 
 | 
  
 | 
                pointAttr = point.pointAttr[point.selected ? 'selected' : '']; 
 | 
  
 | 
                // crisp vector coordinates 
 | 
                width = shapeArgs.width; 
 | 
                left = mathFloor(shapeArgs.x); 
 | 
                right = left + width; 
 | 
                halfWidth = mathRound(width / 2); 
 | 
                //crispX = mathRound(left + halfWidth) + crispCorr; 
 | 
                q1Plot = mathFloor(doQuartiles ? point.q1Plot : point.lowPlot);// + crispCorr; 
 | 
                q3Plot = mathFloor(doQuartiles ? point.q3Plot : point.lowPlot);// + crispCorr; 
 | 
                highPlot = mathFloor(point.highPlot);// + crispCorr; 
 | 
                lowPlot = mathFloor(point.lowPlot);// + crispCorr; 
 | 
                 
 | 
                // Stem attributes 
 | 
                stemAttr.stroke = point.stemColor || options.stemColor || color; 
 | 
                stemAttr['stroke-width'] = pick(point.stemWidth, options.stemWidth, options.lineWidth); 
 | 
                stemAttr.dashstyle = point.stemDashStyle || options.stemDashStyle; 
 | 
                 
 | 
                // Whiskers attributes 
 | 
                whiskersAttr.stroke = point.whiskerColor || options.whiskerColor || color; 
 | 
                whiskersAttr['stroke-width'] = pick(point.whiskerWidth, options.whiskerWidth, options.lineWidth); 
 | 
                 
 | 
                // Median attributes 
 | 
                medianAttr.stroke = point.medianColor || options.medianColor || color; 
 | 
                medianAttr['stroke-width'] = pick(point.medianWidth, options.medianWidth, options.lineWidth); 
 | 
                 
 | 
                 
 | 
                // The stem 
 | 
                crispCorr = (stemAttr['stroke-width'] % 2) / 2; 
 | 
                crispX = left + halfWidth + crispCorr;                 
 | 
                stemPath = [ 
 | 
                    // stem up 
 | 
                    'M', 
 | 
                    crispX, q3Plot, 
 | 
                    'L', 
 | 
                    crispX, highPlot, 
 | 
                     
 | 
                    // stem down 
 | 
                    'M', 
 | 
                    crispX, q1Plot, 
 | 
                    'L', 
 | 
                    crispX, lowPlot, 
 | 
                    'z' 
 | 
                ]; 
 | 
                 
 | 
                // The box 
 | 
                if (doQuartiles) { 
 | 
                    crispCorr = (pointAttr['stroke-width'] % 2) / 2; 
 | 
                    crispX = mathFloor(crispX) + crispCorr; 
 | 
                    q1Plot = mathFloor(q1Plot) + crispCorr; 
 | 
                    q3Plot = mathFloor(q3Plot) + crispCorr; 
 | 
                    left += crispCorr; 
 | 
                    right += crispCorr; 
 | 
                    boxPath = [ 
 | 
                        'M', 
 | 
                        left, q3Plot, 
 | 
                        'L', 
 | 
                        left, q1Plot, 
 | 
                        'L', 
 | 
                        right, q1Plot, 
 | 
                        'L', 
 | 
                        right, q3Plot, 
 | 
                        'L', 
 | 
                        left, q3Plot, 
 | 
                        'z' 
 | 
                    ]; 
 | 
                } 
 | 
                 
 | 
                // The whiskers 
 | 
                if (whiskerLength) { 
 | 
                    crispCorr = (whiskersAttr['stroke-width'] % 2) / 2; 
 | 
                    highPlot = highPlot + crispCorr; 
 | 
                    lowPlot = lowPlot + crispCorr; 
 | 
                    whiskersPath = [ 
 | 
                        // High whisker 
 | 
                        'M', 
 | 
                        crispX - halfWidth * whiskerLength,  
 | 
                        highPlot, 
 | 
                        'L', 
 | 
                        crispX + halfWidth * whiskerLength,  
 | 
                        highPlot, 
 | 
                         
 | 
                        // Low whisker 
 | 
                        'M', 
 | 
                        crispX - halfWidth * whiskerLength,  
 | 
                        lowPlot, 
 | 
                        'L', 
 | 
                        crispX + halfWidth * whiskerLength,  
 | 
                        lowPlot 
 | 
                    ]; 
 | 
                } 
 | 
                 
 | 
                // The median 
 | 
                crispCorr = (medianAttr['stroke-width'] % 2) / 2;                 
 | 
                medianPlot = mathRound(point.medianPlot) + crispCorr; 
 | 
                medianPath = [ 
 | 
                    'M', 
 | 
                    left,  
 | 
                    medianPlot, 
 | 
                    'L', 
 | 
                    right,  
 | 
                    medianPlot, 
 | 
                    'z' 
 | 
                ]; 
 | 
                 
 | 
                // Create or update the graphics 
 | 
                if (graphic) { // update 
 | 
                     
 | 
                    point.stem.animate({ d: stemPath }); 
 | 
                    if (whiskerLength) { 
 | 
                        point.whiskers.animate({ d: whiskersPath }); 
 | 
                    } 
 | 
                    if (doQuartiles) { 
 | 
                        point.box.animate({ d: boxPath }); 
 | 
                    } 
 | 
                    point.medianShape.animate({ d: medianPath }); 
 | 
                     
 | 
                } else { // create new 
 | 
                    point.graphic = graphic = renderer.g() 
 | 
                        .add(series.group); 
 | 
                     
 | 
                    point.stem = renderer.path(stemPath) 
 | 
                        .attr(stemAttr) 
 | 
                        .add(graphic); 
 | 
                         
 | 
                    if (whiskerLength) { 
 | 
                        point.whiskers = renderer.path(whiskersPath)  
 | 
                            .attr(whiskersAttr) 
 | 
                            .add(graphic); 
 | 
                    } 
 | 
                    if (doQuartiles) { 
 | 
                        point.box = renderer.path(boxPath) 
 | 
                            .attr(pointAttr) 
 | 
                            .add(graphic); 
 | 
                    }     
 | 
                    point.medianShape = renderer.path(medianPath) 
 | 
                        .attr(medianAttr) 
 | 
                        .add(graphic); 
 | 
                } 
 | 
            } 
 | 
        }); 
 | 
  
 | 
    } 
 | 
  
 | 
  
 | 
}); 
 | 
  
 | 
/* **************************************************************************** 
 | 
 * End Box plot series code                                                * 
 | 
 *****************************************************************************/ 
 | 
/* **************************************************************************** 
 | 
 * Start error bar series code                                                * 
 | 
 *****************************************************************************/ 
 | 
  
 | 
// 1 - set default options 
 | 
defaultPlotOptions.errorbar = merge(defaultPlotOptions.boxplot, { 
 | 
    color: '#000000', 
 | 
    grouping: false, 
 | 
    linkedTo: ':previous', 
 | 
    tooltip: { 
 | 
        pointFormat: defaultPlotOptions.arearange.tooltip.pointFormat 
 | 
    }, 
 | 
    whiskerWidth: null 
 | 
}); 
 | 
  
 | 
// 2 - Create the series object 
 | 
seriesTypes.errorbar = extendClass(seriesTypes.boxplot, { 
 | 
    type: 'errorbar', 
 | 
    pointArrayMap: ['low', 'high'], // array point configs are mapped to this 
 | 
    toYData: function (point) { // return a plain array for speedy calculation 
 | 
        return [point.low, point.high]; 
 | 
    }, 
 | 
    pointValKey: 'high', // defines the top of the tracker 
 | 
    doQuartiles: false, 
 | 
  
 | 
    /** 
 | 
     * Get the width and X offset, either on top of the linked series column 
 | 
     * or standalone 
 | 
     */ 
 | 
    getColumnMetrics: function () { 
 | 
        return (this.linkedParent && this.linkedParent.columnMetrics) ||  
 | 
            seriesTypes.column.prototype.getColumnMetrics.call(this); 
 | 
    } 
 | 
}); 
 | 
  
 | 
/* **************************************************************************** 
 | 
 * End error bar series code                                                  * 
 | 
 *****************************************************************************/ 
 | 
/* **************************************************************************** 
 | 
 * Start Waterfall series code                                                * 
 | 
 *****************************************************************************/ 
 | 
  
 | 
// 1 - set default options 
 | 
defaultPlotOptions.waterfall = merge(defaultPlotOptions.column, { 
 | 
    lineWidth: 1, 
 | 
    lineColor: '#333', 
 | 
    dashStyle: 'dot', 
 | 
    borderColor: '#333' 
 | 
}); 
 | 
  
 | 
  
 | 
// 2 - Create the series object 
 | 
seriesTypes.waterfall = extendClass(seriesTypes.column, { 
 | 
    type: 'waterfall', 
 | 
  
 | 
    upColorProp: 'fill', 
 | 
  
 | 
    pointArrayMap: ['low', 'y'], 
 | 
  
 | 
    pointValKey: 'y', 
 | 
  
 | 
    /** 
 | 
     * Init waterfall series, force stacking 
 | 
     */ 
 | 
    init: function (chart, options) { 
 | 
        // force stacking 
 | 
        options.stacking = true; 
 | 
  
 | 
        seriesTypes.column.prototype.init.call(this, chart, options); 
 | 
    }, 
 | 
  
 | 
  
 | 
    /** 
 | 
     * Translate data points from raw values 
 | 
     */ 
 | 
    translate: function () { 
 | 
        var series = this, 
 | 
            options = series.options, 
 | 
            axis = series.yAxis, 
 | 
            len, 
 | 
            i, 
 | 
            points, 
 | 
            point, 
 | 
            shapeArgs, 
 | 
            stack, 
 | 
            y, 
 | 
            previousY, 
 | 
            stackPoint, 
 | 
            threshold = options.threshold, 
 | 
            crispCorr = (options.borderWidth % 2) / 2; 
 | 
  
 | 
        // run column series translate 
 | 
        seriesTypes.column.prototype.translate.apply(this); 
 | 
  
 | 
        previousY = threshold; 
 | 
        points = series.points; 
 | 
  
 | 
        for (i = 0, len = points.length; i < len; i++) { 
 | 
            // cache current point object 
 | 
            point = points[i]; 
 | 
            shapeArgs = point.shapeArgs; 
 | 
  
 | 
            // get current stack 
 | 
            stack = series.getStack(i); 
 | 
            stackPoint = stack.points[series.index]; 
 | 
  
 | 
            // override point value for sums 
 | 
            if (isNaN(point.y)) { 
 | 
                point.y = series.yData[i]; 
 | 
            } 
 | 
  
 | 
            // up points 
 | 
            y = mathMax(previousY, previousY + point.y) + stackPoint[0]; 
 | 
            shapeArgs.y = axis.translate(y, 0, 1); 
 | 
  
 | 
  
 | 
            // sum points 
 | 
            if (point.isSum || point.isIntermediateSum) { 
 | 
                shapeArgs.y = axis.translate(stackPoint[1], 0, 1); 
 | 
                shapeArgs.height = axis.translate(stackPoint[0], 0, 1) - shapeArgs.y; 
 | 
  
 | 
            // if it's not the sum point, update previous stack end position 
 | 
            } else { 
 | 
                previousY += stack.total; 
 | 
            } 
 | 
  
 | 
            // negative points 
 | 
            if (shapeArgs.height < 0) { 
 | 
                shapeArgs.y += shapeArgs.height; 
 | 
                shapeArgs.height *= -1; 
 | 
            } 
 | 
  
 | 
            point.plotY = shapeArgs.y = mathRound(shapeArgs.y) - crispCorr; 
 | 
            shapeArgs.height = mathRound(shapeArgs.height); 
 | 
            point.yBottom = shapeArgs.y + shapeArgs.height; 
 | 
        } 
 | 
    }, 
 | 
  
 | 
    /** 
 | 
     * Call default processData then override yData to reflect waterfall's extremes on yAxis 
 | 
     */ 
 | 
    processData: function (force) { 
 | 
        var series = this, 
 | 
            options = series.options, 
 | 
            yData = series.yData, 
 | 
            points = series.points, 
 | 
            point, 
 | 
            dataLength = yData.length, 
 | 
            threshold = options.threshold || 0, 
 | 
            subSum, 
 | 
            sum, 
 | 
            dataMin, 
 | 
            dataMax, 
 | 
            y, 
 | 
            i; 
 | 
  
 | 
        sum = subSum = dataMin = dataMax = threshold; 
 | 
  
 | 
        for (i = 0; i < dataLength; i++) { 
 | 
            y = yData[i]; 
 | 
            point = points && points[i] ? points[i] : {}; 
 | 
  
 | 
            if (y === "sum" || point.isSum) { 
 | 
                yData[i] = sum; 
 | 
            } else if (y === "intermediateSum" || point.isIntermediateSum) { 
 | 
                yData[i] = subSum; 
 | 
                subSum = threshold; 
 | 
            } else { 
 | 
                sum += y; 
 | 
                subSum += y; 
 | 
            } 
 | 
            dataMin = Math.min(sum, dataMin); 
 | 
            dataMax = Math.max(sum, dataMax); 
 | 
        } 
 | 
  
 | 
        Series.prototype.processData.call(this, force); 
 | 
  
 | 
        // Record extremes 
 | 
        series.dataMin = dataMin; 
 | 
        series.dataMax = dataMax; 
 | 
    }, 
 | 
  
 | 
    /** 
 | 
     * Return y value or string if point is sum 
 | 
     */ 
 | 
    toYData: function (pt) { 
 | 
        if (pt.isSum) { 
 | 
            return "sum"; 
 | 
        } else if (pt.isIntermediateSum) { 
 | 
            return "intermediateSum"; 
 | 
        } 
 | 
  
 | 
        return pt.y; 
 | 
    }, 
 | 
  
 | 
    /** 
 | 
     * Postprocess mapping between options and SVG attributes 
 | 
     */ 
 | 
    getAttribs: function () { 
 | 
        seriesTypes.column.prototype.getAttribs.apply(this, arguments); 
 | 
  
 | 
        var series = this, 
 | 
            options = series.options, 
 | 
            stateOptions = options.states, 
 | 
            upColor = options.upColor || series.color, 
 | 
            hoverColor = Highcharts.Color(upColor).brighten(0.1).get(), 
 | 
            seriesDownPointAttr = merge(series.pointAttr), 
 | 
            upColorProp = series.upColorProp; 
 | 
  
 | 
        seriesDownPointAttr[''][upColorProp] = upColor; 
 | 
        seriesDownPointAttr.hover[upColorProp] = stateOptions.hover.upColor || hoverColor; 
 | 
        seriesDownPointAttr.select[upColorProp] = stateOptions.select.upColor || upColor; 
 | 
  
 | 
        each(series.points, function (point) { 
 | 
            if (point.y > 0 && !point.color) { 
 | 
                point.pointAttr = seriesDownPointAttr; 
 | 
                point.color = upColor; 
 | 
            } 
 | 
        }); 
 | 
    }, 
 | 
  
 | 
    /** 
 | 
     * Draw columns' connector lines 
 | 
     */ 
 | 
    getGraphPath: function () { 
 | 
  
 | 
        var data = this.data, 
 | 
            length = data.length, 
 | 
            lineWidth = this.options.lineWidth + this.options.borderWidth, 
 | 
            normalizer = mathRound(lineWidth) % 2 / 2, 
 | 
            path = [], 
 | 
            M = 'M', 
 | 
            L = 'L', 
 | 
            prevArgs, 
 | 
            pointArgs, 
 | 
            i, 
 | 
            d; 
 | 
  
 | 
        for (i = 1; i < length; i++) { 
 | 
            pointArgs = data[i].shapeArgs; 
 | 
            prevArgs = data[i - 1].shapeArgs; 
 | 
  
 | 
            d = [ 
 | 
                M, 
 | 
                prevArgs.x + prevArgs.width, prevArgs.y + normalizer, 
 | 
                L, 
 | 
                pointArgs.x, prevArgs.y + normalizer 
 | 
            ]; 
 | 
  
 | 
            if (data[i - 1].y < 0) { 
 | 
                d[2] += prevArgs.height; 
 | 
                d[5] += prevArgs.height; 
 | 
            } 
 | 
  
 | 
            path = path.concat(d); 
 | 
        } 
 | 
  
 | 
        return path; 
 | 
    }, 
 | 
  
 | 
    /** 
 | 
     * Extremes are recorded in processData 
 | 
     */ 
 | 
    getExtremes: noop, 
 | 
  
 | 
    /** 
 | 
     * Return stack for given index 
 | 
     */ 
 | 
    getStack: function (i) { 
 | 
        var axis = this.yAxis, 
 | 
            stacks = axis.stacks, 
 | 
            key = this.stackKey; 
 | 
  
 | 
        if (this.processedYData[i] < this.options.threshold) { 
 | 
            key = '-' + key; 
 | 
        } 
 | 
  
 | 
        return stacks[key][i]; 
 | 
    }, 
 | 
  
 | 
    drawGraph: Series.prototype.drawGraph 
 | 
}); 
 | 
  
 | 
/* **************************************************************************** 
 | 
 * End Waterfall series code                                                  * 
 | 
 *****************************************************************************/ 
 | 
/* **************************************************************************** 
 | 
 * Start Bubble series code                                                      * 
 | 
 *****************************************************************************/ 
 | 
  
 | 
// 1 - set default options 
 | 
defaultPlotOptions.bubble = merge(defaultPlotOptions.scatter, { 
 | 
    dataLabels: { 
 | 
        inside: true, 
 | 
        style: { 
 | 
            color: 'white', 
 | 
            textShadow: '0px 0px 3px black' 
 | 
        }, 
 | 
        verticalAlign: 'middle' 
 | 
    }, 
 | 
    // displayNegative: true, 
 | 
    marker: { 
 | 
        // fillOpacity: 0.5, 
 | 
        lineColor: null, // inherit from series.color 
 | 
        lineWidth: 1 
 | 
    }, 
 | 
    minSize: 8, 
 | 
    maxSize: '20%', 
 | 
    // negativeColor: null, 
 | 
    tooltip: { 
 | 
        pointFormat: '({point.x}, {point.y}), Size: {point.z}' 
 | 
    }, 
 | 
    turboThreshold: 0, 
 | 
    zThreshold: 0 
 | 
}); 
 | 
  
 | 
// 2 - Create the series object 
 | 
seriesTypes.bubble = extendClass(seriesTypes.scatter, { 
 | 
    type: 'bubble', 
 | 
    pointArrayMap: ['y', 'z'], 
 | 
    trackerGroups: ['group', 'dataLabelsGroup'], 
 | 
     
 | 
    /** 
 | 
     * Mapping between SVG attributes and the corresponding options 
 | 
     */ 
 | 
    pointAttrToOptions: {  
 | 
        stroke: 'lineColor', 
 | 
        'stroke-width': 'lineWidth', 
 | 
        fill: 'fillColor' 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Apply the fillOpacity to all fill positions 
 | 
     */ 
 | 
    applyOpacity: function (fill) { 
 | 
        var markerOptions = this.options.marker, 
 | 
            fillOpacity = pick(markerOptions.fillOpacity, 0.5); 
 | 
         
 | 
        // When called from Legend.colorizeItem, the fill isn't predefined 
 | 
        fill = fill || markerOptions.fillColor || this.color;  
 | 
         
 | 
        if (fillOpacity !== 1) { 
 | 
            fill = Highcharts.Color(fill).setOpacity(fillOpacity).get('rgba'); 
 | 
        } 
 | 
        return fill; 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Extend the convertAttribs method by applying opacity to the fill 
 | 
     */ 
 | 
    convertAttribs: function () { 
 | 
        var obj = Series.prototype.convertAttribs.apply(this, arguments); 
 | 
         
 | 
        obj.fill = this.applyOpacity(obj.fill); 
 | 
         
 | 
        return obj; 
 | 
    }, 
 | 
  
 | 
    /** 
 | 
     * Get the radius for each point based on the minSize, maxSize and each point's Z value. This 
 | 
     * must be done prior to Series.translate because the axis needs to add padding in  
 | 
     * accordance with the point sizes. 
 | 
     */ 
 | 
    getRadii: function (zMin, zMax, minSize, maxSize) { 
 | 
        var len, 
 | 
            i, 
 | 
            pos, 
 | 
            zData = this.zData, 
 | 
            radii = [], 
 | 
            zRange; 
 | 
         
 | 
        // Set the shape type and arguments to be picked up in drawPoints 
 | 
        for (i = 0, len = zData.length; i < len; i++) { 
 | 
            zRange = zMax - zMin; 
 | 
            pos = zRange > 0 ? // relative size, a number between 0 and 1 
 | 
                (zData[i] - zMin) / (zMax - zMin) :  
 | 
                0.5; 
 | 
            radii.push(math.ceil(minSize + pos * (maxSize - minSize)) / 2); 
 | 
        } 
 | 
        this.radii = radii; 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Perform animation on the bubbles 
 | 
     */ 
 | 
    animate: function (init) { 
 | 
        var animation = this.options.animation; 
 | 
         
 | 
        if (!init) { // run the animation 
 | 
            each(this.points, function (point) { 
 | 
                var graphic = point.graphic, 
 | 
                    shapeArgs = point.shapeArgs; 
 | 
  
 | 
                if (graphic && shapeArgs) { 
 | 
                    // start values 
 | 
                    graphic.attr('r', 1); 
 | 
  
 | 
                    // animate 
 | 
                    graphic.animate({ 
 | 
                        r: shapeArgs.r 
 | 
                    }, animation); 
 | 
                } 
 | 
            }); 
 | 
  
 | 
            // delete this function to allow it only once 
 | 
            this.animate = null; 
 | 
        } 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Extend the base translate method to handle bubble size 
 | 
     */ 
 | 
    translate: function () { 
 | 
         
 | 
        var i, 
 | 
            data = this.data, 
 | 
            point, 
 | 
            radius, 
 | 
            radii = this.radii; 
 | 
         
 | 
        // Run the parent method 
 | 
        seriesTypes.scatter.prototype.translate.call(this); 
 | 
         
 | 
        // Set the shape type and arguments to be picked up in drawPoints 
 | 
        i = data.length; 
 | 
         
 | 
        while (i--) { 
 | 
            point = data[i]; 
 | 
            radius = radii ? radii[i] : 0; // #1737 
 | 
  
 | 
            // Flag for negativeColor to be applied in Series.js 
 | 
            point.negative = point.z < (this.options.zThreshold || 0); 
 | 
             
 | 
            if (radius >= this.minPxSize / 2) { 
 | 
                // Shape arguments 
 | 
                point.shapeType = 'circle'; 
 | 
                point.shapeArgs = { 
 | 
                    x: point.plotX, 
 | 
                    y: point.plotY, 
 | 
                    r: radius 
 | 
                }; 
 | 
                 
 | 
                // Alignment box for the data label 
 | 
                point.dlBox = { 
 | 
                    x: point.plotX - radius, 
 | 
                    y: point.plotY - radius, 
 | 
                    width: 2 * radius, 
 | 
                    height: 2 * radius 
 | 
                }; 
 | 
            } else { // below zThreshold 
 | 
                point.shapeArgs = point.plotY = point.dlBox = UNDEFINED; // #1691 
 | 
            } 
 | 
        } 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Get the series' symbol in the legend 
 | 
     *  
 | 
     * @param {Object} legend The legend object 
 | 
     * @param {Object} item The series (this) or point 
 | 
     */ 
 | 
    drawLegendSymbol: function (legend, item) { 
 | 
        var radius = pInt(legend.itemStyle.fontSize) / 2; 
 | 
         
 | 
        item.legendSymbol = this.chart.renderer.circle( 
 | 
            radius, 
 | 
            legend.baseline - radius, 
 | 
            radius 
 | 
        ).attr({ 
 | 
            zIndex: 3 
 | 
        }).add(item.legendGroup); 
 | 
        item.legendSymbol.isMarker = true;     
 | 
         
 | 
    }, 
 | 
     
 | 
    drawPoints: seriesTypes.column.prototype.drawPoints, 
 | 
    alignDataLabel: seriesTypes.column.prototype.alignDataLabel 
 | 
}); 
 | 
  
 | 
/** 
 | 
 * Add logic to pad each axis with the amount of pixels 
 | 
 * necessary to avoid the bubbles to overflow. 
 | 
 */ 
 | 
Axis.prototype.beforePadding = function () { 
 | 
    var axis = this, 
 | 
        axisLength = this.len, 
 | 
        chart = this.chart, 
 | 
        pxMin = 0,  
 | 
        pxMax = axisLength, 
 | 
        isXAxis = this.isXAxis, 
 | 
        dataKey = isXAxis ? 'xData' : 'yData', 
 | 
        min = this.min, 
 | 
        extremes = {}, 
 | 
        smallestSize = math.min(chart.plotWidth, chart.plotHeight), 
 | 
        zMin = Number.MAX_VALUE, 
 | 
        zMax = -Number.MAX_VALUE, 
 | 
        range = this.max - min, 
 | 
        transA = axisLength / range, 
 | 
        activeSeries = []; 
 | 
  
 | 
    // Handle padding on the second pass, or on redraw 
 | 
    if (this.tickPositions) { 
 | 
        each(this.series, function (series) { 
 | 
  
 | 
            var seriesOptions = series.options, 
 | 
                zData; 
 | 
  
 | 
            if (series.type === 'bubble' && series.visible) { 
 | 
  
 | 
                // Correction for #1673 
 | 
                axis.allowZoomOutside = true; 
 | 
  
 | 
                // Cache it 
 | 
                activeSeries.push(series); 
 | 
  
 | 
                if (isXAxis) { // because X axis is evaluated first 
 | 
                 
 | 
                    // For each series, translate the size extremes to pixel values 
 | 
                    each(['minSize', 'maxSize'], function (prop) { 
 | 
                        var length = seriesOptions[prop], 
 | 
                            isPercent = /%$/.test(length); 
 | 
                         
 | 
                        length = pInt(length); 
 | 
                        extremes[prop] = isPercent ? 
 | 
                            smallestSize * length / 100 : 
 | 
                            length; 
 | 
                         
 | 
                    }); 
 | 
                    series.minPxSize = extremes.minSize; 
 | 
                     
 | 
                    // Find the min and max Z 
 | 
                    zData = series.zData; 
 | 
                    if (zData.length) { // #1735 
 | 
                        zMin = math.min( 
 | 
                            zMin, 
 | 
                            math.max( 
 | 
                                arrayMin(zData),  
 | 
                                seriesOptions.displayNegative === false ? seriesOptions.zThreshold : -Number.MAX_VALUE 
 | 
                            ) 
 | 
                        ); 
 | 
                        zMax = math.max(zMax, arrayMax(zData)); 
 | 
                    } 
 | 
                } 
 | 
            } 
 | 
        }); 
 | 
  
 | 
        each(activeSeries, function (series) { 
 | 
  
 | 
            var data = series[dataKey], 
 | 
                i = data.length, 
 | 
                radius; 
 | 
  
 | 
            if (isXAxis) { 
 | 
                series.getRadii(zMin, zMax, extremes.minSize, extremes.maxSize); 
 | 
            } 
 | 
             
 | 
            if (range > 0) { 
 | 
                while (i--) { 
 | 
                    radius = series.radii[i]; 
 | 
                    pxMin = Math.min(((data[i] - min) * transA) - radius, pxMin); 
 | 
                    pxMax = Math.max(((data[i] - min) * transA) + radius, pxMax); 
 | 
                } 
 | 
            } 
 | 
        }); 
 | 
         
 | 
        if (activeSeries.length && range > 0 && pick(this.options.min, this.userMin) === UNDEFINED && pick(this.options.max, this.userMax) === UNDEFINED) { 
 | 
            pxMax -= axisLength; 
 | 
            transA *= (axisLength + pxMin - pxMax) / axisLength; 
 | 
            this.min += pxMin / transA; 
 | 
            this.max += pxMax / transA; 
 | 
        } 
 | 
    } 
 | 
}; 
 | 
  
 | 
/* **************************************************************************** 
 | 
 * End Bubble series code                                                     * 
 | 
 *****************************************************************************/ 
 | 
/** 
 | 
 * Extensions for polar charts. Additionally, much of the geometry required for polar charts is 
 | 
 * gathered in RadialAxes.js. 
 | 
 *  
 | 
 */ 
 | 
  
 | 
var seriesProto = Series.prototype, 
 | 
    pointerProto = Highcharts.Pointer.prototype; 
 | 
  
 | 
  
 | 
  
 | 
/** 
 | 
 * Translate a point's plotX and plotY from the internal angle and radius measures to  
 | 
 * true plotX, plotY coordinates 
 | 
 */ 
 | 
seriesProto.toXY = function (point) { 
 | 
    var xy, 
 | 
        chart = this.chart, 
 | 
        plotX = point.plotX, 
 | 
        plotY = point.plotY; 
 | 
     
 | 
    // Save rectangular plotX, plotY for later computation 
 | 
    point.rectPlotX = plotX; 
 | 
    point.rectPlotY = plotY; 
 | 
     
 | 
    // Record the angle in degrees for use in tooltip 
 | 
    point.clientX = ((plotX / Math.PI * 180) + this.xAxis.pane.options.startAngle) % 360; 
 | 
     
 | 
    // Find the polar plotX and plotY 
 | 
    xy = this.xAxis.postTranslate(point.plotX, this.yAxis.len - plotY); 
 | 
    point.plotX = point.polarPlotX = xy.x - chart.plotLeft; 
 | 
    point.plotY = point.polarPlotY = xy.y - chart.plotTop; 
 | 
}; 
 | 
  
 | 
/**  
 | 
 * Order the tooltip points to get the mouse capture ranges correct. #1915.  
 | 
 */ 
 | 
seriesProto.orderTooltipPoints = function (points) { 
 | 
    if (this.chart.polar) { 
 | 
        points.sort(function (a, b) { 
 | 
            return a.clientX - b.clientX; 
 | 
        }); 
 | 
  
 | 
        // Wrap mouse tracking around to capture movement on the segment to the left 
 | 
        // of the north point (#1469, #2093). 
 | 
        if (points[0]) { 
 | 
            points[0].wrappedClientX = points[0].clientX + 360; 
 | 
            points.push(points[0]); 
 | 
        } 
 | 
    } 
 | 
}; 
 | 
  
 | 
  
 | 
/** 
 | 
 * Add some special init logic to areas and areasplines 
 | 
 */ 
 | 
function initArea(proceed, chart, options) { 
 | 
    proceed.call(this, chart, options); 
 | 
    if (this.chart.polar) { 
 | 
         
 | 
        /** 
 | 
         * Overridden method to close a segment path. While in a cartesian plane the area  
 | 
         * goes down to the threshold, in the polar chart it goes to the center. 
 | 
         */ 
 | 
        this.closeSegment = function (path) { 
 | 
            var center = this.xAxis.center; 
 | 
            path.push( 
 | 
                'L', 
 | 
                center[0], 
 | 
                center[1] 
 | 
            );             
 | 
        }; 
 | 
         
 | 
        // Instead of complicated logic to draw an area around the inner area in a stack, 
 | 
        // just draw it behind 
 | 
        this.closedStacks = true; 
 | 
    } 
 | 
} 
 | 
wrap(seriesTypes.area.prototype, 'init', initArea); 
 | 
wrap(seriesTypes.areaspline.prototype, 'init', initArea); 
 | 
         
 | 
  
 | 
/** 
 | 
 * Overridden method for calculating a spline from one point to the next 
 | 
 */ 
 | 
wrap(seriesTypes.spline.prototype, 'getPointSpline', function (proceed, segment, point, i) { 
 | 
     
 | 
    var ret, 
 | 
        smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc; 
 | 
        denom = smoothing + 1, 
 | 
        plotX,  
 | 
        plotY, 
 | 
        lastPoint, 
 | 
        nextPoint, 
 | 
        lastX, 
 | 
        lastY, 
 | 
        nextX, 
 | 
        nextY, 
 | 
        leftContX, 
 | 
        leftContY, 
 | 
        rightContX, 
 | 
        rightContY, 
 | 
        distanceLeftControlPoint, 
 | 
        distanceRightControlPoint, 
 | 
        leftContAngle, 
 | 
        rightContAngle, 
 | 
        jointAngle; 
 | 
         
 | 
         
 | 
    if (this.chart.polar) { 
 | 
         
 | 
        plotX = point.plotX; 
 | 
        plotY = point.plotY; 
 | 
        lastPoint = segment[i - 1]; 
 | 
        nextPoint = segment[i + 1]; 
 | 
             
 | 
        // Connect ends 
 | 
        if (this.connectEnds) { 
 | 
            if (!lastPoint) { 
 | 
                lastPoint = segment[segment.length - 2]; // not the last but the second last, because the segment is already connected 
 | 
            } 
 | 
            if (!nextPoint) { 
 | 
                nextPoint = segment[1]; 
 | 
            }     
 | 
        } 
 | 
  
 | 
        // find control points 
 | 
        if (lastPoint && nextPoint) { 
 | 
         
 | 
            lastX = lastPoint.plotX; 
 | 
            lastY = lastPoint.plotY; 
 | 
            nextX = nextPoint.plotX; 
 | 
            nextY = nextPoint.plotY; 
 | 
            leftContX = (smoothing * plotX + lastX) / denom; 
 | 
            leftContY = (smoothing * plotY + lastY) / denom; 
 | 
            rightContX = (smoothing * plotX + nextX) / denom; 
 | 
            rightContY = (smoothing * plotY + nextY) / denom; 
 | 
            distanceLeftControlPoint = Math.sqrt(Math.pow(leftContX - plotX, 2) + Math.pow(leftContY - plotY, 2)); 
 | 
            distanceRightControlPoint = Math.sqrt(Math.pow(rightContX - plotX, 2) + Math.pow(rightContY - plotY, 2)); 
 | 
            leftContAngle = Math.atan2(leftContY - plotY, leftContX - plotX); 
 | 
            rightContAngle = Math.atan2(rightContY - plotY, rightContX - plotX); 
 | 
            jointAngle = (Math.PI / 2) + ((leftContAngle + rightContAngle) / 2); 
 | 
                 
 | 
                 
 | 
            // Ensure the right direction, jointAngle should be in the same quadrant as leftContAngle 
 | 
            if (Math.abs(leftContAngle - jointAngle) > Math.PI / 2) { 
 | 
                jointAngle -= Math.PI; 
 | 
            } 
 | 
             
 | 
            // Find the corrected control points for a spline straight through the point 
 | 
            leftContX = plotX + Math.cos(jointAngle) * distanceLeftControlPoint; 
 | 
            leftContY = plotY + Math.sin(jointAngle) * distanceLeftControlPoint; 
 | 
            rightContX = plotX + Math.cos(Math.PI + jointAngle) * distanceRightControlPoint; 
 | 
            rightContY = plotY + Math.sin(Math.PI + jointAngle) * distanceRightControlPoint; 
 | 
             
 | 
            // Record for drawing in next point 
 | 
            point.rightContX = rightContX; 
 | 
            point.rightContY = rightContY; 
 | 
  
 | 
        } 
 | 
         
 | 
         
 | 
        // moveTo or lineTo 
 | 
        if (!i) { 
 | 
            ret = ['M', plotX, plotY]; 
 | 
        } else { // curve from last point to this 
 | 
            ret = [ 
 | 
                'C', 
 | 
                lastPoint.rightContX || lastPoint.plotX, 
 | 
                lastPoint.rightContY || lastPoint.plotY, 
 | 
                leftContX || plotX, 
 | 
                leftContY || plotY, 
 | 
                plotX, 
 | 
                plotY 
 | 
            ]; 
 | 
            lastPoint.rightContX = lastPoint.rightContY = null; // reset for updating series later 
 | 
        } 
 | 
         
 | 
         
 | 
    } else { 
 | 
        ret = proceed.call(this, segment, point, i); 
 | 
    } 
 | 
    return ret; 
 | 
}); 
 | 
  
 | 
/** 
 | 
 * Extend translate. The plotX and plotY values are computed as if the polar chart were a 
 | 
 * cartesian plane, where plotX denotes the angle in radians and (yAxis.len - plotY) is the pixel distance from 
 | 
 * center.  
 | 
 */ 
 | 
wrap(seriesProto, 'translate', function (proceed) { 
 | 
         
 | 
    // Run uber method 
 | 
    proceed.call(this); 
 | 
     
 | 
    // Postprocess plot coordinates 
 | 
    if (this.chart.polar && !this.preventPostTranslate) { 
 | 
        var points = this.points, 
 | 
            i = points.length; 
 | 
        while (i--) { 
 | 
            // Translate plotX, plotY from angle and radius to true plot coordinates 
 | 
            this.toXY(points[i]); 
 | 
        } 
 | 
    } 
 | 
}); 
 | 
  
 | 
/**  
 | 
 * Extend getSegmentPath to allow connecting ends across 0 to provide a closed circle in  
 | 
 * line-like series. 
 | 
 */ 
 | 
wrap(seriesProto, 'getSegmentPath', function (proceed, segment) { 
 | 
         
 | 
    var points = this.points; 
 | 
     
 | 
    // Connect the path 
 | 
    if (this.chart.polar && this.options.connectEnds !== false &&  
 | 
            segment[segment.length - 1] === points[points.length - 1] && points[0].y !== null) { 
 | 
        this.connectEnds = true; // re-used in splines 
 | 
        segment = [].concat(segment, [points[0]]); 
 | 
    } 
 | 
     
 | 
    // Run uber method 
 | 
    return proceed.call(this, segment); 
 | 
     
 | 
}); 
 | 
  
 | 
  
 | 
function polarAnimate(proceed, init) { 
 | 
    var chart = this.chart, 
 | 
        animation = this.options.animation, 
 | 
        group = this.group, 
 | 
        markerGroup = this.markerGroup, 
 | 
        center = this.xAxis.center, 
 | 
        plotLeft = chart.plotLeft, 
 | 
        plotTop = chart.plotTop, 
 | 
        attribs; 
 | 
  
 | 
    // Specific animation for polar charts 
 | 
    if (chart.polar) { 
 | 
         
 | 
        // Enable animation on polar charts only in SVG. In VML, the scaling is different, plus animation 
 | 
        // would be so slow it would't matter. 
 | 
        if (chart.renderer.isSVG) { 
 | 
  
 | 
            if (animation === true) { 
 | 
                animation = {}; 
 | 
            } 
 | 
     
 | 
            // Initialize the animation 
 | 
            if (init) { 
 | 
                 
 | 
                // Scale down the group and place it in the center 
 | 
                attribs = { 
 | 
                    translateX: center[0] + plotLeft, 
 | 
                    translateY: center[1] + plotTop, 
 | 
                    scaleX: 0.001, // #1499 
 | 
                    scaleY: 0.001 
 | 
                }; 
 | 
                     
 | 
                group.attr(attribs); 
 | 
                if (markerGroup) { 
 | 
                    markerGroup.attrSetters = group.attrSetters; 
 | 
                    markerGroup.attr(attribs); 
 | 
                } 
 | 
                 
 | 
            // Run the animation 
 | 
            } else { 
 | 
                attribs = { 
 | 
                    translateX: plotLeft, 
 | 
                    translateY: plotTop, 
 | 
                    scaleX: 1, 
 | 
                    scaleY: 1 
 | 
                }; 
 | 
                group.animate(attribs, animation); 
 | 
                if (markerGroup) { 
 | 
                    markerGroup.animate(attribs, animation); 
 | 
                } 
 | 
                 
 | 
                // Delete this function to allow it only once 
 | 
                this.animate = null; 
 | 
            } 
 | 
        } 
 | 
     
 | 
    // For non-polar charts, revert to the basic animation 
 | 
    } else { 
 | 
        proceed.call(this, init); 
 | 
    }  
 | 
} 
 | 
  
 | 
// Define the animate method for both regular series and column series and their derivatives 
 | 
wrap(seriesProto, 'animate', polarAnimate); 
 | 
wrap(colProto, 'animate', polarAnimate); 
 | 
  
 | 
  
 | 
/** 
 | 
 * Throw in a couple of properties to let setTooltipPoints know we're indexing the points 
 | 
 * in degrees (0-360), not plot pixel width. 
 | 
 */ 
 | 
wrap(seriesProto, 'setTooltipPoints', function (proceed, renew) { 
 | 
         
 | 
    if (this.chart.polar) { 
 | 
        extend(this.xAxis, { 
 | 
            tooltipLen: 360 // degrees are the resolution unit of the tooltipPoints array 
 | 
        });     
 | 
    } 
 | 
     
 | 
    // Run uber method 
 | 
    return proceed.call(this, renew); 
 | 
}); 
 | 
  
 | 
  
 | 
/** 
 | 
 * Extend the column prototype's translate method 
 | 
 */ 
 | 
wrap(colProto, 'translate', function (proceed) { 
 | 
         
 | 
    var xAxis = this.xAxis, 
 | 
        len = this.yAxis.len, 
 | 
        center = xAxis.center, 
 | 
        startAngleRad = xAxis.startAngleRad, 
 | 
        renderer = this.chart.renderer, 
 | 
        start, 
 | 
        points, 
 | 
        point, 
 | 
        i; 
 | 
     
 | 
    this.preventPostTranslate = true; 
 | 
     
 | 
    // Run uber method 
 | 
    proceed.call(this); 
 | 
     
 | 
    // Postprocess plot coordinates 
 | 
    if (xAxis.isRadial) { 
 | 
        points = this.points; 
 | 
        i = points.length; 
 | 
        while (i--) { 
 | 
            point = points[i]; 
 | 
            start = point.barX + startAngleRad; 
 | 
            point.shapeType = 'path'; 
 | 
            point.shapeArgs = { 
 | 
                d: renderer.symbols.arc( 
 | 
                    center[0], 
 | 
                    center[1], 
 | 
                    len - point.plotY, 
 | 
                    null,  
 | 
                    { 
 | 
                        start: start, 
 | 
                        end: start + point.pointWidth, 
 | 
                        innerR: len - pick(point.yBottom, len) 
 | 
                    } 
 | 
                ) 
 | 
            }; 
 | 
            this.toXY(point); // provide correct plotX, plotY for tooltip 
 | 
        } 
 | 
    } 
 | 
}); 
 | 
  
 | 
  
 | 
/** 
 | 
 * Align column data labels outside the columns. #1199. 
 | 
 */ 
 | 
wrap(colProto, 'alignDataLabel', function (proceed, point, dataLabel, options, alignTo, isNew) { 
 | 
     
 | 
    if (this.chart.polar) { 
 | 
        var angle = point.rectPlotX / Math.PI * 180, 
 | 
            align, 
 | 
            verticalAlign; 
 | 
         
 | 
        // Align nicely outside the perimeter of the columns 
 | 
        if (options.align === null) { 
 | 
            if (angle > 20 && angle < 160) { 
 | 
                align = 'left'; // right hemisphere 
 | 
            } else if (angle > 200 && angle < 340) { 
 | 
                align = 'right'; // left hemisphere 
 | 
            } else { 
 | 
                align = 'center'; // top or bottom 
 | 
            } 
 | 
            options.align = align; 
 | 
        } 
 | 
        if (options.verticalAlign === null) { 
 | 
            if (angle < 45 || angle > 315) { 
 | 
                verticalAlign = 'bottom'; // top part 
 | 
            } else if (angle > 135 && angle < 225) { 
 | 
                verticalAlign = 'top'; // bottom part 
 | 
            } else { 
 | 
                verticalAlign = 'middle'; // left or right 
 | 
            } 
 | 
            options.verticalAlign = verticalAlign; 
 | 
        } 
 | 
         
 | 
        seriesProto.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew); 
 | 
    } else { 
 | 
        proceed.call(this, point, dataLabel, options, alignTo, isNew); 
 | 
    } 
 | 
     
 | 
}); 
 | 
  
 | 
/** 
 | 
 * Extend the mouse tracker to return the tooltip position index in terms of 
 | 
 * degrees rather than pixels 
 | 
 */ 
 | 
wrap(pointerProto, 'getIndex', function (proceed, e) { 
 | 
    var ret, 
 | 
        chart = this.chart, 
 | 
        center, 
 | 
        x, 
 | 
        y; 
 | 
     
 | 
    if (chart.polar) { 
 | 
        center = chart.xAxis[0].center; 
 | 
        x = e.chartX - center[0] - chart.plotLeft; 
 | 
        y = e.chartY - center[1] - chart.plotTop; 
 | 
         
 | 
        ret = 180 - Math.round(Math.atan2(x, y) / Math.PI * 180); 
 | 
     
 | 
    } else { 
 | 
     
 | 
        // Run uber method 
 | 
        ret = proceed.call(this, e); 
 | 
    } 
 | 
    return ret; 
 | 
}); 
 | 
  
 | 
/** 
 | 
 * Extend getCoordinates to prepare for polar axis values 
 | 
 */ 
 | 
wrap(pointerProto, 'getCoordinates', function (proceed, e) { 
 | 
    var chart = this.chart, 
 | 
        ret = { 
 | 
            xAxis: [], 
 | 
            yAxis: [] 
 | 
        }; 
 | 
     
 | 
    if (chart.polar) {     
 | 
  
 | 
        each(chart.axes, function (axis) { 
 | 
            var isXAxis = axis.isXAxis, 
 | 
                center = axis.center, 
 | 
                x = e.chartX - center[0] - chart.plotLeft, 
 | 
                y = e.chartY - center[1] - chart.plotTop; 
 | 
             
 | 
            ret[isXAxis ? 'xAxis' : 'yAxis'].push({ 
 | 
                axis: axis, 
 | 
                value: axis.translate( 
 | 
                    isXAxis ? 
 | 
                        Math.PI - Math.atan2(x, y) : // angle  
 | 
                        Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)), // distance from center 
 | 
                    true 
 | 
                ) 
 | 
            }); 
 | 
        }); 
 | 
         
 | 
    } else { 
 | 
        ret = proceed.call(this, e); 
 | 
    } 
 | 
     
 | 
    return ret; 
 | 
}); 
 | 
}(Highcharts)); 
 |