highcharts-rails 4.2.7 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.markdown +34 -0
  3. data/Gemfile +4 -0
  4. data/Rakefile +53 -32
  5. data/app/assets/javascripts/highcharts.js +18775 -17176
  6. data/app/assets/javascripts/highcharts/highcharts-3d.js +1849 -1563
  7. data/app/assets/javascripts/highcharts/highcharts-more.js +2162 -1988
  8. data/app/assets/javascripts/highcharts/modules/accessibility.js +1005 -0
  9. data/app/assets/javascripts/highcharts/modules/annotations.js +408 -401
  10. data/app/assets/javascripts/highcharts/modules/boost.js +561 -546
  11. data/app/assets/javascripts/highcharts/modules/broken-axis.js +330 -324
  12. data/app/assets/javascripts/highcharts/modules/data.js +973 -965
  13. data/app/assets/javascripts/highcharts/modules/drilldown.js +783 -723
  14. data/app/assets/javascripts/highcharts/modules/exporting.js +864 -785
  15. data/app/assets/javascripts/highcharts/modules/funnel.js +290 -306
  16. data/app/assets/javascripts/highcharts/modules/heatmap.js +701 -645
  17. data/app/assets/javascripts/highcharts/modules/no-data-to-display.js +150 -132
  18. data/app/assets/javascripts/highcharts/modules/offline-exporting.js +414 -355
  19. data/app/assets/javascripts/highcharts/modules/overlapping-datalabels.js +164 -0
  20. data/app/assets/javascripts/highcharts/modules/series-label.js +473 -448
  21. data/app/assets/javascripts/highcharts/modules/solid-gauge.js +279 -271
  22. data/app/assets/javascripts/highcharts/modules/treemap.js +921 -886
  23. data/app/assets/javascripts/highcharts/themes/dark-blue.js +307 -244
  24. data/app/assets/javascripts/highcharts/themes/dark-green.js +303 -244
  25. data/app/assets/javascripts/highcharts/themes/dark-unica.js +231 -201
  26. data/app/assets/javascripts/highcharts/themes/gray.js +314 -245
  27. data/app/assets/javascripts/highcharts/themes/grid-light.js +91 -66
  28. data/app/assets/javascripts/highcharts/themes/grid.js +124 -96
  29. data/app/assets/javascripts/highcharts/themes/sand-signika.js +119 -94
  30. data/app/assets/javascripts/highcharts/themes/skies.js +108 -85
  31. data/lib/highcharts/version.rb +1 -1
  32. metadata +13 -14
  33. data/app/assets/javascripts/highcharts/adapters/standalone-framework.js +0 -1
  34. data/app/assets/javascripts/highcharts/modules/canvas-tools.js +0 -3115
  35. data/app/assets/javascripts/highcharts/modules/map.js +0 -2117
