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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Highcharts JS v4.2.6 (2016-08-02)
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.6 (2016-08-02)
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 ? 200 : 12),
322
- height = pick(legendOptions.symbolHeight, horiz ? 12 : 200),
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
- // Data classes
525
- if (colorAxis.options.dataClasses) {
526
- allItems = allItems.concat(colorAxis.getDataClassLegendSymbols());
527
- // Gradient legend
528
- } else {
529
- // Add this axis on top
530
- allItems.push(colorAxis);
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.6 (2016-08-02)
2
+ * @license Highcharts JS v4.2.7 (2016-09-21)
3
3
  * Plugin for displaying a message when there is no data visible in chart.
4
4
  *
5
5
  * (c) 2010-2016 Highsoft AS
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Highcharts JS v4.2.6 (2016-08-02)
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
+ }));