annotations.src.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. /**
  2. * @license Highcharts JS v5.0.6 (2016-12-07)
  3. *
  4. * (c) 2009-2016 Torstein Honsi
  5. *
  6. * License: www.highcharts.com/license
  7. */
  8. (function(factory) {
  9. if (typeof module === 'object' && module.exports) {
  10. module.exports = factory;
  11. } else {
  12. factory(Highcharts);
  13. }
  14. }(function(Highcharts) {
  15. (function(H) {
  16. /**
  17. * (c) 2009-2016 Torstein Honsi
  18. *
  19. * License: www.highcharts.com/license
  20. */
  21. 'use strict';
  22. var defined = H.defined,
  23. isNumber = H.isNumber,
  24. inArray = H.inArray,
  25. isArray = H.isArray,
  26. merge = H.merge,
  27. Chart = H.Chart,
  28. extend = H.extend,
  29. each = H.each;
  30. var ALIGN_FACTOR,
  31. ALLOWED_SHAPES;
  32. ALLOWED_SHAPES = ['path', 'rect', 'circle'];
  33. ALIGN_FACTOR = {
  34. top: 0,
  35. left: 0,
  36. center: 0.5,
  37. middle: 0.5,
  38. bottom: 1,
  39. right: 1
  40. };
  41. function defaultOptions(shapeType) {
  42. var shapeOptions,
  43. options;
  44. options = {
  45. xAxis: 0,
  46. yAxis: 0,
  47. title: {
  48. style: {},
  49. text: '',
  50. x: 0,
  51. y: 0
  52. },
  53. shape: {
  54. params: {
  55. stroke: '#000000',
  56. fill: 'transparent',
  57. strokeWidth: 2
  58. }
  59. }
  60. };
  61. shapeOptions = {
  62. circle: {
  63. params: {
  64. x: 0,
  65. y: 0
  66. }
  67. }
  68. };
  69. if (shapeOptions[shapeType]) {
  70. options.shape = merge(options.shape, shapeOptions[shapeType]);
  71. }
  72. return options;
  73. }
  74. function translatePath(d, xAxis, yAxis, xOffset, yOffset) {
  75. var len = d.length,
  76. i = 0;
  77. while (i < len) {
  78. if (isNumber(d[i]) && isNumber(d[i + 1])) {
  79. d[i] = xAxis.toPixels(d[i]) - xOffset;
  80. d[i + 1] = yAxis.toPixels(d[i + 1]) - yOffset;
  81. i += 2;
  82. } else {
  83. i += 1;
  84. }
  85. }
  86. return d;
  87. }
  88. // Define annotation prototype
  89. var Annotation = function() {
  90. this.init.apply(this, arguments);
  91. };
  92. Annotation.prototype = {
  93. /*
  94. * Initialize the annotation
  95. */
  96. init: function(chart, options) {
  97. var shapeType = options.shape && options.shape.type;
  98. this.chart = chart;
  99. this.options = merge({}, defaultOptions(shapeType), options);
  100. },
  101. /*
  102. * Render the annotation
  103. */
  104. render: function(redraw) {
  105. var annotation = this,
  106. chart = this.chart,
  107. renderer = annotation.chart.renderer,
  108. group = annotation.group,
  109. title = annotation.title,
  110. shape = annotation.shape,
  111. options = annotation.options,
  112. titleOptions = options.title,
  113. shapeOptions = options.shape;
  114. if (!group) {
  115. group = annotation.group = renderer.g();
  116. }
  117. if (!shape && shapeOptions && inArray(shapeOptions.type, ALLOWED_SHAPES) !== -1) {
  118. shape = annotation.shape = renderer[options.shape.type](shapeOptions.params);
  119. shape.add(group);
  120. }
  121. if (!title && titleOptions) {
  122. title = annotation.title = renderer.label(titleOptions);
  123. title.add(group);
  124. }
  125. group.add(chart.annotations.group);
  126. // link annotations to point or series
  127. annotation.linkObjects();
  128. if (redraw !== false) {
  129. annotation.redraw();
  130. }
  131. },
  132. /*
  133. * Redraw the annotation title or shape after options update
  134. */
  135. redraw: function() {
  136. var options = this.options,
  137. chart = this.chart,
  138. group = this.group,
  139. title = this.title,
  140. shape = this.shape,
  141. linkedTo = this.linkedObject,
  142. xAxis = chart.xAxis[options.xAxis],
  143. yAxis = chart.yAxis[options.yAxis],
  144. width = options.width,
  145. height = options.height,
  146. anchorY = ALIGN_FACTOR[options.anchorY],
  147. anchorX = ALIGN_FACTOR[options.anchorX],
  148. shapeParams,
  149. linkType,
  150. series,
  151. param,
  152. bbox,
  153. x,
  154. y;
  155. if (linkedTo) {
  156. linkType = (linkedTo instanceof H.Point) ? 'point' :
  157. (linkedTo instanceof H.Series) ? 'series' : null;
  158. if (linkType === 'point') {
  159. options.xValue = linkedTo.x;
  160. options.yValue = linkedTo.y;
  161. series = linkedTo.series;
  162. } else if (linkType === 'series') {
  163. series = linkedTo;
  164. }
  165. if (group.visibility !== series.group.visibility) {
  166. group.attr({
  167. visibility: series.group.visibility
  168. });
  169. }
  170. }
  171. // Based on given options find annotation pixel position
  172. x = (defined(options.xValue) ? xAxis.toPixels(options.xValue + xAxis.minPointOffset) - xAxis.minPixelPadding : options.x);
  173. y = defined(options.yValue) ? yAxis.toPixels(options.yValue) : options.y;
  174. if (!isNumber(x) || !isNumber(y)) {
  175. return;
  176. }
  177. if (title) {
  178. title.attr(options.title);
  179. title.css(options.title.style);
  180. }
  181. if (shape) {
  182. shapeParams = extend({}, options.shape.params);
  183. if (options.units === 'values') {
  184. for (param in shapeParams) {
  185. if (inArray(param, ['width', 'x']) > -1) {
  186. shapeParams[param] = xAxis.translate(shapeParams[param]);
  187. } else if (inArray(param, ['height', 'y']) > -1) {
  188. shapeParams[param] = yAxis.translate(shapeParams[param]);
  189. }
  190. }
  191. if (shapeParams.width) {
  192. shapeParams.width -= xAxis.toPixels(0) - xAxis.left;
  193. }
  194. if (shapeParams.x) {
  195. shapeParams.x += xAxis.minPixelPadding;
  196. }
  197. if (options.shape.type === 'path') {
  198. translatePath(shapeParams.d, xAxis, yAxis, x, y);
  199. }
  200. }
  201. // move the center of the circle to shape x/y
  202. if (options.shape.type === 'circle') {
  203. shapeParams.x += shapeParams.r;
  204. shapeParams.y += shapeParams.r;
  205. }
  206. shape.attr(shapeParams);
  207. }
  208. group.bBox = null;
  209. // If annotation width or height is not defined in options use bounding box size
  210. if (!isNumber(width)) {
  211. bbox = group.getBBox();
  212. width = bbox.width;
  213. }
  214. if (!isNumber(height)) {
  215. // get bbox only if it wasn't set before
  216. if (!bbox) {
  217. bbox = group.getBBox();
  218. }
  219. height = bbox.height;
  220. }
  221. // Calculate anchor point
  222. if (!isNumber(anchorX)) {
  223. anchorX = ALIGN_FACTOR.center;
  224. }
  225. if (!isNumber(anchorY)) {
  226. anchorY = ALIGN_FACTOR.center;
  227. }
  228. // Translate group according to its dimension and anchor point
  229. x = x - width * anchorX;
  230. y = y - height * anchorY;
  231. if (defined(group.translateX) && defined(group.translateY)) {
  232. group.animate({
  233. translateX: x,
  234. translateY: y
  235. });
  236. } else {
  237. group.translate(x, y);
  238. }
  239. },
  240. /*
  241. * Destroy the annotation
  242. */
  243. destroy: function() {
  244. var annotation = this,
  245. chart = this.chart,
  246. allItems = chart.annotations.allItems,
  247. index = allItems.indexOf(annotation);
  248. if (index > -1) {
  249. allItems.splice(index, 1);
  250. }
  251. each(['title', 'shape', 'group'], function(element) {
  252. if (annotation[element]) {
  253. annotation[element].destroy();
  254. annotation[element] = null;
  255. }
  256. });
  257. annotation.group = annotation.title = annotation.shape = annotation.chart = annotation.options = null;
  258. },
  259. /*
  260. * Update the annotation with a given options
  261. */
  262. update: function(options, redraw) {
  263. extend(this.options, options);
  264. // update link to point or series
  265. this.linkObjects();
  266. this.render(redraw);
  267. },
  268. linkObjects: function() {
  269. var annotation = this,
  270. chart = annotation.chart,
  271. linkedTo = annotation.linkedObject,
  272. linkedId = linkedTo && (linkedTo.id || linkedTo.options.id),
  273. options = annotation.options,
  274. id = options.linkedTo;
  275. if (!defined(id)) {
  276. annotation.linkedObject = null;
  277. } else if (!defined(linkedTo) || id !== linkedId) {
  278. annotation.linkedObject = chart.get(id);
  279. }
  280. }
  281. };
  282. // Add annotations methods to chart prototype
  283. extend(Chart.prototype, {
  284. annotations: {
  285. /*
  286. * Unified method for adding annotations to the chart
  287. */
  288. add: function(options, redraw) {
  289. var annotations = this.allItems,
  290. chart = this.chart,
  291. item,
  292. len;
  293. if (!isArray(options)) {
  294. options = [options];
  295. }
  296. len = options.length;
  297. while (len--) {
  298. item = new Annotation(chart, options[len]);
  299. annotations.push(item);
  300. item.render(redraw);
  301. }
  302. },
  303. /**
  304. * Redraw all annotations, method used in chart events
  305. */
  306. redraw: function() {
  307. each(this.allItems, function(annotation) {
  308. annotation.redraw();
  309. });
  310. }
  311. }
  312. });
  313. // Initialize on chart load
  314. Chart.prototype.callbacks.push(function(chart) {
  315. var options = chart.options.annotations,
  316. group;
  317. group = chart.renderer.g('annotations');
  318. group.attr({
  319. zIndex: 7
  320. });
  321. group.add();
  322. // initialize empty array for annotations
  323. chart.annotations.allItems = [];
  324. // link chart object to annotations
  325. chart.annotations.chart = chart;
  326. // link annotations group element to the chart
  327. chart.annotations.group = group;
  328. if (isArray(options) && options.length > 0) {
  329. chart.annotations.add(chart.options.annotations);
  330. }
  331. // update annotations after chart redraw
  332. H.addEvent(chart, 'redraw', function() {
  333. chart.annotations.redraw();
  334. });
  335. });
  336. }(Highcharts));
  337. }));