highcharts-rails 3.0.6 → 3.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,50 +1,2439 @@
1
- /*
2
- Highcharts JS v3.0.6 (2013-10-04)
3
-
4
- (c) 2009-2013 Torstein Hønsi
5
-
6
- License: www.highcharts.com/license
7
- */
8
- (function(j,C){function J(a,b,c){this.init.call(this,a,b,c)}function K(a,b,c){a.call(this,b,c);if(this.chart.polar)this.closeSegment=function(a){var c=this.xAxis.center;a.push("L",c[0],c[1])},this.closedStacks=!0}function L(a,b){var c=this.chart,d=this.options.animation,g=this.group,f=this.markerGroup,e=this.xAxis.center,i=c.plotLeft,n=c.plotTop;if(c.polar){if(c.renderer.isSVG)if(d===!0&&(d={}),b){if(c={translateX:e[0]+i,translateY:e[1]+n,scaleX:0.001,scaleY:0.001},g.attr(c),f)f.attrSetters=g.attrSetters,
9
- f.attr(c)}else c={translateX:i,translateY:n,scaleX:1,scaleY:1},g.animate(c,d),f&&f.animate(c,d),this.animate=null}else a.call(this,b)}var P=j.arrayMin,Q=j.arrayMax,s=j.each,F=j.extend,p=j.merge,R=j.map,r=j.pick,v=j.pInt,m=j.getOptions().plotOptions,h=j.seriesTypes,x=j.extendClass,M=j.splat,o=j.wrap,N=j.Axis,u=j.Tick,z=j.Series,q=h.column.prototype,t=Math,D=t.round,A=t.floor,S=t.max,w=function(){};F(J.prototype,{init:function(a,b,c){var d=this,g=d.defaultOptions;d.chart=b;if(b.angular)g.background=
10
- {};d.options=a=p(g,a);(a=a.background)&&s([].concat(M(a)).reverse(),function(a){var b=a.backgroundColor,a=p(d.defaultBackgroundOptions,a);if(b)a.backgroundColor=b;a.color=a.backgroundColor;c.options.plotBands.unshift(a)})},defaultOptions:{center:["50%","50%"],size:"85%",startAngle:0},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,innerRadius:0,to:Number.MAX_VALUE,
11
- outerRadius:"105%"}});var G=N.prototype,u=u.prototype,T={getOffset:w,redraw:function(){this.isDirty=!1},render:function(){this.isDirty=!1},setScale:w,setCategories:w,setTitle:w},O={isRadial:!0,defaultRadialGaugeOptions:{labels:{align:"center",x:0,y:null},minorGridLineWidth:0,minorTickInterval:"auto",minorTickLength:10,minorTickPosition:"inside",minorTickWidth:1,plotBands:[],tickLength:10,tickPosition:"inside",tickWidth:2,title:{rotation:0},zIndex:2},defaultRadialXOptions:{gridLineWidth:1,labels:{align:null,
12
- distance:15,x:0,y:null},maxPadding:0,minPadding:0,plotBands:[],showLastLabel:!1,tickLength:0},defaultRadialYOptions:{gridLineInterpolation:"circle",labels:{align:"right",x:-3,y:-2},plotBands:[],showLastLabel:!1,title:{x:4,text:null,rotation:90}},setOptions:function(a){this.options=p(this.defaultOptions,this.defaultRadialOptions,a)},getOffset:function(){G.getOffset.call(this);this.chart.axisOffset[this.side]=0},getLinePath:function(a,b){var c=this.center,b=r(b,c[2]/2-this.offset);return this.chart.renderer.symbols.arc(this.left+
13
- c[0],this.top+c[1],b,b,{start:this.startAngleRad,end:this.endAngleRad,open:!0,innerR:0})},setAxisTranslation:function(){G.setAxisTranslation.call(this);if(this.center&&(this.transA=this.isCircular?(this.endAngleRad-this.startAngleRad)/(this.max-this.min||1):this.center[2]/2/(this.max-this.min||1),this.isXAxis))this.minPixelPadding=this.transA*this.minPointOffset+(this.reversed?(this.endAngleRad-this.startAngleRad)/4:0)},beforeSetTickPositions:function(){this.autoConnect&&(this.max+=this.categories&&
14
- 1||this.pointRange||this.closestPointRange||0)},setAxisSize:function(){G.setAxisSize.call(this);if(this.isRadial)this.center=this.pane.center=h.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},getPosition:function(a,b){if(!this.isCircular)b=this.translate(a),a=this.min;return this.postTranslate(this.translate(a),r(b,this.center[2]/2)-this.offset)},postTranslate:function(a,b){var c=this.chart,
15
- d=this.center,a=this.startAngleRad+a;return{x:c.plotLeft+d[0]+Math.cos(a)*b,y:c.plotTop+d[1]+Math.sin(a)*b}},getPlotBandPath:function(a,b,c){var d=this.center,g=this.startAngleRad,f=d[2]/2,e=[r(c.outerRadius,"100%"),c.innerRadius,r(c.thickness,10)],i=/%$/,n,l=this.isCircular;this.options.gridLineInterpolation==="polygon"?d=this.getPlotLinePath(a).concat(this.getPlotLinePath(b,!0)):(l||(e[0]=this.translate(a),e[1]=this.translate(b)),e=R(e,function(a){i.test(a)&&(a=v(a,10)*f/100);return a}),c.shape===
16
- "circle"||!l?(a=-Math.PI/2,b=Math.PI*1.5,n=!0):(a=g+this.translate(a),b=g+this.translate(b)),d=this.chart.renderer.symbols.arc(this.left+d[0],this.top+d[1],e[0],e[0],{start:a,end:b,innerR:r(e[1],e[0]-e[2]),open:n}));return d},getPlotLinePath:function(a,b){var c=this.center,d=this.chart,g=this.getPosition(a),f,e,i;this.isCircular?i=["M",c[0]+d.plotLeft,c[1]+d.plotTop,"L",g.x,g.y]:this.options.gridLineInterpolation==="circle"?(a=this.translate(a))&&(i=this.getLinePath(0,a)):(f=d.xAxis[0],i=[],a=this.translate(a),
17
- c=f.tickPositions,f.autoConnect&&(c=c.concat([c[0]])),b&&(c=[].concat(c).reverse()),s(c,function(c,b){e=f.getPosition(c,a);i.push(b?"L":"M",e.x,e.y)}));return i},getTitlePosition:function(){var a=this.center,b=this.chart,c=this.options.title;return{x:b.plotLeft+a[0]+(c.x||0),y:b.plotTop+a[1]-{high:0.5,middle:0.25,low:0}[c.align]*a[2]+(c.y||0)}}};o(G,"init",function(a,b,c){var k;var d=b.angular,g=b.polar,f=c.isX,e=d&&f,i,n;n=b.options;var l=c.pane||0;if(d){if(F(this,e?T:O),i=!f)this.defaultRadialOptions=
18
- this.defaultRadialGaugeOptions}else if(g)F(this,O),this.defaultRadialOptions=(i=f)?this.defaultRadialXOptions:p(this.defaultYAxisOptions,this.defaultRadialYOptions);a.call(this,b,c);if(!e&&(d||g)){a=this.options;if(!b.panes)b.panes=[];this.pane=(k=b.panes[l]=b.panes[l]||new J(M(n.pane)[l],b,this),l=k);l=l.options;b.inverted=!1;n.chart.zoomType=null;this.startAngleRad=b=(l.startAngle-90)*Math.PI/180;this.endAngleRad=n=(r(l.endAngle,l.startAngle+360)-90)*Math.PI/180;this.offset=a.offset||0;if((this.isCircular=
19
- i)&&c.max===C&&n-b===2*Math.PI)this.autoConnect=!0}});o(u,"getPosition",function(a,b,c,d,g){var f=this.axis;return f.getPosition?f.getPosition(c):a.call(this,b,c,d,g)});o(u,"getLabelPosition",function(a,b,c,d,g,f,e,i,n){var l=this.axis,k=f.y,h=f.align,j=(l.translate(this.pos)+l.startAngleRad+Math.PI/2)/Math.PI*180%360;l.isRadial?(a=l.getPosition(this.pos,l.center[2]/2+r(f.distance,-25)),f.rotation==="auto"?d.attr({rotation:j}):k===null&&(k=v(d.styles.lineHeight)*0.9-d.getBBox().height/2),h===null&&
20
- (h=l.isCircular?j>20&&j<160?"left":j>200&&j<340?"right":"center":"center",d.attr({align:h})),a.x+=f.x,a.y+=k):a=a.call(this,b,c,d,g,f,e,i,n);return a});o(u,"getMarkPath",function(a,b,c,d,g,f,e){var i=this.axis;i.isRadial?(a=i.getPosition(this.pos,i.center[2]/2+d),b=["M",b,c,"L",a.x,a.y]):b=a.call(this,b,c,d,g,f,e);return b});m.arearange=p(m.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/>'},
21
- trackByArea:!0,dataLabels:{verticalAlign:null,xLow:0,xHigh:0,yLow:0,yHigh:0}});h.arearange=j.extendClass(h.area,{type:"arearange",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"low",getSegments:function(){var a=this;s(a.points,function(b){if(!a.options.connectNulls&&(b.low===null||b.high===null))b.y=null;else if(b.low===null&&b.high!==null)b.y=b.high});z.prototype.getSegments.call(this)},translate:function(){var a=this.yAxis;h.area.prototype.translate.apply(this);
22
- s(this.points,function(b){var c=b.low,d=b.high,g=b.plotY;d===null&&c===null?b.y=null:c===null?(b.plotLow=b.plotY=null,b.plotHigh=a.translate(d,0,1,0,1)):d===null?(b.plotLow=g,b.plotHigh=null):(b.plotLow=g,b.plotHigh=a.translate(d,0,1,0,1))})},getSegmentPath:function(a){var b,c=[],d=a.length,g=z.prototype.getSegmentPath,f,e;e=this.options;var i=e.step;for(b=HighchartsAdapter.grep(a,function(a){return a.plotLow!==null});d--;)f=a[d],f.plotHigh!==null&&c.push({plotX:f.plotX,plotY:f.plotHigh});a=g.call(this,
23
- b);if(i)i===!0&&(i="left"),e.step={left:"right",center:"center",right:"left"}[i];c=g.call(this,c);e.step=i;e=[].concat(a,c);c[0]="L";this.areaPath=this.areaPath.concat(a,c);return e},drawDataLabels:function(){var a=this.data,b=a.length,c,d=[],g=z.prototype,f=this.options.dataLabels,e,i=this.chart.inverted;if(f.enabled||this._hasPointLabels){for(c=b;c--;)e=a[c],e.y=e.high,e.plotY=e.plotHigh,d[c]=e.dataLabel,e.dataLabel=e.dataLabelUpper,e.below=!1,i?(f.align="left",f.x=f.xHigh):f.y=f.yHigh;g.drawDataLabels.apply(this,
24
- arguments);for(c=b;c--;)e=a[c],e.dataLabelUpper=e.dataLabel,e.dataLabel=d[c],e.y=e.low,e.plotY=e.plotLow,e.below=!0,i?(f.align="right",f.x=f.xLow):f.y=f.yLow;g.drawDataLabels.apply(this,arguments)}},alignDataLabel:h.column.prototype.alignDataLabel,getSymbol:h.column.prototype.getSymbol,drawPoints:w});m.areasplinerange=p(m.arearange);h.areasplinerange=x(h.arearange,{type:"areasplinerange",getPointSpline:h.spline.prototype.getPointSpline});m.columnrange=p(m.column,m.arearange,{lineWidth:1,pointRange:null});
25
- h.columnrange=x(h.arearange,{type:"columnrange",translate:function(){var a=this,b=a.yAxis,c;q.translate.apply(a);s(a.points,function(d){var g=d.shapeArgs,f=a.options.minPointLength,e;d.plotHigh=c=b.translate(d.high,0,1,0,1);d.plotLow=d.plotY;e=c;d=d.plotY-c;d<f&&(f-=d,d+=f,e-=f/2);g.height=d;g.y=e})},trackerGroups:["group","dataLabels"],drawGraph:w,pointAttrToOptions:q.pointAttrToOptions,drawPoints:q.drawPoints,drawTracker:q.drawTracker,animate:q.animate,getColumnMetrics:q.getColumnMetrics});m.gauge=
26
- p(m.line,{dataLabels:{enabled:!0,y:15,borderWidth:1,borderColor:"silver",borderRadius:3,style:{fontWeight:"bold"},verticalAlign:"top",zIndex:2},dial:{},pivot:{},tooltip:{headerFormat:""},showInLegend:!1});u={type:"gauge",pointClass:j.extendClass(j.Point,{setState:function(a){this.state=a}}),angular:!0,drawGraph:w,fixedBox:!0,trackerGroups:["group","dataLabels"],translate:function(){var a=this.yAxis,b=this.options,c=a.center;this.generatePoints();s(this.points,function(d){var g=p(b.dial,d.dial),f=
27
- v(r(g.radius,80))*c[2]/200,e=v(r(g.baseLength,70))*f/100,i=v(r(g.rearLength,10))*f/100,n=g.baseWidth||3,l=g.topWidth||1,k=a.startAngleRad+a.translate(d.y,null,null,null,!0);b.wrap===!1&&(k=Math.max(a.startAngleRad,Math.min(a.endAngleRad,k)));k=k*180/Math.PI;d.shapeType="path";d.shapeArgs={d:g.path||["M",-i,-n/2,"L",e,-n/2,f,-l/2,f,l/2,e,n/2,-i,n/2,"z"],translateX:c[0],translateY:c[1],rotation:k};d.plotX=c[0];d.plotY=c[1]})},drawPoints:function(){var a=this,b=a.yAxis.center,c=a.pivot,d=a.options,g=
28
- d.pivot,f=a.chart.renderer;s(a.points,function(c){var b=c.graphic,g=c.shapeArgs,l=g.d,k=p(d.dial,c.dial);b?(b.animate(g),g.d=l):c.graphic=f[c.shapeType](g).attr({stroke:k.borderColor||"none","stroke-width":k.borderWidth||0,fill:k.backgroundColor||"black",rotation:g.rotation}).add(a.group)});c?c.animate({translateX:b[0],translateY:b[1]}):a.pivot=f.circle(0,0,r(g.radius,5)).attr({"stroke-width":g.borderWidth||0,stroke:g.borderColor||"silver",fill:g.backgroundColor||"black"}).translate(b[0],b[1]).add(a.group)},
29
- animate:function(a){var b=this;if(!a)s(b.points,function(a){var d=a.graphic;d&&(d.attr({rotation:b.yAxis.startAngleRad*180/Math.PI}),d.animate({rotation:a.shapeArgs.rotation},b.options.animation))}),b.animate=null},render:function(){this.group=this.plotGroup("group","series",this.visible?"visible":"hidden",this.options.zIndex,this.chart.seriesGroup);h.pie.prototype.render.call(this);this.group.clip(this.chart.clipRect)},setData:h.pie.prototype.setData,drawTracker:h.column.prototype.drawTracker};h.gauge=
30
- j.extendClass(h.line,u);m.boxplot=p(m.column,{fillColor:"#FFFFFF",lineWidth:1,medianWidth:2,states:{hover:{brightness:-0.3}},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/>'},whiskerLength:"50%",whiskerWidth:2});h.boxplot=x(h.column,{type:"boxplot",pointArrayMap:["low","q1","median","q3","high"],
31
- toYData:function(a){return[a.low,a.q1,a.median,a.q3,a.high]},pointValKey:"high",pointAttrToOptions:{fill:"fillColor",stroke:"color","stroke-width":"lineWidth"},drawDataLabels:w,translate:function(){var a=this.yAxis,b=this.pointArrayMap;h.column.prototype.translate.apply(this);s(this.points,function(c){s(b,function(b){c[b]!==null&&(c[b+"Plot"]=a.translate(c[b],0,1,0,1))})})},drawPoints:function(){var a=this,b=a.points,c=a.options,d=a.chart.renderer,g,f,e,i,n,l,k,h,j,m,o,H,p,E,I,q,w,t,v,u,z,y,x=a.doQuartiles!==
32
- !1,B=parseInt(a.options.whiskerLength,10)/100;s(b,function(b){j=b.graphic;z=b.shapeArgs;o={};E={};q={};y=b.color||a.color;if(b.plotY!==C)if(g=b.pointAttr[b.selected?"selected":""],w=z.width,t=A(z.x),v=t+w,u=D(w/2),f=A(x?b.q1Plot:b.lowPlot),e=A(x?b.q3Plot:b.lowPlot),i=A(b.highPlot),n=A(b.lowPlot),o.stroke=b.stemColor||c.stemColor||y,o["stroke-width"]=r(b.stemWidth,c.stemWidth,c.lineWidth),o.dashstyle=b.stemDashStyle||c.stemDashStyle,E.stroke=b.whiskerColor||c.whiskerColor||y,E["stroke-width"]=r(b.whiskerWidth,
33
- c.whiskerWidth,c.lineWidth),q.stroke=b.medianColor||c.medianColor||y,q["stroke-width"]=r(b.medianWidth,c.medianWidth,c.lineWidth),k=o["stroke-width"]%2/2,h=t+u+k,m=["M",h,e,"L",h,i,"M",h,f,"L",h,n,"z"],x&&(k=g["stroke-width"]%2/2,h=A(h)+k,f=A(f)+k,e=A(e)+k,t+=k,v+=k,H=["M",t,e,"L",t,f,"L",v,f,"L",v,e,"L",t,e,"z"]),B&&(k=E["stroke-width"]%2/2,i+=k,n+=k,p=["M",h-u*B,i,"L",h+u*B,i,"M",h-u*B,n,"L",h+u*B,n]),k=q["stroke-width"]%2/2,l=D(b.medianPlot)+k,I=["M",t,l,"L",v,l,"z"],j)b.stem.animate({d:m}),B&&
34
- b.whiskers.animate({d:p}),x&&b.box.animate({d:H}),b.medianShape.animate({d:I});else{b.graphic=j=d.g().add(a.group);b.stem=d.path(m).attr(o).add(j);if(B)b.whiskers=d.path(p).attr(E).add(j);if(x)b.box=d.path(H).attr(g).add(j);b.medianShape=d.path(I).attr(q).add(j)}})}});m.errorbar=p(m.boxplot,{color:"#000000",grouping:!1,linkedTo:":previous",tooltip:{pointFormat:m.arearange.tooltip.pointFormat},whiskerWidth:null});h.errorbar=x(h.boxplot,{type:"errorbar",pointArrayMap:["low","high"],toYData:function(a){return[a.low,
35
- a.high]},pointValKey:"high",doQuartiles:!1,getColumnMetrics:function(){return this.linkedParent&&this.linkedParent.columnMetrics||h.column.prototype.getColumnMetrics.call(this)}});m.waterfall=p(m.column,{lineWidth:1,lineColor:"#333",dashStyle:"dot",borderColor:"#333"});h.waterfall=x(h.column,{type:"waterfall",upColorProp:"fill",pointArrayMap:["low","y"],pointValKey:"y",init:function(a,b){b.stacking=!0;h.column.prototype.init.call(this,a,b)},translate:function(){var a=this.options,b=this.yAxis,c,d,
36
- g,f,e,i,n,l,k;c=a.threshold;a=a.borderWidth%2/2;h.column.prototype.translate.apply(this);l=c;g=this.points;for(d=0,c=g.length;d<c;d++){f=g[d];e=f.shapeArgs;i=this.getStack(d);k=i.points[this.index];if(isNaN(f.y))f.y=this.yData[d];n=S(l,l+f.y)+k[0];e.y=b.translate(n,0,1);f.isSum||f.isIntermediateSum?(e.y=b.translate(k[1],0,1),e.height=b.translate(k[0],0,1)-e.y):l+=i.total;e.height<0&&(e.y+=e.height,e.height*=-1);f.plotY=e.y=D(e.y)-a;e.height=D(e.height);f.yBottom=e.y+e.height}},processData:function(a){var b=
37
- this.yData,c=this.points,d,g=b.length,f=this.options.threshold||0,e,i,h,l,k,j;i=e=h=l=f;for(j=0;j<g;j++)k=b[j],d=c&&c[j]?c[j]:{},k==="sum"||d.isSum?b[j]=i:k==="intermediateSum"||d.isIntermediateSum?(b[j]=e,e=f):(i+=k,e+=k),h=Math.min(i,h),l=Math.max(i,l);z.prototype.processData.call(this,a);this.dataMin=h;this.dataMax=l},toYData:function(a){if(a.isSum)return"sum";else if(a.isIntermediateSum)return"intermediateSum";return a.y},getAttribs:function(){h.column.prototype.getAttribs.apply(this,arguments);
38
- var a=this.options,b=a.states,c=a.upColor||this.color,a=j.Color(c).brighten(0.1).get(),d=p(this.pointAttr),g=this.upColorProp;d[""][g]=c;d.hover[g]=b.hover.upColor||a;d.select[g]=b.select.upColor||c;s(this.points,function(a){if(a.y>0&&!a.color)a.pointAttr=d,a.color=c})},getGraphPath:function(){var a=this.data,b=a.length,c=D(this.options.lineWidth+this.options.borderWidth)%2/2,d=[],g,f,e;for(e=1;e<b;e++)f=a[e].shapeArgs,g=a[e-1].shapeArgs,f=["M",g.x+g.width,g.y+c,"L",f.x,g.y+c],a[e-1].y<0&&(f[2]+=
39
- g.height,f[5]+=g.height),d=d.concat(f);return d},getExtremes:w,getStack:function(a){var b=this.yAxis.stacks,c=this.stackKey;this.processedYData[a]<this.options.threshold&&(c="-"+c);return b[c][a]},drawGraph:z.prototype.drawGraph});m.bubble=p(m.scatter,{dataLabels:{inside:!0,style:{color:"white",textShadow:"0px 0px 3px black"},verticalAlign:"middle"},marker:{lineColor:null,lineWidth:1},minSize:8,maxSize:"20%",tooltip:{pointFormat:"({point.x}, {point.y}), Size: {point.z}"},turboThreshold:0,zThreshold:0});
40
- h.bubble=x(h.scatter,{type:"bubble",pointArrayMap:["y","z"],trackerGroups:["group","dataLabelsGroup"],pointAttrToOptions:{stroke:"lineColor","stroke-width":"lineWidth",fill:"fillColor"},applyOpacity:function(a){var b=this.options.marker,c=r(b.fillOpacity,0.5),a=a||b.fillColor||this.color;c!==1&&(a=j.Color(a).setOpacity(c).get("rgba"));return a},convertAttribs:function(){var a=z.prototype.convertAttribs.apply(this,arguments);a.fill=this.applyOpacity(a.fill);return a},getRadii:function(a,b,c,d){var g,
41
- f,e,i=this.zData,h=[];for(f=0,g=i.length;f<g;f++)e=b-a,e=e>0?(i[f]-a)/(b-a):0.5,h.push(t.ceil(c+e*(d-c))/2);this.radii=h},animate:function(a){var b=this.options.animation;if(!a)s(this.points,function(a){var d=a.graphic,a=a.shapeArgs;d&&a&&(d.attr("r",1),d.animate({r:a.r},b))}),this.animate=null},translate:function(){var a,b=this.data,c,d,g=this.radii;h.scatter.prototype.translate.call(this);for(a=b.length;a--;)c=b[a],d=g?g[a]:0,c.negative=c.z<(this.options.zThreshold||0),d>=this.minPxSize/2?(c.shapeType=
42
- "circle",c.shapeArgs={x:c.plotX,y:c.plotY,r:d},c.dlBox={x:c.plotX-d,y:c.plotY-d,width:2*d,height:2*d}):c.shapeArgs=c.plotY=c.dlBox=C},drawLegendSymbol:function(a,b){var c=v(a.itemStyle.fontSize)/2;b.legendSymbol=this.chart.renderer.circle(c,a.baseline-c,c).attr({zIndex:3}).add(b.legendGroup);b.legendSymbol.isMarker=!0},drawPoints:h.column.prototype.drawPoints,alignDataLabel:h.column.prototype.alignDataLabel});N.prototype.beforePadding=function(){var a=this,b=this.len,c=this.chart,d=0,g=b,f=this.isXAxis,
43
- e=f?"xData":"yData",i=this.min,h={},j=t.min(c.plotWidth,c.plotHeight),k=Number.MAX_VALUE,m=-Number.MAX_VALUE,o=this.max-i,p=b/o,q=[];this.tickPositions&&(s(this.series,function(b){var c=b.options;if(b.type==="bubble"&&b.visible&&(a.allowZoomOutside=!0,q.push(b),f))s(["minSize","maxSize"],function(a){var b=c[a],d=/%$/.test(b),b=v(b);h[a]=d?j*b/100:b}),b.minPxSize=h.minSize,b=b.zData,b.length&&(k=t.min(k,t.max(P(b),c.displayNegative===!1?c.zThreshold:-Number.MAX_VALUE)),m=t.max(m,Q(b)))}),s(q,function(a){var b=
44
- a[e],c=b.length,j;f&&a.getRadii(k,m,h.minSize,h.maxSize);if(o>0)for(;c--;)j=a.radii[c],d=Math.min((b[c]-i)*p-j,d),g=Math.max((b[c]-i)*p+j,g)}),q.length&&o>0&&r(this.options.min,this.userMin)===C&&r(this.options.max,this.userMax)===C&&(g-=b,p*=(b+d-g)/b,this.min+=d/p,this.max+=g/p))};var y=z.prototype,m=j.Pointer.prototype;y.toXY=function(a){var b,c=this.chart;b=a.plotX;var d=a.plotY;a.rectPlotX=b;a.rectPlotY=d;a.clientX=(b/Math.PI*180+this.xAxis.pane.options.startAngle)%360;b=this.xAxis.postTranslate(a.plotX,
45
- this.yAxis.len-d);a.plotX=a.polarPlotX=b.x-c.plotLeft;a.plotY=a.polarPlotY=b.y-c.plotTop};y.orderTooltipPoints=function(a){if(this.chart.polar&&(a.sort(function(a,c){return a.clientX-c.clientX}),a[0]))a[0].wrappedClientX=a[0].clientX+360,a.push(a[0])};o(h.area.prototype,"init",K);o(h.areaspline.prototype,"init",K);o(h.spline.prototype,"getPointSpline",function(a,b,c,d){var g,f,e,i,h,j,k;if(this.chart.polar){g=c.plotX;f=c.plotY;a=b[d-1];e=b[d+1];this.connectEnds&&(a||(a=b[b.length-2]),e||(e=b[1]));
46
- if(a&&e)i=a.plotX,h=a.plotY,b=e.plotX,j=e.plotY,i=(1.5*g+i)/2.5,h=(1.5*f+h)/2.5,e=(1.5*g+b)/2.5,k=(1.5*f+j)/2.5,b=Math.sqrt(Math.pow(i-g,2)+Math.pow(h-f,2)),j=Math.sqrt(Math.pow(e-g,2)+Math.pow(k-f,2)),i=Math.atan2(h-f,i-g),h=Math.atan2(k-f,e-g),k=Math.PI/2+(i+h)/2,Math.abs(i-k)>Math.PI/2&&(k-=Math.PI),i=g+Math.cos(k)*b,h=f+Math.sin(k)*b,e=g+Math.cos(Math.PI+k)*j,k=f+Math.sin(Math.PI+k)*j,c.rightContX=e,c.rightContY=k;d?(c=["C",a.rightContX||a.plotX,a.rightContY||a.plotY,i||g,h||f,g,f],a.rightContX=
47
- a.rightContY=null):c=["M",g,f]}else c=a.call(this,b,c,d);return c});o(y,"translate",function(a){a.call(this);if(this.chart.polar&&!this.preventPostTranslate)for(var a=this.points,b=a.length;b--;)this.toXY(a[b])});o(y,"getSegmentPath",function(a,b){var c=this.points;if(this.chart.polar&&this.options.connectEnds!==!1&&b[b.length-1]===c[c.length-1]&&c[0].y!==null)this.connectEnds=!0,b=[].concat(b,[c[0]]);return a.call(this,b)});o(y,"animate",L);o(q,"animate",L);o(y,"setTooltipPoints",function(a,b){this.chart.polar&&
48
- F(this.xAxis,{tooltipLen:360});return a.call(this,b)});o(q,"translate",function(a){var b=this.xAxis,c=this.yAxis.len,d=b.center,g=b.startAngleRad,f=this.chart.renderer,e,h;this.preventPostTranslate=!0;a.call(this);if(b.isRadial){b=this.points;for(h=b.length;h--;)e=b[h],a=e.barX+g,e.shapeType="path",e.shapeArgs={d:f.symbols.arc(d[0],d[1],c-e.plotY,null,{start:a,end:a+e.pointWidth,innerR:c-r(e.yBottom,c)})},this.toXY(e)}});o(q,"alignDataLabel",function(a,b,c,d,g,f){if(this.chart.polar){a=b.rectPlotX/
49
- Math.PI*180;if(d.align===null)d.align=a>20&&a<160?"left":a>200&&a<340?"right":"center";if(d.verticalAlign===null)d.verticalAlign=a<45||a>315?"bottom":a>135&&a<225?"top":"middle";y.alignDataLabel.call(this,b,c,d,g,f)}else a.call(this,b,c,d,g,f)});o(m,"getIndex",function(a,b){var c,d=this.chart,g;d.polar?(g=d.xAxis[0].center,c=b.chartX-g[0]-d.plotLeft,d=b.chartY-g[1]-d.plotTop,c=180-Math.round(Math.atan2(c,d)/Math.PI*180)):c=a.call(this,b);return c});o(m,"getCoordinates",function(a,b){var c=this.chart,
50
- d={xAxis:[],yAxis:[]};c.polar?s(c.axes,function(a){var f=a.isXAxis,e=a.center,h=b.chartX-e[0]-c.plotLeft,e=b.chartY-e[1]-c.plotTop;d[f?"xAxis":"yAxis"].push({axis:a,value:a.translate(f?Math.PI-Math.atan2(h,e):Math.sqrt(Math.pow(h,2)+Math.pow(e,2)),!0)})}):d=a.call(this,b);return d})})(Highcharts);
1
+ // ==ClosureCompiler==
2
+ // @compilation_level SIMPLE_OPTIMIZATIONS
3
+
4
+ /**
5
+ * @license Highcharts JS v3.0.7 (2013-10-24)
6
+ *
7
+ * (c) 2009-2013 Torstein Hønsi
8
+ *
9
+ * License: www.highcharts.com/license
10
+ */
11
+
12
+ // JSLint options:
13
+ /*global Highcharts, HighchartsAdapter, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console */
14
+
15
+ (function (Highcharts, UNDEFINED) {
16
+ var arrayMin = Highcharts.arrayMin,
17
+ arrayMax = Highcharts.arrayMax,
18
+ each = Highcharts.each,
19
+ extend = Highcharts.extend,
20
+ merge = Highcharts.merge,
21
+ map = Highcharts.map,
22
+ pick = Highcharts.pick,
23
+ pInt = Highcharts.pInt,
24
+ defaultPlotOptions = Highcharts.getOptions().plotOptions,
25
+ seriesTypes = Highcharts.seriesTypes,
26
+ extendClass = Highcharts.extendClass,
27
+ splat = Highcharts.splat,
28
+ wrap = Highcharts.wrap,
29
+ Axis = Highcharts.Axis,
30
+ Tick = Highcharts.Tick,
31
+ Series = Highcharts.Series,
32
+ colProto = seriesTypes.column.prototype,
33
+ math = Math,
34
+ mathRound = math.round,
35
+ mathFloor = math.floor,
36
+ mathMax = math.max,
37
+ noop = function () {};/**
38
+ * The Pane object allows options that are common to a set of X and Y axes.
39
+ *
40
+ * In the future, this can be extended to basic Highcharts and Highstock.
41
+ */
42
+ function Pane(options, chart, firstAxis) {
43
+ this.init.call(this, options, chart, firstAxis);
44
+ }
45
+
46
+ // Extend the Pane prototype
47
+ extend(Pane.prototype, {
48
+
49
+ /**
50
+ * Initiate the Pane object
51
+ */
52
+ init: function (options, chart, firstAxis) {
53
+ var pane = this,
54
+ backgroundOption,
55
+ defaultOptions = pane.defaultOptions;
56
+
57
+ pane.chart = chart;
58
+
59
+ // Set options
60
+ if (chart.angular) { // gauges
61
+ defaultOptions.background = {}; // gets extended by this.defaultBackgroundOptions
62
+ }
63
+ pane.options = options = merge(defaultOptions, options);
64
+
65
+ backgroundOption = options.background;
66
+
67
+ // To avoid having weighty logic to place, update and remove the backgrounds,
68
+ // push them to the first axis' plot bands and borrow the existing logic there.
69
+ if (backgroundOption) {
70
+ each([].concat(splat(backgroundOption)).reverse(), function (config) {
71
+ var backgroundColor = config.backgroundColor; // if defined, replace the old one (specific for gradients)
72
+ config = merge(pane.defaultBackgroundOptions, config);
73
+ if (backgroundColor) {
74
+ config.backgroundColor = backgroundColor;
75
+ }
76
+ config.color = config.backgroundColor; // due to naming in plotBands
77
+ firstAxis.options.plotBands.unshift(config);
78
+ });
79
+ }
80
+ },
81
+
82
+ /**
83
+ * The default options object
84
+ */
85
+ defaultOptions: {
86
+ // background: {conditional},
87
+ center: ['50%', '50%'],
88
+ size: '85%',
89
+ startAngle: 0
90
+ //endAngle: startAngle + 360
91
+ },
92
+
93
+ /**
94
+ * The default background options
95
+ */
96
+ defaultBackgroundOptions: {
97
+ shape: 'circle',
98
+ borderWidth: 1,
99
+ borderColor: 'silver',
100
+ backgroundColor: {
101
+ linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
102
+ stops: [
103
+ [0, '#FFF'],
104
+ [1, '#DDD']
105
+ ]
106
+ },
107
+ from: Number.MIN_VALUE, // corrected to axis min
108
+ innerRadius: 0,
109
+ to: Number.MAX_VALUE, // corrected to axis max
110
+ outerRadius: '105%'
111
+ }
112
+
113
+ });
114
+ var axisProto = Axis.prototype,
115
+ tickProto = Tick.prototype;
116
+
117
+ /**
118
+ * Augmented methods for the x axis in order to hide it completely, used for the X axis in gauges
119
+ */
120
+ var hiddenAxisMixin = {
121
+ getOffset: noop,
122
+ redraw: function () {
123
+ this.isDirty = false; // prevent setting Y axis dirty
124
+ },
125
+ render: function () {
126
+ this.isDirty = false; // prevent setting Y axis dirty
127
+ },
128
+ setScale: noop,
129
+ setCategories: noop,
130
+ setTitle: noop
131
+ };
132
+
133
+ /**
134
+ * Augmented methods for the value axis
135
+ */
136
+ /*jslint unparam: true*/
137
+ var radialAxisMixin = {
138
+ isRadial: true,
139
+
140
+ /**
141
+ * The default options extend defaultYAxisOptions
142
+ */
143
+ defaultRadialGaugeOptions: {
144
+ labels: {
145
+ align: 'center',
146
+ x: 0,
147
+ y: null // auto
148
+ },
149
+ minorGridLineWidth: 0,
150
+ minorTickInterval: 'auto',
151
+ minorTickLength: 10,
152
+ minorTickPosition: 'inside',
153
+ minorTickWidth: 1,
154
+ plotBands: [],
155
+ tickLength: 10,
156
+ tickPosition: 'inside',
157
+ tickWidth: 2,
158
+ title: {
159
+ rotation: 0
160
+ },
161
+ zIndex: 2 // behind dials, points in the series group
162
+ },
163
+
164
+ // Circular axis around the perimeter of a polar chart
165
+ defaultRadialXOptions: {
166
+ gridLineWidth: 1, // spokes
167
+ labels: {
168
+ align: null, // auto
169
+ distance: 15,
170
+ x: 0,
171
+ y: null // auto
172
+ },
173
+ maxPadding: 0,
174
+ minPadding: 0,
175
+ plotBands: [],
176
+ showLastLabel: false,
177
+ tickLength: 0
178
+ },
179
+
180
+ // Radial axis, like a spoke in a polar chart
181
+ defaultRadialYOptions: {
182
+ gridLineInterpolation: 'circle',
183
+ labels: {
184
+ align: 'right',
185
+ x: -3,
186
+ y: -2
187
+ },
188
+ plotBands: [],
189
+ showLastLabel: false,
190
+ title: {
191
+ x: 4,
192
+ text: null,
193
+ rotation: 90
194
+ }
195
+ },
196
+
197
+ /**
198
+ * Merge and set options
199
+ */
200
+ setOptions: function (userOptions) {
201
+
202
+ this.options = merge(
203
+ this.defaultOptions,
204
+ this.defaultRadialOptions,
205
+ userOptions
206
+ );
207
+
208
+ },
209
+
210
+ /**
211
+ * Wrap the getOffset method to return zero offset for title or labels in a radial
212
+ * axis
213
+ */
214
+ getOffset: function () {
215
+ // Call the Axis prototype method (the method we're in now is on the instance)
216
+ axisProto.getOffset.call(this);
217
+
218
+ // Title or label offsets are not counted
219
+ this.chart.axisOffset[this.side] = 0;
220
+ },
221
+
222
+
223
+ /**
224
+ * Get the path for the axis line. This method is also referenced in the getPlotLinePath
225
+ * method.
226
+ */
227
+ getLinePath: function (lineWidth, radius) {
228
+ var center = this.center;
229
+ radius = pick(radius, center[2] / 2 - this.offset);
230
+
231
+ return this.chart.renderer.symbols.arc(
232
+ this.left + center[0],
233
+ this.top + center[1],
234
+ radius,
235
+ radius,
236
+ {
237
+ start: this.startAngleRad,
238
+ end: this.endAngleRad,
239
+ open: true,
240
+ innerR: 0
241
+ }
242
+ );
243
+ },
244
+
245
+ /**
246
+ * Override setAxisTranslation by setting the translation to the difference
247
+ * in rotation. This allows the translate method to return angle for
248
+ * any given value.
249
+ */
250
+ setAxisTranslation: function () {
251
+
252
+ // Call uber method
253
+ axisProto.setAxisTranslation.call(this);
254
+
255
+ // Set transA and minPixelPadding
256
+ if (this.center) { // it's not defined the first time
257
+ if (this.isCircular) {
258
+
259
+ this.transA = (this.endAngleRad - this.startAngleRad) /
260
+ ((this.max - this.min) || 1);
261
+
262
+
263
+ } else {
264
+ this.transA = (this.center[2] / 2) / ((this.max - this.min) || 1);
265
+ }
266
+
267
+ if (this.isXAxis) {
268
+ this.minPixelPadding = this.transA * this.minPointOffset +
269
+ (this.reversed ? (this.endAngleRad - this.startAngleRad) / 4 : 0); // ???
270
+ }
271
+ }
272
+ },
273
+
274
+ /**
275
+ * In case of auto connect, add one closestPointRange to the max value right before
276
+ * tickPositions are computed, so that ticks will extend passed the real max.
277
+ */
278
+ beforeSetTickPositions: function () {
279
+ if (this.autoConnect) {
280
+ this.max += (this.categories && 1) || this.pointRange || this.closestPointRange || 0; // #1197, #2260
281
+ }
282
+ },
283
+
284
+ /**
285
+ * Override the setAxisSize method to use the arc's circumference as length. This
286
+ * allows tickPixelInterval to apply to pixel lengths along the perimeter
287
+ */
288
+ setAxisSize: function () {
289
+
290
+ axisProto.setAxisSize.call(this);
291
+
292
+ if (this.isRadial) {
293
+
294
+ // Set the center array
295
+ this.center = this.pane.center = seriesTypes.pie.prototype.getCenter.call(this.pane);
296
+
297
+ this.len = this.width = this.height = this.isCircular ?
298
+ this.center[2] * (this.endAngleRad - this.startAngleRad) / 2 :
299
+ this.center[2] / 2;
300
+ }
301
+ },
302
+
303
+ /**
304
+ * Returns the x, y coordinate of a point given by a value and a pixel distance
305
+ * from center
306
+ */
307
+ getPosition: function (value, length) {
308
+ if (!this.isCircular) {
309
+ length = this.translate(value);
310
+ value = this.min;
311
+ }
312
+
313
+ return this.postTranslate(
314
+ this.translate(value),
315
+ pick(length, this.center[2] / 2) - this.offset
316
+ );
317
+ },
318
+
319
+ /**
320
+ * Translate from intermediate plotX (angle), plotY (axis.len - radius) to final chart coordinates.
321
+ */
322
+ postTranslate: function (angle, radius) {
323
+
324
+ var chart = this.chart,
325
+ center = this.center;
326
+
327
+ angle = this.startAngleRad + angle;
328
+
329
+ return {
330
+ x: chart.plotLeft + center[0] + Math.cos(angle) * radius,
331
+ y: chart.plotTop + center[1] + Math.sin(angle) * radius
332
+ };
333
+
334
+ },
335
+
336
+ /**
337
+ * Find the path for plot bands along the radial axis
338
+ */
339
+ getPlotBandPath: function (from, to, options) {
340
+ var center = this.center,
341
+ startAngleRad = this.startAngleRad,
342
+ fullRadius = center[2] / 2,
343
+ radii = [
344
+ pick(options.outerRadius, '100%'),
345
+ options.innerRadius,
346
+ pick(options.thickness, 10)
347
+ ],
348
+ percentRegex = /%$/,
349
+ start,
350
+ end,
351
+ open,
352
+ isCircular = this.isCircular, // X axis in a polar chart
353
+ ret;
354
+
355
+ // Polygonal plot bands
356
+ if (this.options.gridLineInterpolation === 'polygon') {
357
+ ret = this.getPlotLinePath(from).concat(this.getPlotLinePath(to, true));
358
+
359
+ // Circular grid bands
360
+ } else {
361
+
362
+ // Plot bands on Y axis (radial axis) - inner and outer radius depend on to and from
363
+ if (!isCircular) {
364
+ radii[0] = this.translate(from);
365
+ radii[1] = this.translate(to);
366
+ }
367
+
368
+ // Convert percentages to pixel values
369
+ radii = map(radii, function (radius) {
370
+ if (percentRegex.test(radius)) {
371
+ radius = (pInt(radius, 10) * fullRadius) / 100;
372
+ }
373
+ return radius;
374
+ });
375
+
376
+ // Handle full circle
377
+ if (options.shape === 'circle' || !isCircular) {
378
+ start = -Math.PI / 2;
379
+ end = Math.PI * 1.5;
380
+ open = true;
381
+ } else {
382
+ start = startAngleRad + this.translate(from);
383
+ end = startAngleRad + this.translate(to);
384
+ }
385
+
386
+
387
+ ret = this.chart.renderer.symbols.arc(
388
+ this.left + center[0],
389
+ this.top + center[1],
390
+ radii[0],
391
+ radii[0],
392
+ {
393
+ start: start,
394
+ end: end,
395
+ innerR: pick(radii[1], radii[0] - radii[2]),
396
+ open: open
397
+ }
398
+ );
399
+ }
400
+
401
+ return ret;
402
+ },
403
+
404
+ /**
405
+ * Find the path for plot lines perpendicular to the radial axis.
406
+ */
407
+ getPlotLinePath: function (value, reverse) {
408
+ var axis = this,
409
+ center = axis.center,
410
+ chart = axis.chart,
411
+ end = axis.getPosition(value),
412
+ xAxis,
413
+ xy,
414
+ tickPositions,
415
+ ret;
416
+
417
+ // Spokes
418
+ if (axis.isCircular) {
419
+ ret = ['M', center[0] + chart.plotLeft, center[1] + chart.plotTop, 'L', end.x, end.y];
420
+
421
+ // Concentric circles
422
+ } else if (axis.options.gridLineInterpolation === 'circle') {
423
+ value = axis.translate(value);
424
+ if (value) { // a value of 0 is in the center
425
+ ret = axis.getLinePath(0, value);
426
+ }
427
+ // Concentric polygons
428
+ } else {
429
+ xAxis = chart.xAxis[0];
430
+ ret = [];
431
+ value = axis.translate(value);
432
+ tickPositions = xAxis.tickPositions;
433
+ if (xAxis.autoConnect) {
434
+ tickPositions = tickPositions.concat([tickPositions[0]]);
435
+ }
436
+ // Reverse the positions for concatenation of polygonal plot bands
437
+ if (reverse) {
438
+ tickPositions = [].concat(tickPositions).reverse();
439
+ }
440
+
441
+ each(tickPositions, function (pos, i) {
442
+ xy = xAxis.getPosition(pos, value);
443
+ ret.push(i ? 'L' : 'M', xy.x, xy.y);
444
+ });
445
+
446
+ }
447
+ return ret;
448
+ },
449
+
450
+ /**
451
+ * Find the position for the axis title, by default inside the gauge
452
+ */
453
+ getTitlePosition: function () {
454
+ var center = this.center,
455
+ chart = this.chart,
456
+ titleOptions = this.options.title;
457
+
458
+ return {
459
+ x: chart.plotLeft + center[0] + (titleOptions.x || 0),
460
+ y: chart.plotTop + center[1] - ({ high: 0.5, middle: 0.25, low: 0 }[titleOptions.align] *
461
+ center[2]) + (titleOptions.y || 0)
462
+ };
463
+ }
464
+
465
+ };
466
+ /*jslint unparam: false*/
467
+
468
+ /**
469
+ * Override axisProto.init to mix in special axis instance functions and function overrides
470
+ */
471
+ wrap(axisProto, 'init', function (proceed, chart, userOptions) {
472
+ var axis = this,
473
+ angular = chart.angular,
474
+ polar = chart.polar,
475
+ isX = userOptions.isX,
476
+ isHidden = angular && isX,
477
+ isCircular,
478
+ startAngleRad,
479
+ endAngleRad,
480
+ options,
481
+ chartOptions = chart.options,
482
+ paneIndex = userOptions.pane || 0,
483
+ pane,
484
+ paneOptions;
485
+
486
+ // Before prototype.init
487
+ if (angular) {
488
+ extend(this, isHidden ? hiddenAxisMixin : radialAxisMixin);
489
+ isCircular = !isX;
490
+ if (isCircular) {
491
+ this.defaultRadialOptions = this.defaultRadialGaugeOptions;
492
+ }
493
+
494
+ } else if (polar) {
495
+ //extend(this, userOptions.isX ? radialAxisMixin : radialAxisMixin);
496
+ extend(this, radialAxisMixin);
497
+ isCircular = isX;
498
+ this.defaultRadialOptions = isX ? this.defaultRadialXOptions : merge(this.defaultYAxisOptions, this.defaultRadialYOptions);
499
+
500
+ }
501
+
502
+ // Run prototype.init
503
+ proceed.call(this, chart, userOptions);
504
+
505
+ if (!isHidden && (angular || polar)) {
506
+ options = this.options;
507
+
508
+ // Create the pane and set the pane options.
509
+ if (!chart.panes) {
510
+ chart.panes = [];
511
+ }
512
+ this.pane = pane = chart.panes[paneIndex] = chart.panes[paneIndex] || new Pane(
513
+ splat(chartOptions.pane)[paneIndex],
514
+ chart,
515
+ axis
516
+ );
517
+ paneOptions = pane.options;
518
+
519
+
520
+ // Disable certain features on angular and polar axes
521
+ chart.inverted = false;
522
+ chartOptions.chart.zoomType = null;
523
+
524
+ // Start and end angle options are
525
+ // given in degrees relative to top, while internal computations are
526
+ // in radians relative to right (like SVG).
527
+ this.startAngleRad = startAngleRad = (paneOptions.startAngle - 90) * Math.PI / 180;
528
+ this.endAngleRad = endAngleRad = (pick(paneOptions.endAngle, paneOptions.startAngle + 360) - 90) * Math.PI / 180;
529
+ this.offset = options.offset || 0;
530
+
531
+ this.isCircular = isCircular;
532
+
533
+ // Automatically connect grid lines?
534
+ if (isCircular && userOptions.max === UNDEFINED && endAngleRad - startAngleRad === 2 * Math.PI) {
535
+ this.autoConnect = true;
536
+ }
537
+ }
538
+
539
+ });
540
+
541
+ /**
542
+ * Add special cases within the Tick class' methods for radial axes.
543
+ */
544
+ wrap(tickProto, 'getPosition', function (proceed, horiz, pos, tickmarkOffset, old) {
545
+ var axis = this.axis;
546
+
547
+ return axis.getPosition ?
548
+ axis.getPosition(pos) :
549
+ proceed.call(this, horiz, pos, tickmarkOffset, old);
550
+ });
551
+
552
+ /**
553
+ * Wrap the getLabelPosition function to find the center position of the label
554
+ * based on the distance option
555
+ */
556
+ wrap(tickProto, 'getLabelPosition', function (proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
557
+ var axis = this.axis,
558
+ optionsY = labelOptions.y,
559
+ ret,
560
+ align = labelOptions.align,
561
+ angle = ((axis.translate(this.pos) + axis.startAngleRad + Math.PI / 2) / Math.PI * 180) % 360;
562
+
563
+ if (axis.isRadial) {
564
+ ret = axis.getPosition(this.pos, (axis.center[2] / 2) + pick(labelOptions.distance, -25));
565
+
566
+ // Automatically rotated
567
+ if (labelOptions.rotation === 'auto') {
568
+ label.attr({
569
+ rotation: angle
570
+ });
571
+
572
+ // Vertically centered
573
+ } else if (optionsY === null) {
574
+ optionsY = pInt(label.styles.lineHeight) * 0.9 - label.getBBox().height / 2;
575
+
576
+ }
577
+
578
+ // Automatic alignment
579
+ if (align === null) {
580
+ if (axis.isCircular) {
581
+ if (angle > 20 && angle < 160) {
582
+ align = 'left'; // right hemisphere
583
+ } else if (angle > 200 && angle < 340) {
584
+ align = 'right'; // left hemisphere
585
+ } else {
586
+ align = 'center'; // top or bottom
587
+ }
588
+ } else {
589
+ align = 'center';
590
+ }
591
+ label.attr({
592
+ align: align
593
+ });
594
+ }
595
+
596
+ ret.x += labelOptions.x;
597
+ ret.y += optionsY;
598
+
599
+ } else {
600
+ ret = proceed.call(this, x, y, label, horiz, labelOptions, tickmarkOffset, index, step);
601
+ }
602
+ return ret;
603
+ });
604
+
605
+ /**
606
+ * Wrap the getMarkPath function to return the path of the radial marker
607
+ */
608
+ wrap(tickProto, 'getMarkPath', function (proceed, x, y, tickLength, tickWidth, horiz, renderer) {
609
+ var axis = this.axis,
610
+ endPoint,
611
+ ret;
612
+
613
+ if (axis.isRadial) {
614
+ endPoint = axis.getPosition(this.pos, axis.center[2] / 2 + tickLength);
615
+ ret = [
616
+ 'M',
617
+ x,
618
+ y,
619
+ 'L',
620
+ endPoint.x,
621
+ endPoint.y
622
+ ];
623
+ } else {
624
+ ret = proceed.call(this, x, y, tickLength, tickWidth, horiz, renderer);
625
+ }
626
+ return ret;
627
+ });/*
628
+ * The AreaRangeSeries class
629
+ *
630
+ */
631
+
632
+ /**
633
+ * Extend the default options with map options
634
+ */
635
+ defaultPlotOptions.arearange = merge(defaultPlotOptions.area, {
636
+ lineWidth: 1,
637
+ marker: null,
638
+ threshold: null,
639
+ tooltip: {
640
+ pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.low}</b> - <b>{point.high}</b><br/>'
641
+ },
642
+ trackByArea: true,
643
+ dataLabels: {
644
+ verticalAlign: null,
645
+ xLow: 0,
646
+ xHigh: 0,
647
+ yLow: 0,
648
+ yHigh: 0
649
+ }
650
+ });
651
+
652
+ /**
653
+ * Add the series type
654
+ */
655
+ seriesTypes.arearange = Highcharts.extendClass(seriesTypes.area, {
656
+ type: 'arearange',
657
+ pointArrayMap: ['low', 'high'],
658
+ toYData: function (point) {
659
+ return [point.low, point.high];
660
+ },
661
+ pointValKey: 'low',
662
+
663
+ /**
664
+ * Extend getSegments to force null points if the higher value is null. #1703.
665
+ */
666
+ getSegments: function () {
667
+ var series = this;
668
+
669
+ each(series.points, function (point) {
670
+ if (!series.options.connectNulls && (point.low === null || point.high === null)) {
671
+ point.y = null;
672
+ } else if (point.low === null && point.high !== null) {
673
+ point.y = point.high;
674
+ }
675
+ });
676
+ Series.prototype.getSegments.call(this);
677
+ },
678
+
679
+ /**
680
+ * Translate data points from raw values x and y to plotX and plotY
681
+ */
682
+ translate: function () {
683
+ var series = this,
684
+ yAxis = series.yAxis;
685
+
686
+ seriesTypes.area.prototype.translate.apply(series);
687
+
688
+ // Set plotLow and plotHigh
689
+ each(series.points, function (point) {
690
+
691
+ var low = point.low,
692
+ high = point.high,
693
+ plotY = point.plotY;
694
+
695
+ if (high === null && low === null) {
696
+ point.y = null;
697
+ } else if (low === null) {
698
+ point.plotLow = point.plotY = null;
699
+ point.plotHigh = yAxis.translate(high, 0, 1, 0, 1);
700
+ } else if (high === null) {
701
+ point.plotLow = plotY;
702
+ point.plotHigh = null;
703
+ } else {
704
+ point.plotLow = plotY;
705
+ point.plotHigh = yAxis.translate(high, 0, 1, 0, 1);
706
+ }
707
+ });
708
+ },
709
+
710
+ /**
711
+ * Extend the line series' getSegmentPath method by applying the segment
712
+ * path to both lower and higher values of the range
713
+ */
714
+ getSegmentPath: function (segment) {
715
+
716
+ var lowSegment,
717
+ highSegment = [],
718
+ i = segment.length,
719
+ baseGetSegmentPath = Series.prototype.getSegmentPath,
720
+ point,
721
+ linePath,
722
+ lowerPath,
723
+ options = this.options,
724
+ step = options.step,
725
+ higherPath;
726
+
727
+ // Remove nulls from low segment
728
+ lowSegment = HighchartsAdapter.grep(segment, function (point) {
729
+ return point.plotLow !== null;
730
+ });
731
+
732
+ // Make a segment with plotX and plotY for the top values
733
+ while (i--) {
734
+ point = segment[i];
735
+ if (point.plotHigh !== null) {
736
+ highSegment.push({
737
+ plotX: point.plotX,
738
+ plotY: point.plotHigh
739
+ });
740
+ }
741
+ }
742
+
743
+ // Get the paths
744
+ lowerPath = baseGetSegmentPath.call(this, lowSegment);
745
+ if (step) {
746
+ if (step === true) {
747
+ step = 'left';
748
+ }
749
+ options.step = { left: 'right', center: 'center', right: 'left' }[step]; // swap for reading in getSegmentPath
750
+ }
751
+ higherPath = baseGetSegmentPath.call(this, highSegment);
752
+ options.step = step;
753
+
754
+ // Create a line on both top and bottom of the range
755
+ linePath = [].concat(lowerPath, higherPath);
756
+
757
+ // For the area path, we need to change the 'move' statement into 'lineTo' or 'curveTo'
758
+ higherPath[0] = 'L'; // this probably doesn't work for spline
759
+ this.areaPath = this.areaPath.concat(lowerPath, higherPath);
760
+
761
+ return linePath;
762
+ },
763
+
764
+ /**
765
+ * Extend the basic drawDataLabels method by running it for both lower and higher
766
+ * values.
767
+ */
768
+ drawDataLabels: function () {
769
+
770
+ var data = this.data,
771
+ length = data.length,
772
+ i,
773
+ originalDataLabels = [],
774
+ seriesProto = Series.prototype,
775
+ dataLabelOptions = this.options.dataLabels,
776
+ point,
777
+ inverted = this.chart.inverted;
778
+
779
+ if (dataLabelOptions.enabled || this._hasPointLabels) {
780
+
781
+ // Step 1: set preliminary values for plotY and dataLabel and draw the upper labels
782
+ i = length;
783
+ while (i--) {
784
+ point = data[i];
785
+
786
+ // Set preliminary values
787
+ point.y = point.high;
788
+ point.plotY = point.plotHigh;
789
+
790
+ // Store original data labels and set preliminary label objects to be picked up
791
+ // in the uber method
792
+ originalDataLabels[i] = point.dataLabel;
793
+ point.dataLabel = point.dataLabelUpper;
794
+
795
+ // Set the default offset
796
+ point.below = false;
797
+ if (inverted) {
798
+ dataLabelOptions.align = 'left';
799
+ dataLabelOptions.x = dataLabelOptions.xHigh;
800
+ } else {
801
+ dataLabelOptions.y = dataLabelOptions.yHigh;
802
+ }
803
+ }
804
+ seriesProto.drawDataLabels.apply(this, arguments); // #1209
805
+
806
+ // Step 2: reorganize and handle data labels for the lower values
807
+ i = length;
808
+ while (i--) {
809
+ point = data[i];
810
+
811
+ // Move the generated labels from step 1, and reassign the original data labels
812
+ point.dataLabelUpper = point.dataLabel;
813
+ point.dataLabel = originalDataLabels[i];
814
+
815
+ // Reset values
816
+ point.y = point.low;
817
+ point.plotY = point.plotLow;
818
+
819
+ // Set the default offset
820
+ point.below = true;
821
+ if (inverted) {
822
+ dataLabelOptions.align = 'right';
823
+ dataLabelOptions.x = dataLabelOptions.xLow;
824
+ } else {
825
+ dataLabelOptions.y = dataLabelOptions.yLow;
826
+ }
827
+ }
828
+ seriesProto.drawDataLabels.apply(this, arguments);
829
+ }
830
+
831
+ },
832
+
833
+ alignDataLabel: seriesTypes.column.prototype.alignDataLabel,
834
+
835
+ getSymbol: seriesTypes.column.prototype.getSymbol,
836
+
837
+ drawPoints: noop
838
+ });/**
839
+ * The AreaSplineRangeSeries class
840
+ */
841
+
842
+ defaultPlotOptions.areasplinerange = merge(defaultPlotOptions.arearange);
843
+
844
+ /**
845
+ * AreaSplineRangeSeries object
846
+ */
847
+ seriesTypes.areasplinerange = extendClass(seriesTypes.arearange, {
848
+ type: 'areasplinerange',
849
+ getPointSpline: seriesTypes.spline.prototype.getPointSpline
850
+ });/**
851
+ * The ColumnRangeSeries class
852
+ */
853
+ defaultPlotOptions.columnrange = merge(defaultPlotOptions.column, defaultPlotOptions.arearange, {
854
+ lineWidth: 1,
855
+ pointRange: null
856
+ });
857
+
858
+ /**
859
+ * ColumnRangeSeries object
860
+ */
861
+ seriesTypes.columnrange = extendClass(seriesTypes.arearange, {
862
+ type: 'columnrange',
863
+ /**
864
+ * Translate data points from raw values x and y to plotX and plotY
865
+ */
866
+ translate: function () {
867
+ var series = this,
868
+ yAxis = series.yAxis,
869
+ plotHigh;
870
+
871
+ colProto.translate.apply(series);
872
+
873
+ // Set plotLow and plotHigh
874
+ each(series.points, function (point) {
875
+ var shapeArgs = point.shapeArgs,
876
+ minPointLength = series.options.minPointLength,
877
+ heightDifference,
878
+ height,
879
+ y;
880
+
881
+ point.plotHigh = plotHigh = yAxis.translate(point.high, 0, 1, 0, 1);
882
+ point.plotLow = point.plotY;
883
+
884
+ // adjust shape
885
+ y = plotHigh;
886
+ height = point.plotY - plotHigh;
887
+
888
+ if (height < minPointLength) {
889
+ heightDifference = (minPointLength - height);
890
+ height += heightDifference;
891
+ y -= heightDifference / 2;
892
+ }
893
+ shapeArgs.height = height;
894
+ shapeArgs.y = y;
895
+ });
896
+ },
897
+ trackerGroups: ['group', 'dataLabels'],
898
+ drawGraph: noop,
899
+ pointAttrToOptions: colProto.pointAttrToOptions,
900
+ drawPoints: colProto.drawPoints,
901
+ drawTracker: colProto.drawTracker,
902
+ animate: colProto.animate,
903
+ getColumnMetrics: colProto.getColumnMetrics
904
+ });
905
+ /*
906
+ * The GaugeSeries class
907
+ */
908
+
909
+
910
+
911
+ /**
912
+ * Extend the default options
913
+ */
914
+ defaultPlotOptions.gauge = merge(defaultPlotOptions.line, {
915
+ dataLabels: {
916
+ enabled: true,
917
+ y: 15,
918
+ borderWidth: 1,
919
+ borderColor: 'silver',
920
+ borderRadius: 3,
921
+ style: {
922
+ fontWeight: 'bold'
923
+ },
924
+ verticalAlign: 'top',
925
+ zIndex: 2
926
+ },
927
+ dial: {
928
+ // radius: '80%',
929
+ // backgroundColor: 'black',
930
+ // borderColor: 'silver',
931
+ // borderWidth: 0,
932
+ // baseWidth: 3,
933
+ // topWidth: 1,
934
+ // baseLength: '70%' // of radius
935
+ // rearLength: '10%'
936
+ },
937
+ pivot: {
938
+ //radius: 5,
939
+ //borderWidth: 0
940
+ //borderColor: 'silver',
941
+ //backgroundColor: 'black'
942
+ },
943
+ tooltip: {
944
+ headerFormat: ''
945
+ },
946
+ showInLegend: false
947
+ });
948
+
949
+ /**
950
+ * Extend the point object
951
+ */
952
+ var GaugePoint = Highcharts.extendClass(Highcharts.Point, {
953
+ /**
954
+ * Don't do any hover colors or anything
955
+ */
956
+ setState: function (state) {
957
+ this.state = state;
958
+ }
959
+ });
960
+
961
+
962
+ /**
963
+ * Add the series type
964
+ */
965
+ var GaugeSeries = {
966
+ type: 'gauge',
967
+ pointClass: GaugePoint,
968
+
969
+ // chart.angular will be set to true when a gauge series is present, and this will
970
+ // be used on the axes
971
+ angular: true,
972
+ drawGraph: noop,
973
+ fixedBox: true,
974
+ trackerGroups: ['group', 'dataLabels'],
975
+
976
+ /**
977
+ * Calculate paths etc
978
+ */
979
+ translate: function () {
980
+
981
+ var series = this,
982
+ yAxis = series.yAxis,
983
+ options = series.options,
984
+ center = yAxis.center;
985
+
986
+ series.generatePoints();
987
+
988
+ each(series.points, function (point) {
989
+
990
+ var dialOptions = merge(options.dial, point.dial),
991
+ radius = (pInt(pick(dialOptions.radius, 80)) * center[2]) / 200,
992
+ baseLength = (pInt(pick(dialOptions.baseLength, 70)) * radius) / 100,
993
+ rearLength = (pInt(pick(dialOptions.rearLength, 10)) * radius) / 100,
994
+ baseWidth = dialOptions.baseWidth || 3,
995
+ topWidth = dialOptions.topWidth || 1,
996
+ rotation = yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true);
997
+
998
+ // Handle the wrap option
999
+ if (options.wrap === false) {
1000
+ rotation = Math.max(yAxis.startAngleRad, Math.min(yAxis.endAngleRad, rotation));
1001
+ }
1002
+ rotation = rotation * 180 / Math.PI;
1003
+
1004
+ point.shapeType = 'path';
1005
+ point.shapeArgs = {
1006
+ d: dialOptions.path || [
1007
+ 'M',
1008
+ -rearLength, -baseWidth / 2,
1009
+ 'L',
1010
+ baseLength, -baseWidth / 2,
1011
+ radius, -topWidth / 2,
1012
+ radius, topWidth / 2,
1013
+ baseLength, baseWidth / 2,
1014
+ -rearLength, baseWidth / 2,
1015
+ 'z'
1016
+ ],
1017
+ translateX: center[0],
1018
+ translateY: center[1],
1019
+ rotation: rotation
1020
+ };
1021
+
1022
+ // Positions for data label
1023
+ point.plotX = center[0];
1024
+ point.plotY = center[1];
1025
+ });
1026
+ },
1027
+
1028
+ /**
1029
+ * Draw the points where each point is one needle
1030
+ */
1031
+ drawPoints: function () {
1032
+
1033
+ var series = this,
1034
+ center = series.yAxis.center,
1035
+ pivot = series.pivot,
1036
+ options = series.options,
1037
+ pivotOptions = options.pivot,
1038
+ renderer = series.chart.renderer;
1039
+
1040
+ each(series.points, function (point) {
1041
+
1042
+ var graphic = point.graphic,
1043
+ shapeArgs = point.shapeArgs,
1044
+ d = shapeArgs.d,
1045
+ dialOptions = merge(options.dial, point.dial); // #1233
1046
+
1047
+ if (graphic) {
1048
+ graphic.animate(shapeArgs);
1049
+ shapeArgs.d = d; // animate alters it
1050
+ } else {
1051
+ point.graphic = renderer[point.shapeType](shapeArgs)
1052
+ .attr({
1053
+ stroke: dialOptions.borderColor || 'none',
1054
+ 'stroke-width': dialOptions.borderWidth || 0,
1055
+ fill: dialOptions.backgroundColor || 'black',
1056
+ rotation: shapeArgs.rotation // required by VML when animation is false
1057
+ })
1058
+ .add(series.group);
1059
+ }
1060
+ });
1061
+
1062
+ // Add or move the pivot
1063
+ if (pivot) {
1064
+ pivot.animate({ // #1235
1065
+ translateX: center[0],
1066
+ translateY: center[1]
1067
+ });
1068
+ } else {
1069
+ series.pivot = renderer.circle(0, 0, pick(pivotOptions.radius, 5))
1070
+ .attr({
1071
+ 'stroke-width': pivotOptions.borderWidth || 0,
1072
+ stroke: pivotOptions.borderColor || 'silver',
1073
+ fill: pivotOptions.backgroundColor || 'black'
1074
+ })
1075
+ .translate(center[0], center[1])
1076
+ .add(series.group);
1077
+ }
1078
+ },
1079
+
1080
+ /**
1081
+ * Animate the arrow up from startAngle
1082
+ */
1083
+ animate: function (init) {
1084
+ var series = this;
1085
+
1086
+ if (!init) {
1087
+ each(series.points, function (point) {
1088
+ var graphic = point.graphic;
1089
+
1090
+ if (graphic) {
1091
+ // start value
1092
+ graphic.attr({
1093
+ rotation: series.yAxis.startAngleRad * 180 / Math.PI
1094
+ });
1095
+
1096
+ // animate
1097
+ graphic.animate({
1098
+ rotation: point.shapeArgs.rotation
1099
+ }, series.options.animation);
1100
+ }
1101
+ });
1102
+
1103
+ // delete this function to allow it only once
1104
+ series.animate = null;
1105
+ }
1106
+ },
1107
+
1108
+ render: function () {
1109
+ this.group = this.plotGroup(
1110
+ 'group',
1111
+ 'series',
1112
+ this.visible ? 'visible' : 'hidden',
1113
+ this.options.zIndex,
1114
+ this.chart.seriesGroup
1115
+ );
1116
+ seriesTypes.pie.prototype.render.call(this);
1117
+ this.group.clip(this.chart.clipRect);
1118
+ },
1119
+
1120
+ setData: seriesTypes.pie.prototype.setData,
1121
+ drawTracker: seriesTypes.column.prototype.drawTracker
1122
+ };
1123
+ seriesTypes.gauge = Highcharts.extendClass(seriesTypes.line, GaugeSeries);/* ****************************************************************************
1124
+ * Start Box plot series code *
1125
+ *****************************************************************************/
1126
+
1127
+ // Set default options
1128
+ defaultPlotOptions.boxplot = merge(defaultPlotOptions.column, {
1129
+ fillColor: '#FFFFFF',
1130
+ lineWidth: 1,
1131
+ //medianColor: null,
1132
+ medianWidth: 2,
1133
+ states: {
1134
+ hover: {
1135
+ brightness: -0.3
1136
+ }
1137
+ },
1138
+ //stemColor: null,
1139
+ //stemDashStyle: 'solid'
1140
+ //stemWidth: null,
1141
+ threshold: null,
1142
+ tooltip: {
1143
+ pointFormat: '<span style="color:{series.color};font-weight:bold">{series.name}</span><br/>' +
1144
+ 'Maximum: {point.high}<br/>' +
1145
+ 'Upper quartile: {point.q3}<br/>' +
1146
+ 'Median: {point.median}<br/>' +
1147
+ 'Lower quartile: {point.q1}<br/>' +
1148
+ 'Minimum: {point.low}<br/>'
1149
+
1150
+ },
1151
+ //whiskerColor: null,
1152
+ whiskerLength: '50%',
1153
+ whiskerWidth: 2
1154
+ });
1155
+
1156
+ // Create the series object
1157
+ seriesTypes.boxplot = extendClass(seriesTypes.column, {
1158
+ type: 'boxplot',
1159
+ pointArrayMap: ['low', 'q1', 'median', 'q3', 'high'], // array point configs are mapped to this
1160
+ toYData: function (point) { // return a plain array for speedy calculation
1161
+ return [point.low, point.q1, point.median, point.q3, point.high];
1162
+ },
1163
+ pointValKey: 'high', // defines the top of the tracker
1164
+
1165
+ /**
1166
+ * One-to-one mapping from options to SVG attributes
1167
+ */
1168
+ pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
1169
+ fill: 'fillColor',
1170
+ stroke: 'color',
1171
+ 'stroke-width': 'lineWidth'
1172
+ },
1173
+
1174
+ /**
1175
+ * Disable data labels for box plot
1176
+ */
1177
+ drawDataLabels: noop,
1178
+
1179
+ /**
1180
+ * Translate data points from raw values x and y to plotX and plotY
1181
+ */
1182
+ translate: function () {
1183
+ var series = this,
1184
+ yAxis = series.yAxis,
1185
+ pointArrayMap = series.pointArrayMap;
1186
+
1187
+ seriesTypes.column.prototype.translate.apply(series);
1188
+
1189
+ // do the translation on each point dimension
1190
+ each(series.points, function (point) {
1191
+ each(pointArrayMap, function (key) {
1192
+ if (point[key] !== null) {
1193
+ point[key + 'Plot'] = yAxis.translate(point[key], 0, 1, 0, 1);
1194
+ }
1195
+ });
1196
+ });
1197
+ },
1198
+
1199
+ /**
1200
+ * Draw the data points
1201
+ */
1202
+ drawPoints: function () {
1203
+ var series = this, //state = series.state,
1204
+ points = series.points,
1205
+ options = series.options,
1206
+ chart = series.chart,
1207
+ renderer = chart.renderer,
1208
+ pointAttr,
1209
+ q1Plot,
1210
+ q3Plot,
1211
+ highPlot,
1212
+ lowPlot,
1213
+ medianPlot,
1214
+ crispCorr,
1215
+ crispX,
1216
+ graphic,
1217
+ stemPath,
1218
+ stemAttr,
1219
+ boxPath,
1220
+ whiskersPath,
1221
+ whiskersAttr,
1222
+ medianPath,
1223
+ medianAttr,
1224
+ width,
1225
+ left,
1226
+ right,
1227
+ halfWidth,
1228
+ shapeArgs,
1229
+ color,
1230
+ doQuartiles = series.doQuartiles !== false, // error bar inherits this series type but doesn't do quartiles
1231
+ whiskerLength = parseInt(series.options.whiskerLength, 10) / 100;
1232
+
1233
+
1234
+ each(points, function (point) {
1235
+
1236
+ graphic = point.graphic;
1237
+ shapeArgs = point.shapeArgs; // the box
1238
+ stemAttr = {};
1239
+ whiskersAttr = {};
1240
+ medianAttr = {};
1241
+ color = point.color || series.color;
1242
+
1243
+ if (point.plotY !== UNDEFINED) {
1244
+
1245
+ pointAttr = point.pointAttr[point.selected ? 'selected' : ''];
1246
+
1247
+ // crisp vector coordinates
1248
+ width = shapeArgs.width;
1249
+ left = mathFloor(shapeArgs.x);
1250
+ right = left + width;
1251
+ halfWidth = mathRound(width / 2);
1252
+ //crispX = mathRound(left + halfWidth) + crispCorr;
1253
+ q1Plot = mathFloor(doQuartiles ? point.q1Plot : point.lowPlot);// + crispCorr;
1254
+ q3Plot = mathFloor(doQuartiles ? point.q3Plot : point.lowPlot);// + crispCorr;
1255
+ highPlot = mathFloor(point.highPlot);// + crispCorr;
1256
+ lowPlot = mathFloor(point.lowPlot);// + crispCorr;
1257
+
1258
+ // Stem attributes
1259
+ stemAttr.stroke = point.stemColor || options.stemColor || color;
1260
+ stemAttr['stroke-width'] = pick(point.stemWidth, options.stemWidth, options.lineWidth);
1261
+ stemAttr.dashstyle = point.stemDashStyle || options.stemDashStyle;
1262
+
1263
+ // Whiskers attributes
1264
+ whiskersAttr.stroke = point.whiskerColor || options.whiskerColor || color;
1265
+ whiskersAttr['stroke-width'] = pick(point.whiskerWidth, options.whiskerWidth, options.lineWidth);
1266
+
1267
+ // Median attributes
1268
+ medianAttr.stroke = point.medianColor || options.medianColor || color;
1269
+ medianAttr['stroke-width'] = pick(point.medianWidth, options.medianWidth, options.lineWidth);
1270
+ medianAttr['stroke-linecap'] = 'round'; // #1638
1271
+
1272
+
1273
+ // The stem
1274
+ crispCorr = (stemAttr['stroke-width'] % 2) / 2;
1275
+ crispX = left + halfWidth + crispCorr;
1276
+ stemPath = [
1277
+ // stem up
1278
+ 'M',
1279
+ crispX, q3Plot,
1280
+ 'L',
1281
+ crispX, highPlot,
1282
+
1283
+ // stem down
1284
+ 'M',
1285
+ crispX, q1Plot,
1286
+ 'L',
1287
+ crispX, lowPlot,
1288
+ 'z'
1289
+ ];
1290
+
1291
+ // The box
1292
+ if (doQuartiles) {
1293
+ crispCorr = (pointAttr['stroke-width'] % 2) / 2;
1294
+ crispX = mathFloor(crispX) + crispCorr;
1295
+ q1Plot = mathFloor(q1Plot) + crispCorr;
1296
+ q3Plot = mathFloor(q3Plot) + crispCorr;
1297
+ left += crispCorr;
1298
+ right += crispCorr;
1299
+ boxPath = [
1300
+ 'M',
1301
+ left, q3Plot,
1302
+ 'L',
1303
+ left, q1Plot,
1304
+ 'L',
1305
+ right, q1Plot,
1306
+ 'L',
1307
+ right, q3Plot,
1308
+ 'L',
1309
+ left, q3Plot,
1310
+ 'z'
1311
+ ];
1312
+ }
1313
+
1314
+ // The whiskers
1315
+ if (whiskerLength) {
1316
+ crispCorr = (whiskersAttr['stroke-width'] % 2) / 2;
1317
+ highPlot = highPlot + crispCorr;
1318
+ lowPlot = lowPlot + crispCorr;
1319
+ whiskersPath = [
1320
+ // High whisker
1321
+ 'M',
1322
+ crispX - halfWidth * whiskerLength,
1323
+ highPlot,
1324
+ 'L',
1325
+ crispX + halfWidth * whiskerLength,
1326
+ highPlot,
1327
+
1328
+ // Low whisker
1329
+ 'M',
1330
+ crispX - halfWidth * whiskerLength,
1331
+ lowPlot,
1332
+ 'L',
1333
+ crispX + halfWidth * whiskerLength,
1334
+ lowPlot
1335
+ ];
1336
+ }
1337
+
1338
+ // The median
1339
+ crispCorr = (medianAttr['stroke-width'] % 2) / 2;
1340
+ medianPlot = mathRound(point.medianPlot) + crispCorr;
1341
+ medianPath = [
1342
+ 'M',
1343
+ left,
1344
+ medianPlot,
1345
+ 'L',
1346
+ right,
1347
+ medianPlot,
1348
+ 'z'
1349
+ ];
1350
+
1351
+ // Create or update the graphics
1352
+ if (graphic) { // update
1353
+
1354
+ point.stem.animate({ d: stemPath });
1355
+ if (whiskerLength) {
1356
+ point.whiskers.animate({ d: whiskersPath });
1357
+ }
1358
+ if (doQuartiles) {
1359
+ point.box.animate({ d: boxPath });
1360
+ }
1361
+ point.medianShape.animate({ d: medianPath });
1362
+
1363
+ } else { // create new
1364
+ point.graphic = graphic = renderer.g()
1365
+ .add(series.group);
1366
+
1367
+ point.stem = renderer.path(stemPath)
1368
+ .attr(stemAttr)
1369
+ .add(graphic);
1370
+
1371
+ if (whiskerLength) {
1372
+ point.whiskers = renderer.path(whiskersPath)
1373
+ .attr(whiskersAttr)
1374
+ .add(graphic);
1375
+ }
1376
+ if (doQuartiles) {
1377
+ point.box = renderer.path(boxPath)
1378
+ .attr(pointAttr)
1379
+ .add(graphic);
1380
+ }
1381
+ point.medianShape = renderer.path(medianPath)
1382
+ .attr(medianAttr)
1383
+ .add(graphic);
1384
+ }
1385
+ }
1386
+ });
1387
+
1388
+ }
1389
+
1390
+
1391
+ });
1392
+
1393
+ /* ****************************************************************************
1394
+ * End Box plot series code *
1395
+ *****************************************************************************/
1396
+ /* ****************************************************************************
1397
+ * Start error bar series code *
1398
+ *****************************************************************************/
1399
+
1400
+ // 1 - set default options
1401
+ defaultPlotOptions.errorbar = merge(defaultPlotOptions.boxplot, {
1402
+ color: '#000000',
1403
+ grouping: false,
1404
+ linkedTo: ':previous',
1405
+ tooltip: {
1406
+ pointFormat: defaultPlotOptions.arearange.tooltip.pointFormat
1407
+ },
1408
+ whiskerWidth: null
1409
+ });
1410
+
1411
+ // 2 - Create the series object
1412
+ seriesTypes.errorbar = extendClass(seriesTypes.boxplot, {
1413
+ type: 'errorbar',
1414
+ pointArrayMap: ['low', 'high'], // array point configs are mapped to this
1415
+ toYData: function (point) { // return a plain array for speedy calculation
1416
+ return [point.low, point.high];
1417
+ },
1418
+ pointValKey: 'high', // defines the top of the tracker
1419
+ doQuartiles: false,
1420
+
1421
+ /**
1422
+ * Get the width and X offset, either on top of the linked series column
1423
+ * or standalone
1424
+ */
1425
+ getColumnMetrics: function () {
1426
+ return (this.linkedParent && this.linkedParent.columnMetrics) ||
1427
+ seriesTypes.column.prototype.getColumnMetrics.call(this);
1428
+ }
1429
+ });
1430
+
1431
+ /* ****************************************************************************
1432
+ * End error bar series code *
1433
+ *****************************************************************************/
1434
+ /* ****************************************************************************
1435
+ * Start Waterfall series code *
1436
+ *****************************************************************************/
1437
+
1438
+ // 1 - set default options
1439
+ defaultPlotOptions.waterfall = merge(defaultPlotOptions.column, {
1440
+ lineWidth: 1,
1441
+ lineColor: '#333',
1442
+ dashStyle: 'dot',
1443
+ borderColor: '#333'
1444
+ });
1445
+
1446
+
1447
+ // 2 - Create the series object
1448
+ seriesTypes.waterfall = extendClass(seriesTypes.column, {
1449
+ type: 'waterfall',
1450
+
1451
+ upColorProp: 'fill',
1452
+
1453
+ pointArrayMap: ['low', 'y'],
1454
+
1455
+ pointValKey: 'y',
1456
+
1457
+ /**
1458
+ * Init waterfall series, force stacking
1459
+ */
1460
+ init: function (chart, options) {
1461
+ // force stacking
1462
+ options.stacking = true;
1463
+
1464
+ seriesTypes.column.prototype.init.call(this, chart, options);
1465
+ },
1466
+
1467
+
1468
+ /**
1469
+ * Translate data points from raw values
1470
+ */
1471
+ translate: function () {
1472
+ var series = this,
1473
+ options = series.options,
1474
+ axis = series.yAxis,
1475
+ len,
1476
+ i,
1477
+ points,
1478
+ point,
1479
+ shapeArgs,
1480
+ stack,
1481
+ y,
1482
+ previousY,
1483
+ stackPoint,
1484
+ threshold = options.threshold,
1485
+ crispCorr = (options.borderWidth % 2) / 2;
1486
+
1487
+ // run column series translate
1488
+ seriesTypes.column.prototype.translate.apply(this);
1489
+
1490
+ previousY = threshold;
1491
+ points = series.points;
1492
+
1493
+ for (i = 0, len = points.length; i < len; i++) {
1494
+ // cache current point object
1495
+ point = points[i];
1496
+ shapeArgs = point.shapeArgs;
1497
+
1498
+ // get current stack
1499
+ stack = series.getStack(i);
1500
+ stackPoint = stack.points[series.index];
1501
+
1502
+ // override point value for sums
1503
+ if (isNaN(point.y)) {
1504
+ point.y = series.yData[i];
1505
+ }
1506
+
1507
+ // up points
1508
+ y = mathMax(previousY, previousY + point.y) + stackPoint[0];
1509
+ shapeArgs.y = axis.translate(y, 0, 1);
1510
+
1511
+
1512
+ // sum points
1513
+ if (point.isSum || point.isIntermediateSum) {
1514
+ shapeArgs.y = axis.translate(stackPoint[1], 0, 1);
1515
+ shapeArgs.height = axis.translate(stackPoint[0], 0, 1) - shapeArgs.y;
1516
+
1517
+ // if it's not the sum point, update previous stack end position
1518
+ } else {
1519
+ previousY += stack.total;
1520
+ }
1521
+
1522
+ // negative points
1523
+ if (shapeArgs.height < 0) {
1524
+ shapeArgs.y += shapeArgs.height;
1525
+ shapeArgs.height *= -1;
1526
+ }
1527
+
1528
+ point.plotY = shapeArgs.y = mathRound(shapeArgs.y) - crispCorr;
1529
+ shapeArgs.height = mathRound(shapeArgs.height);
1530
+ point.yBottom = shapeArgs.y + shapeArgs.height;
1531
+ }
1532
+ },
1533
+
1534
+ /**
1535
+ * Call default processData then override yData to reflect waterfall's extremes on yAxis
1536
+ */
1537
+ processData: function (force) {
1538
+ var series = this,
1539
+ options = series.options,
1540
+ yData = series.yData,
1541
+ points = series.points,
1542
+ point,
1543
+ dataLength = yData.length,
1544
+ threshold = options.threshold || 0,
1545
+ subSum,
1546
+ sum,
1547
+ dataMin,
1548
+ dataMax,
1549
+ y,
1550
+ i;
1551
+
1552
+ sum = subSum = dataMin = dataMax = threshold;
1553
+
1554
+ for (i = 0; i < dataLength; i++) {
1555
+ y = yData[i];
1556
+ point = points && points[i] ? points[i] : {};
1557
+
1558
+ if (y === "sum" || point.isSum) {
1559
+ yData[i] = sum;
1560
+ } else if (y === "intermediateSum" || point.isIntermediateSum) {
1561
+ yData[i] = subSum;
1562
+ subSum = threshold;
1563
+ } else {
1564
+ sum += y;
1565
+ subSum += y;
1566
+ }
1567
+ dataMin = Math.min(sum, dataMin);
1568
+ dataMax = Math.max(sum, dataMax);
1569
+ }
1570
+
1571
+ Series.prototype.processData.call(this, force);
1572
+
1573
+ // Record extremes
1574
+ series.dataMin = dataMin;
1575
+ series.dataMax = dataMax;
1576
+ },
1577
+
1578
+ /**
1579
+ * Return y value or string if point is sum
1580
+ */
1581
+ toYData: function (pt) {
1582
+ if (pt.isSum) {
1583
+ return "sum";
1584
+ } else if (pt.isIntermediateSum) {
1585
+ return "intermediateSum";
1586
+ }
1587
+
1588
+ return pt.y;
1589
+ },
1590
+
1591
+ /**
1592
+ * Postprocess mapping between options and SVG attributes
1593
+ */
1594
+ getAttribs: function () {
1595
+ seriesTypes.column.prototype.getAttribs.apply(this, arguments);
1596
+
1597
+ var series = this,
1598
+ options = series.options,
1599
+ stateOptions = options.states,
1600
+ upColor = options.upColor || series.color,
1601
+ hoverColor = Highcharts.Color(upColor).brighten(0.1).get(),
1602
+ seriesDownPointAttr = merge(series.pointAttr),
1603
+ upColorProp = series.upColorProp;
1604
+
1605
+ seriesDownPointAttr[''][upColorProp] = upColor;
1606
+ seriesDownPointAttr.hover[upColorProp] = stateOptions.hover.upColor || hoverColor;
1607
+ seriesDownPointAttr.select[upColorProp] = stateOptions.select.upColor || upColor;
1608
+
1609
+ each(series.points, function (point) {
1610
+ if (point.y > 0 && !point.color) {
1611
+ point.pointAttr = seriesDownPointAttr;
1612
+ point.color = upColor;
1613
+ }
1614
+ });
1615
+ },
1616
+
1617
+ /**
1618
+ * Draw columns' connector lines
1619
+ */
1620
+ getGraphPath: function () {
1621
+
1622
+ var data = this.data,
1623
+ length = data.length,
1624
+ lineWidth = this.options.lineWidth + this.options.borderWidth,
1625
+ normalizer = mathRound(lineWidth) % 2 / 2,
1626
+ path = [],
1627
+ M = 'M',
1628
+ L = 'L',
1629
+ prevArgs,
1630
+ pointArgs,
1631
+ i,
1632
+ d;
1633
+
1634
+ for (i = 1; i < length; i++) {
1635
+ pointArgs = data[i].shapeArgs;
1636
+ prevArgs = data[i - 1].shapeArgs;
1637
+
1638
+ d = [
1639
+ M,
1640
+ prevArgs.x + prevArgs.width, prevArgs.y + normalizer,
1641
+ L,
1642
+ pointArgs.x, prevArgs.y + normalizer
1643
+ ];
1644
+
1645
+ if (data[i - 1].y < 0) {
1646
+ d[2] += prevArgs.height;
1647
+ d[5] += prevArgs.height;
1648
+ }
1649
+
1650
+ path = path.concat(d);
1651
+ }
1652
+
1653
+ return path;
1654
+ },
1655
+
1656
+ /**
1657
+ * Extremes are recorded in processData
1658
+ */
1659
+ getExtremes: noop,
1660
+
1661
+ /**
1662
+ * Return stack for given index
1663
+ */
1664
+ getStack: function (i) {
1665
+ var axis = this.yAxis,
1666
+ stacks = axis.stacks,
1667
+ key = this.stackKey;
1668
+
1669
+ if (this.processedYData[i] < this.options.threshold) {
1670
+ key = '-' + key;
1671
+ }
1672
+
1673
+ return stacks[key][i];
1674
+ },
1675
+
1676
+ drawGraph: Series.prototype.drawGraph
1677
+ });
1678
+
1679
+ /* ****************************************************************************
1680
+ * End Waterfall series code *
1681
+ *****************************************************************************/
1682
+ /* ****************************************************************************
1683
+ * Start Bubble series code *
1684
+ *****************************************************************************/
1685
+
1686
+ // 1 - set default options
1687
+ defaultPlotOptions.bubble = merge(defaultPlotOptions.scatter, {
1688
+ dataLabels: {
1689
+ inside: true,
1690
+ style: {
1691
+ color: 'white',
1692
+ textShadow: '0px 0px 3px black'
1693
+ },
1694
+ verticalAlign: 'middle'
1695
+ },
1696
+ // displayNegative: true,
1697
+ marker: {
1698
+ // fillOpacity: 0.5,
1699
+ lineColor: null, // inherit from series.color
1700
+ lineWidth: 1
1701
+ },
1702
+ minSize: 8,
1703
+ maxSize: '20%',
1704
+ // negativeColor: null,
1705
+ // sizeBy: 'area'
1706
+ tooltip: {
1707
+ pointFormat: '({point.x}, {point.y}), Size: {point.z}'
1708
+ },
1709
+ turboThreshold: 0,
1710
+ zThreshold: 0
1711
+ });
1712
+
1713
+ // 2 - Create the series object
1714
+ seriesTypes.bubble = extendClass(seriesTypes.scatter, {
1715
+ type: 'bubble',
1716
+ pointArrayMap: ['y', 'z'],
1717
+ trackerGroups: ['group', 'dataLabelsGroup'],
1718
+ bubblePadding: true,
1719
+
1720
+ /**
1721
+ * Mapping between SVG attributes and the corresponding options
1722
+ */
1723
+ pointAttrToOptions: {
1724
+ stroke: 'lineColor',
1725
+ 'stroke-width': 'lineWidth',
1726
+ fill: 'fillColor'
1727
+ },
1728
+
1729
+ /**
1730
+ * Apply the fillOpacity to all fill positions
1731
+ */
1732
+ applyOpacity: function (fill) {
1733
+ var markerOptions = this.options.marker,
1734
+ fillOpacity = pick(markerOptions.fillOpacity, 0.5);
1735
+
1736
+ // When called from Legend.colorizeItem, the fill isn't predefined
1737
+ fill = fill || markerOptions.fillColor || this.color;
1738
+
1739
+ if (fillOpacity !== 1) {
1740
+ fill = Highcharts.Color(fill).setOpacity(fillOpacity).get('rgba');
1741
+ }
1742
+ return fill;
1743
+ },
1744
+
1745
+ /**
1746
+ * Extend the convertAttribs method by applying opacity to the fill
1747
+ */
1748
+ convertAttribs: function () {
1749
+ var obj = Series.prototype.convertAttribs.apply(this, arguments);
1750
+
1751
+ obj.fill = this.applyOpacity(obj.fill);
1752
+
1753
+ return obj;
1754
+ },
1755
+
1756
+ /**
1757
+ * Get the radius for each point based on the minSize, maxSize and each point's Z value. This
1758
+ * must be done prior to Series.translate because the axis needs to add padding in
1759
+ * accordance with the point sizes.
1760
+ */
1761
+ getRadii: function (zMin, zMax, minSize, maxSize) {
1762
+ var len,
1763
+ i,
1764
+ pos,
1765
+ zData = this.zData,
1766
+ radii = [],
1767
+ sizeByArea = this.options.sizeBy !== 'width',
1768
+ zRange;
1769
+
1770
+ // Set the shape type and arguments to be picked up in drawPoints
1771
+ for (i = 0, len = zData.length; i < len; i++) {
1772
+ zRange = zMax - zMin;
1773
+ pos = zRange > 0 ? // relative size, a number between 0 and 1
1774
+ (zData[i] - zMin) / (zMax - zMin) :
1775
+ 0.5;
1776
+ if (sizeByArea) {
1777
+ pos = Math.sqrt(pos);
1778
+ }
1779
+ radii.push(math.ceil(minSize + pos * (maxSize - minSize)) / 2);
1780
+ }
1781
+ this.radii = radii;
1782
+ },
1783
+
1784
+ /**
1785
+ * Perform animation on the bubbles
1786
+ */
1787
+ animate: function (init) {
1788
+ var animation = this.options.animation;
1789
+
1790
+ if (!init) { // run the animation
1791
+ each(this.points, function (point) {
1792
+ var graphic = point.graphic,
1793
+ shapeArgs = point.shapeArgs;
1794
+
1795
+ if (graphic && shapeArgs) {
1796
+ // start values
1797
+ graphic.attr('r', 1);
1798
+
1799
+ // animate
1800
+ graphic.animate({
1801
+ r: shapeArgs.r
1802
+ }, animation);
1803
+ }
1804
+ });
1805
+
1806
+ // delete this function to allow it only once
1807
+ this.animate = null;
1808
+ }
1809
+ },
1810
+
1811
+ /**
1812
+ * Extend the base translate method to handle bubble size
1813
+ */
1814
+ translate: function () {
1815
+
1816
+ var i,
1817
+ data = this.data,
1818
+ point,
1819
+ radius,
1820
+ radii = this.radii;
1821
+
1822
+ // Run the parent method
1823
+ seriesTypes.scatter.prototype.translate.call(this);
1824
+
1825
+ // Set the shape type and arguments to be picked up in drawPoints
1826
+ i = data.length;
1827
+
1828
+ while (i--) {
1829
+ point = data[i];
1830
+ radius = radii ? radii[i] : 0; // #1737
1831
+
1832
+ // Flag for negativeColor to be applied in Series.js
1833
+ point.negative = point.z < (this.options.zThreshold || 0);
1834
+
1835
+ if (radius >= this.minPxSize / 2) {
1836
+ // Shape arguments
1837
+ point.shapeType = 'circle';
1838
+ point.shapeArgs = {
1839
+ x: point.plotX,
1840
+ y: point.plotY,
1841
+ r: radius
1842
+ };
1843
+
1844
+ // Alignment box for the data label
1845
+ point.dlBox = {
1846
+ x: point.plotX - radius,
1847
+ y: point.plotY - radius,
1848
+ width: 2 * radius,
1849
+ height: 2 * radius
1850
+ };
1851
+ } else { // below zThreshold
1852
+ point.shapeArgs = point.plotY = point.dlBox = UNDEFINED; // #1691
1853
+ }
1854
+ }
1855
+ },
1856
+
1857
+ /**
1858
+ * Get the series' symbol in the legend
1859
+ *
1860
+ * @param {Object} legend The legend object
1861
+ * @param {Object} item The series (this) or point
1862
+ */
1863
+ drawLegendSymbol: function (legend, item) {
1864
+ var radius = pInt(legend.itemStyle.fontSize) / 2;
1865
+
1866
+ item.legendSymbol = this.chart.renderer.circle(
1867
+ radius,
1868
+ legend.baseline - radius,
1869
+ radius
1870
+ ).attr({
1871
+ zIndex: 3
1872
+ }).add(item.legendGroup);
1873
+ item.legendSymbol.isMarker = true;
1874
+
1875
+ },
1876
+
1877
+ drawPoints: seriesTypes.column.prototype.drawPoints,
1878
+ alignDataLabel: seriesTypes.column.prototype.alignDataLabel
1879
+ });
1880
+
1881
+ /**
1882
+ * Add logic to pad each axis with the amount of pixels
1883
+ * necessary to avoid the bubbles to overflow.
1884
+ */
1885
+ Axis.prototype.beforePadding = function () {
1886
+ var axis = this,
1887
+ axisLength = this.len,
1888
+ chart = this.chart,
1889
+ pxMin = 0,
1890
+ pxMax = axisLength,
1891
+ isXAxis = this.isXAxis,
1892
+ dataKey = isXAxis ? 'xData' : 'yData',
1893
+ min = this.min,
1894
+ extremes = {},
1895
+ smallestSize = math.min(chart.plotWidth, chart.plotHeight),
1896
+ zMin = Number.MAX_VALUE,
1897
+ zMax = -Number.MAX_VALUE,
1898
+ range = this.max - min,
1899
+ transA = axisLength / range,
1900
+ activeSeries = [];
1901
+
1902
+ // Handle padding on the second pass, or on redraw
1903
+ if (this.tickPositions) {
1904
+ each(this.series, function (series) {
1905
+
1906
+ var seriesOptions = series.options,
1907
+ zData;
1908
+
1909
+ if (series.bubblePadding && series.visible) {
1910
+
1911
+ // Correction for #1673
1912
+ axis.allowZoomOutside = true;
1913
+
1914
+ // Cache it
1915
+ activeSeries.push(series);
1916
+
1917
+ if (isXAxis) { // because X axis is evaluated first
1918
+
1919
+ // For each series, translate the size extremes to pixel values
1920
+ each(['minSize', 'maxSize'], function (prop) {
1921
+ var length = seriesOptions[prop],
1922
+ isPercent = /%$/.test(length);
1923
+
1924
+ length = pInt(length);
1925
+ extremes[prop] = isPercent ?
1926
+ smallestSize * length / 100 :
1927
+ length;
1928
+
1929
+ });
1930
+ series.minPxSize = extremes.minSize;
1931
+
1932
+ // Find the min and max Z
1933
+ zData = series.zData;
1934
+ if (zData.length) { // #1735
1935
+ zMin = math.min(
1936
+ zMin,
1937
+ math.max(
1938
+ arrayMin(zData),
1939
+ seriesOptions.displayNegative === false ? seriesOptions.zThreshold : -Number.MAX_VALUE
1940
+ )
1941
+ );
1942
+ zMax = math.max(zMax, arrayMax(zData));
1943
+ }
1944
+ }
1945
+ }
1946
+ });
1947
+
1948
+ each(activeSeries, function (series) {
1949
+
1950
+ var data = series[dataKey],
1951
+ i = data.length,
1952
+ radius;
1953
+
1954
+ if (isXAxis) {
1955
+ series.getRadii(zMin, zMax, extremes.minSize, extremes.maxSize);
1956
+ }
1957
+
1958
+ if (range > 0) {
1959
+ while (i--) {
1960
+ if (data[i] !== null) {
1961
+ radius = series.radii[i];
1962
+ pxMin = Math.min(((data[i] - min) * transA) - radius, pxMin);
1963
+ pxMax = Math.max(((data[i] - min) * transA) + radius, pxMax);
1964
+ }
1965
+ }
1966
+ }
1967
+ });
1968
+
1969
+ if (activeSeries.length && range > 0 && pick(this.options.min, this.userMin) === UNDEFINED && pick(this.options.max, this.userMax) === UNDEFINED) {
1970
+ pxMax -= axisLength;
1971
+ transA *= (axisLength + pxMin - pxMax) / axisLength;
1972
+ this.min += pxMin / transA;
1973
+ this.max += pxMax / transA;
1974
+ }
1975
+ }
1976
+ };
1977
+
1978
+ /* ****************************************************************************
1979
+ * End Bubble series code *
1980
+ *****************************************************************************/
1981
+ /**
1982
+ * Extensions for polar charts. Additionally, much of the geometry required for polar charts is
1983
+ * gathered in RadialAxes.js.
1984
+ *
1985
+ */
1986
+
1987
+ var seriesProto = Series.prototype,
1988
+ pointerProto = Highcharts.Pointer.prototype;
1989
+
1990
+
1991
+
1992
+ /**
1993
+ * Translate a point's plotX and plotY from the internal angle and radius measures to
1994
+ * true plotX, plotY coordinates
1995
+ */
1996
+ seriesProto.toXY = function (point) {
1997
+ var xy,
1998
+ chart = this.chart,
1999
+ plotX = point.plotX,
2000
+ plotY = point.plotY;
2001
+
2002
+ // Save rectangular plotX, plotY for later computation
2003
+ point.rectPlotX = plotX;
2004
+ point.rectPlotY = plotY;
2005
+
2006
+ // Record the angle in degrees for use in tooltip
2007
+ point.clientX = ((plotX / Math.PI * 180) + this.xAxis.pane.options.startAngle) % 360;
2008
+
2009
+ // Find the polar plotX and plotY
2010
+ xy = this.xAxis.postTranslate(point.plotX, this.yAxis.len - plotY);
2011
+ point.plotX = point.polarPlotX = xy.x - chart.plotLeft;
2012
+ point.plotY = point.polarPlotY = xy.y - chart.plotTop;
2013
+ };
2014
+
2015
+ /**
2016
+ * Order the tooltip points to get the mouse capture ranges correct. #1915.
2017
+ */
2018
+ seriesProto.orderTooltipPoints = function (points) {
2019
+ if (this.chart.polar) {
2020
+ points.sort(function (a, b) {
2021
+ return a.clientX - b.clientX;
2022
+ });
2023
+
2024
+ // Wrap mouse tracking around to capture movement on the segment to the left
2025
+ // of the north point (#1469, #2093).
2026
+ if (points[0]) {
2027
+ points[0].wrappedClientX = points[0].clientX + 360;
2028
+ points.push(points[0]);
2029
+ }
2030
+ }
2031
+ };
2032
+
2033
+
2034
+ /**
2035
+ * Add some special init logic to areas and areasplines
2036
+ */
2037
+ function initArea(proceed, chart, options) {
2038
+ proceed.call(this, chart, options);
2039
+ if (this.chart.polar) {
2040
+
2041
+ /**
2042
+ * Overridden method to close a segment path. While in a cartesian plane the area
2043
+ * goes down to the threshold, in the polar chart it goes to the center.
2044
+ */
2045
+ this.closeSegment = function (path) {
2046
+ var center = this.xAxis.center;
2047
+ path.push(
2048
+ 'L',
2049
+ center[0],
2050
+ center[1]
2051
+ );
2052
+ };
2053
+
2054
+ // Instead of complicated logic to draw an area around the inner area in a stack,
2055
+ // just draw it behind
2056
+ this.closedStacks = true;
2057
+ }
2058
+ }
2059
+ wrap(seriesTypes.area.prototype, 'init', initArea);
2060
+ wrap(seriesTypes.areaspline.prototype, 'init', initArea);
2061
+
2062
+
2063
+ /**
2064
+ * Overridden method for calculating a spline from one point to the next
2065
+ */
2066
+ wrap(seriesTypes.spline.prototype, 'getPointSpline', function (proceed, segment, point, i) {
2067
+
2068
+ var ret,
2069
+ smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc;
2070
+ denom = smoothing + 1,
2071
+ plotX,
2072
+ plotY,
2073
+ lastPoint,
2074
+ nextPoint,
2075
+ lastX,
2076
+ lastY,
2077
+ nextX,
2078
+ nextY,
2079
+ leftContX,
2080
+ leftContY,
2081
+ rightContX,
2082
+ rightContY,
2083
+ distanceLeftControlPoint,
2084
+ distanceRightControlPoint,
2085
+ leftContAngle,
2086
+ rightContAngle,
2087
+ jointAngle;
2088
+
2089
+
2090
+ if (this.chart.polar) {
2091
+
2092
+ plotX = point.plotX;
2093
+ plotY = point.plotY;
2094
+ lastPoint = segment[i - 1];
2095
+ nextPoint = segment[i + 1];
2096
+
2097
+ // Connect ends
2098
+ if (this.connectEnds) {
2099
+ if (!lastPoint) {
2100
+ lastPoint = segment[segment.length - 2]; // not the last but the second last, because the segment is already connected
2101
+ }
2102
+ if (!nextPoint) {
2103
+ nextPoint = segment[1];
2104
+ }
2105
+ }
2106
+
2107
+ // find control points
2108
+ if (lastPoint && nextPoint) {
2109
+
2110
+ lastX = lastPoint.plotX;
2111
+ lastY = lastPoint.plotY;
2112
+ nextX = nextPoint.plotX;
2113
+ nextY = nextPoint.plotY;
2114
+ leftContX = (smoothing * plotX + lastX) / denom;
2115
+ leftContY = (smoothing * plotY + lastY) / denom;
2116
+ rightContX = (smoothing * plotX + nextX) / denom;
2117
+ rightContY = (smoothing * plotY + nextY) / denom;
2118
+ distanceLeftControlPoint = Math.sqrt(Math.pow(leftContX - plotX, 2) + Math.pow(leftContY - plotY, 2));
2119
+ distanceRightControlPoint = Math.sqrt(Math.pow(rightContX - plotX, 2) + Math.pow(rightContY - plotY, 2));
2120
+ leftContAngle = Math.atan2(leftContY - plotY, leftContX - plotX);
2121
+ rightContAngle = Math.atan2(rightContY - plotY, rightContX - plotX);
2122
+ jointAngle = (Math.PI / 2) + ((leftContAngle + rightContAngle) / 2);
2123
+
2124
+
2125
+ // Ensure the right direction, jointAngle should be in the same quadrant as leftContAngle
2126
+ if (Math.abs(leftContAngle - jointAngle) > Math.PI / 2) {
2127
+ jointAngle -= Math.PI;
2128
+ }
2129
+
2130
+ // Find the corrected control points for a spline straight through the point
2131
+ leftContX = plotX + Math.cos(jointAngle) * distanceLeftControlPoint;
2132
+ leftContY = plotY + Math.sin(jointAngle) * distanceLeftControlPoint;
2133
+ rightContX = plotX + Math.cos(Math.PI + jointAngle) * distanceRightControlPoint;
2134
+ rightContY = plotY + Math.sin(Math.PI + jointAngle) * distanceRightControlPoint;
2135
+
2136
+ // Record for drawing in next point
2137
+ point.rightContX = rightContX;
2138
+ point.rightContY = rightContY;
2139
+
2140
+ }
2141
+
2142
+
2143
+ // moveTo or lineTo
2144
+ if (!i) {
2145
+ ret = ['M', plotX, plotY];
2146
+ } else { // curve from last point to this
2147
+ ret = [
2148
+ 'C',
2149
+ lastPoint.rightContX || lastPoint.plotX,
2150
+ lastPoint.rightContY || lastPoint.plotY,
2151
+ leftContX || plotX,
2152
+ leftContY || plotY,
2153
+ plotX,
2154
+ plotY
2155
+ ];
2156
+ lastPoint.rightContX = lastPoint.rightContY = null; // reset for updating series later
2157
+ }
2158
+
2159
+
2160
+ } else {
2161
+ ret = proceed.call(this, segment, point, i);
2162
+ }
2163
+ return ret;
2164
+ });
2165
+
2166
+ /**
2167
+ * Extend translate. The plotX and plotY values are computed as if the polar chart were a
2168
+ * cartesian plane, where plotX denotes the angle in radians and (yAxis.len - plotY) is the pixel distance from
2169
+ * center.
2170
+ */
2171
+ wrap(seriesProto, 'translate', function (proceed) {
2172
+
2173
+ // Run uber method
2174
+ proceed.call(this);
2175
+
2176
+ // Postprocess plot coordinates
2177
+ if (this.chart.polar && !this.preventPostTranslate) {
2178
+ var points = this.points,
2179
+ i = points.length;
2180
+ while (i--) {
2181
+ // Translate plotX, plotY from angle and radius to true plot coordinates
2182
+ this.toXY(points[i]);
2183
+ }
2184
+ }
2185
+ });
2186
+
2187
+ /**
2188
+ * Extend getSegmentPath to allow connecting ends across 0 to provide a closed circle in
2189
+ * line-like series.
2190
+ */
2191
+ wrap(seriesProto, 'getSegmentPath', function (proceed, segment) {
2192
+
2193
+ var points = this.points;
2194
+
2195
+ // Connect the path
2196
+ if (this.chart.polar && this.options.connectEnds !== false &&
2197
+ segment[segment.length - 1] === points[points.length - 1] && points[0].y !== null) {
2198
+ this.connectEnds = true; // re-used in splines
2199
+ segment = [].concat(segment, [points[0]]);
2200
+ }
2201
+
2202
+ // Run uber method
2203
+ return proceed.call(this, segment);
2204
+
2205
+ });
2206
+
2207
+
2208
+ function polarAnimate(proceed, init) {
2209
+ var chart = this.chart,
2210
+ animation = this.options.animation,
2211
+ group = this.group,
2212
+ markerGroup = this.markerGroup,
2213
+ center = this.xAxis.center,
2214
+ plotLeft = chart.plotLeft,
2215
+ plotTop = chart.plotTop,
2216
+ attribs;
2217
+
2218
+ // Specific animation for polar charts
2219
+ if (chart.polar) {
2220
+
2221
+ // Enable animation on polar charts only in SVG. In VML, the scaling is different, plus animation
2222
+ // would be so slow it would't matter.
2223
+ if (chart.renderer.isSVG) {
2224
+
2225
+ if (animation === true) {
2226
+ animation = {};
2227
+ }
2228
+
2229
+ // Initialize the animation
2230
+ if (init) {
2231
+
2232
+ // Scale down the group and place it in the center
2233
+ attribs = {
2234
+ translateX: center[0] + plotLeft,
2235
+ translateY: center[1] + plotTop,
2236
+ scaleX: 0.001, // #1499
2237
+ scaleY: 0.001
2238
+ };
2239
+
2240
+ group.attr(attribs);
2241
+ if (markerGroup) {
2242
+ markerGroup.attrSetters = group.attrSetters;
2243
+ markerGroup.attr(attribs);
2244
+ }
2245
+
2246
+ // Run the animation
2247
+ } else {
2248
+ attribs = {
2249
+ translateX: plotLeft,
2250
+ translateY: plotTop,
2251
+ scaleX: 1,
2252
+ scaleY: 1
2253
+ };
2254
+ group.animate(attribs, animation);
2255
+ if (markerGroup) {
2256
+ markerGroup.animate(attribs, animation);
2257
+ }
2258
+
2259
+ // Delete this function to allow it only once
2260
+ this.animate = null;
2261
+ }
2262
+ }
2263
+
2264
+ // For non-polar charts, revert to the basic animation
2265
+ } else {
2266
+ proceed.call(this, init);
2267
+ }
2268
+ }
2269
+
2270
+ // Define the animate method for both regular series and column series and their derivatives
2271
+ wrap(seriesProto, 'animate', polarAnimate);
2272
+ wrap(colProto, 'animate', polarAnimate);
2273
+
2274
+
2275
+ /**
2276
+ * Throw in a couple of properties to let setTooltipPoints know we're indexing the points
2277
+ * in degrees (0-360), not plot pixel width.
2278
+ */
2279
+ wrap(seriesProto, 'setTooltipPoints', function (proceed, renew) {
2280
+
2281
+ if (this.chart.polar) {
2282
+ extend(this.xAxis, {
2283
+ tooltipLen: 360 // degrees are the resolution unit of the tooltipPoints array
2284
+ });
2285
+ }
2286
+
2287
+ // Run uber method
2288
+ return proceed.call(this, renew);
2289
+ });
2290
+
2291
+
2292
+ /**
2293
+ * Extend the column prototype's translate method
2294
+ */
2295
+ wrap(colProto, 'translate', function (proceed) {
2296
+
2297
+ var xAxis = this.xAxis,
2298
+ len = this.yAxis.len,
2299
+ center = xAxis.center,
2300
+ startAngleRad = xAxis.startAngleRad,
2301
+ renderer = this.chart.renderer,
2302
+ start,
2303
+ points,
2304
+ point,
2305
+ i;
2306
+
2307
+ this.preventPostTranslate = true;
2308
+
2309
+ // Run uber method
2310
+ proceed.call(this);
2311
+
2312
+ // Postprocess plot coordinates
2313
+ if (xAxis.isRadial) {
2314
+ points = this.points;
2315
+ i = points.length;
2316
+ while (i--) {
2317
+ point = points[i];
2318
+ start = point.barX + startAngleRad;
2319
+ point.shapeType = 'path';
2320
+ point.shapeArgs = {
2321
+ d: renderer.symbols.arc(
2322
+ center[0],
2323
+ center[1],
2324
+ len - point.plotY,
2325
+ null,
2326
+ {
2327
+ start: start,
2328
+ end: start + point.pointWidth,
2329
+ innerR: len - pick(point.yBottom, len)
2330
+ }
2331
+ )
2332
+ };
2333
+ this.toXY(point); // provide correct plotX, plotY for tooltip
2334
+ }
2335
+ }
2336
+ });
2337
+
2338
+
2339
+ /**
2340
+ * Align column data labels outside the columns. #1199.
2341
+ */
2342
+ wrap(colProto, 'alignDataLabel', function (proceed, point, dataLabel, options, alignTo, isNew) {
2343
+
2344
+ if (this.chart.polar) {
2345
+ var angle = point.rectPlotX / Math.PI * 180,
2346
+ align,
2347
+ verticalAlign;
2348
+
2349
+ // Align nicely outside the perimeter of the columns
2350
+ if (options.align === null) {
2351
+ if (angle > 20 && angle < 160) {
2352
+ align = 'left'; // right hemisphere
2353
+ } else if (angle > 200 && angle < 340) {
2354
+ align = 'right'; // left hemisphere
2355
+ } else {
2356
+ align = 'center'; // top or bottom
2357
+ }
2358
+ options.align = align;
2359
+ }
2360
+ if (options.verticalAlign === null) {
2361
+ if (angle < 45 || angle > 315) {
2362
+ verticalAlign = 'bottom'; // top part
2363
+ } else if (angle > 135 && angle < 225) {
2364
+ verticalAlign = 'top'; // bottom part
2365
+ } else {
2366
+ verticalAlign = 'middle'; // left or right
2367
+ }
2368
+ options.verticalAlign = verticalAlign;
2369
+ }
2370
+
2371
+ seriesProto.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
2372
+ } else {
2373
+ proceed.call(this, point, dataLabel, options, alignTo, isNew);
2374
+ }
2375
+
2376
+ });
2377
+
2378
+ /**
2379
+ * Extend the mouse tracker to return the tooltip position index in terms of
2380
+ * degrees rather than pixels
2381
+ */
2382
+ wrap(pointerProto, 'getIndex', function (proceed, e) {
2383
+ var ret,
2384
+ chart = this.chart,
2385
+ center,
2386
+ x,
2387
+ y;
2388
+
2389
+ if (chart.polar) {
2390
+ center = chart.xAxis[0].center;
2391
+ x = e.chartX - center[0] - chart.plotLeft;
2392
+ y = e.chartY - center[1] - chart.plotTop;
2393
+
2394
+ ret = 180 - Math.round(Math.atan2(x, y) / Math.PI * 180);
2395
+
2396
+ } else {
2397
+
2398
+ // Run uber method
2399
+ ret = proceed.call(this, e);
2400
+ }
2401
+ return ret;
2402
+ });
2403
+
2404
+ /**
2405
+ * Extend getCoordinates to prepare for polar axis values
2406
+ */
2407
+ wrap(pointerProto, 'getCoordinates', function (proceed, e) {
2408
+ var chart = this.chart,
2409
+ ret = {
2410
+ xAxis: [],
2411
+ yAxis: []
2412
+ };
2413
+
2414
+ if (chart.polar) {
2415
+
2416
+ each(chart.axes, function (axis) {
2417
+ var isXAxis = axis.isXAxis,
2418
+ center = axis.center,
2419
+ x = e.chartX - center[0] - chart.plotLeft,
2420
+ y = e.chartY - center[1] - chart.plotTop;
2421
+
2422
+ ret[isXAxis ? 'xAxis' : 'yAxis'].push({
2423
+ axis: axis,
2424
+ value: axis.translate(
2425
+ isXAxis ?
2426
+ Math.PI - Math.atan2(x, y) : // angle
2427
+ Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)), // distance from center
2428
+ true
2429
+ )
2430
+ });
2431
+ });
2432
+
2433
+ } else {
2434
+ ret = proceed.call(this, e);
2435
+ }
2436
+
2437
+ return ret;
2438
+ });
2439
+ }(Highcharts));