highcharts_rails 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +106 -0
- data/Rakefile +6 -0
- data/highcharts_rails.gemspec +27 -0
- data/lib/highcharts_rails/version.rb +3 -0
- data/lib/highcharts_rails.rb +8 -0
- data/vendor/assets/javascripts/highcharts-3d.src.js +2139 -0
- data/vendor/assets/javascripts/highcharts-more.src.js +2982 -0
- data/vendor/assets/javascripts/highcharts.src.js +22947 -0
- data/vendor/assets/javascripts/js/highcharts-3d.src.js +2085 -0
- data/vendor/assets/javascripts/js/highcharts-more.src.js +2820 -0
- data/vendor/assets/javascripts/js/highcharts.src.js +20917 -0
- data/vendor/assets/javascripts/js/modules/accessibility.src.js +1072 -0
- data/vendor/assets/javascripts/js/modules/annotations.src.js +408 -0
- data/vendor/assets/javascripts/js/modules/boost.src.js +652 -0
- data/vendor/assets/javascripts/js/modules/broken-axis.src.js +338 -0
- data/vendor/assets/javascripts/js/modules/data.src.js +981 -0
- data/vendor/assets/javascripts/js/modules/drilldown.src.js +756 -0
- data/vendor/assets/javascripts/js/modules/exporting.src.js +953 -0
- data/vendor/assets/javascripts/js/modules/funnel.src.js +290 -0
- data/vendor/assets/javascripts/js/modules/gantt.src.js +791 -0
- data/vendor/assets/javascripts/js/modules/grid-axis.src.js +545 -0
- data/vendor/assets/javascripts/js/modules/heatmap.src.js +798 -0
- data/vendor/assets/javascripts/js/modules/no-data-to-display.src.js +150 -0
- data/vendor/assets/javascripts/js/modules/offline-exporting.src.js +492 -0
- data/vendor/assets/javascripts/js/modules/overlapping-datalabels.src.js +164 -0
- data/vendor/assets/javascripts/js/modules/series-label.src.js +606 -0
- data/vendor/assets/javascripts/js/modules/solid-gauge.src.js +305 -0
- data/vendor/assets/javascripts/js/modules/treemap.src.js +881 -0
- data/vendor/assets/javascripts/js/modules/xrange-series.src.js +254 -0
- data/vendor/assets/javascripts/js/themes/dark-blue.js +317 -0
- data/vendor/assets/javascripts/js/themes/dark-green.js +314 -0
- data/vendor/assets/javascripts/js/themes/dark-unica.js +243 -0
- data/vendor/assets/javascripts/js/themes/gray.js +326 -0
- data/vendor/assets/javascripts/js/themes/grid-light.js +99 -0
- data/vendor/assets/javascripts/js/themes/grid.js +131 -0
- data/vendor/assets/javascripts/js/themes/sand-signika.js +129 -0
- data/vendor/assets/javascripts/js/themes/skies.js +112 -0
- data/vendor/assets/javascripts/lib/canvg.src.js +3073 -0
- data/vendor/assets/javascripts/lib/jspdf.src.js +3031 -0
- data/vendor/assets/javascripts/lib/rgbcolor.src.js +299 -0
- data/vendor/assets/javascripts/lib/svg2pdf.src.js +1451 -0
- data/vendor/assets/javascripts/modules/accessibility.src.js +1072 -0
- data/vendor/assets/javascripts/modules/annotations.src.js +408 -0
- data/vendor/assets/javascripts/modules/boost.src.js +652 -0
- data/vendor/assets/javascripts/modules/broken-axis.src.js +338 -0
- data/vendor/assets/javascripts/modules/data.src.js +981 -0
- data/vendor/assets/javascripts/modules/drilldown.src.js +797 -0
- data/vendor/assets/javascripts/modules/exporting.src.js +882 -0
- data/vendor/assets/javascripts/modules/funnel.src.js +304 -0
- data/vendor/assets/javascripts/modules/gantt.src.js +815 -0
- data/vendor/assets/javascripts/modules/grid-axis.src.js +547 -0
- data/vendor/assets/javascripts/modules/heatmap.src.js +810 -0
- data/vendor/assets/javascripts/modules/no-data-to-display.src.js +161 -0
- data/vendor/assets/javascripts/modules/offline-exporting.src.js +492 -0
- data/vendor/assets/javascripts/modules/overlapping-datalabels.src.js +164 -0
- data/vendor/assets/javascripts/modules/series-label.src.js +606 -0
- data/vendor/assets/javascripts/modules/solid-gauge.src.js +316 -0
- data/vendor/assets/javascripts/modules/treemap.src.js +935 -0
- data/vendor/assets/javascripts/modules/xrange-series.src.js +276 -0
- data/vendor/assets/javascripts/themes/dark-blue.js +317 -0
- data/vendor/assets/javascripts/themes/dark-green.js +314 -0
- data/vendor/assets/javascripts/themes/dark-unica.js +243 -0
- data/vendor/assets/javascripts/themes/gray.js +326 -0
- data/vendor/assets/javascripts/themes/grid-light.js +99 -0
- data/vendor/assets/javascripts/themes/grid.js +131 -0
- data/vendor/assets/javascripts/themes/sand-signika.js +129 -0
- data/vendor/assets/javascripts/themes/skies.js +112 -0
- data/vendor/assets/stylesheets/highcharts.scss +610 -0
- metadata +161 -0
@@ -0,0 +1,606 @@
|
|
1
|
+
/**
|
2
|
+
* @license Highcharts JS v5.0.6 (2016-12-07)
|
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
|
+
/**
|
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: []
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
});
|
64
|
+
|
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
|
+
}
|
72
|
+
|
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
|
+
}
|
80
|
+
|
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
|
+
);
|
91
|
+
}
|
92
|
+
|
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);
|
119
|
+
|
120
|
+
// Anchor above label
|
121
|
+
} else if (anchorY < y) {
|
122
|
+
path.push('L', x + lateral, y);
|
123
|
+
|
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];
|
186
|
+
point.chartX = paneLeft + point.plotX;
|
187
|
+
point.chartY = paneTop + point.plotY;
|
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
|
+
}
|
219
|
+
}
|
220
|
+
}
|
221
|
+
|
222
|
+
// Add the real point in order to find positive and negative peaks
|
223
|
+
if (isNumber(point.plotY)) {
|
224
|
+
interpolated.push(point);
|
225
|
+
}
|
226
|
+
}
|
227
|
+
}
|
228
|
+
return interpolated;
|
229
|
+
};
|
230
|
+
|
231
|
+
/**
|
232
|
+
* Check whether a proposed label position is clear of other elements
|
233
|
+
*/
|
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
|
+
}
|
255
|
+
|
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;
|
263
|
+
}
|
264
|
+
|
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
|
+
}
|
276
|
+
|
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
|
+
}
|
311
|
+
|
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
|
+
}
|
323
|
+
}
|
324
|
+
|
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
|
+
}
|
340
|
+
}
|
341
|
+
withinRange = true;
|
342
|
+
}
|
343
|
+
}
|
344
|
+
}
|
345
|
+
|
346
|
+
return !checkDistance || withinRange ? {
|
347
|
+
x: x,
|
348
|
+
y: y,
|
349
|
+
weight: getWeight(distToOthersSquared, connectorPoint ? distToPointSquared : 0),
|
350
|
+
connectorPoint: connectorPoint
|
351
|
+
} : false;
|
352
|
+
|
353
|
+
};
|
354
|
+
|
355
|
+
/**
|
356
|
+
* The main initiator method that runs on chart level after initiation and redraw. It runs in
|
357
|
+
* a timeout to prevent locking, and loops over all series, taking all series and labels into
|
358
|
+
* account when placing the labels.
|
359
|
+
*/
|
360
|
+
Chart.prototype.drawSeriesLabels = function() {
|
361
|
+
var chart = this,
|
362
|
+
labelSeries = this.labelSeries;
|
363
|
+
|
364
|
+
chart.boxesToAvoid = [];
|
365
|
+
|
366
|
+
// Build the interpolated points
|
367
|
+
each(labelSeries, function(series) {
|
368
|
+
series.interpolatedPoints = series.getPointsOnGraph();
|
369
|
+
|
370
|
+
each(series.options.label.boxesToAvoid || [], function(box) {
|
371
|
+
chart.boxesToAvoid.push(box);
|
372
|
+
});
|
373
|
+
});
|
374
|
+
|
375
|
+
each(chart.series, function(series) {
|
376
|
+
var bBox,
|
377
|
+
x,
|
378
|
+
y,
|
379
|
+
results = [],
|
380
|
+
clearPoint,
|
381
|
+
i,
|
382
|
+
best,
|
383
|
+
inverted = chart.inverted,
|
384
|
+
paneLeft = inverted ? series.yAxis.pos : series.xAxis.pos,
|
385
|
+
paneTop = inverted ? series.xAxis.pos : series.yAxis.pos,
|
386
|
+
paneWidth = chart.inverted ? series.yAxis.len : series.xAxis.len,
|
387
|
+
paneHeight = chart.inverted ? series.xAxis.len : series.yAxis.len,
|
388
|
+
points = series.interpolatedPoints,
|
389
|
+
label = series.labelBySeries;
|
390
|
+
|
391
|
+
function insidePane(x, y, bBox) {
|
392
|
+
return x > paneLeft && x <= paneLeft + paneWidth - bBox.width &&
|
393
|
+
y >= paneTop && y <= paneTop + paneHeight - bBox.height;
|
394
|
+
}
|
395
|
+
|
396
|
+
if (series.visible && points) {
|
397
|
+
if (!label) {
|
398
|
+
series.labelBySeries = label = chart.renderer
|
399
|
+
.label(series.name, 0, -9999, 'connector')
|
400
|
+
.css(extend({
|
401
|
+
color: series.color
|
402
|
+
}, series.options.label.styles))
|
403
|
+
.attr({
|
404
|
+
padding: 0,
|
405
|
+
opacity: 0,
|
406
|
+
stroke: series.color,
|
407
|
+
'stroke-width': 1
|
408
|
+
})
|
409
|
+
.add(series.group)
|
410
|
+
.animate({
|
411
|
+
opacity: 1
|
412
|
+
}, {
|
413
|
+
duration: 200
|
414
|
+
});
|
415
|
+
}
|
416
|
+
|
417
|
+
bBox = label.getBBox();
|
418
|
+
bBox.width = Math.round(bBox.width);
|
419
|
+
|
420
|
+
// Ideal positions are centered above or below a point on right side
|
421
|
+
// of chart
|
422
|
+
for (i = points.length - 1; i > 0; i -= 1) {
|
423
|
+
|
424
|
+
// Right - up
|
425
|
+
x = points[i].chartX + labelDistance;
|
426
|
+
y = points[i].chartY - bBox.height - 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
|
+
// Right - down
|
439
|
+
x = points[i].chartX + 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 - down
|
453
|
+
x = points[i].chartX - bBox.width - labelDistance;
|
454
|
+
y = points[i].chartY + 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
|
+
// Left - up
|
467
|
+
x = points[i].chartX - bBox.width - labelDistance;
|
468
|
+
y = points[i].chartY - bBox.height - labelDistance;
|
469
|
+
if (insidePane(x, y, bBox)) {
|
470
|
+
best = series.checkClearPoint(
|
471
|
+
x,
|
472
|
+
y,
|
473
|
+
bBox
|
474
|
+
);
|
475
|
+
}
|
476
|
+
if (best) {
|
477
|
+
results.push(best);
|
478
|
+
}
|
479
|
+
|
480
|
+
}
|
481
|
+
|
482
|
+
// Brute force, try all positions on the chart in a 16x16 grid
|
483
|
+
if (!results.length) {
|
484
|
+
for (x = paneLeft + paneWidth - bBox.width; x >= paneLeft; x -= 16) {
|
485
|
+
for (y = paneTop; y < paneTop + paneHeight - bBox.height; y += 16) {
|
486
|
+
clearPoint = series.checkClearPoint(x, y, bBox, true);
|
487
|
+
if (clearPoint) {
|
488
|
+
results.push(clearPoint);
|
489
|
+
}
|
490
|
+
}
|
491
|
+
}
|
492
|
+
}
|
493
|
+
|
494
|
+
if (results.length) {
|
495
|
+
|
496
|
+
results.sort(function(a, b) {
|
497
|
+
return b.weight - a.weight;
|
498
|
+
});
|
499
|
+
|
500
|
+
best = results[0];
|
501
|
+
|
502
|
+
chart.boxesToAvoid.push({
|
503
|
+
left: best.x,
|
504
|
+
right: best.x + bBox.width,
|
505
|
+
top: best.y,
|
506
|
+
bottom: best.y + bBox.height
|
507
|
+
});
|
508
|
+
|
509
|
+
// Move it if needed
|
510
|
+
if (Math.round(best.x) !== Math.round(label.x) ||
|
511
|
+
Math.round(best.y) !== Math.round(label.y)) {
|
512
|
+
series.labelBySeries
|
513
|
+
.attr({
|
514
|
+
opacity: 0,
|
515
|
+
x: best.x - paneLeft,
|
516
|
+
y: best.y - paneTop,
|
517
|
+
anchorX: best.connectorPoint && best.connectorPoint.plotX,
|
518
|
+
anchorY: best.connectorPoint && best.connectorPoint.plotY
|
519
|
+
})
|
520
|
+
.animate({
|
521
|
+
opacity: 1
|
522
|
+
});
|
523
|
+
|
524
|
+
// Record closest point to stick to for sync redraw
|
525
|
+
series.options.kdNow = true;
|
526
|
+
series.buildKDTree();
|
527
|
+
var closest = series.searchPoint({
|
528
|
+
chartX: best.x,
|
529
|
+
chartY: best.y
|
530
|
+
}, true);
|
531
|
+
label.closest = [
|
532
|
+
closest,
|
533
|
+
best.x - paneLeft - closest.plotX,
|
534
|
+
best.y - paneTop - closest.plotY
|
535
|
+
];
|
536
|
+
|
537
|
+
}
|
538
|
+
|
539
|
+
} else if (label) {
|
540
|
+
series.labelBySeries = label.destroy();
|
541
|
+
}
|
542
|
+
}
|
543
|
+
});
|
544
|
+
};
|
545
|
+
|
546
|
+
/**
|
547
|
+
* Prepare drawing series labels
|
548
|
+
*/
|
549
|
+
function drawLabels(proceed) {
|
550
|
+
|
551
|
+
var chart = this,
|
552
|
+
delay = Math.max(
|
553
|
+
H.animObject(chart.renderer.globalAnimation).duration,
|
554
|
+
250
|
555
|
+
),
|
556
|
+
initial = !chart.hasRendered;
|
557
|
+
|
558
|
+
proceed.apply(chart, [].slice.call(arguments, 1));
|
559
|
+
|
560
|
+
chart.labelSeries = [];
|
561
|
+
|
562
|
+
clearTimeout(chart.seriesLabelTimer);
|
563
|
+
|
564
|
+
// Which series should have labels
|
565
|
+
each(chart.series, function(series) {
|
566
|
+
var options = series.options.label,
|
567
|
+
label = series.labelBySeries,
|
568
|
+
closest = label && label.closest;
|
569
|
+
|
570
|
+
if (options.enabled && series.visible && (series.graph || series.area)) {
|
571
|
+
chart.labelSeries.push(series);
|
572
|
+
|
573
|
+
// The labels are processing heavy, wait until the animation is done
|
574
|
+
if (initial) {
|
575
|
+
delay = Math.max(
|
576
|
+
delay,
|
577
|
+
H.animObject(series.options.animation).duration
|
578
|
+
);
|
579
|
+
}
|
580
|
+
|
581
|
+
// Keep the position updated to the axis while redrawing
|
582
|
+
if (closest) {
|
583
|
+
if (closest[0].plotX !== undefined) {
|
584
|
+
label.animate({
|
585
|
+
x: closest[0].plotX + closest[1],
|
586
|
+
y: closest[0].plotY + closest[2]
|
587
|
+
});
|
588
|
+
} else {
|
589
|
+
label.attr({
|
590
|
+
opacity: 0
|
591
|
+
});
|
592
|
+
}
|
593
|
+
}
|
594
|
+
}
|
595
|
+
});
|
596
|
+
|
597
|
+
chart.seriesLabelTimer = setTimeout(function() {
|
598
|
+
chart.drawSeriesLabels();
|
599
|
+
}, delay);
|
600
|
+
|
601
|
+
}
|
602
|
+
wrap(Chart.prototype, 'render', drawLabels);
|
603
|
+
wrap(Chart.prototype, 'redraw', drawLabels);
|
604
|
+
|
605
|
+
}(Highcharts));
|
606
|
+
}));
|