@@ -0,0 +1,164 @@
1
+ /**
2
+ * @license Highcharts JS v5.0.0 (2016-09-29)
3
+ *
4
+ * (c) 2009-2016 Torstein Honsi
5
+ *
6
+ * License: www.highcharts.com/license
7
+ */
8
+ (function(factory) {
9
+ if (typeof module === 'object' && module.exports) {
10
+ module.exports = factory;
11
+ } else {
12
+ factory(Highcharts);
13
+ }
14
+ }(function(Highcharts) {
15
+ (function(H) {
16
+ /**
17
+ * (c) 2009-2016 Torstein Honsi
18
+ *
19
+ * License: www.highcharts.com/license
20
+ */
21
+ 'use strict';
22
+ /**
23
+ * Highcharts module to hide overlapping data labels. This module is included in Highcharts.
24
+ */
25
+ var Chart = H.Chart,
26
+ each = H.each,
27
+ pick = H.pick,
28
+ addEvent = H.addEvent;
29
+
30
+ // Collect potensial overlapping data labels. Stack labels probably don't need to be
31
+ // considered because they are usually accompanied by data labels that lie inside the columns.
32
+ Chart.prototype.callbacks.push(function(chart) {
33
+ function collectAndHide() {
34
+ var labels = [];
35
+
36
+ each(chart.series, function(series) {
37
+ var dlOptions = series.options.dataLabels,
38
+ collections = series.dataLabelCollections || ['dataLabel']; // Range series have two collections
39
+ if ((dlOptions.enabled || series._hasPointLabels) && !dlOptions.allowOverlap && series.visible) { // #3866
40
+ each(collections, function(coll) {
41
+ each(series.points, function(point) {
42
+ if (point[coll]) {
43
+ point[coll].labelrank = pick(point.labelrank, point.shapeArgs && point.shapeArgs.height); // #4118
44
+ labels.push(point[coll]);
45
+ }
46
+ });
47
+ });
48
+ }
49
+ });
50
+ chart.hideOverlappingLabels(labels);
51
+ }
52
+
53
+ // Do it now ...
54
+ collectAndHide();
55
+
56
+ // ... and after each chart redraw
57
+ addEvent(chart, 'redraw', collectAndHide);
58
+
59
+ });
60
+
61
+ /**
62
+ * Hide overlapping labels. Labels are moved and faded in and out on zoom to provide a smooth
63
+ * visual imression.
64
+ */
65
+ Chart.prototype.hideOverlappingLabels = function(labels) {
66
+
67
+ var len = labels.length,
68
+ label,
69
+ i,
70
+ j,
71
+ label1,
72
+ label2,
73
+ isIntersecting,
74
+ pos1,
75
+ pos2,
76
+ parent1,
77
+ parent2,
78
+ padding,
79
+ intersectRect = function(x1, y1, w1, h1, x2, y2, w2, h2) {
80
+ return !(
81
+ x2 > x1 + w1 ||
82
+ x2 + w2 < x1 ||
83
+ y2 > y1 + h1 ||
84
+ y2 + h2 < y1
85
+ );
86
+ };
87
+
88
+ // Mark with initial opacity
89
+ for (i = 0; i < len; i++) {
90
+ label = labels[i];
91
+ if (label) {
92
+ label.oldOpacity = label.opacity;
93
+ label.newOpacity = 1;
94
+ }
95
+ }
96
+
97
+ // Prevent a situation in a gradually rising slope, that each label
98
+ // will hide the previous one because the previous one always has
99
+ // lower rank.
100
+ labels.sort(function(a, b) {
101
+ return (b.labelrank || 0) - (a.labelrank || 0);
102
+ });
103
+
104
+ // Detect overlapping labels
105
+ for (i = 0; i < len; i++) {
106
+ label1 = labels[i];
107
+
108
+ for (j = i + 1; j < len; ++j) {
109
+ label2 = labels[j];
110
+ if (label1 && label2 && label1.placed && label2.placed && label1.newOpacity !== 0 && label2.newOpacity !== 0) {
111
+ pos1 = label1.alignAttr;
112
+ pos2 = label2.alignAttr;
113
+ parent1 = label1.parentGroup; // Different panes have different positions
114
+ parent2 = label2.parentGroup;
115
+ padding = 2 * (label1.box ? 0 : label1.padding); // Substract the padding if no background or border (#4333)
116
+ isIntersecting = intersectRect(
117
+ pos1.x + parent1.translateX,
118
+ pos1.y + parent1.translateY,
119
+ label1.width - padding,
120
+ label1.height - padding,
121
+ pos2.x + parent2.translateX,
122
+ pos2.y + parent2.translateY,
123
+ label2.width - padding,
124
+ label2.height - padding
125
+ );
126
+
127
+ if (isIntersecting) {
128
+ (label1.labelrank < label2.labelrank ? label1 : label2).newOpacity = 0;
129
+ }
130
+ }
131
+ }
132
+ }
133
+
134
+ // Hide or show
135
+ each(labels, function(label) {
136
+ var complete,
137
+ newOpacity;
138
+
139
+ if (label) {
140
+ newOpacity = label.newOpacity;
141
+
142
+ if (label.oldOpacity !== newOpacity && label.placed) {
143
+
144
+ // Make sure the label is completely hidden to avoid catching clicks (#4362)
145
+ if (newOpacity) {
146
+ label.show(true);
147
+ } else {
148
+ complete = function() {
149
+ label.hide();
150
+ };
151
+ }
152
+
153
+ // Animate or set the opacity
154
+ label.alignAttr.opacity = newOpacity;
155
+ label[label.isOld ? 'animate' : 'attr'](label.alignAttr, null, complete);
156
+
157
+ }
158
+ label.isOld = true;
159
+ }
160
+ });
161
+ };
162
+
163
+ }(Highcharts));
164
+ }));
@@ -1,519 +1,544 @@
1
1
  /**
2
- * EXPERIMENTAL Highcharts module to place labels next to a series in a natural position.
2
+ * @license Highcharts JS v5.0.0 (2016-09-29)
3
3
  *
4
- * TODO:
5
- * - add column support (box collision detection, boxesToAvoid logic)
6
- * - other series types, area etc.
7
- * - avoid data labels, when data labels above, show series label below.
8
- * - add more options (connector, format, formatter)
9
- *
10
- * http://jsfiddle.net/highcharts/L2u9rpwr/
11
- * http://jsfiddle.net/highcharts/y5A37/
12
- * http://jsfiddle.net/highcharts/264Nm/
13
- * http://jsfiddle.net/highcharts/y5A37/
4
+ * (c) 2009-2016 Torstein Honsi
5
+ *
6
+ * License: www.highcharts.com/license
14
7
  */
