ChartUtilities.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. /* *
  2. *
  3. * (c) 2009-2021 Øystein Moseng
  4. *
  5. * Utils for dealing with charts.
  6. *
  7. * License: www.highcharts.com/license
  8. *
  9. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10. *
  11. * */
  12. 'use strict';
  13. import HTMLUtilities from './HTMLUtilities.js';
  14. var stripHTMLTags = HTMLUtilities.stripHTMLTagsFromString;
  15. import U from '../../Core/Utilities.js';
  16. var defined = U.defined, find = U.find, fireEvent = U.fireEvent;
  17. /* eslint-disable valid-jsdoc */
  18. /**
  19. * @return {string}
  20. */
  21. function getChartTitle(chart) {
  22. return stripHTMLTags(chart.options.title.text ||
  23. chart.langFormat('accessibility.defaultChartTitle', { chart: chart }));
  24. }
  25. /**
  26. * Return string with the axis name/title.
  27. * @param {Highcharts.Axis} axis
  28. * @return {string}
  29. */
  30. function getAxisDescription(axis) {
  31. return axis && (axis.userOptions && axis.userOptions.accessibility &&
  32. axis.userOptions.accessibility.description ||
  33. axis.axisTitle && axis.axisTitle.textStr ||
  34. axis.options.id ||
  35. axis.categories && 'categories' ||
  36. axis.dateTime && 'Time' ||
  37. 'values');
  38. }
  39. /**
  40. * Return string with text description of the axis range.
  41. * @param {Highcharts.Axis} axis The axis to get range desc of.
  42. * @return {string} A string with the range description for the axis.
  43. */
  44. function getAxisRangeDescription(axis) {
  45. var axisOptions = axis.options || {};
  46. // Handle overridden range description
  47. if (axisOptions.accessibility &&
  48. typeof axisOptions.accessibility.rangeDescription !== 'undefined') {
  49. return axisOptions.accessibility.rangeDescription;
  50. }
  51. // Handle category axes
  52. if (axis.categories) {
  53. return getCategoryAxisRangeDesc(axis);
  54. }
  55. // Use time range, not from-to?
  56. if (axis.dateTime && (axis.min === 0 || axis.dataMin === 0)) {
  57. return getAxisTimeLengthDesc(axis);
  58. }
  59. // Just use from and to.
  60. // We have the range and the unit to use, find the desc format
  61. return getAxisFromToDescription(axis);
  62. }
  63. /**
  64. * Describe the range of a category axis.
  65. * @param {Highcharts.Axis} axis
  66. * @return {string}
  67. */
  68. function getCategoryAxisRangeDesc(axis) {
  69. var chart = axis.chart;
  70. if (axis.dataMax && axis.dataMin) {
  71. return chart.langFormat('accessibility.axis.rangeCategories', {
  72. chart: chart,
  73. axis: axis,
  74. numCategories: axis.dataMax - axis.dataMin + 1
  75. });
  76. }
  77. return '';
  78. }
  79. /**
  80. * Describe the length of the time window shown on an axis.
  81. * @param {Highcharts.Axis} axis
  82. * @return {string}
  83. */
  84. function getAxisTimeLengthDesc(axis) {
  85. var chart = axis.chart;
  86. var range = {};
  87. var rangeUnit = 'Seconds';
  88. range.Seconds = ((axis.max || 0) - (axis.min || 0)) / 1000;
  89. range.Minutes = range.Seconds / 60;
  90. range.Hours = range.Minutes / 60;
  91. range.Days = range.Hours / 24;
  92. ['Minutes', 'Hours', 'Days'].forEach(function (unit) {
  93. if (range[unit] > 2) {
  94. rangeUnit = unit;
  95. }
  96. });
  97. var rangeValue = range[rangeUnit].toFixed(rangeUnit !== 'Seconds' &&
  98. rangeUnit !== 'Minutes' ? 1 : 0 // Use decimals for days/hours
  99. );
  100. // We have the range and the unit to use, find the desc format
  101. return chart.langFormat('accessibility.axis.timeRange' + rangeUnit, {
  102. chart: chart,
  103. axis: axis,
  104. range: rangeValue.replace('.0', '')
  105. });
  106. }
  107. /**
  108. * Describe an axis from-to range.
  109. * @param {Highcharts.Axis} axis
  110. * @return {string}
  111. */
  112. function getAxisFromToDescription(axis) {
  113. var chart = axis.chart;
  114. var dateRangeFormat = (chart.options &&
  115. chart.options.accessibility &&
  116. chart.options.accessibility.screenReaderSection.axisRangeDateFormat ||
  117. '');
  118. var format = function (axisKey) {
  119. return axis.dateTime ? chart.time.dateFormat(dateRangeFormat, axis[axisKey]) : axis[axisKey];
  120. };
  121. return chart.langFormat('accessibility.axis.rangeFromTo', {
  122. chart: chart,
  123. axis: axis,
  124. rangeFrom: format('min'),
  125. rangeTo: format('max')
  126. });
  127. }
  128. /**
  129. * Get the DOM element for the first point in the series.
  130. * @private
  131. * @param {Highcharts.Series} series
  132. * The series to get element for.
  133. * @return {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement|undefined}
  134. * The DOM element for the point.
  135. */
  136. function getSeriesFirstPointElement(series) {
  137. if (series.points && series.points.length) {
  138. var firstPointWithGraphic = find(series.points, function (p) { return !!p.graphic; });
  139. return (firstPointWithGraphic &&
  140. firstPointWithGraphic.graphic &&
  141. firstPointWithGraphic.graphic.element);
  142. }
  143. }
  144. /**
  145. * Get the DOM element for the series that we put accessibility info on.
  146. * @private
  147. * @param {Highcharts.Series} series
  148. * The series to get element for.
  149. * @return {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement|undefined}
  150. * The DOM element for the series
  151. */
  152. function getSeriesA11yElement(series) {
  153. var firstPointEl = getSeriesFirstPointElement(series);
  154. return (firstPointEl &&
  155. firstPointEl.parentNode || series.graph &&
  156. series.graph.element || series.group &&
  157. series.group.element); // Could be tracker series depending on series type
  158. }
  159. /**
  160. * Remove aria-hidden from element. Also unhides parents of the element, and
  161. * hides siblings that are not explicitly unhidden.
  162. * @private
  163. * @param {Highcharts.Chart} chart
  164. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} element
  165. */
  166. function unhideChartElementFromAT(chart, element) {
  167. element.setAttribute('aria-hidden', false);
  168. if (element === chart.renderTo || !element.parentNode) {
  169. return;
  170. }
  171. // Hide siblings unless their hidden state is already explicitly set
  172. Array.prototype.forEach.call(element.parentNode.childNodes, function (node) {
  173. if (!node.hasAttribute('aria-hidden')) {
  174. node.setAttribute('aria-hidden', true);
  175. }
  176. });
  177. // Repeat for parent
  178. unhideChartElementFromAT(chart, element.parentNode);
  179. }
  180. /**
  181. * Hide series from screen readers.
  182. * @private
  183. * @param {Highcharts.Series} series
  184. * The series to hide
  185. * @return {void}
  186. */
  187. function hideSeriesFromAT(series) {
  188. var seriesEl = getSeriesA11yElement(series);
  189. if (seriesEl) {
  190. seriesEl.setAttribute('aria-hidden', true);
  191. }
  192. }
  193. /**
  194. * Get series objects by series name.
  195. * @private
  196. * @param {Highcharts.Chart} chart
  197. * @param {string} name
  198. * @return {Array<Highcharts.Series>}
  199. */
  200. function getSeriesFromName(chart, name) {
  201. if (!name) {
  202. return chart.series;
  203. }
  204. return (chart.series || []).filter(function (s) {
  205. return s.name === name;
  206. });
  207. }
  208. /**
  209. * Get point in a series from x/y values.
  210. * @private
  211. * @param {Array<Highcharts.Series>} series
  212. * @param {number} x
  213. * @param {number} y
  214. * @return {Highcharts.Point|undefined}
  215. */
  216. function getPointFromXY(series, x, y) {
  217. var i = series.length, res;
  218. while (i--) {
  219. res = find(series[i].points || [], function (p) {
  220. return p.x === x && p.y === y;
  221. });
  222. if (res) {
  223. return res;
  224. }
  225. }
  226. }
  227. /**
  228. * Get relative position of point on an x/y axis from 0 to 1.
  229. * @private
  230. * @param {Highcharts.Axis} axis
  231. * @param {Highcharts.Point} point
  232. * @return {number}
  233. */
  234. function getRelativePointAxisPosition(axis, point) {
  235. if (!defined(axis.dataMin) || !defined(axis.dataMax)) {
  236. return 0;
  237. }
  238. var axisStart = axis.toPixels(axis.dataMin);
  239. var axisEnd = axis.toPixels(axis.dataMax);
  240. // We have to use pixel position because of axis breaks, log axis etc.
  241. var positionProp = axis.coll === 'xAxis' ? 'x' : 'y';
  242. var pointPos = axis.toPixels(point[positionProp] || 0);
  243. return (pointPos - axisStart) / (axisEnd - axisStart);
  244. }
  245. /**
  246. * Get relative position of point on an x/y axis from 0 to 1.
  247. * @private
  248. * @param {Highcharts.Point} point
  249. */
  250. function scrollToPoint(point) {
  251. var xAxis = point.series.xAxis;
  252. var yAxis = point.series.yAxis;
  253. var axis = (xAxis && xAxis.scrollbar ? xAxis : yAxis);
  254. var scrollbar = (axis && axis.scrollbar);
  255. if (scrollbar && defined(scrollbar.to) && defined(scrollbar.from)) {
  256. var range = scrollbar.to - scrollbar.from;
  257. var pos = getRelativePointAxisPosition(axis, point);
  258. scrollbar.updatePosition(pos - range / 2, pos + range / 2);
  259. fireEvent(scrollbar, 'changed', {
  260. from: scrollbar.from,
  261. to: scrollbar.to,
  262. trigger: 'scrollbar',
  263. DOMEvent: null
  264. });
  265. }
  266. }
  267. var ChartUtilities = {
  268. getChartTitle: getChartTitle,
  269. getAxisDescription: getAxisDescription,
  270. getAxisRangeDescription: getAxisRangeDescription,
  271. getPointFromXY: getPointFromXY,
  272. getSeriesFirstPointElement: getSeriesFirstPointElement,
  273. getSeriesFromName: getSeriesFromName,
  274. getSeriesA11yElement: getSeriesA11yElement,
  275. unhideChartElementFromAT: unhideChartElementFromAT,
  276. hideSeriesFromAT: hideSeriesFromAT,
  277. scrollToPoint: scrollToPoint
  278. };
  279. export default ChartUtilities;