highcharts-js-rails 0.1.11 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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));