123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897 |
- /**
- * Accessibility module - Screen Reader support
- *
- * (c) 2010-2017 Highsoft AS
- * Author: Oystein Moseng
- *
- * License: www.highcharts.com/license
- */
- 'use strict';
- import H from '../parts/Globals.js';
- import '../parts/Utilities.js';
- import '../parts/Chart.js';
- import '../parts/Series.js';
- import '../parts/Point.js';
- var win = H.win,
- doc = win.document,
- erase = H.erase,
- addEvent = H.addEvent,
- merge = H.merge,
- // CSS style to hide element from visual users while still exposing it to
- // screen readers
- hiddenStyle = {
- position: 'absolute',
- left: '-9999px',
- top: 'auto',
- width: '1px',
- height: '1px',
- overflow: 'hidden'
- };
- // If a point has one of the special keys defined, we expose all keys to the
- // screen reader.
- H.Series.prototype.commonKeys = ['name', 'id', 'category', 'x', 'value', 'y'];
- H.Series.prototype.specialKeys = [
- 'z', 'open', 'high', 'q3', 'median', 'q1', 'low', 'close'
- ];
- if (H.seriesTypes.pie) {
- // A pie is always simple. Don't quote me on that.
- H.seriesTypes.pie.prototype.specialKeys = [];
- }
- /**
- * HTML encode some characters vulnerable for XSS.
- *
- * @private
- * @function htmlencode
- *
- * @param {string} html
- * The input string.
- *
- * @return {string}
- * The excaped string.
- */
- function htmlencode(html) {
- return html
- .replace(/&/g, '&')
- .replace(/</g, '<')
- .replace(/>/g, '>')
- .replace(/"/g, '"')
- .replace(/'/g, ''')
- .replace(/\//g, '/');
- }
- /**
- * Strip HTML tags away from a string. Used for aria-label attributes, painting
- * on a canvas will fail if the text contains tags.
- *
- * @private
- * @function stripTags
- *
- * @param {string} s
- * The input string.
- *
- * @return {string}
- * The filtered string.
- */
- function stripTags(s) {
- return typeof s === 'string' ? s.replace(/<\/?[^>]+(>|$)/g, '') : s;
- }
- // Accessibility options
- H.setOptions({
- /**
- * Options for configuring accessibility for the chart. Requires the
- * [accessibility module](https://code.highcharts.com/modules/accessibility.js)
- * to be loaded. For a description of the module and information
- * on its features, see
- * [Highcharts Accessibility](http://www.highcharts.com/docs/chart-concepts/accessibility).
- *
- * @since 5.0.0
- * @optionparent accessibility
- */
- accessibility: {
- /**
- * Whether or not to add series descriptions to charts with a single
- * series.
- *
- * @type {boolean}
- * @default false
- * @since 5.0.0
- * @apioption accessibility.describeSingleSeries
- */
- /**
- * Function to run upon clicking the "View as Data Table" link in the
- * screen reader region.
- *
- * By default Highcharts will insert and set focus to a data table
- * representation of the chart.
- *
- * @type {Function}
- * @since 5.0.0
- * @apioption accessibility.onTableAnchorClick
- */
- /**
- * Date format to use for points on datetime axes when describing them
- * to screen reader users.
- *
- * Defaults to the same format as in tooltip.
- *
- * For an overview of the replacement codes, see
- * [dateFormat](/class-reference/Highcharts#dateFormat).
- *
- * @see [pointDateFormatter](#accessibility.pointDateFormatter)
- *
- * @type {string}
- * @since 5.0.0
- * @apioption accessibility.pointDateFormat
- */
- /**
- * Formatter function to determine the date/time format used with
- * points on datetime axes when describing them to screen reader users.
- * Receives one argument, `point`, referring to the point to describe.
- * Should return a date format string compatible with
- * [dateFormat](/class-reference/Highcharts#dateFormat).
- *
- * @see [pointDateFormat](#accessibility.pointDateFormat)
- *
- * @type {Function}
- * @since 5.0.0
- * @apioption accessibility.pointDateFormatter
- */
- /**
- * Formatter function to use instead of the default for point
- * descriptions.
- * Receives one argument, `point`, referring to the point to describe.
- * Should return a String with the description of the point for a screen
- * reader user.
- *
- * @see [point.description](#series.line.data.description)
- *
- * @type {Function}
- * @since 5.0.0
- * @apioption accessibility.pointDescriptionFormatter
- */
- /**
- * Formatter function to use instead of the default for series
- * descriptions. Receives one argument, `series`, referring to the
- * series to describe. Should return a String with the description of
- * the series for a screen reader user.
- *
- * @see [series.description](#plotOptions.series.description)
- *
- * @type {Function}
- * @since 5.0.0
- * @apioption accessibility.seriesDescriptionFormatter
- */
- /**
- * Enable accessibility features for the chart.
- *
- * @since 5.0.0
- */
- enabled: true,
- /**
- * When a series contains more points than this, we no longer expose
- * information about individual points to screen readers.
- *
- * Set to `false` to disable.
- *
- * @type {false|number}
- * @since 5.0.0
- */
- pointDescriptionThreshold: false, // set to false to disable
- /**
- * A formatter function to create the HTML contents of the hidden screen
- * reader information region. Receives one argument, `chart`, referring
- * to the chart object. Should return a String with the HTML content
- * of the region.
- *
- * The link to view the chart as a data table will be added
- * automatically after the custom HTML content.
- *
- * @type {Function}
- * @default undefined
- * @since 5.0.0
- */
- screenReaderSectionFormatter: function (chart) {
- var options = chart.options,
- chartTypes = chart.types || [],
- formatContext = {
- chart: chart,
- numSeries: chart.series && chart.series.length
- },
- // Build axis info - but not for pies and maps. Consider not
- // adding for certain other types as well (funnel, pyramid?)
- axesDesc = (
- chartTypes.length === 1 && chartTypes[0] === 'pie' ||
- chartTypes[0] === 'map'
- ) && {} || chart.getAxesDescription();
- return '<div>' + chart.langFormat(
- 'accessibility.navigationHint', formatContext
- ) + '</div><h3>' +
- (
- options.title.text ?
- htmlencode(options.title.text) :
- chart.langFormat(
- 'accessibility.defaultChartTitle', formatContext
- )
- ) +
- (
- options.subtitle && options.subtitle.text ?
- '. ' + htmlencode(options.subtitle.text) :
- ''
- ) +
- '</h3><h4>' + chart.langFormat(
- 'accessibility.longDescriptionHeading', formatContext
- ) + '</h4><div>' +
- (
- options.chart.description || chart.langFormat(
- 'accessibility.noDescription', formatContext
- )
- ) +
- '</div><h4>' + chart.langFormat(
- 'accessibility.structureHeading', formatContext
- ) + '</h4><div>' +
- (
- options.chart.typeDescription ||
- chart.getTypeDescription()
- ) + '</div>' +
- (axesDesc.xAxis ? (
- '<div>' + axesDesc.xAxis + '</div>'
- ) : '') +
- (axesDesc.yAxis ? (
- '<div>' + axesDesc.yAxis + '</div>'
- ) : '');
- }
- }
- });
- /**
- * A text description of the chart.
- *
- * If the Accessibility module is loaded, this is included by default
- * as a long description of the chart and its contents in the hidden
- * screen reader information region.
- *
- * @see [typeDescription](#chart.typeDescription)
- *
- * @type {string}
- * @since 5.0.0
- * @apioption chart.description
- */
- /**
- * A text description of the chart type.
- *
- * If the Accessibility module is loaded, this will be included in the
- * description of the chart in the screen reader information region.
- *
- *
- * Highcharts will by default attempt to guess the chart type, but for
- * more complex charts it is recommended to specify this property for
- * clarity.
- *
- * @type {string}
- * @since 5.0.0
- * @apioption chart.typeDescription
- */
- /**
- * Utility function. Reverses child nodes of a DOM element.
- *
- * @private
- * @function reverseChildNodes
- *
- * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} node
- */
- function reverseChildNodes(node) {
- var i = node.childNodes.length;
- while (i--) {
- node.appendChild(node.childNodes[i]);
- }
- }
- // Whenever drawing series, put info on DOM elements
- H.addEvent(H.Series, 'afterRender', function () {
- if (this.chart.options.accessibility.enabled) {
- this.setA11yDescription();
- }
- });
- /**
- * Put accessible info on series and points of a series.
- *
- * @private
- * @function Highcharts.Series#setA11yDescription
- */
- H.Series.prototype.setA11yDescription = function () {
- var a11yOptions = this.chart.options.accessibility,
- firstPointEl = (
- this.points &&
- this.points.length &&
- this.points[0].graphic &&
- this.points[0].graphic.element
- ),
- seriesEl = (
- firstPointEl &&
- firstPointEl.parentNode || this.graph &&
- this.graph.element || this.group &&
- this.group.element
- ); // Could be tracker series depending on series type
- if (seriesEl) {
- // For some series types the order of elements do not match the order of
- // points in series. In that case we have to reverse them in order for
- // AT to read them out in an understandable order
- if (seriesEl.lastChild === firstPointEl) {
- reverseChildNodes(seriesEl);
- }
- // Make individual point elements accessible if possible. Note: If
- // markers are disabled there might not be any elements there to make
- // accessible.
- if (
- this.points && (
- this.points.length < a11yOptions.pointDescriptionThreshold ||
- a11yOptions.pointDescriptionThreshold === false
- )
- ) {
- this.points.forEach(function (point) {
- if (point.graphic) {
- point.graphic.element.setAttribute('role', 'img');
- point.graphic.element.setAttribute('tabindex', '-1');
- point.graphic.element.setAttribute('aria-label', stripTags(
- point.series.options.pointDescriptionFormatter &&
- point.series.options.pointDescriptionFormatter(point) ||
- a11yOptions.pointDescriptionFormatter &&
- a11yOptions.pointDescriptionFormatter(point) ||
- point.buildPointInfoString()
- ));
- }
- });
- }
- // Make series element accessible
- if (this.chart.series.length > 1 || a11yOptions.describeSingleSeries) {
- seriesEl.setAttribute(
- 'role',
- this.options.exposeElementToA11y ? 'img' : 'region'
- );
- seriesEl.setAttribute('tabindex', '-1');
- seriesEl.setAttribute(
- 'aria-label',
- stripTags(
- a11yOptions.seriesDescriptionFormatter &&
- a11yOptions.seriesDescriptionFormatter(this) ||
- this.buildSeriesInfoString()
- )
- );
- }
- }
- };
- /**
- * Return string with information about series.
- *
- * @private
- * @function Highcharts.Series#buildSeriesInfoString
- *
- * @return {string}
- */
- H.Series.prototype.buildSeriesInfoString = function () {
- var chart = this.chart,
- desc = this.description || this.options.description,
- description = desc && chart.langFormat(
- 'accessibility.series.description', {
- description: desc,
- series: this
- }
- ),
- xAxisInfo = chart.langFormat(
- 'accessibility.series.xAxisDescription',
- {
- name: this.xAxis && this.xAxis.getDescription(),
- series: this
- }
- ),
- yAxisInfo = chart.langFormat(
- 'accessibility.series.yAxisDescription',
- {
- name: this.yAxis && this.yAxis.getDescription(),
- series: this
- }
- ),
- summaryContext = {
- name: this.name || '',
- ix: this.index + 1,
- numSeries: chart.series.length,
- numPoints: this.points.length,
- series: this
- },
- combination = chart.types.length === 1 ? '' : 'Combination',
- summary = chart.langFormat(
- 'accessibility.series.summary.' + this.type + combination,
- summaryContext
- ) || chart.langFormat(
- 'accessibility.series.summary.default' + combination,
- summaryContext
- );
- return summary + (description ? ' ' + description : '') + (
- chart.yAxis.length > 1 && this.yAxis ?
- ' ' + yAxisInfo : ''
- ) + (
- chart.xAxis.length > 1 && this.xAxis ?
- ' ' + xAxisInfo : ''
- );
- };
- /**
- * Return string with information about point.
- *
- * @private
- * @function Highcharts.Point#buildPointInfoString
- *
- * @return {string}
- */
- H.Point.prototype.buildPointInfoString = function () {
- var point = this,
- series = point.series,
- a11yOptions = series.chart.options.accessibility,
- infoString = '',
- dateTimePoint = series.xAxis && series.xAxis.isDatetimeAxis,
- timeDesc =
- dateTimePoint &&
- series.chart.time.dateFormat(
- a11yOptions.pointDateFormatter &&
- a11yOptions.pointDateFormatter(point) ||
- a11yOptions.pointDateFormat ||
- H.Tooltip.prototype.getXDateFormat.call(
- {
- getDateFormat: H.Tooltip.prototype.getDateFormat,
- chart: series.chart
- },
- point,
- series.chart.options.tooltip,
- series.xAxis
- ),
- point.x
- ),
- hasSpecialKey = H.find(series.specialKeys, function (key) {
- return point[key] !== undefined;
- });
- // If the point has one of the less common properties defined, display all
- // that are defined
- if (hasSpecialKey) {
- if (dateTimePoint) {
- infoString = timeDesc;
- }
- series.commonKeys.concat(series.specialKeys).forEach(function (key) {
- if (point[key] !== undefined && !(dateTimePoint && key === 'x')) {
- infoString += (infoString ? '. ' : '') +
- key + ', ' +
- point[key];
- }
- });
- } else {
- // Pick and choose properties for a succint label
- infoString =
- (
- this.name ||
- timeDesc ||
- this.category ||
- this.id ||
- 'x, ' + this.x
- ) + ', ' +
- (this.value !== undefined ? this.value : this.y);
- }
- return (this.index + 1) + '. ' + infoString + '.' +
- (this.description ? ' ' + this.description : '');
- };
- /**
- * Get descriptive label for axis.
- *
- * @private
- * @function Highcharts.Axis#getDescription
- *
- * @return {string}
- */
- H.Axis.prototype.getDescription = function () {
- return (
- this.userOptions && this.userOptions.description ||
- this.axisTitle && this.axisTitle.textStr ||
- this.options.id ||
- this.categories && 'categories' ||
- this.isDatetimeAxis && 'Time' ||
- 'values'
- );
- };
- // Whenever adding or removing series, keep track of types present in chart
- addEvent(H.Series, 'afterInit', function () {
- var chart = this.chart;
- if (chart.options.accessibility.enabled) {
- chart.types = chart.types || [];
- // Add type to list if does not exist
- if (chart.types.indexOf(this.type) < 0) {
- chart.types.push(this.type);
- }
- }
- });
- addEvent(H.Series, 'remove', function () {
- var chart = this.chart,
- removedSeries = this,
- hasType = false;
- // Check if any of the other series have the same type as this one.
- // Otherwise remove it from the list.
- chart.series.forEach(function (s) {
- if (
- s !== removedSeries &&
- chart.types.indexOf(removedSeries.type) < 0
- ) {
- hasType = true;
- }
- });
- if (!hasType) {
- erase(chart.types, removedSeries.type);
- }
- });
- /**
- * Return simplified description of chart type. Some types will not be familiar
- * to most screen reader users, but in those cases we try to add a description
- * of the type.
- *
- * @private
- * @function Highcharts.Chart#getTypeDescription
- *
- * @return {string}
- */
- H.Chart.prototype.getTypeDescription = function () {
- var firstType = this.types && this.types[0],
- firstSeries = this.series && this.series[0] || {},
- mapTitle = firstSeries.mapTitle,
- typeDesc = this.langFormat(
- 'accessibility.seriesTypeDescriptions.' + firstType,
- { chart: this }
- ),
- formatContext = {
- numSeries: this.series.length,
- numPoints: firstSeries.points && firstSeries.points.length,
- chart: this,
- mapTitle: mapTitle
- },
- multi = this.series && this.series.length === 1 ? 'Single' : 'Multiple';
- if (!firstType) {
- return this.langFormat(
- 'accessibility.chartTypes.emptyChart', formatContext
- );
- }
- if (firstType === 'map') {
- return mapTitle ?
- this.langFormat(
- 'accessibility.chartTypes.mapTypeDescription',
- formatContext
- ) :
- this.langFormat(
- 'accessibility.chartTypes.unknownMap',
- formatContext
- );
- }
- if (this.types.length > 1) {
- return this.langFormat(
- 'accessibility.chartTypes.combinationChart', formatContext
- );
- }
- return (
- this.langFormat(
- 'accessibility.chartTypes.' + firstType + multi,
- formatContext
- ) ||
- this.langFormat(
- 'accessibility.chartTypes.default' + multi,
- formatContext
- )
- ) +
- (typeDesc ? ' ' + typeDesc : '');
- };
- /**
- * Return object with text description of each of the chart's axes.
- *
- * @private
- * @function Highcharts.Chart#getAxesDescription
- *
- * @return {*}
- */
- H.Chart.prototype.getAxesDescription = function () {
- var numXAxes = this.xAxis.length,
- numYAxes = this.yAxis.length,
- desc = {};
- if (numXAxes) {
- desc.xAxis = this.langFormat(
- 'accessibility.axis.xAxisDescription' + (
- numXAxes > 1 ? 'Plural' : 'Singular'
- ),
- {
- chart: this,
- names: this.xAxis.map(function (axis) {
- return axis.getDescription();
- }),
- numAxes: numXAxes
- }
- );
- }
- if (numYAxes) {
- desc.yAxis = this.langFormat(
- 'accessibility.axis.yAxisDescription' + (
- numYAxes > 1 ? 'Plural' : 'Singular'
- ),
- {
- chart: this,
- names: this.yAxis.map(function (axis) {
- return axis.getDescription();
- }),
- numAxes: numYAxes
- }
- );
- }
- return desc;
- };
- /**
- * Set a11y attribs on exporting menu.
- *
- * @private
- * @function Highcharts.Chart#addAccessibleContextMenuAttribs
- */
- H.Chart.prototype.addAccessibleContextMenuAttribs = function () {
- var exportList = this.exportDivElements;
- if (exportList) {
- // Set tabindex on the menu items to allow focusing by script
- // Set role to give screen readers a chance to pick up the contents
- exportList.forEach(function (item) {
- if (item.tagName === 'DIV' &&
- !(item.children && item.children.length)) {
- item.setAttribute('role', 'menuitem');
- item.setAttribute('tabindex', -1);
- }
- });
- // Set accessibility properties on parent div
- exportList[0].parentNode.setAttribute('role', 'menu');
- exportList[0].parentNode.setAttribute(
- 'aria-label',
- this.langFormat(
- 'accessibility.exporting.chartMenuLabel', { chart: this }
- )
- );
- }
- };
- /**
- * Add screen reader region to chart. tableId is the HTML id of the table to
- * focus when clicking the table anchor in the screen reader region.
- *
- * @private
- * @function Highcharts.Chart#addScreenReaderRegion
- *
- * @param {string} id
- *
- * @param {string} tableId
- */
- H.Chart.prototype.addScreenReaderRegion = function (id, tableId) {
- var chart = this,
- hiddenSection = chart.screenReaderRegion = doc.createElement('div'),
- tableShortcut = doc.createElement('h4'),
- tableShortcutAnchor = doc.createElement('a'),
- chartHeading = doc.createElement('h4');
- hiddenSection.setAttribute('id', id);
- hiddenSection.setAttribute('role', 'region');
- hiddenSection.setAttribute(
- 'aria-label',
- chart.langFormat(
- 'accessibility.screenReaderRegionLabel', { chart: this }
- )
- );
- hiddenSection.innerHTML = chart.options.accessibility
- .screenReaderSectionFormatter(chart);
- // Add shortcut to data table if export-data is loaded
- if (chart.getCSV) {
- tableShortcutAnchor.innerHTML = chart.langFormat(
- 'accessibility.viewAsDataTable', { chart: chart }
- );
- tableShortcutAnchor.href = '#' + tableId;
- // Make this unreachable by user tabbing
- tableShortcutAnchor.setAttribute('tabindex', '-1');
- tableShortcutAnchor.onclick =
- chart.options.accessibility.onTableAnchorClick || function () {
- chart.viewData();
- doc.getElementById(tableId).focus();
- };
- tableShortcut.appendChild(tableShortcutAnchor);
- hiddenSection.appendChild(tableShortcut);
- }
- // Note: JAWS seems to refuse to read aria-label on the container, so add an
- // h4 element as title for the chart.
- chartHeading.innerHTML = chart.langFormat(
- 'accessibility.chartHeading', { chart: chart }
- );
- chart.renderTo.insertBefore(chartHeading, chart.renderTo.firstChild);
- chart.renderTo.insertBefore(hiddenSection, chart.renderTo.firstChild);
- // Hide the section and the chart heading
- merge(true, chartHeading.style, hiddenStyle);
- merge(true, hiddenSection.style, hiddenStyle);
- };
- // Make chart container accessible, and wrap table functionality.
- H.Chart.prototype.callbacks.push(function (chart) {
- var options = chart.options,
- a11yOptions = options.accessibility;
- if (!a11yOptions.enabled) {
- return;
- }
- var titleElement,
- descElement = chart.container.getElementsByTagName('desc')[0],
- textElements = chart.container.getElementsByTagName('text'),
- titleId = 'highcharts-title-' + chart.index,
- tableId = 'highcharts-data-table-' + chart.index,
- hiddenSectionId = 'highcharts-information-region-' + chart.index,
- chartTitle = options.title.text || chart.langFormat(
- 'accessibility.defaultChartTitle', { chart: chart }
- ),
- svgContainerTitle = stripTags(chart.langFormat(
- 'accessibility.svgContainerTitle', {
- chartTitle: chartTitle
- }
- ));
- // Add SVG title tag if it is set
- if (svgContainerTitle.length) {
- titleElement = doc.createElementNS(
- 'http://www.w3.org/2000/svg',
- 'title'
- );
- titleElement.textContent = svgContainerTitle;
- titleElement.id = titleId;
- descElement.parentNode.insertBefore(titleElement, descElement);
- }
- chart.renderTo.setAttribute('role', 'region');
- chart.renderTo.setAttribute(
- 'aria-label',
- chart.langFormat(
- 'accessibility.chartContainerLabel',
- {
- title: stripTags(chartTitle),
- chart: chart
- }
- )
- );
- // Set screen reader properties on export menu
- if (
- chart.exportSVGElements &&
- chart.exportSVGElements[0] &&
- chart.exportSVGElements[0].element
- ) {
- // Set event handler on button
- var button = chart.exportSVGElements[0].element,
- oldExportCallback = button.onclick;
- button.onclick = function () {
- oldExportCallback.apply(
- this,
- Array.prototype.slice.call(arguments)
- );
- chart.addAccessibleContextMenuAttribs();
- chart.highlightExportItem(0);
- };
- // Set props on button
- button.setAttribute('role', 'button');
- button.setAttribute(
- 'aria-label',
- chart.langFormat(
- 'accessibility.exporting.menuButtonLabel', { chart: chart }
- )
- );
- // Set props on group
- chart.exportingGroup.element.setAttribute('role', 'region');
- chart.exportingGroup.element.setAttribute(
- 'aria-label',
- chart.langFormat(
- 'accessibility.exporting.exportRegionLabel', { chart: chart }
- )
- );
- }
- // Set screen reader properties on input boxes for range selector. We need
- // to do this regardless of whether or not these are visible, as they are
- // by default part of the page's tabindex unless we set them to -1.
- if (chart.rangeSelector) {
- ['minInput', 'maxInput'].forEach(function (key, i) {
- if (chart.rangeSelector[key]) {
- chart.rangeSelector[key].setAttribute('tabindex', '-1');
- chart.rangeSelector[key].setAttribute('role', 'textbox');
- chart.rangeSelector[key].setAttribute(
- 'aria-label',
- chart.langFormat(
- 'accessibility.rangeSelector' +
- (i ? 'MaxInput' : 'MinInput'), { chart: chart }
- )
- );
- }
- });
- }
- // Hide text elements from screen readers
- [].forEach.call(textElements, function (el) {
- el.setAttribute('aria-hidden', 'true');
- });
- // Add top-secret screen reader region
- chart.addScreenReaderRegion(hiddenSectionId, tableId);
- // Add ID and summary attr to table HTML
- addEvent(chart, 'afterGetTable', function (e) {
- e.html = e.html
- .replace(
- '<table ',
- '<table summary="' + chart.langFormat(
- 'accessibility.tableSummary', { chart: chart }
- ) + '"'
- );
- });
- });
|