highcharts-rails 4.2.6 → 4.2.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }));