highcharts-rails 4.2.7 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  }));