15
- /* eslint indent: [2, 4] */
16
- (function (factory) {
8
+ (function(factory) {
17
9
  if (typeof module === 'object' && module.exports) {
18
10
  module.exports = factory;
19
11
  } else {
20
12
  factory(Highcharts);
21
13
  }
22
- }(function (H) {
23
-
24
- var labelDistance = 3,
25
- wrap = H.wrap,
26
- each = H.each,
27
- extend = H.extend,
28
- isNumber = H.isNumber,
29
- Series = H.Series,
30
- SVGRenderer = H.SVGRenderer,
31
- Chart = H.Chart;
32
-
33
- H.setOptions({
34
- plotOptions: {
35
- series: {
36
- label: {
37
- enabled: true,
38
- // Allow labels to be placed distant to the graph if necessary, and
39
- // draw a connector line to the graph
40
- connectorAllowed: true,
41
- connectorNeighbourDistance: 24, // If the label is closer than this to a neighbour graph, draw a connector
42
- styles: {
43
- fontWeight: 'bold'
14
+ }(function(Highcharts) {
15
+ (function(H) {
16
+ /**
17
+ * (c) 2009-2016 Torstein Honsi
18
+ *
19
+ * License: www.highcharts.com/license
20
+ */
21
+ /**
22
+ * EXPERIMENTAL Highcharts module to place labels next to a series in a natural position.
23
+ *
24
+ * TODO:
25
+ * - add column support (box collision detection, boxesToAvoid logic)
26
+ * - other series types, area etc.
27
+ * - avoid data labels, when data labels above, show series label below.
28
+ * - add more options (connector, format, formatter)
29
+ *
30
+ * http://jsfiddle.net/highcharts/L2u9rpwr/
31
+ * http://jsfiddle.net/highcharts/y5A37/
32
+ * http://jsfiddle.net/highcharts/264Nm/
33
+ * http://jsfiddle.net/highcharts/y5A37/
34
+ */
35
+
36
+ 'use strict';
37
+
38
+ var labelDistance = 3,
39
+ wrap = H.wrap,
40
+ each = H.each,
41
+ extend = H.extend,
42
+ isNumber = H.isNumber,
43
+ Series = H.Series,
44
+ SVGRenderer = H.SVGRenderer,
45
+ Chart = H.Chart;
46
+
47
+ H.setOptions({
48
+ plotOptions: {
49
+ series: {
50
+ label: {
51
+ enabled: true,
52
+ // Allow labels to be placed distant to the graph if necessary, and
53
+ // draw a connector line to the graph
54
+ connectorAllowed: true,
55
+ connectorNeighbourDistance: 24, // If the label is closer than this to a neighbour graph, draw a connector
56
+ styles: {
57
+ fontWeight: 'bold'
58
+ }
59
+ // boxesToAvoid: []
44
60
  }
45
- // boxesToAvoid: []
46
61
  }
47
62
  }
48
- }
49
- });
50
-
51
- /**
52
- * Counter-clockwise, part of the fast line intersection logic
53
- */
54
- function ccw(x1, y1, x2, y2, x3, y3) {
55
- var cw = ((y3 - y1) * (x2 - x1)) - ((y2 - y1) * (x3 - x1));
56
- return cw > 0 ? true : cw < 0 ? false : true;
57
- }
63
+ });
58
64
 
59
- /**
60
- * Detect if two lines intersect
61
- */
62
- function intersectLine(x1, y1, x2, y2, x3, y3, x4, y4) {
63
- return ccw(x1, y1, x3, y3, x4, y4) !== ccw(x2, y2, x3, y3, x4, y4) &&
64
- ccw(x1, y1, x2, y2, x3, y3) !== ccw(x1, y1, x2, y2, x4, y4);
65
- }
65
+ /**
66
+ * Counter-clockwise, part of the fast line intersection logic
67
+ */
68
+ function ccw(x1, y1, x2, y2, x3, y3) {
69
+ var cw = ((y3 - y1) * (x2 - x1)) - ((y2 - y1) * (x3 - x1));
70
+ return cw > 0 ? true : cw < 0 ? false : true;
71
+ }
66
72
 
67
- /**
68
- * Detect if a box intersects with a line
69
- */
70
- function boxIntersectLine(x, y, w, h, x1, y1, x2, y2) {
71
- return (
72
- intersectLine(x, y, x + w, y, x1, y1, x2, y2) || // top of label
73
- intersectLine(x + w, y, x + w, y + h, x1, y1, x2, y2) || // right of label
74
- intersectLine(x, y + h, x + w, y + h, x1, y1, x2, y2) || // bottom of label
75
- intersectLine(x, y, x, y + h, x1, y1, x2, y2) // left of label
76
- );
77
- }
73
+ /**
74
+ * Detect if two lines intersect
75
+ */
76
+ function intersectLine(x1, y1, x2, y2, x3, y3, x4, y4) {
77
+ return ccw(x1, y1, x3, y3, x4, y4) !== ccw(x2, y2, x3, y3, x4, y4) &&
78
+ ccw(x1, y1, x2, y2, x3, y3) !== ccw(x1, y1, x2, y2, x4, y4);
79
+ }
78
80
 
79
- /**
80
- * General symbol definition for labels with connector
81
- */
82
- SVGRenderer.prototype.symbols.connector = function (x, y, w, h, options) {
83
- var anchorX = options && options.anchorX,
84
- anchorY = options && options.anchorY,
85
- path,
86
- yOffset,
87
- lateral = w / 2;
88
-
89
- if (isNumber(anchorX) && isNumber(anchorY)) {
90
-
91
- path = ['M', anchorX, anchorY];
92
-
93
- // Prefer 45 deg connectors
94
- yOffset = y - anchorY;
95
- if (yOffset < 0) {
96
- yOffset = -h - yOffset;
97
- }
98
- if (yOffset < w) {
99
- lateral = anchorX < x + (w / 2) ? yOffset : w - yOffset;
100
- }
101
-
102
- // Anchor below label
103
- if (anchorY > y + h) {
104
- path.push('L', x + lateral, y + h);
105
-
106
- // Anchor above label
107
- } else if (anchorY < y) {
108
- path.push('L', x + lateral, y);
109
-
110
- // Anchor left of label
111
- } else if (anchorX < x) {
112
- path.push('L', x, y + h / 2);
113
-
114
- // Anchor right of label
115
- } else if (anchorX > x + w) {
116
- path.push('L', x + w, y + h / 2);
117
- }
81
+ /**
82
+ * Detect if a box intersects with a line
83
+ */
84
+ function boxIntersectLine(x, y, w, h, x1, y1, x2, y2) {
85
+ return (
86
+ intersectLine(x, y, x + w, y, x1, y1, x2, y2) || // top of label
87
+ intersectLine(x + w, y, x + w, y + h, x1, y1, x2, y2) || // right of label
88
+ intersectLine(x, y + h, x + w, y + h, x1, y1, x2, y2) || // bottom of label
89
+ intersectLine(x, y, x, y + h, x1, y1, x2, y2) // left of label
90
+ );
118
91
  }
119
- return path || [];
120
- };
121
-
122
- /**
123
- * Points to avoid. In addition to actual data points, the label should avoid
124
- * interpolated positions.
125
- */
126
- Series.prototype.getPointsOnGraph = function () {
127
- var distance = 16,
128
- points = this.points,
129
- point,
130
- last,
131
- interpolated = [],
132
- i,
133
- deltaX,
134
- deltaY,
135
- delta,
136
- len,
137
- n,
138
- j,
139
- d,
140
- graph = this.graph || this.area,
141
- node = graph.element,
142
- inverted = this.chart.inverted,
143
- paneLeft = inverted ? this.yAxis.pos : this.xAxis.pos,
144
- paneTop = inverted ? this.xAxis.pos : this.yAxis.pos;
145
-
146
- // For splines, get the point at length (possible caveat: peaks are not correctly detected)
147
- if (this.getPointSpline && node.getPointAtLength) {
148
- // If it is animating towards a path definition, use that briefly, and reset
149
- if (graph.toD) {
150
- d = graph.attr('d');
151
- graph.attr({ d: graph.toD });
152
- }
153
- len = node.getTotalLength();
154
- for (i = 0; i < len; i += distance) {
155
- point = node.getPointAtLength(i);
156
- interpolated.push({
157
- chartX: paneLeft + point.x,
158
- chartY: paneTop + point.y,
159
- plotX: point.x,
160
- plotY: point.y
161
- });
162
- }
163
- if (d) {
164
- graph.attr({ d: d });
165
- }
166
- // Last point
167
- point = points[points.length - 1];
168
- point.chartX = paneLeft + point.plotX;
169
- point.chartY = paneTop + point.plotY;
170
- interpolated.push(point);
171
92
 
172
- // Interpolate
173
- } else {
174
- len = points.length;
175
- for (i = 0; i < len; i += 1) {
93
+ /**
94
+ * General symbol definition for labels with connector
95
+ */
96
+ SVGRenderer.prototype.symbols.connector = function(x, y, w, h, options) {
97
+ var anchorX = options && options.anchorX,
98
+ anchorY = options && options.anchorY,
99
+ path,
100
+ yOffset,
101
+ lateral = w / 2;
102
+
103
+ if (isNumber(anchorX) && isNumber(anchorY)) {
104
+
105
+ path = ['M', anchorX, anchorY];
106
+
107
+ // Prefer 45 deg connectors
108
+ yOffset = y - anchorY;
109
+ if (yOffset < 0) {
110
+ yOffset = -h - yOffset;
111
+ }
112
+ if (yOffset < w) {
113
+ lateral = anchorX < x + (w / 2) ? yOffset : w - yOffset;
114
+ }
115
+
116
+ // Anchor below label
117
+ if (anchorY > y + h) {
118
+ path.push('L', x + lateral, y + h);
176
119
 
177
- point = points[i];
178
- last = points[i - 1];
120
+ // Anchor above label
121
+ } else if (anchorY < y) {
122
+ path.push('L', x + lateral, y);
179
123
 
180
- // Absolute coordinates so we can compare different panes
124
+ // Anchor left of label
125
+ } else if (anchorX < x) {
126
+ path.push('L', x, y + h / 2);
127
+
128
+ // Anchor right of label
129
+ } else if (anchorX > x + w) {
130
+ path.push('L', x + w, y + h / 2);
131
+ }
132
+ }
133
+ return path || [];
134
+ };
135
+
136
+ /**
137
+ * Points to avoid. In addition to actual data points, the label should avoid
138
+ * interpolated positions.
139
+ */
140
+ Series.prototype.getPointsOnGraph = function() {
141
+ var distance = 16,
142
+ points = this.points,
143
+ point,
144
+ last,
145
+ interpolated = [],
146
+ i,
147
+ deltaX,
148
+ deltaY,
149
+ delta,
150
+ len,
151
+ n,
152
+ j,
153
+ d,
154
+ graph = this.graph || this.area,
155
+ node = graph.element,
156
+ inverted = this.chart.inverted,
157
+ paneLeft = inverted ? this.yAxis.pos : this.xAxis.pos,
158
+ paneTop = inverted ? this.xAxis.pos : this.yAxis.pos;
159
+
160
+ // For splines, get the point at length (possible caveat: peaks are not correctly detected)
161
+ if (this.getPointSpline && node.getPointAtLength) {
162
+ // If it is animating towards a path definition, use that briefly, and reset
163
+ if (graph.toD) {
164
+ d = graph.attr('d');
165
+ graph.attr({
166
+ d: graph.toD
167
+ });
168
+ }
169
+ len = node.getTotalLength();
170
+ for (i = 0; i < len; i += distance) {
171
+ point = node.getPointAtLength(i);
172
+ interpolated.push({
173
+ chartX: paneLeft + point.x,
174
+ chartY: paneTop + point.y,
175
+ plotX: point.x,
176
+ plotY: point.y
177
+ });
178
+ }
179
+ if (d) {
180
+ graph.attr({
181
+ d: d
182
+ });
183
+ }
184
+ // Last point
185
+ point = points[points.length - 1];
181
186
  point.chartX = paneLeft + point.plotX;
182
187
  point.chartY = paneTop + point.plotY;
183
-
184
- // Add interpolated points
185
- if (i > 0) {
186
- deltaX = Math.abs(point.chartX - last.chartX);
187
- deltaY = Math.abs(point.chartY - last.chartY);
188
- delta = Math.max(deltaX, deltaY);
189
- if (delta > distance) {
190
-
191
- n = Math.ceil(delta / distance);
192
-
193
- for (j = 1; j < n; j += 1) {
194
- interpolated.push({
195
- chartX: last.chartX + (point.chartX - last.chartX) * (j / n),
196
- chartY: last.chartY + (point.chartY - last.chartY) * (j / n),
197
- plotX: last.plotX + (point.plotX - last.plotX) * (j / n),
198
- plotY: last.plotY + (point.plotY - last.plotY) * (j / n)
199
- });
188
+ interpolated.push(point);
189
+
190
+ // Interpolate
191
+ } else {
192
+ len = points.length;
193
+ for (i = 0; i < len; i += 1) {
194
+
195
+ point = points[i];
196
+ last = points[i - 1];
197
+
198
+ // Absolute coordinates so we can compare different panes
199
+ point.chartX = paneLeft + point.plotX;
200
+ point.chartY = paneTop + point.plotY;
201
+
202
+ // Add interpolated points
203
+ if (i > 0) {
204
+ deltaX = Math.abs(point.chartX - last.chartX);
205
+ deltaY = Math.abs(point.chartY - last.chartY);
206
+ delta = Math.max(deltaX, deltaY);
207
+ if (delta > distance) {
208
+
209
+ n = Math.ceil(delta / distance);
210
+
211
+ for (j = 1; j < n; j += 1) {
212
+ interpolated.push({
213
+ chartX: last.chartX + (point.chartX - last.chartX) * (j / n),
214
+ chartY: last.chartY + (point.chartY - last.chartY) * (j / n),
215
+ plotX: last.plotX + (point.plotX - last.plotX) * (j / n),
216
+ plotY: last.plotY + (point.plotY - last.plotY) * (j / n)
217
+ });
218
+ }
200
219
  }
201
220
  }
202
- }
203
221
 
204
- // Add the real point in order to find positive and negative peaks
205
- if (isNumber(point.plotY)) {
206
- interpolated.push(point);
222
+ // Add the real point in order to find positive and negative peaks
223
+ if (isNumber(point.plotY)) {
224
+ interpolated.push(point);
225
+ }
207
226
  }
208
227
  }
209
- }
210
- return interpolated;
211
- };
212
-
213
- /**
214
- * Check whether a proposed label position is clear of other elements
215
- */
216
- Series.prototype.checkClearPoint = function (x, y, bBox, checkDistance) {
217
- var distToOthersSquared = Number.MAX_VALUE, // distance to other graphs
218
- distToPointSquared = Number.MAX_VALUE,
219
- dist,
220
- connectorPoint,
221
- connectorEnabled = this.options.label.connectorAllowed,
222
-
223
- chart = this.chart,
224
- series,
225
- points,
226
- leastDistance = 16,
227
- withinRange,
228
- i,
229
- j;
230
-
231
- function intersectRect(r1, r2) {
232
- return !(r2.left > r1.right ||
233
- r2.right < r1.left ||
234
- r2.top > r1.bottom ||
235
- r2.bottom < r1.top);
236
- }
228
+ return interpolated;
229
+ };
237
230
 
238
231
  /**
239
- * Get the weight in order to determine the ideal position. Larger distance to
240
- * other series gives more weight. Smaller distance to the actual point (connector points only)
241
- * gives more weight.
232
+ * Check whether a proposed label position is clear of other elements
242
233
  */
243
- function getWeight(distToOthersSquared, distToPointSquared) {
244
- return distToOthersSquared - distToPointSquared;
245
- }
234
+ Series.prototype.checkClearPoint = function(x, y, bBox, checkDistance) {
235
+ var distToOthersSquared = Number.MAX_VALUE, // distance to other graphs
236
+ distToPointSquared = Number.MAX_VALUE,
237
+ dist,
238
+ connectorPoint,
239
+ connectorEnabled = this.options.label.connectorAllowed,
240
+
241
+ chart = this.chart,
242
+ series,
243
+ points,
244
+ leastDistance = 16,
245
+ withinRange,
246
+ i,
247
+ j;
248
+
249
+ function intersectRect(r1, r2) {
250
+ return !(r2.left > r1.right ||
251
+ r2.right < r1.left ||
252
+ r2.top > r1.bottom ||
253
+ r2.bottom < r1.top);
254
+ }
246
255
 
247
- // First check for collision with existing labels
248
- for (i = 0; i < chart.boxesToAvoid.length; i += 1) {
249
- if (intersectRect(chart.boxesToAvoid[i], {
250
- left: x,
251
- right: x + bBox.width,
252
- top: y,
253
- bottom: y + bBox.height
254
- })) {
255
- return false;
256
+ /**
257
+ * Get the weight in order to determine the ideal position. Larger distance to
258
+ * other series gives more weight. Smaller distance to the actual point (connector points only)
259
+ * gives more weight.
260
+ */
261
+ function getWeight(distToOthersSquared, distToPointSquared) {
262
+ return distToOthersSquared - distToPointSquared;
256
263
  }
257
- }
258
264
 
259
- // For each position, check if the lines around the label intersect with any of the
260
- // graphs
261
- for (i = 0; i < chart.series.length; i += 1) {
262
- series = chart.series[i];
263
- points = series.interpolatedPoints;
264
- if (series.visible && points) {
265
- for (j = 1; j < points.length; j += 1) {
266
- // If any of the box sides intersect with the line, return
267
- if (boxIntersectLine(
268
- x,
269
- y,
270
- bBox.width,
271
- bBox.height,
272
- points[j - 1].chartX,
273
- points[j - 1].chartY,
274
- points[j].chartX,
275
- points[j].chartY
276
- )) {
277
- return false;
278
- }
265
+ // First check for collision with existing labels
266
+ for (i = 0; i < chart.boxesToAvoid.length; i += 1) {
267
+ if (intersectRect(chart.boxesToAvoid[i], {
268
+ left: x,
269
+ right: x + bBox.width,
270
+ top: y,
271
+ bottom: y + bBox.height
272
+ })) {
273
+ return false;
274
+ }
275
+ }
279
276
 
280
- // But if it is too far away (a padded box doesn't intersect), also return
281
- if (this === series && !withinRange && checkDistance) {
282
- withinRange = boxIntersectLine(
283
- x - leastDistance,
284
- y - leastDistance,
285
- bBox.width + 2 * leastDistance,
286
- bBox.height + 2 * leastDistance,
287
- points[j - 1].chartX,
288
- points[j - 1].chartY,
289
- points[j].chartX,
290
- points[j].chartY
291
- );
292
- }
277
+ // For each position, check if the lines around the label intersect with any of the
278
+ // graphs
279
+ for (i = 0; i < chart.series.length; i += 1) {
280
+ series = chart.series[i];
281
+ points = series.interpolatedPoints;
282
+ if (series.visible && points) {
283
+ for (j = 1; j < points.length; j += 1) {
284
+ // If any of the box sides intersect with the line, return
285
+ if (boxIntersectLine(
286
+ x,
287
+ y,
288
+ bBox.width,
289
+ bBox.height,
290
+ points[j - 1].chartX,
291
+ points[j - 1].chartY,
292
+ points[j].chartX,
293
+ points[j].chartY
294
+ )) {
295
+ return false;
296
+ }
297
+
298
+ // But if it is too far away (a padded box doesn't intersect), also return
299
+ if (this === series && !withinRange && checkDistance) {
300
+ withinRange = boxIntersectLine(
301
+ x - leastDistance,
302
+ y - leastDistance,
303
+ bBox.width + 2 * leastDistance,
304
+ bBox.height + 2 * leastDistance,
305
+ points[j - 1].chartX,
306
+ points[j - 1].chartY,
307
+ points[j].chartX,
308
+ points[j].chartY
309
+ );
310
+ }
293
311
 
294
- // Find the squared distance from the center of the label
295
- if (this !== series) {
296
- distToOthersSquared = Math.min(
297
- distToOthersSquared,
298
- Math.pow(x + bBox.width / 2 - points[j].chartX, 2) + Math.pow(y + bBox.height / 2 - points[j].chartY, 2),
299
- Math.pow(x - points[j].chartX, 2) + Math.pow(y - points[j].chartY, 2),
300
- Math.pow(x + bBox.width - points[j].chartX, 2) + Math.pow(y - points[j].chartY, 2),
301
- Math.pow(x + bBox.width - points[j].chartX, 2) + Math.pow(y + bBox.height - points[j].chartY, 2),
302
- Math.pow(x - points[j].chartX, 2) + Math.pow(y + bBox.height - points[j].chartY, 2)
303
- );
312
+ // Find the squared distance from the center of the label
313
+ if (this !== series) {
314
+ distToOthersSquared = Math.min(
315
+ distToOthersSquared,
316
+ Math.pow(x + bBox.width / 2 - points[j].chartX, 2) + Math.pow(y + bBox.height / 2 - points[j].chartY, 2),
317
+ Math.pow(x - points[j].chartX, 2) + Math.pow(y - points[j].chartY, 2),
318
+ Math.pow(x + bBox.width - points[j].chartX, 2) + Math.pow(y - points[j].chartY, 2),
319
+ Math.pow(x + bBox.width - points[j].chartX, 2) + Math.pow(y + bBox.height - points[j].chartY, 2),
320
+ Math.pow(x - points[j].chartX, 2) + Math.pow(y + bBox.height - points[j].chartY, 2)
321
+ );
322
+ }
304
323
  }
305
- }
306
324
 
307
- // Do we need a connector?
308
- if (connectorEnabled && this === series && ((checkDistance && !withinRange) ||
309
- distToOthersSquared < Math.pow(this.options.label.connectorNeighbourDistance, 2))) {
310
- for (j = 1; j < points.length; j += 1) {
311
- dist = Math.min(
312
- Math.pow(x + bBox.width / 2 - points[j].chartX, 2) + Math.pow(y + bBox.height / 2 - points[j].chartY, 2),
313
- Math.pow(x - points[j].chartX, 2) + Math.pow(y - points[j].chartY, 2),
314
- Math.pow(x + bBox.width - points[j].chartX, 2) + Math.pow(y - points[j].chartY, 2),
315
- Math.pow(x + bBox.width - points[j].chartX, 2) + Math.pow(y + bBox.height - points[j].chartY, 2),
316
- Math.pow(x - points[j].chartX, 2) + Math.pow(y + bBox.height - points[j].chartY, 2)
317
- );
318
- if (dist < distToPointSquared) {
319
- distToPointSquared = dist;
320
- connectorPoint = points[j];
325
+ // Do we need a connector?
326
+ if (connectorEnabled && this === series && ((checkDistance && !withinRange) ||
327
+ distToOthersSquared < Math.pow(this.options.label.connectorNeighbourDistance, 2))) {
328
+ for (j = 1; j < points.length; j += 1) {
329
+ dist = Math.min(
330
+ Math.pow(x + bBox.width / 2 - points[j].chartX, 2) + Math.pow(y + bBox.height / 2 - points[j].chartY, 2),
331
+ Math.pow(x - points[j].chartX, 2) + Math.pow(y - points[j].chartY, 2),
332
+ Math.pow(x + bBox.width - points[j].chartX, 2) + Math.pow(y - points[j].chartY, 2),
333
+ Math.pow(x + bBox.width - points[j].chartX, 2) + Math.pow(y + bBox.height - points[j].chartY, 2),
334
+ Math.pow(x - points[j].chartX, 2) + Math.pow(y + bBox.height - points[j].chartY, 2)
335
+ );
336
+ if (dist < distToPointSquared) {
337
+ distToPointSquared = dist;
338
+ connectorPoint = points[j];
339
+ }
321
340
  }
341
+ withinRange = true;
322
342
  }
323
- withinRange = true;
324
343
  }
325
344
  }
326
- }
327
-
328
- return !checkDistance || withinRange ? {
329
- x: x,
330
- y: y,
331
- weight: getWeight(distToOthersSquared, connectorPoint ? distToPointSquared : 0),
332
- connectorPoint: connectorPoint
333
- } : false;
334
345
 
335
- };
346
+ return !checkDistance || withinRange ? {
347
+ x: x,
348
+ y: y,
349
+ weight: getWeight(distToOthersSquared, connectorPoint ? distToPointSquared : 0),
350
+ connectorPoint: connectorPoint
351
+ } : false;
336
352
 
353
+ };
337
354
 
338
- /**
339
- * The main initiator method that runs on chart level after initiation and redraw. It runs in
340
- * a timeout to prevent locking, and loops over all series, taking all series and labels into
341
- * account when placing the labels.
342
- */
343
- function drawLabels(proceed) {
344
355
 
345
- var chart = this;
346
-
347
- proceed.call(chart);
356
+ /**
357
+ * The main initiator method that runs on chart level after initiation and redraw. It runs in
358
+ * a timeout to prevent locking, and loops over all series, taking all series and labels into
359
+ * account when placing the labels.
360
+ */
361
+ function drawLabels(proceed) {
348
362
 
349
- clearTimeout(chart.seriesLabelTimer);
363
+ var chart = this;
350
364
 
351
- chart.seriesLabelTimer = setTimeout(function () {
365
+ proceed.call(chart);
352
366
 
353
- chart.boxesToAvoid = [];
367
+ clearTimeout(chart.seriesLabelTimer);
354
368
 
355
- // Build the interpolated points
356
- each(chart.series, function (series) {
357
- var options = series.options.label;
358
- if (options.enabled && series.visible && (series.graph || series.area)) {
359
- series.interpolatedPoints = series.getPointsOnGraph();
369
+ chart.seriesLabelTimer = setTimeout(function() {
360
370
 
361
- each(options.boxesToAvoid || [], function (box) {
362
- chart.boxesToAvoid.push(box);
363
- });
364
- }
365
- });
366
-
367
- each(chart.series, function (series) {
368
- var bBox,
369
- x,
370
- y,
371
- results = [],
372
- clearPoint,
373
- i,
374
- best,
375
- inverted = chart.inverted,
376
- paneLeft = inverted ? series.yAxis.pos : series.xAxis.pos,
377
- paneTop = inverted ? series.xAxis.pos : series.yAxis.pos,
378
- paneWidth = chart.inverted ? series.yAxis.len : series.xAxis.len,
379
- paneHeight = chart.inverted ? series.xAxis.len : series.yAxis.len,
380
- points = series.interpolatedPoints;
381
-
382
- function insidePane(x, y, bBox) {
383
- return x > paneLeft && x <= paneLeft + paneWidth - bBox.width &&
384
- y >= paneTop && y <= paneTop + paneHeight - bBox.height;
385
- }
371
+ chart.boxesToAvoid = [];
386
372
 
387
- if (series.visible && points) {
373
+ // Build the interpolated points
374
+ each(chart.series, function(series) {
375
+ var options = series.options.label;
376
+ if (options.enabled && series.visible && (series.graph || series.area)) {
377
+ series.interpolatedPoints = series.getPointsOnGraph();
388
378
 
389
- if (!series.labelBySeries) {
390
- series.labelBySeries = chart.renderer.label(series.name, 0, -9999, 'connector')
391
- .css(extend({
392
- color: series.color
393
- }, series.options.label.styles))
394
- .attr({
395
- padding: 0,
396
- opacity: 0,
397
- stroke: series.color,
398
- 'stroke-width': 1
399
- })
400
- .add(series.group)
401
- .animate({ opacity: 1 }, { duration: 200 });
379
+ each(options.boxesToAvoid || [], function(box) {
380
+ chart.boxesToAvoid.push(box);
381
+ });
402
382
  }
383
+ });
403
384
 
404
- bBox = series.labelBySeries.getBBox();
405
- bBox.width = Math.round(bBox.width);
385
+ each(chart.series, function(series) {
386
+ var bBox,
387
+ x,
388
+ y,
389
+ results = [],
390
+ clearPoint,
391
+ i,
392
+ best,
393
+ inverted = chart.inverted,
394
+ paneLeft = inverted ? series.yAxis.pos : series.xAxis.pos,
395
+ paneTop = inverted ? series.xAxis.pos : series.yAxis.pos,
396
+ paneWidth = chart.inverted ? series.yAxis.len : series.xAxis.len,
397
+ paneHeight = chart.inverted ? series.xAxis.len : series.yAxis.len,
398
+ points = series.interpolatedPoints;
399
+
400
+ function insidePane(x, y, bBox) {
401
+ return x > paneLeft && x <= paneLeft + paneWidth - bBox.width &&
402
+ y >= paneTop && y <= paneTop + paneHeight - bBox.height;
403
+ }
406
404
 
407
- // Ideal positions are centered above or below a point on right side of chart
408
- for (i = points.length - 1; i > 0; i -= 1) {
405
+ if (series.visible && points) {
409
406
 
410
- // Right - up
411
- x = points[i].chartX + labelDistance;
412
- y = points[i].chartY - bBox.height - labelDistance;
413
- if (insidePane(x, y, bBox)) {
414
- best = series.checkClearPoint(
415
- x,
416
- y,
417
- bBox
418
- );
419
- }
420
- if (best) {
421
- results.push(best);
407
+ if (!series.labelBySeries) {
408
+ series.labelBySeries = chart.renderer.label(series.name, 0, -9999, 'connector')
409
+ .css(extend({
410
+ color: series.color
411
+ }, series.options.label.styles))
412
+ .attr({
413
+ padding: 0,
414
+ opacity: 0,
415
+ stroke: series.color,
416
+ 'stroke-width': 1
417
+ })
418
+ .add(series.group)
419
+ .animate({
420
+ opacity: 1
421
+ }, {
422
+ duration: 200
423
+ });
422
424
  }
423
425
 
424
- // Right - down
425
- x = points[i].chartX + labelDistance;
426
- y = points[i].chartY + labelDistance;
427
- if (insidePane(x, y, bBox)) {
428
- best = series.checkClearPoint(
429
- x,
430
- y,
431
- bBox
432
- );
433
- }
434
- if (best) {
435
- results.push(best);
436
- }
426
+ bBox = series.labelBySeries.getBBox();
427
+ bBox.width = Math.round(bBox.width);
428
+
429
+ // Ideal positions are centered above or below a point on right side of chart
430
+ for (i = points.length - 1; i > 0; i -= 1) {
431
+
432
+ // Right - up
433
+ x = points[i].chartX + labelDistance;
434
+ y = points[i].chartY - bBox.height - labelDistance;
435
+ if (insidePane(x, y, bBox)) {
436
+ best = series.checkClearPoint(
437
+ x,
438
+ y,
439
+ bBox
440
+ );
441
+ }
442
+ if (best) {
443
+ results.push(best);
444
+ }
437
445
 
438
- // Left - down
439
- x = points[i].chartX - bBox.width - labelDistance;
440
- y = points[i].chartY + labelDistance;
441
- if (insidePane(x, y, bBox)) {
442
- best = series.checkClearPoint(
443
- x,
444
- y,
445
- bBox
446
- );
447
- }
448
- if (best) {
449
- results.push(best);
450
- }
446
+ // Right - down
447
+ x = points[i].chartX + labelDistance;
448
+ y = points[i].chartY + labelDistance;
449
+ if (insidePane(x, y, bBox)) {
450
+ best = series.checkClearPoint(
451
+ x,
452
+ y,
453
+ bBox
454
+ );
455
+ }
456
+ if (best) {
457
+ results.push(best);
458
+ }
451
459
 
452
- // Left - up
453
- x = points[i].chartX - bBox.width - labelDistance;
454
- y = points[i].chartY - bBox.height - labelDistance;
455
- if (insidePane(x, y, bBox)) {
456
- best = series.checkClearPoint(
457
- x,
458
- y,
459
- bBox
460
- );
461
- }
462
- if (best) {
463
- results.push(best);
464
- }
460
+ // Left - down
461
+ x = points[i].chartX - bBox.width - labelDistance;
462
+ y = points[i].chartY + labelDistance;
463
+ if (insidePane(x, y, bBox)) {
464
+ best = series.checkClearPoint(
465
+ x,
466
+ y,
467
+ bBox
468
+ );
469
+ }
470
+ if (best) {
471
+ results.push(best);
472
+ }
465
473
 
466
- }
474
+ // Left - up
475
+ x = points[i].chartX - bBox.width - labelDistance;
476
+ y = points[i].chartY - bBox.height - labelDistance;
477
+ if (insidePane(x, y, bBox)) {
478
+ best = series.checkClearPoint(
479
+ x,
480
+ y,
481
+ bBox
482
+ );
483
+ }
484
+ if (best) {
485
+ results.push(best);
486
+ }
487
+
488
+ }
467
489
 
468
- // Brute force, try all positions on the chart in a 16x16 grid
469
- if (!results.length) {
470
- for (x = paneLeft + paneWidth - bBox.width; x >= paneLeft; x -= 16) {
471
- for (y = paneTop; y < paneTop + paneHeight - bBox.height; y += 16) {
472
- clearPoint = series.checkClearPoint(x, y, bBox, true);
473
- if (clearPoint) {
474
- results.push(clearPoint);
490
+ // Brute force, try all positions on the chart in a 16x16 grid
491
+ if (!results.length) {
492
+ for (x = paneLeft + paneWidth - bBox.width; x >= paneLeft; x -= 16) {
493
+ for (y = paneTop; y < paneTop + paneHeight - bBox.height; y += 16) {
494
+ clearPoint = series.checkClearPoint(x, y, bBox, true);
495
+ if (clearPoint) {
496
+ results.push(clearPoint);
497
+ }
475
498
  }
476
499
  }
477
500
  }
478
- }
479
501
 
480
- if (results.length) {
502
+ if (results.length) {
481
503
 
482
- results.sort(function (a, b) {
483
- return b.weight - a.weight;
484
- });
485
-
486
- best = results[0];
487
-
488
- chart.boxesToAvoid.push({
489
- left: best.x,
490
- right: best.x + bBox.width,
491
- top: best.y,
492
- bottom: best.y + bBox.height
493
- });
504
+ results.sort(function(a, b) {
505
+ return b.weight - a.weight;
506
+ });
494
507
 
495
- // Move it if needed
496
- if (Math.round(best.x) !== Math.round(series.labelBySeries.x) || Math.round(best.y) !== Math.round(series.labelBySeries.y)) {
497
- series.labelBySeries
498
- .attr({
499
- x: best.x - paneLeft,
500
- y: best.y - paneTop,
501
- anchorX: best.connectorPoint && best.connectorPoint.plotX,
502
- anchorY: best.connectorPoint && best.connectorPoint.plotY,
503
- opacity: 0
504
- })
505
- .animate({ opacity: 1 });
506
- }
508
+ best = results[0];
509
+
510
+ chart.boxesToAvoid.push({
511
+ left: best.x,
512
+ right: best.x + bBox.width,
513
+ top: best.y,
514
+ bottom: best.y + bBox.height
515
+ });
516
+
517
+ // Move it if needed
518
+ if (Math.round(best.x) !== Math.round(series.labelBySeries.x) || Math.round(best.y) !== Math.round(series.labelBySeries.y)) {
519
+ series.labelBySeries
520
+ .attr({
521
+ x: best.x - paneLeft,
522
+ y: best.y - paneTop,
523
+ anchorX: best.connectorPoint && best.connectorPoint.plotX,
524
+ anchorY: best.connectorPoint && best.connectorPoint.plotY,
525
+ opacity: 0
526
+ })
527
+ .animate({
528
+ opacity: 1
529
+ });
530
+ }
507
531
 
508
- } else if (series.labelBySeries) {
509
- series.labelBySeries = series.labelBySeries.destroy();
532
+ } else if (series.labelBySeries) {
533
+ series.labelBySeries = series.labelBySeries.destroy();
534
+ }
510
535
  }
511
- }
512
- });
513
- }, 350);
536
+ });
537
+ }, 350);
514
538
 
515
- }
516
- wrap(Chart.prototype, 'render', drawLabels);
517
- wrap(Chart.prototype, 'redraw', drawLabels);
539
+ }
540
+ wrap(Chart.prototype, 'render', drawLabels);
541
+ wrap(Chart.prototype, 'redraw', drawLabels);
518
542
 
543
+ }(Highcharts));
519
544
  }));