highcharts-rails 5.0.14 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.markdown +60 -0
  3. data/Rakefile +54 -5
  4. data/app/assets/images/highcharts/earth.svg +432 -0
  5. data/app/assets/javascripts/highcharts.js +5103 -3147
  6. data/app/assets/javascripts/highcharts/highcharts-3d.js +930 -277
  7. data/app/assets/javascripts/highcharts/highcharts-more.js +1374 -249
  8. data/app/assets/javascripts/highcharts/lib/canvg.js +3073 -0
  9. data/app/assets/javascripts/highcharts/lib/jspdf.js +16624 -0
  10. data/app/assets/javascripts/highcharts/lib/rgbcolor.js +299 -0
  11. data/app/assets/javascripts/highcharts/lib/svg2pdf.js +3488 -0
  12. data/app/assets/javascripts/highcharts/modules/accessibility.js +654 -212
  13. data/app/assets/javascripts/highcharts/modules/annotations.js +1552 -274
  14. data/app/assets/javascripts/highcharts/modules/boost-canvas.js +773 -0
  15. data/app/assets/javascripts/highcharts/modules/boost.js +636 -210
  16. data/app/assets/javascripts/highcharts/modules/broken-axis.js +2 -2
  17. data/app/assets/javascripts/highcharts/modules/bullet.js +364 -0
  18. data/app/assets/javascripts/highcharts/modules/data.js +766 -38
  19. data/app/assets/javascripts/highcharts/modules/drag-panes.js +588 -0
  20. data/app/assets/javascripts/highcharts/modules/drilldown.js +106 -36
  21. data/app/assets/javascripts/highcharts/modules/export-data.js +597 -0
  22. data/app/assets/javascripts/highcharts/modules/exporting.js +424 -162
  23. data/app/assets/javascripts/highcharts/modules/funnel.js +144 -22
  24. data/app/assets/javascripts/highcharts/modules/gantt.js +1154 -0
  25. data/app/assets/javascripts/highcharts/modules/grid-axis.js +1 -1
  26. data/app/assets/javascripts/highcharts/modules/heatmap.js +406 -80
  27. data/app/assets/javascripts/highcharts/modules/histogram-bellcurve.js +513 -0
  28. data/app/assets/javascripts/highcharts/modules/item-series.js +126 -0
  29. data/app/assets/javascripts/highcharts/modules/no-data-to-display.js +31 -13
  30. data/app/assets/javascripts/highcharts/modules/offline-exporting.js +179 -57
  31. data/app/assets/javascripts/highcharts/modules/oldie.js +1378 -0
  32. data/app/assets/javascripts/highcharts/modules/overlapping-datalabels.js +8 -6
  33. data/app/assets/javascripts/highcharts/modules/parallel-coordinates.js +494 -0
  34. data/app/assets/javascripts/highcharts/modules/pareto.js +275 -0
  35. data/app/assets/javascripts/highcharts/modules/sankey.js +641 -0
  36. data/app/assets/javascripts/highcharts/modules/series-label.js +355 -145
  37. data/app/assets/javascripts/highcharts/modules/solid-gauge.js +122 -1
  38. data/app/assets/javascripts/highcharts/modules/static-scale.js +64 -0
  39. data/app/assets/javascripts/highcharts/modules/stock.js +1944 -676
  40. data/app/assets/javascripts/highcharts/modules/streamgraph.js +139 -0
  41. data/app/assets/javascripts/highcharts/modules/sunburst.js +2403 -0
  42. data/app/assets/javascripts/highcharts/modules/tilemap.js +1199 -0
  43. data/app/assets/javascripts/highcharts/modules/treemap.js +538 -134
  44. data/app/assets/javascripts/highcharts/modules/variable-pie.js +490 -0
  45. data/app/assets/javascripts/highcharts/modules/variwide.js +283 -0
  46. data/app/assets/javascripts/highcharts/modules/vector.js +294 -0
  47. data/app/assets/javascripts/highcharts/modules/windbarb.js +490 -0
  48. data/app/assets/javascripts/highcharts/modules/wordcloud.js +681 -0
  49. data/app/assets/javascripts/highcharts/modules/xrange.js +615 -0
  50. data/app/assets/javascripts/highcharts/themes/avocado.js +54 -0
  51. data/app/assets/javascripts/highcharts/themes/dark-blue.js +6 -6
  52. data/app/assets/javascripts/highcharts/themes/dark-green.js +6 -6
  53. data/app/assets/javascripts/highcharts/themes/dark-unica.js +6 -6
  54. data/app/assets/javascripts/highcharts/themes/gray.js +14 -10
  55. data/app/assets/javascripts/highcharts/themes/grid-light.js +6 -6
  56. data/app/assets/javascripts/highcharts/themes/grid.js +7 -5
  57. data/app/assets/javascripts/highcharts/themes/sand-signika.js +8 -7
  58. data/app/assets/javascripts/highcharts/themes/skies.js +15 -9
  59. data/app/assets/javascripts/highcharts/themes/sunset.js +53 -0
  60. data/app/assets/stylesheets/highcharts/highcharts.css +802 -0
  61. data/app/assets/stylesheets/highcharts/highcharts.scss +665 -0
  62. data/lib/highcharts/version.rb +1 -1
  63. metadata +31 -1
@@ -1,5 +1,6 @@
1
1
  /**
2
- * @license Highcharts JS v5.0.14 (2017-07-28)
2
+ * @license Highcharts JS v6.0.0 (2017-10-04)
3
+ * Annotations module
3
4
  *
4
5
  * (c) 2009-2017 Torstein Honsi
5
6
  *
@@ -15,393 +16,1670 @@
15
16
  }(function(Highcharts) {
16
17
  (function(H) {
17
18
  /**
18
- * (c) 2009-2017 Torstein Honsi
19
+ * (c) 2009-2017 Highsoft, Black Label
19
20
  *
20
21
  * License: www.highcharts.com/license
21
22
  */
22
23
 
23
- var defined = H.defined,
24
+ var merge = H.merge,
25
+ addEvent = H.addEvent,
26
+ extend = H.extend,
27
+ each = H.each,
28
+ isString = H.isString,
24
29
  isNumber = H.isNumber,
30
+ defined = H.defined,
31
+ isObject = H.isObject,
25
32
  inArray = H.inArray,
26
- isArray = H.isArray,
27
- merge = H.merge,
28
- Chart = H.Chart,
29
- extend = H.extend,
30
- each = H.each;
33
+ erase = H.erase,
34
+ find = H.find,
35
+ format = H.format,
36
+ pick = H.pick,
37
+ destroyObjectProperties = H.destroyObjectProperties,
38
+
39
+ tooltipPrototype = H.Tooltip.prototype,
40
+ seriesPrototype = H.Series.prototype,
41
+ chartPrototype = H.Chart.prototype;
31
42
 
32
- var ALIGN_FACTOR,
33
- ALLOWED_SHAPES;
34
43
 
35
- ALLOWED_SHAPES = ['path', 'rect', 'circle'];
44
+ /* ***************************************************************************
45
+ *
46
+ * MARKER SECTION
47
+ * Contains objects and functions for adding a marker element to a path element
48
+ *
49
+ **************************************************************************** */
36
50
 
