highcharts-rails 5.0.14 → 6.0.0

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