highcharts
Version:
JavaScript charting framework
1,570 lines (1,498 loc) • 505 kB
JavaScript
/**
* @license Highcharts JS v12.1.2 (2024-12-21)
* @module highcharts/modules/accessibility
* @requires highcharts
*
* Accessibility module
*
* (c) 2010-2024 Highsoft AS
* Author: Oystein Moseng
*
* License: www.highcharts.com/license
*/
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(root["_Highcharts"], root["_Highcharts"]["Templating"], root["_Highcharts"]["AST"], root["_Highcharts"]["Legend"], root["_Highcharts"]["Axis"], root["_Highcharts"]["Color"], root["_Highcharts"]["SeriesRegistry"], root["_Highcharts"]["RendererRegistry"], root["_Highcharts"]["SVGRenderer"], root["_Highcharts"]["Point"], root["_Highcharts"]["Series"]);
else if(typeof define === 'function' && define.amd)
define("highcharts/modules/accessibility", ["highcharts/highcharts"], function (amd1) {return factory(amd1,amd1["Templating"],amd1["AST"],amd1["Legend"],amd1["Axis"],amd1["Color"],amd1["SeriesRegistry"],amd1["RendererRegistry"],amd1["SVGRenderer"],amd1["Point"],amd1["Series"]);});
else if(typeof exports === 'object')
exports["highcharts/modules/accessibility"] = factory(root["_Highcharts"], root["_Highcharts"]["Templating"], root["_Highcharts"]["AST"], root["_Highcharts"]["Legend"], root["_Highcharts"]["Axis"], root["_Highcharts"]["Color"], root["_Highcharts"]["SeriesRegistry"], root["_Highcharts"]["RendererRegistry"], root["_Highcharts"]["SVGRenderer"], root["_Highcharts"]["Point"], root["_Highcharts"]["Series"]);
else
root["Highcharts"] = factory(root["Highcharts"], root["Highcharts"]["Templating"], root["Highcharts"]["AST"], root["Highcharts"]["Legend"], root["Highcharts"]["Axis"], root["Highcharts"]["Color"], root["Highcharts"]["SeriesRegistry"], root["Highcharts"]["RendererRegistry"], root["Highcharts"]["SVGRenderer"], root["Highcharts"]["Point"], root["Highcharts"]["Series"]);
})(typeof window === 'undefined' ? this : window, (__WEBPACK_EXTERNAL_MODULE__944__, __WEBPACK_EXTERNAL_MODULE__984__, __WEBPACK_EXTERNAL_MODULE__660__, __WEBPACK_EXTERNAL_MODULE__632__, __WEBPACK_EXTERNAL_MODULE__532__, __WEBPACK_EXTERNAL_MODULE__620__, __WEBPACK_EXTERNAL_MODULE__512__, __WEBPACK_EXTERNAL_MODULE__608__, __WEBPACK_EXTERNAL_MODULE__540__, __WEBPACK_EXTERNAL_MODULE__260__, __WEBPACK_EXTERNAL_MODULE__820__) => {
return /******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ 660:
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_MODULE__660__;
/***/ }),
/***/ 532:
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_MODULE__532__;
/***/ }),
/***/ 620:
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_MODULE__620__;
/***/ }),
/***/ 632:
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_MODULE__632__;
/***/ }),
/***/ 260:
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_MODULE__260__;
/***/ }),
/***/ 608:
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_MODULE__608__;
/***/ }),
/***/ 540:
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_MODULE__540__;
/***/ }),
/***/ 820:
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_MODULE__820__;
/***/ }),
/***/ 512:
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_MODULE__512__;
/***/ }),
/***/ 984:
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_MODULE__984__;
/***/ }),
/***/ 944:
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_MODULE__944__;
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ (() => {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = (module) => {
/******/ var getter = module && module.__esModule ?
/******/ () => (module['default']) :
/******/ () => (module);
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
"default": () => (/* binding */ accessibility_src)
});
// EXTERNAL MODULE: external {"amd":["highcharts/highcharts"],"commonjs":["highcharts"],"commonjs2":["highcharts"],"root":["Highcharts"]}
var highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_ = __webpack_require__(944);
var highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default = /*#__PURE__*/__webpack_require__.n(highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_);
;// ./code/es-modules/Accessibility/Utils/HTMLUtilities.js
/* *
*
* (c) 2009-2024 Øystein Moseng
*
* Utility functions for accessibility module.
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
const { doc, win } = (highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default());
const { css } = (highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default());
/* *
*
* Constants
*
* */
const simulatedEventTarget = win.EventTarget && new win.EventTarget() || 'none';
/* *
*
* Functions
*
* */
/* eslint-disable valid-jsdoc */
/**
* @private
* @param {Highcharts.HTMLDOMElement} el
* @param {string} className
* @return {void}
*/
function addClass(el, className) {
if (el.classList) {
el.classList.add(className);
}
else if (el.className.indexOf(className) < 0) {
// Note: Dumb check for class name exists, should be fine for practical
// use cases, but will return false positives if the element has a class
// that contains the className.
el.className += ' ' + className;
}
}
/**
* @private
* @param {Highcharts.HTMLDOMElement} el
* @param {string} className
* @return {void}
*/
function removeClass(el, className) {
if (el.classList) {
el.classList.remove(className);
}
else {
// Note: Dumb logic that will break if the element has a class name that
// consists of className plus something else.
el.className = el.className.replace(new RegExp(className, 'g'), '');
}
}
/**
* Utility function to clone a mouse event for re-dispatching.
* @private
*/
function cloneMouseEvent(e) {
if (typeof win.MouseEvent === 'function') {
return new win.MouseEvent(e.type, e);
}
// No MouseEvent support, try using initMouseEvent
if (doc.createEvent) {
const evt = doc.createEvent('MouseEvent');
if (evt.initMouseEvent) {
evt.initMouseEvent(e.type, e.bubbles, // #10561, #12161
e.cancelable, e.view || win, e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget);
return evt;
}
}
return getFakeMouseEvent(e.type);
}
/**
* Utility function to clone a touch event for re-dispatching.
* @private
*/
function cloneTouchEvent(e) {
const touchListToTouchArray = (l) => {
const touchArray = [];
for (let i = 0; i < l.length; ++i) {
const item = l.item(i);
if (item) {
touchArray.push(item);
}
}
return touchArray;
};
if (typeof win.TouchEvent === 'function') {
const newEvent = new win.TouchEvent(e.type, {
touches: touchListToTouchArray(e.touches),
targetTouches: touchListToTouchArray(e.targetTouches),
changedTouches: touchListToTouchArray(e.changedTouches),
ctrlKey: e.ctrlKey,
shiftKey: e.shiftKey,
altKey: e.altKey,
metaKey: e.metaKey,
bubbles: e.bubbles,
cancelable: e.cancelable,
composed: e.composed,
detail: e.detail,
view: e.view
});
if (e.defaultPrevented) {
newEvent.preventDefault();
}
return newEvent;
}
const fakeEvt = cloneMouseEvent(e);
fakeEvt.touches = e.touches;
fakeEvt.changedTouches = e.changedTouches;
fakeEvt.targetTouches = e.targetTouches;
return fakeEvt;
}
/**
* @private
*/
function escapeStringForHTML(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/\//g, '/');
}
/**
* Get an element by ID
* @private
*/
function getElement(id) {
return doc.getElementById(id);
}
/**
* Get a fake mouse event of a given type. If relatedTarget is not given,
* it will point to simulatedEventTarget, as an indicator that the event
* is fake.
* @private
*/
function getFakeMouseEvent(type, position, relatedTarget) {
const pos = position || {
x: 0,
y: 0
};
if (typeof win.MouseEvent === 'function') {
return new win.MouseEvent(type, {
bubbles: true,
cancelable: true,
composed: true,
button: 0,
buttons: 1,
relatedTarget: relatedTarget || simulatedEventTarget,
view: win,
detail: type === 'click' ? 1 : 0,
screenX: pos.x,
screenY: pos.y,
clientX: pos.x,
clientY: pos.y
});
}
// No MouseEvent support, try using initMouseEvent
if (doc.createEvent) {
const evt = doc.createEvent('MouseEvent');
if (evt.initMouseEvent) {
evt.initMouseEvent(type, true, // Bubble
true, // Cancel
win, // View
type === 'click' ? 1 : 0, // Detail
// Coords
pos.x, pos.y, pos.x, pos.y,
// Pressed keys
false, false, false, false, 0, // Button
null // Related target
);
return evt;
}
}
return { type: type };
}
/**
* Get an appropriate heading level for an element. Corresponds to the
* heading level below the previous heading in the DOM.
*
* Note: Only detects previous headings in the DOM that are siblings,
* ancestors, or previous siblings of ancestors. Headings that are nested below
* siblings of ancestors (cousins et.al) are not picked up. This is because it
* is ambiguous whether or not the nesting is for layout purposes or indicates a
* separate section.
*
* @private
* @param {Highcharts.HTMLDOMElement} [element]
* @return {string} The heading tag name (h1, h2 etc).
* If no nearest heading is found, "p" is returned.
*/
function getHeadingTagNameForElement(element) {
const getIncreasedHeadingLevel = (tagName) => {
const headingLevel = parseInt(tagName.slice(1), 10), newLevel = Math.min(6, headingLevel + 1);
return 'h' + newLevel;
};
const isHeading = (tagName) => /^H[1-6]$/i.test(tagName);
const getPreviousSiblingsHeading = (el) => {
let sibling = el;
while (sibling = sibling.previousSibling) { // eslint-disable-line
const tagName = sibling.tagName || '';
if (isHeading(tagName)) {
return tagName;
}
}
return '';
};
const getHeadingRecursive = (el) => {
const prevSiblingsHeading = getPreviousSiblingsHeading(el);
if (prevSiblingsHeading) {
return getIncreasedHeadingLevel(prevSiblingsHeading);
}
// No previous siblings are headings, try parent node
const parent = el.parentElement;
if (!parent) {
return 'h6';
}
const parentTagName = parent.tagName;
if (isHeading(parentTagName)) {
return getIncreasedHeadingLevel(parentTagName);
}
return getHeadingRecursive(parent);
};
return getHeadingRecursive(element);
}
/**
* Remove an element from the DOM.
* @private
* @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} [element]
* @return {void}
*/
function removeElement(element) {
if (element && element.parentNode) {
element.parentNode.removeChild(element);
}
}
/**
* Remove all child nodes from an element.
* @private
* @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} [element]
* @return {void}
*/
function removeChildNodes(element) {
while (element.lastChild) {
element.removeChild(element.lastChild);
}
}
/**
* Utility function. Reverses child nodes of a DOM element.
* @private
*/
function reverseChildNodes(node) {
let i = node.childNodes.length;
while (i--) {
node.appendChild(node.childNodes[i]);
}
}
/**
* Used for aria-label attributes, painting on a canvas will fail if the
* text contains tags.
* @private
*/
function stripHTMLTagsFromString(str, isForExport = false) {
return (typeof str === 'string') ?
(isForExport ?
str.replace(/<\/?[^>]+(>|$)/g, '') :
str.replace(/<\/?(?!\s)[^>]+(>|$)/g, '')) : str;
}
/**
* Utility function for hiding an element visually, but still keeping it
* available to screen reader users.
* @private
*/
function visuallyHideElement(element) {
css(element, {
position: 'absolute',
width: '1px',
height: '1px',
overflow: 'hidden',
whiteSpace: 'nowrap',
clip: 'rect(1px, 1px, 1px, 1px)',
marginTop: '-3px',
'-ms-filter': 'progid:DXImageTransform.Microsoft.Alpha(Opacity=1)',
filter: 'alpha(opacity=1)',
opacity: 0.01
});
}
/* *
*
* Default Export
*
* */
const HTMLUtilities = {
addClass,
cloneMouseEvent,
cloneTouchEvent,
escapeStringForHTML,
getElement,
getFakeMouseEvent,
getHeadingTagNameForElement,
removeChildNodes,
removeClass,
removeElement,
reverseChildNodes,
simulatedEventTarget,
stripHTMLTagsFromString,
visuallyHideElement
};
/* harmony default export */ const Utils_HTMLUtilities = (HTMLUtilities);
// EXTERNAL MODULE: external {"amd":["highcharts/highcharts","Templating"],"commonjs":["highcharts","Templating"],"commonjs2":["highcharts","Templating"],"root":["Highcharts","Templating"]}
var highcharts_Templating_commonjs_highcharts_Templating_commonjs2_highcharts_Templating_root_Highcharts_Templating_ = __webpack_require__(984);
var highcharts_Templating_commonjs_highcharts_Templating_commonjs2_highcharts_Templating_root_Highcharts_Templating_default = /*#__PURE__*/__webpack_require__.n(highcharts_Templating_commonjs_highcharts_Templating_commonjs2_highcharts_Templating_root_Highcharts_Templating_);
;// ./code/es-modules/Accessibility/A11yI18n.js
/* *
*
* Accessibility module - internationalization support
*
* (c) 2010-2024 Highsoft AS
* Author: Øystein Moseng
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
const { format } = (highcharts_Templating_commonjs_highcharts_Templating_commonjs2_highcharts_Templating_root_Highcharts_Templating_default());
const { getNestedProperty, pick } = (highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default());
/* *
*
* Composition
*
* */
var A11yI18nComposition;
(function (A11yI18nComposition) {
/* *
*
* Declarations
*
* */
/* *
*
* Functions
*
* */
/**
* @private
*/
function compose(ChartClass) {
const chartProto = ChartClass.prototype;
if (!chartProto.langFormat) {
chartProto.langFormat = langFormat;
}
}
A11yI18nComposition.compose = compose;
/**
* I18n utility function. Format a single array or plural statement in a
* format string. If the statement is not an array or plural statement,
* returns the statement within brackets. Invalid array statements return
* an empty string.
*
* @private
* @function formatExtendedStatement
* @param {string} statement
* @param {Highcharts.Dictionary<*>} ctx
* Context to apply to the format string.
*/
function formatExtendedStatement(statement, ctx) {
const eachStart = statement.indexOf('#each('), pluralStart = statement.indexOf('#plural('), indexStart = statement.indexOf('['), indexEnd = statement.indexOf(']');
let arr, result;
// Dealing with an each-function?
if (eachStart > -1) {
const eachEnd = statement.slice(eachStart).indexOf(')') + eachStart, preEach = statement.substring(0, eachStart), postEach = statement.substring(eachEnd + 1), eachStatement = statement.substring(eachStart + 6, eachEnd), eachArguments = eachStatement.split(',');
let lenArg = Number(eachArguments[1]), len;
result = '';
arr = getNestedProperty(eachArguments[0], ctx);
if (arr) {
lenArg = isNaN(lenArg) ? arr.length : lenArg;
len = lenArg < 0 ?
arr.length + lenArg :
Math.min(lenArg, arr.length); // Overshoot
// Run through the array for the specified length
for (let i = 0; i < len; ++i) {
result += preEach + arr[i] + postEach;
}
}
return result.length ? result : '';
}
// Dealing with a plural-function?
if (pluralStart > -1) {
const pluralEnd = (statement.slice(pluralStart).indexOf(')') + pluralStart), pluralStatement = statement.substring(pluralStart + 8, pluralEnd), pluralArguments = pluralStatement.split(','), num = Number(getNestedProperty(pluralArguments[0], ctx));
switch (num) {
case 0:
result = pick(pluralArguments[4], pluralArguments[1]);
break;
case 1:
result = pick(pluralArguments[2], pluralArguments[1]);
break;
case 2:
result = pick(pluralArguments[3], pluralArguments[1]);
break;
default:
result = pluralArguments[1];
}
return result ? stringTrim(result) : '';
}
// Array index
if (indexStart > -1) {
const arrayName = statement.substring(0, indexStart), ix = Number(statement.substring(indexStart + 1, indexEnd));
let val;
arr = getNestedProperty(arrayName, ctx);
if (!isNaN(ix) && arr) {
if (ix < 0) {
val = arr[arr.length + ix];
// Handle negative overshoot
if (typeof val === 'undefined') {
val = arr[0];
}
}
else {
val = arr[ix];
// Handle positive overshoot
if (typeof val === 'undefined') {
val = arr[arr.length - 1];
}
}
}
return typeof val !== 'undefined' ? val : '';
}
// Standard substitution, delegate to format or similar
return '{' + statement + '}';
}
/* eslint-disable max-len */
/**
* i18n formatting function. Extends Highcharts.format() functionality by
* also handling arrays and plural conditionals. Arrays can be indexed as
* follows:
*
* - Format: 'This is the first index: {myArray[0]}. The last: {myArray[-1]}.'
*
* - Context: { myArray: [0, 1, 2, 3, 4, 5] }
*
* - Result: 'This is the first index: 0. The last: 5.'
*
*
* They can also be iterated using the #each() function. This will repeat
* the contents of the bracket expression for each element. Example:
*
* - Format: 'List contains: {#each(myArray)cm }'
*
* - Context: { myArray: [0, 1, 2] }
*
* - Result: 'List contains: 0cm 1cm 2cm '
*
*
* The #each() function optionally takes a length parameter. If positive,
* this parameter specifies the max number of elements to iterate through.
* If negative, the function will subtract the number from the length of the
* array. Use this to stop iterating before the array ends. Example:
*
* - Format: 'List contains: {#each(myArray, -1), }and {myArray[-1]}.'
*
* - Context: { myArray: [0, 1, 2, 3] }
*
* - Result: 'List contains: 0, 1, 2, and 3.'
*
*
* Use the #plural() function to pick a string depending on whether or not a
* context object is 1. Arguments are #plural(obj, plural, singular).
* Example:
*
* - Format: 'Has {numPoints} {#plural(numPoints, points, point}.'
*
* - Context: { numPoints: 5 }
*
* - Result: 'Has 5 points.'
*
*
* Optionally there are additional parameters for dual and none:
* #plural(obj, plural, singular, dual, none). Example:
*
* - Format: 'Has {#plural(numPoints, many points, one point, two points,
* none}.'
*
* - Context: { numPoints: 2 }
*
* - Result: 'Has two points.'
*
*
* The dual or none parameters will take precedence if they are supplied.
*
* @requires modules/accessibility
*
* @function Highcharts.i18nFormat
*
* @param {string} formatString
* The string to format.
*
* @param {Highcharts.Dictionary<*>} context
* Context to apply to the format string.
*
* @param {Highcharts.Chart} chart
* A `Chart` instance with a time object and numberFormatter, passed on to
* format().
*
* @deprecated
*
* @return {string}
* The formatted string.
*/
function i18nFormat(formatString, context, chart) {
const getFirstBracketStatement = (sourceStr, offset) => {
const str = sourceStr.slice(offset || 0), startBracket = str.indexOf('{'), endBracket = str.indexOf('}');
if (startBracket > -1 && endBracket > startBracket) {
return {
statement: str.substring(startBracket + 1, endBracket),
begin: offset + startBracket + 1,
end: offset + endBracket
};
}
}, tokens = [];
let bracketRes, constRes, cursor = 0;
// Tokenize format string into bracket statements and constants
do {
bracketRes = getFirstBracketStatement(formatString, cursor);
constRes = formatString.substring(cursor, bracketRes && bracketRes.begin - 1);
// If we have constant content before this bracket statement, add it
if (constRes.length) {
tokens.push({
value: constRes,
type: 'constant'
});
}
// Add the bracket statement
if (bracketRes) {
tokens.push({
value: bracketRes.statement,
type: 'statement'
});
}
cursor = bracketRes ? bracketRes.end + 1 : cursor + 1;
} while (bracketRes);
// Perform the formatting. The formatArrayStatement function returns
// the statement in brackets if it is not an array statement, which
// means it gets picked up by format below.
tokens.forEach((token) => {
if (token.type === 'statement') {
token.value = formatExtendedStatement(token.value, context);
}
});
// Join string back together and pass to format to pick up non-array
// statements.
return format(tokens.reduce((acc, cur) => acc + cur.value, ''), context, chart);
}
A11yI18nComposition.i18nFormat = i18nFormat;
/* eslint-enable max-len */
/**
* Apply context to a format string from lang options of the chart.
*
* @requires modules/accessibility
*
* @function Highcharts.Chart#langFormat
*
* @param {string} langKey
* Key (using dot notation) into lang option structure.
*
* @param {Highcharts.Dictionary<*>} context
* Context to apply to the format string.
*
* @return {string}
* The formatted string.
*/
function langFormat(langKey, context) {
const keys = langKey.split('.');
let formatString = this.options.lang, i = 0;
for (; i < keys.length; ++i) {
formatString = formatString && formatString[keys[i]];
}
return typeof formatString === 'string' ?
i18nFormat(formatString, context, this) : '';
}
/**
* @private
* @function stringTrim
*
* @param {string} str
* The input string
*
* @return {string}
* The trimmed string
*/
function stringTrim(str) {
return str.trim && str.trim() || str.replace(/^\s+|\s+$/g, '');
}
})(A11yI18nComposition || (A11yI18nComposition = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ const A11yI18n = (A11yI18nComposition);
;// ./code/es-modules/Accessibility/Utils/ChartUtilities.js
/* *
*
* (c) 2009-2024 Øystein Moseng
*
* Utils for dealing with charts.
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
const { doc: ChartUtilities_doc } = (highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default());
const { stripHTMLTagsFromString: stripHTMLTags } = Utils_HTMLUtilities;
const { defined, find, fireEvent } = (highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default());
/* *
*
* Functions
*
* */
/* eslint-disable valid-jsdoc */
/**
* Fire an event on an element that is either wrapped by Highcharts,
* or a DOM element.
* @private
*/
function fireEventOnWrappedOrUnwrappedElement(el, eventObject) {
const type = eventObject.type;
const hcEvents = el.hcEvents;
if (!!ChartUtilities_doc.createEvent &&
(el.dispatchEvent || el.fireEvent)) {
if (el.dispatchEvent) {
el.dispatchEvent(eventObject);
}
else {
el.fireEvent(type, eventObject);
}
}
else if (hcEvents && hcEvents[type]) {
fireEvent(el, type, eventObject);
}
else if (el.element) {
fireEventOnWrappedOrUnwrappedElement(el.element, eventObject);
}
}
/**
* @private
*/
function getChartTitle(chart) {
return stripHTMLTags(chart.options.title.text ||
chart.langFormat('accessibility.defaultChartTitle', { chart: chart }), chart.renderer.forExport);
}
/**
* Return string with the axis name/title.
* @private
*/
function getAxisDescription(axis) {
return axis && (axis.options.accessibility?.description ||
axis.axisTitle?.textStr ||
axis.options.id ||
axis.categories && 'categories' ||
axis.dateTime && 'Time' ||
'values');
}
/**
* Return string with text description of the axis range.
* @private
* @param {Highcharts.Axis} axis
* The axis to get range desc of.
* @return {string}
* A string with the range description for the axis.
*/
function getAxisRangeDescription(axis) {
const axisOptions = axis.options || {};
// Handle overridden range description
if (axisOptions.accessibility &&
typeof axisOptions.accessibility.rangeDescription !== 'undefined') {
return axisOptions.accessibility.rangeDescription;
}
// Handle category axes
if (axis.categories) {
return getCategoryAxisRangeDesc(axis);
}
// Use time range, not from-to?
if (axis.dateTime && (axis.min === 0 || axis.dataMin === 0)) {
return getAxisTimeLengthDesc(axis);
}
// Just use from and to.
// We have the range and the unit to use, find the desc format
return getAxisFromToDescription(axis);
}
/**
* Describe the range of a category axis.
* @private
*/
function getCategoryAxisRangeDesc(axis) {
const chart = axis.chart;
if (axis.dataMax && axis.dataMin) {
return chart.langFormat('accessibility.axis.rangeCategories', {
chart: chart,
axis: axis,
numCategories: axis.dataMax - axis.dataMin + 1
});
}
return '';
}
/**
* Describe the length of the time window shown on an axis.
* @private
*/
function getAxisTimeLengthDesc(axis) {
const chart = axis.chart, range = {}, min = axis.dataMin || axis.min || 0, max = axis.dataMax || axis.max || 0;
let rangeUnit = 'Seconds';
range.Seconds = (max - min) / 1000;
range.Minutes = range.Seconds / 60;
range.Hours = range.Minutes / 60;
range.Days = range.Hours / 24;
['Minutes', 'Hours', 'Days'].forEach(function (unit) {
if (range[unit] > 2) {
rangeUnit = unit;
}
});
const rangeValue = range[rangeUnit].toFixed(rangeUnit !== 'Seconds' &&
rangeUnit !== 'Minutes' ? 1 : 0 // Use decimals for days/hours
);
// We have the range and the unit to use, find the desc format
return chart.langFormat('accessibility.axis.timeRange' + rangeUnit, {
chart: chart,
axis: axis,
range: rangeValue.replace('.0', '')
});
}
/**
* Describe an axis from-to range.
* @private
*/
function getAxisFromToDescription(axis) {
const chart = axis.chart, options = chart.options, dateRangeFormat = (options &&
options.accessibility &&
options.accessibility.screenReaderSection.axisRangeDateFormat ||
''), extremes = {
min: axis.dataMin || axis.min || 0,
max: axis.dataMax || axis.max || 0
}, format = function (key) {
return axis.dateTime ?
chart.time.dateFormat(dateRangeFormat, extremes[key]) :
extremes[key].toString();
};
return chart.langFormat('accessibility.axis.rangeFromTo', {
chart: chart,
axis: axis,
rangeFrom: format('min'),
rangeTo: format('max')
});
}
/**
* Get the DOM element for the first point in the series.
* @private
* @param {Highcharts.Series} series
* The series to get element for.
* @return {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement|undefined}
* The DOM element for the point.
*/
function getSeriesFirstPointElement(series) {
if (series.points && series.points.length) {
const firstPointWithGraphic = find(series.points, (p) => !!p.graphic);
return (firstPointWithGraphic &&
firstPointWithGraphic.graphic &&
firstPointWithGraphic.graphic.element);
}
}
/**
* Get the DOM element for the series that we put accessibility info on.
* @private
* @param {Highcharts.Series} series
* The series to get element for.
* @return {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement|undefined}
* The DOM element for the series
*/
function getSeriesA11yElement(series) {
const firstPointEl = getSeriesFirstPointElement(series);
return (firstPointEl &&
firstPointEl.parentNode || series.graph &&
series.graph.element || series.group &&
series.group.element); // Could be tracker series depending on series type
}
/**
* Remove aria-hidden from element. Also unhides parents of the element, and
* hides siblings that are not explicitly unhidden.
* @private
*/
function unhideChartElementFromAT(chart, element) {
element.setAttribute('aria-hidden', false);
if (element === chart.renderTo ||
!element.parentNode ||
element.parentNode === ChartUtilities_doc.body // #16126: Full screen printing
) {
return;
}
// Hide siblings unless their hidden state is already explicitly set
Array.prototype.forEach.call(element.parentNode.childNodes, function (node) {
if (!node.hasAttribute('aria-hidden')) {
node.setAttribute('aria-hidden', true);
}
});
// Repeat for parent
unhideChartElementFromAT(chart, element.parentNode);
}
/**
* Hide series from screen readers.
* @private
*/
function hideSeriesFromAT(series) {
const seriesEl = getSeriesA11yElement(series);
if (seriesEl) {
seriesEl.setAttribute('aria-hidden', true);
}
}
/**
* Get series objects by series name.
* @private
*/
function getSeriesFromName(chart, name) {
if (!name) {
return chart.series;
}
return (chart.series || []).filter(function (s) {
return s.name === name;
});
}
/**
* Get point in a series from x/y values.
* @private
*/
function getPointFromXY(series, x, y) {
let i = series.length, res;
while (i--) {
res = find(series[i].points || [], function (p) {
return p.x === x && p.y === y;
});
if (res) {
return res;
}
}
}
/**
* Get relative position of point on an x/y axis from 0 to 1.
* @private
*/
function getRelativePointAxisPosition(axis, point) {
if (!defined(axis.dataMin) || !defined(axis.dataMax)) {
return 0;
}
const axisStart = axis.toPixels(axis.dataMin), axisEnd = axis.toPixels(axis.dataMax),
// We have to use pixel position because of axis breaks, log axis etc.
positionProp = axis.coll === 'xAxis' ? 'x' : 'y', pointPos = axis.toPixels(point[positionProp] || 0);
return (pointPos - axisStart) / (axisEnd - axisStart);
}
/**
* Get relative position of point on an x/y axis from 0 to 1.
* @private
*/
function scrollAxisToPoint(point) {
const xAxis = point.series.xAxis, yAxis = point.series.yAxis, axis = (xAxis && xAxis.scrollbar ? xAxis : yAxis), scrollbar = (axis && axis.scrollbar);
if (scrollbar && defined(scrollbar.to) && defined(scrollbar.from)) {
const range = scrollbar.to - scrollbar.from;
const pos = getRelativePointAxisPosition(axis, point);
scrollbar.updatePosition(pos - range / 2, pos + range / 2);
fireEvent(scrollbar, 'changed', {
from: scrollbar.from,
to: scrollbar.to,
trigger: 'scrollbar',
DOMEvent: null
});
}
}
/* *
*
* Default Export
*
* */
const ChartUtilities = {
fireEventOnWrappedOrUnwrappedElement,
getChartTitle,
getAxisDescription,
getAxisRangeDescription,
getPointFromXY,
getSeriesFirstPointElement,
getSeriesFromName,
getSeriesA11yElement,
unhideChartElementFromAT,
hideSeriesFromAT,
scrollAxisToPoint
};
/* harmony default export */ const Utils_ChartUtilities = (ChartUtilities);
;// ./code/es-modules/Accessibility/Utils/DOMElementProvider.js
/* *
*
* (c) 2009-2024 Øystein Moseng
*
* Class that can keep track of elements added to DOM and clean them up on
* destroy.
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
const { doc: DOMElementProvider_doc } = (highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default());
const { removeElement: DOMElementProvider_removeElement } = Utils_HTMLUtilities;
/* *
*
* Class
*
* */
/**
* @private
*/
class DOMElementProvider {
/* *
*
* Constructor
*
* */
constructor() {
this.elements = [];
}
/**
* Create an element and keep track of it for later removal.
* Same args as document.createElement
* @private
*/
createElement() {
const el = DOMElementProvider_doc.createElement.apply(DOMElementProvider_doc, arguments);
this.elements.push(el);
return el;
}
/**
* Destroy created element, removing it from the DOM.
* @private
*/
removeElement(element) {
DOMElementProvider_removeElement(element);
this.elements.splice(this.elements.indexOf(element), 1);
}
/**
* Destroy all created elements, removing them from the DOM.
* @private
*/
destroyCreatedElements() {
this.elements.forEach(function (element) {
DOMElementProvider_removeElement(element);
});
this.elements = [];
}
}
/* *
*
* Default Export
*
* */
/* harmony default export */ const Utils_DOMElementProvider = (DOMElementProvider);
;// ./code/es-modules/Accessibility/Utils/EventProvider.js
/* *
*
* (c) 2009-2024 Øystein Moseng
*
* Class that can keep track of events added, and clean them up on destroy.
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
const { addEvent } = (highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default());
/**
* @private
*/
class EventProvider {
/* *
*
* Constructor
*
* */
constructor() {
this.eventRemovers = [];
}
/**
* Add an event to an element and keep track of it for later removal.
* Same args as Highcharts.addEvent.
* @private
*/
addEvent() {
const remover = addEvent.apply((highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default()), arguments);
this.eventRemovers.push({
element: arguments[0], // HTML element
remover
});
return remover;
}
/**
* Remove added event.
* @private
*/
removeEvent(event) {
const pos = this.eventRemovers.map((e) => e.remover).indexOf(event);
this.eventRemovers[pos].remover();
this.eventRemovers.splice(pos, 1);
}
/**
* Remove all added events.
* @private
*/
removeAddedEvents() {
this.eventRemovers.map((e) => e.remover)
.forEach((remover) => remover());
this.eventRemovers = [];
}
}
/* *
*
* Default Export
*
* */
/* harmony default export */ const Utils_EventProvider = (EventProvider);
;// ./code/es-modules/Accessibility/AccessibilityComponent.js
/* *
*
* (c) 2009-2024 Øystein Moseng
*
* Accessibility component class definition
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
const { fireEventOnWrappedOrUnwrappedElement: AccessibilityComponent_fireEventOnWrappedOrUnwrappedElement } = Utils_ChartUtilities;
const { getFakeMouseEvent: AccessibilityComponent_getFakeMouseEvent } = Utils_HTMLUtilities;
/* *
*
* Class
*
* */
/**
* The AccessibilityComponent base class, representing a part of the chart that
* has accessibility logic connected to it. This class can be inherited from to
* create a custom accessibility component for a chart.
*
* Components should take care to destroy added elements and unregister event
* handlers on destroy. This is handled automatically if using `this.addEvent`
* and `this.createElement`.
*
* @sample highcharts/accessibility/custom-component
* Custom accessibility component
*
* @requires modules/accessibility
* @class
* @name Highcharts.AccessibilityComponent
*/
class AccessibilityComponent {
/* *
*
* Functions
*
* */
/**
* Called when accessibility is disabled or chart is destroyed.
*
* @function Highcharts.AccessibilityComponent#destroy
*/
destroy() { }
/**
* Get keyboard navigation handler for this component.
*
* @function Highcharts.AccessibilityComponent#getKeyboardNavigation
* @return {Highcharts.KeyboardNavigationHandler|Array<Highcharts.KeyboardNavigationHandler>}
* The keyboard navigation handler(s) for this component.
*/
getKeyboardNavigation() {
return [];
}
/**
* Called on component initialization.
*
* @function Highcharts.AccessibilityComponent#init
*/
init() { }
/**
* Called on every chart render.
*
* @function Highcharts.AccessibilityComponent#onChartRender
*/
onChartRender() { }
/**
* Called on updates to the chart, including options changes.
* Note that this is also called on first render of chart.
*
* @function Highcharts.AccessibilityComponent#onChartUpdate
*/
onChartUpdate() { }
/**
* Initialize the class
* @private
* @param {Highcharts.Chart} chart The chart object
* @param {Highcharts.ProxyProvider} proxyProvider The proxy provider of the accessibility module
*/
initBase(chart, proxyProvider) {
this.chart = chart;
this.eventProvider = new Utils_EventProvider();
this.domElementProvider = new Utils_DOMElementProvider();
this.proxyProvider = proxyProvider;
// Key code enum for common keys
this.keyCodes = {
left: 37,
right: 39,
up: 38,
down: 40,
enter: 13,
space: 32,
esc: 27,
tab: 9,
pageUp: 33,
pageDown: 34,
end: 35,
home: 36
};
}
/**
* Add an event to an element and keep track of it for later removal.
* See EventProvider for details.
* @private
*/
addEvent(el, type, fn, options) {
return this.eventProvider.addEvent(el, type, fn, options);
}
/**
* Create an element and keep track of it for later removal.
* See DOMElementProvider for details.
* @private
*/
createElement(tagName, options) {
return this.domElementProvider.createElement(tagName, options);
}
/**
* Fire a fake click event on an element. It is useful to have this on
* AccessibilityComponent for users of custom components.
* @private
*/
fakeClickEvent(el) {
const fakeEvent = AccessibilityComponent_getFakeMouseEvent('click');
AccessibilityComponent_fireEventOnWrappedOrUnwrappedElement(el, fakeEvent);
}
/**
* Remove traces of the component.
* @private
*/
destroyBase() {
this.domElementProvider.destroyCreatedElements();
this.eventProvider.removeAddedEvents();
}
}
/* *
*
* Default Export
*
* */
/* harmony default export */ const Accessibility_AccessibilityComponent = (AccessibilityComponent);
;// ./code/es-modules/Accessibility/KeyboardNavigationHandler.js
/* *
*
* (c) 2009-2024 Øystein Moseng
*
* Keyboard navigation handler base class definition
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
const { find: KeyboardNavigationHandler_find } = (highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default());
/* *
*
* Class
*
* */
/**
* Define a keyboard navigation handler for use with a
* Highcharts.AccessibilityComponent instance. This functions as an abstraction
* layer for keyboard navigation, and defines a map of keyCodes to handler
* functions.
*
* @requires modules/accessibility
*
* @sample highcharts/accessibility/custom-component
* Custom accessibility component
*
* @class
* @name Highcharts.KeyboardNavigationHandler
*
* @param {Highcharts.Chart} chart
* The chart this module should act on.
*
* @param {Highcharts.KeyboardNavigationHandlerOptionsObject} options
* Options for the keyboard navigation handler.
*/
class KeyboardNavigationHandler {
/* *
*
* Constructor
*
* */
constructor(chart, options) {
this.chart = chart;
this.keyCodeMap = options.keyCodeMap || [];
this.validate = options.validate;
this.init = options.init;
this.terminate = options.terminate;
// Response enum
this.response = {
success: 1, // Keycode was handled
prev: 2, // Move to prev module
next: 3, // Move to next module
noHandler: 4, // There is no handler for this keycode
fail: 5 // Handler failed
};
}
/* *
*
* Functions
*
* */
/**
* Find handler function(s) for key code in the keyCodeMap and run it.
*
* @function KeyboardNavigationHandler#run
* @param {global.KeyboardEvent} e
* @return {number} Returns a response code indicating whether the run was
* a success/fail/unhandled, or if we should move to next/prev module.
*/
run(e) {
const keyCode = e.which || e.keyCode;
let response = this.response.noHandler;
const handlerCodeSet = KeyboardNavigationHandler_find(this.keyCodeMap, function (codeSet) {
return codeSet[0].indexOf(keyCode) > -1;
});
if (handlerCodeSet) {
response = handlerCodeSet[1].call(this, keyCode, e);
}
else if (keyCode === 9) {
// Default tab handler, move to next/prev module
response = this.response[e.shiftKey ? 'prev' : 'next'];
}
return response;
}
}
/* *
*
* Default Export
*
* */
/* harmony default export */ const Accessibility_KeyboardNavigationHandler = (KeyboardNavigationHandler);
/* *
*
* API Declarations
*
* */
/**
* Options for the keyboard navigation handler.
*
* @interface Highcharts.KeyboardNavigationHandlerOptionsObject
*/ /**
* An array containing pairs of an array of keycodes, mapped to a handler
* function. When the keycode is received, the handler is called with the
* keycode as parameter.
* @name Highcharts.KeyboardNavigationHandlerOptionsObject#keyCodeMap
* @type {Array<Array<Array<number>, Function>>}
*/ /**
* Function to run on initialization of module.
* @name Highcharts.KeyboardNavigationHandlerOptionsObject#init
* @type {Function}
*/ /**
* Function to run before moving to next/prev module. Receives moving direction
* as parameter: +1 for next, -1 for previous.
* @name Highcharts.KeyboardNavigationHandlerOptionsObject#terminate
* @type {Function|undefined}
*/ /**
* Function to run to validate module. Should return false if module should not
* run, true otherwise. Receives chart as parameter.
* @name Highcharts.KeyboardNavigationHandlerOptionsObject#validate
* @type {Function|undefined}
*/
(''); // Keeps doclets above in JS file
;// ./code/es-modules/Accessibility/Components/ContainerComponent.js
/* *
*
* (c) 2009-2024 Øystein Moseng
*
* Accessibility component for chart container.
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
const { unhideChartElementFromAT: ContainerComponent_unhideChartElementFromAT, getChartTitle: ContainerComponent_getChartTitle } = Utils_ChartUtilities;
const { doc: ContainerComponent_doc } = (highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default());
const { stripHTMLTagsFromString: ContainerComponent_stripHTMLTags } = Utils_HTMLUtilities;
/**
* The ContainerComponent class
*
* @private
* @class
* @name Highcharts.ContainerComponent
*/
class ContainerComponent extends Accessibility_AccessibilityComponent {
/* *
*
* Functions
*
* */
/* eslint-disable valid-jsdoc */
/**
* Called on first render/updates to the chart, including options changes.
*/
onChartUpdate() {
this.handleSVGTitleElement();
this.setSVGContainerLabel();
this.setGraphicContainerAttrs();
this.setRenderToAttrs();
this.makeCreditsAccessible();
}
/**
* @private
*/
handleSVGTitleElement() {
const chart = this.chart, titleId = 'highcharts-title-' + chart.index, titleContents = ContainerComponent_stripHTMLTags(chart.langFormat('accessibility.svgContainerTitle', {
chartTitle: ContainerComponent_getChartTitle(chart)
}));
if (titleContents.length) {
const titleElement = this.svgTitleElement =
this.svgTitleElement || ContainerComponent_doc.createElementNS('http://www.w3.org/2000/svg', 'title');
titleElement.textContent = titleContents;
titleElement.id = titleId;
chart.renderTo.insertBefore(titleElement, chart.renderTo.firstChild);
}
}
/**
* @private
*/
setSVGContainerLabel() {
const chart = this.chart, svgContainerLabel = chart.langFormat('accessibility.svgContainerLabel', {
chartTitle: ContainerComponent_getChartTitle(chart)
});
if (chart.renderer.box && svgContainerLabel.length) {
chart.renderer.box.setAttribute('ari