controllableMixin.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /* *
  2. *
  3. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4. *
  5. * */
  6. 'use strict';
  7. import ControlPoint from './../ControlPoint.js';
  8. import MockPoint from './../MockPoint.js';
  9. import Tooltip from '../../parts/Tooltip.js';
  10. import U from './../../parts/Utilities.js';
  11. var isObject = U.isObject, isString = U.isString, merge = U.merge, splat = U.splat;
  12. /**
  13. * An object which denots a controllable's anchor positions - relative and
  14. * absolute.
  15. *
  16. * @private
  17. * @interface Highcharts.AnnotationAnchorObject
  18. */ /**
  19. * Relative to the plot area position
  20. * @name Highcharts.AnnotationAnchorObject#relativePosition
  21. * @type {Highcharts.BBoxObject}
  22. */ /**
  23. * Absolute position
  24. * @name Highcharts.AnnotationAnchorObject#absolutePosition
  25. * @type {Highcharts.BBoxObject}
  26. */
  27. /**
  28. * @interface Highcharts.AnnotationControllable
  29. */ /**
  30. * @name Highcharts.AnnotationControllable#annotation
  31. * @type {Highcharts.Annotation}
  32. */ /**
  33. * @name Highcharts.AnnotationControllable#chart
  34. * @type {Highcharts.Chart}
  35. */ /**
  36. * @name Highcharts.AnnotationControllable#collection
  37. * @type {string}
  38. */ /**
  39. * @private
  40. * @name Highcharts.AnnotationControllable#controlPoints
  41. * @type {Array<Highcharts.AnnotationControlPoint>}
  42. */ /**
  43. * @name Highcharts.AnnotationControllable#points
  44. * @type {Array<Highcharts.Point>}
  45. */
  46. import './../../parts/Tooltip.js';
  47. /* eslint-disable no-invalid-this, valid-jsdoc */
  48. /**
  49. * It provides methods for handling points, control points
  50. * and points transformations.
  51. *
  52. * @private
  53. * @mixin
  54. * @name Highcharts.AnnotationControllableMixin
  55. */
  56. var controllableMixin = {
  57. /**
  58. * Init the controllable
  59. */
  60. init: function (annotation, options, index) {
  61. this.annotation = annotation;
  62. this.chart = annotation.chart;
  63. this.options = options;
  64. this.points = [];
  65. this.controlPoints = [];
  66. this.index = index;
  67. this.linkPoints();
  68. this.addControlPoints();
  69. },
  70. /**
  71. * Redirect attr usage on the controllable graphic element.
  72. */
  73. attr: function () {
  74. this.graphic.attr.apply(this.graphic, arguments);
  75. },
  76. /**
  77. * Get the controllable's points options.
  78. *
  79. * @return {Array<Highcharts.PointOptionsObject>}
  80. * An array of points' options.
  81. */
  82. getPointsOptions: function () {
  83. var options = this.options;
  84. return (options.points || (options.point && splat(options.point)));
  85. },
  86. /**
  87. * Utility function for mapping item's options
  88. * to element's attribute
  89. *
  90. * @param {Highcharts.AnnotationsLabelsOptions|Highcharts.AnnotationsShapesOptions} options
  91. *
  92. * @return {Highcharts.SVGAttributes}
  93. * Mapped options.
  94. */
  95. attrsFromOptions: function (options) {
  96. var map = this.constructor.attrsMap, attrs = {}, key, mappedKey, styledMode = this.chart.styledMode;
  97. for (key in options) { // eslint-disable-line guard-for-in
  98. mappedKey = map[key];
  99. if (mappedKey &&
  100. (!styledMode ||
  101. ['fill', 'stroke', 'stroke-width']
  102. .indexOf(mappedKey) === -1)) {
  103. attrs[mappedKey] = options[key];
  104. }
  105. }
  106. return attrs;
  107. },
  108. /**
  109. * Returns object which denotes anchor position - relative and absolute.
  110. *
  111. * @param {Highcharts.AnnotationPointType} point
  112. * A point like object.
  113. *
  114. * @return {Highcharts.AnnotationAnchorObject} a controllable anchor
  115. */
  116. anchor: function (point) {
  117. var plotBox = point.series.getPlotBox(), box = point.mock ?
  118. point.toAnchor() :
  119. Tooltip.prototype.getAnchor.call({
  120. chart: point.series.chart
  121. }, point), anchor = {
  122. x: box[0] + (this.options.x || 0),
  123. y: box[1] + (this.options.y || 0),
  124. height: box[2] || 0,
  125. width: box[3] || 0
  126. };
  127. return {
  128. relativePosition: anchor,
  129. absolutePosition: merge(anchor, {
  130. x: anchor.x + plotBox.translateX,
  131. y: anchor.y + plotBox.translateY
  132. })
  133. };
  134. },
  135. /**
  136. * Map point's options to a point-like object.
  137. *
  138. * @param {string|Function|Highcharts.AnnotationMockPointOptionsObject|Highcharts.AnnotationPointType} pointOptions
  139. * Point's options.
  140. *
  141. * @param {Highcharts.AnnotationPointType} point
  142. * A point-like instance.
  143. *
  144. * @return {Highcharts.AnnotationPointType|null}
  145. * if the point is found/set returns this point, otherwise null
  146. */
  147. point: function (pointOptions, point) {
  148. if (pointOptions && pointOptions.series) {
  149. return pointOptions;
  150. }
  151. if (!point || point.series === null) {
  152. if (isObject(pointOptions)) {
  153. point = new MockPoint(this.chart, this, pointOptions);
  154. }
  155. else if (isString(pointOptions)) {
  156. point = this.chart.get(pointOptions) || null;
  157. }
  158. else if (typeof pointOptions === 'function') {
  159. var pointConfig = pointOptions.call(point, this);
  160. point = pointConfig.series ?
  161. pointConfig :
  162. new MockPoint(this.chart, this, pointOptions);
  163. }
  164. }
  165. return point;
  166. },
  167. /**
  168. * Find point-like objects based on points options.
  169. *
  170. * @return {Array<Annotation.PointLike>} an array of point-like objects
  171. */
  172. linkPoints: function () {
  173. var pointsOptions = this.getPointsOptions(), points = this.points, len = (pointsOptions && pointsOptions.length) || 0, i, point;
  174. for (i = 0; i < len; i++) {
  175. point = this.point(pointsOptions[i], points[i]);
  176. if (!point) {
  177. points.length = 0;
  178. return;
  179. }
  180. if (point.mock) {
  181. point.refresh();
  182. }
  183. points[i] = point;
  184. }
  185. return points;
  186. },
  187. /**
  188. * Add control points to a controllable.
  189. */
  190. addControlPoints: function () {
  191. var controlPointsOptions = this.options.controlPoints;
  192. (controlPointsOptions || []).forEach(function (controlPointOptions, i) {
  193. var options = merge(this.options.controlPointOptions, controlPointOptions);
  194. if (!options.index) {
  195. options.index = i;
  196. }
  197. controlPointsOptions[i] = options;
  198. this.controlPoints.push(new ControlPoint(this.chart, this, options));
  199. }, this);
  200. },
  201. /**
  202. * Check if a controllable should be rendered/redrawn.
  203. *
  204. * @return {boolean}
  205. * Whether a controllable should be drawn.
  206. */
  207. shouldBeDrawn: function () {
  208. return Boolean(this.points.length);
  209. },
  210. /**
  211. * Render a controllable.
  212. */
  213. render: function (_parentGroup) {
  214. this.controlPoints.forEach(function (controlPoint) {
  215. controlPoint.render();
  216. });
  217. },
  218. /**
  219. * Redraw a controllable.
  220. *
  221. * @param {boolean} [animation]
  222. */
  223. redraw: function (animation) {
  224. this.controlPoints.forEach(function (controlPoint) {
  225. controlPoint.redraw(animation);
  226. });
  227. },
  228. /**
  229. * Transform a controllable with a specific transformation.
  230. *
  231. * @param {string} transformation a transformation name
  232. * @param {number|null} cx origin x transformation
  233. * @param {number|null} cy origin y transformation
  234. * @param {number} p1 param for the transformation
  235. * @param {number} [p2] param for the transformation
  236. */
  237. transform: function (transformation, cx, cy, p1, p2) {
  238. if (this.chart.inverted) {
  239. var temp = cx;
  240. cx = cy;
  241. cy = temp;
  242. }
  243. this.points.forEach(function (point, i) {
  244. this.transformPoint(transformation, cx, cy, p1, p2, i);
  245. }, this);
  246. },
  247. /**
  248. * Transform a point with a specific transformation
  249. * If a transformed point is a real point it is replaced with
  250. * the mock point.
  251. *
  252. * @param {string} transformation a transformation name
  253. * @param {number|null} cx origin x transformation
  254. * @param {number|null} cy origin y transformation
  255. * @param {number} p1 param for the transformation
  256. * @param {number|undefined} p2 param for the transformation
  257. * @param {number} i index of the point
  258. */
  259. transformPoint: function (transformation, cx, cy, p1, p2, i) {
  260. var point = this.points[i];
  261. if (!point.mock) {
  262. point = this.points[i] = MockPoint.fromPoint(point);
  263. }
  264. point[transformation](cx, cy, p1, p2);
  265. },
  266. /**
  267. * Translate a controllable.
  268. *
  269. * @param {number} dx translation for x coordinate
  270. * @param {number} dy translation for y coordinate
  271. **/
  272. translate: function (dx, dy) {
  273. this.transform('translate', null, null, dx, dy);
  274. },
  275. /**
  276. * Translate a specific point within a controllable.
  277. *
  278. * @param {number} dx translation for x coordinate
  279. * @param {number} dy translation for y coordinate
  280. * @param {number} i index of the point
  281. **/
  282. translatePoint: function (dx, dy, i) {
  283. this.transformPoint('translate', null, null, dx, dy, i);
  284. },
  285. /**
  286. * Translate shape within controllable item.
  287. * Replaces `controllable.translate` method.
  288. *
  289. * @param {number} dx translation for x coordinate
  290. * @param {number} dy translation for y coordinate
  291. */
  292. translateShape: function (dx, dy) {
  293. var chart = this.annotation.chart,
  294. // Annotation.options
  295. shapeOptions = this.annotation.userOptions,
  296. // Chart.options.annotations
  297. annotationIndex = chart.annotations.indexOf(this.annotation), chartOptions = chart.options.annotations[annotationIndex];
  298. this.translatePoint(dx, dy, 0);
  299. // Options stored in:
  300. // - chart (for exporting)
  301. // - current config (for redraws)
  302. chartOptions[this.collection][this.index].point = this.options.point;
  303. shapeOptions[this.collection][this.index].point = this.options.point;
  304. },
  305. /**
  306. * Rotate a controllable.
  307. *
  308. * @param {number} cx origin x rotation
  309. * @param {number} cy origin y rotation
  310. * @param {number} radians
  311. **/
  312. rotate: function (cx, cy, radians) {
  313. this.transform('rotate', cx, cy, radians);
  314. },
  315. /**
  316. * Scale a controllable.
  317. *
  318. * @param {number} cx origin x rotation
  319. * @param {number} cy origin y rotation
  320. * @param {number} sx scale factor x
  321. * @param {number} sy scale factor y
  322. */
  323. scale: function (cx, cy, sx, sy) {
  324. this.transform('scale', cx, cy, sx, sy);
  325. },
  326. /**
  327. * Set control points' visibility.
  328. *
  329. * @param {boolean} visible
  330. */
  331. setControlPointsVisibility: function (visible) {
  332. this.controlPoints.forEach(function (controlPoint) {
  333. controlPoint.setVisibility(visible);
  334. });
  335. },
  336. /**
  337. * Destroy a controllable.
  338. */
  339. destroy: function () {
  340. if (this.graphic) {
  341. this.graphic = this.graphic.destroy();
  342. }
  343. if (this.tracker) {
  344. this.tracker = this.tracker.destroy();
  345. }
  346. this.controlPoints.forEach(function (controlPoint) {
  347. controlPoint.destroy();
  348. });
  349. this.chart = null;
  350. this.points = null;
  351. this.controlPoints = null;
  352. this.options = null;
  353. if (this.annotation) {
  354. this.annotation = null;
  355. }
  356. },
  357. /**
  358. * Update a controllable.
  359. *
  360. * @param {Object} newOptions
  361. */
  362. update: function (newOptions) {
  363. var annotation = this.annotation, options = merge(true, this.options, newOptions), parentGroup = this.graphic.parentGroup;
  364. this.destroy();
  365. this.constructor(annotation, options);
  366. this.render(parentGroup);
  367. this.redraw();
  368. }
  369. };
  370. export default controllableMixin;