annotations.src.js 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321
  1. /* *
  2. *
  3. * (c) 2009-2017 Highsoft, Black Label
  4. *
  5. * License: www.highcharts.com/license
  6. *
  7. * */
  8. 'use strict';
  9. import H from '../parts/Globals.js';
  10. import U from '../parts/Utilities.js';
  11. var defined = U.defined,
  12. destroyObjectProperties = U.destroyObjectProperties,
  13. erase = U.erase,
  14. extend = U.extend,
  15. pick = U.pick,
  16. splat = U.splat,
  17. wrap = U.wrap;
  18. import '../parts/Chart.js';
  19. import controllableMixin from './controllable/controllableMixin.js';
  20. import ControllableRect from './controllable/ControllableRect.js';
  21. import ControllableCircle from './controllable/ControllableCircle.js';
  22. import ControllablePath from './controllable/ControllablePath.js';
  23. import ControllableImage from './controllable/ControllableImage.js';
  24. import ControllableLabel from './controllable/ControllableLabel.js';
  25. import eventEmitterMixin from './eventEmitterMixin.js';
  26. import MockPoint from './MockPoint.js';
  27. import ControlPoint from './ControlPoint.js';
  28. var merge = H.merge,
  29. addEvent = H.addEvent,
  30. fireEvent = H.fireEvent,
  31. find = H.find,
  32. reduce = H.reduce,
  33. chartProto = H.Chart.prototype;
  34. /* *********************************************************************
  35. *
  36. * ANNOTATION
  37. *
  38. ******************************************************************** */
  39. /**
  40. * @typedef {
  41. * Annotation.ControllableCircle|
  42. * Annotation.ControllableImage|
  43. * Annotation.ControllablePath|
  44. * Annotation.ControllableRect
  45. * }
  46. * Annotation.Shape
  47. */
  48. /**
  49. * @typedef {Annotation.ControllableLabel} Annotation.Label
  50. */
  51. /**
  52. * An annotation class which serves as a container for items like labels or
  53. * shapes. Created items are positioned on the chart either by linking them to
  54. * existing points or created mock points
  55. *
  56. * @class
  57. * @name Highcharts.Annotation
  58. *
  59. * @param {Highcharts.Chart} chart a chart instance
  60. * @param {Highcharts.AnnotationsOptions} userOptions the options object
  61. */
  62. var Annotation = H.Annotation = function (chart, userOptions) {
  63. var labelsAndShapes;
  64. /**
  65. * The chart that the annotation belongs to.
  66. *
  67. * @type {Highcharts.Chart}
  68. */
  69. this.chart = chart;
  70. /**
  71. * The array of points which defines the annotation.
  72. *
  73. * @type {Array<Highcharts.Point>}
  74. */
  75. this.points = [];
  76. /**
  77. * The array of control points.
  78. *
  79. * @type {Array<Annotation.ControlPoint>}
  80. */
  81. this.controlPoints = [];
  82. this.coll = 'annotations';
  83. /**
  84. * The array of labels which belong to the annotation.
  85. *
  86. * @type {Array<Annotation.Label>}
  87. */
  88. this.labels = [];
  89. /**
  90. * The array of shapes which belong to the annotation.
  91. *
  92. * @type {Array<Annotation.Shape>}
  93. */
  94. this.shapes = [];
  95. /**
  96. * The options for the annotations.
  97. *
  98. * @type {Highcharts.AnnotationsOptions}
  99. */
  100. this.options = merge(this.defaultOptions, userOptions);
  101. /**
  102. * The user options for the annotations.
  103. *
  104. * @type {Highcharts.AnnotationsOptions}
  105. */
  106. this.userOptions = userOptions;
  107. // Handle labels and shapes - those are arrays
  108. // Merging does not work with arrays (stores reference)
  109. labelsAndShapes = this.getLabelsAndShapesOptions(
  110. this.options,
  111. userOptions
  112. );
  113. this.options.labels = labelsAndShapes.labels;
  114. this.options.shapes = labelsAndShapes.shapes;
  115. /**
  116. * The callback that reports to the overlapping-labels module which
  117. * labels it should account for.
  118. *
  119. * @name labelCollector
  120. * @memberOf Annotation#
  121. * @type {Function}
  122. */
  123. /**
  124. * The group svg element.
  125. *
  126. * @name group
  127. * @memberOf Annotation#
  128. * @type {Highcharts.SVGElement}
  129. */
  130. /**
  131. * The group svg element of the annotation's shapes.
  132. *
  133. * @name shapesGroup
  134. * @memberOf Annotation#
  135. * @type {Highcharts.SVGElement}
  136. */
  137. /**
  138. * The group svg element of the annotation's labels.
  139. *
  140. * @name labelsGroup
  141. * @memberOf Annotation#
  142. * @type {Highcharts.SVGElement}
  143. */
  144. this.init(chart, this.options);
  145. };
  146. merge(
  147. true,
  148. Annotation.prototype,
  149. controllableMixin,
  150. eventEmitterMixin,
  151. /** @lends Annotation# */
  152. {
  153. /**
  154. * List of events for `annotation.options.events` that should not be
  155. * added to `annotation.graphic` but to the `annotation`.
  156. *
  157. * @type {Array<string>}
  158. */
  159. nonDOMEvents: ['add', 'afterUpdate', 'drag', 'remove'],
  160. /**
  161. * A basic type of an annotation. It allows to add custom labels
  162. * or shapes. The items can be tied to points, axis coordinates
  163. * or chart pixel coordinates.
  164. *
  165. * @sample highcharts/annotations/basic/
  166. * Basic annotations
  167. * @sample highcharts/demo/annotations/
  168. * Advanced annotations
  169. * @sample highcharts/css/annotations
  170. * Styled mode
  171. * @sample highcharts/annotations-advanced/controllable
  172. * Controllable items
  173. * @sample {highstock} stock/annotations/fibonacci-retracements
  174. * Custom annotation, Fibonacci retracement
  175. *
  176. * @type {Array<*>}
  177. * @since 6.0.0
  178. * @requires modules/annotations
  179. * @optionparent annotations
  180. */
  181. defaultOptions: {
  182. /**
  183. * Sets an ID for an annotation. Can be user later when removing an
  184. * annotation in [Chart#removeAnnotation(id)](
  185. * /class-reference/Highcharts.Chart#removeAnnotation) method.
  186. *
  187. * @type {string|number}
  188. * @apioption annotations.id
  189. */
  190. /**
  191. * Whether the annotation is visible.
  192. *
  193. * @sample highcharts/annotations/visible/
  194. * Set annotation visibility
  195. */
  196. visible: true,
  197. /**
  198. * Allow an annotation to be draggable by a user. Possible
  199. * values are `"x"`, `"xy"`, `"y"` and `""` (disabled).
  200. *
  201. * @sample highcharts/annotations/draggable/
  202. * Annotations draggable: 'xy'
  203. *
  204. * @type {string}
  205. * @validvalue ["x", "xy", "y", ""]
  206. */
  207. draggable: 'xy',
  208. /**
  209. * Options for annotation's labels. Each label inherits options
  210. * from the labelOptions object. An option from the labelOptions
  211. * can be overwritten by config for a specific label.
  212. *
  213. * @requires modules/annotations
  214. */
  215. labelOptions: {
  216. /**
  217. * The alignment of the annotation's label. If right,
  218. * the right side of the label should be touching the point.
  219. *
  220. * @sample highcharts/annotations/label-position/
  221. * Set labels position
  222. *
  223. * @type {Highcharts.AlignValue}
  224. */
  225. align: 'center',
  226. /**
  227. * Whether to allow the annotation's labels to overlap.
  228. * To make the labels less sensitive for overlapping,
  229. * the can be set to 0.
  230. *
  231. * @sample highcharts/annotations/tooltip-like/
  232. * Hide overlapping labels
  233. */
  234. allowOverlap: false,
  235. /**
  236. * The background color or gradient for the annotation's label.
  237. *
  238. * @sample highcharts/annotations/label-presentation/
  239. * Set labels graphic options
  240. *
  241. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  242. */
  243. backgroundColor: 'rgba(0, 0, 0, 0.75)',
  244. /**
  245. * The border color for the annotation's label.
  246. *
  247. * @sample highcharts/annotations/label-presentation/
  248. * Set labels graphic options
  249. *
  250. * @type {Highcharts.ColorString}
  251. */
  252. borderColor: 'black',
  253. /**
  254. * The border radius in pixels for the annotaiton's label.
  255. *
  256. * @sample highcharts/annotations/label-presentation/
  257. * Set labels graphic options
  258. */
  259. borderRadius: 3,
  260. /**
  261. * The border width in pixels for the annotation's label
  262. *
  263. * @sample highcharts/annotations/label-presentation/
  264. * Set labels graphic options
  265. */
  266. borderWidth: 1,
  267. /**
  268. * A class name for styling by CSS.
  269. *
  270. * @sample highcharts/css/annotations
  271. * Styled mode annotations
  272. *
  273. * @since 6.0.5
  274. */
  275. className: '',
  276. /**
  277. * Whether to hide the annotation's label
  278. * that is outside the plot area.
  279. *
  280. * @sample highcharts/annotations/label-crop-overflow/
  281. * Crop or justify labels
  282. */
  283. crop: false,
  284. /**
  285. * The label's pixel distance from the point.
  286. *
  287. * @sample highcharts/annotations/label-position/
  288. * Set labels position
  289. *
  290. * @type {number}
  291. * @apioption annotations.labelOptions.distance
  292. */
  293. /**
  294. * A
  295. * [format](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  296. * string for the data label.
  297. *
  298. * @see [plotOptions.series.dataLabels.format](plotOptions.series.dataLabels.format.html)
  299. *
  300. * @sample highcharts/annotations/label-text/
  301. * Set labels text
  302. *
  303. * @type {string}
  304. * @apioption annotations.labelOptions.format
  305. */
  306. /**
  307. * Alias for the format option.
  308. *
  309. * @see [format](annotations.labelOptions.format.html)
  310. *
  311. * @sample highcharts/annotations/label-text/
  312. * Set labels text
  313. *
  314. * @type {string}
  315. * @apioption annotations.labelOptions.text
  316. */
  317. /**
  318. * Callback JavaScript function to format the annotation's
  319. * label. Note that if a `format` or `text` are defined, the
  320. * format or text take precedence and the formatter is ignored.
  321. * `This` refers to a point object.
  322. *
  323. * @sample highcharts/annotations/label-text/
  324. * Set labels text
  325. *
  326. * @type {Highcharts.FormatterCallbackFunction<Highcharts.Point>}
  327. * @default function () { return defined(this.y) ? this.y : 'Annotation label'; }
  328. */
  329. formatter: function () {
  330. return defined(this.y) ? this.y : 'Annotation label';
  331. },
  332. /**
  333. * How to handle the annotation's label that flow outside the
  334. * plot area. The justify option aligns the label inside the
  335. * plot area.
  336. *
  337. * @sample highcharts/annotations/label-crop-overflow/
  338. * Crop or justify labels
  339. *
  340. * @validvalue ["allow", "justify"]
  341. */
  342. overflow: 'justify',
  343. /**
  344. * When either the borderWidth or the backgroundColor is set,
  345. * this is the padding within the box.
  346. *
  347. * @sample highcharts/annotations/label-presentation/
  348. * Set labels graphic options
  349. */
  350. padding: 5,
  351. /**
  352. * The shadow of the box. The shadow can be an object
  353. * configuration containing `color`, `offsetX`, `offsetY`,
  354. * `opacity` and `width`.
  355. *
  356. * @sample highcharts/annotations/label-presentation/
  357. * Set labels graphic options
  358. *
  359. * @type {boolean|Highcharts.ShadowOptionsObject}
  360. */
  361. shadow: false,
  362. /**
  363. * The name of a symbol to use for the border around the label.
  364. * Symbols are predefined functions on the Renderer object.
  365. *
  366. * @sample highcharts/annotations/shapes/
  367. * Available shapes for labels
  368. */
  369. shape: 'callout',
  370. /**
  371. * Styles for the annotation's label.
  372. *
  373. * @see [plotOptions.series.dataLabels.style](plotOptions.series.dataLabels.style.html)
  374. *
  375. * @sample highcharts/annotations/label-presentation/
  376. * Set labels graphic options
  377. *
  378. * @type {Highcharts.CSSObject}
  379. */
  380. style: {
  381. /** @ignore */
  382. fontSize: '11px',
  383. /** @ignore */
  384. fontWeight: 'normal',
  385. /** @ignore */
  386. color: 'contrast'
  387. },
  388. /**
  389. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  390. * to render the annotation's label.
  391. */
  392. useHTML: false,
  393. /**
  394. * The vertical alignment of the annotation's label.
  395. *
  396. * @sample highcharts/annotations/label-position/
  397. * Set labels position
  398. *
  399. * @type {Highcharts.VerticalAlignValue}
  400. */
  401. verticalAlign: 'bottom',
  402. /**
  403. * The x position offset of the label relative to the point.
  404. * Note that if a `distance` is defined, the distance takes
  405. * precedence over `x` and `y` options.
  406. *
  407. * @sample highcharts/annotations/label-position/
  408. * Set labels position
  409. */
  410. x: 0,
  411. /**
  412. * The y position offset of the label relative to the point.
  413. * Note that if a `distance` is defined, the distance takes
  414. * precedence over `x` and `y` options.
  415. *
  416. * @sample highcharts/annotations/label-position/
  417. * Set labels position
  418. */
  419. y: -16
  420. },
  421. /**
  422. * An array of labels for the annotation. For options that apply to
  423. * multiple labels, they can be added to the
  424. * [labelOptions](annotations.labelOptions.html).
  425. *
  426. * @type {Array<*>}
  427. * @extends annotations.labelOptions
  428. * @apioption annotations.labels
  429. */
  430. /**
  431. * This option defines the point to which the label will be
  432. * connected. It can be either the point which exists in the
  433. * series - it is referenced by the point's id - or a new point with
  434. * defined x, y properties and optionally axes.
  435. *
  436. * @sample highcharts/annotations/mock-point/
  437. * Attach annotation to a mock point
  438. *
  439. * @type {string|Highcharts.MockPointOptionsObject}
  440. * @requires modules/annotations
  441. * @apioption annotations.labels.point
  442. */
  443. /**
  444. * The x position of the point. Units can be either in axis
  445. * or chart pixel coordinates.
  446. *
  447. * @type {number}
  448. * @apioption annotations.labels.point.x
  449. */
  450. /**
  451. * The y position of the point. Units can be either in axis
  452. * or chart pixel coordinates.
  453. *
  454. * @type {number}
  455. * @apioption annotations.labels.point.y
  456. */
  457. /**
  458. * This number defines which xAxis the point is connected to. It
  459. * refers to either the axis id or the index of the axis in the
  460. * xAxis array. If the option is not configured or the axis is not
  461. * found the point's x coordinate refers to the chart pixels.
  462. *
  463. * @type {number|string}
  464. * @apioption annotations.labels.point.xAxis
  465. */
  466. /**
  467. * This number defines which yAxis the point is connected to. It
  468. * refers to either the axis id or the index of the axis in the
  469. * yAxis array. If the option is not configured or the axis is not
  470. * found the point's y coordinate refers to the chart pixels.
  471. *
  472. * @type {number|string}
  473. * @apioption annotations.labels.point.yAxis
  474. */
  475. /**
  476. * An array of shapes for the annotation. For options that apply to
  477. * multiple shapes, then can be added to the
  478. * [shapeOptions](annotations.shapeOptions.html).
  479. *
  480. * @type {Array<*>}
  481. * @extends annotations.shapeOptions
  482. * @apioption annotations.shapes
  483. */
  484. /**
  485. * This option defines the point to which the shape will be
  486. * connected. It can be either the point which exists in the
  487. * series - it is referenced by the point's id - or a new point with
  488. * defined x, y properties and optionally axes.
  489. *
  490. * @type {string|Highcharts.MockPointOptionsObject}
  491. * @extends annotations.labels.point
  492. * @apioption annotations.shapes.point
  493. */
  494. /**
  495. * An array of points for the shape. This option is available for
  496. * shapes which can use multiple points such as path. A point can be
  497. * either a point object or a point's id.
  498. *
  499. * @see [annotations.shapes.point](annotations.shapes.point.html)
  500. *
  501. * @type {Array<string|Highcharts.MockPointOptionsObject>}
  502. * @extends annotations.labels.point
  503. * @apioption annotations.shapes.points
  504. */
  505. /**
  506. * Id of the marker which will be drawn at the final vertex of the
  507. * path. Custom markers can be defined in defs property.
  508. *
  509. * @see [defs.markers](defs.markers.html)
  510. *
  511. * @sample highcharts/annotations/custom-markers/
  512. * Define a custom marker for annotations
  513. *
  514. * @type {string}
  515. * @apioption annotations.shapes.markerEnd
  516. */
  517. /**
  518. * Id of the marker which will be drawn at the first vertex of the
  519. * path. Custom markers can be defined in defs property.
  520. *
  521. * @see [defs.markers](defs.markers.html)
  522. *
  523. * @sample {highcharts} highcharts/annotations/custom-markers/
  524. * Define a custom marker for annotations
  525. *
  526. * @type {string}
  527. * @apioption annotations.shapes.markerStart
  528. */
  529. /**
  530. * Options for annotation's shapes. Each shape inherits options from
  531. * the shapeOptions object. An option from the shapeOptions can be
  532. * overwritten by config for a specific shape.
  533. *
  534. * @requires modules/annotations
  535. */
  536. shapeOptions: {
  537. /**
  538. * The width of the shape.
  539. *
  540. * @sample highcharts/annotations/shape/
  541. * Basic shape annotation
  542. *
  543. * @type {number}
  544. * @apioption annotations.shapeOptions.width
  545. **/
  546. /**
  547. * The height of the shape.
  548. *
  549. * @sample highcharts/annotations/shape/
  550. * Basic shape annotation
  551. *
  552. * @type {number}
  553. * @apioption annotations.shapeOptions.height
  554. */
  555. /**
  556. * The type of the shape, e.g. circle or rectangle.
  557. *
  558. * @sample highcharts/annotations/shape/
  559. * Basic shape annotation
  560. *
  561. * @type {string}
  562. * @default 'rect'
  563. * @apioption annotations.shapeOptions.type
  564. */
  565. /**
  566. * The color of the shape's stroke.
  567. *
  568. * @sample highcharts/annotations/shape/
  569. * Basic shape annotation
  570. *
  571. * @type {Highcharts.ColorString}
  572. */
  573. stroke: 'rgba(0, 0, 0, 0.75)',
  574. /**
  575. * The pixel stroke width of the shape.
  576. *
  577. * @sample highcharts/annotations/shape/
  578. * Basic shape annotation
  579. */
  580. strokeWidth: 1,
  581. /**
  582. * The color of the shape's fill.
  583. *
  584. * @sample highcharts/annotations/shape/
  585. * Basic shape annotation
  586. *
  587. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  588. */
  589. fill: 'rgba(0, 0, 0, 0.75)',
  590. /**
  591. * The radius of the shape.
  592. *
  593. * @sample highcharts/annotations/shape/
  594. * Basic shape annotation
  595. */
  596. r: 0,
  597. /**
  598. * Defines additional snapping area around an annotation
  599. * making this annotation to focus. Defined in pixels.
  600. */
  601. snap: 2
  602. },
  603. /**
  604. * Options for annotation's control points. Each control point
  605. * inherits options from controlPointOptions object.
  606. * Options from the controlPointOptions can be overwritten
  607. * by options in a specific control point.
  608. *
  609. * @type {Annotation.ControlPoint.Options}
  610. * @requires modules/annotations
  611. * @apioption annotations.controlPointOptions
  612. */
  613. controlPointOptions: {
  614. /**
  615. * @function {Annotation.ControlPoint.Positioner}
  616. * @apioption annotations.controlPointOptions.positioner
  617. */
  618. symbol: 'circle',
  619. width: 10,
  620. height: 10,
  621. style: {
  622. stroke: 'black',
  623. 'stroke-width': 2,
  624. fill: 'white'
  625. },
  626. visible: false,
  627. events: {}
  628. },
  629. /**
  630. * Event callback when annotation is added to the chart.
  631. *
  632. * @type {Highcharts.EventCallbackFunction<Highcharts.Annotation>}
  633. * @since 7.1.0
  634. * @apioption annotations.events.add
  635. */
  636. /**
  637. * Event callback when annotation is updated (e.g. drag and
  638. * droppped or resized by control points).
  639. *
  640. * @type {Highcharts.EventCallbackFunction<Highcharts.Annotation>}
  641. * @since 7.1.0
  642. * @apioption annotations.events.afterUpdate
  643. */
  644. /**
  645. * Event callback when annotation is removed from the chart.
  646. *
  647. * @type {Highcharts.EventCallbackFunction<Highcharts.Annotation>}
  648. * @since 7.1.0
  649. * @apioption annotations.events.remove
  650. */
  651. /**
  652. * Events available in annotations.
  653. *
  654. * @requires modules/annotations
  655. */
  656. events: {},
  657. /**
  658. * The Z index of the annotation.
  659. */
  660. zIndex: 6
  661. },
  662. /**
  663. * Initialize the annotation.
  664. *
  665. * @param {Highcharts.Chart}
  666. * The chart
  667. * @param {Highcharts.AnnotationsOptions}
  668. * The user options for the annotation
  669. */
  670. init: function () {
  671. this.linkPoints();
  672. this.addControlPoints();
  673. this.addShapes();
  674. this.addLabels();
  675. this.addClipPaths();
  676. this.setLabelCollector();
  677. },
  678. getLabelsAndShapesOptions: function (baseOptions, newOptions) {
  679. var mergedOptions = {};
  680. ['labels', 'shapes'].forEach(function (name) {
  681. if (baseOptions[name]) {
  682. mergedOptions[name] = splat(newOptions[name]).map(
  683. function (basicOptions, i) {
  684. return merge(baseOptions[name][i], basicOptions);
  685. }
  686. );
  687. }
  688. });
  689. return mergedOptions;
  690. },
  691. addShapes: function () {
  692. (this.options.shapes || []).forEach(function (shapeOptions, i) {
  693. var shape = this.initShape(shapeOptions, i);
  694. merge(true, this.options.shapes[i], shape.options);
  695. }, this);
  696. },
  697. addLabels: function () {
  698. (this.options.labels || []).forEach(function (labelsOptions, i) {
  699. var labels = this.initLabel(labelsOptions, i);
  700. merge(true, this.options.labels[i], labels.options);
  701. }, this);
  702. },
  703. addClipPaths: function () {
  704. this.setClipAxes();
  705. if (this.clipXAxis && this.clipYAxis) {
  706. this.clipRect = this.chart.renderer.clipRect(
  707. this.getClipBox()
  708. );
  709. }
  710. },
  711. setClipAxes: function () {
  712. var xAxes = this.chart.xAxis,
  713. yAxes = this.chart.yAxis,
  714. linkedAxes = reduce(
  715. (this.options.labels || [])
  716. .concat(this.options.shapes || []),
  717. function (axes, labelOrShape) {
  718. return [
  719. xAxes[
  720. labelOrShape &&
  721. labelOrShape.point &&
  722. labelOrShape.point.xAxis
  723. ] || axes[0],
  724. yAxes[
  725. labelOrShape &&
  726. labelOrShape.point &&
  727. labelOrShape.point.yAxis
  728. ] || axes[1]
  729. ];
  730. },
  731. []
  732. );
  733. this.clipXAxis = linkedAxes[0];
  734. this.clipYAxis = linkedAxes[1];
  735. },
  736. getClipBox: function () {
  737. return {
  738. x: this.clipXAxis.left,
  739. y: this.clipYAxis.top,
  740. width: this.clipXAxis.width,
  741. height: this.clipYAxis.height
  742. };
  743. },
  744. setLabelCollector: function () {
  745. var annotation = this;
  746. annotation.labelCollector = function () {
  747. return annotation.labels.reduce(
  748. function (labels, label) {
  749. if (!label.options.allowOverlap) {
  750. labels.push(label.graphic);
  751. }
  752. return labels;
  753. },
  754. []
  755. );
  756. };
  757. annotation.chart.labelCollectors.push(
  758. annotation.labelCollector
  759. );
  760. },
  761. /**
  762. * Set an annotation options.
  763. *
  764. * @param {Highcharts.AnnotationsOptions} - user options for an annotation
  765. */
  766. setOptions: function (userOptions) {
  767. this.options = merge(this.defaultOptions, userOptions);
  768. },
  769. redraw: function (animation) {
  770. this.linkPoints();
  771. if (!this.graphic) {
  772. this.render();
  773. }
  774. if (this.clipRect) {
  775. this.clipRect.animate(this.getClipBox());
  776. }
  777. this.redrawItems(this.shapes, animation);
  778. this.redrawItems(this.labels, animation);
  779. controllableMixin.redraw.call(this, animation);
  780. },
  781. /**
  782. * @param {Array<(Annotation.Label|Annotation.Shape)>} items
  783. * @param {boolean} [animation]
  784. */
  785. redrawItems: function (items, animation) {
  786. var i = items.length;
  787. // needs a backward loop
  788. // labels/shapes array might be modified
  789. // due to destruction of the item
  790. while (i--) {
  791. this.redrawItem(items[i], animation);
  792. }
  793. },
  794. render: function () {
  795. var renderer = this.chart.renderer;
  796. this.graphic = renderer
  797. .g('annotation')
  798. .attr({
  799. zIndex: this.options.zIndex,
  800. visibility: this.options.visible ?
  801. 'visible' :
  802. 'hidden'
  803. })
  804. .add();
  805. this.shapesGroup = renderer
  806. .g('annotation-shapes')
  807. .add(this.graphic)
  808. .clip(this.chart.plotBoxClip);
  809. this.labelsGroup = renderer
  810. .g('annotation-labels')
  811. .attr({
  812. // hideOverlappingLabels requires translation
  813. translateX: 0,
  814. translateY: 0
  815. })
  816. .add(this.graphic);
  817. if (this.clipRect) {
  818. this.graphic.clip(this.clipRect);
  819. }
  820. this.addEvents();
  821. controllableMixin.render.call(this);
  822. },
  823. /**
  824. * Set the annotation's visibility.
  825. *
  826. * @param {Boolean} [visible] - Whether to show or hide an annotation.
  827. * If the param is omitted, the annotation's visibility is toggled.
  828. */
  829. setVisibility: function (visibility) {
  830. var options = this.options,
  831. visible = pick(visibility, !options.visible);
  832. this.graphic.attr(
  833. 'visibility',
  834. visible ? 'visible' : 'hidden'
  835. );
  836. if (!visible) {
  837. this.setControlPointsVisibility(false);
  838. }
  839. options.visible = visible;
  840. },
  841. setControlPointsVisibility: function (visible) {
  842. var setItemControlPointsVisibility = function (item) {
  843. item.setControlPointsVisibility(visible);
  844. };
  845. controllableMixin.setControlPointsVisibility.call(
  846. this,
  847. visible
  848. );
  849. this.shapes.forEach(setItemControlPointsVisibility);
  850. this.labels.forEach(setItemControlPointsVisibility);
  851. },
  852. /**
  853. * Destroy the annotation. This function does not touch the chart
  854. * that the annotation belongs to (all annotations are kept in
  855. * the chart.annotations array) - it is recommended to use
  856. * {@link Highcharts.Chart#removeAnnotation} instead.
  857. */
  858. destroy: function () {
  859. var chart = this.chart,
  860. destroyItem = function (item) {
  861. item.destroy();
  862. };
  863. this.labels.forEach(destroyItem);
  864. this.shapes.forEach(destroyItem);
  865. this.clipXAxis = null;
  866. this.clipYAxis = null;
  867. erase(chart.labelCollectors, this.labelCollector);
  868. eventEmitterMixin.destroy.call(this);
  869. controllableMixin.destroy.call(this);
  870. destroyObjectProperties(this, chart);
  871. },
  872. /**
  873. * See {@link Highcharts.Chart#removeAnnotation}.
  874. */
  875. remove: function () {
  876. // Let chart.update() remove annoations on demand
  877. return this.chart.removeAnnotation(this);
  878. },
  879. update: function (userOptions) {
  880. var chart = this.chart,
  881. labelsAndShapes = this.getLabelsAndShapesOptions(
  882. this.userOptions,
  883. userOptions
  884. ),
  885. userOptionsIndex = chart.annotations.indexOf(this),
  886. options = H.merge(true, this.userOptions, userOptions);
  887. options.labels = labelsAndShapes.labels;
  888. options.shapes = labelsAndShapes.shapes;
  889. this.destroy();
  890. this.constructor(chart, options);
  891. // Update options in chart options, used in exporting (#9767):
  892. chart.options.annotations[userOptionsIndex] = options;
  893. this.isUpdating = true;
  894. this.redraw();
  895. this.isUpdating = false;
  896. fireEvent(this, 'afterUpdate');
  897. },
  898. /* *************************************************************
  899. * ITEM SECTION
  900. * Contains methods for handling a single item in an annotation
  901. **************************************************************** */
  902. /**
  903. * Initialisation of a single shape
  904. *
  905. * @param {Object} shapeOptions - a confg object for a single shape
  906. */
  907. initShape: function (shapeOptions, index) {
  908. var options = merge(
  909. this.options.shapeOptions,
  910. {
  911. controlPointOptions: this.options.controlPointOptions
  912. },
  913. shapeOptions
  914. ),
  915. shape = new Annotation.shapesMap[options.type](
  916. this,
  917. options,
  918. index
  919. );
  920. shape.itemType = 'shape';
  921. this.shapes.push(shape);
  922. return shape;
  923. },
  924. /**
  925. * Initialisation of a single label
  926. *
  927. * @param {Object} labelOptions
  928. **/
  929. initLabel: function (labelOptions, index) {
  930. var options = merge(
  931. this.options.labelOptions,
  932. {
  933. controlPointOptions: this.options.controlPointOptions
  934. },
  935. labelOptions
  936. ),
  937. label = new ControllableLabel(
  938. this,
  939. options,
  940. index
  941. );
  942. label.itemType = 'label';
  943. this.labels.push(label);
  944. return label;
  945. },
  946. /**
  947. * Redraw a single item.
  948. *
  949. * @param {Annotation.Label|Annotation.Shape} item
  950. * @param {boolean} [animation]
  951. */
  952. redrawItem: function (item, animation) {
  953. item.linkPoints();
  954. if (!item.shouldBeDrawn()) {
  955. this.destroyItem(item);
  956. } else {
  957. if (!item.graphic) {
  958. this.renderItem(item);
  959. }
  960. item.redraw(
  961. pick(animation, true) && item.graphic.placed
  962. );
  963. if (item.points.length) {
  964. this.adjustVisibility(item);
  965. }
  966. }
  967. },
  968. /**
  969. * Hide or show annotaiton attached to points.
  970. *
  971. * @param {Annotation.Label|Annotation.Shape} item
  972. */
  973. adjustVisibility: function (item) { // #9481
  974. var hasVisiblePoints = false,
  975. label = item.graphic;
  976. item.points.forEach(function (point) {
  977. if (
  978. point.series.visible !== false &&
  979. point.visible !== false
  980. ) {
  981. hasVisiblePoints = true;
  982. }
  983. });
  984. if (!hasVisiblePoints) {
  985. label.hide();
  986. } else if (label.visibility === 'hidden') {
  987. label.show();
  988. }
  989. },
  990. /**
  991. * Destroy a single item.
  992. *
  993. * @param {Annotation.Label|Annotation.Shape} item
  994. */
  995. destroyItem: function (item) {
  996. // erase from shapes or labels array
  997. erase(this[item.itemType + 's'], item);
  998. item.destroy();
  999. },
  1000. /**
  1001. * @private
  1002. */
  1003. renderItem: function (item) {
  1004. item.render(
  1005. item.itemType === 'label' ?
  1006. this.labelsGroup :
  1007. this.shapesGroup
  1008. );
  1009. }
  1010. }
  1011. );
  1012. /**
  1013. * An object uses for mapping between a shape type and a constructor.
  1014. * To add a new shape type extend this object with type name as a key
  1015. * and a constructor as its value.
  1016. */
  1017. Annotation.shapesMap = {
  1018. 'rect': ControllableRect,
  1019. 'circle': ControllableCircle,
  1020. 'path': ControllablePath,
  1021. 'image': ControllableImage
  1022. };
  1023. Annotation.types = {};
  1024. Annotation.MockPoint = MockPoint;
  1025. Annotation.ControlPoint = ControlPoint;
  1026. H.extendAnnotation = function (
  1027. Constructor,
  1028. BaseConstructor,
  1029. prototype,
  1030. defaultOptions
  1031. ) {
  1032. BaseConstructor = BaseConstructor || Annotation;
  1033. merge(
  1034. true,
  1035. Constructor.prototype,
  1036. BaseConstructor.prototype,
  1037. prototype
  1038. );
  1039. Constructor.prototype.defaultOptions = merge(
  1040. Constructor.prototype.defaultOptions,
  1041. defaultOptions || {}
  1042. );
  1043. };
  1044. /* *********************************************************************
  1045. *
  1046. * EXTENDING CHART PROTOTYPE
  1047. *
  1048. ******************************************************************** */
  1049. extend(chartProto, /** @lends Highcharts.Chart# */ {
  1050. initAnnotation: function (userOptions) {
  1051. var Constructor =
  1052. Annotation.types[userOptions.type] || Annotation,
  1053. annotation = new Constructor(this, userOptions);
  1054. this.annotations.push(annotation);
  1055. return annotation;
  1056. },
  1057. /**
  1058. * Add an annotation to the chart after render time.
  1059. *
  1060. * @param {Highcharts.AnnotationsOptions} options
  1061. * The annotation options for the new, detailed annotation.
  1062. * @param {boolean} [redraw]
  1063. *
  1064. * @return {Highcharts.Annotation} - The newly generated annotation.
  1065. */
  1066. addAnnotation: function (userOptions, redraw) {
  1067. var annotation = this.initAnnotation(userOptions);
  1068. this.options.annotations.push(annotation.options);
  1069. if (pick(redraw, true)) {
  1070. annotation.redraw();
  1071. }
  1072. return annotation;
  1073. },
  1074. /**
  1075. * Remove an annotation from the chart.
  1076. *
  1077. * @param {String|Number|Annotation} idOrAnnotation - The annotation's id or
  1078. * direct annotation object.
  1079. */
  1080. removeAnnotation: function (idOrAnnotation) {
  1081. var annotations = this.annotations,
  1082. annotation = idOrAnnotation.coll === 'annotations' ?
  1083. idOrAnnotation :
  1084. find(
  1085. annotations,
  1086. function (annotation) {
  1087. return annotation.options.id === idOrAnnotation;
  1088. }
  1089. );
  1090. if (annotation) {
  1091. fireEvent(annotation, 'remove');
  1092. erase(this.options.annotations, annotation.options);
  1093. erase(annotations, annotation);
  1094. annotation.destroy();
  1095. }
  1096. },
  1097. drawAnnotations: function () {
  1098. this.plotBoxClip.attr(this.plotBox);
  1099. this.annotations.forEach(function (annotation) {
  1100. annotation.redraw();
  1101. });
  1102. }
  1103. });
  1104. // Let chart.update() update annotations
  1105. chartProto.collectionsWithUpdate.push('annotations');
  1106. // Let chart.update() create annoations on demand
  1107. chartProto.collectionsWithInit.annotations = [chartProto.addAnnotation];
  1108. chartProto.callbacks.push(function (chart) {
  1109. chart.annotations = [];
  1110. if (!chart.options.annotations) {
  1111. chart.options.annotations = [];
  1112. }
  1113. chart.plotBoxClip = this.renderer.clipRect(this.plotBox);
  1114. chart.controlPointsGroup = chart.renderer
  1115. .g('control-points')
  1116. .attr({ zIndex: 99 })
  1117. .clip(chart.plotBoxClip)
  1118. .add();
  1119. chart.options.annotations.forEach(function (annotationOptions, i) {
  1120. var annotation = chart.initAnnotation(annotationOptions);
  1121. chart.options.annotations[i] = annotation.options;
  1122. });
  1123. chart.drawAnnotations();
  1124. addEvent(chart, 'redraw', chart.drawAnnotations);
  1125. addEvent(chart, 'destroy', function () {
  1126. chart.plotBoxClip.destroy();
  1127. chart.controlPointsGroup.destroy();
  1128. });
  1129. });
  1130. wrap(
  1131. H.Pointer.prototype,
  1132. 'onContainerMouseDown',
  1133. function (proceed) {
  1134. if (!this.chart.hasDraggedAnnotation) {
  1135. proceed.apply(this, Array.prototype.slice.call(arguments, 1));
  1136. }
  1137. }
  1138. );