UNPKG

highcharts

Version:
1,570 lines (1,498 loc) 505 kB
/** * @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, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&#x27;') .replace(/\//g, '&#x2F;'); } /** * 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