highcharts-js-rails 0.1.11 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,9 @@
1
+ ## v0.2.0 (2013-01-07) ##
2
+
3
+ * Allow block syntax for Highcharts.new.
4
+ * Allow a Hash to be passed to xAxis, yAxis, or series instead of an Array of 1 Hash.
5
+ * Update Highcharts to 2.3.5.
6
+
1
7
  ## v0.1.11 (2012-12-13) ##
2
8
 
3
9
  * Add support for series data point hashes. (Thanks tanelj!)
data/README.md CHANGED
@@ -39,14 +39,15 @@ Example
39
39
  Ruby:
40
40
 
41
41
  ```ruby
42
- chart = Highcharts.new
43
- chart.chart({:renderTo => 'graph'})
44
- chart.title('Highcharts Example')
45
- chart.xAxis([{:categories => ['October 12', 'October 13', 'October 14']}])
46
- chart.yAxis([{:title => 'Impressions', :min => 0}])
47
- chart.series([{:name => 'Impressions', :yAxis => 0, :type => 'line', :data => [100000, 122000, 127000]}])
48
- chart.legend({:layout => 'vertical', :align => 'right', :verticalAlign => 'top', :x => -10, :y => 100, :borderWidth => 0})
49
- chart.tooltip({:formatter => "function(){ return '<b>' + this.series.name + '</b><br/>' + this.x + ': ' + this.y; }"})
42
+ chart = Highcharts.new do |chart|
43
+ chart.chart(:renderTo => 'graph')
44
+ chart.title('Highcharts Example')
45
+ chart.xAxis(:categories => ['October 12', 'October 13', 'October 14'])
46
+ chart.yAxis(:title => 'Impressions', :min => 0)
47
+ chart.series(:name => 'Impressions', :yAxis => 0, :type => 'line', :data => [100000, 122000, 127000])
48
+ chart.legend(:layout => 'vertical', :align => 'right', :verticalAlign => 'top', :x => -10, :y => 100, :borderWidth => 0)
49
+ chart.tooltip(:formatter => "function(){ return '<b>' + this.series.name + '</b><br/>' + this.x + ': ' + this.y; }")
50
+ end
50
51
  ```
51
52
 
52
53
  HTML/ERB:
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = 'highcharts-js-rails'
6
- s.version = '0.1.11'
6
+ s.version = '0.2.0'
7
7
  s.authors = ['Alex Robbin']
8
8
  s.email = ['agrobbin@gmail.com']
9
9
  s.homepage = 'https://github.com/agrobbin/highcharts-js-rails'
@@ -16,6 +16,8 @@ class Highcharts < ActionView::Base
16
16
  'xAxis' => 'Axis::X',
17
17
  'yAxis' => 'Axis::Y'
18
18
  }
19
+
20
+ yield self
19
21
  end
20
22
 
21
23
  def inspect
@@ -30,7 +32,7 @@ class Highcharts < ActionView::Base
30
32
  # For xAxis, yAxis, and series, we need to take the array that is passed as the option
31
33
  # and for each value, instantiate a new class.
32
34
  @options[method] = if %w(xAxis yAxis series).include?(method.to_s)
33
- args.first.collect {|v| determine_method_class(method).constantize.new(v)}
35
+ Array.wrap(args.first).collect {|v| determine_method_class(method).constantize.new(v)}
34
36
  # For all others, just instantiate the class with the original arguments.
35
37
  else
36
38
  determine_method_class(method).constantize.new(*args)