37
- ALIGN_FACTOR = {
38
- top: 0,
39
- left: 0,
40
- center: 0.5,
41
- middle: 0.5,
42
- bottom: 1,
43
- right: 1
51
+ /**
52
+ * Options for configuring markers for annotations.
53
+ *
54
+ * An example of the arrow marker:
55
+ * <pre>
56
+ * {
57
+ * arrow: {
58
+ * id: 'arrow',
59
+ * refY: 5,
60
+ * refX: 5,
61
+ * markerWidth: 10,
62
+ * markerHeight: 10,
63
+ * children: [{
64
+ * tagName: 'path',
65
+ * attrs: {
66
+ * d: 'M 0 0 L 10 5 L 0 10 Z',
67
+ * strokeWidth: 0
68
+ * }
69
+ * }]
70
+ * }
71
+ * }
72
+ * </pre>
73
+ * @type {Object}
74
+ * @sample highcharts/annotations/custom-markers/
75
+ * Define a custom marker for annotations
76
+ * @since 6.0.0
77
+ * @apioption defs.markers
78
+ */
79
+ var defaultMarkers = {
80
+ arrow: {
81
+ render: false,
82
+ id: 'arrow',
83
+ refY: 5,
84
+ refX: 5,
85
+ markerWidth: 10,
86
+ markerHeight: 10,
87
+ children: [{
88
+ tagName: 'path',
89
+ attrs: {
90
+ d: 'M 0 0 L 10 5 L 0 10 Z', // triangle (used as an arrow)
91
+ strokeWidth: 0
92
+ }
93
+ }]
94
+ }
44
95
  };
45
96
 
46
- function defaultOptions(shapeType) {
47
- var shapeOptions,
48
- options;
97
+ var MarkerMixin = {
98
+ markerSetter: function(markerType) {
99
+ return function(value) {
100
+ this.attr(markerType, 'url(#' + value + ')');
101
+ };
102
+ }
103
+ };
49
104
 
50
- options = {
51
- xAxis: 0,
52
- yAxis: 0,
53
- title: {
54
- style: {},
55
- text: '',
56
- x: 0,
57
- y: 0
105
+ extend(MarkerMixin, {
106
+ markerEndSetter: MarkerMixin.markerSetter('marker-end'),
107
+ markerStartSetter: MarkerMixin.markerSetter('marker-start')
108
+ });
109
+
110
+
111
+ H.SVGRenderer.prototype.addMarker = function(id, markerOptions) {
112
+ var markerId = pick(id, H.uniqueKey()),
113
+ marker = this.createElement('marker').attr({
114
+ id: markerId,
115
+ markerWidth: pick(markerOptions.markerWidth, 20),
116
+ markerHeight: pick(markerOptions.markerHeight, 20),
117
+ refX: markerOptions.refX || 0,
118
+ refY: markerOptions.refY || 0,
119
+ orient: markerOptions.orient || 'auto'
120
+ }).add(this.defs),
121
+
122
+ attrs = {
123
+ stroke: markerOptions.color || 'none',
124
+ fill: markerOptions.color || 'rgba(0, 0, 0, 0.75)'
58
125
  },
59
- shape: {
60
- params: {
61
- stroke: '#000000',
62
- fill: 'transparent',
63
- strokeWidth: 2
64
- }
65
- }
66
- };
126
+ children = markerOptions.children;
67
127
 
68
- shapeOptions = {
69
- circle: {
70
- params: {
71
- x: 0,
72
- y: 0
73
- }
74
- }
75
- };
128
+ marker.id = markerId;
76
129
 
77
- if (shapeOptions[shapeType]) {
78
- options.shape = merge(options.shape, shapeOptions[shapeType]);
79
- }
130
+ each(children, function(child) {
131
+ this.createElement(child.tagName)
132
+ .attr(merge(attrs, child.attrs))
133
+ .add(marker);
134
+ }, this);
80
135
 
81
- return options;
82
- }
136
+ return marker;
137
+ };
83
138
 
84
- function translatePath(d, xAxis, yAxis, xOffset, yOffset) {
85
- var len = d.length,
86
- i = 0;
87
139
 
88
- while (i < len) {
89
- if (isNumber(d[i]) && isNumber(d[i + 1])) {
90
- d[i] = xAxis.toPixels(d[i]) - xOffset;
91
- d[i + 1] = yAxis.toPixels(d[i + 1]) - yOffset;
92
- i += 2;
93
- } else {
94
- i += 1;
95
- }
96
- }
140
+ /* ***************************************************************************
141
+ *
142
+ * MOCK POINT
143
+ *
144
+ **************************************************************************** */
145
+
146
+ /**
147
+ * A mock point configuration.
148
+ *
149
+ * @typedef {Object} MockPointOptions
150
+ * @property {Number} x - x value for the point in xAxis scale or pixels
151
+ * @property {Number} y - y value for the point in yAxis scale or pixels
152
+ * @property {String|Number} [xAxis] - xAxis index or id
153
+ * @property {String|Number} [yAxis] - yAxis index or id
154
+ */
155
+
156
+
157
+ /**
158
+ * A trimmed point object which imitates {@link Highchart.Point} class.
159
+ * It is created when there is a need of pointing to some chart's position
160
+ * using axis values or pixel values
161
+ *
162
+ * @class MockPoint
163
+ * @memberOf Highcharts
164
+ *
165
+ * @param {Highcharts.Chart} - the chart object
166
+ * @param {MockPointOptions} - the options object
167
+ */
168
+ var MockPoint = H.MockPoint = function(chart, options) {
169
+ this.mock = true;
170
+ this.series = {
171
+ visible: true,
172
+ chart: chart,
173
+ getPlotBox: seriesPrototype.getPlotBox
174
+ };
97
175
 
98
- return d;
99
- }
176
+ // this.plotX
177
+ // this.plotY
100
178
 
179
+ /* Those might not exist if a specific axis was not found/defined */
180
+ // this.x?
181
+ // this.y?
101
182
 
102
- // Define annotation prototype
103
- var Annotation = function() {
104
- this.init.apply(this, arguments);
183
+ this.init(chart, options);
105
184
  };
106
- Annotation.prototype = {
107
- /*
108
- * Initialize the annotation
185
+
186
+ /**
187
+ * A factory function for creating a mock point object
188
+ *
189
+ * @function #mockPoint
190
+ * @memberOf Highcharts
191
+ *
192
+ * @param {MockPointOptions} mockPointOptions
193
+ * @return {MockPoint} a mock point
194
+ */
195
+ var mockPoint = H.mockPoint = function(chart, mockPointOptions) {
196
+ return new MockPoint(chart, mockPointOptions);
197
+ };
198
+
199
+ MockPoint.prototype = {
200
+ /**
201
+ * Initialisation of the mock point
202
+ *
203
+ * @function #init
204
+ * @memberOf Highcharts.MockPoint#
205
+ *
206
+ * @param {Highcharts.Chart} chart - a chart object to which the mock point
207
+ * is attached
208
+ * @param {MockPointOptions} options - a config for the mock point
109
209
  */
110
210
  init: function(chart, options) {
111
- var shapeType = options.shape && options.shape.type;
211
+ var xAxisId = options.xAxis,
212
+ xAxis = defined(xAxisId) ?
213
+ chart.xAxis[xAxisId] || chart.get(xAxisId) :
214
+ null,
215
+
216
+ yAxisId = options.yAxis,
217
+ yAxis = defined(yAxisId) ?
218
+ chart.yAxis[yAxisId] || chart.get(yAxisId) :
219
+ null;
112
220
 
113
- this.chart = chart;
114
- this.options = merge({}, defaultOptions(shapeType), options);
221
+
222
+ if (xAxis) {
223
+ this.x = options.x;
224
+ this.series.xAxis = xAxis;
225
+ } else {
226
+ this.plotX = options.x;
227
+ }
228
+
229
+ if (yAxis) {
230
+ this.y = options.y;
231
+ this.series.yAxis = yAxis;
232
+ } else {
233
+ this.plotY = options.y;
234
+ }
115
235
  },
116
236
 
117
- /*
118
- * Render the annotation
237
+ /**
238
+ * Update of the point's coordinates (plotX/plotY)
239
+ *
240
+ * @function #translate
241
+ * @memberOf Highcharts.MockPoint#
242
+ *
243
+ * @return {undefined}
119
244
  */
120
- render: function(redraw) {
121
- var annotation = this,
122
- chart = this.chart,
123
- renderer = annotation.chart.renderer,
124
- group = annotation.group,
125
- title = annotation.title,
126
- shape = annotation.shape,
127
- options = annotation.options,
128
- titleOptions = options.title,
129
- shapeOptions = options.shape;
130
-
131
- if (!group) {
132
- group = annotation.group = renderer.g();
245
+ translate: function() {
246
+ var series = this.series,
247
+ xAxis = series.xAxis,
248
+ yAxis = series.yAxis,
249
+ plotX = this.plotX,
250
+ plotY = this.plotY,
251
+ isInside = true;
252
+
253
+ if (xAxis) {
254
+ this.plotX = plotX = xAxis.toPixels(this.x, true);
255
+
256
+ isInside = plotX >= 0 && plotX <= xAxis.len;
133
257
  }
134
258
 
259
+ if (yAxis) {
260
+ this.plotY = plotY = yAxis.toPixels(this.y, true);
135
261
 
136
- if (!shape && shapeOptions && inArray(shapeOptions.type, ALLOWED_SHAPES) !== -1) {
137
- shape = annotation.shape = renderer[options.shape.type](shapeOptions.params);
138
- shape.add(group);
262
+ isInside = isInside && plotY >= 0 && plotY <= yAxis.len;
139
263
  }
140
264
 
141
- if (!title && titleOptions) {
142
- title = annotation.title = renderer.label(titleOptions);
143
- title.add(group);
265
+ this.isInside = isInside;
266
+ },
267
+
268
+ /**
269
+ * Returns a box to which an item can be aligned to
270
+ *
271
+ * @function #alignToBox
272
+ * @memberOf Highcharts.MockPoint#
273
+ *
274
+ * @param {Boolean} [forceTranslate=false] - whether to update the point's
275
+ * coordinates
276
+ * @return {Array.<Number>} A quadruple of numbers which denotes x, y,
277
+ * width and height of the box
278
+ **/
279
+ alignToBox: function(forceTranslate) {
280
+ if (forceTranslate) {
281
+ this.translate();
144
282
  }
145
283
 
146
- group.add(chart.annotations.group);
284
+ var x = this.plotX,
285
+ y = this.plotY,
286
+ temp;
147
287
 
148
- // link annotations to point or series
149
- annotation.linkObjects();
150
288
 
151
- if (redraw !== false) {
152
- annotation.redraw();
289
+ if (this.series.chart.inverted) {
290
+ temp = x;
291
+ x = y;
292
+ y = temp;
153
293
  }
294
+
295
+ return [x, y, 0, 0];
154
296
  },
155
297
 
156
- /*
157
- * Redraw the annotation title or shape after options update
298
+ /**
299
+ * Returns a label config object -
300
+ * the same as Highcharts.Point.prototype.getLabelConfig
301
+ *
302
+ * @function #getLabelConfig
303
+ * @memberOf Highcharts.MockPoint#
304
+ *
305
+ * @return {Object} labelConfig - label config object
306
+ * @return {Number|undefined} labelConfig.x - x value translated to x axis scale
307
+ * @return {Number|undefined} labelConfig.y - y value translated to y axis scale
308
+ * @return {MockPoint} labelConfig.point - the instance of the point
309
+ */
310
+ getLabelConfig: function() {
311
+ return {
312
+ x: this.x,
313
+ y: this.y,
314
+ point: this
315
+ };
316
+ }
317
+ };
318
+
319
+
320
+ /* ***************************************************************************
321
+ *
322
+ * ANNOTATION
323
+ *
324
+ **************************************************************************** */
325
+
326
+ H.defaultOptions.annotations = [];
327
+
328
+ /**
329
+ * An annotation class which serves as a container for items like labels or shapes.
330
+ * Created items are positioned on the chart either by linking them to
331
+ * existing points or created mock points
332
+ *
333
+ * @class Annotation
334
+ * @memberOf Highcharts
335
+ *
336
+ * @param {Highcharts.Chart} - the chart object
337
+ * @param {AnnotationOptions} - the options object
338
+ */
339
+ var Annotation = H.Annotation = function(chart, userOptions) {
340
+ this.chart = chart;
341
+
342
+ this.labels = [];
343
+ this.shapes = [];
344
+
345
+ this.options = merge(this.defaultOptions, userOptions);
346
+
347
+ this.init(chart, userOptions);
348
+ };
349
+
350
+ Annotation.prototype = {
351
+ /**
352
+ * Shapes which do not have background - the object is used for proper
353
+ * setting of the contrast color
354
+ *
355
+ * @memberOf Highcharts.Annotation#
356
+ * @type {Array.<String>}
158
357
  */
358
+ shapesWithoutBackground: ['connector'],
359
+
360
+ /**
361
+ * A map object which allows to map options attributes to element attributes
362
+ *
363
+ * @memberOf Highcharts.Annotation#
364
+ * @type {Object}
365
+ */
366
+ attrsMap: {
367
+ backgroundColor: 'fill',
368
+ borderColor: 'stroke',
369
+ borderWidth: 'stroke-width',
370
+ strokeWidth: 'stroke-width',
371
+ stroke: 'stroke',
372
+ fill: 'fill',
373
+ zIndex: 'zIndex',
374
+ width: 'width',
375
+ height: 'height',
376
+ borderRadius: 'r',
377
+ r: 'r',
378
+ padding: 'padding',
379
+ dashStyle: 'dashstyle'
380
+ },
381
+
382
+ /**
383
+ * Options for configuring annotations, for example labels, arrows or
384
+ * shapes. Annotations can be tied to points, axis coordinates or chart
385
+ * pixel coordinates.
386
+ *
387
+ * @type {Array<Object>}
388
+ * @sample highcharts/annotations/basic/
389
+ * Basic annotations
390
+ * @sample {highstock} stock/annotations/fibonacci-retracements
391
+ * Custom annotation, Fibonacci retracement
392
+ * @since 6.0.0
393
+ * @optionparent annotations
394
+ **/
395
+ defaultOptions: {
396
+
397
+ /**
398
+ * Whether the annotation is visible.
399
+ *
400
+ * @sample highcharts/annotations/visible/
401
+ * Set annotation visibility
402
+ */
403
+ visible: true,
404
+
405
+ /**
406
+ * Options for annotation's labels. Each label inherits options
407
+ * from the labelOptions object. An option from the labelOptions can be
408
+ * overwritten by config for a specific label.
409
+ */
410
+ labelOptions: {
411
+
412
+ /**
413
+ * The alignment of the annotation's label. If right,
414
+ * the right side of the label should be touching the point.
415
+ *
416
+ * @validvalue ["left", "center", "right"]
417
+ * @sample highcharts/annotations/label-position/
418
+ * Set labels position
419
+ */
420
+ align: 'center',
421
+
422
+ /**
423
+ * Whether to allow the annotation's labels to overlap.
424
+ * To make the labels less sensitive for overlapping,
425
+ * the can be set to 0.
426
+ *
427
+ * @sample highcharts/annotations/tooltip-like/
428
+ * Hide overlapping labels
429
+ */
430
+ allowOverlap: false,
431
+
432
+ /**
433
+ * The background color or gradient for the annotation's label.
434
+ *
435
+ * @type {Color}
436
+ * @sample highcharts/annotations/label-presentation/
437
+ * Set labels graphic options
438
+ */
439
+ backgroundColor: 'rgba(0, 0, 0, 0.75)',
440
+
441
+ /**
442
+ * The border color for the annotation's label.
443
+ *
444
+ * @type {Color}
445
+ * @sample highcharts/annotations/label-presentation/
446
+ * Set labels graphic options
447
+ */
448
+ borderColor: 'black',
449
+
450
+ /**
451
+ * The border radius in pixels for the annotaiton's label.
452
+ *
453
+ * @sample highcharts/annotations/label-presentation/
454
+ * Set labels graphic options
455
+ */
456
+ borderRadius: 1,
457
+
458
+ /**
459
+ * The border width in pixels for the annotation's label
460
+ *
461
+ * @sample highcharts/annotations/label-presentation/
462
+ * Set labels graphic options
463
+ */
464
+ borderWidth: 1,
465
+
466
+ /**
467
+ * Whether to hide the annotation's label that is outside the plot area.
468
+ *
469
+ * @sample highcharts/annotations/label-crop-overflow/
470
+ * Crop or justify labels
471
+ */
472
+ crop: false,
473
+
474
+ /**
475
+ * A [format](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting) string for the data label.
476
+ *
477
+ * @type {String}
478
+ * @see [plotOptions.series.dataLabels.format](plotOptions.series.dataLabels.format.html)
479
+ * @sample highcharts/annotations/label-text/
480
+ * Set labels text
481
+ * @default undefined
482
+ * @apioption annotations.labelOptions.format
483
+ **/
484
+
485
+ /**
486
+ * Alias for the format option.
487
+ *
488
+ * @type {String}
489
+ * @see [format](annotations.labelOptions.format.html)
490
+ * @sample highcharts/annotations/label-text/
491
+ * Set labels text
492
+ * @default undefined
493
+ * @apioption annotations.labelOptions.text
494
+ */
495
+
496
+ /**
497
+ * Callback JavaScript function to format the annotation's label. Note that
498
+ * if a `format` or `text` are defined, the format or text take precedence
499
+ * and the formatter is ignored. `This` refers to a point object.
500
+ *
501
+ * @type {Function}
502
+ * @sample highcharts/annotations/label-text/
503
+ * Set labels text
504
+ * @default function () {
505
+ * return defined(this.y) ? this.y : 'Annotation label';
506
+ * }
507
+ **/
508
+ formatter: function() {
509
+ return defined(this.y) ? this.y : 'Annotation label';
510
+ },
511
+
512
+ /**
513
+ * How to handle the annotation's label that flow outside the plot
514
+ * area. The justify option aligns the label inside the plot area.
515
+ *
516
+ * @validvalue ["none", "justify"]
517
+ * @sample highcharts/annotations/label-crop-overflow/
518
+ * Crop or justify labels
519
+ **/
520
+ overflow: 'justify',
521
+
522
+ /**
523
+ * When either the borderWidth or the backgroundColor is set,
524
+ * this is the padding within the box.
525
+ *
526
+ * @sample highcharts/annotations/label-presentation/
527
+ * Set labels graphic options
528
+ */
529
+ padding: 5,
530
+
531
+ /**
532
+ * The shadow of the box. The shadow can be an object configuration
533
+ * containing `color`, `offsetX`, `offsetY`, `opacity` and `width`.
534
+ *
535
+ * @type {Boolean|Object}
536
+ * @sample highcharts/annotations/label-presentation/
537
+ * Set labels graphic options
538
+ */
539
+ shadow: false,
540
+
541
+ /**
542
+ * The name of a symbol to use for the border around the label.
543
+ * Symbols are predefined functions on the Renderer object.
544
+ *
545
+ * @type {String}
546
+ * @sample highcharts/annotations/shapes/
547
+ * Available shapes for labels
548
+ **/
549
+ shape: 'callout',
550
+
551
+ /**
552
+ * Styles for the annotation's label.
553
+ *
554
+ * @type {CSSObject}
555
+ * @sample highcharts/annotations/label-presentation/
556
+ * Set labels graphic options
557
+ * @see [plotOptions.series.dataLabels.style](plotOptions.series.dataLabels.style.html)
558
+ **/
559
+ style: {
560
+ fontSize: '11px',
561
+ fontWeigth: 'bold',
562
+ color: 'contrast'
563
+ },
564
+
565
+ /**
566
+ * Whether to [use HTML](http://www.highcharts.com/docs/chart-concepts/labels-
567
+ * and-string-formatting#html) to render the annotation's label.
568
+ *
569
+ * @type {Boolean}
570
+ * @default false
571
+ **/
572
+ useHTML: false,
573
+
574
+ /**
575
+ * The vertical alignment of the annotation's label.
576
+ *
577
+ * @type {String}
578
+ * @validvalue ["top", "middle", "bottom"]
579
+ * @sample highcharts/annotations/label-position/
580
+ * Set labels position
581
+ **/
582
+ verticalAlign: 'bottom',
583
+
584
+ /**
585
+ * The x position offset of the label relative to the point.
586
+ * Note that if a `distance` is defined, the distance takes
587
+ * precedence over `x` and `y` options.
588
+ *
589
+ * @sample highcharts/annotations/label-position/
590
+ * Set labels position
591
+ **/
592
+ x: 0,
593
+
594
+ /**
595
+ * The y position offset of the label relative to the point.
596
+ * Note that if a `distance` is defined, the distance takes
597
+ * precedence over `x` and `y` options.
598
+ *
599
+ * @sample highcharts/annotations/label-position/
600
+ * Set labels position
601
+ **/
602
+ y: -16
603
+
604
+ /**
605
+ * The label's pixel distance from the point.
606
+ *
607
+ * @type {Number}
608
+ * @sample highcharts/annotations/label-position/
609
+ * Set labels position
610
+ * @default undefined
611
+ * @apioption annotations.labelOptions.distance
612
+ **/
613
+ },
614
+
615
+ /**
616
+ * An array of labels for the annotation. For options that apply to multiple
617
+ * labels, they can be added to the [labelOptions](annotations.labelOptions.html).
618
+ *
619
+ * @type {Array<Object>}
620
+ * @extends annotations.labelOptions
621
+ * @apioption annotations.labels
622
+ */
623
+
624
+ /**
625
+ * This option defines the point to which the label will be connected.
626
+ * It can be either the point which exists in the series - it is referenced
627
+ * by the point's id - or a new point with defined x, y properies
628
+ * and optionally axes.
629
+ *
630
+ * @type {String|Object}
631
+ * @sample highcharts/annotations/mock-point/
632
+ * Attach annotation to a mock point
633
+ * @apioption annotations.labels.point
634
+ */
635
+
636
+ /**
637
+ * The x position of the point. Units can be either in axis
638
+ * or chart pixel coordinates.
639
+ *
640
+ * @type {Number}
641
+ * @apioption annotations.labels.point.x
642
+ */
643
+
644
+ /**
645
+ * The y position of the point. Units can be either in axis
646
+ * or chart pixel coordinates.
647
+ *
648
+ * @type {Number}
649
+ * @apioption annotations.labels.point.y
650
+ */
651
+
652
+ /**
653
+ * This number defines which xAxis the point is connected to. It refers
654
+ * to either the axis id or the index of the axis in the xAxis array.
655
+ * If the option is not configured or the axis is not found the point's
656
+ * x coordinate refers to the chart pixels.
657
+ *
658
+ * @type {Number|String}
659
+ * @apioption annotations.labels.point.xAxis
660
+ */
661
+
662
+ /**
663
+ * This number defines which yAxis the point is connected to. It refers
664
+ * to either the axis id or the index of the axis in the yAxis array.
665
+ * If the option is not configured or the axis is not found the point's
666
+ * y coordinate refers to the chart pixels.
667
+ *
668
+ * @type {Number|String}
669
+ * @apioption annotations.labels.point.yAxis
670
+ */
671
+
672
+
673
+ /**
674
+ * Options for annotation's shapes. Each shape inherits options
675
+ * from the shapeOptions object. An option from the shapeOptions can be
676
+ * overwritten by config for a specific shape.
677
+ *
678
+ * @type {Object}
679
+ **/
680
+ shapeOptions: {
681
+
682
+ /**
683
+ * The color of the shape's stroke.
684
+ *
685
+ * @type {Color}
686
+ * @sample highcharts/annotations/shape/
687
+ * Basic shape annotation
688
+ **/
689
+ stroke: 'rgba(0, 0, 0, 0.75)',
690
+
691
+ /**
692
+ * The pixel stroke width of the shape.
693
+ *
694
+ * @sample highcharts/annotations/shape/
695
+ * Basic shape annotation
696
+ **/
697
+ strokeWidth: 1,
698
+
699
+ /**
700
+ * The color of the shape's fill.
701
+ *
702
+ * @type {Color}
703
+ * @sample highcharts/annotations/shape/
704
+ * Basic shape annotation
705
+ **/
706
+ fill: 'rgba(0, 0, 0, 0.75)',
707
+
708
+ /**
709
+ * The type of the shape, e.g. circle or rectangle.
710
+ *
711
+ * @type {String}
712
+ * @sample highcharts/annotations/shape/
713
+ * Basic shape annotation
714
+ * @default 'rect'
715
+ * @apioption annotations.shapeOptions.type
716
+ **/
717
+
718
+ /**
719
+ * The radius of the shape.
720
+ *
721
+ * @sample highcharts/annotations/shape/
722
+ * Basic shape annotation
723
+ **/
724
+ r: 0
725
+
726
+ /**
727
+ * The width of the shape.
728
+ *
729
+ * @type {Number}
730
+ * @sample highcharts/annotations/shape/
731
+ * Basic shape annotation
732
+ * @apioption annotations.shapeOptions.width
733
+ **/
734
+
735
+ /**
736
+ * The height of the shape.
737
+ *
738
+ * @type {Number}
739
+ * @sample highcharts/annotations/shape/
740
+ * Basic shape annotation
741
+ * @apioption annotations.shapeOptions.height
742
+ **/
743
+ },
744
+
745
+ /**
746
+ * The Z index of the annotation.
747
+ *
748
+ * @type {Number}
749
+ * @default 6
750
+ **/
751
+ zIndex: 6
752
+
753
+ /**
754
+ * An array of shapes for the annotation. For options that apply to multiple
755
+ * shapes, then can be added to the [shapeOptions](annotations.shapeOptions.html).
756
+ *
757
+ * @type {Array<Object>}
758
+ * @extends annotations.shapeOptions
759
+ * @apioption annotations.shapes
760
+ */
761
+
762
+ /**
763
+ * This option defines the point to which the shape will be connected.
764
+ * It can be either the point which exists in the series - it is referenced
765
+ * by the point's id - or a new point with defined x, y properties
766
+ * and optionally axes.
767
+ *
768
+ * @type {String|Object}
769
+ * @extends annotations.labels.point
770
+ * @apioption annotations.shapes.point
771
+ */
772
+
773
+ /**
774
+ * An array of points for the shape. This option is available for shapes
775
+ * which can use multiple points such as path. A point can be either
776
+ * a point object or a point's id.
777
+ *
778
+ * @type {Array}
779
+ * @see [annotations.shapes.point](annotations.shapes.point.html)
780
+ * @apioption annotations.shapes.points
781
+ */
782
+
783
+ /**
784
+ * Id of the marker which will be drawn at the final vertex of the path.
785
+ * Custom markers can be defined in defs property.
786
+ *
787
+ * @type {String}
788
+ * @see [defs.markers](defs.markers.html)
789
+ * @sample highcharts/annotations/custom-markers/
790
+ * Define a custom marker for annotations
791
+ * @apioption annotations.shapes.markerEnd
792
+ **/
793
+
794
+ /**
795
+ * Id of the marker which will be drawn at the first vertex of the path.
796
+ * Custom markers can be defined in defs property.
797
+ *
798
+ * @type {String}
799
+ * @see [defs.markers](defs.markers.html)
800
+ * @sample {highcharts} highcharts/annotations/custom-markers/
801
+ * Define a custom marker for annotations
802
+ * @apioption annotations.shapes.markerStart
803
+ **/
804
+ },
805
+
806
+ /**
807
+ * Annotation initialisation
808
+ *
809
+ * @function #init
810
+ * @memberOf Highcharts.Annotation#
811
+ *
812
+ * @return {undefined}
813
+ **/
814
+ init: function() {
815
+ var anno = this;
816
+ each(this.options.labels || [], this.initLabel, this);
817
+ each(this.options.shapes || [], this.initShape, this);
818
+
819
+ // Push the callback that reports to the overlapping-labels module which
820
+ // labels it should account for.
821
+ this.chart.labelCollectors.push(function() {
822
+ var labels = [];
823
+ each(anno.labels, function(label) {
824
+ if (!label.options.allowOverlap) {
825
+ labels.push(label);
826
+ }
827
+ });
828
+ return labels;
829
+ });
830
+ },
831
+
832
+ /**
833
+ * Main method for drawing an annotation, it is called everytime on chart redraw
834
+ * and once on chart's load
835
+ *
836
+ * @function #redraw
837
+ * @memberOf Highcharts.Annotation#
838
+ *
839
+ * @return {undefined}
840
+ **/
159
841
  redraw: function() {
160
- var options = this.options,
161
- chart = this.chart,
162
- group = this.group,
163
- title = this.title,
164
- shape = this.shape,
165
- linkedTo = this.linkedObject,
166
- xAxis = chart.xAxis[options.xAxis],
167
- yAxis = chart.yAxis[options.yAxis],
168
- width = options.width,
169
- height = options.height,
170
- anchorY = ALIGN_FACTOR[options.anchorY],
171
- anchorX = ALIGN_FACTOR[options.anchorX],
172
- shapeParams,
173
- linkType,
174
- series,
175
- bbox,
176
- x,
177
- y;
178
-
179
- if (linkedTo) {
180
- linkType = (linkedTo instanceof H.Point) ? 'point' :
181
- (linkedTo instanceof H.Series) ? 'series' : null;
182
-
183
- if (linkType === 'point') {
184
- options.xValue = linkedTo.x;
185
- options.yValue = linkedTo.y;
186
- series = linkedTo.series;
187
- } else if (linkType === 'series') {
188
- series = linkedTo;
189
- }
842
+ if (!this.group) {
843
+ this.render();
844
+ }
190
845
 
191
- if (group.visibility !== series.group.visibility) {
192
- group.attr({
193
- visibility: series.group.visibility
194
- });
195
- }
846
+ this.redrawItems(this.shapes);
847
+ this.redrawItems(this.labels);
848
+ },
849
+
850
+ /**
851
+ * @function #redrawItems
852
+ * @memberOf Highcharts.Annotation#
853
+ *
854
+ * @param {Array<Object>} items
855
+ * @return {undefined}
856
+ **/
857
+ redrawItems: function(items) {
858
+ var i = items.length;
859
+
860
+ // needs a backward loop
861
+ // labels/shapes array might be modified due to destruction of the item
862
+ while (i--) {
863
+ this.redrawItem(items[i]);
196
864
  }
865
+ },
197
866
 
867
+ /**
868
+ * @function #render
869
+ * @memberOf Highcharts.Annotation#
870
+ *
871
+ * @return {undefined}
872
+ **/
873
+ render: function() {
874
+ var renderer = this.chart.renderer;
875
+
876
+ var group = this.group = renderer.g('annotation')
877
+ .attr({
878
+ zIndex: this.options.zIndex,
879
+ visibility: this.options.visible ? 'visible' : 'hidden'
880
+ })
881
+ .add();
882
+
883
+ this.shapesGroup = renderer.g('annotation-shapes').add(group);
884
+ this.labelsGroup = renderer.g('annotation-labels').attr({
885
+ // hideOverlappingLabels requires translation
886
+ translateX: 0,
887
+ translateY: 0
888
+ }).add(group);
889
+
890
+ this.shapesGroup.clip(this.chart.plotBoxClip);
891
+ },
198
892
 
199
- // Based on given options find annotation pixel position
200
- x = (defined(options.xValue) ? xAxis.toPixels(options.xValue + xAxis.minPointOffset) - xAxis.minPixelPadding : options.x);
201
- y = defined(options.yValue) ? yAxis.toPixels(options.yValue) : options.y;
893
+ /**
894
+ * @function #setVisible
895
+ * @memberOf Highcharts.Annotation#
896
+ *
897
+ * @param {Boolean} [visibility] - whether to show or hide an annotation.
898
+ * If the param is omitted, the annotation's visibility is toggled
899
+ * @return {undefined}
900
+ **/
901
+ setVisible: function(visibility) {
902
+ var options = this.options,
903
+ visible = pick(visibility, !options.visible);
904
+
905
+ this.group.attr({
906
+ visibility: visible ? 'visible' : 'hidden'
907
+ });
202
908
 
203
- if (!isNumber(x) || !isNumber(y)) {
204
- return;
909
+ options.visible = visible;
910
+ },
911
+
912
+
913
+ /**
914
+ * Destroying an annotation
915
+ *
916
+ * @function #destroy
917
+ * @memberOf Highcharts.Annotation#
918
+ *
919
+ * @return {undefined}
920
+ **/
921
+ destroy: function() {
922
+ var chart = this.chart;
923
+
924
+ each(this.labels, function(label) {
925
+ label.destroy();
926
+ });
927
+
928
+ each(this.shapes, function(shape) {
929
+ shape.destroy();
930
+ });
931
+
932
+ destroyObjectProperties(this, chart);
933
+ },
934
+
935
+
936
+ /* ***********************************************************************
937
+ * ITEM SECTION
938
+ * Contains methods for handling a single item in an annotation
939
+ *********************************************************************** */
940
+
941
+ /**
942
+ * Initialisation of a single shape
943
+ *
944
+ * @function #initShape
945
+ * @memberOf Highcharts.Annotation#
946
+ *
947
+ * @param {Object} shapeOptions - a confg object for a single shape
948
+ * @return {undefined}
949
+ **/
950
+ initShape: function(shapeOptions) {
951
+ var renderer = this.chart.renderer,
952
+ options = merge(this.options.shapeOptions, shapeOptions),
953
+ attr = this.attrsFromOptions(options),
954
+
955
+ type = renderer[options.type] ? options.type : 'rect',
956
+ shape = renderer[type](0, -9e9, 0, 0);
957
+
958
+ shape.points = [];
959
+ shape.type = type;
960
+ shape.options = options;
961
+ shape.itemType = 'shape';
962
+
963
+ if (type === 'path') {
964
+ extend(shape, {
965
+ markerStartSetter: MarkerMixin.markerStartSetter,
966
+ markerEndSetter: MarkerMixin.markerEndSetter,
967
+ markerStart: MarkerMixin.markerStart,
968
+ markerEnd: MarkerMixin.markerEnd
969
+ });
205
970
  }
206
971
 
972
+ shape.attr(attr);
207
973
 
208
- if (title) {
209
- title.attr(options.title);
210
- title.css(options.title.style);
974
+ this.shapes.push(shape);
975
+ },
976
+
977
+ /**
978
+ * Initialisation of a single label
979
+ *
980
+ * @function #initShape
981
+ * @memberOf Highcharts.Annotation#
982
+ *
983
+ * @param {Object} labelOptions
984
+ * @return {undefined}
985
+ **/
986
+ initLabel: function(labelOptions) {
987
+ var options = merge(this.options.labelOptions, labelOptions),
988
+ style = options.style,
989
+ attr = this.attrsFromOptions(options),
990
+
991
+ label = this.chart.renderer.label(
992
+ '',
993
+ 0, -9e9,
994
+ options.shape,
995
+ null,
996
+ null,
997
+ options.useHTML,
998
+ null,
999
+ 'annotation-label'
1000
+ );
1001
+
1002
+ if (style.color === 'contrast') {
1003
+ style.color = this.chart.renderer.getContrast(
1004
+ inArray(options.shape, this.shapesWithoutBackground) > -1 ?
1005
+ '#FFFFFF' :
1006
+ options.backgroundColor
1007
+ );
211
1008
  }
212
1009
 
213
- if (shape) {
214
- shapeParams = extend({}, options.shape.params);
1010
+ label.points = [];
1011
+ label.options = options;
1012
+ label.itemType = 'label';
215
1013
 
216
- if (options.units === 'values') {
217
- H.objectEach(shapeParams, function(val, param) {
218
- if (inArray(param, ['width', 'x']) > -1) {
219
- shapeParams[param] = xAxis.translate(shapeParams[param]);
220
- } else if (inArray(param, ['height', 'y']) > -1) {
221
- shapeParams[param] = yAxis.translate(shapeParams[param]);
222
- }
223
- });
1014
+ // Labelrank required for hideOverlappingLabels()
1015
+ label.labelrank = options.labelrank;
1016
+ label.annotation = this;
224
1017
 
225
- if (shapeParams.width) {
226
- shapeParams.width -= xAxis.toPixels(0) - xAxis.left;
227
- }
1018
+ label.attr(attr).css(style).shadow(options.shadow);
228
1019
 
229
- if (shapeParams.x) {
230
- shapeParams.x += xAxis.minPixelPadding;
231
- }
1020
+ this.labels.push(label);
1021
+ },
232
1022
 
233
- if (options.shape.type === 'path') {
234
- translatePath(shapeParams.d, xAxis, yAxis, x, y);
235
- }
1023
+ /**
1024
+ * Redrawing a single item
1025
+ *
1026
+ * @function #redrawItem
1027
+ * @memberOf Highcharts.Annotation#
1028
+ *
1029
+ * @param {Object} item
1030
+ * @return {undefined}
1031
+ */
1032
+ redrawItem: function(item) {
1033
+ var points = this.linkPoints(item),
1034
+ itemOptions = item.options,
1035
+ text;
1036
+
1037
+ if (!points.length) {
1038
+ this.destroyItem(item);
1039
+
1040
+ } else {
1041
+ if (!item.parentGroup) {
1042
+ this.renderItem(item);
236
1043
  }
237
1044
 
238
- // move the center of the circle to shape x/y
239
- if (options.shape.type === 'circle') {
240
- shapeParams.x += shapeParams.r;
241
- shapeParams.y += shapeParams.r;
1045
+ if (item.itemType === 'label') {
1046
+ text = itemOptions.format || itemOptions.text;
1047
+ item.attr({
1048
+ text: text ?
1049
+ format(text, points[0].getLabelConfig()) : itemOptions.formatter.call(points[0])
1050
+ });
242
1051
  }
243
1052
 
244
- shape.attr(shapeParams);
1053
+
1054
+ if (item.type === 'path') {
1055
+ this.redrawPath(item);
1056
+
1057
+ } else {
1058
+ this.alignItem(item, !item.placed);
1059
+ }
245
1060
  }
1061
+ },
246
1062
 
247
- group.bBox = null;
1063
+ /**
1064
+ * Destroing a single item
1065
+ *
1066
+ * @function #destroyItem
1067
+ * @memberOf Highcharts.Annotation#
1068
+ *
1069
+ * @param {Object} item
1070
+ * @return {undefined}
1071
+ */
1072
+ destroyItem: function(item) {
1073
+ // erase from shapes or labels array
1074
+ erase(this[item.itemType + 's'], item);
1075
+ item.destroy();
1076
+ },
248
1077
 
249
- // If annotation width or height is not defined in options use bounding box size
250
- if (!isNumber(width)) {
251
- bbox = group.getBBox();
252
- width = bbox.width;
1078
+ /**
1079
+ * Returns a point object
1080
+ *
1081
+ * @function #pointItem
1082
+ * @memberOf Highcharts.Annotation#
1083
+ *
1084
+ * @param {Object} pointOptions
1085
+ * @param {Highcharts.MockPoint|Highcharts.Point} point
1086
+ * @return {Highcharts.MockPoint|Highcharts.Point|null} if the point is
1087
+ * found/exists returns this point, otherwise null
1088
+ */
1089
+ pointItem: function(pointOptions, point) {
1090
+ if (!point || point.series === null) {
1091
+ if (isObject(pointOptions)) {
1092
+ point = mockPoint(this.chart, pointOptions);
1093
+
1094
+ } else if (isString(pointOptions)) {
1095
+ point = this.chart.get(pointOptions) || null;
1096
+ }
253
1097
  }
254
1098
 
255
- if (!isNumber(height)) {
256
- // get bbox only if it wasn't set before
257
- if (!bbox) {
258
- bbox = group.getBBox();
1099
+ return point;
1100
+ },
1101
+
1102
+ /**
1103
+ * Linking item with the point or points and returning an array of linked points
1104
+ *
1105
+ * @function #linkPoints
1106
+ * @memberOf Highcharts.Annotation#
1107
+ *
1108
+ * @param {Object} item
1109
+ * @return {
1110
+ * Highcharts.Point|
1111
+ * Highcharts.MockPoint|
1112
+ * Array<Highcharts.Point|Highcharts.MockPoint>
1113
+ * }
1114
+ */
1115
+ linkPoints: function(item) {
1116
+ var pointsOptions = item.options.points || (item.options.point && H.splat(item.options.point)),
1117
+ points = item.points,
1118
+ len = pointsOptions && pointsOptions.length,
1119
+ i,
1120
+ point;
1121
+
1122
+ for (i = 0; i < len; i++) {
1123
+ point = this.pointItem(pointsOptions[i], points[i]);
1124
+
1125
+ if (!point) {
1126
+ return (item.points = []);
259
1127
  }
260
1128
 
261
- height = bbox.height;
1129
+ points[i] = point;
262
1130
  }
263
1131
 
264
- // Calculate anchor point
265
- if (!isNumber(anchorX)) {
266
- anchorX = ALIGN_FACTOR.center;
1132
+ return points;
1133
+ },
1134
+
1135
+ /**
1136
+ * Aligning the item and setting its anchor
1137
+ *
1138
+ * @function #alignItem
1139
+ * @memberOf Highcharts.Annotation#
1140
+ *
1141
+ * @param {Object} item
1142
+ * @param {Boolean} isNew - if the label is re-positioned (is not new) it is animated
1143
+ * @return {undefined}
1144
+ */
1145
+ alignItem: function(item, isNew) {
1146
+ var anchor = this.itemAnchor(item, item.points[0]),
1147
+ attrs = this.itemPosition(item, anchor);
1148
+
1149
+ if (attrs) {
1150
+ item.alignAttr = attrs;
1151
+ item.placed = true;
1152
+
1153
+ attrs.anchorX = anchor.absolutePosition.x;
1154
+ attrs.anchorY = anchor.absolutePosition.y;
1155
+
1156
+ item[isNew ? 'attr' : 'animate'](attrs);
1157
+
1158
+ } else {
1159
+ item.placed = false;
1160
+
1161
+ item.attr({
1162
+ x: 0,
1163
+ y: -9e9
1164
+ });
267
1165
  }
1166
+ },
1167
+
1168
+ redrawPath: function(pathItem, isNew) {
1169
+ var points = pathItem.points,
1170
+ strokeWidth = pathItem['stroke-width'],
1171
+ d = ['M'],
1172
+ pointIndex = 0,
1173
+ dIndex = 0,
1174
+ len = points && points.length,
1175
+ crispSegmentIndex,
1176
+ anchor,
1177
+ point,
1178
+ showPath;
1179
+
1180
+ if (len) {
1181
+ do {
1182
+ point = points[pointIndex];
1183
+
1184
+ anchor = this.itemAnchor(pathItem, point).absolutePosition;
1185
+ d[++dIndex] = anchor.x;
1186
+ d[++dIndex] = anchor.y;
1187
+
1188
+ // crisping line, it might be replaced with Renderer.prototype.crispLine
1189
+ // but it requires creating many temporary arrays
1190
+ crispSegmentIndex = dIndex % 5;
1191
+ if (crispSegmentIndex === 0) {
1192
+ if (d[crispSegmentIndex + 1] === d[crispSegmentIndex + 4]) {
1193
+ d[crispSegmentIndex + 1] = d[crispSegmentIndex + 4] = Math.round(d[crispSegmentIndex + 1]) - (strokeWidth % 2 / 2);
1194
+ }
268
1195
 
269
- if (!isNumber(anchorY)) {
270
- anchorY = ALIGN_FACTOR.center;
1196
+ if (d[crispSegmentIndex + 2] === d[crispSegmentIndex + 5]) {
1197
+ d[crispSegmentIndex + 2] = d[crispSegmentIndex + 5] = Math.round(d[crispSegmentIndex + 2]) + (strokeWidth % 2 / 2);
1198
+ }
1199
+ }
1200
+
1201
+ if (pointIndex < len - 1) {
1202
+ d[++dIndex] = 'L';
1203
+ }
1204
+
1205
+ showPath = point.series.visible;
1206
+
1207
+ } while (++pointIndex < len && showPath);
271
1208
  }
272
1209
 
273
- // Translate group according to its dimension and anchor point
274
- x = x - width * anchorX;
275
- y = y - height * anchorY;
276
1210
 
277
- if (defined(group.translateX) && defined(group.translateY)) {
278
- group.animate({
279
- translateX: x,
280
- translateY: y
1211
+ if (showPath) {
1212
+ pathItem[isNew ? 'attr' : 'animate']({
1213
+ d: d
281
1214
  });
1215
+
282
1216
  } else {
283
- group.translate(x, y);
1217
+ pathItem.attr({
1218
+ d: 'M 0 ' + -9e9
1219
+ });
284
1220
  }
1221
+
1222
+ pathItem.placed = showPath;
285
1223
  },
286
1224
 
287
- /*
288
- * Destroy the annotation
289
- */
290
- destroy: function() {
291
- var annotation = this,
1225
+ renderItem: function(item) {
1226
+ item.add(item.itemType === 'label' ? this.labelsGroup : this.shapesGroup);
1227
+
1228
+ this.setItemMarkers(item);
1229
+ },
1230
+
1231
+ setItemMarkers: function(item) {
1232
+ var itemOptions = item.options,
292
1233
  chart = this.chart,
293
- allItems = chart.annotations.allItems,
294
- index = allItems.indexOf(annotation);
1234
+ markers = chart.options.defs.markers,
1235
+ fill = itemOptions.fill,
1236
+ color = defined(fill) && fill !== 'none' ? fill : itemOptions.stroke,
1237
+
1238
+
1239
+ setMarker = function(markerType) {
1240
+ var markerId = itemOptions[markerType],
1241
+ marker,
1242
+ predefinedMarker,
1243
+ key;
1244
+
1245
+ if (markerId) {
1246
+ for (key in markers) {
1247
+ marker = markers[key];
1248
+ if (markerId === marker.id) {
1249
+ predefinedMarker = marker;
1250
+ break;
1251
+ }
1252
+ }
295
1253
 
296
- if (index > -1) {
297
- allItems.splice(index, 1);
298
- }
1254
+ if (predefinedMarker) {
1255
+ marker = item[markerType] = chart.renderer.addMarker(
1256
+ null,
1257
+ merge(predefinedMarker, {
1258
+ color: color
1259
+ })
1260
+ );
299
1261
 
300
- each(['title', 'shape', 'group'], function(element) {
301
- if (annotation[element]) {
302
- annotation[element].destroy();
303
- annotation[element] = null;
304
- }
305
- });
1262
+ item.attr(markerType, marker.id);
1263
+ }
1264
+ }
1265
+ };
306
1266
 
307
- annotation.group = annotation.title = annotation.shape = annotation.chart = annotation.options = null;
1267
+ each(['markerStart', 'markerEnd'], setMarker);
308
1268
  },
309
1269
 
310
- /*
311
- * Update the annotation with a given options
1270
+ /**
1271
+ * An object which denotes an anchor position
1272
+ *
1273
+ * @typedef {Object} AnchorPosition
1274
+ * @property {Number} AnchorPosition.x
1275
+ * @property {Number} AnchorPosition.y
1276
+ * @property {Number} AnchorPosition.height
1277
+ * @property {Number} AnchorPosition.width
1278
+ */
1279
+
1280
+ /**
1281
+ * Returns object which denotes anchor position - relative and absolute
1282
+ *
1283
+ * @function #itemAnchor
1284
+ * @memberOf Highcharts.Annotation#
1285
+ *
1286
+ * @param {Object} item
1287
+ * @param {Highcharts.Point|Highcharts.MockPoint} point
1288
+ * @return {Object} anchor
1289
+ * @return {AnchorPosition} anchor.relativePosition - relative to the plot area position
1290
+ * @return {AnchorPosition} anchor.absolutePosition - absolute position
312
1291
  */
313
- update: function(options, redraw) {
314
- extend(this.options, options);
1292
+ itemAnchor: function(item, point) {
1293
+ var plotBox = point.series.getPlotBox(),
1294
+
1295
+ box = point.mock ?
1296
+ point.alignToBox(true) :
1297
+ tooltipPrototype.getAnchor.call({
1298
+ chart: this.chart
1299
+ }, point),
1300
+
1301
+ anchor = {
1302
+ x: box[0],
1303
+ y: box[1],
1304
+ height: box[2] || 0,
1305
+ width: box[3] || 0
1306
+ };
1307
+
1308
+ return {
1309
+ relativePosition: anchor,
1310
+ absolutePosition: merge(anchor, {
1311
+ x: anchor.x + plotBox.translateX,
1312
+ y: anchor.y + plotBox.translateY
1313
+ })
1314
+ };
1315
+ },
1316
+
1317
+ /**
1318
+ * Returns the item position
1319
+ *
1320
+ * @function #itemPosition
1321
+ * @memberOf Highcharts.Annotation#
1322
+ *
1323
+ * @param {Object} item
1324
+ * @param {AnchorPosition} anchor
1325
+ * @return {Object|null} position
1326
+ * @return {Number} position.x
1327
+ * @return {Number} position.y
1328
+ */
1329
+ itemPosition: function(item, anchor) {
1330
+ var chart = this.chart,
1331
+ point = item.points[0],
1332
+ itemOptions = item.options,
1333
+ anchorAbsolutePosition = anchor.absolutePosition,
1334
+ anchorRelativePosition = anchor.relativePosition,
1335
+ itemPosition,
1336
+ alignTo,
1337
+ itemPosRelativeX,
1338
+ itemPosRelativeY,
1339
+
1340
+ showItem = point.series.visible && point.isInside !== false;
1341
+
1342
+ if (showItem) {
1343
+
1344
+ if (defined(itemOptions.distance) || itemOptions.positioner) {
1345
+ itemPosition = (itemOptions.positioner || tooltipPrototype.getPosition).call({
1346
+ chart: chart,
1347
+ distance: pick(itemOptions.distance, 16)
1348
+ },
1349
+ item.width,
1350
+ item.height, {
1351
+ plotX: anchorRelativePosition.x,
1352
+ plotY: anchorRelativePosition.y,
1353
+ negative: point.negative,
1354
+ ttBelow: point.ttBelow,
1355
+ h: anchorRelativePosition.height || anchorRelativePosition.width
1356
+ }
1357
+ );
1358
+
1359
+ } else {
1360
+ alignTo = {
1361
+ x: anchorAbsolutePosition.x,
1362
+ y: anchorAbsolutePosition.y,
1363
+ width: 0,
1364
+ height: 0
1365
+ };
1366
+
1367
+ itemPosition = this.alignedPosition(
1368
+ extend(itemOptions, {
1369
+ width: item.width,
1370
+ height: item.height
1371
+ }),
1372
+ alignTo
1373
+ );
1374
+
1375
+ if (item.options.overflow === 'justify') {
1376
+ itemPosition = this.alignedPosition(
1377
+ this.justifiedOptions(item, itemOptions, itemPosition),
1378
+ alignTo
1379
+ );
1380
+ }
1381
+ }
315
1382
 
316
- // update link to point or series
317
- this.linkObjects();
318
1383
 
319
- this.render(redraw);
1384
+ if (itemOptions.crop) {
1385
+ itemPosRelativeX = itemPosition.x - chart.plotLeft;
1386
+ itemPosRelativeY = itemPosition.y - chart.plotTop;
1387
+
1388
+ showItem =
1389
+ chart.isInsidePlot(itemPosRelativeX, itemPosRelativeY) &&
1390
+ chart.isInsidePlot(
1391
+ itemPosRelativeX + item.width,
1392
+ itemPosRelativeY + item.height
1393
+ );
1394
+ }
1395
+ }
1396
+
1397
+ return showItem ? itemPosition : null;
320
1398
  },
321
1399
 
322
- linkObjects: function() {
323
- var annotation = this,
324
- chart = annotation.chart,
325
- linkedTo = annotation.linkedObject,
326
- linkedId = linkedTo && (linkedTo.id || linkedTo.options.id),
327
- options = annotation.options,
328
- id = options.linkedTo;
1400
+ /**
1401
+ * Returns new aligned position based alignment options and box to align to.
1402
+ * It is almost a one-to-one copy from SVGElement.prototype.align
1403
+ * except it does not use and mutate an element
1404
+ *
1405
+ * @function #alignedPosition
1406
+ * @memberOf Highcharts.Annotation#
1407
+ *
1408
+ * @param {Object} alignOptions
1409
+ * @param {Object} box
1410
+ * @return {Object} aligned position
1411
+ **/
1412
+ alignedPosition: function(alignOptions, box) {
1413
+ var align = alignOptions.align,
1414
+ vAlign = alignOptions.verticalAlign,
1415
+ x = (box.x || 0) + (alignOptions.x || 0),
1416
+ y = (box.y || 0) + (alignOptions.y || 0),
1417
+
1418
+ alignFactor,
1419
+ vAlignFactor;
1420
+
1421
+ if (align === 'right') {
1422
+ alignFactor = 1;
1423
+ } else if (align === 'center') {
1424
+ alignFactor = 2;
1425
+ }
1426
+ if (alignFactor) {
1427
+ x += (box.width - (alignOptions.width || 0)) / alignFactor;
1428
+ }
329
1429
 
330
- if (!defined(id)) {
331
- annotation.linkedObject = null;
332
- } else if (!defined(linkedTo) || id !== linkedId) {
333
- annotation.linkedObject = chart.get(id);
1430
+ if (vAlign === 'bottom') {
1431
+ vAlignFactor = 1;
1432
+ } else if (vAlign === 'middle') {
1433
+ vAlignFactor = 2;
1434
+ }
1435
+ if (vAlignFactor) {
1436
+ y += (box.height - (alignOptions.height || 0)) / vAlignFactor;
334
1437
  }
335
- }
336
- };
337
1438
 
1439
+ return {
1440
+ x: Math.round(x),
1441
+ y: Math.round(y)
1442
+ };
1443
+ },
338
1444
 
339
- // Add annotations methods to chart prototype
340
- extend(Chart.prototype, {
341
- annotations: {
342
- /*
343
- * Unified method for adding annotations to the chart
344
- */
345
- add: function(options, redraw) {
346
- var annotations = this.allItems,
347
- chart = this.chart,
348
- item,
349
- len;
350
-
351
- if (!isArray(options)) {
352
- options = [options];
1445
+ /**
1446
+ * Returns new alignment options for a label if the label is outside the plot area.
1447
+ * It is almost a one-to-one copy from Series.prototype.justifyDataLabel
1448
+ * except it does not mutate the label and it works with absolute instead of relative position
1449
+ *
1450
+ * @function #justifiedOptions
1451
+ * @memberOf Highcharts.Annotation#
1452
+ *
1453
+ * @param {Object} label
1454
+ * @param {Object} alignOptions
1455
+ * @param {Object} alignAttr
1456
+ * @return {Object} justified options
1457
+ **/
1458
+ justifiedOptions: function(label, alignOptions, alignAttr) {
1459
+ var chart = this.chart,
1460
+ align = alignOptions.align,
1461
+ verticalAlign = alignOptions.verticalAlign,
1462
+ padding = label.box ? 0 : (label.padding || 0),
1463
+ bBox = label.getBBox(),
1464
+ off,
1465
+
1466
+ options = {
1467
+ align: align,
1468
+ verticalAlign: verticalAlign,
1469
+ x: alignOptions.x,
1470
+ y: alignOptions.y,
1471
+ width: label.width,
1472
+ height: label.height
1473
+ },
1474
+
1475
+ x = alignAttr.x - chart.plotLeft,
1476
+ y = alignAttr.y - chart.plotTop;
1477
+
1478
+ // Off left
1479
+ off = x + padding;
1480
+ if (off < 0) {
1481
+ if (align === 'right') {
1482
+ options.align = 'left';
1483
+ } else {
1484
+ options.x = -off;
353
1485
  }
1486
+ }
354
1487
 
355
- len = options.length;
1488
+ // Off right
1489
+ off = x + bBox.width - padding;
1490
+ if (off > chart.plotWidth) {
1491
+ if (align === 'left') {
1492
+ options.align = 'right';
1493
+ } else {
1494
+ options.x = chart.plotWidth - off;
1495
+ }
1496
+ }
356
1497
 
357
- while (len--) {
358
- item = new Annotation(chart, options[len]);
359
- annotations.push(item);
360
- item.render(redraw);
1498
+ // Off top
1499
+ off = y + padding;
1500
+ if (off < 0) {
1501
+ if (verticalAlign === 'bottom') {
1502
+ options.verticalAlign = 'top';
1503
+ } else {
1504
+ options.y = -off;
361
1505
  }
362
- },
1506
+ }
363
1507
 
364
- /**
365
- * Redraw all annotations, method used in chart events
366
- */
367
- redraw: function() {
368
- each(this.allItems, function(annotation) {
369
- annotation.redraw();
1508
+ // Off bottom
1509
+ off = y + bBox.height - padding;
1510
+ if (off > chart.plotHeight) {
1511
+ if (verticalAlign === 'top') {
1512
+ options.verticalAlign = 'bottom';
1513
+ } else {
1514
+ options.y = chart.plotHeight - off;
1515
+ }
1516
+ }
1517
+
1518
+ return options;
1519
+ },
1520
+
1521
+
1522
+ /**
1523
+ * Utility function for mapping item's options to element's attribute
1524
+ *
1525
+ * @function #attrsFromOptions
1526
+ * @memberOf Highcharts.Annotation#
1527
+ *
1528
+ * @param {Object} options
1529
+ * @return {Object} mapped options
1530
+ **/
1531
+ attrsFromOptions: function(options) {
1532
+ var map = this.attrsMap,
1533
+ attrs = {},
1534
+ key,
1535
+ mappedKey;
1536
+
1537
+ for (key in options) {
1538
+ mappedKey = map[key];
1539
+ if (mappedKey) {
1540
+ attrs[mappedKey] = options[key];
1541
+ }
1542
+ }
1543
+
1544
+ return attrs;
1545
+ }
1546
+ };
1547
+
1548
+ /* ***************************************************************************
1549
+ *
1550
+ * EXTENDING CHART PROTOTYPE
1551
+ *
1552
+ **************************************************************************** */
1553
+
1554
+ H.extend(chartPrototype, {
1555
+ addAnnotation: function(userOptions, redraw) {
1556
+ var annotation = new Annotation(this, userOptions);
1557
+
1558
+ this.annotations.push(annotation);
1559
+
1560
+ if (pick(redraw, true)) {
1561
+ annotation.redraw();
1562
+ }
1563
+
1564
+ return annotation;
1565
+ },
1566
+
1567
+ removeAnnotation: function(id) {
1568
+ var annotations = this.annotations,
1569
+ annotation = find(annotations, function(annotation) {
1570
+ return annotation.options.id === id;
370
1571
  });
1572
+
1573
+ if (annotation) {
1574
+ erase(annotations, annotation);
1575
+ annotation.destroy();
1576
+ }
1577
+ },
1578
+
1579
+ drawAnnotations: function() {
1580
+ var clip = this.plotBoxClip,
1581
+ plotBox = this.plotBox;
1582
+
1583
+ if (clip) {
1584
+ clip.attr(plotBox);
1585
+ } else {
1586
+ this.plotBoxClip = this.renderer.clipRect(plotBox);
371
1587
  }
1588
+
1589
+ each(this.annotations, function(annotation) {
1590
+ annotation.redraw();
1591
+ });
372
1592
  }
373
1593
  });
374
1594
 
375
1595
 
376
- // Initialize on chart load
377
- Chart.prototype.callbacks.push(function(chart) {
378
- var options = chart.options.annotations,
379
- group;
1596
+ chartPrototype.callbacks.push(function(chart) {
1597
+ chart.annotations = [];
380
1598
 
381
- group = chart.renderer.g('annotations');
382
- group.attr({
383
- zIndex: 7
1599
+ each(chart.options.annotations, function(annotationOptions) {
1600
+ chart.addAnnotation(annotationOptions, false);
384
1601
  });
385
- group.add();
386
1602
 
387
- // initialize empty array for annotations
388
- chart.annotations.allItems = [];
1603
+ chart.drawAnnotations();
1604
+ addEvent(chart, 'redraw', chart.drawAnnotations);
1605
+ addEvent(chart, 'destroy', function() {
1606
+ var plotBoxClip = chart.plotBoxClip;
389
1607
 
390
- // link chart object to annotations
391
- chart.annotations.chart = chart;
1608
+ if (plotBoxClip && plotBoxClip.destroy) {
1609
+ plotBoxClip.destroy();
1610
+ }
1611
+ });
1612
+ });
392
1613
 
393
- // link annotations group element to the chart
394
- chart.annotations.group = group;
395
1614
 
396
- if (isArray(options) && options.length > 0) {
397
- chart.annotations.add(chart.options.annotations);
398
- }
1615
+ H.wrap(chartPrototype, 'getContainer', function(p) {
1616
+ p.call(this);
399
1617
 
400
- // update annotations after chart redraw
401
- H.addEvent(chart, 'redraw', function() {
402
- chart.annotations.redraw();
1618
+ var renderer = this.renderer,
1619
+ options = this.options,
1620
+ key,
1621
+ markers,
1622
+ marker;
1623
+
1624
+ options.defs = merge(options.defs || {}, {
1625
+ markers: defaultMarkers
403
1626
  });
1627
+ markers = options.defs.markers;
1628
+
1629
+ for (key in markers) {
1630
+ marker = markers[key];
1631
+
1632
+ if (pick(marker.render, true)) {
1633
+ renderer.addMarker(marker.id, marker);
1634
+ }
1635
+ }
404
1636
  });
405
1637
 
1638
+
1639
+ /* ************************************************************************* */
1640
+
1641
+ /**
1642
+ * General symbol definition for labels with connector
1643
+ */
1644
+ H.SVGRenderer.prototype.symbols.connector = function(x, y, w, h, options) {
1645
+ var anchorX = options && options.anchorX,
1646
+ anchorY = options && options.anchorY,
1647
+ path,
1648
+ yOffset,
1649
+ lateral = w / 2;
1650
+
1651
+ if (isNumber(anchorX) && isNumber(anchorY)) {
1652
+
1653
+ path = ['M', anchorX, anchorY];
1654
+
1655
+ // Prefer 45 deg connectors
1656
+ yOffset = y - anchorY;
1657
+ if (yOffset < 0) {
1658
+ yOffset = -h - yOffset;
1659
+ }
1660
+ if (yOffset < w) {
1661
+ lateral = anchorX < x + (w / 2) ? yOffset : w - yOffset;
1662
+ }
1663
+
1664
+ // Anchor below label
1665
+ if (anchorY > y + h) {
1666
+ path.push('L', x + lateral, y + h);
1667
+
1668
+ // Anchor above label
1669
+ } else if (anchorY < y) {
1670
+ path.push('L', x + lateral, y);
1671
+
1672
+ // Anchor left of label
1673
+ } else if (anchorX < x) {
1674
+ path.push('L', x, y + h / 2);
1675
+
1676
+ // Anchor right of label
1677
+ } else if (anchorX > x + w) {
1678
+ path.push('L', x + w, y + h / 2);
1679
+ }
1680
+ }
1681
+ return path || [];
1682
+ };
1683
+
406
1684
  }(Highcharts));
407
1685
  }));