/* *
|
*
|
* (c) 2009-2019 Øystein Moseng
|
*
|
* Accessibility component for exporting menu.
|
*
|
* License: www.highcharts.com/license
|
*
|
* */
|
|
'use strict';
|
|
Object.defineProperty(exports, "__esModule", {
|
value: true
|
});
|
|
var _Globals = require('../../../parts/Globals.js');
|
|
var _Globals2 = _interopRequireDefault(_Globals);
|
|
var _AccessibilityComponent = require('../AccessibilityComponent.js');
|
|
var _AccessibilityComponent2 = _interopRequireDefault(_AccessibilityComponent);
|
|
var _KeyboardNavigationHandler = require('../KeyboardNavigationHandler.js');
|
|
var _KeyboardNavigationHandler2 = _interopRequireDefault(_KeyboardNavigationHandler);
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
/**
|
* Show the export menu and focus the first item (if exists).
|
*
|
* @private
|
* @function Highcharts.Chart#showExportMenu
|
*/
|
_Globals2.default.Chart.prototype.showExportMenu = function () {
|
if (this.exportSVGElements && this.exportSVGElements[0]) {
|
this.exportSVGElements[0].element.onclick();
|
this.highlightExportItem(0);
|
}
|
};
|
|
/**
|
* Hide export menu.
|
*
|
* @private
|
* @function Highcharts.Chart#hideExportMenu
|
*/
|
_Globals2.default.Chart.prototype.hideExportMenu = function () {
|
var chart = this,
|
exportList = chart.exportDivElements;
|
|
if (exportList && chart.exportContextMenu) {
|
// Reset hover states etc.
|
exportList.forEach(function (el) {
|
if (el.className === 'highcharts-menu-item' && el.onmouseout) {
|
el.onmouseout();
|
}
|
});
|
chart.highlightedExportItemIx = 0;
|
// Hide the menu div
|
chart.exportContextMenu.hideMenu();
|
// Make sure the chart has focus and can capture keyboard events
|
chart.container.focus();
|
}
|
};
|
|
/**
|
* Highlight export menu item by index.
|
*
|
* @private
|
* @function Highcharts.Chart#highlightExportItem
|
*
|
* @param {number} ix
|
*
|
* @return {true|undefined}
|
*/
|
_Globals2.default.Chart.prototype.highlightExportItem = function (ix) {
|
var listItem = this.exportDivElements && this.exportDivElements[ix],
|
curHighlighted = this.exportDivElements && this.exportDivElements[this.highlightedExportItemIx],
|
hasSVGFocusSupport;
|
|
if (listItem && listItem.tagName === 'DIV' && !(listItem.children && listItem.children.length)) {
|
// Test if we have focus support for SVG elements
|
hasSVGFocusSupport = !!(this.renderTo.getElementsByTagName('g')[0] || {}).focus;
|
|
// Only focus if we can set focus back to the elements after
|
// destroying the menu (#7422)
|
if (listItem.focus && hasSVGFocusSupport) {
|
listItem.focus();
|
}
|
if (curHighlighted && curHighlighted.onmouseout) {
|
curHighlighted.onmouseout();
|
}
|
if (listItem.onmouseover) {
|
listItem.onmouseover();
|
}
|
this.highlightedExportItemIx = ix;
|
return true;
|
}
|
};
|
|
/**
|
* Try to highlight the last valid export menu item.
|
*
|
* @private
|
* @function Highcharts.Chart#highlightLastExportItem
|
*/
|
_Globals2.default.Chart.prototype.highlightLastExportItem = function () {
|
var chart = this,
|
i;
|
|
if (chart.exportDivElements) {
|
i = chart.exportDivElements.length;
|
while (i--) {
|
if (chart.highlightExportItem(i)) {
|
return true;
|
}
|
}
|
}
|
return false;
|
};
|
|
/**
|
* The MenuComponent class
|
*
|
* @private
|
* @class
|
* @name Highcharts.MenuComponent
|
* @param {Highcharts.Chart} chart
|
* Chart object
|
*/
|
var MenuComponent = function MenuComponent(chart) {
|
this.initBase(chart);
|
this.init();
|
};
|
MenuComponent.prototype = new _AccessibilityComponent2.default();
|
_Globals2.default.extend(MenuComponent.prototype, /** @lends Highcharts.MenuComponent */{
|
|
/**
|
* Init the component
|
*/
|
init: function init() {
|
var chart = this.chart;
|
// Hide the export menu from screen readers when it is hidden visually
|
this.addEvent(chart, 'exportMenuHidden', function () {
|
var menu = this.exportContextMenu;
|
if (menu) {
|
menu.setAttribute('aria-hidden', true);
|
}
|
});
|
},
|
|
/**
|
* Called on each render of the chart. We need to update positioning of the
|
* proxy overlay.
|
*/
|
onChartRender: function onChartRender() {
|
var component = this,
|
chart = this.chart,
|
a11yOptions = chart.options.accessibility;
|
|
// Always start with a clean slate
|
this.removeElement(this.exportProxyGroup);
|
|
// Set screen reader properties on export menu
|
if (chart.options.exporting && chart.options.exporting.enabled !== false && chart.options.exporting.accessibility && chart.options.exporting.accessibility.enabled && chart.exportSVGElements && chart.exportSVGElements[0] && chart.exportSVGElements[0].element) {
|
// Set event handler on button if not already done
|
var button = chart.exportSVGElements[0],
|
buttonElement = button.element,
|
oldExportCallback = buttonElement.onclick;
|
if (this.wrappedButton !== buttonElement) {
|
buttonElement.onclick = function () {
|
oldExportCallback.apply(this, Array.prototype.slice.call(arguments));
|
component.addAccessibleContextMenuAttribs();
|
chart.highlightExportItem(0);
|
};
|
this.wrappedButton = buttonElement;
|
}
|
|
// Proxy button and group
|
this.exportProxyGroup = this.addProxyGroup(
|
// Wrap in a region div if verbosity is high
|
a11yOptions.landmarkVerbosity === 'all' ? {
|
'aria-label': chart.langFormat('accessibility.exporting.exportRegionLabel', { chart: chart }),
|
'role': 'region'
|
} : null);
|
|
this.exportButtonProxy = this.createProxyButton(button, this.exportProxyGroup, {
|
'aria-label': chart.langFormat('accessibility.exporting.menuButtonLabel', { chart: chart })
|
});
|
}
|
},
|
|
/**
|
* Add ARIA to context menu
|
* @private
|
*/
|
addAccessibleContextMenuAttribs: function addAccessibleContextMenuAttribs() {
|
var chart = this.chart,
|
exportList = chart.exportDivElements,
|
contextMenu = chart.exportContextMenu;
|
|
if (exportList && exportList.length) {
|
// 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', chart.langFormat('accessibility.exporting.chartMenuLabel', { chart: chart }));
|
}
|
if (contextMenu) {
|
this.unhideElementFromScreenReaders(contextMenu);
|
}
|
},
|
|
/**
|
* Get keyboard navigation handler for this component.
|
* @return {Highcharts.KeyboardNavigationHandler}
|
*/
|
getKeyboardNavigation: function getKeyboardNavigation() {
|
var keys = this.keyCodes,
|
chart = this.chart,
|
a11yOptions = chart.options.accessibility,
|
component = this;
|
|
return new _KeyboardNavigationHandler2.default(chart, {
|
keyCodeMap: [
|
// Arrow prev handler
|
[[keys.left, keys.up], function () {
|
var i = chart.highlightedExportItemIx || 0;
|
|
// Try to highlight prev item in list. Highlighting e.g.
|
// separators will fail.
|
while (i--) {
|
if (chart.highlightExportItem(i)) {
|
return this.response.success;
|
}
|
}
|
|
// We failed, so wrap around or move to prev module
|
if (a11yOptions.keyboardNavigation.wrapAround) {
|
chart.highlightLastExportItem();
|
return this.response.success;
|
}
|
return this.response.prev;
|
}],
|
|
// Arrow next handler
|
[[keys.right, keys.down], function () {
|
var i = (chart.highlightedExportItemIx || 0) + 1;
|
|
// Try to highlight next item in list. Highlighting e.g.
|
// separators will fail.
|
for (; i < chart.exportDivElements.length; ++i) {
|
if (chart.highlightExportItem(i)) {
|
return this.response.success;
|
}
|
}
|
|
// We failed, so wrap around or move to next module
|
if (a11yOptions.keyboardNavigation.wrapAround) {
|
chart.highlightExportItem(0);
|
return this.response.success;
|
}
|
return this.response.next;
|
}],
|
|
// Click handler
|
[[keys.enter, keys.space], function () {
|
component.fakeClickEvent(chart.exportDivElements[chart.highlightedExportItemIx]);
|
return this.response.success;
|
}],
|
|
// ESC handler
|
[[keys.esc], function () {
|
return this.response.prev;
|
}]],
|
|
// Only run exporting navigation if exporting support exists and is
|
// enabled on chart
|
validate: function validate() {
|
return chart.exportChart && chart.options.exporting.enabled !== false && chart.options.exporting.accessibility.enabled !== false;
|
},
|
|
// Show export menu
|
init: function init(direction) {
|
chart.showExportMenu();
|
|
// If coming back to export menu from other module, try to
|
// highlight last item in menu
|
if (direction < 0) {
|
chart.highlightLastExportItem();
|
}
|
},
|
|
// Hide the menu
|
terminate: function terminate() {
|
chart.hideExportMenu();
|
}
|
});
|
}
|
|
});
|
|
exports.default = MenuComponent;
|