@@ -1,35 +1,1581 @@
1
- /*
2
- Highcharts JS v2.3.3 (2012-10-04)
3
-
4
- (c) 2009-2011 Torstein Hønsi
5
-
6
- License: www.highcharts.com/license
7
- */
8
- (function(h,u){function z(a,b,c){this.init.call(this,a,b,c)}function A(a,b,c){a.call(this,b,c);if(this.chart.polar)this.closeSegment=function(a){var b=this.xAxis.center;a.push("L",b[0],b[1])},this.closedStacks=!0}function B(a,b){var c=this.chart,d=this.options.animation,e=this.group,g=this.markerGroup,f=this.xAxis.center,j=c.plotLeft,n=c.plotTop;if(c.polar){if(c.renderer.isSVG)if(d===!0&&(d={}),b){if(e.attrSetters.scaleX=e.attrSetters.scaleY=function(a,b){this[b]=a;this.scaleX!==u&&this.scaleY!==
9
- u&&this.element.setAttribute("transform","translate("+this.translateX+","+this.translateY+") scale("+this.scaleX+","+this.scaleY+")");return!1},c={translateX:f[0]+j,translateY:f[1]+n,scaleX:0,scaleY:0},e.attr(c),g)g.attrSetters=e.attrSetters,g.attr(c)}else c={translateX:j,translateY:n,scaleX:1,scaleY:1},e.animate(c,d),g&&g.animate(c,d),this.animate=null}else a.call(this,b)}var p=h.each,v=h.extend,o=h.merge,D=h.map,m=h.pick,w=h.pInt,k=h.getOptions().plotOptions,i=h.seriesTypes,E=h.extendClass,l=h.wrap,
10
- r=h.Axis,G=h.Tick,y=h.Series,q=i.column.prototype,s=function(){};v(z.prototype,{init:function(a,b,c){var d=this,e=d.defaultOptions;d.chart=b;if(b.angular)e.background={};d.options=a=o(e,a);(a=a.background)&&p([].concat(h.splat(a)).reverse(),function(a){var b=a.backgroundColor,a=o(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,
11
- 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,outerRadius:"105%"}});var x=r.prototype,r=G.prototype,H={getOffset:s,redraw:function(){this.isDirty=!1},render:function(){this.isDirty=!1},setScale:s,setCategories:s,setTitle:s},F={isRadial:!0,defaultRadialGaugeOptions:{labels:{align:"center",x:0,y:null},minorGridLineWidth:0,minorTickInterval:"auto",minorTickLength:10,minorTickPosition:"inside",
12
- minorTickWidth:1,plotBands:[],tickLength:10,tickPosition:"inside",tickWidth:2,title:{rotation:0},zIndex:2},defaultRadialXOptions:{gridLineWidth:1,labels:{align:null,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=o(this.defaultOptions,this.defaultRadialOptions,a)},getOffset:function(){x.getOffset.call(this);
13
- this.chart.axisOffset[this.side]=0;this.center=this.pane.center=i.pie.prototype.getCenter.call(this.pane)},getLinePath:function(a,b){var c=this.center,b=m(b,c[2]/2-this.offset);return this.chart.renderer.symbols.arc(this.left+c[0],this.top+c[1],b,b,{start:this.startAngleRad,end:this.endAngleRad,open:!0,innerR:0})},setAxisTranslation:function(){x.setAxisTranslation.call(this);if(this.center&&(this.transA=this.isCircular?(this.endAngleRad-this.startAngleRad)/(this.max-this.min||1):this.center[2]/2/
14
- (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&&1||this.pointRange||this.closestPointRange)},setAxisSize:function(){x.setAxisSize.call(this);if(this.center)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=
15
- this.translate(a),a=this.min;return this.postTranslate(this.translate(a),m(b,this.center[2]/2)-this.offset)},postTranslate:function(a,b){var c=this.chart,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,e=this.startAngleRad,g=d[2]/2,f=[m(c.outerRadius,"100%"),c.innerRadius,m(c.thickness,10)],j=/%$/,n,C=this.isCircular;this.options.gridLineInterpolation==="polygon"?d=this.getPlotLinePath(a).concat(this.getPlotLinePath(b,
16
- !0)):(C||(f[0]=this.translate(a),f[1]=this.translate(b)),f=D(f,function(a){j.test(a)&&(a=w(a,10)*g/100);return a}),c.shape==="circle"||!C?(a=-Math.PI/2,b=Math.PI*1.5,n=!0):(a=e+this.translate(a),b=e+this.translate(b)),d=this.chart.renderer.symbols.arc(this.left+d[0],this.top+d[1],f[0],f[0],{start:a,end:b,innerR:m(f[1],f[0]-f[2]),open:n}));return d},getPlotLinePath:function(a,b){var c=this.center,d=this.chart,e=this.getPosition(a),g,f,j;this.isCircular?j=["M",c[0]+d.plotLeft,c[1]+d.plotTop,"L",e.x,
17
- e.y]:this.options.gridLineInterpolation==="circle"?(a=this.translate(a))&&(j=this.getLinePath(0,a)):(g=d.xAxis[0],j=[],a=this.translate(a),c=g.tickPositions,g.autoConnect&&(c=c.concat([c[0]])),b&&(c=[].concat(c).reverse()),p(c,function(b,c){f=g.getPosition(b,a);j.push(c?"L":"M",f.x,f.y)}));return j},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)}}};l(x,"init",function(a,
18
- b,c){var d=this,e=b.angular,g=b.polar,f=c.isX,j=e&&f,n;if(e){if(v(this,j?H:F),n=!f)this.defaultRadialOptions=this.defaultRadialGaugeOptions}else if(g)v(this,F),this.defaultRadialOptions=(n=f)?this.defaultRadialXOptions:o(this.defaultYAxisOptions,this.defaultRadialYOptions);a.call(this,b,c);if(!j&&(e||g)){a=this.options;if(!b.panes)b.panes=D(h.splat(b.options.pane),function(a){return new z(a,b,d)});this.pane=e=b.panes[c.pane||0];g=e.options;b.inverted=!1;b.options.chart.zoomType=null;this.startAngleRad=
19
- e=(g.startAngle-90)*Math.PI/180;this.endAngleRad=g=(m(g.endAngle,g.startAngle+360)-90)*Math.PI/180;this.offset=a.offset||0;if((this.isCircular=n)&&c.max===u&&g-e===2*Math.PI)this.autoConnect=!0}});l(r,"getPosition",function(a,b,c,d,e){var g=this.axis;return g.getPosition?g.getPosition(c):a.call(this,b,c,d,e)});l(r,"getLabelPosition",function(a,b,c,d,e,g,f,j,n){var h=this.axis,i=g.y,l=g.align,k=(h.translate(this.pos)+h.startAngleRad+Math.PI/2)/Math.PI*180;h.isRadial?(a=h.getPosition(this.pos,h.center[2]/
20
- 2+m(g.distance,-25)),g.rotation==="auto"?d.attr({rotation:k}):i===null&&(i=w(d.styles.lineHeight)*0.9-d.getBBox().height/2),l===null&&(l=h.isCircular?k>20&&k<160?"left":k>200&&k<340?"right":"center":"center",d.attr({align:l})),a.x+=g.x,a.y+=i):a=a.call(this,b,c,d,e,g,f,j,n);return a});l(r,"getMarkPath",function(a,b,c,d,e,g,f){var j=this.axis;j.isRadial?(a=j.getPosition(this.pos,j.center[2]/2+d),b=["M",b,c,"L",a.x,a.y]):b=a.call(this,b,c,d,e,g,f);return b});k.arearange=o(k.area,{lineWidth:1,marker:null,
21
- threshold:null,tooltip:{pointFormat:'<span style="color:{series.color}">{series.name}</span>: <b>{point.low}</b> - <b>{point.high}</b><br/>'},trackByArea:!0,dataLabels:{verticalAlign:null,xLow:0,xHigh:0,yLow:0,yHigh:0},shadow:!1});r=h.extendClass(h.Point,{applyOptions:function(a,b){var c=this.series,d=c.pointArrayMap,e=0,g=0,f=d.length;if(typeof a==="object"&&typeof a.length!=="number")v(this,a),this.options=a;else if(a.length){if(a.length>f){if(typeof a[0]==="string")this.name=a[0];else if(typeof a[0]===
22
- "number")this.x=a[0];e++}for(;g<f;)this[d[g++]]=a[e++]}this.y=this[c.pointValKey];if(this.x===u&&c)this.x=b===u?c.autoIncrement():b;return this},toYData:function(){return[this.low,this.high]}});i.arearange=h.extendClass(i.area,{type:"arearange",pointArrayMap:["low","high"],pointClass:r,pointValKey:"low",translate:function(){var a=this.yAxis;i.area.prototype.translate.apply(this);p(this.points,function(b){if(b.y!==null)b.plotLow=b.plotY,b.plotHigh=a.translate(b.high,0,1,0,1)})},getSegmentPath:function(a){for(var b=
23
- [],c=a.length,d=y.prototype.getSegmentPath,e;c--;)e=a[c],b.push({plotX:e.plotX,plotY:e.plotHigh});a=d.call(this,a);d=d.call(this,b);b=[].concat(a,d);d[0]="L";this.areaPath=this.areaPath.concat(a,d);return b},drawDataLabels:function(){var a=this.data,b=a.length,c,d=[],e=y.prototype,g=this.options.dataLabels,f,j=this.chart.inverted;if(g.enabled||this._hasPointLabels){for(c=b;c--;)f=a[c],f.y=f.high,f.plotY=f.plotHigh,d[c]=f.dataLabel,f.dataLabel=f.dataLabelUpper,f.below=!1,j?(g.align="left",g.x=g.xHigh):
24
- g.y=g.yHigh;e.drawDataLabels.apply(this,arguments);for(c=b;c--;)f=a[c],f.dataLabelUpper=f.dataLabel,f.dataLabel=d[c],f.y=f.low,f.plotY=f.plotLow,f.below=!0,j?(g.align="right",g.x=g.xLow):g.y=g.yLow;e.drawDataLabels.apply(this,arguments)}},alignDataLabel:i.column.prototype.alignDataLabel,getSymbol:i.column.prototype.getSymbol,drawPoints:s});k.areasplinerange=o(k.arearange);i.areasplinerange=E(i.arearange,{type:"areasplinerange",getPointSpline:i.spline.prototype.getPointSpline});k.columnrange=o(k.column,
25
- k.arearange,{lineWidth:1,pointRange:null});i.columnrange=E(i.arearange,{type:"columnrange",translate:function(){var a=this.yAxis,b;q.translate.apply(this);p(this.points,function(c){var d=c.shapeArgs;c.plotHigh=b=a.translate(c.high,0,1,0,1);c.plotLow=c.plotY;d.y=b;d.height=c.plotY-b;c.trackerArgs=d})},drawGraph:s,pointAttrToOptions:q.pointAttrToOptions,drawPoints:q.drawPoints,drawTracker:q.drawTracker,animate:q.animate});k.gauge=o(k.line,{dataLabels:{enabled:!0,y:15,borderWidth:1,borderColor:"silver",
26
- borderRadius:3,style:{fontWeight:"bold"},verticalAlign:"top"},dial:{},pivot:{},tooltip:{headerFormat:""},showInLegend:!1});k={type:"gauge",pointClass:h.extendClass(h.Point,{setState:function(a){this.state=a}}),angular:!0,translate:function(){var a=this,b=a.yAxis,c=b.center;a.generatePoints();p(a.points,function(d){var e=o(a.options.dial,d.dial),g=w(m(e.radius,80))*c[2]/200,f=w(m(e.baseLength,70))*g/100,j=w(m(e.rearLength,10))*g/100,h=e.baseWidth||3,i=e.topWidth||1;d.shapeType="path";d.shapeArgs={d:e.path||
27
- ["M",-j,-h/2,"L",f,-h/2,g,-i/2,g,i/2,f,h/2,-j,h/2,"z"],translateX:c[0],translateY:c[1],rotation:(b.startAngleRad+b.translate(d.y))*180/Math.PI};d.plotX=c[0];d.plotY=c[1]})},drawPoints:function(){var a=this,b=a.yAxis.center,c=a.pivot,d=a.options,e=d.pivot;p(a.points,function(b){var c=b.graphic,e=b.shapeArgs,h=e.d,i=o(d.dial,b.dial);c?(c.animate(e),e.d=h):b.graphic=a.chart.renderer[b.shapeType](e).attr({stroke:i.borderColor||"none","stroke-width":i.borderWidth||0,fill:i.backgroundColor||"black",rotation:e.rotation}).add(a.group)});
28
- c?c.animate({cx:b[0],cy:b[1]}):a.pivot=a.chart.renderer.circle(b[0],b[1],m(e.radius,5)).attr({"stroke-width":e.borderWidth||0,stroke:e.borderColor||"silver",fill:e.backgroundColor||"black"}).add(a.group)},animate:function(){var a=this;p(a.points,function(b){var c=b.graphic;c&&(c.attr({rotation:a.yAxis.startAngleRad*180/Math.PI}),c.animate({rotation:b.shapeArgs.rotation},a.options.animation))});a.animate=null},render:function(){this.group=this.plotGroup("group","series",this.visible?"visible":"hidden",
29
- this.options.zIndex,this.chart.seriesGroup);i.pie.prototype.render.call(this);this.group.clip(this.chart.clipRect)},setData:i.pie.prototype.setData,drawTracker:i.column.prototype.drawTracker};i.gauge=h.extendClass(i.line,k);var t=y.prototype,k=h.MouseTracker.prototype;t.toXY=function(a){var b,c=this.chart;b=a.plotX;var d=a.plotY;a.rectPlotX=b;a.rectPlotY=d;a.deg=b/Math.PI*180;b=this.xAxis.postTranslate(a.plotX,this.yAxis.len-d);a.plotX=a.polarPlotX=b.x-c.plotLeft;a.plotY=a.polarPlotY=b.y-c.plotTop};
30
- l(i.area.prototype,"init",A);l(i.areaspline.prototype,"init",A);l(i.spline.prototype,"getPointSpline",function(a,b,c,d){var e,g,f,j,h,i,k;if(this.chart.polar){e=c.plotX;g=c.plotY;a=b[d-1];f=b[d+1];this.connectEnds&&(a||(a=b[b.length-2]),f||(f=b[1]));if(a&&f)j=a.plotX,h=a.plotY,b=f.plotX,i=f.plotY,j=(1.5*e+j)/2.5,h=(1.5*g+h)/2.5,f=(1.5*e+b)/2.5,k=(1.5*g+i)/2.5,b=Math.sqrt(Math.pow(j-e,2)+Math.pow(h-g,2)),i=Math.sqrt(Math.pow(f-e,2)+Math.pow(k-g,2)),j=Math.atan2(h-g,j-e),h=Math.atan2(k-g,f-e),k=Math.PI/
31
- 2+(j+h)/2,Math.abs(j-k)>Math.PI/2&&(k-=Math.PI),j=e+Math.cos(k)*b,h=g+Math.sin(k)*b,f=e+Math.cos(Math.PI+k)*i,k=g+Math.sin(Math.PI+k)*i,c.rightContX=f,c.rightContY=k;d?(c=["C",a.rightContX||a.plotX,a.rightContY||a.plotY,j||e,h||g,e,g],a.rightContX=a.rightContY=null):c=["M",e,g]}else c=a.call(this,b,c,d);return c});l(t,"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])});l(t,"getSegmentPath",function(a,b){var c=
32
- 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)});l(t,"animate",B);l(q,"animate",B);l(t,"setTooltipPoints",function(a,b){this.chart.polar&&v(this.xAxis,{tooltipLen:360,tooltipPosName:"deg"});return a.call(this,b)});l(q,"translate",function(a){var b=this.xAxis,c=this.yAxis.len,d=b.center,e=b.startAngleRad,g=this.chart.renderer,f,h;this.preventPostTranslate=!0;a.call(this);if(b.isRadial){b=
33
- this.points;for(h=b.length;h--;)f=b[h],a=f.barX+e,f.shapeType="path",f.shapeArgs={d:g.symbols.arc(d[0],d[1],c-f.plotY,null,{start:a,end:a+f.pointWidth,innerR:c-m(f.yBottom,c)})},this.toXY(f)}});l(q,"alignDataLabel",function(a,b,c,d,e,g){if(this.chart.polar){a=b.rectPlotX/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";t.alignDataLabel.call(this,b,c,d,e,g)}else a.call(this,
34
- b,c,d,e,g)});l(k,"getIndex",function(a,b){var c,d=this.chart,e;d.polar?(e=d.xAxis[0].center,c=b.chartX-e[0]-d.plotLeft,d=b.chartY-e[1]-d.plotTop,c=180-Math.round(Math.atan2(c,d)/Math.PI*180)):c=a.call(this,b);return c});l(k,"getMouseCoordinates",function(a,b){var c=this.chart,d={xAxis:[],yAxis:[]};c.polar?p(c.axes,function(a){var g=a.isXAxis,f=a.center,h=b.chartX-f[0]-c.plotLeft,f=b.chartY-f[1]-c.plotTop;d[g?"xAxis":"yAxis"].push({axis:a,value:a.translate(g?Math.PI-Math.atan2(h,f):Math.sqrt(Math.pow(h,
35
- 2)+Math.pow(f,2)),!0)})}):d=a.call(this,b);return d})})(Highcharts);
1
+ // ==ClosureCompiler==
2
+ // @compilation_level SIMPLE_OPTIMIZATIONS
3
+
4
+ /**
5
+ * @license Highcharts JS v2.3.5 (2012-12-19)
6
+ *
7
+ * (c) 2009-2011 Torstein Hønsi
8
+ *
9
+ * License: www.highcharts.com/license
10
+ */
11
+
12
+ // JSLint options:
13
+ /*global Highcharts, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console */
14
+
15
+ (function (Highcharts, UNDEFINED) {
16
+ var each = Highcharts.each,
17
+ extend = Highcharts.extend,
18
+ merge = Highcharts.merge,
19
+ map = Highcharts.map,
20
+ pick = Highcharts.pick,
21
+ pInt = Highcharts.pInt,
22
+ defaultPlotOptions = Highcharts.getOptions().plotOptions,
23
+ seriesTypes = Highcharts.seriesTypes,
24
+ extendClass = Highcharts.extendClass,
25
+ splat = Highcharts.splat,
26
+ wrap = Highcharts.wrap,
27
+ Axis = Highcharts.Axis,
28
+ Tick = Highcharts.Tick,
29
+ Series = Highcharts.Series,
30
+ colProto = seriesTypes.column.prototype,
31
+ noop = function () {};/**
32
+ * The Pane object allows options that are common to a set of X and Y axes.
33
+ *
34
+ * In the future, this can be extended to basic Highcharts and Highstock.
35
+ */
36
+ function Pane(options, chart, firstAxis) {
37
+ this.init.call(this, options, chart, firstAxis);
38
+ }
39
+
40
+ // Extend the Pane prototype
41
+ extend(Pane.prototype, {
42
+
43
+ /**
44
+ * Initiate the Pane object
45
+ */
46
+ init: function (options, chart, firstAxis) {
47
+ var pane = this,
48
+ backgroundOption,
49
+ defaultOptions = pane.defaultOptions;
50
+
51
+ pane.chart = chart;
52
+
53
+ // Set options
54
+ if (chart.angular) { // gauges
55
+ defaultOptions.background = {}; // gets extended by this.defaultBackgroundOptions
56
+ }
57
+ pane.options = options = merge(defaultOptions, options);
58
+
59
+ backgroundOption = options.background;
60
+
61
+ // To avoid having weighty logic to place, update and remove the backgrounds,
62
+ // push them to the first axis' plot bands and borrow the existing logic there.
63
+ if (backgroundOption) {
64
+ each([].concat(splat(backgroundOption)).reverse(), function (config) {
65
+ var backgroundColor = config.backgroundColor; // if defined, replace the old one (specific for gradients)
66
+ config = merge(pane.defaultBackgroundOptions, config);
67
+ if (backgroundColor) {
68
+ config.backgroundColor = backgroundColor;
69
+ }
70
+ config.color = config.backgroundColor; // due to naming in plotBands
71
+ firstAxis.options.plotBands.unshift(config);
72
+ });
73
+ }
74
+ },
75
+
76
+ /**
77
+ * The default options object
78
+ */
79
+ defaultOptions: {
80
+ // background: {conditional},
81
+ center: ['50%', '50%'],
82
+ size: '85%',
83
+ startAngle: 0
84
+ //endAngle: startAngle + 360
85
+ },
86
+
87
+ /**
88
+ * The default background options
89
+ */
90
+ defaultBackgroundOptions: {
91
+ shape: 'circle',
92
+ borderWidth: 1,
93
+ borderColor: 'silver',
94
+ backgroundColor: {
95
+ linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
96
+ stops: [
97
+ [0, '#FFF'],
98
+ [1, '#DDD']
99
+ ]
100
+ },
101
+ from: Number.MIN_VALUE, // corrected to axis min
102
+ innerRadius: 0,
103
+ to: Number.MAX_VALUE, // corrected to axis max
104
+ outerRadius: '105%'
105
+ }
106
+
107
+ });
108
+ var axisProto = Axis.prototype,
109
+ tickProto = Tick.prototype;
110
+
111
+ /**
112
+ * Augmented methods for the x axis in order to hide it completely, used for the X axis in gauges
113
+ */
114
+ var hiddenAxisMixin = {
115
+ getOffset: noop,
116
+ redraw: function () {
117
+ this.isDirty = false; // prevent setting Y axis dirty
118
+ },
119
+ render: function () {
120
+ this.isDirty = false; // prevent setting Y axis dirty
121
+ },
122
+ setScale: noop,
123
+ setCategories: noop,
124
+ setTitle: noop
125
+ };
126
+
127
+ /**
128
+ * Augmented methods for the value axis
129
+ */
130
+ /*jslint unparam: true*/
131
+ var radialAxisMixin = {
132
+ isRadial: true,
133
+
134
+ /**
135
+ * The default options extend defaultYAxisOptions
136
+ */
137
+ defaultRadialGaugeOptions: {
138
+ labels: {
139
+ align: 'center',
140
+ x: 0,
141
+ y: null // auto
142
+ },
143
+ minorGridLineWidth: 0,
144
+ minorTickInterval: 'auto',
145
+ minorTickLength: 10,
146
+ minorTickPosition: 'inside',
147
+ minorTickWidth: 1,
148
+ plotBands: [],
149
+ tickLength: 10,
150
+ tickPosition: 'inside',
151
+ tickWidth: 2,
152
+ title: {
153
+ rotation: 0
154
+ },
155
+ zIndex: 2 // behind dials, points in the series group
156
+ },
157
+
158
+ // Circular axis around the perimeter of a polar chart
159
+ defaultRadialXOptions: {
160
+ gridLineWidth: 1, // spokes
161
+ labels: {
162
+ align: null, // auto
163
+ distance: 15,
164
+ x: 0,
165
+ y: null // auto
166
+ },
167
+ maxPadding: 0,
168
+ minPadding: 0,
169
+ plotBands: [],
170
+ showLastLabel: false,
171
+ tickLength: 0
172
+ },
173
+
174
+ // Radial axis, like a spoke in a polar chart
175
+ defaultRadialYOptions: {
176
+ gridLineInterpolation: 'circle',
177
+ labels: {
178
+ align: 'right',
179
+ x: -3,
180
+ y: -2
181
+ },
182
+ plotBands: [],
183
+ showLastLabel: false,
184
+ title: {
185
+ x: 4,
186
+ text: null,
187
+ rotation: 90
188
+ }
189
+ },
190
+
191
+ /**
192
+ * Merge and set options
193
+ */
194
+ setOptions: function (userOptions) {
195
+
196
+ this.options = merge(
197
+ this.defaultOptions,
198
+ this.defaultRadialOptions,
199
+ userOptions
200
+ );
201
+
202
+ },
203
+
204
+ /**
205
+ * Wrap the getOffset method to return zero offset for title or labels in a radial
206
+ * axis
207
+ */
208
+ getOffset: function () {
209
+ // Call the Axis prototype method (the method we're in now is on the instance)
210
+ axisProto.getOffset.call(this);
211
+
212
+ // Title or label offsets are not counted
213
+ this.chart.axisOffset[this.side] = 0;
214
+
215
+ // Set the center array
216
+ this.center = this.pane.center = seriesTypes.pie.prototype.getCenter.call(this.pane);
217
+ },
218
+
219
+
220
+ /**
221
+ * Get the path for the axis line. This method is also referenced in the getPlotLinePath
222
+ * method.
223
+ */
224
+ getLinePath: function (lineWidth, radius) {
225
+ var center = this.center;
226
+ radius = pick(radius, center[2] / 2 - this.offset);
227
+
228
+ return this.chart.renderer.symbols.arc(
229
+ this.left + center[0],
230
+ this.top + center[1],
231
+ radius,
232
+ radius,
233
+ {
234
+ start: this.startAngleRad,
235
+ end: this.endAngleRad,
236
+ open: true,
237
+ innerR: 0
238
+ }
239
+ );
240
+ },
241
+
242
+ /**
243
+ * Override setAxisTranslation by setting the translation to the difference
244
+ * in rotation. This allows the translate method to return angle for
245
+ * any given value.
246
+ */
247
+ setAxisTranslation: function () {
248
+
249
+ // Call uber method
250
+ axisProto.setAxisTranslation.call(this);
251
+
252
+ // Set transA and minPixelPadding
253
+ if (this.center) { // it's not defined the first time
254
+ if (this.isCircular) {
255
+
256
+ this.transA = (this.endAngleRad - this.startAngleRad) /
257
+ ((this.max - this.min) || 1);
258
+
259
+
260
+ } else {
261
+ this.transA = (this.center[2] / 2) / ((this.max - this.min) || 1);
262
+ }
263
+
264
+ if (this.isXAxis) {
265
+ this.minPixelPadding = this.transA * this.minPointOffset +
266
+ (this.reversed ? (this.endAngleRad - this.startAngleRad) / 4 : 0); // ???
267
+ }
268
+ }
269
+ },
270
+
271
+ /**
272
+ * In case of auto connect, add one closestPointRange to the max value right before
273
+ * tickPositions are computed, so that ticks will extend passed the real max.
274
+ */
275
+ beforeSetTickPositions: function () {
276
+ if (this.autoConnect) {
277
+ this.max += (this.categories && 1) || this.pointRange || this.closestPointRange; // #1197
278
+ }
279
+ },
280
+
281
+ /**
282
+ * Override the setAxisSize method to use the arc's circumference as length. This
283
+ * allows tickPixelInterval to apply to pixel lengths along the perimeter
284
+ */
285
+ setAxisSize: function () {
286
+
287
+ axisProto.setAxisSize.call(this);
288
+
289
+ if (this.center) { // it's not defined the first time
290
+ this.len = this.width = this.height = this.isCircular ?
291
+ this.center[2] * (this.endAngleRad - this.startAngleRad) / 2 :
292
+ this.center[2] / 2;
293
+ }
294
+ },
295
+
296
+ /**
297
+ * Returns the x, y coordinate of a point given by a value and a pixel distance
298
+ * from center
299
+ */
300
+ getPosition: function (value, length) {
301
+ if (!this.isCircular) {
302
+ length = this.translate(value);
303
+ value = this.min;
304
+ }
305
+
306
+ return this.postTranslate(
307
+ this.translate(value),
308
+ pick(length, this.center[2] / 2) - this.offset
309
+ );
310
+ },
311
+
312
+ /**
313
+ * Translate from intermediate plotX (angle), plotY (axis.len - radius) to final chart coordinates.
314
+ */
315
+ postTranslate: function (angle, radius) {
316
+
317
+ var chart = this.chart,
318
+ center = this.center;
319
+
320
+ angle = this.startAngleRad + angle;
321
+
322
+ return {
323
+ x: chart.plotLeft + center[0] + Math.cos(angle) * radius,
324
+ y: chart.plotTop + center[1] + Math.sin(angle) * radius
325
+ };
326
+
327
+ },
328
+
329
+ /**
330
+ * Find the path for plot bands along the radial axis
331
+ */
332
+ getPlotBandPath: function (from, to, options) {
333
+ var center = this.center,
334
+ startAngleRad = this.startAngleRad,
335
+ fullRadius = center[2] / 2,
336
+ radii = [
337
+ pick(options.outerRadius, '100%'),
338
+ options.innerRadius,
339
+ pick(options.thickness, 10)
340
+ ],
341
+ percentRegex = /%$/,
342
+ start,
343
+ end,
344
+ open,
345
+ isCircular = this.isCircular, // X axis in a polar chart
346
+ ret;
347
+
348
+ // Polygonal plot bands
349
+ if (this.options.gridLineInterpolation === 'polygon') {
350
+ ret = this.getPlotLinePath(from).concat(this.getPlotLinePath(to, true));
351
+
352
+ // Circular grid bands
353
+ } else {
354
+
355
+ // Plot bands on Y axis (radial axis) - inner and outer radius depend on to and from
356
+ if (!isCircular) {
357
+ radii[0] = this.translate(from);
358
+ radii[1] = this.translate(to);
359
+ }
360
+
361
+ // Convert percentages to pixel values
362
+ radii = map(radii, function (radius) {
363
+ if (percentRegex.test(radius)) {
364
+ radius = (pInt(radius, 10) * fullRadius) / 100;
365
+ }
366
+ return radius;
367
+ });
368
+
369
+ // Handle full circle
370
+ if (options.shape === 'circle' || !isCircular) {
371
+ start = -Math.PI / 2;
372
+ end = Math.PI * 1.5;
373
+ open = true;
374
+ } else {
375
+ start = startAngleRad + this.translate(from);
376
+ end = startAngleRad + this.translate(to);
377
+ }
378
+
379
+
380
+ ret = this.chart.renderer.symbols.arc(
381
+ this.left + center[0],
382
+ this.top + center[1],
383
+ radii[0],
384
+ radii[0],
385
+ {
386
+ start: start,
387
+ end: end,
388
+ innerR: pick(radii[1], radii[0] - radii[2]),
389
+ open: open
390
+ }
391
+ );
392
+ }
393
+
394
+ return ret;
395
+ },
396
+
397
+ /**
398
+ * Find the path for plot lines perpendicular to the radial axis.
399
+ */
400
+ getPlotLinePath: function (value, reverse) {
401
+ var axis = this,
402
+ center = axis.center,
403
+ chart = axis.chart,
404
+ end = axis.getPosition(value),
405
+ xAxis,
406
+ xy,
407
+ tickPositions,
408
+ ret;
409
+
410
+ // Spokes
411
+ if (axis.isCircular) {
412
+ ret = ['M', center[0] + chart.plotLeft, center[1] + chart.plotTop, 'L', end.x, end.y];
413
+
414
+ // Concentric circles
415
+ } else if (axis.options.gridLineInterpolation === 'circle') {
416
+ value = axis.translate(value);
417
+ if (value) { // a value of 0 is in the center
418
+ ret = axis.getLinePath(0, value);
419
+ }
420
+ // Concentric polygons
421
+ } else {
422
+ xAxis = chart.xAxis[0];
423
+ ret = [];
424
+ value = axis.translate(value);
425
+ tickPositions = xAxis.tickPositions;
426
+ if (xAxis.autoConnect) {
427
+ tickPositions = tickPositions.concat([tickPositions[0]]);
428
+ }
429
+ // Reverse the positions for concatenation of polygonal plot bands
430
+ if (reverse) {
431
+ tickPositions = [].concat(tickPositions).reverse();
432
+ }
433
+
434
+ each(tickPositions, function (pos, i) {
435
+ xy = xAxis.getPosition(pos, value);
436
+ ret.push(i ? 'L' : 'M', xy.x, xy.y);
437
+ });
438
+
439
+ }
440
+ return ret;
441
+ },
442
+
443
+ /**
444
+ * Find the position for the axis title, by default inside the gauge
445
+ */
446
+ getTitlePosition: function () {
447
+ var center = this.center,
448
+ chart = this.chart,
449
+ titleOptions = this.options.title;
450
+
451
+ return {
452
+ x: chart.plotLeft + center[0] + (titleOptions.x || 0),
453
+ y: chart.plotTop + center[1] - ({ high: 0.5, middle: 0.25, low: 0 }[titleOptions.align] *
454
+ center[2]) + (titleOptions.y || 0)
455
+ };
456
+ }
457
+
458
+ };
459
+ /*jslint unparam: false*/
460
+
461
+ /**
462
+ * Override axisProto.init to mix in special axis instance functions and function overrides
463
+ */
464
+ wrap(axisProto, 'init', function (proceed, chart, userOptions) {
465
+ var axis = this,
466
+ angular = chart.angular,
467
+ polar = chart.polar,
468
+ isX = userOptions.isX,
469
+ isHidden = angular && isX,
470
+ isCircular,
471
+ startAngleRad,
472
+ endAngleRad,
473
+ options,
474
+ chartOptions = chart.options,
475
+ paneIndex = userOptions.pane || 0,
476
+ pane,
477
+ paneOptions;
478
+
479
+ // Before prototype.init
480
+ if (angular) {
481
+ extend(this, isHidden ? hiddenAxisMixin : radialAxisMixin);
482
+ isCircular = !isX;
483
+ if (isCircular) {
484
+ this.defaultRadialOptions = this.defaultRadialGaugeOptions;
485
+ }
486
+
487
+ } else if (polar) {
488
+ //extend(this, userOptions.isX ? radialAxisMixin : radialAxisMixin);
489
+ extend(this, radialAxisMixin);
490
+ isCircular = isX;
491
+ this.defaultRadialOptions = isX ? this.defaultRadialXOptions : merge(this.defaultYAxisOptions, this.defaultRadialYOptions);
492
+
493
+ }
494
+
495
+ // Run prototype.init
496
+ proceed.call(this, chart, userOptions);
497
+
498
+ if (!isHidden && (angular || polar)) {
499
+ options = this.options;
500
+
501
+ // Create the pane and set the pane options.
502
+ if (!chart.panes) {
503
+ chart.panes = [];
504
+ }
505
+ this.pane = chart.panes[paneIndex] = pane = new Pane(
506
+ splat(chartOptions.pane)[paneIndex],
507
+ chart,
508
+ axis
509
+ );
510
+ paneOptions = pane.options;
511
+
512
+
513
+ // Disable certain features on angular and polar axes
514
+ chart.inverted = false;
515
+ chartOptions.chart.zoomType = null;
516
+
517
+ // Start and end angle options are
518
+ // given in degrees relative to top, while internal computations are
519
+ // in radians relative to right (like SVG).
520
+ this.startAngleRad = startAngleRad = (paneOptions.startAngle - 90) * Math.PI / 180;
521
+ this.endAngleRad = endAngleRad = (pick(paneOptions.endAngle, paneOptions.startAngle + 360) - 90) * Math.PI / 180;
522
+ this.offset = options.offset || 0;
523
+
524
+ this.isCircular = isCircular;
525
+
526
+ // Automatically connect grid lines?
527
+ if (isCircular && userOptions.max === UNDEFINED && endAngleRad - startAngleRad === 2 * Math.PI) {
528
+ this.autoConnect = true;
529
+ }
530
+ }
531
+
532
+ });
533
+
534
+ /**
535
+ * Add special cases within the Tick class' methods for radial axes.
536
+ */
537
+ wrap(tickProto, 'getPosition', function (proceed, horiz, pos, tickmarkOffset, old) {
538
+ var axis = this.axis;
539
+
540
+ return axis.getPosition ?
541
+ axis.getPosition(pos) :
542
+ proceed.call(this, horiz, pos, tickmarkOffset, old);
543
+ });
544
+
545
+ /**
546
+ * Wrap the getLabelPosition function to find the center position of the label
547
+ * based on the distance option
548
+ */
549
+ wrap(tickProto, 'getLabelPosition', function (proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
550
+ var axis = this.axis,
551
+ optionsY = labelOptions.y,
552
+ ret,
553
+ align = labelOptions.align,
554
+ angle = (axis.translate(this.pos) + axis.startAngleRad + Math.PI / 2) / Math.PI * 180;
555
+
556
+ if (axis.isRadial) {
557
+ ret = axis.getPosition(this.pos, (axis.center[2] / 2) + pick(labelOptions.distance, -25));
558
+
559
+ // Automatically rotated
560
+ if (labelOptions.rotation === 'auto') {
561
+ label.attr({
562
+ rotation: angle
563
+ });
564
+
565
+ // Vertically centered
566
+ } else if (optionsY === null) {
567
+ optionsY = pInt(label.styles.lineHeight) * 0.9 - label.getBBox().height / 2;
568
+
569
+ }
570
+
571
+ // Automatic alignment
572
+ if (align === null) {
573
+ if (axis.isCircular) {
574
+ if (angle > 20 && angle < 160) {
575
+ align = 'left'; // right hemisphere
576
+ } else if (angle > 200 && angle < 340) {
577
+ align = 'right'; // left hemisphere
578
+ } else {
579
+ align = 'center'; // top or bottom
580
+ }
581
+ } else {
582
+ align = 'center';
583
+ }
584
+ label.attr({
585
+ align: align
586
+ });
587
+ }
588
+
589
+ ret.x += labelOptions.x;
590
+ ret.y += optionsY;
591
+
592
+ } else {
593
+ ret = proceed.call(this, x, y, label, horiz, labelOptions, tickmarkOffset, index, step);
594
+ }
595
+ return ret;
596
+ });
597
+
598
+ /**
599
+ * Wrap the getMarkPath function to return the path of the radial marker
600
+ */
601
+ wrap(tickProto, 'getMarkPath', function (proceed, x, y, tickLength, tickWidth, horiz, renderer) {
602
+ var axis = this.axis,
603
+ endPoint,
604
+ ret;
605
+
606
+ if (axis.isRadial) {
607
+ endPoint = axis.getPosition(this.pos, axis.center[2] / 2 + tickLength);
608
+ ret = [
609
+ 'M',
610
+ x,
611
+ y,
612
+ 'L',
613
+ endPoint.x,
614
+ endPoint.y
615
+ ];
616
+ } else {
617
+ ret = proceed.call(this, x, y, tickLength, tickWidth, horiz, renderer);
618
+ }
619
+ return ret;
620
+ });/*
621
+ * The AreaRangeSeries class
622
+ *
623
+ */
624
+
625
+ /**
626
+ * Extend the default options with map options
627
+ */
628
+ defaultPlotOptions.arearange = merge(defaultPlotOptions.area, {
629
+ lineWidth: 1,
630
+ marker: null,
631
+ threshold: null,
632
+ tooltip: {
633
+ pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.low}</b> - <b>{point.high}</b><br/>'
634
+ },
635
+ trackByArea: true,
636
+ dataLabels: {
637
+ verticalAlign: null,
638
+ xLow: 0,
639
+ xHigh: 0,
640
+ yLow: 0,
641
+ yHigh: 0
642
+ },
643
+ shadow: false
644
+ });
645
+
646
+ /**
647
+ * Extend the point object
648
+ */
649
+ var RangePoint = Highcharts.extendClass(Highcharts.Point, {
650
+ /**
651
+ * Apply the options containing the x and low/high data and possible some extra properties.
652
+ * This is called on point init or from point.update. Extends base Point by adding
653
+ * multiple y-like values.
654
+ *
655
+ * @param {Object} options
656
+ */
657
+ applyOptions: function (options, x) {
658
+ var point = this,
659
+ series = point.series,
660
+ pointArrayMap = series.pointArrayMap,
661
+ i = 0,
662
+ j = 0,
663
+ valueCount = pointArrayMap.length;
664
+
665
+
666
+ // object input
667
+ if (typeof options === 'object' && typeof options.length !== 'number') {
668
+
669
+ // copy options directly to point
670
+ extend(point, options);
671
+
672
+ point.options = options;
673
+
674
+ } else if (options.length) { // array
675
+ // with leading x value
676
+ if (options.length > valueCount) {
677
+ if (typeof options[0] === 'string') {
678
+ point.name = options[0];
679
+ } else if (typeof options[0] === 'number') {
680
+ point.x = options[0];
681
+ }
682
+ i++;
683
+ }
684
+ while (j < valueCount) {
685
+ point[pointArrayMap[j++]] = options[i++];
686
+ }
687
+ }
688
+
689
+ // Handle null and make low alias y
690
+ /*if (point.high === null) {
691
+ point.low = null;
692
+ }*/
693
+ point.y = point[series.pointValKey];
694
+
695
+ // If no x is set by now, get auto incremented value. All points must have an
696
+ // x value, however the y value can be null to create a gap in the series
697
+ if (point.x === UNDEFINED && series) {
698
+ point.x = x === UNDEFINED ? series.autoIncrement() : x;
699
+ }
700
+
701
+ return point;
702
+ },
703
+
704
+ /**
705
+ * Return a plain array for speedy calculation
706
+ */
707
+ toYData: function () {
708
+ return [this.low, this.high];
709
+ }
710
+ });
711
+
712
+ /**
713
+ * Add the series type
714
+ */
715
+ seriesTypes.arearange = Highcharts.extendClass(seriesTypes.area, {
716
+ type: 'arearange',
717
+ pointArrayMap: ['low', 'high'],
718
+ pointClass: RangePoint,
719
+ pointValKey: 'low',
720
+
721
+ /**
722
+ * Translate data points from raw values x and y to plotX and plotY
723
+ */
724
+ translate: function () {
725
+ var series = this,
726
+ yAxis = series.yAxis;
727
+
728
+ seriesTypes.area.prototype.translate.apply(series);
729
+
730
+ // Set plotLow and plotHigh
731
+ each(series.points, function (point) {
732
+
733
+ if (point.y !== null) {
734
+ point.plotLow = point.plotY;
735
+ point.plotHigh = yAxis.translate(point.high, 0, 1, 0, 1);
736
+ }
737
+ });
738
+ },
739
+
740
+ /**
741
+ * Extend the line series' getSegmentPath method by applying the segment
742
+ * path to both lower and higher values of the range
743
+ */
744
+ getSegmentPath: function (segment) {
745
+
746
+ var highSegment = [],
747
+ i = segment.length,
748
+ baseGetSegmentPath = Series.prototype.getSegmentPath,
749
+ point,
750
+ linePath,
751
+ lowerPath,
752
+ options = this.options,
753
+ step = options.step,
754
+ higherPath;
755
+
756
+ // Make a segment with plotX and plotY for the top values
757
+ while (i--) {
758
+ point = segment[i];
759
+ highSegment.push({
760
+ plotX: point.plotX,
761
+ plotY: point.plotHigh
762
+ });
763
+ }
764
+
765
+ // Get the paths
766
+ lowerPath = baseGetSegmentPath.call(this, segment);
767
+ if (step) {
768
+ if (step === true) {
769
+ step = 'left';
770
+ }
771
+ options.step = { left: 'right', center: 'center', right: 'left' }[step]; // swap for reading in getSegmentPath
772
+ }
773
+ higherPath = baseGetSegmentPath.call(this, highSegment);
774
+ options.step = step;
775
+
776
+ // Create a line on both top and bottom of the range
777
+ linePath = [].concat(lowerPath, higherPath);
778
+
779
+ // For the area path, we need to change the 'move' statement into 'lineTo' or 'curveTo'
780
+ higherPath[0] = 'L'; // this probably doesn't work for spline
781
+ this.areaPath = this.areaPath.concat(lowerPath, higherPath);
782
+
783
+ return linePath;
784
+ },
785
+
786
+ /**
787
+ * Extend the basic drawDataLabels method by running it for both lower and higher
788
+ * values.
789
+ */
790
+ drawDataLabels: function () {
791
+
792
+ var data = this.data,
793
+ length = data.length,
794
+ i,
795
+ originalDataLabels = [],
796
+ seriesProto = Series.prototype,
797
+ dataLabelOptions = this.options.dataLabels,
798
+ point,
799
+ inverted = this.chart.inverted;
800
+
801
+ if (dataLabelOptions.enabled || this._hasPointLabels) {
802
+
803
+ // Step 1: set preliminary values for plotY and dataLabel and draw the upper labels
804
+ i = length;
805
+ while (i--) {
806
+ point = data[i];
807
+
808
+ // Set preliminary values
809
+ point.y = point.high;
810
+ point.plotY = point.plotHigh;
811
+
812
+ // Store original data labels and set preliminary label objects to be picked up
813
+ // in the uber method
814
+ originalDataLabels[i] = point.dataLabel;
815
+ point.dataLabel = point.dataLabelUpper;
816
+
817
+ // Set the default offset
818
+ point.below = false;
819
+ if (inverted) {
820
+ dataLabelOptions.align = 'left';
821
+ dataLabelOptions.x = dataLabelOptions.xHigh;
822
+ } else {
823
+ dataLabelOptions.y = dataLabelOptions.yHigh;
824
+ }
825
+ }
826
+ seriesProto.drawDataLabels.apply(this, arguments); // #1209
827
+
828
+ // Step 2: reorganize and handle data labels for the lower values
829
+ i = length;
830
+ while (i--) {
831
+ point = data[i];
832
+
833
+ // Move the generated labels from step 1, and reassign the original data labels
834
+ point.dataLabelUpper = point.dataLabel;
835
+ point.dataLabel = originalDataLabels[i];
836
+
837
+ // Reset values
838
+ point.y = point.low;
839
+ point.plotY = point.plotLow;
840
+
841
+ // Set the default offset
842
+ point.below = true;
843
+ if (inverted) {
844
+ dataLabelOptions.align = 'right';
845
+ dataLabelOptions.x = dataLabelOptions.xLow;
846
+ } else {
847
+ dataLabelOptions.y = dataLabelOptions.yLow;
848
+ }
849
+ }
850
+ seriesProto.drawDataLabels.apply(this, arguments);
851
+ }
852
+
853
+ },
854
+
855
+ alignDataLabel: seriesTypes.column.prototype.alignDataLabel,
856
+
857
+ getSymbol: seriesTypes.column.prototype.getSymbol,
858
+
859
+ drawPoints: noop
860
+ });/**
861
+ * The AreaSplineRangeSeries class
862
+ */
863
+
864
+ defaultPlotOptions.areasplinerange = merge(defaultPlotOptions.arearange);
865
+
866
+ /**
867
+ * AreaSplineRangeSeries object
868
+ */
869
+ seriesTypes.areasplinerange = extendClass(seriesTypes.arearange, {
870
+ type: 'areasplinerange',
871
+ getPointSpline: seriesTypes.spline.prototype.getPointSpline
872
+ });/**
873
+ * The ColumnRangeSeries class
874
+ */
875
+ defaultPlotOptions.columnrange = merge(defaultPlotOptions.column, defaultPlotOptions.arearange, {
876
+ lineWidth: 1,
877
+ pointRange: null
878
+ });
879
+
880
+ /**
881
+ * ColumnRangeSeries object
882
+ */
883
+ seriesTypes.columnrange = extendClass(seriesTypes.arearange, {
884
+ type: 'columnrange',
885
+ /**
886
+ * Translate data points from raw values x and y to plotX and plotY
887
+ */
888
+ translate: function () {
889
+ var series = this,
890
+ yAxis = series.yAxis,
891
+ plotHigh;
892
+
893
+ colProto.translate.apply(series);
894
+
895
+ // Set plotLow and plotHigh
896
+ each(series.points, function (point) {
897
+ var shapeArgs = point.shapeArgs;
898
+
899
+ point.plotHigh = plotHigh = yAxis.translate(point.high, 0, 1, 0, 1);
900
+ point.plotLow = point.plotY;
901
+
902
+ // adjust shape
903
+ shapeArgs.y = plotHigh;
904
+ shapeArgs.height = point.plotY - plotHigh;
905
+
906
+ point.trackerArgs = shapeArgs;
907
+ });
908
+ },
909
+ drawGraph: noop,
910
+ pointAttrToOptions: colProto.pointAttrToOptions,
911
+ drawPoints: colProto.drawPoints,
912
+ drawTracker: colProto.drawTracker,
913
+ animate: colProto.animate
914
+ });/*
915
+ * The GaugeSeries class
916
+ */
917
+
918
+
919
+
920
+ /**
921
+ * Extend the default options
922
+ */
923
+ defaultPlotOptions.gauge = merge(defaultPlotOptions.line, {
924
+ dataLabels: {
925
+ enabled: true,
926
+ y: 15,
927
+ borderWidth: 1,
928
+ borderColor: 'silver',
929
+ borderRadius: 3,
930
+ style: {
931
+ fontWeight: 'bold'
932
+ },
933
+ verticalAlign: 'top',
934
+ zIndex: 2
935
+ },
936
+ dial: {
937
+ // radius: '80%',
938
+ // backgroundColor: 'black',
939
+ // borderColor: 'silver',
940
+ // borderWidth: 0,
941
+ // baseWidth: 3,
942
+ // topWidth: 1,
943
+ // baseLength: '70%' // of radius
944
+ // rearLength: '10%'
945
+ },
946
+ pivot: {
947
+ //radius: 5,
948
+ //borderWidth: 0
949
+ //borderColor: 'silver',
950
+ //backgroundColor: 'black'
951
+ },
952
+ tooltip: {
953
+ headerFormat: ''
954
+ },
955
+ showInLegend: false
956
+ });
957
+
958
+ /**
959
+ * Extend the point object
960
+ */
961
+ var GaugePoint = Highcharts.extendClass(Highcharts.Point, {
962
+ /**
963
+ * Don't do any hover colors or anything
964
+ */
965
+ setState: function (state) {
966
+ this.state = state;
967
+ }
968
+ });
969
+
970
+
971
+ /**
972
+ * Add the series type
973
+ */
974
+ var GaugeSeries = {
975
+ type: 'gauge',
976
+ pointClass: GaugePoint,
977
+
978
+ // chart.angular will be set to true when a gauge series is present, and this will
979
+ // be used on the axes
980
+ angular: true,
981
+
982
+ /* *
983
+ * Extend the bindAxes method by adding radial features to the axes
984
+ * /
985
+ _bindAxes: function () {
986
+ Series.prototype.bindAxes.call(this);
987
+
988
+ extend(this.xAxis, gaugeXAxisMixin);
989
+ extend(this.yAxis, radialAxisMixin);
990
+ this.yAxis.onBind();
991
+ },*/
992
+
993
+ /**
994
+ * Calculate paths etc
995
+ */
996
+ translate: function () {
997
+
998
+ var series = this,
999
+ yAxis = series.yAxis,
1000
+ center = yAxis.center;
1001
+
1002
+ series.generatePoints();
1003
+
1004
+ each(series.points, function (point) {
1005
+
1006
+ var dialOptions = merge(series.options.dial, point.dial),
1007
+ radius = (pInt(pick(dialOptions.radius, 80)) * center[2]) / 200,
1008
+ baseLength = (pInt(pick(dialOptions.baseLength, 70)) * radius) / 100,
1009
+ rearLength = (pInt(pick(dialOptions.rearLength, 10)) * radius) / 100,
1010
+ baseWidth = dialOptions.baseWidth || 3,
1011
+ topWidth = dialOptions.topWidth || 1;
1012
+
1013
+ point.shapeType = 'path';
1014
+ point.shapeArgs = {
1015
+ d: dialOptions.path || [
1016
+ 'M',
1017
+ -rearLength, -baseWidth / 2,
1018
+ 'L',
1019
+ baseLength, -baseWidth / 2,
1020
+ radius, -topWidth / 2,
1021
+ radius, topWidth / 2,
1022
+ baseLength, baseWidth / 2,
1023
+ -rearLength, baseWidth / 2,
1024
+ 'z'
1025
+ ],
1026
+ translateX: center[0],
1027
+ translateY: center[1],
1028
+ rotation: (yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true)) * 180 / Math.PI
1029
+ };
1030
+
1031
+ // Positions for data label
1032
+ point.plotX = center[0];
1033
+ point.plotY = center[1];
1034
+ });
1035
+ },
1036
+
1037
+ /**
1038
+ * Draw the points where each point is one needle
1039
+ */
1040
+ drawPoints: function () {
1041
+
1042
+ var series = this,
1043
+ center = series.yAxis.center,
1044
+ pivot = series.pivot,
1045
+ options = series.options,
1046
+ pivotOptions = options.pivot,
1047
+ renderer = series.chart.renderer;
1048
+
1049
+ each(series.points, function (point) {
1050
+
1051
+ var graphic = point.graphic,
1052
+ shapeArgs = point.shapeArgs,
1053
+ d = shapeArgs.d,
1054
+ dialOptions = merge(options.dial, point.dial); // #1233
1055
+
1056
+ if (graphic) {
1057
+ graphic.animate(shapeArgs);
1058
+ shapeArgs.d = d; // animate alters it
1059
+ } else {
1060
+ point.graphic = renderer[point.shapeType](shapeArgs)
1061
+ .attr({
1062
+ stroke: dialOptions.borderColor || 'none',
1063
+ 'stroke-width': dialOptions.borderWidth || 0,
1064
+ fill: dialOptions.backgroundColor || 'black',
1065
+ rotation: shapeArgs.rotation // required by VML when animation is false
1066
+ })
1067
+ .add(series.group);
1068
+ }
1069
+ });
1070
+
1071
+ // Add or move the pivot
1072
+ if (pivot) {
1073
+ pivot.animate({ // #1235
1074
+ translateX: center[0],
1075
+ translateY: center[1]
1076
+ });
1077
+ } else {
1078
+ series.pivot = renderer.circle(0, 0, pick(pivotOptions.radius, 5))
1079
+ .attr({
1080
+ 'stroke-width': pivotOptions.borderWidth || 0,
1081
+ stroke: pivotOptions.borderColor || 'silver',
1082
+ fill: pivotOptions.backgroundColor || 'black'
1083
+ })
1084
+ .translate(center[0], center[1])
1085
+ .add(series.group);
1086
+ }
1087
+ },
1088
+
1089
+ /**
1090
+ * Animate the arrow up from startAngle
1091
+ */
1092
+ animate: function () {
1093
+ var series = this;
1094
+
1095
+ each(series.points, function (point) {
1096
+ var graphic = point.graphic;
1097
+
1098
+ if (graphic) {
1099
+ // start value
1100
+ graphic.attr({
1101
+ rotation: series.yAxis.startAngleRad * 180 / Math.PI
1102
+ });
1103
+
1104
+ // animate
1105
+ graphic.animate({
1106
+ rotation: point.shapeArgs.rotation
1107
+ }, series.options.animation);
1108
+ }
1109
+ });
1110
+
1111
+ // delete this function to allow it only once
1112
+ series.animate = null;
1113
+ },
1114
+
1115
+ render: function () {
1116
+ this.group = this.plotGroup(
1117
+ 'group',
1118
+ 'series',
1119
+ this.visible ? 'visible' : 'hidden',
1120
+ this.options.zIndex,
1121
+ this.chart.seriesGroup
1122
+ );
1123
+ seriesTypes.pie.prototype.render.call(this);
1124
+ this.group.clip(this.chart.clipRect);
1125
+ },
1126
+
1127
+ setData: seriesTypes.pie.prototype.setData,
1128
+ drawTracker: seriesTypes.column.prototype.drawTracker
1129
+ };
1130
+ seriesTypes.gauge = Highcharts.extendClass(seriesTypes.line, GaugeSeries);/**
1131
+ * Extensions for polar charts. Additionally, much of the geometry required for polar charts is
1132
+ * gathered in RadialAxes.js.
1133
+ *
1134
+ */
1135
+
1136
+ var seriesProto = Series.prototype,
1137
+ mouseTrackerProto = Highcharts.MouseTracker.prototype;
1138
+
1139
+
1140
+
1141
+ /**
1142
+ * Translate a point's plotX and plotY from the internal angle and radius measures to
1143
+ * true plotX, plotY coordinates
1144
+ */
1145
+ seriesProto.toXY = function (point) {
1146
+ var xy,
1147
+ chart = this.chart,
1148
+ plotX = point.plotX,
1149
+ plotY = point.plotY;
1150
+
1151
+ // Save rectangular plotX, plotY for later computation
1152
+ point.rectPlotX = plotX;
1153
+ point.rectPlotY = plotY;
1154
+
1155
+ // Record the angle in degrees for use in tooltip
1156
+ point.deg = plotX / Math.PI * 180;
1157
+
1158
+ // Find the polar plotX and plotY
1159
+ xy = this.xAxis.postTranslate(point.plotX, this.yAxis.len - plotY);
1160
+ point.plotX = point.polarPlotX = xy.x - chart.plotLeft;
1161
+ point.plotY = point.polarPlotY = xy.y - chart.plotTop;
1162
+ };
1163
+
1164
+
1165
+ /**
1166
+ * Add some special init logic to areas and areasplines
1167
+ */
1168
+ function initArea(proceed, chart, options) {
1169
+ proceed.call(this, chart, options);
1170
+ if (this.chart.polar) {
1171
+
1172
+ /**
1173
+ * Overridden method to close a segment path. While in a cartesian plane the area
1174
+ * goes down to the threshold, in the polar chart it goes to the center.
1175
+ */
1176
+ this.closeSegment = function (path) {
1177
+ var center = this.xAxis.center;
1178
+ path.push(
1179
+ 'L',
1180
+ center[0],
1181
+ center[1]
1182
+ );
1183
+ };
1184
+
1185
+ // Instead of complicated logic to draw an area around the inner area in a stack,
1186
+ // just draw it behind
1187
+ this.closedStacks = true;
1188
+ }
1189
+ }
1190
+ wrap(seriesTypes.area.prototype, 'init', initArea);
1191
+ wrap(seriesTypes.areaspline.prototype, 'init', initArea);
1192
+
1193
+
1194
+ /**
1195
+ * Overridden method for calculating a spline from one point to the next
1196
+ */
1197
+ wrap(seriesTypes.spline.prototype, 'getPointSpline', function (proceed, segment, point, i) {
1198
+
1199
+ var ret,
1200
+ smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc;
1201
+ denom = smoothing + 1,
1202
+ plotX,
1203
+ plotY,
1204
+ lastPoint,
1205
+ nextPoint,
1206
+ lastX,
1207
+ lastY,
1208
+ nextX,
1209
+ nextY,
1210
+ leftContX,
1211
+ leftContY,
1212
+ rightContX,
1213
+ rightContY,
1214
+ distanceLeftControlPoint,
1215
+ distanceRightControlPoint,
1216
+ leftContAngle,
1217
+ rightContAngle,
1218
+ jointAngle;
1219
+
1220
+
1221
+ if (this.chart.polar) {
1222
+
1223
+ plotX = point.plotX;
1224
+ plotY = point.plotY;
1225
+ lastPoint = segment[i - 1];
1226
+ nextPoint = segment[i + 1];
1227
+
1228
+ // Connect ends
1229
+ if (this.connectEnds) {
1230
+ if (!lastPoint) {
1231
+ lastPoint = segment[segment.length - 2]; // not the last but the second last, because the segment is already connected
1232
+ }
1233
+ if (!nextPoint) {
1234
+ nextPoint = segment[1];
1235
+ }
1236
+ }
1237
+
1238
+ // find control points
1239
+ if (lastPoint && nextPoint) {
1240
+
1241
+ lastX = lastPoint.plotX;
1242
+ lastY = lastPoint.plotY;
1243
+ nextX = nextPoint.plotX;
1244
+ nextY = nextPoint.plotY;
1245
+ leftContX = (smoothing * plotX + lastX) / denom;
1246
+ leftContY = (smoothing * plotY + lastY) / denom;
1247
+ rightContX = (smoothing * plotX + nextX) / denom;
1248
+ rightContY = (smoothing * plotY + nextY) / denom;
1249
+ distanceLeftControlPoint = Math.sqrt(Math.pow(leftContX - plotX, 2) + Math.pow(leftContY - plotY, 2));
1250
+ distanceRightControlPoint = Math.sqrt(Math.pow(rightContX - plotX, 2) + Math.pow(rightContY - plotY, 2));
1251
+ leftContAngle = Math.atan2(leftContY - plotY, leftContX - plotX);
1252
+ rightContAngle = Math.atan2(rightContY - plotY, rightContX - plotX);
1253
+ jointAngle = (Math.PI / 2) + ((leftContAngle + rightContAngle) / 2);
1254
+
1255
+
1256
+ // Ensure the right direction, jointAngle should be in the same quadrant as leftContAngle
1257
+ if (Math.abs(leftContAngle - jointAngle) > Math.PI / 2) {
1258
+ jointAngle -= Math.PI;
1259
+ }
1260
+
1261
+ // Find the corrected control points for a spline straight through the point
1262
+ leftContX = plotX + Math.cos(jointAngle) * distanceLeftControlPoint;
1263
+ leftContY = plotY + Math.sin(jointAngle) * distanceLeftControlPoint;
1264
+ rightContX = plotX + Math.cos(Math.PI + jointAngle) * distanceRightControlPoint;
1265
+ rightContY = plotY + Math.sin(Math.PI + jointAngle) * distanceRightControlPoint;
1266
+
1267
+ // Record for drawing in next point
1268
+ point.rightContX = rightContX;
1269
+ point.rightContY = rightContY;
1270
+
1271
+ }
1272
+
1273
+
1274
+ // moveTo or lineTo
1275
+ if (!i) {
1276
+ ret = ['M', plotX, plotY];
1277
+ } else { // curve from last point to this
1278
+ ret = [
1279
+ 'C',
1280
+ lastPoint.rightContX || lastPoint.plotX,
1281
+ lastPoint.rightContY || lastPoint.plotY,
1282
+ leftContX || plotX,
1283
+ leftContY || plotY,
1284
+ plotX,
1285
+ plotY
1286
+ ];
1287
+ lastPoint.rightContX = lastPoint.rightContY = null; // reset for updating series later
1288
+ }
1289
+
1290
+
1291
+ } else {
1292
+ ret = proceed.call(this, segment, point, i);
1293
+ }
1294
+ return ret;
1295
+ });
1296
+
1297
+ /**
1298
+ * Extend translate. The plotX and plotY values are computed as if the polar chart were a
1299
+ * cartesian plane, where plotX denotes the angle in radians and (yAxis.len - plotY) is the pixel distance from
1300
+ * center.
1301
+ */
1302
+ wrap(seriesProto, 'translate', function (proceed) {
1303
+
1304
+ // Run uber method
1305
+ proceed.call(this);
1306
+
1307
+ // Postprocess plot coordinates
1308
+ if (this.chart.polar && !this.preventPostTranslate) {
1309
+ var points = this.points,
1310
+ i = points.length;
1311
+ while (i--) {
1312
+ // Translate plotX, plotY from angle and radius to true plot coordinates
1313
+ this.toXY(points[i]);
1314
+ }
1315
+ }
1316
+ });
1317
+
1318
+ /**
1319
+ * Extend getSegmentPath to allow connecting ends across 0 to provide a closed circle in
1320
+ * line-like series.
1321
+ */
1322
+ wrap(seriesProto, 'getSegmentPath', function (proceed, segment) {
1323
+
1324
+ var points = this.points;
1325
+
1326
+ // Connect the path
1327
+ if (this.chart.polar && this.options.connectEnds !== false &&
1328
+ segment[segment.length - 1] === points[points.length - 1] && points[0].y !== null) {
1329
+ this.connectEnds = true; // re-used in splines
1330
+ segment = [].concat(segment, [points[0]]);
1331
+ }
1332
+
1333
+ // Run uber method
1334
+ return proceed.call(this, segment);
1335
+
1336
+ });
1337
+
1338
+
1339
+ function polarAnimate(proceed, init) {
1340
+ var chart = this.chart,
1341
+ animation = this.options.animation,
1342
+ group = this.group,
1343
+ markerGroup = this.markerGroup,
1344
+ center = this.xAxis.center,
1345
+ plotLeft = chart.plotLeft,
1346
+ plotTop = chart.plotTop,
1347
+ attribs;
1348
+
1349
+ // Specific animation for polar charts
1350
+ if (chart.polar) {
1351
+
1352
+ // Enable animation on polar charts only in SVG. In VML, the scaling is different, plus animation
1353
+ // would be so slow it would't matter.
1354
+ if (chart.renderer.isSVG) {
1355
+
1356
+ if (animation === true) {
1357
+ animation = {};
1358
+ }
1359
+
1360
+ // Initialize the animation
1361
+ if (init) {
1362
+
1363
+ // Create an SVG specific attribute setter for scaleX and scaleY
1364
+ group.attrSetters.scaleX = group.attrSetters.scaleY = function (value, key) {
1365
+ this[key] = value;
1366
+ if (this.scaleX !== UNDEFINED && this.scaleY !== UNDEFINED) {
1367
+ this.element.setAttribute('transform', 'translate(' + this.translateX + ',' + this.translateY + ') scale(' +
1368
+ this.scaleX + ',' + this.scaleY + ')');
1369
+ }
1370
+ return false;
1371
+ };
1372
+
1373
+ // Scale down the group and place it in the center
1374
+ attribs = {
1375
+ translateX: center[0] + plotLeft,
1376
+ translateY: center[1] + plotTop,
1377
+ scaleX: 0,
1378
+ scaleY: 0
1379
+ };
1380
+
1381
+ group.attr(attribs);
1382
+ if (markerGroup) {
1383
+ markerGroup.attrSetters = group.attrSetters;
1384
+ markerGroup.attr(attribs);
1385
+ }
1386
+
1387
+ // Run the animation
1388
+ } else {
1389
+ attribs = {
1390
+ translateX: plotLeft,
1391
+ translateY: plotTop,
1392
+ scaleX: 1,
1393
+ scaleY: 1
1394
+ };
1395
+ group.animate(attribs, animation);
1396
+ if (markerGroup) {
1397
+ markerGroup.animate(attribs, animation);
1398
+ }
1399
+
1400
+ // Delete this function to allow it only once
1401
+ this.animate = null;
1402
+ }
1403
+ }
1404
+
1405
+ // For non-polar charts, revert to the basic animation
1406
+ } else {
1407
+ proceed.call(this, init);
1408
+ }
1409
+ }
1410
+
1411
+ // Define the animate method for both regular series and column series and their derivatives
1412
+ wrap(seriesProto, 'animate', polarAnimate);
1413
+ wrap(colProto, 'animate', polarAnimate);
1414
+
1415
+
1416
+ /**
1417
+ * Throw in a couple of properties to let setTooltipPoints know we're indexing the points
1418
+ * in degrees (0-360), not plot pixel width.
1419
+ */
1420
+ wrap(seriesProto, 'setTooltipPoints', function (proceed, renew) {
1421
+
1422
+ if (this.chart.polar) {
1423
+ extend(this.xAxis, {
1424
+ tooltipLen: 360, // degrees are the resolution unit of the tooltipPoints array
1425
+ tooltipPosName: 'deg'
1426
+ });
1427
+ }
1428
+
1429
+ // Run uber method
1430
+ return proceed.call(this, renew);
1431
+ });
1432
+
1433
+
1434
+ /**
1435
+ * Extend the column prototype's translate method
1436
+ */
1437
+ wrap(colProto, 'translate', function (proceed) {
1438
+
1439
+ var xAxis = this.xAxis,
1440
+ len = this.yAxis.len,
1441
+ center = xAxis.center,
1442
+ startAngleRad = xAxis.startAngleRad,
1443
+ renderer = this.chart.renderer,
1444
+ start,
1445
+ points,
1446
+ point,
1447
+ i;
1448
+
1449
+ this.preventPostTranslate = true;
1450
+
1451
+ // Run uber method
1452
+ proceed.call(this);
1453
+
1454
+ // Postprocess plot coordinates
1455
+ if (xAxis.isRadial) {
1456
+ points = this.points;
1457
+ i = points.length;
1458
+ while (i--) {
1459
+ point = points[i];
1460
+ start = point.barX + startAngleRad;
1461
+ point.shapeType = 'path';
1462
+ point.shapeArgs = {
1463
+ d: renderer.symbols.arc(
1464
+ center[0],
1465
+ center[1],
1466
+ len - point.plotY,
1467
+ null,
1468
+ {
1469
+ start: start,
1470
+ end: start + point.pointWidth,
1471
+ innerR: len - pick(point.yBottom, len)
1472
+ }
1473
+ )
1474
+ };
1475
+ this.toXY(point); // provide correct plotX, plotY for tooltip
1476
+ }
1477
+ }
1478
+ });
1479
+
1480
+
1481
+ /**
1482
+ * Align column data labels outside the columns. #1199.
1483
+ */
1484
+ wrap(colProto, 'alignDataLabel', function (proceed, point, dataLabel, options, alignTo, isNew) {
1485
+
1486
+ if (this.chart.polar) {
1487
+ var angle = point.rectPlotX / Math.PI * 180,
1488
+ align,
1489
+ verticalAlign;
1490
+
1491
+ // Align nicely outside the perimeter of the columns
1492
+ if (options.align === null) {
1493
+ if (angle > 20 && angle < 160) {
1494
+ align = 'left'; // right hemisphere
1495
+ } else if (angle > 200 && angle < 340) {
1496
+ align = 'right'; // left hemisphere
1497
+ } else {
1498
+ align = 'center'; // top or bottom
1499
+ }
1500
+ options.align = align;
1501
+ }
1502
+ if (options.verticalAlign === null) {
1503
+ if (angle < 45 || angle > 315) {
1504
+ verticalAlign = 'bottom'; // top part
1505
+ } else if (angle > 135 && angle < 225) {
1506
+ verticalAlign = 'top'; // bottom part
1507
+ } else {
1508
+ verticalAlign = 'middle'; // left or right
1509
+ }
1510
+ options.verticalAlign = verticalAlign;
1511
+ }
1512
+
1513
+ seriesProto.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
1514
+ } else {
1515
+ proceed.call(this, point, dataLabel, options, alignTo, isNew);
1516
+ }
1517
+
1518
+ });
1519
+
1520
+ /**
1521
+ * Extend the mouse tracker to return the tooltip position index in terms of
1522
+ * degrees rather than pixels
1523
+ */
1524
+ wrap(mouseTrackerProto, 'getIndex', function (proceed, e) {
1525
+ var ret,
1526
+ chart = this.chart,
1527
+ center,
1528
+ x,
1529
+ y;
1530
+
1531
+ if (chart.polar) {
1532
+ center = chart.xAxis[0].center;
1533
+ x = e.chartX - center[0] - chart.plotLeft;
1534
+ y = e.chartY - center[1] - chart.plotTop;
1535
+
1536
+ ret = 180 - Math.round(Math.atan2(x, y) / Math.PI * 180);
1537
+
1538
+ } else {
1539
+
1540
+ // Run uber method
1541
+ ret = proceed.call(this, e);
1542
+ }
1543
+ return ret;
1544
+ });
1545
+
1546
+ /**
1547
+ * Extend getMouseCoordinates to prepare for polar axis values
1548
+ */
1549
+ wrap(mouseTrackerProto, 'getMouseCoordinates', function (proceed, e) {
1550
+ var chart = this.chart,
1551
+ ret = {
1552
+ xAxis: [],
1553
+ yAxis: []
1554
+ };
1555
+
1556
+ if (chart.polar) {
1557
+
1558
+ each(chart.axes, function (axis) {
1559
+ var isXAxis = axis.isXAxis,
1560
+ center = axis.center,
1561
+ x = e.chartX - center[0] - chart.plotLeft,
1562
+ y = e.chartY - center[1] - chart.plotTop;
1563
+
1564
+ ret[isXAxis ? 'xAxis' : 'yAxis'].push({
1565
+ axis: axis,
1566
+ value: axis.translate(
1567
+ isXAxis ?
1568
+ Math.PI - Math.atan2(x, y) : // angle
1569
+ Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)), // distance from center
1570
+ true
1571
+ )
1572
+ });
1573
+ });
1574
+
1575
+ } else {
1576
+ ret = proceed.call(this, e);
1577
+ }
1578
+
1579
+ return ret;
1580
+ });
1581
+ }(Highcharts));