highcharts-rails 4.2.6 → 4.2.7
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.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +46 -0
- data/Rakefile +1 -0
- data/app/assets/javascripts/highcharts.js +252 -171
- data/app/assets/javascripts/highcharts/highcharts-3d.js +2 -1
- data/app/assets/javascripts/highcharts/highcharts-more.js +81 -50
- data/app/assets/javascripts/highcharts/modules/boost.js +4 -0
- data/app/assets/javascripts/highcharts/modules/broken-axis.js +1 -1
- data/app/assets/javascripts/highcharts/modules/canvas-tools.js +1 -1
- data/app/assets/javascripts/highcharts/modules/data.js +4 -1
- data/app/assets/javascripts/highcharts/modules/drilldown.js +50 -51
- data/app/assets/javascripts/highcharts/modules/exporting.js +6 -5
- data/app/assets/javascripts/highcharts/modules/heatmap.js +24 -16
- data/app/assets/javascripts/highcharts/modules/no-data-to-display.js +1 -1
- data/app/assets/javascripts/highcharts/modules/offline-exporting.js +1 -2
- data/app/assets/javascripts/highcharts/modules/series-label.js +519 -0
- data/app/assets/javascripts/highcharts/modules/solid-gauge.js +1 -1
- data/app/assets/javascripts/highcharts/modules/treemap.js +6 -4
- data/lib/highcharts/version.rb +1 -1
- metadata +2 -1
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license Highcharts JS v4.2.
|
2
|
+
* @license Highcharts JS v4.2.7 (2016-09-21)
|
3
3
|
* Exporting module
|
4
4
|
*
|
5
5
|
* (c) 2010-2016 Torstein Honsi
|
@@ -206,12 +206,13 @@ extend(Chart.prototype, {
|
|
206
206
|
.replace(/isShadow="[^"]+"/g, '')
|
207
207
|
.replace(/symbolName="[^"]+"/g, '')
|
208
208
|
.replace(/jQuery[0-9]+="[^"]+"/g, '')
|
209
|
+
.replace(/url\(("|")(\S+)("|")\)/g, 'url($2)')
|
209
210
|
.replace(/url\([^#]+#/g, 'url(#')
|
210
211
|
.replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
|
211
212
|
.replace(/ (NS[0-9]+\:)?href=/g, ' xlink:href=') // #3567
|
212
213
|
.replace(/\n/, ' ')
|
213
214
|
// Any HTML added to the container after the SVG (#894)
|
214
|
-
.replace(/<\/svg>.*?$/, '</svg>')
|
215
|
+
.replace(/<\/svg>.*?$/, '</svg>')
|
215
216
|
// Batik doesn't support rgba fills and strokes (#3095)
|
216
217
|
.replace(/(fill|stroke)="rgba\(([ 0-9]+,[ 0-9]+,[ 0-9]+),([ 0-9\.]+)\)"/g, '$1="rgb($2)" $1-opacity="$3"')
|
217
218
|
/* This fails in IE < 8
|
@@ -263,7 +264,7 @@ extend(Chart.prototype, {
|
|
263
264
|
html,
|
264
265
|
options = merge(chart.options, additionalOptions), // copy the options and add extra options
|
265
266
|
allowHTML = options.exporting.allowHTML;
|
266
|
-
|
267
|
+
|
267
268
|
|
268
269
|
// IE compatibility hack for generating SVG content that it doesn't really understand
|
269
270
|
if (!doc.createElementNS) {
|
@@ -360,7 +361,7 @@ extend(Chart.prototype, {
|
|
360
361
|
html = '<foreignObject x="0" y="0" width="200" height="200">' +
|
361
362
|
'<body xmlns="http://www.w3.org/1999/xhtml">' +
|
362
363
|
html[1] +
|
363
|
-
'</body>' +
|
364
|
+
'</body>' +
|
364
365
|
'</foreignObject>';
|
365
366
|
svg = svg.replace('</svg>', html + '</svg>');
|
366
367
|
}
|
@@ -398,7 +399,7 @@ extend(Chart.prototype, {
|
|
398
399
|
* @param {Object} chartOptions Additional chart options for the SVG representation of the chart
|
399
400
|
*/
|
400
401
|
exportChart: function (options, chartOptions) {
|
401
|
-
|
402
|
+
|
402
403
|
var svg = this.getSVGForExport(options, chartOptions);
|
403
404
|
|
404
405
|
// merge the options
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license Highcharts JS v4.2.
|
2
|
+
* @license Highcharts JS v4.2.7 (2016-09-21)
|
3
3
|
*
|
4
4
|
* (c) 2011-2016 Torstein Honsi
|
5
5
|
*
|
@@ -42,7 +42,6 @@
|
|
42
42
|
* The ColorAxis object for inclusion in gradient legends
|
43
43
|
*/
|
44
44
|
var ColorAxis = Highcharts.ColorAxis = function () {
|
45
|
-
this.isColorAxis = true;
|
46
45
|
this.init.apply(this, arguments);
|
47
46
|
};
|
48
47
|
extend(ColorAxis.prototype, Axis.prototype);
|
@@ -68,12 +67,15 @@
|
|
68
67
|
},
|
69
68
|
minColor: '#EFEFFF',
|
70
69
|
maxColor: '#003875',
|
71
|
-
tickLength: 5
|
70
|
+
tickLength: 5,
|
71
|
+
showInLegend: true
|
72
72
|
},
|
73
73
|
init: function (chart, userOptions) {
|
74
74
|
var horiz = chart.options.legend.layout !== 'vertical',
|
75
75
|
options;
|
76
76
|
|
77
|
+
this.coll = 'colorAxis';
|
78
|
+
|
77
79
|
// Build the options
|
78
80
|
options = merge(this.defaultColorAxisOptions, {
|
79
81
|
side: horiz ? 2 : 1,
|
@@ -81,8 +83,7 @@
|
|
81
83
|
}, userOptions, {
|
82
84
|
opposite: !horiz,
|
83
85
|
showEmpty: false,
|
84
|
-
title: null
|
85
|
-
isColor: true
|
86
|
+
title: null
|
86
87
|
});
|
87
88
|
|
88
89
|
Axis.prototype.init.call(this, chart, options);
|
@@ -99,6 +100,9 @@
|
|
99
100
|
// Override original axis properties
|
100
101
|
this.horiz = horiz;
|
101
102
|
this.zoomEnabled = false;
|
103
|
+
|
104
|
+
// Add default values
|
105
|
+
this.defaultLegendLength = 200;
|
102
106
|
},
|
103
107
|
|
104
108
|
/*
|
@@ -183,12 +187,12 @@
|
|
183
187
|
Axis.prototype.setOptions.call(this, userOptions);
|
184
188
|
|
185
189
|
this.options.crosshair = this.options.marker;
|
186
|
-
this.coll = 'colorAxis';
|
187
190
|
},
|
188
191
|
|
189
192
|
setAxisSize: function () {
|
190
193
|
var symbol = this.legendSymbol,
|
191
194
|
chart = this.chart,
|
195
|
+
legendOptions = chart.options.legend || {},
|
192
196
|
x,
|
193
197
|
y,
|
194
198
|
width,
|
@@ -204,6 +208,9 @@
|
|
204
208
|
|
205
209
|
this.len = this.horiz ? width : height;
|
206
210
|
this.pos = this.horiz ? x : y;
|
211
|
+
} else {
|
212
|
+
// Fake length for disabled legend to avoid tick issues and such (#5205)
|
213
|
+
this.len = (this.horiz ? legendOptions.symbolWidth : legendOptions.symbolHeight) || this.defaultLegendLength;
|
207
214
|
}
|
208
215
|
},
|
209
216
|
|
@@ -318,8 +325,8 @@
|
|
318
325
|
var padding = legend.padding,
|
319
326
|
legendOptions = legend.options,
|
320
327
|
horiz = this.horiz,
|
321
|
-
width = pick(legendOptions.symbolWidth, horiz ?
|
322
|
-
height = pick(legendOptions.symbolHeight, horiz ? 12 :
|
328
|
+
width = pick(legendOptions.symbolWidth, horiz ? this.defaultLegendLength : 12),
|
329
|
+
height = pick(legendOptions.symbolHeight, horiz ? 12 : this.defaultLegendLength),
|
323
330
|
labelPadding = pick(legendOptions.labelPadding, horiz ? 16 : 30),
|
324
331
|
itemDistance = pick(legendOptions.itemDistance, 10);
|
325
332
|
|
@@ -520,14 +527,15 @@
|
|
520
527
|
colorAxis = this.chart.colorAxis[0];
|
521
528
|
|
522
529
|
if (colorAxis) {
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
530
|
+
if (colorAxis.options.showInLegend) {
|
531
|
+
// Data classes
|
532
|
+
if (colorAxis.options.dataClasses) {
|
533
|
+
allItems = allItems.concat(colorAxis.getDataClassLegendSymbols());
|
534
|
+
// Gradient legend
|
535
|
+
} else {
|
536
|
+
// Add this axis on top
|
537
|
+
allItems.push(colorAxis);
|
538
|
+
}
|
531
539
|
}
|
532
540
|
|
533
541
|
// Don't add the color axis' series
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license Highcharts JS v4.2.
|
2
|
+
* @license Highcharts JS v4.2.7 (2016-09-21)
|
3
3
|
* Client side exporting module
|
4
4
|
*
|
5
5
|
* (c) 2015 Torstein Honsi / Oystein Moseng
|
@@ -325,7 +325,6 @@
|
|
325
325
|
};
|
326
326
|
|
327
327
|
// If we have embedded images and are exporting to JPEG/PNG, Microsoft browsers won't handle it, so fall back
|
328
|
-
// docs
|
329
328
|
if (isMSBrowser && imageType !== 'image/svg+xml' && chart.container.getElementsByTagName('image').length) {
|
330
329
|
fallbackToExportServer();
|
331
330
|
return;
|
@@ -0,0 +1,519 @@
|
|
1
|
+
/**
|
2
|
+
* EXPERIMENTAL Highcharts module to place labels next to a series in a natural position.
|
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/
|
14
|
+
*/
|
15
|
+
/* eslint indent: [2, 4] */
|
16
|
+
(function (factory) {
|
17
|
+
if (typeof module === 'object' && module.exports) {
|
18
|
+
module.exports = factory;
|
19
|
+
} else {
|
20
|
+
factory(Highcharts);
|
21
|
+
}
|
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'
|
44
|
+
}
|
45
|
+
// boxesToAvoid: []
|
46
|
+
}
|
47
|
+
}
|
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
|
+
}
|
58
|
+
|
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
|
+
}
|
66
|
+
|
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
|
+
}
|
78
|
+
|
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
|
+
}
|
118
|
+
}
|
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
|
+
|
172
|
+
// Interpolate
|
173
|
+
} else {
|
174
|
+
len = points.length;
|
175
|
+
for (i = 0; i < len; i += 1) {
|
176
|
+
|
177
|
+
point = points[i];
|
178
|
+
last = points[i - 1];
|
179
|
+
|
180
|
+
// Absolute coordinates so we can compare different panes
|
181
|
+
point.chartX = paneLeft + point.plotX;
|
182
|
+
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
|
+
});
|
200
|
+
}
|
201
|
+
}
|
202
|
+
}
|
203
|
+
|
204
|
+
// Add the real point in order to find positive and negative peaks
|
205
|
+
if (isNumber(point.plotY)) {
|
206
|
+
interpolated.push(point);
|
207
|
+
}
|
208
|
+
}
|
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
|
+
}
|
237
|
+
|
238
|
+
/**
|
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.
|
242
|
+
*/
|
243
|
+
function getWeight(distToOthersSquared, distToPointSquared) {
|
244
|
+
return distToOthersSquared - distToPointSquared;
|
245
|
+
}
|
246
|
+
|
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
|
+
}
|
258
|
+
|
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
|
+
}
|
279
|
+
|
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
|
+
}
|
293
|
+
|
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
|
+
);
|
304
|
+
}
|
305
|
+
}
|
306
|
+
|
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];
|
321
|
+
}
|
322
|
+
}
|
323
|
+
withinRange = true;
|
324
|
+
}
|
325
|
+
}
|
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
|
+
|
335
|
+
};
|
336
|
+
|
337
|
+
|
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
|
+
|
345
|
+
var chart = this;
|
346
|
+
|
347
|
+
proceed.call(chart);
|
348
|
+
|
349
|
+
clearTimeout(chart.seriesLabelTimer);
|
350
|
+
|
351
|
+
chart.seriesLabelTimer = setTimeout(function () {
|
352
|
+
|
353
|
+
chart.boxesToAvoid = [];
|
354
|
+
|
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();
|
360
|
+
|
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
|
+
}
|
386
|
+
|
387
|
+
if (series.visible && points) {
|
388
|
+
|
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 });
|
402
|
+
}
|
403
|
+
|
404
|
+
bBox = series.labelBySeries.getBBox();
|
405
|
+
bBox.width = Math.round(bBox.width);
|
406
|
+
|
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) {
|
409
|
+
|
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);
|
422
|
+
}
|
423
|
+
|
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
|
+
}
|
437
|
+
|
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
|
+
}
|
451
|
+
|
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
|
+
}
|
465
|
+
|
466
|
+
}
|
467
|
+
|
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);
|
475
|
+
}
|
476
|
+
}
|
477
|
+
}
|
478
|
+
}
|
479
|
+
|
480
|
+
if (results.length) {
|
481
|
+
|
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
|
+
});
|
494
|
+
|
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
|
+
}
|
507
|
+
|
508
|
+
} else if (series.labelBySeries) {
|
509
|
+
series.labelBySeries = series.labelBySeries.destroy();
|
510
|
+
}
|
511
|
+
}
|
512
|
+
});
|
513
|
+
}, 350);
|
514
|
+
|
515
|
+
}
|
516
|
+
wrap(Chart.prototype, 'render', drawLabels);
|
517
|
+
wrap(Chart.prototype, 'redraw', drawLabels);
|
518
|
+
|
519
|
+
}));
|