{"version":3,"sources":[""],"sourcesContent":["/**\n * @license Highcharts JS v11.2.0 (2023-10-30)\n *\n * Accessibility module\n *\n * (c) 2010-2021 Highsoft AS\n * Author: Oystein Moseng\n *\n * License: www.highcharts.com/license\n */\n(function (factory) {\n if (typeof module === 'object' && module.exports) {\n factory['default'] = factory;\n module.exports = factory;\n } else if (typeof define === 'function' && define.amd) {\n define('highcharts/modules/accessibility', ['highcharts'], function (Highcharts) {\n factory(Highcharts);\n factory.Highcharts = Highcharts;\n return factory;\n });\n } else {\n factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);\n }\n}(function (Highcharts) {\n 'use strict';\n var _modules = Highcharts ? Highcharts._modules : {};\n function _registerModule(obj, path, args, fn) {\n if (!obj.hasOwnProperty(path)) {\n obj[path] = fn.apply(null, args);\n\n if (typeof CustomEvent === 'function') {\n window.dispatchEvent(new CustomEvent(\n 'HighchartsModuleLoaded',\n { detail: { path: path, module: obj[path] } }\n ));\n }\n }\n }\n _registerModule(_modules, 'Accessibility/Utils/HTMLUtilities.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Utility functions for accessibility module.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { doc, win } = H;\n const { css } = U;\n /* *\n *\n * Constants\n *\n * */\n const simulatedEventTarget = win.EventTarget && new win.EventTarget() || 'none';\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * @private\n * @param {Highcharts.HTMLDOMElement} el\n * @param {string} className\n * @return {void}\n */\n function addClass(el, className) {\n if (el.classList) {\n el.classList.add(className);\n }\n else if (el.className.indexOf(className) < 0) {\n // Note: Dumb check for class name exists, should be fine for practical\n // use cases, but will return false positives if the element has a class\n // that contains the className.\n el.className += ' ' + className;\n }\n }\n /**\n * @private\n * @param {Highcharts.HTMLDOMElement} el\n * @param {string} className\n * @return {void}\n */\n function removeClass(el, className) {\n if (el.classList) {\n el.classList.remove(className);\n }\n else {\n // Note: Dumb logic that will break if the element has a class name that\n // consists of className plus something else.\n el.className = el.className.replace(new RegExp(className, 'g'), '');\n }\n }\n /**\n * Utility function to clone a mouse event for re-dispatching.\n * @private\n */\n function cloneMouseEvent(e) {\n if (typeof win.MouseEvent === 'function') {\n return new win.MouseEvent(e.type, e);\n }\n // No MouseEvent support, try using initMouseEvent\n if (doc.createEvent) {\n const evt = doc.createEvent('MouseEvent');\n if (evt.initMouseEvent) {\n evt.initMouseEvent(e.type, e.bubbles, // #10561, #12161\n 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);\n return evt;\n }\n }\n return getFakeMouseEvent(e.type);\n }\n /**\n * Utility function to clone a touch event for re-dispatching.\n * @private\n */\n function cloneTouchEvent(e) {\n const touchListToTouchArray = (l) => {\n const touchArray = [];\n for (let i = 0; i < l.length; ++i) {\n const item = l.item(i);\n if (item) {\n touchArray.push(item);\n }\n }\n return touchArray;\n };\n if (typeof win.TouchEvent === 'function') {\n const newEvent = new win.TouchEvent(e.type, {\n touches: touchListToTouchArray(e.touches),\n targetTouches: touchListToTouchArray(e.targetTouches),\n changedTouches: touchListToTouchArray(e.changedTouches),\n ctrlKey: e.ctrlKey,\n shiftKey: e.shiftKey,\n altKey: e.altKey,\n metaKey: e.metaKey,\n bubbles: e.bubbles,\n cancelable: e.cancelable,\n composed: e.composed,\n detail: e.detail,\n view: e.view\n });\n if (e.defaultPrevented) {\n newEvent.preventDefault();\n }\n return newEvent;\n }\n const fakeEvt = cloneMouseEvent(e);\n fakeEvt.touches = e.touches;\n fakeEvt.changedTouches = e.changedTouches;\n fakeEvt.targetTouches = e.targetTouches;\n return fakeEvt;\n }\n /**\n * @private\n */\n function escapeStringForHTML(str) {\n return str\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n .replace(/\\//g, '/');\n }\n /**\n * Get an element by ID\n * @private\n */\n function getElement(id) {\n return doc.getElementById(id);\n }\n /**\n * Get a fake mouse event of a given type. If relatedTarget is not given,\n * it will point to simulatedEventTarget, as an indicator that the event\n * is fake.\n * @private\n */\n function getFakeMouseEvent(type, position, relatedTarget) {\n const pos = position || {\n x: 0,\n y: 0\n };\n if (typeof win.MouseEvent === 'function') {\n return new win.MouseEvent(type, {\n bubbles: true,\n cancelable: true,\n composed: true,\n button: 0,\n buttons: 1,\n relatedTarget: relatedTarget || simulatedEventTarget,\n view: win,\n detail: type === 'click' ? 1 : 0,\n screenX: pos.x,\n screenY: pos.y,\n clientX: pos.x,\n clientY: pos.y\n });\n }\n // No MouseEvent support, try using initMouseEvent\n if (doc.createEvent) {\n const evt = doc.createEvent('MouseEvent');\n if (evt.initMouseEvent) {\n evt.initMouseEvent(type, true, // Bubble\n true, // Cancel\n win, // View\n type === 'click' ? 1 : 0, // Detail\n // Coords\n pos.x, pos.y, pos.x, pos.y, \n // Pressed keys\n false, false, false, false, 0, // button\n null // related target\n );\n return evt;\n }\n }\n return { type: type };\n }\n /**\n * Get an appropriate heading level for an element. Corresponds to the\n * heading level below the previous heading in the DOM.\n *\n * Note: Only detects previous headings in the DOM that are siblings,\n * ancestors, or previous siblings of ancestors. Headings that are nested below\n * siblings of ancestors (cousins et.al) are not picked up. This is because it\n * is ambiguous whether or not the nesting is for layout purposes or indicates a\n * separate section.\n *\n * @private\n * @param {Highcharts.HTMLDOMElement} [element]\n * @return {string} The heading tag name (h1, h2 etc).\n * If no nearest heading is found, \"p\" is returned.\n */\n function getHeadingTagNameForElement(element) {\n const getIncreasedHeadingLevel = (tagName) => {\n const headingLevel = parseInt(tagName.slice(1), 10), newLevel = Math.min(6, headingLevel + 1);\n return 'h' + newLevel;\n };\n const isHeading = (tagName) => /H[1-6]/.test(tagName);\n const getPreviousSiblingsHeading = (el) => {\n let sibling = el;\n while (sibling = sibling.previousSibling) { // eslint-disable-line\n const tagName = sibling.tagName || '';\n if (isHeading(tagName)) {\n return tagName;\n }\n }\n return '';\n };\n const getHeadingRecursive = (el) => {\n const prevSiblingsHeading = getPreviousSiblingsHeading(el);\n if (prevSiblingsHeading) {\n return getIncreasedHeadingLevel(prevSiblingsHeading);\n }\n // No previous siblings are headings, try parent node\n const parent = el.parentElement;\n if (!parent) {\n return 'p';\n }\n const parentTagName = parent.tagName;\n if (isHeading(parentTagName)) {\n return getIncreasedHeadingLevel(parentTagName);\n }\n return getHeadingRecursive(parent);\n };\n return getHeadingRecursive(element);\n }\n /**\n * Remove an element from the DOM.\n * @private\n * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} [element]\n * @return {void}\n */\n function removeElement(element) {\n if (element && element.parentNode) {\n element.parentNode.removeChild(element);\n }\n }\n /**\n * Remove all child nodes from an element.\n * @private\n * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} [element]\n * @return {void}\n */\n function removeChildNodes(element) {\n while (element.lastChild) {\n element.removeChild(element.lastChild);\n }\n }\n /**\n * Utility function. Reverses child nodes of a DOM element.\n * @private\n */\n function reverseChildNodes(node) {\n let i = node.childNodes.length;\n while (i--) {\n node.appendChild(node.childNodes[i]);\n }\n }\n /**\n * Used for aria-label attributes, painting on a canvas will fail if the\n * text contains tags.\n * @private\n */\n function stripHTMLTagsFromString(str, isForExport = false) {\n return (typeof str === 'string') ?\n (isForExport ?\n str.replace(/<\\/?[^>]+(>|$)/g, '') :\n str.replace(/<\\/?(?!\\s)[^>]+(>|$)/g, '')) : str;\n }\n /**\n * Utility function for hiding an element visually, but still keeping it\n * available to screen reader users.\n * @private\n */\n function visuallyHideElement(element) {\n css(element, {\n position: 'absolute',\n width: '1px',\n height: '1px',\n overflow: 'hidden',\n whiteSpace: 'nowrap',\n clip: 'rect(1px, 1px, 1px, 1px)',\n marginTop: '-3px',\n '-ms-filter': 'progid:DXImageTransform.Microsoft.Alpha(Opacity=1)',\n filter: 'alpha(opacity=1)',\n opacity: 0.01\n });\n }\n /* *\n *\n * Default Export\n *\n * */\n const HTMLUtilities = {\n addClass,\n cloneMouseEvent,\n cloneTouchEvent,\n escapeStringForHTML,\n getElement,\n getFakeMouseEvent,\n getHeadingTagNameForElement,\n removeChildNodes,\n removeClass,\n removeElement,\n reverseChildNodes,\n simulatedEventTarget,\n stripHTMLTagsFromString,\n visuallyHideElement\n };\n\n return HTMLUtilities;\n });\n _registerModule(_modules, 'Accessibility/A11yI18n.js', [_modules['Core/Templating.js'], _modules['Core/Utilities.js']], function (F, U) {\n /* *\n *\n * Accessibility module - internationalization support\n *\n * (c) 2010-2021 Highsoft AS\n * Author: Øystein Moseng\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { format } = F;\n const { getNestedProperty, pick } = U;\n /* *\n *\n * Composition\n *\n * */\n var A11yI18nComposition;\n (function (A11yI18nComposition) {\n /* *\n *\n * Declarations\n *\n * */\n /* *\n *\n * Constants\n *\n * */\n const composedMembers = [];\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * @private\n */\n function compose(ChartClass) {\n if (U.pushUnique(composedMembers, ChartClass)) {\n const chartProto = ChartClass.prototype;\n chartProto.langFormat = langFormat;\n }\n return ChartClass;\n }\n A11yI18nComposition.compose = compose;\n /**\n * i18n utility function. Format a single array or plural statement in a\n * format string. If the statement is not an array or plural statement,\n * returns the statement within brackets. Invalid array statements return\n * an empty string.\n *\n * @private\n * @function formatExtendedStatement\n * @param {string} statement\n * @param {Highcharts.Dictionary<*>} ctx\n * Context to apply to the format string.\n */\n function formatExtendedStatement(statement, ctx) {\n const eachStart = statement.indexOf('#each('), pluralStart = statement.indexOf('#plural('), indexStart = statement.indexOf('['), indexEnd = statement.indexOf(']');\n let arr, result;\n // Dealing with an each-function?\n if (eachStart > -1) {\n 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(',');\n let lenArg = Number(eachArguments[1]), len;\n result = '';\n arr = getNestedProperty(eachArguments[0], ctx);\n if (arr) {\n lenArg = isNaN(lenArg) ? arr.length : lenArg;\n len = lenArg < 0 ?\n arr.length + lenArg :\n Math.min(lenArg, arr.length); // Overshoot\n // Run through the array for the specified length\n for (let i = 0; i < len; ++i) {\n result += preEach + arr[i] + postEach;\n }\n }\n return result.length ? result : '';\n }\n // Dealing with a plural-function?\n if (pluralStart > -1) {\n const pluralEnd = (statement.slice(pluralStart).indexOf(')') + pluralStart), pluralStatement = statement.substring(pluralStart + 8, pluralEnd), pluralArguments = pluralStatement.split(','), num = Number(getNestedProperty(pluralArguments[0], ctx));\n switch (num) {\n case 0:\n result = pick(pluralArguments[4], pluralArguments[1]);\n break;\n case 1:\n result = pick(pluralArguments[2], pluralArguments[1]);\n break;\n case 2:\n result = pick(pluralArguments[3], pluralArguments[1]);\n break;\n default:\n result = pluralArguments[1];\n }\n return result ? stringTrim(result) : '';\n }\n // Array index\n if (indexStart > -1) {\n const arrayName = statement.substring(0, indexStart), ix = Number(statement.substring(indexStart + 1, indexEnd));\n let val;\n arr = getNestedProperty(arrayName, ctx);\n if (!isNaN(ix) && arr) {\n if (ix < 0) {\n val = arr[arr.length + ix];\n // Handle negative overshoot\n if (typeof val === 'undefined') {\n val = arr[0];\n }\n }\n else {\n val = arr[ix];\n // Handle positive overshoot\n if (typeof val === 'undefined') {\n val = arr[arr.length - 1];\n }\n }\n }\n return typeof val !== 'undefined' ? val : '';\n }\n // Standard substitution, delegate to format or similar\n return '{' + statement + '}';\n }\n /* eslint-disable max-len */\n /**\n * i18n formatting function. Extends Highcharts.format() functionality by\n * also handling arrays and plural conditionals. Arrays can be indexed as\n * follows:\n *\n * - Format: 'This is the first index: {myArray[0]}. The last: {myArray[-1]}.'\n *\n * - Context: { myArray: [0, 1, 2, 3, 4, 5] }\n *\n * - Result: 'This is the first index: 0. The last: 5.'\n *\n *\n * They can also be iterated using the #each() function. This will repeat\n * the contents of the bracket expression for each element. Example:\n *\n * - Format: 'List contains: {#each(myArray)cm }'\n *\n * - Context: { myArray: [0, 1, 2] }\n *\n * - Result: 'List contains: 0cm 1cm 2cm '\n *\n *\n * The #each() function optionally takes a length parameter. If positive,\n * this parameter specifies the max number of elements to iterate through.\n * If negative, the function will subtract the number from the length of the\n * array. Use this to stop iterating before the array ends. Example:\n *\n * - Format: 'List contains: {#each(myArray, -1) }and {myArray[-1]}.'\n *\n * - Context: { myArray: [0, 1, 2, 3] }\n *\n * - Result: 'List contains: 0, 1, 2, and 3.'\n *\n *\n * Use the #plural() function to pick a string depending on whether or not a\n * context object is 1. Arguments are #plural(obj, plural, singular).\n * Example:\n *\n * - Format: 'Has {numPoints} {#plural(numPoints, points, point}.'\n *\n * - Context: { numPoints: 5 }\n *\n * - Result: 'Has 5 points.'\n *\n *\n * Optionally there are additional parameters for dual and none:\n * #plural(obj, plural, singular, dual, none). Example:\n *\n * - Format: 'Has {#plural(numPoints, many points, one point, two points,\n * none}.'\n *\n * - Context: { numPoints: 2 }\n *\n * - Result: 'Has two points.'\n *\n *\n * The dual or none parameters will take precedence if they are supplied.\n *\n * @requires modules/accessibility\n *\n * @function Highcharts.i18nFormat\n *\n * @param {string} formatString\n * The string to format.\n *\n * @param {Highcharts.Dictionary<*>} context\n * Context to apply to the format string.\n *\n * @param {Highcharts.Chart} chart\n * A `Chart` instance with a time object and numberFormatter, passed on to\n * format().\n *\n * @deprecated\n *\n * @return {string}\n * The formatted string.\n */\n function i18nFormat(formatString, context, chart) {\n const getFirstBracketStatement = (sourceStr, offset) => {\n const str = sourceStr.slice(offset || 0), startBracket = str.indexOf('{'), endBracket = str.indexOf('}');\n if (startBracket > -1 && endBracket > startBracket) {\n return {\n statement: str.substring(startBracket + 1, endBracket),\n begin: offset + startBracket + 1,\n end: offset + endBracket\n };\n }\n }, tokens = [];\n let bracketRes, constRes, cursor = 0;\n // Tokenize format string into bracket statements and constants\n do {\n bracketRes = getFirstBracketStatement(formatString, cursor);\n constRes = formatString.substring(cursor, bracketRes && bracketRes.begin - 1);\n // If we have constant content before this bracket statement, add it\n if (constRes.length) {\n tokens.push({\n value: constRes,\n type: 'constant'\n });\n }\n // Add the bracket statement\n if (bracketRes) {\n tokens.push({\n value: bracketRes.statement,\n type: 'statement'\n });\n }\n cursor = bracketRes ? bracketRes.end + 1 : cursor + 1;\n } while (bracketRes);\n // Perform the formatting. The formatArrayStatement function returns\n // the statement in brackets if it is not an array statement, which\n // means it gets picked up by format below.\n tokens.forEach((token) => {\n if (token.type === 'statement') {\n token.value = formatExtendedStatement(token.value, context);\n }\n });\n // Join string back together and pass to format to pick up non-array\n // statements.\n return format(tokens.reduce((acc, cur) => acc + cur.value, ''), context, chart);\n }\n A11yI18nComposition.i18nFormat = i18nFormat;\n /* eslint-enable max-len */\n /**\n * Apply context to a format string from lang options of the chart.\n *\n * @requires modules/accessibility\n *\n * @function Highcharts.Chart#langFormat\n *\n * @param {string} langKey\n * Key (using dot notation) into lang option structure.\n *\n * @param {Highcharts.Dictionary<*>} context\n * Context to apply to the format string.\n *\n * @return {string}\n * The formatted string.\n */\n function langFormat(langKey, context) {\n const keys = langKey.split('.');\n let formatString = this.options.lang, i = 0;\n for (; i < keys.length; ++i) {\n formatString = formatString && formatString[keys[i]];\n }\n return typeof formatString === 'string' ?\n i18nFormat(formatString, context, this) : '';\n }\n /**\n * @private\n * @function stringTrim\n *\n * @param {string} str\n * The input string\n *\n * @return {string}\n * The trimmed string\n */\n function stringTrim(str) {\n return str.trim && str.trim() || str.replace(/^\\s+|\\s+$/g, '');\n }\n })(A11yI18nComposition || (A11yI18nComposition = {}));\n /* *\n *\n * Default Export\n *\n * */\n\n return A11yI18nComposition;\n });\n _registerModule(_modules, 'Accessibility/Utils/ChartUtilities.js', [_modules['Core/Globals.js'], _modules['Accessibility/Utils/HTMLUtilities.js'], _modules['Core/Utilities.js']], function (H, HU, U) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Utils for dealing with charts.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { doc } = H;\n const { stripHTMLTagsFromString: stripHTMLTags } = HU;\n const { defined, find, fireEvent } = U;\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Fire an event on an element that is either wrapped by Highcharts,\n * or a DOM element.\n * @private\n */\n function fireEventOnWrappedOrUnwrappedElement(el, eventObject) {\n const type = eventObject.type;\n const hcEvents = el.hcEvents;\n if (!!doc.createEvent &&\n (el.dispatchEvent || el.fireEvent)) {\n if (el.dispatchEvent) {\n el.dispatchEvent(eventObject);\n }\n else {\n el.fireEvent(type, eventObject);\n }\n }\n else if (hcEvents && hcEvents[type]) {\n fireEvent(el, type, eventObject);\n }\n else if (el.element) {\n fireEventOnWrappedOrUnwrappedElement(el.element, eventObject);\n }\n }\n /**\n * @private\n */\n function getChartTitle(chart) {\n return stripHTMLTags(chart.options.title.text ||\n chart.langFormat('accessibility.defaultChartTitle', { chart: chart }), chart.renderer.forExport);\n }\n /**\n * Return string with the axis name/title.\n * @private\n */\n function getAxisDescription(axis) {\n return axis && (axis.options.accessibility?.description ||\n axis.axisTitle?.textStr ||\n axis.options.id ||\n axis.categories && 'categories' ||\n axis.dateTime && 'Time' ||\n 'values');\n }\n /**\n * Return string with text description of the axis range.\n * @private\n * @param {Highcharts.Axis} axis\n * The axis to get range desc of.\n * @return {string}\n * A string with the range description for the axis.\n */\n function getAxisRangeDescription(axis) {\n const axisOptions = axis.options || {};\n // Handle overridden range description\n if (axisOptions.accessibility &&\n typeof axisOptions.accessibility.rangeDescription !== 'undefined') {\n return axisOptions.accessibility.rangeDescription;\n }\n // Handle category axes\n if (axis.categories) {\n return getCategoryAxisRangeDesc(axis);\n }\n // Use time range, not from-to?\n if (axis.dateTime && (axis.min === 0 || axis.dataMin === 0)) {\n return getAxisTimeLengthDesc(axis);\n }\n // Just use from and to.\n // We have the range and the unit to use, find the desc format\n return getAxisFromToDescription(axis);\n }\n /**\n * Describe the range of a category axis.\n * @private\n */\n function getCategoryAxisRangeDesc(axis) {\n const chart = axis.chart;\n if (axis.dataMax && axis.dataMin) {\n return chart.langFormat('accessibility.axis.rangeCategories', {\n chart: chart,\n axis: axis,\n numCategories: axis.dataMax - axis.dataMin + 1\n });\n }\n return '';\n }\n /**\n * Describe the length of the time window shown on an axis.\n * @private\n */\n function getAxisTimeLengthDesc(axis) {\n const chart = axis.chart, range = {}, min = axis.dataMin || axis.min || 0, max = axis.dataMax || axis.max || 0;\n let rangeUnit = 'Seconds';\n range.Seconds = (max - min) / 1000;\n range.Minutes = range.Seconds / 60;\n range.Hours = range.Minutes / 60;\n range.Days = range.Hours / 24;\n ['Minutes', 'Hours', 'Days'].forEach(function (unit) {\n if (range[unit] > 2) {\n rangeUnit = unit;\n }\n });\n const rangeValue = range[rangeUnit].toFixed(rangeUnit !== 'Seconds' &&\n rangeUnit !== 'Minutes' ? 1 : 0 // Use decimals for days/hours\n );\n // We have the range and the unit to use, find the desc format\n return chart.langFormat('accessibility.axis.timeRange' + rangeUnit, {\n chart: chart,\n axis: axis,\n range: rangeValue.replace('.0', '')\n });\n }\n /**\n * Describe an axis from-to range.\n * @private\n */\n function getAxisFromToDescription(axis) {\n const chart = axis.chart, options = chart.options, dateRangeFormat = (options &&\n options.accessibility &&\n options.accessibility.screenReaderSection.axisRangeDateFormat ||\n ''), extremes = {\n min: axis.dataMin || axis.min || 0,\n max: axis.dataMax || axis.max || 0\n }, format = function (key) {\n return axis.dateTime ?\n chart.time.dateFormat(dateRangeFormat, extremes[key]) :\n extremes[key].toString();\n };\n return chart.langFormat('accessibility.axis.rangeFromTo', {\n chart: chart,\n axis: axis,\n rangeFrom: format('min'),\n rangeTo: format('max')\n });\n }\n /**\n * Get the DOM element for the first point in the series.\n * @private\n * @param {Highcharts.Series} series\n * The series to get element for.\n * @return {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement|undefined}\n * The DOM element for the point.\n */\n function getSeriesFirstPointElement(series) {\n if (series.points && series.points.length) {\n const firstPointWithGraphic = find(series.points, (p) => !!p.graphic);\n return (firstPointWithGraphic &&\n firstPointWithGraphic.graphic &&\n firstPointWithGraphic.graphic.element);\n }\n }\n /**\n * Get the DOM element for the series that we put accessibility info on.\n * @private\n * @param {Highcharts.Series} series\n * The series to get element for.\n * @return {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement|undefined}\n * The DOM element for the series\n */\n function getSeriesA11yElement(series) {\n const firstPointEl = getSeriesFirstPointElement(series);\n return (firstPointEl &&\n firstPointEl.parentNode || series.graph &&\n series.graph.element || series.group &&\n series.group.element); // Could be tracker series depending on series type\n }\n /**\n * Remove aria-hidden from element. Also unhides parents of the element, and\n * hides siblings that are not explicitly unhidden.\n * @private\n */\n function unhideChartElementFromAT(chart, element) {\n element.setAttribute('aria-hidden', false);\n if (element === chart.renderTo ||\n !element.parentNode ||\n element.parentNode === doc.body // #16126: Full screen printing\n ) {\n return;\n }\n // Hide siblings unless their hidden state is already explicitly set\n Array.prototype.forEach.call(element.parentNode.childNodes, function (node) {\n if (!node.hasAttribute('aria-hidden')) {\n node.setAttribute('aria-hidden', true);\n }\n });\n // Repeat for parent\n unhideChartElementFromAT(chart, element.parentNode);\n }\n /**\n * Hide series from screen readers.\n * @private\n */\n function hideSeriesFromAT(series) {\n const seriesEl = getSeriesA11yElement(series);\n if (seriesEl) {\n seriesEl.setAttribute('aria-hidden', true);\n }\n }\n /**\n * Get series objects by series name.\n * @private\n */\n function getSeriesFromName(chart, name) {\n if (!name) {\n return chart.series;\n }\n return (chart.series || []).filter(function (s) {\n return s.name === name;\n });\n }\n /**\n * Get point in a series from x/y values.\n * @private\n */\n function getPointFromXY(series, x, y) {\n let i = series.length, res;\n while (i--) {\n res = find(series[i].points || [], function (p) {\n return p.x === x && p.y === y;\n });\n if (res) {\n return res;\n }\n }\n }\n /**\n * Get relative position of point on an x/y axis from 0 to 1.\n * @private\n */\n function getRelativePointAxisPosition(axis, point) {\n if (!defined(axis.dataMin) || !defined(axis.dataMax)) {\n return 0;\n }\n const axisStart = axis.toPixels(axis.dataMin), axisEnd = axis.toPixels(axis.dataMax), \n // We have to use pixel position because of axis breaks, log axis etc.\n positionProp = axis.coll === 'xAxis' ? 'x' : 'y', pointPos = axis.toPixels(point[positionProp] || 0);\n return (pointPos - axisStart) / (axisEnd - axisStart);\n }\n /**\n * Get relative position of point on an x/y axis from 0 to 1.\n * @private\n */\n function scrollAxisToPoint(point) {\n const xAxis = point.series.xAxis, yAxis = point.series.yAxis, axis = (xAxis && xAxis.scrollbar ? xAxis : yAxis), scrollbar = (axis && axis.scrollbar);\n if (scrollbar && defined(scrollbar.to) && defined(scrollbar.from)) {\n const range = scrollbar.to - scrollbar.from;\n const pos = getRelativePointAxisPosition(axis, point);\n scrollbar.updatePosition(pos - range / 2, pos + range / 2);\n fireEvent(scrollbar, 'changed', {\n from: scrollbar.from,\n to: scrollbar.to,\n trigger: 'scrollbar',\n DOMEvent: null\n });\n }\n }\n /* *\n *\n * Default Export\n *\n * */\n const ChartUtilities = {\n fireEventOnWrappedOrUnwrappedElement,\n getChartTitle,\n getAxisDescription,\n getAxisRangeDescription,\n getPointFromXY,\n getSeriesFirstPointElement,\n getSeriesFromName,\n getSeriesA11yElement,\n unhideChartElementFromAT,\n hideSeriesFromAT,\n scrollAxisToPoint\n };\n\n return ChartUtilities;\n });\n _registerModule(_modules, 'Accessibility/Utils/DOMElementProvider.js', [_modules['Core/Globals.js'], _modules['Accessibility/Utils/HTMLUtilities.js']], function (H, HU) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Class that can keep track of elements added to DOM and clean them up on\n * destroy.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { doc } = H;\n const { removeElement } = HU;\n /* *\n *\n * Class\n *\n * */\n /**\n * @private\n */\n class DOMElementProvider {\n /* *\n *\n * Constructor\n *\n * */\n constructor() {\n this.elements = [];\n }\n /**\n * Create an element and keep track of it for later removal.\n * Same args as document.createElement\n * @private\n */\n createElement() {\n const el = doc.createElement.apply(doc, arguments);\n this.elements.push(el);\n return el;\n }\n /**\n * Destroy all created elements, removing them from the DOM.\n * @private\n */\n destroyCreatedElements() {\n this.elements.forEach(function (element) {\n removeElement(element);\n });\n this.elements = [];\n }\n }\n /* *\n *\n * Default Export\n *\n * */\n\n return DOMElementProvider;\n });\n _registerModule(_modules, 'Accessibility/Utils/EventProvider.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Class that can keep track of events added, and clean them up on destroy.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { addEvent } = U;\n /* *\n *\n * Class\n *\n * */\n /**\n * @private\n */\n class EventProvider {\n /* *\n *\n * Constructor\n *\n * */\n constructor() {\n this.eventRemovers = [];\n }\n /**\n * Add an event to an element and keep track of it for later removal.\n * Same args as Highcharts.addEvent.\n * @private\n */\n addEvent() {\n const remover = addEvent.apply(H, arguments);\n this.eventRemovers.push(remover);\n return remover;\n }\n /**\n * Remove all added events.\n * @private\n */\n removeAddedEvents() {\n this.eventRemovers.forEach((remover) => remover());\n this.eventRemovers = [];\n }\n }\n /* *\n *\n * Default Export\n *\n * */\n\n return EventProvider;\n });\n _registerModule(_modules, 'Accessibility/AccessibilityComponent.js', [_modules['Accessibility/Utils/ChartUtilities.js'], _modules['Accessibility/Utils/DOMElementProvider.js'], _modules['Accessibility/Utils/EventProvider.js'], _modules['Accessibility/Utils/HTMLUtilities.js'], _modules['Core/Utilities.js']], function (CU, DOMElementProvider, EventProvider, HU, U) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Accessibility component class definition\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { fireEventOnWrappedOrUnwrappedElement } = CU;\n const { getFakeMouseEvent } = HU;\n const { extend } = U;\n /* *\n *\n * Class\n *\n * */\n /**\n * The AccessibilityComponent base class, representing a part of the chart that\n * has accessibility logic connected to it. This class can be inherited from to\n * create a custom accessibility component for a chart.\n *\n * Components should take care to destroy added elements and unregister event\n * handlers on destroy. This is handled automatically if using this.addEvent and\n * this.createElement.\n *\n * @sample highcharts/accessibility/custom-component\n * Custom accessibility component\n *\n * @requires module:modules/accessibility\n * @class\n * @name Highcharts.AccessibilityComponent\n */\n class AccessibilityComponent {\n constructor() {\n /* *\n *\n * Properties\n *\n * */\n this.chart = void 0;\n this.domElementProvider = void 0;\n this.eventProvider = void 0;\n this.keyCodes = void 0;\n this.proxyProvider = void 0;\n }\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Initialize the class\n * @private\n * @param {Highcharts.Chart} chart The chart object\n * @param {Highcharts.ProxyProvider} proxyProvider The proxy provider of the accessibility module\n */\n initBase(chart, proxyProvider) {\n this.chart = chart;\n this.eventProvider = new EventProvider();\n this.domElementProvider = new DOMElementProvider();\n this.proxyProvider = proxyProvider;\n // Key code enum for common keys\n this.keyCodes = {\n left: 37,\n right: 39,\n up: 38,\n down: 40,\n enter: 13,\n space: 32,\n esc: 27,\n tab: 9,\n pageUp: 33,\n pageDown: 34,\n end: 35,\n home: 36\n };\n }\n /**\n * Add an event to an element and keep track of it for later removal.\n * See EventProvider for details.\n * @private\n */\n addEvent(el, type, fn, options) {\n return this.eventProvider.addEvent(el, type, fn, options);\n }\n /**\n * Create an element and keep track of it for later removal.\n * See DOMElementProvider for details.\n * @private\n */\n createElement(tagName, options) {\n return this.domElementProvider.createElement(tagName, options);\n }\n /**\n * Fire a fake click event on an element. It is useful to have this on\n * AccessibilityComponent for users of custom components.\n */\n fakeClickEvent(el) {\n const fakeEvent = getFakeMouseEvent('click');\n fireEventOnWrappedOrUnwrappedElement(el, fakeEvent);\n }\n /**\n * Remove traces of the component.\n * @private\n */\n destroyBase() {\n this.domElementProvider.destroyCreatedElements();\n this.eventProvider.removeAddedEvents();\n }\n }\n extend(AccessibilityComponent.prototype, \n /** @lends Highcharts.AccessibilityComponent */\n {\n /**\n * Called on component initialization.\n */\n init() { },\n /**\n * Get keyboard navigation handler for this component.\n * @private\n */\n getKeyboardNavigation: function () { },\n /**\n * Called on updates to the chart, including options changes.\n * Note that this is also called on first render of chart.\n */\n onChartUpdate() { },\n /**\n * Called on every chart render.\n */\n onChartRender() { },\n /**\n * Called when accessibility is disabled or chart is destroyed.\n */\n destroy() { }\n });\n /* *\n *\n * Default Export\n *\n * */\n\n return AccessibilityComponent;\n });\n _registerModule(_modules, 'Accessibility/KeyboardNavigationHandler.js', [_modules['Core/Utilities.js']], function (U) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Keyboard navigation handler base class definition\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { find } = U;\n /* *\n *\n * Class\n *\n * */\n /**\n * Define a keyboard navigation handler for use with a\n * Highcharts.AccessibilityComponent instance. This functions as an abstraction\n * layer for keyboard navigation, and defines a map of keyCodes to handler\n * functions.\n *\n * @requires module:modules/accessibility\n *\n * @sample highcharts/accessibility/custom-component\n * Custom accessibility component\n *\n * @class\n * @name Highcharts.KeyboardNavigationHandler\n *\n * @param {Highcharts.Chart} chart\n * The chart this module should act on.\n *\n * @param {Highcharts.KeyboardNavigationHandlerOptionsObject} options\n * Options for the keyboard navigation handler.\n */\n class KeyboardNavigationHandler {\n /* *\n *\n * Constructor\n *\n * */\n constructor(chart, options) {\n this.chart = chart;\n this.keyCodeMap = options.keyCodeMap || [];\n this.validate = options.validate;\n this.init = options.init;\n this.terminate = options.terminate;\n // Response enum\n this.response = {\n success: 1,\n prev: 2,\n next: 3,\n noHandler: 4,\n fail: 5 // Handler failed\n };\n }\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Find handler function(s) for key code in the keyCodeMap and run it.\n *\n * @function KeyboardNavigationHandler#run\n * @param {global.KeyboardEvent} e\n * @return {number} Returns a response code indicating whether the run was\n * a success/fail/unhandled, or if we should move to next/prev module.\n */\n run(e) {\n const keyCode = e.which || e.keyCode;\n let response = this.response.noHandler;\n const handlerCodeSet = find(this.keyCodeMap, function (codeSet) {\n return codeSet[0].indexOf(keyCode) > -1;\n });\n if (handlerCodeSet) {\n response = handlerCodeSet[1].call(this, keyCode, e);\n }\n else if (keyCode === 9) {\n // Default tab handler, move to next/prev module\n response = this.response[e.shiftKey ? 'prev' : 'next'];\n }\n return response;\n }\n }\n /* *\n *\n * Default Export\n *\n * */\n /* *\n *\n * API Declarations\n *\n * */\n /**\n * Options for the keyboard navigation handler.\n *\n * @interface Highcharts.KeyboardNavigationHandlerOptionsObject\n */ /**\n * An array containing pairs of an array of keycodes, mapped to a handler\n * function. When the keycode is received, the handler is called with the\n * keycode as parameter.\n * @name Highcharts.KeyboardNavigationHandlerOptionsObject#keyCodeMap\n * @type {Array, Function>>}\n */ /**\n * Function to run on initialization of module.\n * @name Highcharts.KeyboardNavigationHandlerOptionsObject#init\n * @type {Function}\n */ /**\n * Function to run before moving to next/prev module. Receives moving direction\n * as parameter: +1 for next, -1 for previous.\n * @name Highcharts.KeyboardNavigationHandlerOptionsObject#terminate\n * @type {Function|undefined}\n */ /**\n * Function to run to validate module. Should return false if module should not\n * run, true otherwise. Receives chart as parameter.\n * @name Highcharts.KeyboardNavigationHandlerOptionsObject#validate\n * @type {Function|undefined}\n */\n (''); // keeps doclets above in JS file\n\n return KeyboardNavigationHandler;\n });\n _registerModule(_modules, 'Accessibility/Components/ContainerComponent.js', [_modules['Accessibility/AccessibilityComponent.js'], _modules['Accessibility/KeyboardNavigationHandler.js'], _modules['Accessibility/Utils/ChartUtilities.js'], _modules['Core/Globals.js'], _modules['Accessibility/Utils/HTMLUtilities.js']], function (AccessibilityComponent, KeyboardNavigationHandler, CU, H, HU) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Accessibility component for chart container.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { unhideChartElementFromAT, getChartTitle } = CU;\n const { doc } = H;\n const { stripHTMLTagsFromString: stripHTMLTags } = HU;\n /**\n * The ContainerComponent class\n *\n * @private\n * @class\n * @name Highcharts.ContainerComponent\n */\n class ContainerComponent extends AccessibilityComponent {\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Called on first render/updates to the chart, including options changes.\n */\n onChartUpdate() {\n this.handleSVGTitleElement();\n this.setSVGContainerLabel();\n this.setGraphicContainerAttrs();\n this.setRenderToAttrs();\n this.makeCreditsAccessible();\n }\n /**\n * @private\n */\n handleSVGTitleElement() {\n const chart = this.chart, titleId = 'highcharts-title-' + chart.index, titleContents = stripHTMLTags(chart.langFormat('accessibility.svgContainerTitle', {\n chartTitle: getChartTitle(chart)\n }));\n if (titleContents.length) {\n const titleElement = this.svgTitleElement =\n this.svgTitleElement || doc.createElementNS('http://www.w3.org/2000/svg', 'title');\n titleElement.textContent = titleContents;\n titleElement.id = titleId;\n chart.renderTo.insertBefore(titleElement, chart.renderTo.firstChild);\n }\n }\n /**\n * @private\n */\n setSVGContainerLabel() {\n const chart = this.chart, svgContainerLabel = chart.langFormat('accessibility.svgContainerLabel', {\n chartTitle: getChartTitle(chart)\n });\n if (chart.renderer.box && svgContainerLabel.length) {\n chart.renderer.box.setAttribute('aria-label', svgContainerLabel);\n }\n }\n /**\n * @private\n */\n setGraphicContainerAttrs() {\n const chart = this.chart, label = chart.langFormat('accessibility.graphicContainerLabel', {\n chartTitle: getChartTitle(chart)\n });\n if (label.length) {\n chart.container.setAttribute('aria-label', label);\n }\n }\n /**\n * Set attributes on the chart container element.\n * @private\n */\n setRenderToAttrs() {\n const chart = this.chart, shouldHaveLandmark = chart.options.accessibility\n .landmarkVerbosity !== 'disabled', containerLabel = chart.langFormat('accessibility.chartContainerLabel', {\n title: getChartTitle(chart),\n chart: chart\n });\n if (containerLabel) {\n chart.renderTo.setAttribute('role', shouldHaveLandmark ? 'region' : 'group');\n chart.renderTo.setAttribute('aria-label', containerLabel);\n }\n }\n /**\n * @private\n */\n makeCreditsAccessible() {\n const chart = this.chart, credits = chart.credits;\n if (credits) {\n if (credits.textStr) {\n credits.element.setAttribute('aria-label', chart.langFormat('accessibility.credits', {\n creditsStr: stripHTMLTags(credits.textStr, chart.renderer.forExport)\n }));\n }\n unhideChartElementFromAT(chart, credits.element);\n }\n }\n /**\n * Empty handler to just set focus on chart\n * @private\n */\n getKeyboardNavigation() {\n const chart = this.chart;\n return new KeyboardNavigationHandler(chart, {\n keyCodeMap: [],\n validate: function () {\n return true;\n },\n init: function () {\n const a11y = chart.accessibility;\n if (a11y) {\n a11y.keyboardNavigation.tabindexContainer.focus();\n }\n }\n });\n }\n /**\n * Accessibility disabled/chart destroyed.\n */\n destroy() {\n this.chart.renderTo.setAttribute('aria-hidden', true);\n }\n }\n /* *\n *\n * Default Export\n *\n * */\n\n return ContainerComponent;\n });\n _registerModule(_modules, 'Accessibility/FocusBorder.js', [_modules['Core/Utilities.js']], function (U) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Extend SVG and Chart classes with focus border capabilities.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { addEvent, pick } = U;\n /* *\n *\n * Composition\n *\n * */\n var FocusBorderComposition;\n (function (FocusBorderComposition) {\n /* *\n *\n * Declarations\n *\n * */\n /* *\n *\n * Constants\n *\n * */\n const composedMembers = [];\n // Attributes that trigger a focus border update\n const svgElementBorderUpdateTriggers = [\n 'x', 'y', 'transform', 'width', 'height', 'r', 'd', 'stroke-width'\n ];\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * @private\n */\n function compose(ChartClass, SVGElementClass) {\n if (U.pushUnique(composedMembers, ChartClass)) {\n const chartProto = ChartClass.prototype;\n chartProto.renderFocusBorder = chartRenderFocusBorder;\n chartProto.setFocusToElement = chartSetFocusToElement;\n }\n if (U.pushUnique(composedMembers, SVGElementClass)) {\n const svgElementProto = SVGElementClass.prototype;\n svgElementProto.addFocusBorder = svgElementAddFocusBorder;\n svgElementProto.removeFocusBorder = svgElementRemoveFocusBorder;\n }\n }\n FocusBorderComposition.compose = compose;\n /**\n * Redraws the focus border on the currently focused element.\n *\n * @private\n * @function Highcharts.Chart#renderFocusBorder\n */\n function chartRenderFocusBorder() {\n const focusElement = this.focusElement, focusBorderOptions = this.options.accessibility.keyboardNavigation.focusBorder;\n if (focusElement) {\n focusElement.removeFocusBorder();\n if (focusBorderOptions.enabled) {\n focusElement.addFocusBorder(focusBorderOptions.margin, {\n stroke: focusBorderOptions.style.color,\n strokeWidth: focusBorderOptions.style.lineWidth,\n r: focusBorderOptions.style.borderRadius\n });\n }\n }\n }\n /**\n * Set chart's focus to an SVGElement. Calls focus() on it, and draws the\n * focus border. This is used by multiple components.\n *\n * @private\n * @function Highcharts.Chart#setFocusToElement\n *\n * @param {Highcharts.SVGElement} svgElement\n * Element to draw the border around.\n *\n * @param {SVGDOMElement|HTMLDOMElement} [focusElement]\n * If supplied, it draws the border around svgElement and sets the focus to\n * focusElement.\n */\n function chartSetFocusToElement(svgElement, focusElement) {\n const focusBorderOptions = this.options.accessibility.keyboardNavigation.focusBorder, browserFocusElement = focusElement || svgElement.element;\n // Set browser focus if possible\n if (browserFocusElement &&\n browserFocusElement.focus) {\n // If there is no focusin-listener, add one to work around Edge\n // where Narrator is not reading out points despite calling focus().\n if (!(browserFocusElement.hcEvents &&\n browserFocusElement.hcEvents.focusin)) {\n addEvent(browserFocusElement, 'focusin', function () { });\n }\n browserFocusElement.focus();\n // Hide default focus ring\n if (focusBorderOptions.hideBrowserFocusOutline) {\n browserFocusElement.style.outline = 'none';\n }\n }\n if (this.focusElement) {\n this.focusElement.removeFocusBorder();\n }\n this.focusElement = svgElement;\n this.renderFocusBorder();\n }\n /**\n * Add hook to destroy focus border if SVG element is destroyed, unless\n * hook already exists.\n * @private\n * @param el Element to add destroy hook to\n */\n function svgElementAddDestroyFocusBorderHook(el) {\n if (el.focusBorderDestroyHook) {\n return;\n }\n const origDestroy = el.destroy;\n el.destroy = function () {\n if (el.focusBorder && el.focusBorder.destroy) {\n el.focusBorder.destroy();\n }\n return origDestroy.apply(el, arguments);\n };\n el.focusBorderDestroyHook = origDestroy;\n }\n /**\n * Add focus border functionality to SVGElements. Draws a new rect on top of\n * element around its bounding box. This is used by multiple components.\n *\n * @private\n * @function Highcharts.SVGElement#addFocusBorder\n *\n * @param {number} margin\n *\n * @param {SVGAttributes} attribs\n */\n function svgElementAddFocusBorder(margin, attribs) {\n // Allow updating by just adding new border\n if (this.focusBorder) {\n this.removeFocusBorder();\n }\n // Add the border rect\n const bb = this.getBBox(), pad = pick(margin, 3), parent = this.parentGroup, scaleX = this.scaleX || parent && parent.scaleX, scaleY = this.scaleY || parent && parent.scaleY, oneDefined = scaleX ? !scaleY : scaleY, scaleBoth = oneDefined ? Math.abs(scaleX || scaleY || 1) :\n (Math.abs(scaleX || 1) + Math.abs(scaleY || 1)) / 2;\n bb.x += this.translateX ? this.translateX : 0;\n bb.y += this.translateY ? this.translateY : 0;\n let borderPosX = bb.x - pad, borderPosY = bb.y - pad, borderWidth = bb.width + 2 * pad, borderHeight = bb.height + 2 * pad;\n /**\n * For text elements, apply x and y offset, #11397.\n * @private\n */\n function getTextAnchorCorrection(text) {\n let posXCorrection = 0, posYCorrection = 0;\n if (text.attr('text-anchor') === 'middle') {\n posXCorrection = posYCorrection = 0.5;\n }\n else if (!text.rotation) {\n posYCorrection = 0.75;\n }\n else {\n posXCorrection = 0.25;\n }\n return {\n x: posXCorrection,\n y: posYCorrection\n };\n }\n const isLabel = !!this.text;\n if (this.element.nodeName === 'text' || isLabel) {\n const isRotated = !!this.rotation;\n const correction = !isLabel ? getTextAnchorCorrection(this) :\n {\n x: isRotated ? 1 : 0,\n y: 0\n };\n const attrX = +this.attr('x');\n const attrY = +this.attr('y');\n if (!isNaN(attrX)) {\n borderPosX = attrX - (bb.width * correction.x) - pad;\n }\n if (!isNaN(attrY)) {\n borderPosY = attrY - (bb.height * correction.y) - pad;\n }\n if (isLabel && isRotated) {\n const temp = borderWidth;\n borderWidth = borderHeight;\n borderHeight = temp;\n if (!isNaN(attrX)) {\n borderPosX = attrX - (bb.height * correction.x) - pad;\n }\n if (!isNaN(attrY)) {\n borderPosY = attrY - (bb.width * correction.y) - pad;\n }\n }\n }\n this.focusBorder = this.renderer.rect(borderPosX, borderPosY, borderWidth, borderHeight, parseInt((attribs && attribs.r || 0).toString(), 10) / scaleBoth)\n .addClass('highcharts-focus-border')\n .attr({\n zIndex: 99\n })\n .add(parent);\n if (!this.renderer.styledMode) {\n this.focusBorder.attr({\n stroke: attribs && attribs.stroke,\n 'stroke-width': (attribs && attribs.strokeWidth || 0) / scaleBoth\n });\n }\n avgElementAddUpdateFocusBorderHooks(this, margin, attribs);\n svgElementAddDestroyFocusBorderHook(this);\n }\n /**\n * Add hooks to update the focus border of an element when the element\n * size/position is updated, unless already added.\n * @private\n * @param el Element to add update hooks to\n * @param updateParams Parameters to pass through to addFocusBorder when updating.\n */\n function avgElementAddUpdateFocusBorderHooks(el, ...updateParams) {\n if (el.focusBorderUpdateHooks) {\n return;\n }\n el.focusBorderUpdateHooks = {};\n svgElementBorderUpdateTriggers.forEach((trigger) => {\n const setterKey = trigger + 'Setter';\n const origSetter = el[setterKey] || el._defaultSetter;\n el.focusBorderUpdateHooks[setterKey] = origSetter;\n el[setterKey] = function () {\n const ret = origSetter.apply(el, arguments);\n el.addFocusBorder.apply(el, updateParams);\n return ret;\n };\n });\n }\n /**\n * Remove hook from SVG element added by addDestroyFocusBorderHook, if\n * existing.\n * @private\n * @param el Element to remove destroy hook from\n */\n function svgElementRemoveDestroyFocusBorderHook(el) {\n if (!el.focusBorderDestroyHook) {\n return;\n }\n el.destroy = el.focusBorderDestroyHook;\n delete el.focusBorderDestroyHook;\n }\n /**\n * Add focus border functionality to SVGElements. Draws a new rect on top of\n * element around its bounding box. This is used by multiple components.\n * @private\n * @function Highcharts.SVGElement#removeFocusBorder\n */\n function svgElementRemoveFocusBorder() {\n svgElementRemoveUpdateFocusBorderHooks(this);\n svgElementRemoveDestroyFocusBorderHook(this);\n if (this.focusBorder) {\n this.focusBorder.destroy();\n delete this.focusBorder;\n }\n }\n /**\n * Remove hooks from SVG element added by addUpdateFocusBorderHooks, if\n * existing.\n * @private\n * @param el Element to remove update hooks from\n */\n function svgElementRemoveUpdateFocusBorderHooks(el) {\n if (!el.focusBorderUpdateHooks) {\n return;\n }\n Object.keys(el.focusBorderUpdateHooks).forEach((setterKey) => {\n const origSetter = el.focusBorderUpdateHooks[setterKey];\n if (origSetter === el._defaultSetter) {\n delete el[setterKey];\n }\n else {\n el[setterKey] = origSetter;\n }\n });\n delete el.focusBorderUpdateHooks;\n }\n })(FocusBorderComposition || (FocusBorderComposition = {}));\n /* *\n *\n * Default Export\n *\n * */\n\n return FocusBorderComposition;\n });\n _registerModule(_modules, 'Accessibility/Utils/Announcer.js', [_modules['Core/Renderer/HTML/AST.js'], _modules['Accessibility/Utils/DOMElementProvider.js'], _modules['Core/Globals.js'], _modules['Accessibility/Utils/HTMLUtilities.js'], _modules['Core/Utilities.js']], function (AST, DOMElementProvider, H, HU, U) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Create announcer to speak messages to screen readers and other AT.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { doc } = H;\n const { addClass, visuallyHideElement } = HU;\n const { attr } = U;\n /* *\n *\n * Class\n *\n * */\n class Announcer {\n /* *\n *\n * Constructor\n *\n * */\n constructor(chart, type) {\n this.chart = chart;\n this.domElementProvider = new DOMElementProvider();\n this.announceRegion = this.addAnnounceRegion(type);\n }\n /* *\n *\n * Functions\n *\n * */\n destroy() {\n this.domElementProvider.destroyCreatedElements();\n }\n announce(message) {\n AST.setElementHTML(this.announceRegion, message);\n // Delete contents after a little while to avoid user finding the live\n // region in the DOM.\n if (this.clearAnnouncementRegionTimer) {\n clearTimeout(this.clearAnnouncementRegionTimer);\n }\n this.clearAnnouncementRegionTimer = setTimeout(() => {\n this.announceRegion.innerHTML = AST.emptyHTML;\n delete this.clearAnnouncementRegionTimer;\n }, 3000);\n }\n addAnnounceRegion(type) {\n const chartContainer = (this.chart.announcerContainer || this.createAnnouncerContainer()), div = this.domElementProvider.createElement('div');\n attr(div, {\n 'aria-hidden': false,\n 'aria-live': type,\n 'aria-atomic': true\n });\n if (this.chart.styledMode) {\n addClass(div, 'highcharts-visually-hidden');\n }\n else {\n visuallyHideElement(div);\n }\n chartContainer.appendChild(div);\n return div;\n }\n createAnnouncerContainer() {\n const chart = this.chart, container = doc.createElement('div');\n attr(container, {\n 'aria-hidden': false,\n 'class': 'highcharts-announcer-container'\n });\n container.style.position = 'relative';\n chart.renderTo.insertBefore(container, chart.renderTo.firstChild);\n chart.announcerContainer = container;\n return container;\n }\n }\n /* *\n *\n * Default Export\n *\n * */\n\n return Announcer;\n });\n _registerModule(_modules, 'Accessibility/Components/AnnotationsA11y.js', [_modules['Accessibility/Utils/HTMLUtilities.js']], function (HTMLUtilities) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Annotations accessibility code.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { escapeStringForHTML, stripHTMLTagsFromString } = HTMLUtilities;\n /* *\n *\n * Functions\n *\n * */\n /**\n * Get list of all annotation labels in the chart.\n *\n * @private\n * @param {Highcharts.Chart} chart The chart to get annotation info on.\n * @return {Array} The labels, or empty array if none.\n */\n function getChartAnnotationLabels(chart) {\n const annotations = chart.annotations || [];\n return annotations.reduce((acc, cur) => {\n if (cur.options &&\n cur.options.visible !== false) {\n acc = acc.concat(cur.labels);\n }\n return acc;\n }, []);\n }\n /**\n * Get the text of an annotation label.\n *\n * @private\n * @param {Object} label The annotation label object\n * @return {string} The text in the label.\n */\n function getLabelText(label) {\n return ((label.options &&\n label.options.accessibility &&\n label.options.accessibility.description) ||\n (label.graphic &&\n label.graphic.text &&\n label.graphic.text.textStr) ||\n '');\n }\n /**\n * Describe an annotation label.\n *\n * @private\n * @param {Object} label The annotation label object to describe\n * @return {string} The description for the label.\n */\n function getAnnotationLabelDescription(label) {\n const a11yDesc = (label.options &&\n label.options.accessibility &&\n label.options.accessibility.description);\n if (a11yDesc) {\n return a11yDesc;\n }\n const chart = label.chart;\n const labelText = getLabelText(label);\n const points = label.points;\n const getAriaLabel = (point) => (point.graphic &&\n point.graphic.element &&\n point.graphic.element.getAttribute('aria-label') ||\n '');\n const getValueDesc = (point) => {\n const valDesc = (point.accessibility &&\n point.accessibility.valueDescription ||\n getAriaLabel(point));\n const seriesName = (point &&\n point.series.name ||\n '');\n return (seriesName ? seriesName + ', ' : '') + 'data point ' + valDesc;\n };\n const pointValueDescriptions = points\n .filter((p) => !!p.graphic) // Filter out mock points\n .map(getValueDesc)\n // Filter out points we can't describe\n .filter((desc) => !!desc);\n const numPoints = pointValueDescriptions.length;\n const pointsSelector = numPoints > 1 ?\n 'MultiplePoints' : numPoints ?\n 'SinglePoint' : 'NoPoints';\n const langFormatStr = ('accessibility.screenReaderSection.annotations.description' +\n pointsSelector);\n const context = {\n annotationText: labelText,\n annotation: label,\n numPoints: numPoints,\n annotationPoint: pointValueDescriptions[0],\n additionalAnnotationPoints: pointValueDescriptions.slice(1)\n };\n return chart.langFormat(langFormatStr, context);\n }\n /**\n * Return array of HTML strings for each annotation label in the chart.\n *\n * @private\n * @param {Highcharts.Chart} chart The chart to get annotation info on.\n * @return {Array} Array of strings with HTML content for each annotation label.\n */\n function getAnnotationListItems(chart) {\n const labels = getChartAnnotationLabels(chart);\n return labels.map((label) => {\n const desc = escapeStringForHTML(stripHTMLTagsFromString(getAnnotationLabelDescription(label), chart.renderer.forExport));\n return desc ? `
  • ${desc}
  • ` : '';\n });\n }\n /**\n * Return the annotation info for a chart as string.\n *\n * @private\n * @param {Highcharts.Chart} chart The chart to get annotation info on.\n * @return {string} String with HTML content or empty string if no annotations.\n */\n function getAnnotationsInfoHTML(chart) {\n const annotations = chart.annotations;\n if (!(annotations && annotations.length)) {\n return '';\n }\n const annotationItems = getAnnotationListItems(chart);\n return `
      ${annotationItems.join(' ')}
    `;\n }\n /**\n * Return the texts for the annotation(s) connected to a point, or empty array\n * if none.\n *\n * @private\n * @param {Highcharts.Point} point The data point to get the annotation info from.\n * @return {Array} Annotation texts\n */\n function getPointAnnotationTexts(point) {\n const labels = getChartAnnotationLabels(point.series.chart);\n const pointLabels = labels\n .filter((label) => label.points.indexOf(point) > -1);\n if (!pointLabels.length) {\n return [];\n }\n return pointLabels.map((label) => `${getLabelText(label)}`);\n }\n /* *\n *\n * Default Export\n *\n * */\n const AnnotationsA11y = {\n getAnnotationsInfoHTML,\n getAnnotationLabelDescription,\n getAnnotationListItems,\n getPointAnnotationTexts\n };\n\n return AnnotationsA11y;\n });\n _registerModule(_modules, 'Accessibility/Components/InfoRegionsComponent.js', [_modules['Accessibility/A11yI18n.js'], _modules['Accessibility/AccessibilityComponent.js'], _modules['Accessibility/Utils/Announcer.js'], _modules['Accessibility/Components/AnnotationsA11y.js'], _modules['Core/Renderer/HTML/AST.js'], _modules['Accessibility/Utils/ChartUtilities.js'], _modules['Core/Templating.js'], _modules['Core/Globals.js'], _modules['Accessibility/Utils/HTMLUtilities.js'], _modules['Core/Utilities.js']], function (A11yI18n, AccessibilityComponent, Announcer, AnnotationsA11y, AST, CU, F, H, HU, U) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Accessibility component for chart info region and table.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { getAnnotationsInfoHTML } = AnnotationsA11y;\n const { getAxisDescription, getAxisRangeDescription, getChartTitle, unhideChartElementFromAT } = CU;\n const { format } = F;\n const { doc } = H;\n const { addClass, getElement, getHeadingTagNameForElement, stripHTMLTagsFromString, visuallyHideElement } = HU;\n const { attr, pick } = U;\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * @private\n */\n function getTableSummary(chart) {\n return chart.langFormat('accessibility.table.tableSummary', { chart: chart });\n }\n /**\n * @private\n */\n function getTypeDescForMapChart(chart, formatContext) {\n return formatContext.mapTitle ?\n chart.langFormat('accessibility.chartTypes.mapTypeDescription', formatContext) :\n chart.langFormat('accessibility.chartTypes.unknownMap', formatContext);\n }\n /**\n * @private\n */\n function getTypeDescForCombinationChart(chart, formatContext) {\n return chart.langFormat('accessibility.chartTypes.combinationChart', formatContext);\n }\n /**\n * @private\n */\n function getTypeDescForEmptyChart(chart, formatContext) {\n return chart.langFormat('accessibility.chartTypes.emptyChart', formatContext);\n }\n /**\n * @private\n */\n function buildTypeDescriptionFromSeries(chart, types, context) {\n const firstType = types[0], typeExplaination = chart.langFormat('accessibility.seriesTypeDescriptions.' + firstType, context), multi = chart.series && chart.series.length < 2 ? 'Single' : 'Multiple';\n return (chart.langFormat('accessibility.chartTypes.' + firstType + multi, context) ||\n chart.langFormat('accessibility.chartTypes.default' + multi, context)) + (typeExplaination ? ' ' + typeExplaination : '');\n }\n /**\n * Return simplified explaination of chart type. Some types will not be\n * familiar to most users, but in those cases we try to add an explaination\n * of the type.\n *\n * @private\n * @function Highcharts.Chart#getTypeDescription\n * @param {Array} types The series types in this chart.\n * @return {string} The text description of the chart type.\n */\n function getTypeDescription(chart, types) {\n const firstType = types[0], firstSeries = chart.series && chart.series[0] || {}, mapTitle = chart.mapView && chart.mapView.geoMap &&\n chart.mapView.geoMap.title, formatContext = {\n numSeries: chart.series.length,\n numPoints: firstSeries.points && firstSeries.points.length,\n chart,\n mapTitle\n };\n if (!firstType) {\n return getTypeDescForEmptyChart(chart, formatContext);\n }\n if (firstType === 'map' || firstType === 'tiledwebmap') {\n return getTypeDescForMapChart(chart, formatContext);\n }\n if (chart.types.length > 1) {\n return getTypeDescForCombinationChart(chart, formatContext);\n }\n return buildTypeDescriptionFromSeries(chart, types, formatContext);\n }\n /**\n * @private\n */\n function stripEmptyHTMLTags(str) {\n return str.replace(/<(\\w+)[^>]*?>\\s*<\\/\\1>/g, '');\n }\n /* *\n *\n * Class\n *\n * */\n /**\n * The InfoRegionsComponent class\n *\n * @private\n * @class\n * @name Highcharts.InfoRegionsComponent\n */\n class InfoRegionsComponent extends AccessibilityComponent {\n constructor() {\n /* *\n *\n * Properties\n *\n * */\n super(...arguments);\n this.announcer = void 0;\n this.screenReaderSections = {};\n }\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Init the component\n * @private\n */\n init() {\n const chart = this.chart;\n const component = this;\n this.initRegionsDefinitions();\n this.addEvent(chart, 'aftergetTableAST', function (e) {\n component.onDataTableCreated(e);\n });\n this.addEvent(chart, 'afterViewData', function (e) {\n if (e.wasHidden) {\n component.dataTableDiv = e.element;\n // Use a small delay to give browsers & AT time to\n // register the new table.\n setTimeout(function () {\n component.focusDataTable();\n }, 300);\n }\n });\n this.addEvent(chart, 'afterHideData', function () {\n if (component.viewDataTableButton) {\n component.viewDataTableButton\n .setAttribute('aria-expanded', 'false');\n }\n });\n this.announcer = new Announcer(chart, 'assertive');\n }\n /**\n * @private\n */\n initRegionsDefinitions() {\n const component = this, accessibilityOptions = this.chart.options.accessibility;\n this.screenReaderSections = {\n before: {\n element: null,\n buildContent: function (chart) {\n const formatter = accessibilityOptions.screenReaderSection\n .beforeChartFormatter;\n return formatter ? formatter(chart) :\n component.defaultBeforeChartFormatter(chart);\n },\n insertIntoDOM: function (el, chart) {\n chart.renderTo.insertBefore(el, chart.renderTo.firstChild);\n },\n afterInserted: function () {\n if (typeof component.sonifyButtonId !== 'undefined') {\n component.initSonifyButton(component.sonifyButtonId);\n }\n if (typeof component.dataTableButtonId !== 'undefined') {\n component.initDataTableButton(component.dataTableButtonId);\n }\n }\n },\n after: {\n element: null,\n buildContent: function (chart) {\n const formatter = accessibilityOptions.screenReaderSection\n .afterChartFormatter;\n return formatter ? formatter(chart) :\n component.defaultAfterChartFormatter();\n },\n insertIntoDOM: function (el, chart) {\n chart.renderTo.insertBefore(el, chart.container.nextSibling);\n },\n afterInserted: function () {\n if (component.chart.accessibility &&\n accessibilityOptions.keyboardNavigation.enabled) {\n component.chart.accessibility\n .keyboardNavigation.updateExitAnchor(); // #15986\n }\n }\n }\n };\n }\n /**\n * Called on chart render. Have to update the sections on render, in order\n * to get a11y info from series.\n */\n onChartRender() {\n const component = this;\n this.linkedDescriptionElement = this.getLinkedDescriptionElement();\n this.setLinkedDescriptionAttrs();\n Object.keys(this.screenReaderSections).forEach(function (regionKey) {\n component.updateScreenReaderSection(regionKey);\n });\n }\n /**\n * @private\n */\n getLinkedDescriptionElement() {\n const chartOptions = this.chart.options, linkedDescOption = chartOptions.accessibility.linkedDescription;\n if (!linkedDescOption) {\n return;\n }\n if (typeof linkedDescOption !== 'string') {\n return linkedDescOption;\n }\n const query = format(linkedDescOption, this.chart), queryMatch = doc.querySelectorAll(query);\n if (queryMatch.length === 1) {\n return queryMatch[0];\n }\n }\n /**\n * @private\n */\n setLinkedDescriptionAttrs() {\n const el = this.linkedDescriptionElement;\n if (el) {\n el.setAttribute('aria-hidden', 'true');\n addClass(el, 'highcharts-linked-description');\n }\n }\n /**\n * @private\n * @param {string} regionKey\n * The name/key of the region to update\n */\n updateScreenReaderSection(regionKey) {\n const chart = this.chart;\n const region = this.screenReaderSections[regionKey];\n const content = region.buildContent(chart);\n const sectionDiv = region.element = (region.element || this.createElement('div'));\n const hiddenDiv = (sectionDiv.firstChild || this.createElement('div'));\n if (content) {\n this.setScreenReaderSectionAttribs(sectionDiv, regionKey);\n AST.setElementHTML(hiddenDiv, content);\n sectionDiv.appendChild(hiddenDiv);\n region.insertIntoDOM(sectionDiv, chart);\n if (chart.styledMode) {\n addClass(hiddenDiv, 'highcharts-visually-hidden');\n }\n else {\n visuallyHideElement(hiddenDiv);\n }\n unhideChartElementFromAT(chart, hiddenDiv);\n if (region.afterInserted) {\n region.afterInserted();\n }\n }\n else {\n if (sectionDiv.parentNode) {\n sectionDiv.parentNode.removeChild(sectionDiv);\n }\n region.element = null;\n }\n }\n /**\n * Apply a11y attributes to a screen reader info section\n * @private\n * @param {Highcharts.HTMLDOMElement} sectionDiv The section element\n * @param {string} regionKey Name/key of the region we are setting attrs for\n */\n setScreenReaderSectionAttribs(sectionDiv, regionKey) {\n const chart = this.chart, labelText = chart.langFormat('accessibility.screenReaderSection.' + regionKey +\n 'RegionLabel', { chart: chart, chartTitle: getChartTitle(chart) }), sectionId = `highcharts-screen-reader-region-${regionKey}-${chart.index}`;\n attr(sectionDiv, {\n id: sectionId,\n 'aria-label': labelText || void 0\n });\n // Sections are wrapped to be positioned relatively to chart in case\n // elements inside are tabbed to.\n sectionDiv.style.position = 'relative';\n if (labelText) {\n sectionDiv.setAttribute('role', chart.options.accessibility.landmarkVerbosity === 'all' ?\n 'region' : 'group');\n }\n }\n /**\n * @private\n */\n defaultBeforeChartFormatter() {\n const chart = this.chart, format = chart.options.accessibility.screenReaderSection\n .beforeChartFormat;\n if (!format) {\n return '';\n }\n const axesDesc = this.getAxesDescription(), shouldHaveSonifyBtn = (chart.sonify &&\n chart.options.sonification &&\n chart.options.sonification.enabled), sonifyButtonId = 'highcharts-a11y-sonify-data-btn-' +\n chart.index, dataTableButtonId = 'hc-linkto-highcharts-data-table-' +\n chart.index, annotationsList = getAnnotationsInfoHTML(chart), annotationsTitleStr = chart.langFormat('accessibility.screenReaderSection.annotations.heading', { chart: chart }), context = {\n headingTagName: getHeadingTagNameForElement(chart.renderTo),\n chartTitle: getChartTitle(chart),\n typeDescription: this.getTypeDescriptionText(),\n chartSubtitle: this.getSubtitleText(),\n chartLongdesc: this.getLongdescText(),\n xAxisDescription: axesDesc.xAxis,\n yAxisDescription: axesDesc.yAxis,\n playAsSoundButton: shouldHaveSonifyBtn ?\n this.getSonifyButtonText(sonifyButtonId) : '',\n viewTableButton: chart.getCSV ?\n this.getDataTableButtonText(dataTableButtonId) : '',\n annotationsTitle: annotationsList ? annotationsTitleStr : '',\n annotationsList: annotationsList\n }, formattedString = A11yI18n.i18nFormat(format, context, chart);\n this.dataTableButtonId = dataTableButtonId;\n this.sonifyButtonId = sonifyButtonId;\n return stripEmptyHTMLTags(formattedString);\n }\n /**\n * @private\n */\n defaultAfterChartFormatter() {\n const chart = this.chart;\n const format = chart.options.accessibility.screenReaderSection\n .afterChartFormat;\n if (!format) {\n return '';\n }\n const context = { endOfChartMarker: this.getEndOfChartMarkerText() };\n const formattedString = A11yI18n.i18nFormat(format, context, chart);\n return stripEmptyHTMLTags(formattedString);\n }\n /**\n * @private\n */\n getLinkedDescription() {\n const el = this.linkedDescriptionElement, content = el && el.innerHTML || '';\n return stripHTMLTagsFromString(content, this.chart.renderer.forExport);\n }\n /**\n * @private\n */\n getLongdescText() {\n const chartOptions = this.chart.options, captionOptions = chartOptions.caption, captionText = captionOptions && captionOptions.text, linkedDescription = this.getLinkedDescription();\n return (chartOptions.accessibility.description ||\n linkedDescription ||\n captionText ||\n '');\n }\n /**\n * @private\n */\n getTypeDescriptionText() {\n const chart = this.chart;\n return chart.types ?\n chart.options.accessibility.typeDescription ||\n getTypeDescription(chart, chart.types) : '';\n }\n /**\n * @private\n */\n getDataTableButtonText(buttonId) {\n const chart = this.chart, buttonText = chart.langFormat('accessibility.table.viewAsDataTableButtonText', { chart: chart, chartTitle: getChartTitle(chart) });\n return '';\n }\n /**\n * @private\n */\n getSonifyButtonText(buttonId) {\n const chart = this.chart;\n if (chart.options.sonification &&\n chart.options.sonification.enabled === false) {\n return '';\n }\n const buttonText = chart.langFormat('accessibility.sonification.playAsSoundButtonText', { chart: chart, chartTitle: getChartTitle(chart) });\n return '';\n }\n /**\n * @private\n */\n getSubtitleText() {\n const subtitle = (this.chart.options.subtitle);\n return stripHTMLTagsFromString(subtitle && subtitle.text || '', this.chart.renderer.forExport);\n }\n /**\n * @private\n */\n getEndOfChartMarkerText() {\n const chart = this.chart, markerText = chart.langFormat('accessibility.screenReaderSection.endOfChartMarker', { chart: chart }), id = 'highcharts-end-of-chart-marker-' + chart.index;\n return '
    ' + markerText + '
    ';\n }\n /**\n * @private\n * @param {Highcharts.Dictionary} e\n */\n onDataTableCreated(e) {\n const chart = this.chart;\n if (chart.options.accessibility.enabled) {\n if (this.viewDataTableButton) {\n this.viewDataTableButton.setAttribute('aria-expanded', 'true');\n }\n const attributes = e.tree.attributes || {};\n attributes.tabindex = -1;\n attributes.summary = getTableSummary(chart);\n e.tree.attributes = attributes;\n }\n }\n /**\n * @private\n */\n focusDataTable() {\n const tableDiv = this.dataTableDiv, table = tableDiv && tableDiv.getElementsByTagName('table')[0];\n if (table && table.focus) {\n table.focus();\n }\n }\n /**\n * @private\n * @param {string} sonifyButtonId\n */\n initSonifyButton(sonifyButtonId) {\n const el = this.sonifyButton = getElement(sonifyButtonId);\n const chart = this.chart;\n const defaultHandler = (e) => {\n if (el) {\n el.setAttribute('aria-hidden', 'true');\n el.setAttribute('aria-label', '');\n }\n e.preventDefault();\n e.stopPropagation();\n const announceMsg = chart.langFormat('accessibility.sonification.playAsSoundClickAnnouncement', { chart: chart });\n this.announcer.announce(announceMsg);\n setTimeout(() => {\n if (el) {\n el.removeAttribute('aria-hidden');\n el.removeAttribute('aria-label');\n }\n if (chart.sonify) {\n chart.sonify();\n }\n }, 1000); // Delay to let screen reader speak the button press\n };\n if (el && chart) {\n el.setAttribute('tabindex', -1);\n el.onclick = function (e) {\n const onPlayAsSoundClick = (chart.options.accessibility &&\n chart.options.accessibility.screenReaderSection\n .onPlayAsSoundClick);\n (onPlayAsSoundClick || defaultHandler).call(this, e, chart);\n };\n }\n }\n /**\n * Set attribs and handlers for default viewAsDataTable button if exists.\n * @private\n * @param {string} tableButtonId\n */\n initDataTableButton(tableButtonId) {\n const el = this.viewDataTableButton = getElement(tableButtonId), chart = this.chart, tableId = tableButtonId.replace('hc-linkto-', '');\n if (el) {\n attr(el, {\n tabindex: -1,\n 'aria-expanded': !!getElement(tableId)\n });\n el.onclick = chart.options.accessibility\n .screenReaderSection.onViewDataTableClick ||\n function () {\n chart.viewData();\n };\n }\n }\n /**\n * Return object with text description of each of the chart's axes.\n * @private\n */\n getAxesDescription() {\n const chart = this.chart, shouldDescribeColl = function (collectionKey, defaultCondition) {\n const axes = chart[collectionKey];\n return axes.length > 1 || axes[0] &&\n pick(axes[0].options.accessibility &&\n axes[0].options.accessibility.enabled, defaultCondition);\n }, hasNoMap = !!chart.types &&\n chart.types.indexOf('map') < 0 &&\n chart.types.indexOf('treemap') < 0 &&\n chart.types.indexOf('tilemap') < 0, hasCartesian = !!chart.hasCartesianSeries, showXAxes = shouldDescribeColl('xAxis', !chart.angular && hasCartesian && hasNoMap), showYAxes = shouldDescribeColl('yAxis', hasCartesian && hasNoMap), desc = {};\n if (showXAxes) {\n desc.xAxis = this.getAxisDescriptionText('xAxis');\n }\n if (showYAxes) {\n desc.yAxis = this.getAxisDescriptionText('yAxis');\n }\n return desc;\n }\n /**\n * @private\n */\n getAxisDescriptionText(collectionKey) {\n const chart = this.chart;\n const axes = chart[collectionKey];\n return chart.langFormat('accessibility.axis.' + collectionKey + 'Description' + (axes.length > 1 ? 'Plural' : 'Singular'), {\n chart: chart,\n names: axes.map(function (axis) {\n return getAxisDescription(axis);\n }),\n ranges: axes.map(function (axis) {\n return getAxisRangeDescription(axis);\n }),\n numAxes: axes.length\n });\n }\n /**\n * Remove component traces\n */\n destroy() {\n if (this.announcer) {\n this.announcer.destroy();\n }\n }\n }\n /* *\n *\n * Default Export\n *\n * */\n\n return InfoRegionsComponent;\n });\n _registerModule(_modules, 'Accessibility/Components/MenuComponent.js', [_modules['Core/Chart/Chart.js'], _modules['Core/Utilities.js'], _modules['Accessibility/AccessibilityComponent.js'], _modules['Accessibility/KeyboardNavigationHandler.js'], _modules['Accessibility/Utils/ChartUtilities.js'], _modules['Accessibility/Utils/HTMLUtilities.js']], function (Chart, U, AccessibilityComponent, KeyboardNavigationHandler, ChartUtilities, HTMLUtilities) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Accessibility component for exporting menu.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { attr } = U;\n const { getChartTitle, unhideChartElementFromAT } = ChartUtilities;\n const { getFakeMouseEvent } = HTMLUtilities;\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Get the wrapped export button element of a chart.\n * @private\n */\n function getExportMenuButtonElement(chart) {\n return chart.exportSVGElements && chart.exportSVGElements[0];\n }\n /**\n * @private\n */\n function exportingShouldHaveA11y(chart) {\n const exportingOpts = chart.options.exporting, exportButton = getExportMenuButtonElement(chart);\n return !!(exportingOpts &&\n exportingOpts.enabled !== false &&\n exportingOpts.accessibility &&\n exportingOpts.accessibility.enabled &&\n exportButton &&\n exportButton.element);\n }\n /* *\n *\n * Class\n *\n * */\n /**\n * The MenuComponent class\n *\n * @private\n * @class\n * @name Highcharts.MenuComponent\n */\n class MenuComponent extends AccessibilityComponent {\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Init the component\n */\n init() {\n const chart = this.chart, component = this;\n this.addEvent(chart, 'exportMenuShown', function () {\n component.onMenuShown();\n });\n this.addEvent(chart, 'exportMenuHidden', function () {\n component.onMenuHidden();\n });\n this.createProxyGroup();\n }\n /**\n * @private\n */\n onMenuHidden() {\n const menu = this.chart.exportContextMenu;\n if (menu) {\n menu.setAttribute('aria-hidden', 'true');\n }\n this.setExportButtonExpandedState('false');\n }\n /**\n * @private\n */\n onMenuShown() {\n const chart = this.chart, menu = chart.exportContextMenu;\n if (menu) {\n this.addAccessibleContextMenuAttribs();\n unhideChartElementFromAT(chart, menu);\n }\n this.setExportButtonExpandedState('true');\n }\n /**\n * @private\n * @param {string} stateStr\n */\n setExportButtonExpandedState(stateStr) {\n if (this.exportButtonProxy) {\n this.exportButtonProxy.innerElement.setAttribute('aria-expanded', stateStr);\n }\n }\n /**\n * Called on each render of the chart. We need to update positioning of the\n * proxy overlay.\n */\n onChartRender() {\n const chart = this.chart, focusEl = chart.focusElement, a11y = chart.accessibility;\n this.proxyProvider.clearGroup('chartMenu');\n this.proxyMenuButton();\n if (this.exportButtonProxy &&\n focusEl &&\n focusEl === chart.exportingGroup) {\n if (focusEl.focusBorder) {\n chart.setFocusToElement(focusEl, this.exportButtonProxy.innerElement);\n }\n else if (a11y) {\n a11y.keyboardNavigation.tabindexContainer.focus();\n }\n }\n }\n /**\n * @private\n */\n proxyMenuButton() {\n const chart = this.chart;\n const proxyProvider = this.proxyProvider;\n const buttonEl = getExportMenuButtonElement(chart);\n if (exportingShouldHaveA11y(chart) && buttonEl) {\n this.exportButtonProxy = proxyProvider.addProxyElement('chartMenu', { click: buttonEl }, 'button', {\n 'aria-label': chart.langFormat('accessibility.exporting.menuButtonLabel', {\n chart: chart,\n chartTitle: getChartTitle(chart)\n }),\n 'aria-expanded': false,\n title: chart.options.lang.contextButtonTitle || null\n });\n }\n }\n /**\n * @private\n */\n createProxyGroup() {\n const chart = this.chart;\n if (chart && this.proxyProvider) {\n this.proxyProvider.addGroup('chartMenu');\n }\n }\n /**\n * @private\n */\n addAccessibleContextMenuAttribs() {\n const chart = this.chart, exportList = chart.exportDivElements;\n if (exportList && exportList.length) {\n // Set tabindex on the menu items to allow focusing by script\n // Set role to give screen readers a chance to pick up the contents\n exportList.forEach((item) => {\n if (item) {\n if (item.tagName === 'LI' &&\n !(item.children && item.children.length)) {\n item.setAttribute('tabindex', -1);\n }\n else {\n item.setAttribute('aria-hidden', 'true');\n }\n }\n });\n // Set accessibility properties on parent div\n const parentDiv = (exportList[0] && exportList[0].parentNode);\n if (parentDiv) {\n attr(parentDiv, {\n 'aria-hidden': void 0,\n 'aria-label': chart.langFormat('accessibility.exporting.chartMenuLabel', { chart }),\n role: 'list' // Needed for webkit/VO\n });\n }\n }\n }\n /**\n * Get keyboard navigation handler for this component.\n * @private\n */\n getKeyboardNavigation() {\n const keys = this.keyCodes, chart = this.chart, component = this;\n return new KeyboardNavigationHandler(chart, {\n keyCodeMap: [\n // Arrow prev handler\n [\n [keys.left, keys.up],\n function () {\n return component.onKbdPrevious(this);\n }\n ],\n // Arrow next handler\n [\n [keys.right, keys.down],\n function () {\n return component.onKbdNext(this);\n }\n ],\n // Click handler\n [\n [keys.enter, keys.space],\n function () {\n return component.onKbdClick(this);\n }\n ]\n ],\n // Only run exporting navigation if exporting support exists and is\n // enabled on chart\n validate: function () {\n return !!chart.exporting &&\n chart.options.exporting.enabled !== false &&\n chart.options.exporting.accessibility.enabled !==\n false;\n },\n // Focus export menu button\n init: function () {\n const proxy = component.exportButtonProxy;\n const svgEl = component.chart.exportingGroup;\n if (proxy && svgEl) {\n chart.setFocusToElement(svgEl, proxy.innerElement);\n }\n },\n // Hide the menu\n terminate: function () {\n chart.hideExportMenu();\n }\n });\n }\n /**\n * @private\n * @param {Highcharts.KeyboardNavigationHandler} keyboardNavigationHandler\n * @return {number} Response code\n */\n onKbdPrevious(keyboardNavigationHandler) {\n const chart = this.chart;\n const a11yOptions = chart.options.accessibility;\n const response = keyboardNavigationHandler.response;\n // Try to highlight prev item in list. Highlighting e.g.\n // separators will fail.\n let i = chart.highlightedExportItemIx || 0;\n while (i--) {\n if (chart.highlightExportItem(i)) {\n return response.success;\n }\n }\n // We failed, so wrap around or move to prev module\n if (a11yOptions.keyboardNavigation.wrapAround) {\n chart.highlightLastExportItem();\n return response.success;\n }\n return response.prev;\n }\n /**\n * @private\n * @param {Highcharts.KeyboardNavigationHandler} keyboardNavigationHandler\n * @return {number} Response code\n */\n onKbdNext(keyboardNavigationHandler) {\n const chart = this.chart;\n const a11yOptions = chart.options.accessibility;\n const response = keyboardNavigationHandler.response;\n // Try to highlight next item in list. Highlighting e.g.\n // separators will fail.\n for (let i = (chart.highlightedExportItemIx || 0) + 1; i < chart.exportDivElements.length; ++i) {\n if (chart.highlightExportItem(i)) {\n return response.success;\n }\n }\n // We failed, so wrap around or move to next module\n if (a11yOptions.keyboardNavigation.wrapAround) {\n chart.highlightExportItem(0);\n return response.success;\n }\n return response.next;\n }\n /**\n * @private\n * @param {Highcharts.KeyboardNavigationHandler} keyboardNavigationHandler\n * @return {number} Response code\n */\n onKbdClick(keyboardNavigationHandler) {\n const chart = this.chart;\n const curHighlightedItem = chart.exportDivElements[chart.highlightedExportItemIx];\n const exportButtonElement = getExportMenuButtonElement(chart).element;\n if (chart.openMenu) {\n this.fakeClickEvent(curHighlightedItem);\n }\n else {\n this.fakeClickEvent(exportButtonElement);\n chart.highlightExportItem(0);\n }\n return keyboardNavigationHandler.response.success;\n }\n }\n /* *\n *\n * Class Namespace\n *\n * */\n (function (MenuComponent) {\n /* *\n *\n * Declarations\n *\n * */\n /* *\n *\n * Constants\n *\n * */\n const composedMembers = [];\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * @private\n */\n function compose(ChartClass) {\n if (U.pushUnique(composedMembers, ChartClass)) {\n const chartProto = Chart.prototype;\n chartProto.hideExportMenu = chartHideExportMenu;\n chartProto.highlightExportItem = chartHighlightExportItem;\n chartProto.highlightLastExportItem = chartHighlightLastExportItem;\n chartProto.showExportMenu = chartShowExportMenu;\n }\n }\n MenuComponent.compose = compose;\n /**\n * Show the export menu and focus the first item (if exists).\n *\n * @private\n * @function Highcharts.Chart#showExportMenu\n */\n function chartShowExportMenu() {\n const exportButton = getExportMenuButtonElement(this);\n if (exportButton) {\n const el = exportButton.element;\n if (el.onclick) {\n el.onclick(getFakeMouseEvent('click'));\n }\n }\n }\n /**\n * @private\n * @function Highcharts.Chart#hideExportMenu\n */\n function chartHideExportMenu() {\n const chart = this, exportList = chart.exportDivElements;\n if (exportList && chart.exportContextMenu && chart.openMenu) {\n // Reset hover states etc.\n exportList.forEach((el) => {\n if (el &&\n el.className === 'highcharts-menu-item' &&\n el.onmouseout) {\n el.onmouseout(getFakeMouseEvent('mouseout'));\n }\n });\n chart.highlightedExportItemIx = 0;\n // Hide the menu div\n chart.exportContextMenu.hideMenu();\n // Make sure the chart has focus and can capture keyboard events\n chart.container.focus();\n }\n }\n /**\n * Highlight export menu item by index.\n *\n * @private\n * @function Highcharts.Chart#highlightExportItem\n */\n function chartHighlightExportItem(ix) {\n const listItem = this.exportDivElements && this.exportDivElements[ix];\n const curHighlighted = this.exportDivElements &&\n this.exportDivElements[this.highlightedExportItemIx];\n if (listItem &&\n listItem.tagName === 'LI' &&\n !(listItem.children && listItem.children.length)) {\n // Test if we have focus support for SVG elements\n const hasSVGFocusSupport = !!(this.renderTo.getElementsByTagName('g')[0] || {}).focus;\n // Only focus if we can set focus back to the elements after\n // destroying the menu (#7422)\n if (listItem.focus && hasSVGFocusSupport) {\n listItem.focus();\n }\n if (curHighlighted && curHighlighted.onmouseout) {\n curHighlighted.onmouseout(getFakeMouseEvent('mouseout'));\n }\n if (listItem.onmouseover) {\n listItem.onmouseover(getFakeMouseEvent('mouseover'));\n }\n this.highlightedExportItemIx = ix;\n return true;\n }\n return false;\n }\n /**\n * Try to highlight the last valid export menu item.\n *\n * @private\n * @function Highcharts.Chart#highlightLastExportItem\n */\n function chartHighlightLastExportItem() {\n const chart = this;\n if (chart.exportDivElements) {\n let i = chart.exportDivElements.length;\n while (i--) {\n if (chart.highlightExportItem(i)) {\n return true;\n }\n }\n }\n return false;\n }\n })(MenuComponent || (MenuComponent = {}));\n /* *\n *\n * Default Export\n *\n * */\n\n return MenuComponent;\n });\n _registerModule(_modules, 'Accessibility/KeyboardNavigation.js', [_modules['Core/Globals.js'], _modules['Accessibility/Components/MenuComponent.js'], _modules['Core/Utilities.js'], _modules['Accessibility/Utils/EventProvider.js'], _modules['Accessibility/Utils/HTMLUtilities.js']], function (H, MenuComponent, U, EventProvider, HTMLUtilities) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Main keyboard navigation handling.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { doc, win } = H;\n const { addEvent, fireEvent } = U;\n const { getElement, simulatedEventTarget } = HTMLUtilities;\n /* *\n *\n * Class\n *\n * */\n /**\n * The KeyboardNavigation class, containing the overall keyboard navigation\n * logic for the chart.\n *\n * @requires module:modules/accessibility\n *\n * @private\n * @class\n * @param {Highcharts.Chart} chart\n * Chart object\n * @param {Object} components\n * Map of component names to AccessibilityComponent objects.\n * @name Highcharts.KeyboardNavigation\n */\n class KeyboardNavigation {\n /* *\n *\n * Constructor\n *\n * */\n constructor(chart, components) {\n /* *\n *\n * Properties\n *\n * */\n this.chart = void 0;\n this.components = void 0;\n this.currentModuleIx = NaN;\n this.eventProvider = void 0;\n this.exitAnchor = void 0;\n this.modules = [];\n this.tabindexContainer = void 0;\n this.init(chart, components);\n }\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Initialize the class\n * @private\n * @param {Highcharts.Chart} chart\n * Chart object\n * @param {Object} components\n * Map of component names to AccessibilityComponent objects.\n */\n init(chart, components) {\n const ep = this.eventProvider = new EventProvider();\n this.chart = chart;\n this.components = components;\n this.modules = [];\n this.currentModuleIx = 0;\n this.update();\n ep.addEvent(this.tabindexContainer, 'keydown', (e) => this.onKeydown(e));\n ep.addEvent(this.tabindexContainer, 'focus', (e) => this.onFocus(e));\n ['mouseup', 'touchend'].forEach((eventName) => ep.addEvent(doc, eventName, (e) => this.onMouseUp(e)));\n ['mousedown', 'touchstart'].forEach((eventName) => ep.addEvent(chart.renderTo, eventName, () => {\n this.isClickingChart = true;\n }));\n }\n /**\n * Update the modules for the keyboard navigation.\n * @param {Array} [order]\n * Array specifying the tab order of the components.\n */\n update(order) {\n const a11yOptions = this.chart.options.accessibility, keyboardOptions = a11yOptions && a11yOptions.keyboardNavigation, components = this.components;\n this.updateContainerTabindex();\n if (keyboardOptions &&\n keyboardOptions.enabled &&\n order &&\n order.length) {\n // We (still) have keyboard navigation. Update module list\n this.modules = order.reduce(function (modules, componentName) {\n const navModules = components[componentName]\n .getKeyboardNavigation();\n return modules.concat(navModules);\n }, []);\n this.updateExitAnchor();\n }\n else {\n this.modules = [];\n this.currentModuleIx = 0;\n this.removeExitAnchor();\n }\n }\n /**\n * We use an exit anchor to move focus out of chart whenever we want, by\n * setting focus to this div and not preventing the default tab action. We\n * also use this when users come back into the chart by tabbing back, in\n * order to navigate from the end of the chart.\n * @private\n */\n updateExitAnchor() {\n const endMarkerId = `highcharts-end-of-chart-marker-${this.chart.index}`, endMarker = getElement(endMarkerId);\n this.removeExitAnchor();\n if (endMarker) {\n this.makeElementAnExitAnchor(endMarker);\n this.exitAnchor = endMarker;\n }\n else {\n this.createExitAnchor();\n }\n }\n /**\n * Move to prev/next module.\n * @private\n * @param {number} direction\n * Direction to move. +1 for next, -1 for prev.\n * @return {boolean}\n * True if there was a valid module in direction.\n */\n move(direction) {\n const curModule = this.modules && this.modules[this.currentModuleIx];\n if (curModule && curModule.terminate) {\n curModule.terminate(direction);\n }\n // Remove existing focus border if any\n if (this.chart.focusElement) {\n this.chart.focusElement.removeFocusBorder();\n }\n this.currentModuleIx += direction;\n const newModule = this.modules && this.modules[this.currentModuleIx];\n if (newModule) {\n if (newModule.validate && !newModule.validate()) {\n return this.move(direction); // Invalid module, recurse\n }\n if (newModule.init) {\n newModule.init(direction); // Valid module, init it\n return true;\n }\n }\n // No module\n this.currentModuleIx = 0; // Reset counter\n // Set focus to chart or exit anchor depending on direction\n this.exiting = true;\n if (direction > 0) {\n this.exitAnchor && this.exitAnchor.focus();\n }\n else {\n this.tabindexContainer.focus();\n }\n return false;\n }\n /**\n * Function to run on container focus\n * @private\n * @param {global.FocusEvent} e Browser focus event.\n */\n onFocus(e) {\n const chart = this.chart, focusComesFromChart = (e.relatedTarget &&\n chart.container.contains(e.relatedTarget)), a11yOptions = chart.options.accessibility, keyboardOptions = a11yOptions && a11yOptions.keyboardNavigation, enabled = keyboardOptions && keyboardOptions.enabled;\n // Init keyboard nav if tabbing into chart\n if (enabled &&\n !this.exiting &&\n !this.tabbingInBackwards &&\n !this.isClickingChart &&\n !focusComesFromChart) {\n const ix = this.getFirstValidModuleIx();\n if (ix !== null) {\n this.currentModuleIx = ix;\n this.modules[ix].init(1);\n }\n }\n this.exiting = false;\n }\n /**\n * Reset chart navigation state if we mouse click and it's not already\n * reset. Reset fully if outside the chart, otherwise just hide focus\n * indicator.\n * @private\n */\n onMouseUp(e) {\n delete this.isClickingChart;\n if (!this.keyboardReset &&\n e.relatedTarget !== simulatedEventTarget) {\n const chart = this.chart;\n if (!e.target ||\n !chart.container.contains(e.target)) {\n const curMod = this.modules &&\n this.modules[this.currentModuleIx || 0];\n if (curMod && curMod.terminate) {\n curMod.terminate();\n }\n this.currentModuleIx = 0;\n }\n if (chart.focusElement) {\n chart.focusElement.removeFocusBorder();\n delete chart.focusElement;\n }\n this.keyboardReset = true;\n }\n }\n /**\n * Function to run on keydown\n * @private\n * @param {global.KeyboardEvent} ev Browser keydown event.\n */\n onKeydown(ev) {\n const e = ev || win.event, curNavModule = (this.modules &&\n this.modules.length &&\n this.modules[this.currentModuleIx]);\n let preventDefault;\n const target = e.target;\n if (target &&\n target.nodeName === 'INPUT' &&\n !target.classList.contains('highcharts-a11y-proxy-element')) {\n return;\n }\n // Used for resetting nav state when clicking outside chart\n this.keyboardReset = false;\n // Used for sending focus out of the chart by the modules.\n this.exiting = false;\n // If there is a nav module for the current index, run it.\n // Otherwise, we are outside of the chart in some direction.\n if (curNavModule) {\n const response = curNavModule.run(e);\n if (response === curNavModule.response.success) {\n preventDefault = true;\n }\n else if (response === curNavModule.response.prev) {\n preventDefault = this.move(-1);\n }\n else if (response === curNavModule.response.next) {\n preventDefault = this.move(1);\n }\n if (preventDefault) {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n }\n /**\n * Chart container should have tabindex if navigation is enabled.\n * @private\n */\n updateContainerTabindex() {\n const a11yOptions = this.chart.options.accessibility, keyboardOptions = a11yOptions && a11yOptions.keyboardNavigation, shouldHaveTabindex = !(keyboardOptions && keyboardOptions.enabled === false), chart = this.chart, container = chart.container;\n let tabindexContainer;\n if (chart.renderTo.hasAttribute('tabindex')) {\n container.removeAttribute('tabindex');\n tabindexContainer = chart.renderTo;\n }\n else {\n tabindexContainer = container;\n }\n this.tabindexContainer = tabindexContainer;\n const curTabindex = tabindexContainer.getAttribute('tabindex');\n if (shouldHaveTabindex && !curTabindex) {\n tabindexContainer.setAttribute('tabindex', '0');\n }\n else if (!shouldHaveTabindex) {\n chart.container.removeAttribute('tabindex');\n }\n }\n /**\n * Add new exit anchor to the chart.\n * @private\n */\n createExitAnchor() {\n const chart = this.chart, exitAnchor = this.exitAnchor = doc.createElement('div');\n chart.renderTo.appendChild(exitAnchor);\n this.makeElementAnExitAnchor(exitAnchor);\n }\n /**\n * Add attributes and events to an element to make it function as an\n * exit anchor.\n * @private\n */\n makeElementAnExitAnchor(el) {\n const chartTabindex = this.tabindexContainer.getAttribute('tabindex') || 0;\n el.setAttribute('class', 'highcharts-exit-anchor');\n el.setAttribute('tabindex', chartTabindex);\n el.setAttribute('aria-hidden', false);\n // Handle focus\n this.addExitAnchorEventsToEl(el);\n }\n /**\n * Destroy the exit anchor and remove from DOM.\n * @private\n */\n removeExitAnchor() {\n if (this.exitAnchor && this.exitAnchor.parentNode) {\n this.exitAnchor.parentNode.removeChild(this.exitAnchor);\n delete this.exitAnchor;\n }\n }\n /**\n * Add focus handler to exit anchor element.\n * @private\n */\n addExitAnchorEventsToEl(element) {\n const chart = this.chart, keyboardNavigation = this;\n this.eventProvider.addEvent(element, 'focus', function (ev) {\n const e = ev || win.event, focusComesFromChart = (e.relatedTarget &&\n chart.container.contains(e.relatedTarget)), comingInBackwards = !(focusComesFromChart || keyboardNavigation.exiting);\n if (chart.focusElement) {\n delete chart.focusElement;\n }\n if (comingInBackwards) {\n // Focus the container instead\n keyboardNavigation.tabbingInBackwards = true;\n keyboardNavigation.tabindexContainer.focus();\n delete keyboardNavigation.tabbingInBackwards;\n e.preventDefault();\n // Move to last valid keyboard nav module\n // Note the we don't run it, just set the index\n if (keyboardNavigation.modules &&\n keyboardNavigation.modules.length) {\n keyboardNavigation.currentModuleIx =\n keyboardNavigation.modules.length - 1;\n const curModule = keyboardNavigation.modules[keyboardNavigation.currentModuleIx];\n // Validate the module\n if (curModule &&\n curModule.validate && !curModule.validate()) {\n // Invalid. Try moving backwards to find next valid.\n keyboardNavigation.move(-1);\n }\n else if (curModule) {\n // We have a valid module, init it\n curModule.init(-1);\n }\n }\n }\n else {\n // Don't skip the next focus, we only skip once.\n keyboardNavigation.exiting = false;\n }\n });\n }\n /**\n * Get the ix of the first module that either does not require validation or\n * validates positively.\n * @private\n */\n getFirstValidModuleIx() {\n const len = this.modules.length;\n for (let i = 0; i < len; ++i) {\n const mod = this.modules[i];\n if (!mod.validate || mod.validate()) {\n return i;\n }\n }\n return null;\n }\n /**\n * Remove all traces of keyboard navigation.\n * @private\n */\n destroy() {\n this.removeExitAnchor();\n this.eventProvider.removeAddedEvents();\n this.chart.container.removeAttribute('tabindex');\n }\n }\n /* *\n *\n * Class Namespace\n *\n * */\n (function (KeyboardNavigation) {\n /* *\n *\n * Declarations\n *\n * */\n /* *\n *\n * Construction\n *\n * */\n const composedMembers = [];\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Composition function.\n * @private\n */\n function compose(ChartClass) {\n MenuComponent.compose(ChartClass);\n if (U.pushUnique(composedMembers, ChartClass)) {\n const chartProto = ChartClass.prototype;\n chartProto.dismissPopupContent = chartDismissPopupContent;\n }\n if (U.pushUnique(composedMembers, doc)) {\n addEvent(doc, 'keydown', documentOnKeydown);\n }\n return ChartClass;\n }\n KeyboardNavigation.compose = compose;\n /**\n * Dismiss popup content in chart, including export menu and tooltip.\n * @private\n */\n function chartDismissPopupContent() {\n const chart = this;\n fireEvent(this, 'dismissPopupContent', {}, function () {\n if (chart.tooltip) {\n chart.tooltip.hide(0);\n }\n chart.hideExportMenu();\n });\n }\n /**\n * Add event listener to document to detect ESC key press and dismiss\n * hover/popup content.\n * @private\n */\n function documentOnKeydown(e) {\n const keycode = e.which || e.keyCode;\n const esc = 27;\n if (keycode === esc && H.charts) {\n H.charts.forEach((chart) => {\n if (chart && chart.dismissPopupContent) {\n chart.dismissPopupContent();\n }\n });\n }\n }\n })(KeyboardNavigation || (KeyboardNavigation = {}));\n /* *\n *\n * Default Export\n *\n * */\n\n return KeyboardNavigation;\n });\n _registerModule(_modules, 'Accessibility/Components/LegendComponent.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Globals.js'], _modules['Core/Legend/Legend.js'], _modules['Core/Utilities.js'], _modules['Accessibility/AccessibilityComponent.js'], _modules['Accessibility/KeyboardNavigationHandler.js'], _modules['Accessibility/Utils/ChartUtilities.js'], _modules['Accessibility/Utils/HTMLUtilities.js']], function (A, H, Legend, U, AccessibilityComponent, KeyboardNavigationHandler, CU, HU) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Accessibility component for chart legend.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { animObject } = A;\n const { doc } = H;\n const { addEvent, fireEvent, isNumber, pick, syncTimeout } = U;\n const { getChartTitle } = CU;\n const { stripHTMLTagsFromString: stripHTMLTags, addClass, removeClass } = HU;\n /* *\n *\n * Functions\n *\n * */\n /**\n * @private\n */\n function scrollLegendToItem(legend, itemIx) {\n const itemPage = (legend.allItems[itemIx].legendItem || {}).pageIx, curPage = legend.currentPage;\n if (typeof itemPage !== 'undefined' && itemPage + 1 !== curPage) {\n legend.scroll(1 + itemPage - curPage);\n }\n }\n /**\n * @private\n */\n function shouldDoLegendA11y(chart) {\n const items = chart.legend && chart.legend.allItems, legendA11yOptions = (chart.options.legend.accessibility || {}), unsupportedColorAxis = chart.colorAxis && chart.colorAxis.some((c) => !c.dataClasses || !c.dataClasses.length);\n return !!(items && items.length &&\n !unsupportedColorAxis &&\n legendA11yOptions.enabled !== false);\n }\n /**\n * @private\n */\n function setLegendItemHoverState(hoverActive, item) {\n const legendItem = item.legendItem || {};\n item.setState(hoverActive ? 'hover' : '', true);\n for (const key of ['group', 'label', 'symbol']) {\n const svgElement = legendItem[key];\n const element = svgElement && svgElement.element || svgElement;\n if (element) {\n fireEvent(element, hoverActive ? 'mouseover' : 'mouseout');\n }\n }\n }\n /* *\n *\n * Class\n *\n * */\n /**\n * The LegendComponent class\n *\n * @private\n * @class\n * @name Highcharts.LegendComponent\n */\n class LegendComponent extends AccessibilityComponent {\n constructor() {\n /* *\n *\n * Properties\n *\n * */\n super(...arguments);\n this.highlightedLegendItemIx = NaN;\n this.proxyGroup = null;\n }\n /* *\n *\n * Functions\n *\n * */\n /**\n * Init the component\n * @private\n */\n init() {\n const component = this;\n this.recreateProxies();\n // Note: Chart could create legend dynamically, so events cannot be\n // tied to the component's chart's current legend.\n // @todo 1. attach component to created legends\n // @todo 2. move listeners to composition and access `this.component`\n this.addEvent(Legend, 'afterScroll', function () {\n if (this.chart === component.chart) {\n component.proxyProvider.updateGroupProxyElementPositions('legend');\n component.updateLegendItemProxyVisibility();\n if (component.highlightedLegendItemIx > -1) {\n this.chart.highlightLegendItem(component.highlightedLegendItemIx);\n }\n }\n });\n this.addEvent(Legend, 'afterPositionItem', function (e) {\n if (this.chart === component.chart && this.chart.renderer) {\n component.updateProxyPositionForItem(e.item);\n }\n });\n this.addEvent(Legend, 'afterRender', function () {\n if (this.chart === component.chart &&\n this.chart.renderer &&\n component.recreateProxies()) {\n syncTimeout(() => component.proxyProvider\n .updateGroupProxyElementPositions('legend'), animObject(pick(this.chart.renderer.globalAnimation, true)).duration);\n }\n });\n }\n /**\n * Update visibility of legend items when using paged legend\n * @private\n */\n updateLegendItemProxyVisibility() {\n const chart = this.chart;\n const legend = chart.legend;\n const items = legend.allItems || [];\n const curPage = legend.currentPage || 1;\n const clipHeight = legend.clipHeight || 0;\n let legendItem;\n items.forEach((item) => {\n if (item.a11yProxyElement) {\n const hasPages = legend.pages && legend.pages.length;\n const proxyEl = item.a11yProxyElement.element;\n let hide = false;\n legendItem = item.legendItem || {};\n if (hasPages) {\n const itemPage = legendItem.pageIx || 0;\n const y = legendItem.y || 0;\n const h = legendItem.label ?\n Math.round(legendItem.label.getBBox().height) :\n 0;\n hide = y + h - legend.pages[itemPage] > clipHeight ||\n itemPage !== curPage - 1;\n }\n if (hide) {\n if (chart.styledMode) {\n addClass(proxyEl, 'highcharts-a11y-invisible');\n }\n else {\n proxyEl.style.visibility = 'hidden';\n }\n }\n else {\n removeClass(proxyEl, 'highcharts-a11y-invisible');\n proxyEl.style.visibility = '';\n }\n }\n });\n }\n /**\n * @private\n */\n onChartRender() {\n if (!shouldDoLegendA11y(this.chart)) {\n this.removeProxies();\n }\n }\n /**\n * @private\n */\n highlightAdjacentLegendPage(direction) {\n const chart = this.chart;\n const legend = chart.legend;\n const curPageIx = legend.currentPage || 1;\n const newPageIx = curPageIx + direction;\n const pages = legend.pages || [];\n if (newPageIx > 0 && newPageIx <= pages.length) {\n let i = 0, res;\n for (const item of legend.allItems) {\n if (((item.legendItem || {}).pageIx || 0) + 1 === newPageIx) {\n res = chart.highlightLegendItem(i);\n if (res) {\n this.highlightedLegendItemIx = i;\n }\n }\n ++i;\n }\n }\n }\n /**\n * @private\n */\n updateProxyPositionForItem(item) {\n if (item.a11yProxyElement) {\n item.a11yProxyElement.refreshPosition();\n }\n }\n /**\n * Returns false if legend a11y is disabled and proxies were not created,\n * true otherwise.\n * @private\n */\n recreateProxies() {\n const focusedElement = doc.activeElement;\n const proxyGroup = this.proxyGroup;\n const shouldRestoreFocus = focusedElement && proxyGroup &&\n proxyGroup.contains(focusedElement);\n this.removeProxies();\n if (shouldDoLegendA11y(this.chart)) {\n this.addLegendProxyGroup();\n this.proxyLegendItems();\n this.updateLegendItemProxyVisibility();\n this.updateLegendTitle();\n if (shouldRestoreFocus) {\n this.chart.highlightLegendItem(this.highlightedLegendItemIx);\n }\n return true;\n }\n return false;\n }\n /**\n * @private\n */\n removeProxies() {\n this.proxyProvider.removeGroup('legend');\n }\n /**\n * @private\n */\n updateLegendTitle() {\n const chart = this.chart;\n const legendTitle = stripHTMLTags((chart.legend &&\n chart.legend.options.title &&\n chart.legend.options.title.text ||\n '').replace(/
    /g, ' '), chart.renderer.forExport);\n const legendLabel = chart.langFormat('accessibility.legend.legendLabel' + (legendTitle ? '' : 'NoTitle'), {\n chart,\n legendTitle,\n chartTitle: getChartTitle(chart)\n });\n this.proxyProvider.updateGroupAttrs('legend', {\n 'aria-label': legendLabel\n });\n }\n /**\n * @private\n */\n addLegendProxyGroup() {\n const a11yOptions = this.chart.options.accessibility;\n const groupRole = a11yOptions.landmarkVerbosity === 'all' ?\n 'region' : null;\n this.proxyGroup = this.proxyProvider.addGroup('legend', 'ul', {\n // Filled by updateLegendTitle, to keep up to date without\n // recreating group\n 'aria-label': '_placeholder_',\n role: groupRole\n });\n }\n /**\n * @private\n */\n proxyLegendItems() {\n const component = this, items = (this.chart.legend || {}).allItems || [];\n let legendItem;\n items.forEach((item) => {\n legendItem = item.legendItem || {};\n if (legendItem.label && legendItem.label.element) {\n component.proxyLegendItem(item);\n }\n });\n }\n /**\n * @private\n * @param {Highcharts.BubbleLegendItem|Point|Highcharts.Series} item\n */\n proxyLegendItem(item) {\n const legendItem = item.legendItem || {};\n if (!legendItem.label || !legendItem.group) {\n return;\n }\n const itemLabel = this.chart.langFormat('accessibility.legend.legendItem', {\n chart: this.chart,\n itemName: stripHTMLTags(item.name, this.chart.renderer.forExport),\n item\n });\n const attribs = {\n tabindex: -1,\n 'aria-pressed': item.visible,\n 'aria-label': itemLabel\n };\n // Considers useHTML\n const proxyPositioningElement = legendItem.group.div ?\n legendItem.label :\n legendItem.group;\n item.a11yProxyElement = this.proxyProvider.addProxyElement('legend', {\n click: legendItem.label,\n visual: proxyPositioningElement.element\n }, 'button', attribs);\n }\n /**\n * Get keyboard navigation handler for this component.\n * @private\n */\n getKeyboardNavigation() {\n const keys = this.keyCodes, component = this, chart = this.chart;\n return new KeyboardNavigationHandler(chart, {\n keyCodeMap: [\n [\n [keys.left, keys.right, keys.up, keys.down],\n function (keyCode) {\n return component.onKbdArrowKey(this, keyCode);\n }\n ],\n [\n [keys.enter, keys.space],\n function () {\n return component.onKbdClick(this);\n }\n ],\n [\n [keys.pageDown, keys.pageUp],\n function (keyCode) {\n const direction = keyCode === keys.pageDown ? 1 : -1;\n component.highlightAdjacentLegendPage(direction);\n return this.response.success;\n }\n ]\n ],\n validate: function () {\n return component.shouldHaveLegendNavigation();\n },\n init: function () {\n chart.highlightLegendItem(0);\n component.highlightedLegendItemIx = 0;\n },\n terminate: function () {\n component.highlightedLegendItemIx = -1;\n chart.legend.allItems.forEach((item) => setLegendItemHoverState(false, item));\n }\n });\n }\n /**\n * Arrow key navigation\n * @private\n */\n onKbdArrowKey(keyboardNavigationHandler, keyCode) {\n const keys = this.keyCodes, response = keyboardNavigationHandler.response, chart = this.chart, a11yOptions = chart.options.accessibility, numItems = chart.legend.allItems.length, direction = (keyCode === keys.left || keyCode === keys.up) ? -1 : 1;\n const res = chart.highlightLegendItem(this.highlightedLegendItemIx + direction);\n if (res) {\n this.highlightedLegendItemIx += direction;\n return response.success;\n }\n if (numItems > 1 &&\n a11yOptions.keyboardNavigation.wrapAround) {\n keyboardNavigationHandler.init(direction);\n return response.success;\n }\n return response.success;\n }\n /**\n * @private\n * @param {Highcharts.KeyboardNavigationHandler} keyboardNavigationHandler\n * @return {number} Response code\n */\n onKbdClick(keyboardNavigationHandler) {\n const legendItem = this.chart.legend.allItems[this.highlightedLegendItemIx];\n if (legendItem && legendItem.a11yProxyElement) {\n legendItem.a11yProxyElement.click();\n }\n return keyboardNavigationHandler.response.success;\n }\n /**\n * @private\n */\n shouldHaveLegendNavigation() {\n if (!shouldDoLegendA11y(this.chart)) {\n return false;\n }\n const chart = this.chart, legendOptions = chart.options.legend || {}, legendA11yOptions = (legendOptions.accessibility || {});\n return !!(chart.legend.display &&\n legendA11yOptions.keyboardNavigation &&\n legendA11yOptions.keyboardNavigation.enabled);\n }\n /**\n * Clean up\n * @private\n */\n destroy() {\n this.removeProxies();\n }\n }\n /* *\n *\n * Class Namespace\n *\n * */\n (function (LegendComponent) {\n /* *\n *\n * Declarations\n *\n * */\n /* *\n *\n * Constants\n *\n * */\n const composedMembers = [];\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Highlight legend item by index.\n * @private\n */\n function chartHighlightLegendItem(ix) {\n const items = this.legend.allItems;\n const oldIx = this.accessibility &&\n this.accessibility.components.legend.highlightedLegendItemIx;\n const itemToHighlight = items[ix], legendItem = itemToHighlight.legendItem || {};\n if (itemToHighlight) {\n if (isNumber(oldIx) && items[oldIx]) {\n setLegendItemHoverState(false, items[oldIx]);\n }\n scrollLegendToItem(this.legend, ix);\n const legendItemProp = legendItem.label;\n const proxyBtn = itemToHighlight.a11yProxyElement &&\n itemToHighlight.a11yProxyElement.innerElement;\n if (legendItemProp && legendItemProp.element && proxyBtn) {\n this.setFocusToElement(legendItemProp, proxyBtn);\n }\n setLegendItemHoverState(true, itemToHighlight);\n return true;\n }\n return false;\n }\n /**\n * @private\n */\n function compose(ChartClass, LegendClass) {\n if (U.pushUnique(composedMembers, ChartClass)) {\n const chartProto = ChartClass.prototype;\n chartProto.highlightLegendItem = chartHighlightLegendItem;\n }\n if (U.pushUnique(composedMembers, LegendClass)) {\n addEvent(LegendClass, 'afterColorizeItem', legendOnAfterColorizeItem);\n }\n }\n LegendComponent.compose = compose;\n /**\n * Keep track of pressed state for legend items.\n * @private\n */\n function legendOnAfterColorizeItem(e) {\n const chart = this.chart, a11yOptions = chart.options.accessibility, legendItem = e.item;\n if (a11yOptions.enabled && legendItem && legendItem.a11yProxyElement) {\n legendItem.a11yProxyElement.innerElement.setAttribute('aria-pressed', e.visible ? 'true' : 'false');\n }\n }\n })(LegendComponent || (LegendComponent = {}));\n /* *\n *\n * Default Export\n *\n * */\n\n return LegendComponent;\n });\n _registerModule(_modules, 'Core/Axis/NavigatorAxisComposition.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {\n /* *\n *\n * (c) 2010-2021 Torstein Honsi\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { isTouchDevice } = H;\n const { addEvent, correctFloat, defined, isNumber, pick } = U;\n /* *\n *\n * Constants\n *\n * */\n const composedMembers = [];\n /* *\n *\n * Functions\n *\n * */\n /**\n * @private\n */\n function onAxisInit() {\n const axis = this;\n if (!axis.navigatorAxis) {\n axis.navigatorAxis = new NavigatorAxisAdditions(axis);\n }\n }\n /**\n * For Stock charts, override selection zooming with some special features\n * because X axis zooming is already allowed by the Navigator and Range\n * selector.\n * @private\n */\n function onAxisZoom(e) {\n const axis = this, chart = axis.chart, chartOptions = chart.options, navigator = chartOptions.navigator, navigatorAxis = axis.navigatorAxis, pinchType = chart.zooming.pinchType, rangeSelector = chartOptions.rangeSelector, zoomType = chart.zooming.type;\n if (axis.isXAxis && ((navigator && navigator.enabled) ||\n (rangeSelector && rangeSelector.enabled))) {\n // For y only zooming, ignore the X axis completely\n if (zoomType === 'y') {\n e.zoomed = false;\n // For xy zooming, record the state of the zoom before zoom\n // selection, then when the reset button is pressed, revert to\n // this state. This should apply only if the chart is\n // initialized with a range (#6612), otherwise zoom all the way\n // out.\n }\n else if (((!isTouchDevice && zoomType === 'xy') ||\n (isTouchDevice && pinchType === 'xy')) &&\n axis.options.range) {\n const previousZoom = navigatorAxis.previousZoom;\n if (defined(e.newMin)) {\n navigatorAxis.previousZoom = [axis.min, axis.max];\n }\n else if (previousZoom) {\n e.newMin = previousZoom[0];\n e.newMax = previousZoom[1];\n navigatorAxis.previousZoom = void 0;\n }\n }\n }\n if (typeof e.zoomed !== 'undefined') {\n e.preventDefault();\n }\n }\n /* *\n *\n * Class\n *\n * */\n /**\n * @private\n * @class\n */\n class NavigatorAxisAdditions {\n /* *\n *\n * Static Functions\n *\n * */\n /**\n * @private\n */\n static compose(AxisClass) {\n if (U.pushUnique(composedMembers, AxisClass)) {\n AxisClass.keepProps.push('navigatorAxis');\n addEvent(AxisClass, 'init', onAxisInit);\n addEvent(AxisClass, 'zoom', onAxisZoom);\n }\n }\n /* *\n *\n * Constructors\n *\n * */\n constructor(axis) {\n this.axis = axis;\n }\n /* *\n *\n * Functions\n *\n * */\n /**\n * @private\n */\n destroy() {\n this.axis = void 0;\n }\n /**\n * Add logic to normalize the zoomed range in order to preserve the pressed\n * state of range selector buttons\n *\n * @private\n * @function Highcharts.Axis#toFixedRange\n */\n toFixedRange(pxMin, pxMax, fixedMin, fixedMax) {\n const axis = this.axis, chart = axis.chart;\n let newMin = pick(fixedMin, axis.translate(pxMin, true, !axis.horiz)), newMax = pick(fixedMax, axis.translate(pxMax, true, !axis.horiz));\n const fixedRange = chart && chart.fixedRange, halfPointRange = (axis.pointRange || 0) / 2;\n // Add/remove half point range to/from the extremes (#1172)\n if (!defined(fixedMin)) {\n newMin = correctFloat(newMin + halfPointRange);\n }\n if (!defined(fixedMax)) {\n newMax = correctFloat(newMax - halfPointRange);\n }\n // Make sure panning to the edges does not decrease the zoomed range\n if (fixedRange && axis.dataMin && axis.dataMax) {\n if (newMax >= axis.dataMax) {\n newMin = correctFloat(axis.dataMax - fixedRange);\n }\n if (newMin <= axis.dataMin) {\n newMax = correctFloat(axis.dataMin + fixedRange);\n }\n }\n if (!isNumber(newMin) || !isNumber(newMax)) { // #1195, #7411\n newMin = newMax = void 0;\n }\n return {\n min: newMin,\n max: newMax\n };\n }\n }\n /* *\n *\n * Default Export\n *\n * */\n\n return NavigatorAxisAdditions;\n });\n _registerModule(_modules, 'Stock/Navigator/NavigatorDefaults.js', [_modules['Core/Color/Color.js'], _modules['Core/Series/SeriesRegistry.js']], function (Color, SeriesRegistry) {\n /* *\n *\n * (c) 2010-2021 Torstein Honsi\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { parse: color } = Color;\n const { seriesTypes } = SeriesRegistry;\n /* *\n *\n * Constants\n *\n * */\n /**\n * The navigator is a small series below the main series, displaying\n * a view of the entire data set. It provides tools to zoom in and\n * out on parts of the data as well as panning across the dataset.\n *\n * @product highstock gantt\n * @optionparent navigator\n */\n const NavigatorDefaults = {\n /**\n * Whether the navigator and scrollbar should adapt to updated data\n * in the base X axis. When loading data async, as in the demo below,\n * this should be `false`. Otherwise new data will trigger navigator\n * redraw, which will cause unwanted looping. In the demo below, the\n * data in the navigator is set only once. On navigating, only the main\n * chart content is updated.\n *\n * @sample {highstock} stock/demo/lazy-loading/\n * Set to false with async data loading\n *\n * @type {boolean}\n * @default true\n * @apioption navigator.adaptToUpdatedData\n */\n /**\n * An integer identifying the index to use for the base series, or a\n * string representing the id of the series.\n *\n * **Note**: As of Highcharts 5.0, this is now a deprecated option.\n * Prefer [series.showInNavigator](#plotOptions.series.showInNavigator).\n *\n * @see [series.showInNavigator](#plotOptions.series.showInNavigator)\n *\n * @deprecated\n * @type {number|string}\n * @default 0\n * @apioption navigator.baseSeries\n */\n /**\n * Enable or disable the navigator.\n *\n * @sample {highstock} stock/navigator/enabled/\n * Disable the navigator\n *\n * @type {boolean}\n * @default true\n * @apioption navigator.enabled\n */\n /**\n * When the chart is inverted, whether to draw the navigator on the\n * opposite side.\n *\n * @type {boolean}\n * @default false\n * @since 5.0.8\n * @apioption navigator.opposite\n */\n /**\n * The height of the navigator.\n *\n * @sample {highstock} stock/navigator/height/\n * A higher navigator\n */\n height: 40,\n /**\n * The distance from the nearest element, the X axis or X axis labels.\n *\n * @sample {highstock} stock/navigator/margin/\n * A margin of 2 draws the navigator closer to the X axis labels\n */\n margin: 25,\n /**\n * Whether the mask should be inside the range marking the zoomed\n * range, or outside. In Highcharts Stock 1.x it was always `false`.\n *\n * @sample {highstock} stock/demo/maskinside-false/\n * False, mask outside\n *\n * @since 2.0\n */\n maskInside: true,\n /**\n * Options for the handles for dragging the zoomed area.\n *\n * @sample {highstock} stock/navigator/handles/\n * Colored handles\n */\n handles: {\n /**\n * Width for handles.\n *\n * @sample {highstock} stock/navigator/styled-handles/\n * Styled handles\n *\n * @since 6.0.0\n */\n width: 7,\n /**\n * Height for handles.\n *\n * @sample {highstock} stock/navigator/styled-handles/\n * Styled handles\n *\n * @since 6.0.0\n */\n height: 15,\n /**\n * Array to define shapes of handles. 0-index for left, 1-index for\n * right.\n *\n * Additionally, the URL to a graphic can be given on this form:\n * `url(graphic.png)`. Note that for the image to be applied to\n * exported charts, its URL needs to be accessible by the export\n * server.\n *\n * Custom callbacks for symbol path generation can also be added to\n * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then\n * used by its method name, as shown in the demo.\n *\n * @sample {highstock} stock/navigator/styled-handles/\n * Styled handles\n *\n * @type {Array}\n * @default [\"navigator-handle\", \"navigator-handle\"]\n * @since 6.0.0\n */\n symbols: ['navigator-handle', 'navigator-handle'],\n /**\n * Allows to enable/disable handles.\n *\n * @since 6.0.0\n */\n enabled: true,\n /**\n * The width for the handle border and the stripes inside.\n *\n * @sample {highstock} stock/navigator/styled-handles/\n * Styled handles\n *\n * @since 6.0.0\n * @apioption navigator.handles.lineWidth\n */\n lineWidth: 1,\n /**\n * The fill for the handle.\n *\n * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}\n */\n backgroundColor: \"#f2f2f2\" /* Palette.neutralColor5 */,\n /**\n * The stroke for the handle border and the stripes inside.\n *\n * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}\n */\n borderColor: \"#999999\" /* Palette.neutralColor40 */\n },\n /**\n * The color of the mask covering the areas of the navigator series\n * that are currently not visible in the main series. The default\n * color is bluish with an opacity of 0.3 to see the series below.\n *\n * @see In styled mode, the mask is styled with the\n * `.highcharts-navigator-mask` and\n * `.highcharts-navigator-mask-inside` classes.\n *\n * @sample {highstock} stock/navigator/maskfill/\n * Blue, semi transparent mask\n *\n * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}\n * @default rgba(102,133,194,0.3)\n */\n maskFill: color(\"#667aff\" /* Palette.highlightColor60 */).setOpacity(0.3).get(),\n /**\n * The color of the line marking the currently zoomed area in the\n * navigator.\n *\n * @sample {highstock} stock/navigator/outline/\n * 2px blue outline\n *\n * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}\n * @default #cccccc\n */\n outlineColor: \"#999999\" /* Palette.neutralColor40 */,\n /**\n * The width of the line marking the currently zoomed area in the\n * navigator.\n *\n * @see In styled mode, the outline stroke width is set with the\n * `.highcharts-navigator-outline` class.\n *\n * @sample {highstock} stock/navigator/outline/\n * 2px blue outline\n *\n * @type {number}\n */\n outlineWidth: 1,\n /**\n * Options for the navigator series. Available options are the same\n * as any series, documented at [plotOptions](#plotOptions.series)\n * and [series](#series).\n *\n * Unless data is explicitly defined on navigator.series, the data\n * is borrowed from the first series in the chart.\n *\n * Default series options for the navigator series are:\n * ```js\n * series: {\n * type: 'areaspline',\n * fillOpacity: 0.05,\n * dataGrouping: {\n * smoothed: true\n * },\n * lineWidth: 1,\n * marker: {\n * enabled: false\n * }\n * }\n * ```\n *\n * @see In styled mode, the navigator series is styled with the\n * `.highcharts-navigator-series` class.\n *\n * @sample {highstock} stock/navigator/series-data/\n * Using a separate data set for the navigator\n * @sample {highstock} stock/navigator/series/\n * A green navigator series\n *\n * @type {*|Array<*>|Highcharts.SeriesOptionsType|Array}\n */\n series: {\n /**\n * The type of the navigator series.\n *\n * Heads up:\n * In column-type navigator, zooming is limited to at least one\n * point with its `pointRange`.\n *\n * @sample {highstock} stock/navigator/column/\n * Column type navigator\n *\n * @type {string}\n * @default {highstock} `areaspline` if defined, otherwise `line`\n * @default {gantt} gantt\n */\n type: (typeof seriesTypes.areaspline === 'undefined' ?\n 'line' :\n 'areaspline'),\n /**\n * The fill opacity of the navigator series.\n */\n fillOpacity: 0.05,\n /**\n * The pixel line width of the navigator series.\n */\n lineWidth: 1,\n /**\n * @ignore-option\n */\n compare: null,\n /**\n * @ignore-option\n */\n sonification: {\n enabled: false\n },\n /**\n * Unless data is explicitly defined, the data is borrowed from the\n * first series in the chart.\n *\n * @type {Array|object|null>}\n * @product highstock\n * @apioption navigator.series.data\n */\n /**\n * Data grouping options for the navigator series.\n *\n * @extends plotOptions.series.dataGrouping\n */\n dataGrouping: {\n approximation: 'average',\n enabled: true,\n groupPixelWidth: 2,\n // Replace smoothed property by anchors, #12455.\n firstAnchor: 'firstPoint',\n anchor: 'middle',\n lastAnchor: 'lastPoint',\n // Day and week differs from plotOptions.series.dataGrouping\n units: [\n ['millisecond', [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]],\n ['second', [1, 2, 5, 10, 15, 30]],\n ['minute', [1, 2, 5, 10, 15, 30]],\n ['hour', [1, 2, 3, 4, 6, 8, 12]],\n ['day', [1, 2, 3, 4]],\n ['week', [1, 2, 3]],\n ['month', [1, 3, 6]],\n ['year', null]\n ]\n },\n /**\n * Data label options for the navigator series. Data labels are\n * disabled by default on the navigator series.\n *\n * @extends plotOptions.series.dataLabels\n */\n dataLabels: {\n enabled: false,\n zIndex: 2 // #1839\n },\n id: 'highcharts-navigator-series',\n className: 'highcharts-navigator-series',\n /**\n * Sets the fill color of the navigator series.\n *\n * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}\n * @apioption navigator.series.color\n */\n /**\n * Line color for the navigator series. Allows setting the color\n * while disallowing the default candlestick setting.\n *\n * @type {Highcharts.ColorString|null}\n */\n lineColor: null,\n marker: {\n enabled: false\n },\n /**\n * Since Highcharts Stock v8, default value is the same as default\n * `pointRange` defined for a specific type (e.g. `null` for\n * column type).\n *\n * In Highcharts Stock version < 8, defaults to 0.\n *\n * @extends plotOptions.series.pointRange\n * @type {number|null}\n * @apioption navigator.series.pointRange\n */\n /**\n * The threshold option. Setting it to 0 will make the default\n * navigator area series draw its area from the 0 value and up.\n *\n * @type {number|null}\n */\n threshold: null\n },\n /**\n * Enable or disable navigator sticking to right, while adding new\n * points. If `undefined`, the navigator sticks to the axis maximum only\n * if it was already at the maximum prior to adding points.\n *\n * @type {boolean}\n * @default undefined\n * @since 10.2.1\n * @sample {highstock} stock/navigator/sticktomax-false/\n * stickToMax set to false\n * @apioption navigator.stickToMax\n */\n /**\n * Options for the navigator X axis. Default series options for the\n * navigator xAxis are:\n * ```js\n * xAxis: {\n * tickWidth: 0,\n * lineWidth: 0,\n * gridLineWidth: 1,\n * tickPixelInterval: 200,\n * labels: {\n * align: 'left',\n * style: {\n * color: '#888'\n * },\n * x: 3,\n * y: -4\n * }\n * }\n * ```\n *\n * @extends xAxis\n * @excluding linkedTo, maxZoom, minRange, opposite, range, scrollbar,\n * showEmpty, maxRange\n */\n xAxis: {\n /**\n * Additional range on the right side of the xAxis. Works similar to\n * xAxis.maxPadding, but value is set in milliseconds.\n * Can be set for both, main xAxis and navigator's xAxis.\n *\n * @since 6.0.0\n */\n overscroll: 0,\n className: 'highcharts-navigator-xaxis',\n tickLength: 0,\n lineWidth: 0,\n gridLineColor: \"#e6e6e6\" /* Palette.neutralColor10 */,\n gridLineWidth: 1,\n tickPixelInterval: 200,\n labels: {\n align: 'left',\n /**\n * @type {Highcharts.CSSObject}\n */\n style: {\n /** @ignore */\n color: \"#000000\" /* Palette.neutralColor100 */,\n /** @ignore */\n fontSize: '0.7em',\n /** @ignore */\n opacity: 0.6,\n /** @ignore */\n textOutline: '2px contrast'\n },\n x: 3,\n y: -4\n },\n crosshair: false\n },\n /**\n * Options for the navigator Y axis. Default series options for the\n * navigator yAxis are:\n * ```js\n * yAxis: {\n * gridLineWidth: 0,\n * startOnTick: false,\n * endOnTick: false,\n * minPadding: 0.1,\n * maxPadding: 0.1,\n * labels: {\n * enabled: false\n * },\n * title: {\n * text: null\n * },\n * tickWidth: 0\n * }\n * ```\n *\n * @extends yAxis\n * @excluding height, linkedTo, maxZoom, minRange, ordinal, range,\n * showEmpty, scrollbar, top, units, maxRange, minLength,\n * maxLength, resize\n */\n yAxis: {\n className: 'highcharts-navigator-yaxis',\n gridLineWidth: 0,\n startOnTick: false,\n endOnTick: false,\n minPadding: 0.1,\n maxPadding: 0.1,\n labels: {\n enabled: false\n },\n crosshair: false,\n title: {\n text: null\n },\n tickLength: 0,\n tickWidth: 0\n }\n };\n /* *\n *\n * Default Export\n *\n * */\n /* *\n *\n * API Options\n *\n * */\n /**\n * Maximum range which can be set using the navigator's handles.\n * Opposite of [xAxis.minRange](#xAxis.minRange).\n *\n * @sample {highstock} stock/navigator/maxrange/\n * Defined max and min range\n *\n * @type {number}\n * @since 6.0.0\n * @product highstock gantt\n * @apioption xAxis.maxRange\n */\n (''); // keeps doclets above in JS file\n\n return NavigatorDefaults;\n });\n _registerModule(_modules, 'Stock/Navigator/NavigatorSymbols.js', [], function () {\n /* *\n *\n * (c) 2010-2021 Torstein Honsi\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n /* *\n *\n * Constants\n *\n * */\n /**\n * Draw one of the handles on the side of the zoomed range in the navigator.\n * @private\n */\n function navigatorHandle(_x, _y, width, height, options = {}) {\n const halfWidth = options.width ? options.width / 2 : width, markerPosition = Math.round(halfWidth / 3) + 0.5;\n height = options.height || height;\n return [\n ['M', -halfWidth - 1, 0.5],\n ['L', halfWidth, 0.5],\n ['L', halfWidth, height + 0.5],\n ['L', -halfWidth - 1, height + 0.5],\n ['L', -halfWidth - 1, 0.5],\n ['M', -markerPosition, 4],\n ['L', -markerPosition, height - 3],\n ['M', markerPosition - 1, 4],\n ['L', markerPosition - 1, height - 3]\n ];\n }\n /* *\n *\n * Default Export\n *\n * */\n const NavigatorSymbols = {\n 'navigator-handle': navigatorHandle\n };\n\n return NavigatorSymbols;\n });\n _registerModule(_modules, 'Stock/Navigator/NavigatorComposition.js', [_modules['Core/Defaults.js'], _modules['Core/Globals.js'], _modules['Core/Axis/NavigatorAxisComposition.js'], _modules['Stock/Navigator/NavigatorDefaults.js'], _modules['Stock/Navigator/NavigatorSymbols.js'], _modules['Core/Renderer/RendererRegistry.js'], _modules['Core/Utilities.js']], function (D, H, NavigatorAxisAdditions, NavigatorDefaults, NavigatorSymbols, RendererRegistry, U) {\n /* *\n *\n * (c) 2010-2021 Torstein Honsi\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { defaultOptions, setOptions } = D;\n const { isTouchDevice } = H;\n const { getRendererType } = RendererRegistry;\n const { addEvent, extend, merge, pick } = U;\n /* *\n *\n * Constants\n *\n * */\n const composedMembers = [];\n /* *\n *\n * Variables\n *\n * */\n let NavigatorConstructor;\n /* *\n *\n * Functions\n *\n * */\n /**\n * @private\n */\n function compose(AxisClass, ChartClass, NavigatorClass, SeriesClass) {\n NavigatorAxisAdditions.compose(AxisClass);\n NavigatorConstructor = NavigatorClass;\n if (U.pushUnique(composedMembers, ChartClass)) {\n const chartProto = ChartClass.prototype;\n chartProto.callbacks.push(onChartCallback);\n addEvent(ChartClass, 'afterAddSeries', onChartAfterAddSeries);\n addEvent(ChartClass, 'afterSetChartSize', onChartAfterSetChartSize);\n addEvent(ChartClass, 'afterUpdate', onChartAfterUpdate);\n addEvent(ChartClass, 'beforeRender', onChartBeforeRender);\n addEvent(ChartClass, 'beforeShowResetZoom', onChartBeforeShowResetZoom);\n addEvent(ChartClass, 'update', onChartUpdate);\n }\n if (U.pushUnique(composedMembers, SeriesClass)) {\n addEvent(SeriesClass, 'afterUpdate', onSeriesAfterUpdate);\n }\n if (U.pushUnique(composedMembers, getRendererType)) {\n extend(getRendererType().prototype.symbols, NavigatorSymbols);\n }\n if (U.pushUnique(composedMembers, setOptions)) {\n extend(defaultOptions, { navigator: NavigatorDefaults });\n }\n }\n /**\n * Handle adding new series.\n * @private\n */\n function onChartAfterAddSeries() {\n if (this.navigator) {\n // Recompute which series should be shown in navigator, and add them\n this.navigator.setBaseSeries(null, false);\n }\n }\n /**\n * For stock charts, extend the Chart.setChartSize method so that we can set the\n * final top position of the navigator once the height of the chart, including\n * the legend, is determined. #367. We can't use Chart.getMargins, because\n * labels offsets are not calculated yet.\n * @private\n */\n function onChartAfterSetChartSize() {\n const legend = this.legend, navigator = this.navigator;\n let legendOptions, xAxis, yAxis;\n if (navigator) {\n legendOptions = legend && legend.options;\n xAxis = navigator.xAxis;\n yAxis = navigator.yAxis;\n const { scrollbarHeight, scrollButtonSize } = navigator;\n // Compute the top position\n if (this.inverted) {\n navigator.left = navigator.opposite ?\n this.chartWidth - scrollbarHeight -\n navigator.height :\n this.spacing[3] + scrollbarHeight;\n navigator.top = this.plotTop + scrollButtonSize;\n }\n else {\n navigator.left = pick(xAxis.left, this.plotLeft + scrollButtonSize);\n navigator.top = navigator.navigatorOptions.top ||\n this.chartHeight -\n navigator.height -\n scrollbarHeight -\n (this.scrollbar?.options.margin || 0) -\n this.spacing[2] -\n (this.rangeSelector && this.extraBottomMargin ?\n this.rangeSelector.getHeight() :\n 0) -\n ((legendOptions &&\n legendOptions.verticalAlign === 'bottom' &&\n legendOptions.layout !== 'proximate' && // #13392\n legendOptions.enabled &&\n !legendOptions.floating) ?\n legend.legendHeight +\n pick(legendOptions.margin, 10) :\n 0) -\n (this.titleOffset ? this.titleOffset[2] : 0);\n }\n if (xAxis && yAxis) { // false if navigator is disabled (#904)\n if (this.inverted) {\n xAxis.options.left = yAxis.options.left = navigator.left;\n }\n else {\n xAxis.options.top = yAxis.options.top = navigator.top;\n }\n xAxis.setAxisSize();\n yAxis.setAxisSize();\n }\n }\n }\n /**\n * Initialize navigator, if no scrolling exists yet.\n * @private\n */\n function onChartAfterUpdate(event) {\n if (!this.navigator && !this.scroller &&\n (this.options.navigator.enabled ||\n this.options.scrollbar.enabled)) {\n this.scroller = this.navigator = new NavigatorConstructor(this);\n if (pick(event.redraw, true)) {\n this.redraw(event.animation); // #7067\n }\n }\n }\n /**\n * Initialize navigator for stock charts\n * @private\n */\n function onChartBeforeRender() {\n const options = this.options;\n if (options.navigator.enabled ||\n options.scrollbar.enabled) {\n this.scroller = this.navigator = new NavigatorConstructor(this);\n }\n }\n /**\n * For Stock charts. For x only zooming, do not to create the zoom button\n * because X axis zooming is already allowed by the Navigator and Range\n * selector. (#9285)\n * @private\n */\n function onChartBeforeShowResetZoom() {\n const chartOptions = this.options, navigator = chartOptions.navigator, rangeSelector = chartOptions.rangeSelector;\n if (((navigator && navigator.enabled) ||\n (rangeSelector && rangeSelector.enabled)) &&\n ((!isTouchDevice &&\n this.zooming.type === 'x') ||\n (isTouchDevice && this.zooming.pinchType === 'x'))) {\n return false;\n }\n }\n /**\n * @private\n */\n function onChartCallback(chart) {\n const navigator = chart.navigator;\n // Initialize the navigator\n if (navigator && chart.xAxis[0]) {\n const extremes = chart.xAxis[0].getExtremes();\n navigator.render(extremes.min, extremes.max);\n }\n }\n /**\n * Merge options, if no scrolling exists yet\n * @private\n */\n function onChartUpdate(e) {\n const navigatorOptions = (e.options.navigator || {}), scrollbarOptions = (e.options.scrollbar || {});\n if (!this.navigator && !this.scroller &&\n (navigatorOptions.enabled || scrollbarOptions.enabled)) {\n merge(true, this.options.navigator, navigatorOptions);\n merge(true, this.options.scrollbar, scrollbarOptions);\n delete e.options.navigator;\n delete e.options.scrollbar;\n }\n }\n /**\n * Handle updating series\n * @private\n */\n function onSeriesAfterUpdate() {\n if (this.chart.navigator && !this.options.isInternal) {\n this.chart.navigator.setBaseSeries(null, false);\n }\n }\n /* *\n *\n * Default Export\n *\n * */\n const NavigatorComposition = {\n compose\n };\n\n return NavigatorComposition;\n });\n _registerModule(_modules, 'Core/Axis/ScrollbarAxis.js', [_modules['Core/Utilities.js']], function (U) {\n /* *\n *\n * (c) 2010-2023 Torstein Honsi\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { addEvent, defined, pick, pushUnique } = U;\n /* *\n *\n * Composition\n *\n * */\n var ScrollbarAxis;\n (function (ScrollbarAxis) {\n /* *\n *\n * Constants\n *\n * */\n const composedMembers = [];\n /* *\n *\n * Variables\n *\n * */\n let Scrollbar;\n /* *\n *\n * Functions\n *\n * */\n /**\n * Attaches to axis events to create scrollbars if enabled.\n *\n * @private\n *\n * @param {Highcharts.Axis} AxisClass\n * Axis class to extend.\n *\n * @param {Highcharts.Scrollbar} ScrollbarClass\n * Scrollbar class to use.\n */\n function compose(AxisClass, ScrollbarClass) {\n if (pushUnique(composedMembers, ScrollbarClass)) {\n Scrollbar = ScrollbarClass;\n }\n if (pushUnique(composedMembers, AxisClass)) {\n addEvent(AxisClass, 'afterGetOffset', onAxisAfterGetOffset);\n addEvent(AxisClass, 'afterInit', onAxisAfterInit);\n addEvent(AxisClass, 'afterRender', onAxisAfterRender);\n }\n }\n ScrollbarAxis.compose = compose;\n /** @private */\n function getExtremes(axis) {\n const axisMin = pick(axis.options && axis.options.min, axis.min);\n const axisMax = pick(axis.options && axis.options.max, axis.max);\n return {\n axisMin,\n axisMax,\n scrollMin: defined(axis.dataMin) ?\n Math.min(axisMin, axis.min, axis.dataMin, pick(axis.threshold, Infinity)) : axisMin,\n scrollMax: defined(axis.dataMax) ?\n Math.max(axisMax, axis.max, axis.dataMax, pick(axis.threshold, -Infinity)) : axisMax\n };\n }\n /**\n * Make space for a scrollbar.\n * @private\n */\n function onAxisAfterGetOffset() {\n const axis = this, scrollbar = axis.scrollbar, opposite = scrollbar && !scrollbar.options.opposite, index = axis.horiz ? 2 : opposite ? 3 : 1;\n if (scrollbar) {\n // Reset scrollbars offsets\n axis.chart.scrollbarsOffsets = [0, 0];\n axis.chart.axisOffset[index] +=\n scrollbar.size + (scrollbar.options.margin || 0);\n }\n }\n /**\n * Wrap axis initialization and create scrollbar if enabled.\n * @private\n */\n function onAxisAfterInit() {\n const axis = this;\n if (axis.options &&\n axis.options.scrollbar &&\n axis.options.scrollbar.enabled) {\n // Predefined options:\n axis.options.scrollbar.vertical = !axis.horiz;\n axis.options.startOnTick = axis.options.endOnTick = false;\n axis.scrollbar = new Scrollbar(axis.chart.renderer, axis.options.scrollbar, axis.chart);\n addEvent(axis.scrollbar, 'changed', function (e) {\n const { axisMin, axisMax, scrollMin: unitedMin, scrollMax: unitedMax } = getExtremes(axis), range = unitedMax - unitedMin;\n let to, from;\n // #12834, scroll when show/hide series, wrong extremes\n if (!defined(axisMin) || !defined(axisMax)) {\n return;\n }\n if ((axis.horiz && !axis.reversed) ||\n (!axis.horiz && axis.reversed)) {\n to = unitedMin + range * this.to;\n from = unitedMin + range * this.from;\n }\n else {\n // Y-values in browser are reversed, but this also\n // applies for reversed horizontal axis:\n to = unitedMin + range * (1 - this.from);\n from = unitedMin + range * (1 - this.to);\n }\n if (this.shouldUpdateExtremes(e.DOMType)) {\n // #17977, set animation to undefined instead of true\n const animate = e.DOMType === 'mousemove' ||\n e.DOMType === 'touchmove' ? false : void 0;\n axis.setExtremes(from, to, true, animate, e);\n }\n else {\n // When live redraw is disabled, don't change extremes\n // Only change the position of the scollbar thumb\n this.setRange(this.from, this.to);\n }\n });\n }\n }\n /**\n * Wrap rendering axis, and update scrollbar if one is created.\n * @private\n */\n function onAxisAfterRender() {\n const axis = this, { scrollMin, scrollMax } = getExtremes(axis), scrollbar = axis.scrollbar, offset = (axis.axisTitleMargin + (axis.titleOffset || 0)), scrollbarsOffsets = axis.chart.scrollbarsOffsets, axisMargin = axis.options.margin || 0;\n let offsetsIndex, from, to;\n if (scrollbar && scrollbarsOffsets) {\n if (axis.horiz) {\n // Reserve space for labels/title\n if (!axis.opposite) {\n scrollbarsOffsets[1] += offset;\n }\n scrollbar.position(axis.left, (axis.top +\n axis.height +\n 2 +\n scrollbarsOffsets[1] -\n (axis.opposite ? axisMargin : 0)), axis.width, axis.height);\n // Next scrollbar should reserve space for margin (if set)\n if (!axis.opposite) {\n scrollbarsOffsets[1] += axisMargin;\n }\n offsetsIndex = 1;\n }\n else {\n // Reserve space for labels/title\n if (axis.opposite) {\n scrollbarsOffsets[0] += offset;\n }\n let xPosition;\n if (!scrollbar.options.opposite) {\n xPosition = axis.opposite ? 0 : axisMargin;\n }\n else {\n xPosition = axis.left +\n axis.width +\n 2 +\n scrollbarsOffsets[0] -\n (axis.opposite ? 0 : axisMargin);\n }\n scrollbar.position(xPosition, axis.top, axis.width, axis.height);\n // Next scrollbar should reserve space for margin (if set)\n if (axis.opposite) {\n scrollbarsOffsets[0] += axisMargin;\n }\n offsetsIndex = 0;\n }\n scrollbarsOffsets[offsetsIndex] += scrollbar.size +\n (scrollbar.options.margin || 0);\n if (isNaN(scrollMin) ||\n isNaN(scrollMax) ||\n !defined(axis.min) ||\n !defined(axis.max) ||\n axis.min === axis.max // #10733\n ) {\n // Default action: when extremes are the same or there is\n // not extremes on the axis, but scrollbar exists, make it\n // full size\n scrollbar.setRange(0, 1);\n }\n else {\n from = ((axis.min - scrollMin) /\n (scrollMax - scrollMin));\n to = ((axis.max - scrollMin) /\n (scrollMax - scrollMin));\n if ((axis.horiz && !axis.reversed) ||\n (!axis.horiz && axis.reversed)) {\n scrollbar.setRange(from, to);\n }\n else {\n // Inverse vertical axis\n scrollbar.setRange(1 - to, 1 - from);\n }\n }\n }\n }\n })(ScrollbarAxis || (ScrollbarAxis = {}));\n /* *\n *\n * Default Export\n *\n * */\n\n return ScrollbarAxis;\n });\n _registerModule(_modules, 'Stock/Scrollbar/ScrollbarDefaults.js', [_modules['Core/Globals.js']], function (H) {\n /* *\n *\n * (c) 2010-2021 Torstein Honsi\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { isTouchDevice } = H;\n /* *\n *\n * Constant\n *\n * */\n /**\n *\n * The scrollbar is a means of panning over the X axis of a stock chart.\n * Scrollbars can also be applied to other types of axes.\n *\n * Another approach to scrollable charts is the [chart.scrollablePlotArea](\n * https://api.highcharts.com/highcharts/chart.scrollablePlotArea) option that\n * is especially suitable for simpler cartesian charts on mobile.\n *\n * In styled mode, all the presentational options for the\n * scrollbar are replaced by the classes `.highcharts-scrollbar-thumb`,\n * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,\n * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.\n *\n * @sample stock/yaxis/inverted-bar-scrollbar/\n * A scrollbar on a simple bar chart\n *\n * @product highstock gantt\n * @optionparent scrollbar\n *\n * @private\n */\n const ScrollbarDefaults = {\n /**\n * The height of the scrollbar. If `buttonsEnabled` is true , the height\n * also applies to the width of the scroll arrows so that they are always\n * squares.\n *\n * @sample stock/scrollbar/style/\n * Non-default height\n *\n * @type {number}\n */\n height: 10,\n /**\n * The border rounding radius of the bar.\n *\n * @sample stock/scrollbar/style/\n * Scrollbar styling\n */\n barBorderRadius: 5,\n /**\n * The corner radius of the scrollbar buttons.\n *\n * @sample stock/scrollbar/style/\n * Scrollbar styling\n */\n buttonBorderRadius: 0,\n /**\n * Enable or disable the buttons at the end of the scrollbar.\n *\n * @since 11.0.0\n */\n buttonsEnabled: false,\n /**\n * Enable or disable the scrollbar.\n *\n * @sample stock/scrollbar/enabled/\n * Disable the scrollbar, only use navigator\n *\n * @type {boolean}\n * @default true\n * @apioption scrollbar.enabled\n */\n /**\n * Whether to redraw the main chart as the scrollbar or the navigator\n * zoomed window is moved. Defaults to `true` for modern browsers and\n * `false` for legacy IE browsers as well as mobile devices.\n *\n * @sample stock/scrollbar/liveredraw\n * Setting live redraw to false\n *\n * @type {boolean}\n * @since 1.3\n */\n liveRedraw: void 0,\n /**\n * The margin between the scrollbar and its axis when the scrollbar is\n * applied directly to an axis, or the navigator in case that is enabled.\n * Defaults to 10 for axis, 0 for navigator.\n *\n * @type {number|undefined}\n */\n margin: void 0,\n /**\n * The minimum width of the scrollbar.\n *\n * @since 1.2.5\n */\n minWidth: 6,\n /** @ignore-option */\n opposite: true,\n /**\n * Whether to show or hide the scrollbar when the scrolled content is\n * zoomed out to it full extent.\n *\n * @type {boolean}\n * @default true\n * @apioption scrollbar.showFull\n */\n step: 0.2,\n /**\n * The z index of the scrollbar group.\n */\n zIndex: 3,\n /**\n * The background color of the scrollbar itself.\n *\n * @sample stock/scrollbar/style/\n * Scrollbar styling\n *\n * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}\n */\n barBackgroundColor: \"#cccccc\" /* Palette.neutralColor20 */,\n /**\n * The width of the bar's border.\n *\n * @sample stock/scrollbar/style/\n * Scrollbar styling\n */\n barBorderWidth: 0,\n /**\n * The color of the scrollbar's border.\n *\n * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}\n */\n barBorderColor: \"#cccccc\" /* Palette.neutralColor20 */,\n /**\n * The color of the small arrow inside the scrollbar buttons.\n *\n * @sample stock/scrollbar/style/\n * Scrollbar styling\n *\n * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}\n */\n buttonArrowColor: \"#333333\" /* Palette.neutralColor80 */,\n /**\n * The color of scrollbar buttons.\n *\n * @sample stock/scrollbar/style/\n * Scrollbar styling\n *\n * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}\n */\n buttonBackgroundColor: \"#e6e6e6\" /* Palette.neutralColor10 */,\n /**\n * The color of the border of the scrollbar buttons.\n *\n * @sample stock/scrollbar/style/\n * Scrollbar styling\n *\n * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}\n */\n buttonBorderColor: \"#cccccc\" /* Palette.neutralColor20 */,\n /**\n * The border width of the scrollbar buttons.\n *\n * @sample stock/scrollbar/style/\n * Scrollbar styling\n */\n buttonBorderWidth: 1,\n /**\n * The color of the small rifles in the middle of the scrollbar.\n *\n * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}\n */\n rifleColor: 'none',\n /**\n * The color of the track background.\n *\n * @sample stock/scrollbar/style/\n * Scrollbar styling\n *\n * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}\n */\n trackBackgroundColor: 'rgba(255, 255, 255, 0.001)',\n /**\n * The color of the border of the scrollbar track.\n *\n * @sample stock/scrollbar/style/\n * Scrollbar styling\n *\n * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}\n */\n trackBorderColor: \"#cccccc\" /* Palette.neutralColor20 */,\n /**\n * The corner radius of the border of the scrollbar track.\n *\n * @sample stock/scrollbar/style/\n * Scrollbar styling\n */\n trackBorderRadius: 5,\n /**\n * The width of the border of the scrollbar track.\n *\n * @sample stock/scrollbar/style/\n * Scrollbar styling\n */\n trackBorderWidth: 1\n };\n /* *\n *\n * Default Export\n *\n * */\n\n return ScrollbarDefaults;\n });\n _registerModule(_modules, 'Stock/Scrollbar/Scrollbar.js', [_modules['Core/Defaults.js'], _modules['Core/Globals.js'], _modules['Core/Axis/ScrollbarAxis.js'], _modules['Stock/Scrollbar/ScrollbarDefaults.js'], _modules['Core/Utilities.js']], function (D, H, ScrollbarAxis, ScrollbarDefaults, U) {\n /* *\n *\n * (c) 2010-2021 Torstein Honsi\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { defaultOptions } = D;\n const { addEvent, correctFloat, defined, destroyObjectProperties, fireEvent, merge, pick, removeEvent } = U;\n /* *\n *\n * Constants\n *\n * */\n /* eslint-disable no-invalid-this, valid-jsdoc */\n /**\n * A reusable scrollbar, internally used in Highcharts Stock's\n * navigator and optionally on individual axes.\n *\n * @private\n * @class\n * @name Highcharts.Scrollbar\n * @param {Highcharts.SVGRenderer} renderer\n * @param {Highcharts.ScrollbarOptions} options\n * @param {Highcharts.Chart} chart\n */\n class Scrollbar {\n /* *\n *\n * Static Functions\n *\n * */\n static compose(AxisClass) {\n ScrollbarAxis.compose(AxisClass, Scrollbar);\n }\n /**\n * When we have vertical scrollbar, rifles and arrow in buttons should be\n * rotated. The same method is used in Navigator's handles, to rotate them.\n *\n * @function Highcharts.swapXY\n *\n * @param {Highcharts.SVGPathArray} path\n * Path to be rotated.\n *\n * @param {boolean} [vertical]\n * If vertical scrollbar, swap x-y values.\n *\n * @return {Highcharts.SVGPathArray}\n * Rotated path.\n *\n * @requires modules/stock\n */\n static swapXY(path, vertical) {\n if (vertical) {\n path.forEach((seg) => {\n const len = seg.length;\n let temp;\n for (let i = 0; i < len; i += 2) {\n temp = seg[i + 1];\n if (typeof temp === 'number') {\n seg[i + 1] = seg[i + 2];\n seg[i + 2] = temp;\n }\n }\n });\n }\n return path;\n }\n /* *\n *\n * Constructors\n *\n * */\n constructor(renderer, options, chart) {\n /* *\n *\n * Properties\n *\n * */\n this._events = [];\n this.chart = void 0;\n this.chartX = 0;\n this.chartY = 0;\n this.from = 0;\n this.group = void 0;\n this.options = void 0;\n this.renderer = void 0;\n this.scrollbar = void 0;\n this.scrollbarButtons = [];\n this.scrollbarGroup = void 0;\n this.scrollbarLeft = 0;\n this.scrollbarRifles = void 0;\n this.scrollbarStrokeWidth = 1;\n this.scrollbarTop = 0;\n this.size = 0;\n this.to = 0;\n this.track = void 0;\n this.trackBorderWidth = 1;\n this.userOptions = void 0;\n this.x = 0;\n this.y = 0;\n this.init(renderer, options, chart);\n }\n /* *\n *\n * Functions\n *\n * */\n /**\n * Set up the mouse and touch events for the Scrollbar\n *\n * @private\n * @function Highcharts.Scrollbar#addEvents\n */\n addEvents() {\n const buttonsOrder = this.options.inverted ? [1, 0] : [0, 1], buttons = this.scrollbarButtons, bar = this.scrollbarGroup.element, track = this.track.element, mouseDownHandler = this.mouseDownHandler.bind(this), mouseMoveHandler = this.mouseMoveHandler.bind(this), mouseUpHandler = this.mouseUpHandler.bind(this);\n // Mouse events\n const _events = [\n [\n buttons[buttonsOrder[0]].element,\n 'click',\n this.buttonToMinClick.bind(this)\n ],\n [\n buttons[buttonsOrder[1]].element,\n 'click',\n this.buttonToMaxClick.bind(this)\n ],\n [track, 'click', this.trackClick.bind(this)],\n [bar, 'mousedown', mouseDownHandler],\n [bar.ownerDocument, 'mousemove', mouseMoveHandler],\n [bar.ownerDocument, 'mouseup', mouseUpHandler]\n ];\n // Touch events\n if (H.hasTouch) {\n _events.push([bar, 'touchstart', mouseDownHandler], [bar.ownerDocument, 'touchmove', mouseMoveHandler], [bar.ownerDocument, 'touchend', mouseUpHandler]);\n }\n // Add them all\n _events.forEach(function (args) {\n addEvent.apply(null, args);\n });\n this._events = _events;\n }\n buttonToMaxClick(e) {\n const scroller = this;\n const range = ((scroller.to - scroller.from) *\n pick(scroller.options.step, 0.2));\n scroller.updatePosition(scroller.from + range, scroller.to + range);\n fireEvent(scroller, 'changed', {\n from: scroller.from,\n to: scroller.to,\n trigger: 'scrollbar',\n DOMEvent: e\n });\n }\n buttonToMinClick(e) {\n const scroller = this;\n const range = correctFloat(scroller.to - scroller.from) *\n pick(scroller.options.step, 0.2);\n scroller.updatePosition(correctFloat(scroller.from - range), correctFloat(scroller.to - range));\n fireEvent(scroller, 'changed', {\n from: scroller.from,\n to: scroller.to,\n trigger: 'scrollbar',\n DOMEvent: e\n });\n }\n /**\n * Get normalized (0-1) cursor position over the scrollbar\n *\n * @private\n * @function Highcharts.Scrollbar#cursorToScrollbarPosition\n *\n * @param {*} normalizedEvent\n * normalized event, with chartX and chartY values\n *\n * @return {Highcharts.Dictionary}\n * Local position {chartX, chartY}\n */\n cursorToScrollbarPosition(normalizedEvent) {\n const scroller = this, options = scroller.options, minWidthDifference = options.minWidth > scroller.calculatedWidth ?\n options.minWidth :\n 0; // minWidth distorts translation\n return {\n chartX: (normalizedEvent.chartX - scroller.x -\n scroller.xOffset) /\n (scroller.barWidth - minWidthDifference),\n chartY: (normalizedEvent.chartY - scroller.y -\n scroller.yOffset) /\n (scroller.barWidth - minWidthDifference)\n };\n }\n /**\n * Destroys allocated elements.\n *\n * @private\n * @function Highcharts.Scrollbar#destroy\n */\n destroy() {\n const scroller = this, navigator = scroller.chart.scroller;\n // Disconnect events added in addEvents\n scroller.removeEvents();\n // Destroy properties\n [\n 'track',\n 'scrollbarRifles',\n 'scrollbar',\n 'scrollbarGroup',\n 'group'\n ].forEach(function (prop) {\n if (scroller[prop] && scroller[prop].destroy) {\n scroller[prop] = scroller[prop].destroy();\n }\n });\n // #6421, chart may have more scrollbars\n if (navigator && scroller === navigator.scrollbar) {\n navigator.scrollbar = null;\n // Destroy elements in collection\n destroyObjectProperties(navigator.scrollbarButtons);\n }\n }\n /**\n * Draw the scrollbar buttons with arrows\n *\n * @private\n * @function Highcharts.Scrollbar#drawScrollbarButton\n * @param {number} index\n * 0 is left, 1 is right\n */\n drawScrollbarButton(index) {\n const scroller = this, renderer = scroller.renderer, scrollbarButtons = scroller.scrollbarButtons, options = scroller.options, size = scroller.size, group = renderer.g().add(scroller.group);\n scrollbarButtons.push(group);\n if (options.buttonsEnabled) {\n // Create a rectangle for the scrollbar button\n const rect = renderer.rect()\n .addClass('highcharts-scrollbar-button')\n .add(group);\n // Presentational attributes\n if (!scroller.chart.styledMode) {\n rect.attr({\n stroke: options.buttonBorderColor,\n 'stroke-width': options.buttonBorderWidth,\n fill: options.buttonBackgroundColor\n });\n }\n // Place the rectangle based on the rendered stroke width\n rect.attr(rect.crisp({\n x: -0.5,\n y: -0.5,\n // +1 to compensate for crispifying in rect method\n width: size + 1,\n height: size + 1,\n r: options.buttonBorderRadius\n }, rect.strokeWidth()));\n // Button arrow\n const arrow = renderer\n .path(Scrollbar.swapXY([[\n 'M',\n size / 2 + (index ? -1 : 1),\n size / 2 - 3\n ], [\n 'L',\n size / 2 + (index ? -1 : 1),\n size / 2 + 3\n ], [\n 'L',\n size / 2 + (index ? 2 : -2),\n size / 2\n ]], options.vertical))\n .addClass('highcharts-scrollbar-arrow')\n .add(scrollbarButtons[index]);\n if (!scroller.chart.styledMode) {\n arrow.attr({\n fill: options.buttonArrowColor\n });\n }\n }\n }\n /**\n * @private\n * @function Highcharts.Scrollbar#init\n * @param {Highcharts.SVGRenderer} renderer\n * @param {Highcharts.ScrollbarOptions} options\n * @param {Highcharts.Chart} chart\n */\n init(renderer, options, chart) {\n const scroller = this;\n scroller.scrollbarButtons = [];\n scroller.renderer = renderer;\n scroller.userOptions = options;\n scroller.options = merge(ScrollbarDefaults, defaultOptions.scrollbar, options);\n scroller.options.margin = pick(scroller.options.margin, 10);\n scroller.chart = chart;\n // backward compatibility\n scroller.size = pick(scroller.options.size, scroller.options.height);\n // Init\n if (options.enabled) {\n scroller.render();\n scroller.addEvents();\n }\n }\n mouseDownHandler(e) {\n const scroller = this, normalizedEvent = scroller.chart.pointer.normalize(e), mousePosition = scroller.cursorToScrollbarPosition(normalizedEvent);\n scroller.chartX = mousePosition.chartX;\n scroller.chartY = mousePosition.chartY;\n scroller.initPositions = [scroller.from, scroller.to];\n scroller.grabbedCenter = true;\n }\n /**\n * Event handler for the mouse move event.\n * @private\n */\n mouseMoveHandler(e) {\n const scroller = this, normalizedEvent = scroller.chart.pointer.normalize(e), options = scroller.options, direction = options.vertical ?\n 'chartY' : 'chartX', initPositions = scroller.initPositions || [];\n let scrollPosition, chartPosition, change;\n // In iOS, a mousemove event with e.pageX === 0 is fired when\n // holding the finger down in the center of the scrollbar. This\n // should be ignored.\n if (scroller.grabbedCenter &&\n // #4696, scrollbar failed on Android\n (!e.touches || e.touches[0][direction] !== 0)) {\n chartPosition = scroller.cursorToScrollbarPosition(normalizedEvent)[direction];\n scrollPosition = scroller[direction];\n change = chartPosition - scrollPosition;\n scroller.hasDragged = true;\n scroller.updatePosition(initPositions[0] + change, initPositions[1] + change);\n if (scroller.hasDragged) {\n fireEvent(scroller, 'changed', {\n from: scroller.from,\n to: scroller.to,\n trigger: 'scrollbar',\n DOMType: e.type,\n DOMEvent: e\n });\n }\n }\n }\n /**\n * Event handler for the mouse up event.\n * @private\n */\n mouseUpHandler(e) {\n const scroller = this;\n if (scroller.hasDragged) {\n fireEvent(scroller, 'changed', {\n from: scroller.from,\n to: scroller.to,\n trigger: 'scrollbar',\n DOMType: e.type,\n DOMEvent: e\n });\n }\n scroller.grabbedCenter =\n scroller.hasDragged =\n scroller.chartX =\n scroller.chartY = null;\n }\n /**\n * Position the scrollbar, method called from a parent with defined\n * dimensions.\n *\n * @private\n * @function Highcharts.Scrollbar#position\n * @param {number} x\n * x-position on the chart\n * @param {number} y\n * y-position on the chart\n * @param {number} width\n * width of the scrollbar\n * @param {number} height\n * height of the scorllbar\n */\n position(x, y, width, height) {\n const scroller = this, options = scroller.options, { buttonsEnabled, margin = 0, vertical } = options, method = scroller.rendered ? 'animate' : 'attr';\n let xOffset = height, yOffset = 0;\n // Make the scrollbar visible when it is repositioned, #15763.\n scroller.group.show();\n scroller.x = x;\n scroller.y = y + this.trackBorderWidth;\n scroller.width = width; // width with buttons\n scroller.height = height;\n scroller.xOffset = xOffset;\n scroller.yOffset = yOffset;\n // If Scrollbar is a vertical type, swap options:\n if (vertical) {\n scroller.width = scroller.yOffset = width = yOffset = scroller.size;\n scroller.xOffset = xOffset = 0;\n scroller.yOffset = yOffset = buttonsEnabled ? scroller.size : 0;\n // width without buttons\n scroller.barWidth = height - (buttonsEnabled ? width * 2 : 0);\n scroller.x = x = x + margin;\n }\n else {\n scroller.height = height = scroller.size;\n scroller.xOffset = xOffset = buttonsEnabled ? scroller.size : 0;\n // width without buttons\n scroller.barWidth = width - (buttonsEnabled ? height * 2 : 0);\n scroller.y = scroller.y + margin;\n }\n // Set general position for a group:\n scroller.group[method]({\n translateX: x,\n translateY: scroller.y\n });\n // Resize background/track:\n scroller.track[method]({\n width: width,\n height: height\n });\n // Move right/bottom button to its place:\n scroller.scrollbarButtons[1][method]({\n translateX: vertical ? 0 : width - xOffset,\n translateY: vertical ? height - yOffset : 0\n });\n }\n /**\n * Removes the event handlers attached previously with addEvents.\n *\n * @private\n * @function Highcharts.Scrollbar#removeEvents\n */\n removeEvents() {\n this._events.forEach(function (args) {\n removeEvent.apply(null, args);\n });\n this._events.length = 0;\n }\n /**\n * Render scrollbar with all required items.\n *\n * @private\n * @function Highcharts.Scrollbar#render\n */\n render() {\n const scroller = this, renderer = scroller.renderer, options = scroller.options, size = scroller.size, styledMode = scroller.chart.styledMode, group = renderer.g('scrollbar')\n .attr({\n zIndex: options.zIndex\n })\n .hide() // initially hide the scrollbar #15863\n .add();\n // Draw the scrollbar group\n scroller.group = group;\n // Draw the scrollbar track:\n scroller.track = renderer.rect()\n .addClass('highcharts-scrollbar-track')\n .attr({\n r: options.trackBorderRadius || 0,\n height: size,\n width: size\n }).add(group);\n if (!styledMode) {\n scroller.track.attr({\n fill: options.trackBackgroundColor,\n stroke: options.trackBorderColor,\n 'stroke-width': options.trackBorderWidth\n });\n }\n const trackBorderWidth = scroller.trackBorderWidth =\n scroller.track.strokeWidth();\n scroller.track.attr({\n x: -trackBorderWidth % 2 / 2,\n y: -trackBorderWidth % 2 / 2\n });\n // Draw the scrollbar itself\n scroller.scrollbarGroup = renderer.g().add(group);\n scroller.scrollbar = renderer.rect()\n .addClass('highcharts-scrollbar-thumb')\n .attr({\n height: size - trackBorderWidth,\n width: size - trackBorderWidth,\n r: options.barBorderRadius || 0\n }).add(scroller.scrollbarGroup);\n scroller.scrollbarRifles = renderer\n .path(Scrollbar.swapXY([\n ['M', -3, size / 4],\n ['L', -3, 2 * size / 3],\n ['M', 0, size / 4],\n ['L', 0, 2 * size / 3],\n ['M', 3, size / 4],\n ['L', 3, 2 * size / 3]\n ], options.vertical))\n .addClass('highcharts-scrollbar-rifles')\n .add(scroller.scrollbarGroup);\n if (!styledMode) {\n scroller.scrollbar.attr({\n fill: options.barBackgroundColor,\n stroke: options.barBorderColor,\n 'stroke-width': options.barBorderWidth\n });\n scroller.scrollbarRifles.attr({\n stroke: options.rifleColor,\n 'stroke-width': 1\n });\n }\n scroller.scrollbarStrokeWidth = scroller.scrollbar.strokeWidth();\n scroller.scrollbarGroup.translate(-scroller.scrollbarStrokeWidth % 2 / 2, -scroller.scrollbarStrokeWidth % 2 / 2);\n // Draw the buttons:\n scroller.drawScrollbarButton(0);\n scroller.drawScrollbarButton(1);\n }\n /**\n * Set scrollbar size, with a given scale.\n *\n * @private\n * @function Highcharts.Scrollbar#setRange\n * @param {number} from\n * scale (0-1) where bar should start\n * @param {number} to\n * scale (0-1) where bar should end\n */\n setRange(from, to) {\n const scroller = this, options = scroller.options, vertical = options.vertical, minWidth = options.minWidth, fullWidth = scroller.barWidth, method = (this.rendered &&\n !this.hasDragged &&\n !(this.chart.navigator && this.chart.navigator.hasDragged)) ? 'animate' : 'attr';\n if (!defined(fullWidth)) {\n return;\n }\n const toPX = fullWidth * Math.min(to, 1);\n let fromPX, newSize;\n from = Math.max(from, 0);\n fromPX = Math.ceil(fullWidth * from);\n scroller.calculatedWidth = newSize = correctFloat(toPX - fromPX);\n // We need to recalculate position, if minWidth is used\n if (newSize < minWidth) {\n fromPX = (fullWidth - minWidth + newSize) * from;\n newSize = minWidth;\n }\n const newPos = Math.floor(fromPX + scroller.xOffset + scroller.yOffset);\n const newRiflesPos = newSize / 2 - 0.5; // -0.5 -> rifle line width / 2\n // Store current position:\n scroller.from = from;\n scroller.to = to;\n if (!vertical) {\n scroller.scrollbarGroup[method]({\n translateX: newPos\n });\n scroller.scrollbar[method]({\n width: newSize\n });\n scroller.scrollbarRifles[method]({\n translateX: newRiflesPos\n });\n scroller.scrollbarLeft = newPos;\n scroller.scrollbarTop = 0;\n }\n else {\n scroller.scrollbarGroup[method]({\n translateY: newPos\n });\n scroller.scrollbar[method]({\n height: newSize\n });\n scroller.scrollbarRifles[method]({\n translateY: newRiflesPos\n });\n scroller.scrollbarTop = newPos;\n scroller.scrollbarLeft = 0;\n }\n if (newSize <= 12) {\n scroller.scrollbarRifles.hide();\n }\n else {\n scroller.scrollbarRifles.show();\n }\n // Show or hide the scrollbar based on the showFull setting\n if (options.showFull === false) {\n if (from <= 0 && to >= 1) {\n scroller.group.hide();\n }\n else {\n scroller.group.show();\n }\n }\n scroller.rendered = true;\n }\n /**\n * Checks if the extremes should be updated in response to a scrollbar\n * change event.\n *\n * @private\n * @function Highcharts.Scrollbar#shouldUpdateExtremes\n */\n shouldUpdateExtremes(eventType) {\n return (pick(this.options.liveRedraw, H.svg &&\n !H.isTouchDevice &&\n !this.chart.boosted) ||\n // Mouseup always should change extremes\n eventType === 'mouseup' ||\n eventType === 'touchend' ||\n // Internal events\n !defined(eventType));\n }\n trackClick(e) {\n const scroller = this;\n const normalizedEvent = scroller.chart.pointer.normalize(e), range = scroller.to - scroller.from, top = scroller.y + scroller.scrollbarTop, left = scroller.x + scroller.scrollbarLeft;\n if ((scroller.options.vertical && normalizedEvent.chartY > top) ||\n (!scroller.options.vertical && normalizedEvent.chartX > left)) {\n // On the top or on the left side of the track:\n scroller.updatePosition(scroller.from + range, scroller.to + range);\n }\n else {\n // On the bottom or the right side of the track:\n scroller.updatePosition(scroller.from - range, scroller.to - range);\n }\n fireEvent(scroller, 'changed', {\n from: scroller.from,\n to: scroller.to,\n trigger: 'scrollbar',\n DOMEvent: e\n });\n }\n /**\n * Update the scrollbar with new options\n *\n * @private\n * @function Highcharts.Scrollbar#update\n * @param {Highcharts.ScrollbarOptions} options\n */\n update(options) {\n this.destroy();\n this.init(this.chart.renderer, merge(true, this.options, options), this.chart);\n }\n /**\n * Update position option in the Scrollbar, with normalized 0-1 scale\n *\n * @private\n * @function Highcharts.Scrollbar#updatePosition\n * @param {number} from\n * @param {number} to\n */\n updatePosition(from, to) {\n if (to > 1) {\n from = correctFloat(1 - correctFloat(to - from));\n to = 1;\n }\n if (from < 0) {\n to = correctFloat(to - from);\n from = 0;\n }\n this.from = from;\n this.to = to;\n }\n }\n /* *\n *\n * Static Properties\n *\n * */\n Scrollbar.defaultOptions = ScrollbarDefaults;\n /* *\n *\n * Registry\n *\n * */\n defaultOptions.scrollbar = merge(true, Scrollbar.defaultOptions, defaultOptions.scrollbar);\n /* *\n *\n * Default Export\n *\n * */\n\n return Scrollbar;\n });\n _registerModule(_modules, 'Stock/Navigator/Navigator.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Defaults.js'], _modules['Core/Globals.js'], _modules['Core/Axis/NavigatorAxisComposition.js'], _modules['Stock/Navigator/NavigatorComposition.js'], _modules['Stock/Scrollbar/Scrollbar.js'], _modules['Core/Utilities.js']], function (Axis, D, H, NavigatorAxisAdditions, NavigatorComposition, Scrollbar, U) {\n /* *\n *\n * (c) 2010-2021 Torstein Honsi\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { defaultOptions } = D;\n const { hasTouch, isTouchDevice } = H;\n const { addEvent, clamp, correctFloat, defined, destroyObjectProperties, erase, extend, find, fireEvent, isArray, isNumber, merge, pick, removeEvent, splat } = U;\n /* *\n *\n * Functions\n *\n * */\n /**\n * Finding the min or max of a set of variables where we don't know if they are\n * defined, is a pattern that is repeated several places in Highcharts. Consider\n * making this a global utility method.\n * @private\n */\n function numExt(extreme, ...args) {\n const numbers = [].filter.call(args, isNumber);\n if (numbers.length) {\n return Math[extreme].apply(0, numbers);\n }\n }\n /* *\n *\n * Class\n *\n * */\n /**\n * The Navigator class\n *\n * @private\n * @class\n * @name Highcharts.Navigator\n *\n * @param {Highcharts.Chart} chart\n * Chart object\n */\n class Navigator {\n /* *\n *\n * Static Functions\n *\n * */\n static compose(AxisClass, ChartClass, SeriesClass) {\n NavigatorComposition.compose(AxisClass, ChartClass, Navigator, SeriesClass);\n }\n /* *\n *\n * Constructor\n *\n * */\n constructor(chart) {\n /* *\n *\n * Properties\n *\n * */\n this.baseSeries = void 0;\n this.chart = void 0;\n this.handles = void 0;\n this.height = void 0;\n this.left = void 0;\n this.navigatorEnabled = void 0;\n this.navigatorGroup = void 0;\n this.navigatorOptions = void 0;\n this.navigatorSeries = void 0;\n this.navigatorSize = void 0;\n this.opposite = void 0;\n this.outline = void 0;\n this.range = void 0;\n this.rendered = void 0;\n this.scrollbarHeight = 0;\n this.scrollButtonSize = void 0;\n this.shades = void 0;\n this.size = void 0;\n this.top = void 0;\n this.xAxis = void 0;\n this.yAxis = void 0;\n this.zoomedMax = void 0;\n this.zoomedMin = void 0;\n this.init(chart);\n }\n /* *\n *\n * Functions\n *\n * */\n /**\n * Draw one of the handles on the side of the zoomed range in the navigator.\n *\n * @private\n * @function Highcharts.Navigator#drawHandle\n *\n * @param {number} x\n * The x center for the handle\n *\n * @param {number} index\n * 0 for left and 1 for right\n *\n * @param {boolean|undefined} inverted\n * Flag for chart.inverted\n *\n * @param {string} verb\n * Use 'animate' or 'attr'\n */\n drawHandle(x, index, inverted, verb) {\n const navigator = this, height = navigator.navigatorOptions.handles.height;\n // Place it\n navigator.handles[index][verb](inverted ? {\n translateX: Math.round(navigator.left + navigator.height / 2),\n translateY: Math.round(navigator.top + parseInt(x, 10) + 0.5 - height)\n } : {\n translateX: Math.round(navigator.left + parseInt(x, 10)),\n translateY: Math.round(navigator.top + navigator.height / 2 - height / 2 - 1)\n });\n }\n /**\n * Render outline around the zoomed range\n *\n * @private\n * @function Highcharts.Navigator#drawOutline\n *\n * @param {number} zoomedMin\n * in pixels position where zoomed range starts\n *\n * @param {number} zoomedMax\n * in pixels position where zoomed range ends\n *\n * @param {boolean|undefined} inverted\n * flag if chart is inverted\n *\n * @param {string} verb\n * use 'animate' or 'attr'\n */\n drawOutline(zoomedMin, zoomedMax, inverted, verb) {\n const navigator = this, maskInside = navigator.navigatorOptions.maskInside, outlineWidth = navigator.outline.strokeWidth(), halfOutline = outlineWidth / 2, outlineCorrection = (outlineWidth % 2) / 2, // #5800\n scrollButtonSize = navigator.scrollButtonSize, navigatorSize = navigator.size, navigatorTop = navigator.top, height = navigator.height, lineTop = navigatorTop - halfOutline, lineBtm = navigatorTop + height;\n let left = navigator.left, verticalMin, path;\n if (inverted) {\n verticalMin = navigatorTop + zoomedMax + outlineCorrection;\n zoomedMax = navigatorTop + zoomedMin + outlineCorrection;\n path = [\n [\n 'M',\n left + height,\n navigatorTop - scrollButtonSize - outlineCorrection\n ],\n // top right of zoomed range\n ['L', left + height, verticalMin],\n ['L', left, verticalMin],\n ['M', left, zoomedMax],\n ['L', left + height, zoomedMax],\n [\n 'L',\n left + height,\n navigatorTop + navigatorSize + scrollButtonSize\n ]\n ];\n if (maskInside) {\n path.push(\n // upper left of zoomed range\n ['M', left + height, verticalMin - halfOutline], \n // upper right of z.r.\n [\n 'L',\n left + height,\n zoomedMax + halfOutline\n ]);\n }\n }\n else {\n left -= scrollButtonSize;\n zoomedMin += left + scrollButtonSize - outlineCorrection;\n zoomedMax += left + scrollButtonSize - outlineCorrection;\n path = [\n // left\n ['M', left, lineTop],\n // upper left of zoomed range\n ['L', zoomedMin, lineTop],\n // lower left of z.r.\n ['L', zoomedMin, lineBtm],\n // lower right of z.r.\n ['M', zoomedMax, lineBtm],\n // upper right of z.r.\n ['L', zoomedMax, lineTop],\n // right\n [\n 'L',\n left + navigatorSize + scrollButtonSize * 2,\n navigatorTop + halfOutline\n ]\n ];\n if (maskInside) {\n path.push(\n // upper left of zoomed range\n ['M', zoomedMin - halfOutline, lineTop], \n // upper right of z.r.\n ['L', zoomedMax + halfOutline, lineTop]);\n }\n }\n navigator.outline[verb]({\n d: path\n });\n }\n /**\n * Render outline around the zoomed range\n *\n * @private\n * @function Highcharts.Navigator#drawMasks\n *\n * @param {number} zoomedMin\n * in pixels position where zoomed range starts\n *\n * @param {number} zoomedMax\n * in pixels position where zoomed range ends\n *\n * @param {boolean|undefined} inverted\n * flag if chart is inverted\n *\n * @param {string} verb\n * use 'animate' or 'attr'\n */\n drawMasks(zoomedMin, zoomedMax, inverted, verb) {\n const navigator = this, left = navigator.left, top = navigator.top, navigatorHeight = navigator.height;\n let height, width, x, y;\n // Determine rectangle position & size\n // According to (non)inverted position:\n if (inverted) {\n x = [left, left, left];\n y = [top, top + zoomedMin, top + zoomedMax];\n width = [navigatorHeight, navigatorHeight, navigatorHeight];\n height = [\n zoomedMin,\n zoomedMax - zoomedMin,\n navigator.size - zoomedMax\n ];\n }\n else {\n x = [left, left + zoomedMin, left + zoomedMax];\n y = [top, top, top];\n width = [\n zoomedMin,\n zoomedMax - zoomedMin,\n navigator.size - zoomedMax\n ];\n height = [navigatorHeight, navigatorHeight, navigatorHeight];\n }\n navigator.shades.forEach((shade, i) => {\n shade[verb]({\n x: x[i],\n y: y[i],\n width: width[i],\n height: height[i]\n });\n });\n }\n /**\n * Generate DOM elements for a navigator:\n *\n * - main navigator group\n *\n * - all shades\n *\n * - outline\n *\n * - handles\n *\n * @private\n * @function Highcharts.Navigator#renderElements\n */\n renderElements() {\n const navigator = this, navigatorOptions = navigator.navigatorOptions, maskInside = navigatorOptions.maskInside, chart = navigator.chart, inverted = chart.inverted, renderer = chart.renderer, mouseCursor = {\n cursor: inverted ? 'ns-resize' : 'ew-resize'\n }, \n // Create the main navigator group\n navigatorGroup = navigator.navigatorGroup = renderer\n .g('navigator')\n .attr({\n zIndex: 8,\n visibility: 'hidden'\n })\n .add();\n // Create masks, each mask will get events and fill:\n [\n !maskInside,\n maskInside,\n !maskInside\n ].forEach((hasMask, index) => {\n const shade = renderer.rect()\n .addClass('highcharts-navigator-mask' +\n (index === 1 ? '-inside' : '-outside'))\n .add(navigatorGroup);\n if (!chart.styledMode) {\n shade.attr({\n fill: hasMask ?\n navigatorOptions.maskFill :\n 'rgba(0,0,0,0)'\n });\n if (index === 1) {\n shade.css(mouseCursor);\n }\n }\n navigator.shades[index] = shade;\n });\n // Create the outline:\n navigator.outline = renderer.path()\n .addClass('highcharts-navigator-outline')\n .add(navigatorGroup);\n if (!chart.styledMode) {\n navigator.outline.attr({\n 'stroke-width': navigatorOptions.outlineWidth,\n stroke: navigatorOptions.outlineColor\n });\n }\n // Create the handlers:\n if (navigatorOptions.handles && navigatorOptions.handles.enabled) {\n const handlesOptions = navigatorOptions.handles, { height, width } = handlesOptions;\n [0, 1].forEach((index) => {\n navigator.handles[index] = renderer.symbol(handlesOptions.symbols[index], -width / 2 - 1, 0, width, height, handlesOptions);\n if (chart.inverted) {\n navigator.handles[index].attr({\n rotation: 90,\n rotationOriginX: Math.floor(-width / 2),\n rotationOriginY: (height + width) / 2\n });\n }\n // zIndex = 6 for right handle, 7 for left.\n // Can't be 10, because of the tooltip in inverted chart #2908\n navigator.handles[index].attr({ zIndex: 7 - index })\n .addClass('highcharts-navigator-handle ' +\n 'highcharts-navigator-handle-' +\n ['left', 'right'][index]).add(navigatorGroup);\n if (!chart.styledMode) {\n navigator.handles[index]\n .attr({\n fill: handlesOptions.backgroundColor,\n stroke: handlesOptions.borderColor,\n 'stroke-width': handlesOptions.lineWidth\n })\n .css(mouseCursor);\n }\n });\n }\n }\n /**\n * Update navigator\n *\n * @private\n * @function Highcharts.Navigator#update\n *\n * @param {Highcharts.NavigatorOptions} options\n * Options to merge in when updating navigator\n */\n update(options) {\n // Remove references to old navigator series in base series\n (this.series || []).forEach((series) => {\n if (series.baseSeries) {\n delete series.baseSeries.navigatorSeries;\n }\n });\n // Destroy and rebuild navigator\n this.destroy();\n const chartOptions = this.chart.options;\n merge(true, chartOptions.navigator, options);\n this.init(this.chart);\n }\n /**\n * Render the navigator\n *\n * @private\n * @function Highcharts.Navigator#render\n * @param {number} min\n * X axis value minimum\n * @param {number} max\n * X axis value maximum\n * @param {number} [pxMin]\n * Pixel value minimum\n * @param {number} [pxMax]\n * Pixel value maximum\n */\n render(min, max, pxMin, pxMax) {\n const navigator = this, chart = navigator.chart, xAxis = navigator.xAxis, pointRange = xAxis.pointRange || 0, scrollbarXAxis = xAxis.navigatorAxis.fake ? chart.xAxis[0] : xAxis, navigatorEnabled = navigator.navigatorEnabled, rendered = navigator.rendered, inverted = chart.inverted, minRange = chart.xAxis[0].minRange, maxRange = chart.xAxis[0].options.maxRange, scrollButtonSize = navigator.scrollButtonSize;\n let navigatorWidth, scrollbarLeft, scrollbarTop, scrollbarHeight = navigator.scrollbarHeight, navigatorSize, verb;\n // Don't redraw while moving the handles (#4703).\n if (this.hasDragged && !defined(pxMin)) {\n return;\n }\n min = correctFloat(min - pointRange / 2);\n max = correctFloat(max + pointRange / 2);\n // Don't render the navigator until we have data (#486, #4202, #5172).\n if (!isNumber(min) || !isNumber(max)) {\n // However, if navigator was already rendered, we may need to resize\n // it. For example hidden series, but visible navigator (#6022).\n if (rendered) {\n pxMin = 0;\n pxMax = pick(xAxis.width, scrollbarXAxis.width);\n }\n else {\n return;\n }\n }\n navigator.left = pick(xAxis.left, \n // in case of scrollbar only, without navigator\n chart.plotLeft + scrollButtonSize +\n (inverted ? chart.plotWidth : 0));\n let zoomedMax = navigator.size = navigatorSize = pick(xAxis.len, (inverted ? chart.plotHeight : chart.plotWidth) -\n 2 * scrollButtonSize);\n if (inverted) {\n navigatorWidth = scrollbarHeight;\n }\n else {\n navigatorWidth = navigatorSize + 2 * scrollButtonSize;\n }\n // Get the pixel position of the handles\n pxMin = pick(pxMin, xAxis.toPixels(min, true));\n pxMax = pick(pxMax, xAxis.toPixels(max, true));\n // Verify (#1851, #2238)\n if (!isNumber(pxMin) || Math.abs(pxMin) === Infinity) {\n pxMin = 0;\n pxMax = navigatorWidth;\n }\n // Are we below the minRange? (#2618, #6191)\n const newMin = xAxis.toValue(pxMin, true), newMax = xAxis.toValue(pxMax, true), currentRange = Math.abs(correctFloat(newMax - newMin));\n if (currentRange < minRange) {\n if (this.grabbedLeft) {\n pxMin = xAxis.toPixels(newMax - minRange - pointRange, true);\n }\n else if (this.grabbedRight) {\n pxMax = xAxis.toPixels(newMin + minRange + pointRange, true);\n }\n }\n else if (defined(maxRange) &&\n correctFloat(currentRange - pointRange) > maxRange) {\n if (this.grabbedLeft) {\n pxMin = xAxis.toPixels(newMax - maxRange - pointRange, true);\n }\n else if (this.grabbedRight) {\n pxMax = xAxis.toPixels(newMin + maxRange + pointRange, true);\n }\n }\n // Handles are allowed to cross, but never exceed the plot area\n navigator.zoomedMax = clamp(Math.max(pxMin, pxMax), 0, zoomedMax);\n navigator.zoomedMin = clamp(navigator.fixedWidth ?\n navigator.zoomedMax - navigator.fixedWidth :\n Math.min(pxMin, pxMax), 0, zoomedMax);\n navigator.range = navigator.zoomedMax - navigator.zoomedMin;\n zoomedMax = Math.round(navigator.zoomedMax);\n const zoomedMin = Math.round(navigator.zoomedMin);\n if (navigatorEnabled) {\n navigator.navigatorGroup.attr({\n visibility: 'inherit'\n });\n // Place elements\n verb = rendered && !navigator.hasDragged ? 'animate' : 'attr';\n navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);\n navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);\n if (navigator.navigatorOptions.handles.enabled) {\n navigator.drawHandle(zoomedMin, 0, inverted, verb);\n navigator.drawHandle(zoomedMax, 1, inverted, verb);\n }\n }\n if (navigator.scrollbar) {\n if (inverted) {\n scrollbarTop = navigator.top - scrollButtonSize;\n scrollbarLeft = navigator.left - scrollbarHeight +\n (navigatorEnabled || !scrollbarXAxis.opposite ? 0 :\n // Multiple axes has offsets:\n (scrollbarXAxis.titleOffset || 0) +\n // Self margin from the axis.title\n scrollbarXAxis.axisTitleMargin);\n scrollbarHeight = navigatorSize + 2 * scrollButtonSize;\n }\n else {\n scrollbarTop = navigator.top + (navigatorEnabled ?\n navigator.height :\n -scrollbarHeight);\n scrollbarLeft = navigator.left - scrollButtonSize;\n }\n // Reposition scrollbar\n navigator.scrollbar.position(scrollbarLeft, scrollbarTop, navigatorWidth, scrollbarHeight);\n // Keep scale 0-1\n navigator.scrollbar.setRange(\n // Use real value, not rounded because range can be very small\n // (#1716)\n navigator.zoomedMin / (navigatorSize || 1), navigator.zoomedMax / (navigatorSize || 1));\n }\n navigator.rendered = true;\n fireEvent(this, 'afterRender');\n }\n /**\n * Set up the mouse and touch events for the navigator\n *\n * @private\n * @function Highcharts.Navigator#addMouseEvents\n */\n addMouseEvents() {\n const navigator = this, chart = navigator.chart, container = chart.container;\n let eventsToUnbind = [], mouseMoveHandler, mouseUpHandler;\n /**\n * Create mouse events' handlers.\n * Make them as separate functions to enable wrapping them:\n */\n navigator.mouseMoveHandler = mouseMoveHandler = function (e) {\n navigator.onMouseMove(e);\n };\n navigator.mouseUpHandler = mouseUpHandler = function (e) {\n navigator.onMouseUp(e);\n };\n // Add shades and handles mousedown events\n eventsToUnbind = navigator.getPartsEvents('mousedown');\n // Add mouse move and mouseup events. These are bind to doc/container,\n // because Navigator.grabbedSomething flags are stored in mousedown\n // events\n eventsToUnbind.push(addEvent(chart.renderTo, 'mousemove', mouseMoveHandler), addEvent(container.ownerDocument, 'mouseup', mouseUpHandler));\n // Touch events\n if (hasTouch) {\n eventsToUnbind.push(addEvent(chart.renderTo, 'touchmove', mouseMoveHandler), addEvent(container.ownerDocument, 'touchend', mouseUpHandler));\n eventsToUnbind.concat(navigator.getPartsEvents('touchstart'));\n }\n navigator.eventsToUnbind = eventsToUnbind;\n // Data events\n if (navigator.series && navigator.series[0]) {\n eventsToUnbind.push(addEvent(navigator.series[0].xAxis, 'foundExtremes', function () {\n chart.navigator.modifyNavigatorAxisExtremes();\n }));\n }\n }\n /**\n * Generate events for handles and masks\n *\n * @private\n * @function Highcharts.Navigator#getPartsEvents\n *\n * @param {string} eventName\n * Event name handler, 'mousedown' or 'touchstart'\n *\n * @return {Array}\n * An array of functions to remove navigator functions from the\n * events again.\n */\n getPartsEvents(eventName) {\n const navigator = this, events = [];\n ['shades', 'handles'].forEach(function (name) {\n navigator[name].forEach(function (navigatorItem, index) {\n events.push(addEvent(navigatorItem.element, eventName, function (e) {\n navigator[name + 'Mousedown'](e, index);\n }));\n });\n });\n return events;\n }\n /**\n * Mousedown on a shaded mask, either:\n *\n * - will be stored for future drag&drop\n *\n * - will directly shift to a new range\n *\n * @private\n * @function Highcharts.Navigator#shadesMousedown\n *\n * @param {Highcharts.PointerEventObject} e\n * Mouse event\n *\n * @param {number} index\n * Index of a mask in Navigator.shades array\n */\n shadesMousedown(e, index) {\n e = this.chart.pointer.normalize(e);\n const navigator = this, chart = navigator.chart, xAxis = navigator.xAxis, zoomedMin = navigator.zoomedMin, navigatorSize = navigator.size, range = navigator.range;\n let navigatorPosition = navigator.left, chartX = e.chartX, fixedMax, fixedMin, ext, left;\n // For inverted chart, swap some options:\n if (chart.inverted) {\n chartX = e.chartY;\n navigatorPosition = navigator.top;\n }\n if (index === 1) {\n // Store information for drag&drop\n navigator.grabbedCenter = chartX;\n navigator.fixedWidth = range;\n navigator.dragOffset = chartX - zoomedMin;\n }\n else {\n // Shift the range by clicking on shaded areas\n left = chartX - navigatorPosition - range / 2;\n if (index === 0) {\n left = Math.max(0, left);\n }\n else if (index === 2 && left + range >= navigatorSize) {\n left = navigatorSize - range;\n if (navigator.reversedExtremes) {\n // #7713\n left -= range;\n fixedMin = navigator.getUnionExtremes().dataMin;\n }\n else {\n // #2293, #3543\n fixedMax = navigator.getUnionExtremes().dataMax;\n }\n }\n if (left !== zoomedMin) { // it has actually moved\n navigator.fixedWidth = range; // #1370\n ext = xAxis.navigatorAxis.toFixedRange(left, left + range, fixedMin, fixedMax);\n if (defined(ext.min)) { // #7411\n chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true, null, // auto animation\n { trigger: 'navigator' });\n }\n }\n }\n }\n /**\n * Mousedown on a handle mask.\n * Will store necessary information for drag&drop.\n *\n * @private\n * @function Highcharts.Navigator#handlesMousedown\n * @param {Highcharts.PointerEventObject} e\n * Mouse event\n * @param {number} index\n * Index of a handle in Navigator.handles array\n */\n handlesMousedown(e, index) {\n e = this.chart.pointer.normalize(e);\n const navigator = this, chart = navigator.chart, baseXAxis = chart.xAxis[0], \n // For reversed axes, min and max are changed,\n // so the other extreme should be stored\n reverse = navigator.reversedExtremes;\n if (index === 0) {\n // Grab the left handle\n navigator.grabbedLeft = true;\n navigator.otherHandlePos = navigator.zoomedMax;\n navigator.fixedExtreme = reverse ? baseXAxis.min : baseXAxis.max;\n }\n else {\n // Grab the right handle\n navigator.grabbedRight = true;\n navigator.otherHandlePos = navigator.zoomedMin;\n navigator.fixedExtreme = reverse ? baseXAxis.max : baseXAxis.min;\n }\n chart.fixedRange = null;\n }\n /**\n * Mouse move event based on x/y mouse position.\n *\n * @private\n * @function Highcharts.Navigator#onMouseMove\n *\n * @param {Highcharts.PointerEventObject} e\n * Mouse event\n */\n onMouseMove(e) {\n const navigator = this, chart = navigator.chart, navigatorSize = navigator.navigatorSize, range = navigator.range, dragOffset = navigator.dragOffset, inverted = chart.inverted;\n let left = navigator.left, chartX;\n // In iOS, a mousemove event with e.pageX === 0 is fired when holding\n // the finger down in the center of the scrollbar. This should be\n // ignored.\n if (!e.touches || e.touches[0].pageX !== 0) { // #4696\n e = chart.pointer.normalize(e);\n chartX = e.chartX;\n // Swap some options for inverted chart\n if (inverted) {\n left = navigator.top;\n chartX = e.chartY;\n }\n // Drag left handle or top handle\n if (navigator.grabbedLeft) {\n navigator.hasDragged = true;\n navigator.render(0, 0, chartX - left, navigator.otherHandlePos);\n // Drag right handle or bottom handle\n }\n else if (navigator.grabbedRight) {\n navigator.hasDragged = true;\n navigator.render(0, 0, navigator.otherHandlePos, chartX - left);\n // Drag scrollbar or open area in navigator\n }\n else if (navigator.grabbedCenter) {\n navigator.hasDragged = true;\n if (chartX < dragOffset) { // outside left\n chartX = dragOffset;\n // outside right\n }\n else if (chartX >\n navigatorSize + dragOffset - range) {\n chartX = navigatorSize + dragOffset - range;\n }\n navigator.render(0, 0, chartX - dragOffset, chartX - dragOffset + range);\n }\n if (navigator.hasDragged &&\n navigator.scrollbar &&\n pick(navigator.scrollbar.options.liveRedraw, \n // By default, don't run live redraw on touch\n // devices or if the chart is in boost.\n !isTouchDevice &&\n !this.chart.boosted)) {\n e.DOMType = e.type;\n setTimeout(function () {\n navigator.onMouseUp(e);\n }, 0);\n }\n }\n }\n /**\n * Mouse up event based on x/y mouse position.\n *\n * @private\n * @function Highcharts.Navigator#onMouseUp\n * @param {Highcharts.PointerEventObject} e\n * Mouse event\n */\n onMouseUp(e) {\n const navigator = this, chart = navigator.chart, xAxis = navigator.xAxis, scrollbar = navigator.scrollbar, DOMEvent = e.DOMEvent || e, inverted = chart.inverted, verb = navigator.rendered && !navigator.hasDragged ?\n 'animate' : 'attr';\n let zoomedMax, zoomedMin, unionExtremes, fixedMin, fixedMax, ext;\n if (\n // MouseUp is called for both, navigator and scrollbar (that order),\n // which causes calling afterSetExtremes twice. Prevent first call\n // by checking if scrollbar is going to set new extremes (#6334)\n (navigator.hasDragged && (!scrollbar || !scrollbar.hasDragged)) ||\n e.trigger === 'scrollbar') {\n unionExtremes = navigator.getUnionExtremes();\n // When dragging one handle, make sure the other one doesn't change\n if (navigator.zoomedMin === navigator.otherHandlePos) {\n fixedMin = navigator.fixedExtreme;\n }\n else if (navigator.zoomedMax === navigator.otherHandlePos) {\n fixedMax = navigator.fixedExtreme;\n }\n // Snap to right edge (#4076)\n if (navigator.zoomedMax === navigator.size) {\n fixedMax = navigator.reversedExtremes ?\n unionExtremes.dataMin :\n unionExtremes.dataMax;\n }\n // Snap to left edge (#7576)\n if (navigator.zoomedMin === 0) {\n fixedMin = navigator.reversedExtremes ?\n unionExtremes.dataMax :\n unionExtremes.dataMin;\n }\n ext = xAxis.navigatorAxis.toFixedRange(navigator.zoomedMin, navigator.zoomedMax, fixedMin, fixedMax);\n if (defined(ext.min)) {\n chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true, \n // Run animation when clicking buttons, scrollbar track etc,\n // but not when dragging handles or scrollbar\n navigator.hasDragged ? false : null, {\n trigger: 'navigator',\n triggerOp: 'navigator-drag',\n DOMEvent: DOMEvent // #1838\n });\n }\n }\n if (e.DOMType !== 'mousemove' &&\n e.DOMType !== 'touchmove') {\n navigator.grabbedLeft = navigator.grabbedRight =\n navigator.grabbedCenter = navigator.fixedWidth =\n navigator.fixedExtreme = navigator.otherHandlePos =\n navigator.hasDragged = navigator.dragOffset = null;\n }\n // Update position of navigator shades, outline and handles (#12573)\n if (navigator.navigatorEnabled &&\n isNumber(navigator.zoomedMin) &&\n isNumber(navigator.zoomedMax)) {\n zoomedMin = Math.round(navigator.zoomedMin);\n zoomedMax = Math.round(navigator.zoomedMax);\n if (navigator.shades) {\n navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);\n }\n if (navigator.outline) {\n navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);\n }\n if (navigator.navigatorOptions.handles.enabled &&\n Object.keys(navigator.handles).length ===\n navigator.handles.length) {\n navigator.drawHandle(zoomedMin, 0, inverted, verb);\n navigator.drawHandle(zoomedMax, 1, inverted, verb);\n }\n }\n }\n /**\n * Removes the event handlers attached previously with addEvents.\n *\n * @private\n * @function Highcharts.Navigator#removeEvents\n */\n removeEvents() {\n if (this.eventsToUnbind) {\n this.eventsToUnbind.forEach(function (unbind) {\n unbind();\n });\n this.eventsToUnbind = void 0;\n }\n this.removeBaseSeriesEvents();\n }\n /**\n * Remove data events.\n *\n * @private\n * @function Highcharts.Navigator#removeBaseSeriesEvents\n */\n removeBaseSeriesEvents() {\n const baseSeries = this.baseSeries || [];\n if (this.navigatorEnabled && baseSeries[0]) {\n if (this.navigatorOptions.adaptToUpdatedData !== false) {\n baseSeries.forEach(function (series) {\n removeEvent(series, 'updatedData', this.updatedDataHandler);\n }, this);\n }\n // We only listen for extremes-events on the first baseSeries\n if (baseSeries[0].xAxis) {\n removeEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);\n }\n }\n }\n /**\n * Initialize the Navigator object\n *\n * @private\n * @function Highcharts.Navigator#init\n */\n init(chart) {\n const chartOptions = chart.options, navigatorOptions = chartOptions.navigator || {}, navigatorEnabled = navigatorOptions.enabled, scrollbarOptions = chartOptions.scrollbar || {}, scrollbarEnabled = scrollbarOptions.enabled, height = navigatorEnabled && navigatorOptions.height || 0, scrollbarHeight = scrollbarEnabled && scrollbarOptions.height || 0, scrollButtonSize = scrollbarOptions.buttonsEnabled && scrollbarHeight || 0;\n this.handles = [];\n this.shades = [];\n this.chart = chart;\n this.setBaseSeries();\n this.height = height;\n this.scrollbarHeight = scrollbarHeight;\n this.scrollButtonSize = scrollButtonSize;\n this.scrollbarEnabled = scrollbarEnabled;\n this.navigatorEnabled = navigatorEnabled;\n this.navigatorOptions = navigatorOptions;\n this.scrollbarOptions = scrollbarOptions;\n this.opposite = pick(navigatorOptions.opposite, Boolean(!navigatorEnabled && chart.inverted)); // #6262\n const navigator = this, baseSeries = navigator.baseSeries, xAxisIndex = chart.xAxis.length, yAxisIndex = chart.yAxis.length, baseXaxis = baseSeries && baseSeries[0] && baseSeries[0].xAxis ||\n chart.xAxis[0] || { options: {} };\n chart.isDirtyBox = true;\n if (navigator.navigatorEnabled) {\n // an x axis is required for scrollbar also\n navigator.xAxis = new Axis(chart, merge({\n // inherit base xAxis' break and ordinal options\n breaks: baseXaxis.options.breaks,\n ordinal: baseXaxis.options.ordinal\n }, navigatorOptions.xAxis, {\n id: 'navigator-x-axis',\n yAxis: 'navigator-y-axis',\n type: 'datetime',\n index: xAxisIndex,\n isInternal: true,\n offset: 0,\n keepOrdinalPadding: true,\n startOnTick: false,\n endOnTick: false,\n minPadding: 0,\n maxPadding: 0,\n zoomEnabled: false\n }, chart.inverted ? {\n offsets: [scrollButtonSize, 0, -scrollButtonSize, 0],\n width: height\n } : {\n offsets: [0, -scrollButtonSize, 0, scrollButtonSize],\n height: height\n }), 'xAxis');\n navigator.yAxis = new Axis(chart, merge(navigatorOptions.yAxis, {\n id: 'navigator-y-axis',\n alignTicks: false,\n offset: 0,\n index: yAxisIndex,\n isInternal: true,\n reversed: pick((navigatorOptions.yAxis &&\n navigatorOptions.yAxis.reversed), (chart.yAxis[0] && chart.yAxis[0].reversed), false),\n zoomEnabled: false\n }, chart.inverted ? {\n width: height\n } : {\n height: height\n }), 'yAxis');\n // If we have a base series, initialize the navigator series\n if (baseSeries || navigatorOptions.series.data) {\n navigator.updateNavigatorSeries(false);\n // If not, set up an event to listen for added series\n }\n else if (chart.series.length === 0) {\n navigator.unbindRedraw = addEvent(chart, 'beforeRedraw', function () {\n // We've got one, now add it as base\n if (chart.series.length > 0 && !navigator.series) {\n navigator.setBaseSeries();\n navigator.unbindRedraw(); // reset\n }\n });\n }\n navigator.reversedExtremes = (chart.inverted && !navigator.xAxis.reversed) || (!chart.inverted && navigator.xAxis.reversed);\n // Render items, so we can bind events to them:\n navigator.renderElements();\n // Add mouse events\n navigator.addMouseEvents();\n // in case of scrollbar only, fake an x axis to get translation\n }\n else {\n navigator.xAxis = {\n chart,\n navigatorAxis: {\n fake: true\n },\n translate: function (value, reverse) {\n const axis = chart.xAxis[0], ext = axis.getExtremes(), scrollTrackWidth = axis.len - 2 * scrollButtonSize, min = numExt('min', axis.options.min, ext.dataMin), valueRange = numExt('max', axis.options.max, ext.dataMax) - min;\n return reverse ?\n // from pixel to value\n (value * valueRange / scrollTrackWidth) + min :\n // from value to pixel\n scrollTrackWidth * (value - min) / valueRange;\n },\n toPixels: function (value) {\n return this.translate(value);\n },\n toValue: function (value) {\n return this.translate(value, true);\n }\n };\n navigator.xAxis.navigatorAxis.axis = navigator.xAxis;\n navigator.xAxis.navigatorAxis.toFixedRange = (NavigatorAxisAdditions.prototype.toFixedRange.bind(navigator.xAxis.navigatorAxis));\n }\n // Initialize the scrollbar\n if (chart.options.scrollbar.enabled) {\n const options = merge(chart.options.scrollbar, { vertical: chart.inverted });\n if (!isNumber(options.margin) && navigator.navigatorEnabled) {\n options.margin = chart.inverted ? -3 : 3;\n }\n chart.scrollbar = navigator.scrollbar = new Scrollbar(chart.renderer, options, chart);\n addEvent(navigator.scrollbar, 'changed', function (e) {\n const range = navigator.size, to = range * this.to, from = range * this.from;\n navigator.hasDragged = navigator.scrollbar.hasDragged;\n navigator.render(0, 0, from, to);\n if (this.shouldUpdateExtremes(e.DOMType)) {\n setTimeout(function () {\n navigator.onMouseUp(e);\n });\n }\n });\n }\n // Add data events\n navigator.addBaseSeriesEvents();\n // Add redraw events\n navigator.addChartEvents();\n }\n /**\n * Get the union data extremes of the chart - the outer data extremes of the\n * base X axis and the navigator axis.\n *\n * @private\n * @function Highcharts.Navigator#getUnionExtremes\n */\n getUnionExtremes(returnFalseOnNoBaseSeries) {\n const baseAxis = this.chart.xAxis[0], navAxis = this.xAxis, navAxisOptions = navAxis.options, baseAxisOptions = baseAxis.options;\n let ret;\n if (!returnFalseOnNoBaseSeries || baseAxis.dataMin !== null) {\n ret = {\n dataMin: pick(// #4053\n navAxisOptions && navAxisOptions.min, numExt('min', baseAxisOptions.min, baseAxis.dataMin, navAxis.dataMin, navAxis.min)),\n dataMax: pick(navAxisOptions && navAxisOptions.max, numExt('max', baseAxisOptions.max, baseAxis.dataMax, navAxis.dataMax, navAxis.max))\n };\n }\n return ret;\n }\n /**\n * Set the base series and update the navigator series from this. With a bit\n * of modification we should be able to make this an API method to be called\n * from the outside\n *\n * @private\n * @function Highcharts.Navigator#setBaseSeries\n * @param {Highcharts.SeriesOptionsType} [baseSeriesOptions]\n * Additional series options for a navigator\n * @param {boolean} [redraw]\n * Whether to redraw after update.\n */\n setBaseSeries(baseSeriesOptions, redraw) {\n const chart = this.chart, baseSeries = this.baseSeries = [];\n baseSeriesOptions = (baseSeriesOptions ||\n chart.options && chart.options.navigator.baseSeries ||\n (chart.series.length ?\n // Find the first non-navigator series (#8430)\n find(chart.series, (s) => (!s.options.isInternal)).index :\n 0));\n // Iterate through series and add the ones that should be shown in\n // navigator.\n (chart.series || []).forEach((series, i) => {\n if (\n // Don't include existing nav series\n !series.options.isInternal &&\n (series.options.showInNavigator ||\n (i === baseSeriesOptions ||\n series.options.id === baseSeriesOptions) &&\n series.options.showInNavigator !== false)) {\n baseSeries.push(series);\n }\n });\n // When run after render, this.xAxis already exists\n if (this.xAxis && !this.xAxis.navigatorAxis.fake) {\n this.updateNavigatorSeries(true, redraw);\n }\n }\n /**\n * Update series in the navigator from baseSeries, adding new if does not\n * exist.\n *\n * @private\n * @function Highcharts.Navigator.updateNavigatorSeries\n */\n updateNavigatorSeries(addEvents, redraw) {\n const navigator = this, chart = navigator.chart, baseSeries = navigator.baseSeries, navSeriesMixin = {\n enableMouseTracking: false,\n index: null,\n linkedTo: null,\n group: 'nav',\n padXAxis: false,\n xAxis: 'navigator-x-axis',\n yAxis: 'navigator-y-axis',\n showInLegend: false,\n stacking: void 0,\n isInternal: true,\n states: {\n inactive: {\n opacity: 1\n }\n }\n }, \n // Remove navigator series that are no longer in the baseSeries\n navigatorSeries = navigator.series =\n (navigator.series || []).filter((navSeries) => {\n const base = navSeries.baseSeries;\n if (baseSeries.indexOf(base) < 0) { // Not in array\n // If there is still a base series connected to this\n // series, remove event handler and reference.\n if (base) {\n removeEvent(base, 'updatedData', navigator.updatedDataHandler);\n delete base.navigatorSeries;\n }\n // Kill the nav series. It may already have been\n // destroyed (#8715).\n if (navSeries.chart) {\n navSeries.destroy();\n }\n return false;\n }\n return true;\n });\n let baseOptions, mergedNavSeriesOptions, chartNavigatorSeriesOptions = navigator.navigatorOptions.series, baseNavigatorOptions;\n // Go through each base series and merge the options to create new\n // series\n if (baseSeries && baseSeries.length) {\n baseSeries.forEach((base) => {\n const linkedNavSeries = base.navigatorSeries, userNavOptions = extend(\n // Grab color and visibility from base as default\n {\n color: base.color,\n visible: base.visible\n }, !isArray(chartNavigatorSeriesOptions) ?\n chartNavigatorSeriesOptions :\n defaultOptions.navigator.series);\n // Don't update if the series exists in nav and we have disabled\n // adaptToUpdatedData.\n if (linkedNavSeries &&\n navigator.navigatorOptions.adaptToUpdatedData === false) {\n return;\n }\n navSeriesMixin.name = 'Navigator ' + baseSeries.length;\n baseOptions = base.options || {};\n baseNavigatorOptions = baseOptions.navigatorOptions || {};\n // The dataLabels options are not merged correctly\n // if the settings are an array, #13847.\n userNavOptions.dataLabels = splat(userNavOptions.dataLabels);\n mergedNavSeriesOptions = merge(baseOptions, navSeriesMixin, userNavOptions, baseNavigatorOptions);\n // Once nav series type is resolved, pick correct pointRange\n mergedNavSeriesOptions.pointRange = pick(\n // Stricte set pointRange in options\n userNavOptions.pointRange, baseNavigatorOptions.pointRange, \n // Fallback to default values, e.g. `null` for column\n defaultOptions.plotOptions[mergedNavSeriesOptions.type || 'line'].pointRange);\n // Merge data separately. Do a slice to avoid mutating the\n // navigator options from base series (#4923).\n const navigatorSeriesData = baseNavigatorOptions.data || userNavOptions.data;\n navigator.hasNavigatorData =\n navigator.hasNavigatorData || !!navigatorSeriesData;\n mergedNavSeriesOptions.data =\n navigatorSeriesData ||\n baseOptions.data && baseOptions.data.slice(0);\n // Update or add the series\n if (linkedNavSeries && linkedNavSeries.options) {\n linkedNavSeries.update(mergedNavSeriesOptions, redraw);\n }\n else {\n base.navigatorSeries = chart.initSeries(mergedNavSeriesOptions);\n base.navigatorSeries.baseSeries = base; // Store ref\n navigatorSeries.push(base.navigatorSeries);\n }\n });\n }\n // If user has defined data (and no base series) or explicitly defined\n // navigator.series as an array, we create these series on top of any\n // base series.\n if (chartNavigatorSeriesOptions.data &&\n !(baseSeries && baseSeries.length) ||\n isArray(chartNavigatorSeriesOptions)) {\n navigator.hasNavigatorData = false;\n // Allow navigator.series to be an array\n chartNavigatorSeriesOptions =\n splat(chartNavigatorSeriesOptions);\n chartNavigatorSeriesOptions.forEach((userSeriesOptions, i) => {\n navSeriesMixin.name =\n 'Navigator ' + (navigatorSeries.length + 1);\n mergedNavSeriesOptions = merge(defaultOptions.navigator.series, {\n // Since we don't have a base series to pull color from,\n // try to fake it by using color from series with same\n // index. Otherwise pull from the colors array. We need\n // an explicit color as otherwise updates will increment\n // color counter and we'll get a new color for each\n // update of the nav series.\n color: chart.series[i] &&\n !chart.series[i].options.isInternal &&\n chart.series[i].color ||\n chart.options.colors[i] ||\n chart.options.colors[0]\n }, navSeriesMixin, userSeriesOptions);\n mergedNavSeriesOptions.data = userSeriesOptions.data;\n if (mergedNavSeriesOptions.data) {\n navigator.hasNavigatorData = true;\n navigatorSeries.push(chart.initSeries(mergedNavSeriesOptions));\n }\n });\n }\n if (addEvents) {\n this.addBaseSeriesEvents();\n }\n }\n /**\n * Add data events.\n * For example when main series is updated we need to recalculate extremes\n *\n * @private\n * @function Highcharts.Navigator#addBaseSeriesEvent\n */\n addBaseSeriesEvents() {\n const navigator = this, baseSeries = navigator.baseSeries || [];\n // Bind modified extremes event to first base's xAxis only.\n // In event of > 1 base-xAxes, the navigator will ignore those.\n // Adding this multiple times to the same axis is no problem, as\n // duplicates should be discarded by the browser.\n if (baseSeries[0] && baseSeries[0].xAxis) {\n baseSeries[0].eventsToUnbind.push(addEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes));\n }\n baseSeries.forEach((base) => {\n // Link base series show/hide to navigator series visibility\n base.eventsToUnbind.push(addEvent(base, 'show', function () {\n if (this.navigatorSeries) {\n this.navigatorSeries.setVisible(true, false);\n }\n }));\n base.eventsToUnbind.push(addEvent(base, 'hide', function () {\n if (this.navigatorSeries) {\n this.navigatorSeries.setVisible(false, false);\n }\n }));\n // Respond to updated data in the base series, unless explicitily\n // not adapting to data changes.\n if (this.navigatorOptions.adaptToUpdatedData !== false) {\n if (base.xAxis) {\n base.eventsToUnbind.push(addEvent(base, 'updatedData', this.updatedDataHandler));\n }\n }\n // Handle series removal\n base.eventsToUnbind.push(addEvent(base, 'remove', function () {\n if (this.navigatorSeries) {\n erase(navigator.series, this.navigatorSeries);\n if (defined(this.navigatorSeries.options)) {\n this.navigatorSeries.remove(false);\n }\n delete this.navigatorSeries;\n }\n }));\n });\n }\n /**\n * Get minimum from all base series connected to the navigator\n * @private\n * @param {number} currentSeriesMin\n * Minium from the current series\n * @return {number}\n * Minimum from all series\n */\n getBaseSeriesMin(currentSeriesMin) {\n return this.baseSeries.reduce(function (min, series) {\n // (#10193)\n return Math.min(min, series.xData && series.xData.length ?\n series.xData[0] : min);\n }, currentSeriesMin);\n }\n /**\n * Set the navigator x axis extremes to reflect the total. The navigator\n * extremes should always be the extremes of the union of all series in the\n * chart as well as the navigator series.\n *\n * @private\n * @function Highcharts.Navigator#modifyNavigatorAxisExtremes\n */\n modifyNavigatorAxisExtremes() {\n const xAxis = this.xAxis;\n if (typeof xAxis.getExtremes !== 'undefined') {\n const unionExtremes = this.getUnionExtremes(true);\n if (unionExtremes &&\n (unionExtremes.dataMin !== xAxis.min ||\n unionExtremes.dataMax !== xAxis.max)) {\n xAxis.min = unionExtremes.dataMin;\n xAxis.max = unionExtremes.dataMax;\n }\n }\n }\n /**\n * Hook to modify the base axis extremes with information from the Navigator\n *\n * @private\n * @function Highcharts.Navigator#modifyBaseAxisExtremes\n */\n modifyBaseAxisExtremes() {\n const baseXAxis = this, navigator = baseXAxis.chart.navigator, baseExtremes = baseXAxis.getExtremes(), baseMin = baseExtremes.min, baseMax = baseExtremes.max, baseDataMin = baseExtremes.dataMin, baseDataMax = baseExtremes.dataMax, range = baseMax - baseMin, stickToMin = navigator.stickToMin, stickToMax = navigator.stickToMax, overscroll = pick(baseXAxis.options.overscroll, 0), navigatorSeries = navigator.series && navigator.series[0], hasSetExtremes = !!baseXAxis.setExtremes, \n // When the extremes have been set by range selector button, don't\n // stick to min or max. The range selector buttons will handle the\n // extremes. (#5489)\n unmutable = baseXAxis.eventArgs &&\n baseXAxis.eventArgs.trigger === 'rangeSelectorButton';\n let newMax, newMin;\n if (!unmutable) {\n // If the zoomed range is already at the min, move it to the right\n // as new data comes in\n if (stickToMin) {\n newMin = baseDataMin;\n newMax = newMin + range;\n }\n // If the zoomed range is already at the max, move it to the right\n // as new data comes in\n if (stickToMax) {\n newMax = baseDataMax + overscroll;\n // If stickToMin is true, the new min value is set above\n if (!stickToMin) {\n newMin = Math.max(baseDataMin, // don't go below data extremes (#13184)\n newMax - range, navigator.getBaseSeriesMin(navigatorSeries && navigatorSeries.xData ?\n navigatorSeries.xData[0] :\n -Number.MAX_VALUE));\n }\n }\n // Update the extremes\n if (hasSetExtremes && (stickToMin || stickToMax)) {\n if (isNumber(newMin)) {\n baseXAxis.min = baseXAxis.userMin = newMin;\n baseXAxis.max = baseXAxis.userMax = newMax;\n }\n }\n }\n // Reset\n navigator.stickToMin =\n navigator.stickToMax = null;\n }\n /**\n * Handler for updated data on the base series. When data is modified, the\n * navigator series must reflect it. This is called from the Chart.redraw\n * function before axis and series extremes are computed.\n *\n * @private\n * @function Highcharts.Navigator#updateDataHandler\n */\n updatedDataHandler() {\n const navigator = this.chart.navigator, baseSeries = this, navigatorSeries = this.navigatorSeries, shouldStickToMax = navigator.reversedExtremes ?\n Math.round(navigator.zoomedMin) === 0 :\n Math.round(navigator.zoomedMax) >= Math.round(navigator.size);\n // If the scrollbar is scrolled all the way to the right, keep right as\n // new data comes in, unless user set navigator.stickToMax to false.\n navigator.stickToMax = pick(this.chart.options.navigator &&\n this.chart.options.navigator.stickToMax, shouldStickToMax);\n navigator.stickToMin = navigator.shouldStickToMin(baseSeries, navigator);\n // Set the navigator series data to the new data of the base series\n if (navigatorSeries && !navigator.hasNavigatorData) {\n navigatorSeries.options.pointStart = baseSeries.xData[0];\n navigatorSeries.setData(baseSeries.options.data, false, null, false); // #5414\n }\n }\n /**\n * Detect if the zoomed area should stick to the minimum, #14742.\n *\n * @private\n * @function Highcharts.Navigator#shouldStickToMin\n */\n shouldStickToMin(baseSeries, navigator) {\n const xDataMin = navigator.getBaseSeriesMin(baseSeries.xData[0]), xAxis = baseSeries.xAxis, max = xAxis.max, min = xAxis.min, range = xAxis.options.range;\n let stickToMin = true;\n if (isNumber(max) && isNumber(min)) {\n // If range declared, stick to the minimum only if the range\n // is smaller than the data set range.\n if (range && max - xDataMin > 0) {\n stickToMin = max - xDataMin < range;\n }\n else {\n // If the current axis minimum falls outside the new\n // updated dataset, we must adjust.\n stickToMin = min <= xDataMin;\n }\n }\n else {\n stickToMin = false; // #15864\n }\n return stickToMin;\n }\n /**\n * Add chart events, like redrawing navigator, when chart requires that.\n *\n * @private\n * @function Highcharts.Navigator#addChartEvents\n */\n addChartEvents() {\n if (!this.eventsToUnbind) {\n this.eventsToUnbind = [];\n }\n this.eventsToUnbind.push(\n // Move the scrollbar after redraw, like after data updata even if\n // axes don't redraw\n addEvent(this.chart, 'redraw', function () {\n const navigator = this.navigator, xAxis = navigator && (navigator.baseSeries &&\n navigator.baseSeries[0] &&\n navigator.baseSeries[0].xAxis ||\n this.xAxis[0]); // #5709, #13114\n if (xAxis) {\n navigator.render(xAxis.min, xAxis.max);\n }\n }), \n // Make room for the navigator, can be placed around the chart:\n addEvent(this.chart, 'getMargins', function () {\n let chart = this, navigator = chart.navigator, marginName = navigator.opposite ?\n 'plotTop' : 'marginBottom';\n if (chart.inverted) {\n marginName = navigator.opposite ?\n 'marginRight' : 'plotLeft';\n }\n chart[marginName] =\n (chart[marginName] || 0) + (navigator.navigatorEnabled || !chart.inverted ?\n navigator.height + navigator.scrollbarHeight :\n 0) + navigator.navigatorOptions.margin;\n }));\n }\n /**\n * Destroys allocated elements.\n *\n * @private\n * @function Highcharts.Navigator#destroy\n */\n destroy() {\n // Disconnect events added in addEvents\n this.removeEvents();\n if (this.xAxis) {\n erase(this.chart.xAxis, this.xAxis);\n erase(this.chart.axes, this.xAxis);\n }\n if (this.yAxis) {\n erase(this.chart.yAxis, this.yAxis);\n erase(this.chart.axes, this.yAxis);\n }\n // Destroy series\n (this.series || []).forEach((s) => {\n if (s.destroy) {\n s.destroy();\n }\n });\n // Destroy properties\n [\n 'series', 'xAxis', 'yAxis', 'shades', 'outline', 'scrollbarTrack',\n 'scrollbarRifles', 'scrollbarGroup', 'scrollbar', 'navigatorGroup',\n 'rendered'\n ].forEach((prop) => {\n if (this[prop] && this[prop].destroy) {\n this[prop].destroy();\n }\n this[prop] = null;\n });\n // Destroy elements in collection\n [this.handles].forEach((coll) => {\n destroyObjectProperties(coll);\n });\n }\n }\n /* *\n *\n * Default Export\n *\n * */\n\n return Navigator;\n });\n _registerModule(_modules, 'Accessibility/Components/NavigatorComponent.js', [_modules['Accessibility/AccessibilityComponent.js'], _modules['Accessibility/Utils/Announcer.js'], _modules['Accessibility/KeyboardNavigationHandler.js'], _modules['Stock/Navigator/Navigator.js'], _modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Templating.js'], _modules['Core/Utilities.js'], _modules['Accessibility/Utils/HTMLUtilities.js'], _modules['Accessibility/Utils/ChartUtilities.js']], function (AccessibilityComponent, Announcer, KeyboardNavigationHandler, Navigator, A, T, U, HU, CU) {\n /* *\n *\n * (c) 2009-2023 Highsoft AS\n *\n * Accessibility component for the navigator.\n *\n * Author: Øystein Moseng\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n /* *\n *\n * Imports\n *\n * */\n const { animObject } = A;\n const { format } = T;\n const { clamp, pick, syncTimeout } = U;\n const { getFakeMouseEvent } = HU;\n const { getAxisRangeDescription, fireEventOnWrappedOrUnwrappedElement } = CU;\n /**\n * The NavigatorComponent class\n *\n * @private\n * @class\n * @name Highcharts.NavigatorComponent\n */\n class NavigatorComponent extends AccessibilityComponent {\n constructor() {\n super(...arguments);\n this.announcer = void 0;\n }\n /**\n * Init the component\n * @private\n */\n init() {\n const chart = this.chart, component = this;\n this.announcer = new Announcer(chart, 'polite');\n // Update positions after render\n this.addEvent(Navigator, 'afterRender', function () {\n if (this.chart === component.chart &&\n this.chart.renderer) {\n syncTimeout(() => {\n component.proxyProvider\n .updateGroupProxyElementPositions('navigator');\n component.updateHandleValues();\n }, animObject(pick(this.chart.renderer.globalAnimation, true)).duration);\n }\n });\n }\n /**\n * Called on updates\n * @private\n */\n onChartUpdate() {\n const chart = this.chart, options = chart.options;\n if (options.navigator.accessibility?.enabled) {\n const verbosity = options.accessibility.landmarkVerbosity, groupFormatStr = options.lang\n .accessibility?.navigator.groupLabel;\n // We just recreate the group for simplicity. Could consider\n // updating the existing group if the verbosity has not changed.\n this.proxyProvider.removeGroup('navigator');\n this.proxyProvider.addGroup('navigator', 'div', {\n role: verbosity === 'all' ? 'region' : 'group',\n 'aria-label': format(groupFormatStr, { chart }, chart)\n });\n const handleFormatStr = options.lang\n .accessibility?.navigator.handleLabel;\n [0, 1].forEach((n) => {\n const handle = this.getHandleByIx(n);\n if (handle) {\n const proxyEl = this.proxyProvider.addProxyElement('navigator', {\n click: handle\n }, 'input', {\n type: 'range',\n 'aria-label': format(handleFormatStr, { handleIx: n, chart }, chart)\n });\n this[n ? 'maxHandleProxy' : 'minHandleProxy'] =\n proxyEl.innerElement;\n proxyEl.innerElement.style.pointerEvents = 'none';\n proxyEl.innerElement.oninput =\n () => this.updateNavigator();\n }\n });\n this.updateHandleValues();\n }\n else {\n this.proxyProvider.removeGroup('navigator');\n }\n }\n /**\n * Get navigation for a navigator handle.\n * @private\n * @return {Highcharts.KeyboardNavigationHandler} The module object.\n */\n getNavigatorHandleNavigation(handleIx) {\n const component = this, chart = this.chart, proxyEl = handleIx ? this.maxHandleProxy : this.minHandleProxy, keys = this.keyCodes;\n return new KeyboardNavigationHandler(chart, {\n keyCodeMap: [[\n [keys.left, keys.right, keys.up, keys.down],\n function (keyCode) {\n if (proxyEl) {\n const delta = keyCode === keys.left ||\n keyCode === keys.up ? -1 : 1;\n proxyEl.value = '' + clamp(parseFloat(proxyEl.value) + delta, 0, 100);\n component.updateNavigator(() => {\n const handle = component.getHandleByIx(handleIx);\n if (handle) {\n chart.setFocusToElement(handle, proxyEl);\n }\n });\n }\n return this.response.success;\n }\n ]],\n init: () => {\n chart.setFocusToElement(this.getHandleByIx(handleIx), proxyEl);\n },\n validate: () => !!(this.getHandleByIx(handleIx) && proxyEl &&\n chart.options.navigator.accessibility?.enabled)\n });\n }\n /**\n * Get keyboard navigation handlers for this component.\n * @return {Array}\n * List of module objects.\n */\n getKeyboardNavigation() {\n return [\n this.getNavigatorHandleNavigation(0),\n this.getNavigatorHandleNavigation(1)\n ];\n }\n /**\n * Remove component traces\n */\n destroy() {\n if (this.updateNavigatorThrottleTimer) {\n clearTimeout(this.updateNavigatorThrottleTimer);\n }\n this.proxyProvider.removeGroup('navigator');\n if (this.announcer) {\n this.announcer.destroy();\n }\n }\n /**\n * Update the value of the handles to match current navigator pos.\n * @private\n */\n updateHandleValues() {\n const navigator = this.chart.navigator;\n if (navigator && this.minHandleProxy && this.maxHandleProxy) {\n const length = navigator.size;\n this.minHandleProxy.value =\n '' + Math.round(navigator.zoomedMin / length * 100);\n this.maxHandleProxy.value =\n '' + Math.round(navigator.zoomedMax / length * 100);\n }\n }\n /**\n * Get a navigator handle by its index\n * @private\n */\n getHandleByIx(ix) {\n const navigator = this.chart.navigator;\n return navigator && navigator.handles &&\n navigator.handles[ix];\n }\n /**\n * Update navigator to match changed proxy values.\n * @private\n */\n updateNavigator(beforeAnnounce) {\n const performUpdate = (beforeAnnounce) => {\n const chart = this.chart, navigator = chart.navigator;\n if (navigator && this.minHandleProxy && this.maxHandleProxy) {\n const chartPos = chart.pointer.getChartPosition(), minNewX = parseFloat(this.minHandleProxy.value) /\n 100 * navigator.size, maxNewX = parseFloat(this.maxHandleProxy.value) /\n 100 * navigator.size;\n // Fire fake events in order for each handle.\n [\n [0, 'mousedown', navigator.zoomedMin],\n [0, 'mousemove', minNewX],\n [0, 'mouseup', minNewX],\n [1, 'mousedown', navigator.zoomedMax],\n [1, 'mousemove', maxNewX],\n [1, 'mouseup', maxNewX]\n ].forEach(([handleIx, type, x]) => {\n const handle = this.getHandleByIx(handleIx)?.element;\n if (handle) {\n fireEventOnWrappedOrUnwrappedElement(handle, getFakeMouseEvent(type, {\n x: chartPos.left + navigator.left + x,\n y: chartPos.top + navigator.top\n }, handle));\n }\n });\n if (beforeAnnounce) {\n beforeAnnounce();\n }\n // Announce the update\n const announceFormatStr = chart.options.lang\n .accessibility?.navigator.changeAnnouncement, axisRangeDescription = getAxisRangeDescription(chart.xAxis[0]);\n this.announcer.announce(format(announceFormatStr, { axisRangeDescription, chart }, chart));\n }\n };\n // Throttle updates so as not to reduce performance with\n // continuous keypress.\n if (this.updateNavigatorThrottleTimer) {\n clearTimeout(this.updateNavigatorThrottleTimer);\n }\n this.updateNavigatorThrottleTimer = setTimeout(performUpdate.bind(this, beforeAnnounce), 20);\n }\n }\n /* *\n *\n * Export Default\n *\n * */\n\n return NavigatorComponent;\n });\n _registerModule(_modules, 'Accessibility/Components/SeriesComponent/SeriesDescriber.js', [_modules['Accessibility/Components/AnnotationsA11y.js'], _modules['Accessibility/Utils/ChartUtilities.js'], _modules['Core/Templating.js'], _modules['Accessibility/Utils/HTMLUtilities.js'], _modules['Core/Utilities.js']], function (AnnotationsA11y, ChartUtilities, F, HTMLUtilities, U) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Place desriptions on a series and its points.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { getPointAnnotationTexts } = AnnotationsA11y;\n const { getAxisDescription, getSeriesFirstPointElement, getSeriesA11yElement, unhideChartElementFromAT } = ChartUtilities;\n const { format, numberFormat } = F;\n const { reverseChildNodes, stripHTMLTagsFromString: stripHTMLTags } = HTMLUtilities;\n const { find, isNumber, isString, pick, defined } = U;\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * @private\n */\n function findFirstPointWithGraphic(point) {\n const sourcePointIndex = point.index;\n if (!point.series || !point.series.data || !defined(sourcePointIndex)) {\n return null;\n }\n return find(point.series.data, function (p) {\n return !!(p &&\n typeof p.index !== 'undefined' &&\n p.index > sourcePointIndex &&\n p.graphic &&\n p.graphic.element);\n }) || null;\n }\n /**\n * Whether or not we should add a mock point element in\n * order to describe a point that has no graphic.\n * @private\n */\n function shouldAddMockPoint(point) {\n // Note: Sunburst series use isNull for hidden points on drilldown.\n // Ignore these.\n const series = point.series, chart = series && series.chart, isSunburst = series && series.is('sunburst'), isNull = point.isNull, shouldDescribeNull = chart &&\n chart\n .options.accessibility.point.describeNull;\n return isNull && !isSunburst && shouldDescribeNull;\n }\n /**\n * @private\n */\n function makeMockElement(point, pos) {\n const renderer = point.series.chart.renderer, mock = renderer.rect(pos.x, pos.y, 1, 1);\n mock.attr({\n 'class': 'highcharts-a11y-mock-point',\n fill: 'none',\n opacity: 0,\n 'fill-opacity': 0,\n 'stroke-opacity': 0\n });\n return mock;\n }\n /**\n * @private\n */\n function addMockPointElement(point) {\n const series = point.series, firstPointWithGraphic = findFirstPointWithGraphic(point), firstGraphic = firstPointWithGraphic && firstPointWithGraphic.graphic, parentGroup = firstGraphic ?\n firstGraphic.parentGroup :\n series.graph || series.group, mockPos = firstPointWithGraphic ? {\n x: pick(point.plotX, firstPointWithGraphic.plotX, 0),\n y: pick(point.plotY, firstPointWithGraphic.plotY, 0)\n } : {\n x: pick(point.plotX, 0),\n y: pick(point.plotY, 0)\n }, mockElement = makeMockElement(point, mockPos);\n if (parentGroup && parentGroup.element) {\n point.graphic = mockElement;\n point.hasMockGraphic = true;\n mockElement.add(parentGroup);\n // Move to correct pos in DOM\n parentGroup.element.insertBefore(mockElement.element, firstGraphic ? firstGraphic.element : null);\n return mockElement.element;\n }\n }\n /**\n * @private\n */\n function hasMorePointsThanDescriptionThreshold(series) {\n const chartA11yOptions = series.chart.options.accessibility, threshold = (chartA11yOptions.series.pointDescriptionEnabledThreshold);\n return !!(threshold !== false &&\n series.points &&\n series.points.length >= +threshold);\n }\n /**\n * @private\n */\n function shouldSetScreenReaderPropsOnPoints(series) {\n const seriesA11yOptions = series.options.accessibility || {};\n return !hasMorePointsThanDescriptionThreshold(series) &&\n !seriesA11yOptions.exposeAsGroupOnly;\n }\n /**\n * @private\n */\n function shouldSetKeyboardNavPropsOnPoints(series) {\n const chartA11yOptions = series.chart.options.accessibility, seriesNavOptions = chartA11yOptions.keyboardNavigation.seriesNavigation;\n return !!(series.points && (series.points.length <\n +seriesNavOptions.pointNavigationEnabledThreshold ||\n seriesNavOptions.pointNavigationEnabledThreshold === false));\n }\n /**\n * @private\n */\n function shouldDescribeSeriesElement(series) {\n const chart = series.chart, chartOptions = chart.options.chart, chartHas3d = chartOptions.options3d && chartOptions.options3d.enabled, hasMultipleSeries = chart.series.length > 1, describeSingleSeriesOption = chart.options.accessibility.series.describeSingleSeries, exposeAsGroupOnlyOption = (series.options.accessibility || {}).exposeAsGroupOnly, noDescribe3D = chartHas3d && hasMultipleSeries;\n return !noDescribe3D && (hasMultipleSeries || describeSingleSeriesOption ||\n exposeAsGroupOnlyOption || hasMorePointsThanDescriptionThreshold(series));\n }\n /**\n * @private\n */\n function pointNumberToString(point, value) {\n const series = point.series, chart = series.chart, a11yPointOptions = chart.options.accessibility.point || {}, seriesA11yPointOptions = series.options.accessibility &&\n series.options.accessibility.point || {}, tooltipOptions = series.tooltipOptions || {}, lang = chart.options.lang;\n if (isNumber(value)) {\n return numberFormat(value, seriesA11yPointOptions.valueDecimals ||\n a11yPointOptions.valueDecimals ||\n tooltipOptions.valueDecimals ||\n -1, lang.decimalPoint, lang.accessibility.thousandsSep || lang.thousandsSep);\n }\n return value;\n }\n /**\n * @private\n */\n function getSeriesDescriptionText(series) {\n const seriesA11yOptions = series.options.accessibility || {}, descOpt = seriesA11yOptions.description;\n return descOpt && series.chart.langFormat('accessibility.series.description', {\n description: descOpt,\n series: series\n }) || '';\n }\n /**\n * @private\n */\n function getSeriesAxisDescriptionText(series, axisCollection) {\n const axis = series[axisCollection];\n return series.chart.langFormat('accessibility.series.' + axisCollection + 'Description', {\n name: getAxisDescription(axis),\n series: series\n });\n }\n /**\n * Get accessible time description for a point on a datetime axis.\n *\n * @private\n */\n function getPointA11yTimeDescription(point) {\n const series = point.series, chart = series.chart, seriesA11yOptions = series.options.accessibility &&\n series.options.accessibility.point || {}, a11yOptions = chart.options.accessibility.point || {}, dateXAxis = series.xAxis && series.xAxis.dateTime;\n if (dateXAxis) {\n const tooltipDateFormat = dateXAxis.getXDateFormat(point.x || 0, chart.options.tooltip.dateTimeLabelFormats), dateFormat = seriesA11yOptions.dateFormatter &&\n seriesA11yOptions.dateFormatter(point) ||\n a11yOptions.dateFormatter && a11yOptions.dateFormatter(point) ||\n seriesA11yOptions.dateFormat ||\n a11yOptions.dateFormat ||\n tooltipDateFormat;\n return chart.time.dateFormat(dateFormat, point.x || 0, void 0);\n }\n }\n /**\n * @private\n */\n function getPointXDescription(point) {\n const timeDesc = getPointA11yTimeDescription(point), xAxis = point.series.xAxis || {}, pointCategory = xAxis.categories && defined(point.category) &&\n ('' + point.category).replace('
    ', ' '), canUseId = defined(point.id) &&\n ('' + point.id).indexOf('highcharts-') < 0, fallback = 'x, ' + point.x;\n return point.name || timeDesc || pointCategory ||\n (canUseId ? point.id : fallback);\n }\n /**\n * @private\n */\n function getPointArrayMapValueDescription(point, prefix, suffix) {\n const pre = prefix || '', suf = suffix || '', keyToValStr = function (key) {\n const num = pointNumberToString(point, pick(point[key], point.options[key]));\n return num !== void 0 ?\n key + ': ' + pre + num + suf :\n num;\n }, pointArrayMap = point.series.pointArrayMap;\n return pointArrayMap.reduce(function (desc, key) {\n const propDesc = keyToValStr(key);\n return propDesc ?\n (desc + (desc.length ? ', ' : '') + propDesc) :\n desc;\n }, '');\n }\n /**\n * @private\n */\n function getPointValue(point) {\n const series = point.series, a11yPointOpts = series.chart.options.accessibility.point || {}, seriesA11yPointOpts = series.chart.options.accessibility &&\n series.chart.options.accessibility.point || {}, tooltipOptions = series.tooltipOptions || {}, valuePrefix = seriesA11yPointOpts.valuePrefix ||\n a11yPointOpts.valuePrefix ||\n tooltipOptions.valuePrefix ||\n '', valueSuffix = seriesA11yPointOpts.valueSuffix ||\n a11yPointOpts.valueSuffix ||\n tooltipOptions.valueSuffix ||\n '', fallbackKey = (typeof point.value !==\n 'undefined' ?\n 'value' : 'y'), fallbackDesc = pointNumberToString(point, point[fallbackKey]);\n if (point.isNull) {\n return series.chart.langFormat('accessibility.series.nullPointValue', {\n point: point\n });\n }\n if (series.pointArrayMap) {\n return getPointArrayMapValueDescription(point, valuePrefix, valueSuffix);\n }\n return valuePrefix + fallbackDesc + valueSuffix;\n }\n /**\n * Return the description for the annotation(s) connected to a point, or\n * empty string if none.\n *\n * @private\n * @param {Highcharts.Point} point\n * The data point to get the annotation info from.\n * @return {string}\n * Annotation description\n */\n function getPointAnnotationDescription(point) {\n const chart = point.series.chart;\n const langKey = 'accessibility.series.pointAnnotationsDescription';\n const annotations = getPointAnnotationTexts(point);\n const context = { point, annotations };\n return annotations.length ? chart.langFormat(langKey, context) : '';\n }\n /**\n * Return string with information about point.\n * @private\n */\n function getPointValueDescription(point) {\n const series = point.series, chart = series.chart, seriesA11yOptions = series.options.accessibility, seriesValueDescFormat = seriesA11yOptions && seriesA11yOptions.point &&\n seriesA11yOptions.point.valueDescriptionFormat, pointValueDescriptionFormat = seriesValueDescFormat ||\n chart.options.accessibility.point.valueDescriptionFormat, showXDescription = pick(series.xAxis &&\n series.xAxis.options.accessibility &&\n series.xAxis.options.accessibility.enabled, !chart.angular && series.type !== 'flowmap'), xDesc = showXDescription ? getPointXDescription(point) : '', context = {\n point: point,\n index: defined(point.index) ? (point.index + 1) : '',\n xDescription: xDesc,\n value: getPointValue(point),\n separator: showXDescription ? ', ' : ''\n };\n return format(pointValueDescriptionFormat, context, chart);\n }\n /**\n * Return string with information about point.\n * @private\n */\n function defaultPointDescriptionFormatter(point) {\n const series = point.series, shouldExposeSeriesName = series.chart.series.length > 1 ||\n series.options.name, valText = getPointValueDescription(point), description = point.options && point.options.accessibility &&\n point.options.accessibility.description, userDescText = description ? ' ' + description : '', seriesNameText = shouldExposeSeriesName ? ' ' + series.name + '.' : '', annotationsDesc = getPointAnnotationDescription(point), pointAnnotationsText = annotationsDesc ? ' ' + annotationsDesc : '';\n point.accessibility = point.accessibility || {};\n point.accessibility.valueDescription = valText;\n return valText + userDescText + seriesNameText + pointAnnotationsText;\n }\n /**\n * Set a11y props on a point element\n * @private\n * @param {Highcharts.Point} point\n * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} pointElement\n */\n function setPointScreenReaderAttribs(point, pointElement) {\n const series = point.series, seriesPointA11yOptions = series.options.accessibility?.point || {}, a11yPointOptions = series.chart.options.accessibility.point || {}, label = stripHTMLTags((isString(seriesPointA11yOptions.descriptionFormat) &&\n format(seriesPointA11yOptions.descriptionFormat, point, series.chart)) ||\n seriesPointA11yOptions.descriptionFormatter?.(point) ||\n (isString(a11yPointOptions.descriptionFormat) &&\n format(a11yPointOptions.descriptionFormat, point, series.chart)) ||\n a11yPointOptions.descriptionFormatter?.(point) ||\n defaultPointDescriptionFormatter(point), series.chart.renderer.forExport);\n pointElement.setAttribute('role', 'img');\n pointElement.setAttribute('aria-label', label);\n }\n /**\n * Add accessible info to individual point elements of a series\n * @private\n * @param {Highcharts.Series} series\n */\n function describePointsInSeries(series) {\n const setScreenReaderProps = shouldSetScreenReaderPropsOnPoints(series), setKeyboardProps = shouldSetKeyboardNavPropsOnPoints(series), shouldDescribeNullPoints = series.chart.options.accessibility\n .point.describeNull;\n if (setScreenReaderProps || setKeyboardProps) {\n series.points.forEach((point) => {\n const pointEl = point.graphic && point.graphic.element ||\n shouldAddMockPoint(point) && addMockPointElement(point), pointA11yDisabled = (point.options &&\n point.options.accessibility &&\n point.options.accessibility.enabled === false);\n if (pointEl) {\n if (point.isNull && !shouldDescribeNullPoints) {\n pointEl.setAttribute('aria-hidden', true);\n return;\n }\n // We always set tabindex, as long as we are setting props.\n // When setting tabindex, also remove default outline to\n // avoid ugly border on click.\n pointEl.setAttribute('tabindex', '-1');\n if (!series.chart.styledMode) {\n pointEl.style.outline = 'none';\n }\n if (setScreenReaderProps && !pointA11yDisabled) {\n setPointScreenReaderAttribs(point, pointEl);\n }\n else {\n pointEl.setAttribute('aria-hidden', true);\n }\n }\n });\n }\n }\n /**\n * Return string with information about series.\n * @private\n */\n function defaultSeriesDescriptionFormatter(series) {\n const chart = series.chart, chartTypes = chart.types || [], description = getSeriesDescriptionText(series), shouldDescribeAxis = function (coll) {\n return chart[coll] && chart[coll].length > 1 && series[coll];\n }, seriesNumber = series.index + 1, xAxisInfo = getSeriesAxisDescriptionText(series, 'xAxis'), yAxisInfo = getSeriesAxisDescriptionText(series, 'yAxis'), summaryContext = {\n seriesNumber,\n series,\n chart\n }, combinationSuffix = chartTypes.length > 1 ? 'Combination' : '', summary = chart.langFormat('accessibility.series.summary.' + series.type + combinationSuffix, summaryContext) || chart.langFormat('accessibility.series.summary.default' + combinationSuffix, summaryContext), axisDescription = (shouldDescribeAxis('yAxis') ? ' ' + yAxisInfo + '.' : '') + (shouldDescribeAxis('xAxis') ? ' ' + xAxisInfo + '.' : ''), formatStr = pick(series.options.accessibility &&\n series.options.accessibility.descriptionFormat, chart.options.accessibility.series.descriptionFormat, '');\n return format(formatStr, {\n seriesDescription: summary,\n authorDescription: (description ? ' ' + description : ''),\n axisDescription,\n series,\n chart,\n seriesNumber\n }, void 0);\n }\n /**\n * Set a11y props on a series element\n * @private\n * @param {Highcharts.Series} series\n * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} seriesElement\n */\n function describeSeriesElement(series, seriesElement) {\n const seriesA11yOptions = series.options.accessibility || {}, a11yOptions = series.chart.options.accessibility, landmarkVerbosity = a11yOptions.landmarkVerbosity;\n // Handle role attribute\n if (seriesA11yOptions.exposeAsGroupOnly) {\n seriesElement.setAttribute('role', 'img');\n }\n else if (landmarkVerbosity === 'all') {\n seriesElement.setAttribute('role', 'region');\n }\n else {\n seriesElement.setAttribute('role', 'group');\n }\n seriesElement.setAttribute('tabindex', '-1');\n if (!series.chart.styledMode) {\n // Don't show browser outline on click, despite tabindex\n seriesElement.style.outline = 'none';\n }\n seriesElement.setAttribute('aria-label', stripHTMLTags(a11yOptions.series.descriptionFormatter &&\n a11yOptions.series.descriptionFormatter(series) ||\n defaultSeriesDescriptionFormatter(series), series.chart.renderer.forExport));\n }\n /**\n * Put accessible info on series and points of a series.\n * @param {Highcharts.Series} series The series to add info on.\n */\n function describeSeries(series) {\n const chart = series.chart, firstPointEl = getSeriesFirstPointElement(series), seriesEl = getSeriesA11yElement(series), is3d = chart.is3d && chart.is3d();\n if (seriesEl) {\n // For some series types the order of elements do not match the\n // order of points in series. In that case we have to reverse them\n // in order for AT to read them out in an understandable order.\n // Due to z-index issues we cannot do this for 3D charts.\n if (seriesEl.lastChild === firstPointEl && !is3d) {\n reverseChildNodes(seriesEl);\n }\n describePointsInSeries(series);\n unhideChartElementFromAT(chart, seriesEl);\n if (shouldDescribeSeriesElement(series)) {\n describeSeriesElement(series, seriesEl);\n }\n else {\n seriesEl.removeAttribute('aria-label');\n }\n }\n }\n /* *\n *\n * Default Export\n *\n * */\n const SeriesDescriber = {\n defaultPointDescriptionFormatter,\n defaultSeriesDescriptionFormatter,\n describeSeries\n };\n\n return SeriesDescriber;\n });\n _registerModule(_modules, 'Accessibility/Components/SeriesComponent/NewDataAnnouncer.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js'], _modules['Accessibility/Utils/Announcer.js'], _modules['Accessibility/Utils/ChartUtilities.js'], _modules['Accessibility/Utils/EventProvider.js'], _modules['Accessibility/Components/SeriesComponent/SeriesDescriber.js']], function (H, U, Announcer, ChartUtilities, EventProvider, SeriesDescriber) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Handle announcing new data for a chart.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { addEvent, defined } = U;\n const { getChartTitle } = ChartUtilities;\n const { defaultPointDescriptionFormatter, defaultSeriesDescriptionFormatter } = SeriesDescriber;\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * @private\n */\n function chartHasAnnounceEnabled(chart) {\n return !!chart.options.accessibility.announceNewData.enabled;\n }\n /**\n * @private\n */\n function findPointInDataArray(point) {\n const candidates = point.series.data.filter((candidate) => (point.x === candidate.x && point.y === candidate.y));\n return candidates.length === 1 ? candidates[0] : point;\n }\n /**\n * Get array of unique series from two arrays\n * @private\n */\n function getUniqueSeries(arrayA, arrayB) {\n const uniqueSeries = (arrayA || []).concat(arrayB || []).reduce((acc, cur) => {\n acc[cur.name + cur.index] = cur;\n return acc;\n }, {});\n return Object\n .keys(uniqueSeries)\n .map((ix) => uniqueSeries[ix]);\n }\n /* *\n *\n * Class\n *\n * */\n /**\n * @private\n * @class\n */\n class NewDataAnnouncer {\n /* *\n *\n * Constructor\n *\n * */\n constructor(chart) {\n /* *\n *\n * Public\n *\n * */\n this.announcer = void 0;\n this.dirty = {\n allSeries: {}\n };\n this.eventProvider = void 0;\n this.lastAnnouncementTime = 0;\n this.chart = chart;\n }\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Initialize the new data announcer.\n * @private\n */\n init() {\n const chart = this.chart;\n const announceOptions = (chart.options.accessibility.announceNewData);\n const announceType = announceOptions.interruptUser ?\n 'assertive' : 'polite';\n this.lastAnnouncementTime = 0;\n this.dirty = {\n allSeries: {}\n };\n this.eventProvider = new EventProvider();\n this.announcer = new Announcer(chart, announceType);\n this.addEventListeners();\n }\n /**\n * Remove traces of announcer.\n * @private\n */\n destroy() {\n this.eventProvider.removeAddedEvents();\n this.announcer.destroy();\n }\n /**\n * Add event listeners for the announcer\n * @private\n */\n addEventListeners() {\n const announcer = this, chart = this.chart, e = this.eventProvider;\n e.addEvent(chart, 'afterApplyDrilldown', function () {\n announcer.lastAnnouncementTime = 0;\n });\n e.addEvent(chart, 'afterAddSeries', function (e) {\n announcer.onSeriesAdded(e.series);\n });\n e.addEvent(chart, 'redraw', function () {\n announcer.announceDirtyData();\n });\n }\n /**\n * On new data series added, update dirty list.\n * @private\n * @param {Highcharts.Series} series\n */\n onSeriesAdded(series) {\n if (chartHasAnnounceEnabled(this.chart)) {\n this.dirty.hasDirty = true;\n this.dirty.allSeries[series.name + series.index] = series;\n // Add it to newSeries storage unless we already have one\n this.dirty.newSeries = defined(this.dirty.newSeries) ?\n void 0 : series;\n }\n }\n /**\n * Gather what we know and announce the data to user.\n * @private\n */\n announceDirtyData() {\n const chart = this.chart, announcer = this;\n if (chart.options.accessibility.announceNewData &&\n this.dirty.hasDirty) {\n let newPoint = this.dirty.newPoint;\n // If we have a single new point, see if we can find it in the\n // data array. Otherwise we can only pass through options to\n // the description builder, and it is a bit sparse in info.\n if (newPoint) {\n newPoint = findPointInDataArray(newPoint);\n }\n this.queueAnnouncement(Object\n .keys(this.dirty.allSeries)\n .map((ix) => announcer.dirty.allSeries[ix]), this.dirty.newSeries, newPoint);\n // Reset\n this.dirty = {\n allSeries: {}\n };\n }\n }\n /**\n * Announce to user that there is new data.\n * @private\n * @param {Array} dirtySeries\n * Array of series with new data.\n * @param {Highcharts.Series} [newSeries]\n * If a single new series was added, a reference to this series.\n * @param {Highcharts.Point} [newPoint]\n * If a single point was added, a reference to this point.\n */\n queueAnnouncement(dirtySeries, newSeries, newPoint) {\n const chart = this.chart;\n const annOptions = chart.options.accessibility.announceNewData;\n if (annOptions.enabled) {\n const now = +new Date();\n const dTime = now - this.lastAnnouncementTime;\n const time = Math.max(0, annOptions.minAnnounceInterval - dTime);\n // Add series from previously queued announcement.\n const allSeries = getUniqueSeries(this.queuedAnnouncement && this.queuedAnnouncement.series, dirtySeries);\n // Build message and announce\n const message = this.buildAnnouncementMessage(allSeries, newSeries, newPoint);\n if (message) {\n // Is there already one queued?\n if (this.queuedAnnouncement) {\n clearTimeout(this.queuedAnnouncementTimer);\n }\n // Build the announcement\n this.queuedAnnouncement = {\n time: now,\n message: message,\n series: allSeries\n };\n // Queue the announcement\n this.queuedAnnouncementTimer = setTimeout(() => {\n if (this && this.announcer) {\n this.lastAnnouncementTime = +new Date();\n this.announcer.announce(this.queuedAnnouncement.message);\n delete this.queuedAnnouncement;\n delete this.queuedAnnouncementTimer;\n }\n }, time);\n }\n }\n }\n /**\n * Get announcement message for new data.\n * @private\n * @param {Array} dirtySeries\n * Array of series with new data.\n * @param {Highcharts.Series} [newSeries]\n * If a single new series was added, a reference to this series.\n * @param {Highcharts.Point} [newPoint]\n * If a single point was added, a reference to this point.\n *\n * @return {string|null}\n * The announcement message to give to user.\n */\n buildAnnouncementMessage(dirtySeries, newSeries, newPoint) {\n const chart = this.chart, annOptions = chart.options.accessibility.announceNewData;\n // User supplied formatter?\n if (annOptions.announcementFormatter) {\n const formatterRes = annOptions.announcementFormatter(dirtySeries, newSeries, newPoint);\n if (formatterRes !== false) {\n return formatterRes.length ? formatterRes : null;\n }\n }\n // Default formatter - use lang options\n const multiple = H.charts && H.charts.length > 1 ?\n 'Multiple' : 'Single', langKey = newSeries ? 'newSeriesAnnounce' + multiple :\n newPoint ? 'newPointAnnounce' + multiple : 'newDataAnnounce', chartTitle = getChartTitle(chart);\n return chart.langFormat('accessibility.announceNewData.' + langKey, {\n chartTitle: chartTitle,\n seriesDesc: newSeries ?\n defaultSeriesDescriptionFormatter(newSeries) :\n null,\n pointDesc: newPoint ?\n defaultPointDescriptionFormatter(newPoint) :\n null,\n point: newPoint,\n series: newSeries\n });\n }\n }\n /* *\n *\n * Class Namespace\n *\n * */\n (function (NewDataAnnouncer) {\n /* *\n *\n * Declarations\n *\n * */\n /* *\n *\n * Static Properties\n *\n * */\n NewDataAnnouncer.composedMembers = [];\n /* *\n *\n * Static Functions\n *\n * */\n /**\n * @private\n */\n function compose(SeriesClass) {\n if (U.pushUnique(NewDataAnnouncer.composedMembers, SeriesClass)) {\n addEvent(SeriesClass, 'addPoint', seriesOnAddPoint);\n addEvent(SeriesClass, 'updatedData', seriesOnUpdatedData);\n }\n }\n NewDataAnnouncer.compose = compose;\n /**\n * On new point added, update dirty list.\n * @private\n * @param {Highcharts.Point} point\n */\n function seriesOnAddPoint(e) {\n const chart = this.chart, newDataAnnouncer = this.newDataAnnouncer;\n if (newDataAnnouncer &&\n newDataAnnouncer.chart === chart &&\n chartHasAnnounceEnabled(chart)) {\n // Add it to newPoint storage unless we already have one\n newDataAnnouncer.dirty.newPoint = (defined(newDataAnnouncer.dirty.newPoint) ?\n void 0 :\n e.point);\n }\n }\n /**\n * On new data in the series, make sure we add it to the dirty list.\n * @private\n * @param {Highcharts.Series} series\n */\n function seriesOnUpdatedData() {\n const chart = this.chart, newDataAnnouncer = this.newDataAnnouncer;\n if (newDataAnnouncer &&\n newDataAnnouncer.chart === chart &&\n chartHasAnnounceEnabled(chart)) {\n newDataAnnouncer.dirty.hasDirty = true;\n newDataAnnouncer.dirty.allSeries[this.name + this.index] = this;\n }\n }\n })(NewDataAnnouncer || (NewDataAnnouncer = {}));\n /* *\n *\n * Default Export\n *\n * */\n\n return NewDataAnnouncer;\n });\n _registerModule(_modules, 'Accessibility/ProxyElement.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js'], _modules['Accessibility/Utils/EventProvider.js'], _modules['Accessibility/Utils/ChartUtilities.js'], _modules['Accessibility/Utils/HTMLUtilities.js']], function (H, U, EventProvider, ChartUtilities, HTMLUtilities) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Proxy elements are used to shadow SVG elements in HTML for assistive\n * technology, such as screen readers or voice input software.\n *\n * The ProxyElement class represents such an element, and deals with\n * overlay positioning and mirroring events for the target.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { doc } = H;\n const { attr, css, merge } = U;\n const { fireEventOnWrappedOrUnwrappedElement } = ChartUtilities;\n const { cloneMouseEvent, cloneTouchEvent, getFakeMouseEvent, removeElement } = HTMLUtilities;\n /* *\n *\n * Class\n *\n * */\n /**\n * Represents a proxy element that overlays a target and relays events\n * to its target.\n *\n * @private\n * @class\n */\n class ProxyElement {\n /* *\n *\n * Constructor\n *\n * */\n constructor(chart, target, proxyElementType = 'button', wrapperElementType, attributes) {\n this.chart = chart;\n this.target = target;\n this.eventProvider = new EventProvider();\n const innerEl = this.innerElement =\n doc.createElement(proxyElementType), wrapperEl = this.element = wrapperElementType ?\n doc.createElement(wrapperElementType) : innerEl;\n if (!chart.styledMode) {\n this.hideElementVisually(innerEl);\n }\n if (wrapperElementType) {\n if (wrapperElementType === 'li' && !chart.styledMode) {\n wrapperEl.style.listStyle = 'none';\n }\n wrapperEl.appendChild(innerEl);\n this.element = wrapperEl;\n }\n this.updateTarget(target, attributes);\n }\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Fake a click event on the target.\n */\n click() {\n const pos = this.getTargetPosition();\n pos.x += pos.width / 2;\n pos.y += pos.height / 2;\n const fakeEventObject = getFakeMouseEvent('click', pos);\n fireEventOnWrappedOrUnwrappedElement(this.target.click, fakeEventObject);\n }\n /**\n * Update the target to be proxied. The position and events are updated to\n * match the new target.\n * @param target The new target definition\n * @param attributes New HTML attributes to apply to the proxy. Set an\n * attribute to null to remove.\n */\n updateTarget(target, attributes) {\n this.target = target;\n this.updateCSSClassName();\n const attrs = attributes || {};\n Object.keys(attrs).forEach((a) => {\n if (attrs[a] === null) {\n delete attrs[a];\n }\n });\n const targetAriaLabel = this.getTargetAttr(target.click, 'aria-label');\n attr(this.innerElement, merge(targetAriaLabel ? {\n 'aria-label': targetAriaLabel\n } : {}, attrs));\n this.eventProvider.removeAddedEvents();\n this.addProxyEventsToElement(this.innerElement, target.click);\n this.refreshPosition();\n }\n /**\n * Refresh the position of the proxy element to match the current target\n */\n refreshPosition() {\n const bBox = this.getTargetPosition();\n css(this.innerElement, {\n width: (bBox.width || 1) + 'px',\n height: (bBox.height || 1) + 'px',\n left: (Math.round(bBox.x) || 0) + 'px',\n top: (Math.round(bBox.y) || 0) + 'px'\n });\n }\n /**\n * Remove button from DOM, and clear events.\n */\n remove() {\n this.eventProvider.removeAddedEvents();\n removeElement(this.element);\n }\n // -------------------------- private ------------------------------------\n /**\n * Update the CSS class name to match target\n */\n updateCSSClassName() {\n const stringHasNoTooltip = (s) => (s.indexOf('highcharts-no-tooltip') > -1);\n const legend = this.chart.legend;\n const groupDiv = legend.group && legend.group.div;\n const noTooltipOnGroup = stringHasNoTooltip(groupDiv && groupDiv.className || '');\n const targetClassName = this.getTargetAttr(this.target.click, 'class') || '';\n const noTooltipOnTarget = stringHasNoTooltip(targetClassName);\n this.innerElement.className = noTooltipOnGroup || noTooltipOnTarget ?\n 'highcharts-a11y-proxy-element highcharts-no-tooltip' :\n 'highcharts-a11y-proxy-element';\n }\n /**\n * Mirror events for a proxy element to a target\n */\n addProxyEventsToElement(element, target) {\n [\n 'click', 'touchstart', 'touchend', 'touchcancel', 'touchmove',\n 'mouseover', 'mouseenter', 'mouseleave', 'mouseout'\n ].forEach((evtType) => {\n const isTouchEvent = evtType.indexOf('touch') === 0;\n this.eventProvider.addEvent(element, evtType, (e) => {\n const clonedEvent = isTouchEvent ?\n cloneTouchEvent(e) :\n cloneMouseEvent(e);\n if (target) {\n fireEventOnWrappedOrUnwrappedElement(target, clonedEvent);\n }\n e.stopPropagation();\n // #9682, #15318: Touch scrolling didnt work when touching\n // proxy\n if (!isTouchEvent) {\n e.preventDefault();\n }\n }, { passive: false });\n });\n }\n /**\n * Set visually hidden style on a proxy element\n */\n hideElementVisually(el) {\n css(el, {\n borderWidth: 0,\n backgroundColor: 'transparent',\n cursor: 'pointer',\n outline: 'none',\n opacity: 0.001,\n filter: 'alpha(opacity=1)',\n zIndex: 999,\n overflow: 'hidden',\n padding: 0,\n margin: 0,\n display: 'block',\n position: 'absolute',\n '-ms-filter': 'progid:DXImageTransform.Microsoft.Alpha(Opacity=1)'\n });\n }\n /**\n * Get the position relative to chart container for the target\n */\n getTargetPosition() {\n const clickTarget = this.target.click;\n // We accept both DOM elements and wrapped elements as click targets.\n const clickTargetElement = clickTarget.element ?\n clickTarget.element :\n clickTarget;\n const posElement = this.target.visual || clickTargetElement;\n const chartDiv = this.chart.renderTo;\n if (chartDiv && posElement && posElement.getBoundingClientRect) {\n const rectEl = posElement.getBoundingClientRect(), chartPos = this.chart.pointer.getChartPosition();\n return {\n x: (rectEl.left - chartPos.left) / chartPos.scaleX,\n y: (rectEl.top - chartPos.top) / chartPos.scaleY,\n width: rectEl.right / chartPos.scaleX -\n rectEl.left / chartPos.scaleX,\n height: rectEl.bottom / chartPos.scaleY -\n rectEl.top / chartPos.scaleY\n };\n }\n return { x: 0, y: 0, width: 1, height: 1 };\n }\n /**\n * Get an attribute value of a target\n */\n getTargetAttr(target, key) {\n if (target.element) {\n return target.element.getAttribute(key);\n }\n return target.getAttribute(key);\n }\n }\n /* *\n *\n * Default Export\n *\n * */\n\n return ProxyElement;\n });\n _registerModule(_modules, 'Accessibility/ProxyProvider.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js'], _modules['Accessibility/Utils/ChartUtilities.js'], _modules['Accessibility/Utils/DOMElementProvider.js'], _modules['Accessibility/Utils/HTMLUtilities.js'], _modules['Accessibility/ProxyElement.js']], function (H, U, CU, DOMElementProvider, HU, ProxyElement) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Proxy elements are used to shadow SVG elements in HTML for assistive\n * technology, such as screen readers or voice input software.\n *\n * The ProxyProvider keeps track of all proxy elements of the a11y module,\n * and updating their order and positioning.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { doc } = H;\n const { attr, css } = U;\n const { unhideChartElementFromAT } = CU;\n const { removeElement, removeChildNodes } = HU;\n /* *\n *\n * Class\n *\n * */\n /**\n * Keeps track of all proxy elements and proxy groups.\n *\n * @private\n * @class\n */\n class ProxyProvider {\n /* *\n *\n * Constructor\n *\n * */\n constructor(chart) {\n this.chart = chart;\n this.domElementProvider = new DOMElementProvider();\n this.groups = {};\n this.groupOrder = [];\n this.beforeChartProxyPosContainer = this.createProxyPosContainer('before');\n this.afterChartProxyPosContainer = this.createProxyPosContainer('after');\n this.update();\n }\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable */\n /**\n * Add a new proxy element to a group, proxying a target control.\n */\n addProxyElement(groupKey, target, proxyElementType = 'button', attributes) {\n const group = this.groups[groupKey];\n if (!group) {\n throw new Error('ProxyProvider.addProxyElement: Invalid group key ' + groupKey);\n }\n const wrapperElementType = group.type === 'ul' || group.type === 'ol' ?\n 'li' : void 0, proxy = new ProxyElement(this.chart, target, proxyElementType, wrapperElementType, attributes);\n group.proxyContainerElement.appendChild(proxy.element);\n group.proxyElements.push(proxy);\n return proxy;\n }\n /**\n * Create a group that will contain proxy elements. The group order is\n * automatically updated according to the last group order keys.\n *\n * Returns the added group.\n */\n addGroup(groupKey, groupElementType = 'div', attributes) {\n const existingGroup = this.groups[groupKey];\n if (existingGroup) {\n return existingGroup.groupElement;\n }\n const proxyContainer = this.domElementProvider\n .createElement(groupElementType);\n // If we want to add a role to the group, and still use e.g.\n // a list group, we need a wrapper div around the proxyContainer.\n // Used for setting region role on legend.\n let groupElement;\n if (attributes && attributes.role && groupElementType !== 'div') {\n groupElement = this.domElementProvider.createElement('div');\n groupElement.appendChild(proxyContainer);\n }\n else {\n groupElement = proxyContainer;\n }\n groupElement.className = 'highcharts-a11y-proxy-group highcharts-a11y-proxy-group-' +\n groupKey.replace(/\\W/g, '-');\n this.groups[groupKey] = {\n proxyContainerElement: proxyContainer,\n groupElement,\n type: groupElementType,\n proxyElements: []\n };\n attr(groupElement, attributes || {});\n if (groupElementType === 'ul') {\n proxyContainer.setAttribute('role', 'list'); // Needed for webkit\n }\n // Add the group to the end by default, and perhaps then we\n // won't have to reorder the whole set of groups.\n this.afterChartProxyPosContainer.appendChild(groupElement);\n this.updateGroupOrder(this.groupOrder);\n return groupElement;\n }\n /**\n * Update HTML attributes of a group.\n */\n updateGroupAttrs(groupKey, attributes) {\n const group = this.groups[groupKey];\n if (!group) {\n throw new Error('ProxyProvider.updateGroupAttrs: Invalid group key ' + groupKey);\n }\n attr(group.groupElement, attributes);\n }\n /**\n * Reorder the proxy groups.\n *\n * The group key \"series\" refers to the chart's data points / element.\n * This is so that the keyboardNavigation.order option can be used to\n * determine the proxy group order.\n */\n updateGroupOrder(groupKeys) {\n // Store so that we can update order when a new group is created\n this.groupOrder = groupKeys.slice();\n // Don't unnecessarily reorder, because keyboard focus is lost\n if (this.isDOMOrderGroupOrder()) {\n return;\n }\n const seriesIx = groupKeys.indexOf('series');\n const beforeKeys = seriesIx > -1 ? groupKeys.slice(0, seriesIx) : groupKeys;\n const afterKeys = seriesIx > -1 ? groupKeys.slice(seriesIx + 1) : [];\n // Store focused element since it will be lost when reordering\n const activeElement = doc.activeElement;\n // Add groups to correct container\n ['before', 'after'].forEach((pos) => {\n const posContainer = this[pos === 'before' ?\n 'beforeChartProxyPosContainer' :\n 'afterChartProxyPosContainer'];\n const keys = pos === 'before' ? beforeKeys : afterKeys;\n removeChildNodes(posContainer);\n keys.forEach((groupKey) => {\n const group = this.groups[groupKey];\n if (group) {\n posContainer.appendChild(group.groupElement);\n }\n });\n });\n // Attempt to restore focus after reordering, but note that this may\n // cause screen readers re-announcing the button.\n if ((this.beforeChartProxyPosContainer.contains(activeElement) ||\n this.afterChartProxyPosContainer.contains(activeElement)) &&\n activeElement && activeElement.focus) {\n activeElement.focus();\n }\n }\n /**\n * Remove all proxy elements in a group\n */\n clearGroup(groupKey) {\n const group = this.groups[groupKey];\n if (!group) {\n throw new Error('ProxyProvider.clearGroup: Invalid group key ' + groupKey);\n }\n removeChildNodes(group.proxyContainerElement);\n }\n /**\n * Remove a group from the DOM and from the proxy provider's group list.\n * All child elements are removed.\n * If the group does not exist, nothing happens.\n */\n removeGroup(groupKey) {\n const group = this.groups[groupKey];\n if (group) {\n removeElement(group.groupElement);\n delete this.groups[groupKey];\n }\n }\n /**\n * Update the position and order of all proxy groups and elements\n */\n update() {\n this.updatePosContainerPositions();\n this.updateGroupOrder(this.groupOrder);\n this.updateProxyElementPositions();\n }\n /**\n * Update all proxy element positions\n */\n updateProxyElementPositions() {\n Object.keys(this.groups).forEach(this.updateGroupProxyElementPositions.bind(this));\n }\n /**\n * Update a group's proxy elements' positions.\n * If the group does not exist, nothing happens.\n */\n updateGroupProxyElementPositions(groupKey) {\n const group = this.groups[groupKey];\n if (group) {\n group.proxyElements.forEach((el) => el.refreshPosition());\n }\n }\n /**\n * Remove all added elements\n */\n destroy() {\n this.domElementProvider.destroyCreatedElements();\n }\n // -------------------------- private ------------------------------------\n /**\n * Create and return a pos container element (the overall containers for\n * the proxy groups).\n */\n createProxyPosContainer(classNamePostfix) {\n const el = this.domElementProvider.createElement('div');\n el.setAttribute('aria-hidden', 'false');\n el.className = 'highcharts-a11y-proxy-container' + (classNamePostfix ? '-' + classNamePostfix : '');\n css(el, {\n top: '0',\n left: '0'\n });\n if (!this.chart.styledMode) {\n el.style.whiteSpace = 'nowrap';\n el.style.position = 'absolute';\n }\n return el;\n }\n /**\n * Get an array of group keys that corresponds to the current group order\n * in the DOM.\n */\n getCurrentGroupOrderInDOM() {\n const getGroupKeyFromElement = (el) => {\n const allGroups = Object.keys(this.groups);\n let i = allGroups.length;\n while (i--) {\n const groupKey = allGroups[i];\n const group = this.groups[groupKey];\n if (group && el === group.groupElement) {\n return groupKey;\n }\n }\n };\n const getChildrenGroupOrder = (el) => {\n const childrenOrder = [];\n const children = el.children;\n for (let i = 0; i < children.length; ++i) {\n const groupKey = getGroupKeyFromElement(children[i]);\n if (groupKey) {\n childrenOrder.push(groupKey);\n }\n }\n return childrenOrder;\n };\n const before = getChildrenGroupOrder(this.beforeChartProxyPosContainer);\n const after = getChildrenGroupOrder(this.afterChartProxyPosContainer);\n before.push('series');\n return before.concat(after);\n }\n /**\n * Check if the current DOM order matches the current group order, so that\n * a reordering/update is unnecessary.\n */\n isDOMOrderGroupOrder() {\n const domOrder = this.getCurrentGroupOrderInDOM();\n const groupOrderWithGroups = this.groupOrder.filter((x) => x === 'series' || !!this.groups[x]);\n let i = domOrder.length;\n if (i !== groupOrderWithGroups.length) {\n return false;\n }\n while (i--) {\n if (domOrder[i] !== groupOrderWithGroups[i]) {\n return false;\n }\n }\n return true;\n }\n /**\n * Update the DOM positions of the before/after proxy\n * positioning containers for the groups.\n */\n updatePosContainerPositions() {\n const chart = this.chart;\n // If exporting, don't add these containers to the DOM.\n if (chart.renderer.forExport) {\n return;\n }\n const rendererSVGEl = chart.renderer.box;\n chart.container.insertBefore(this.afterChartProxyPosContainer, rendererSVGEl.nextSibling);\n chart.container.insertBefore(this.beforeChartProxyPosContainer, rendererSVGEl);\n unhideChartElementFromAT(this.chart, this.afterChartProxyPosContainer);\n unhideChartElementFromAT(this.chart, this.beforeChartProxyPosContainer);\n }\n }\n /* *\n *\n * Export Default\n *\n * */\n\n return ProxyProvider;\n });\n _registerModule(_modules, 'Stock/RangeSelector/RangeSelectorDefaults.js', [], function () {\n /* *\n *\n * (c) 2010-2021 Torstein Honsi\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n /* *\n *\n * Declarations\n *\n * */\n /**\n * Language object. The language object is global and it can't be set\n * on each chart initialization. Instead, use `Highcharts.setOptions` to\n * set it before any chart is initialized.\n *\n * ```js\n * Highcharts.setOptions({\n * lang: {\n * months: [\n * 'Janvier', 'Février', 'Mars', 'Avril',\n * 'Mai', 'Juin', 'Juillet', 'Août',\n * 'Septembre', 'Octobre', 'Novembre', 'Décembre'\n * ],\n * weekdays: [\n * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',\n * 'Jeudi', 'Vendredi', 'Samedi'\n * ]\n * }\n * });\n * ```\n *\n * @optionparent lang\n */\n const lang = {\n /**\n * The text for the label for the range selector buttons.\n *\n * @product highstock gantt\n */\n rangeSelectorZoom: 'Zoom',\n /**\n * The text for the label for the \"from\" input box in the range\n * selector. Since v9.0, this string is empty as the label is not\n * rendered by default.\n *\n * @product highstock gantt\n */\n rangeSelectorFrom: '',\n /**\n * The text for the label for the \"to\" input box in the range selector.\n *\n * @product highstock gantt\n */\n rangeSelectorTo: '→'\n };\n /**\n * The range selector is a tool for selecting ranges to display within\n * the chart. It provides buttons to select preconfigured ranges in\n * the chart, like 1 day, 1 week, 1 month etc. It also provides input\n * boxes where min and max dates can be manually input.\n *\n * @product highstock gantt\n * @optionparent rangeSelector\n */\n const rangeSelector = {\n /**\n * Whether to enable all buttons from the start. By default buttons are\n * only enabled if the corresponding time range exists on the X axis,\n * but enabling all buttons allows for dynamically loading different\n * time ranges.\n *\n * @sample {highstock} stock/rangeselector/allbuttonsenabled-true/\n * All buttons enabled\n *\n * @since 2.0.3\n */\n allButtonsEnabled: false,\n /**\n * An array of configuration objects for the buttons.\n *\n * Defaults to:\n * ```js\n * buttons: [{\n * type: 'month',\n * count: 1,\n * text: '1m',\n * title: 'View 1 month'\n * }, {\n * type: 'month',\n * count: 3,\n * text: '3m',\n * title: 'View 3 months'\n * }, {\n * type: 'month',\n * count: 6,\n * text: '6m',\n * title: 'View 6 months'\n * }, {\n * type: 'ytd',\n * text: 'YTD',\n * title: 'View year to date'\n * }, {\n * type: 'year',\n * count: 1,\n * text: '1y',\n * title: 'View 1 year'\n * }, {\n * type: 'all',\n * text: 'All',\n * title: 'View all'\n * }]\n * ```\n *\n * @sample {highstock} stock/demo/rangeselector-datagrouping/\n * Data grouping by buttons\n *\n * @type {Array<*>}\n */\n buttons: void 0,\n /**\n * How many units of the defined type the button should span. If `type`\n * is \"month\" and `count` is 3, the button spans three months.\n *\n * @type {number}\n * @default 1\n * @apioption rangeSelector.buttons.count\n */\n /**\n * Fires when clicking on the rangeSelector button. One parameter,\n * event, is passed to the function, containing common event\n * information.\n *\n * ```js\n * click: function(e) {\n * console.log(this);\n * }\n * ```\n *\n * Return false to stop default button's click action.\n *\n * @sample {highstock} stock/rangeselector/button-click/\n * Click event on the button\n *\n * @type {Highcharts.RangeSelectorClickCallbackFunction}\n * @apioption rangeSelector.buttons.events.click\n */\n /**\n * Additional range (in milliseconds) added to the end of the calculated\n * time span.\n *\n * @sample {highstock} stock/rangeselector/min-max-offsets/\n * Button offsets\n *\n * @type {number}\n * @default 0\n * @since 6.0.0\n * @apioption rangeSelector.buttons.offsetMax\n */\n /**\n * Additional range (in milliseconds) added to the start of the\n * calculated time span.\n *\n * @sample {highstock} stock/rangeselector/min-max-offsets/\n * Button offsets\n *\n * @type {number}\n * @default 0\n * @since 6.0.0\n * @apioption rangeSelector.buttons.offsetMin\n */\n /**\n * When buttons apply dataGrouping on a series, by default zooming\n * in/out will deselect buttons and unset dataGrouping. Enable this\n * option to keep buttons selected when extremes change.\n *\n * @sample {highstock} stock/rangeselector/preserve-datagrouping/\n * Different preserveDataGrouping settings\n *\n * @type {boolean}\n * @default false\n * @since 6.1.2\n * @apioption rangeSelector.buttons.preserveDataGrouping\n */\n /**\n * A custom data grouping object for each button.\n *\n * @see [series.dataGrouping](#plotOptions.series.dataGrouping)\n *\n * @sample {highstock} stock/demo/rangeselector-datagrouping/\n * Data grouping by range selector buttons\n *\n * @type {*}\n * @extends plotOptions.series.dataGrouping\n * @apioption rangeSelector.buttons.dataGrouping\n */\n /**\n * The text for the button itself.\n *\n * @type {string}\n * @apioption rangeSelector.buttons.text\n */\n /**\n * Explanation for the button, shown as a tooltip on hover, and used by\n * assistive technology.\n *\n * @type {string}\n * @apioption rangeSelector.buttons.title\n */\n /**\n * Defined the time span for the button. Can be one of `millisecond`,\n * `second`, `minute`, `hour`, `day`, `week`, `month`, `year`, `ytd`,\n * and `all`.\n *\n * @type {Highcharts.RangeSelectorButtonTypeValue}\n * @apioption rangeSelector.buttons.type\n */\n /**\n * The space in pixels between the buttons in the range selector.\n */\n buttonSpacing: 5,\n /**\n * Whether to collapse the range selector buttons into a dropdown when\n * there is not enough room to show everything in a single row, instead\n * of dividing the range selector into multiple rows.\n * Can be one of the following:\n * - `always`: Always collapse\n * - `responsive`: Only collapse when there is not enough room\n * - `never`: Never collapse\n *\n * @sample {highstock} stock/rangeselector/dropdown/\n * Dropdown option\n *\n * @validvalue [\"always\", \"responsive\", \"never\"]\n * @since 9.0.0\n */\n dropdown: 'responsive',\n /**\n * Enable or disable the range selector. Default to `true` for stock\n * charts, using the `stockChart` factory.\n *\n * @sample {highstock} stock/rangeselector/enabled/\n * Disable the range selector\n *\n * @type {boolean|undefined}\n * @default {highstock} true\n */\n enabled: void 0,\n /**\n * The vertical alignment of the rangeselector box. Allowed properties\n * are `top`, `middle`, `bottom`.\n *\n * @sample {highstock} stock/rangeselector/vertical-align-middle/\n * Middle\n * @sample {highstock} stock/rangeselector/vertical-align-bottom/\n * Bottom\n *\n * @type {Highcharts.VerticalAlignValue}\n * @since 6.0.0\n */\n verticalAlign: 'top',\n /**\n * A collection of attributes for the buttons. The object takes SVG\n * attributes like `fill`, `stroke`, `stroke-width`, as well as `style`,\n * a collection of CSS properties for the text.\n *\n * The object can also be extended with states, so you can set\n * presentational options for `hover`, `select` or `disabled` button\n * states.\n *\n * CSS styles for the text label.\n *\n * In styled mode, the buttons are styled by the\n * `.highcharts-range-selector-buttons .highcharts-button` rule with its\n * different states.\n *\n * @sample {highstock} stock/rangeselector/styling/\n * Styling the buttons and inputs\n *\n * @type {Highcharts.SVGAttributes}\n */\n buttonTheme: {\n /** @ignore */\n width: 28,\n /** @ignore */\n height: 18,\n /** @ignore */\n padding: 2,\n /** @ignore */\n zIndex: 7 // #484, #852\n },\n /**\n * When the rangeselector is floating, the plot area does not reserve\n * space for it. This opens for positioning anywhere on the chart.\n *\n * @sample {highstock} stock/rangeselector/floating/\n * Placing the range selector between the plot area and the\n * navigator\n *\n * @since 6.0.0\n */\n floating: false,\n /**\n * The x offset of the range selector relative to its horizontal\n * alignment within `chart.spacingLeft` and `chart.spacingRight`.\n *\n * @since 6.0.0\n */\n x: 0,\n /**\n * The y offset of the range selector relative to its horizontal\n * alignment within `chart.spacingLeft` and `chart.spacingRight`.\n *\n * @since 6.0.0\n */\n y: 0,\n /**\n * Deprecated. The height of the range selector. Currently it is\n * calculated dynamically.\n *\n * @deprecated\n * @type {number|undefined}\n * @since 2.1.9\n */\n height: void 0,\n /**\n * The border color of the date input boxes.\n *\n * @sample {highstock} stock/rangeselector/styling/\n * Styling the buttons and inputs\n *\n * @type {Highcharts.ColorString}\n * @since 1.3.7\n */\n inputBoxBorderColor: 'none',\n /**\n * The pixel height of the date input boxes.\n *\n * @sample {highstock} stock/rangeselector/styling/\n * Styling the buttons and inputs\n *\n * @since 1.3.7\n */\n inputBoxHeight: 17,\n /**\n * The pixel width of the date input boxes. When `undefined`, the width\n * is fitted to the rendered content.\n *\n * @sample {highstock} stock/rangeselector/styling/\n * Styling the buttons and inputs\n *\n * @type {number|undefined}\n * @since 1.3.7\n */\n inputBoxWidth: void 0,\n /**\n * The date format in the input boxes when not selected for editing.\n * Defaults to `%e %b %Y`.\n *\n * This is used to determine which type of input to show,\n * `datetime-local`, `date` or `time` and falling back to `text` when\n * the browser does not support the input type or the format contains\n * milliseconds.\n *\n * @sample {highstock} stock/rangeselector/input-type/\n * Input types\n * @sample {highstock} stock/rangeselector/input-format/\n * Milliseconds in the range selector\n *\n */\n inputDateFormat: '%e %b %Y',\n /**\n * A custom callback function to parse values entered in the input boxes\n * and return a valid JavaScript time as milliseconds since 1970.\n * The first argument passed is a value to parse,\n * second is a boolean indicating use of the UTC time.\n *\n * This will only get called for inputs of type `text`. Since v8.2.3,\n * the input type is dynamically determined based on the granularity\n * of the `inputDateFormat` and the browser support.\n *\n * @sample {highstock} stock/rangeselector/input-format/\n * Milliseconds in the range selector\n *\n * @type {Highcharts.RangeSelectorParseCallbackFunction}\n * @since 1.3.3\n */\n inputDateParser: void 0,\n /**\n * The date format in the input boxes when they are selected for\n * editing. This must be a format that is recognized by JavaScript\n * Date.parse.\n *\n * This will only be used for inputs of type `text`. Since v8.2.3,\n * the input type is dynamically determined based on the granularity\n * of the `inputDateFormat` and the browser support.\n *\n * @sample {highstock} stock/rangeselector/input-format/\n * Milliseconds in the range selector\n *\n */\n inputEditDateFormat: '%Y-%m-%d',\n /**\n * Enable or disable the date input boxes.\n */\n inputEnabled: true,\n /**\n * Positioning for the input boxes. Allowed properties are `align`,\n * `x` and `y`.\n *\n * @since 1.2.4\n */\n inputPosition: {\n /**\n * The alignment of the input box. Allowed properties are `left`,\n * `center`, `right`.\n *\n * @sample {highstock} stock/rangeselector/input-button-position/\n * Alignment\n *\n * @type {Highcharts.AlignValue}\n * @since 6.0.0\n */\n align: 'right',\n /**\n * X offset of the input row.\n */\n x: 0,\n /**\n * Y offset of the input row.\n */\n y: 0\n },\n /**\n * The space in pixels between the labels and the date input boxes in\n * the range selector.\n *\n * @since 9.0.0\n */\n inputSpacing: 5,\n /**\n * The index of the button to appear pre-selected.\n *\n * @type {number}\n */\n selected: void 0,\n /**\n * Positioning for the button row.\n *\n * @since 1.2.4\n */\n buttonPosition: {\n /**\n * The alignment of the input box. Allowed properties are `left`,\n * `center`, `right`.\n *\n * @sample {highstock} stock/rangeselector/input-button-position/\n * Alignment\n *\n * @type {Highcharts.AlignValue}\n * @since 6.0.0\n */\n align: 'left',\n /**\n * X offset of the button row.\n */\n x: 0,\n /**\n * Y offset of the button row.\n */\n y: 0\n },\n /**\n * CSS for the HTML inputs in the range selector.\n *\n * In styled mode, the inputs are styled by the\n * `.highcharts-range-input text` rule in SVG mode, and\n * `input.highcharts-range-selector` when active.\n *\n * @sample {highstock} stock/rangeselector/styling/\n * Styling the buttons and inputs\n *\n * @type {Highcharts.CSSObject}\n * @apioption rangeSelector.inputStyle\n */\n inputStyle: {\n /** @ignore */\n color: \"#334eff\" /* Palette.highlightColor80 */,\n /** @ignore */\n cursor: 'pointer',\n /** @ignore */\n fontSize: '0.8em'\n },\n /**\n * CSS styles for the labels - the Zoom, From and To texts.\n *\n * In styled mode, the labels are styled by the\n * `.highcharts-range-label` class.\n *\n * @sample {highstock} stock/rangeselector/styling/\n * Styling the buttons and inputs\n *\n * @type {Highcharts.CSSObject}\n */\n labelStyle: {\n /** @ignore */\n color: \"#666666\" /* Palette.neutralColor60 */,\n /** @ignore */\n fontSize: '0.8em'\n }\n };\n /* *\n *\n * Default Export\n *\n * */\n const RangeSelectorDefaults = {\n lang,\n rangeSelector\n };\n\n return RangeSelectorDefaults;\n });\n _registerModule(_modules, 'Stock/RangeSelector/RangeSelectorComposition.js', [_modules['Core/Defaults.js'], _modules['Stock/RangeSelector/RangeSelectorDefaults.js'], _modules['Core/Utilities.js']], function (D, RangeSelectorDefaults, U) {\n /* *\n *\n * (c) 2010-2021 Torstein Honsi\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { defaultOptions, setOptions } = D;\n const { addEvent, defined, extend, find, isNumber, merge, pick } = U;\n /* *\n *\n * Constants\n *\n * */\n const chartDestroyEvents = [];\n const composedMembers = [];\n /* *\n *\n * Variables\n *\n * */\n let RangeSelectorConstructor;\n /* *\n *\n * Functions\n *\n * */\n /**\n * Get the axis min value based on the range option and the current max. For\n * stock charts this is extended via the {@link RangeSelector} so that if the\n * selected range is a multiple of months or years, it is compensated for\n * various month lengths.\n *\n * @private\n * @function Highcharts.Axis#minFromRange\n * @return {number|undefined}\n * The new minimum value.\n */\n function axisMinFromRange() {\n const rangeOptions = this.range, type = rangeOptions.type, max = this.max, time = this.chart.time, \n // Get the true range from a start date\n getTrueRange = function (base, count) {\n const timeName = type === 'year' ?\n 'FullYear' : 'Month';\n const date = new time.Date(base);\n const basePeriod = time.get(timeName, date);\n time.set(timeName, date, basePeriod + count);\n if (basePeriod === time.get(timeName, date)) {\n time.set('Date', date, 0); // #6537\n }\n return date.getTime() - base;\n };\n let min, range;\n if (isNumber(rangeOptions)) {\n min = max - rangeOptions;\n range = rangeOptions;\n }\n else if (rangeOptions) {\n min = max + getTrueRange(max, -(rangeOptions.count || 1));\n // Let the fixedRange reflect initial settings (#5930)\n if (this.chart) {\n this.chart.fixedRange = max - min;\n }\n }\n const dataMin = pick(this.dataMin, Number.MIN_VALUE);\n if (!isNumber(min)) {\n min = dataMin;\n }\n if (min <= dataMin) {\n min = dataMin;\n if (typeof range === 'undefined') { // #4501\n range = getTrueRange(min, rangeOptions.count);\n }\n this.newMax = Math.min(min + range, pick(this.dataMax, Number.MAX_VALUE));\n }\n if (!isNumber(max)) {\n min = void 0;\n }\n else if (!isNumber(rangeOptions) &&\n rangeOptions &&\n rangeOptions._offsetMin) {\n min += rangeOptions._offsetMin;\n }\n return min;\n }\n /**\n * @private\n */\n function compose(AxisClass, ChartClass, RangeSelectorClass) {\n RangeSelectorConstructor = RangeSelectorClass;\n if (U.pushUnique(composedMembers, AxisClass)) {\n AxisClass.prototype.minFromRange = axisMinFromRange;\n }\n if (U.pushUnique(composedMembers, ChartClass)) {\n addEvent(ChartClass, 'afterGetContainer', onChartAfterGetContainer);\n addEvent(ChartClass, 'beforeRender', onChartBeforeRender);\n addEvent(ChartClass, 'destroy', onChartDestroy);\n addEvent(ChartClass, 'getMargins', onChartGetMargins);\n addEvent(ChartClass, 'render', onChartRender);\n addEvent(ChartClass, 'update', onChartUpdate);\n const chartProto = ChartClass.prototype;\n chartProto.callbacks.push(onChartCallback);\n }\n if (U.pushUnique(composedMembers, setOptions)) {\n extend(defaultOptions, { rangeSelector: RangeSelectorDefaults.rangeSelector });\n extend(defaultOptions.lang, RangeSelectorDefaults.lang);\n }\n }\n /**\n * Initialize rangeselector for stock charts\n * @private\n */\n function onChartAfterGetContainer() {\n if (this.options.rangeSelector &&\n this.options.rangeSelector.enabled) {\n this.rangeSelector = new RangeSelectorConstructor(this);\n }\n }\n /**\n * @private\n */\n function onChartBeforeRender() {\n const chart = this, axes = chart.axes, rangeSelector = chart.rangeSelector;\n if (rangeSelector) {\n if (isNumber(rangeSelector.deferredYTDClick)) {\n rangeSelector.clickButton(rangeSelector.deferredYTDClick);\n delete rangeSelector.deferredYTDClick;\n }\n axes.forEach((axis) => {\n axis.updateNames();\n axis.setScale();\n });\n chart.getAxisMargins();\n rangeSelector.render();\n const verticalAlign = rangeSelector.options.verticalAlign;\n if (!rangeSelector.options.floating) {\n if (verticalAlign === 'bottom') {\n this.extraBottomMargin = true;\n }\n else if (verticalAlign !== 'middle') {\n this.extraTopMargin = true;\n }\n }\n }\n }\n /**\n * @private\n */\n function onChartCallback(chart) {\n let extremes, legend, alignTo, verticalAlign;\n const rangeSelector = chart.rangeSelector, redraw = () => {\n if (rangeSelector) {\n extremes = chart.xAxis[0].getExtremes();\n legend = chart.legend;\n verticalAlign = (rangeSelector &&\n rangeSelector.options.verticalAlign);\n if (isNumber(extremes.min)) {\n rangeSelector.render(extremes.min, extremes.max);\n }\n // Re-align the legend so that it's below the rangeselector\n if (legend.display &&\n verticalAlign === 'top' &&\n verticalAlign === legend.options.verticalAlign) {\n // Create a new alignment box for the legend.\n alignTo = merge(chart.spacingBox);\n if (legend.options.layout === 'vertical') {\n alignTo.y = chart.plotTop;\n }\n else {\n alignTo.y += rangeSelector.getHeight();\n }\n legend.group.placed = false; // Don't animate the alignment.\n legend.align(alignTo);\n }\n }\n };\n if (rangeSelector) {\n const events = find(chartDestroyEvents, (e) => e[0] === chart);\n if (!events) {\n chartDestroyEvents.push([chart, [\n // redraw the scroller on setExtremes\n addEvent(chart.xAxis[0], 'afterSetExtremes', function (e) {\n if (rangeSelector) {\n rangeSelector.render(e.min, e.max);\n }\n }),\n // redraw the scroller chart resize\n addEvent(chart, 'redraw', redraw)\n ]]);\n }\n // do it now\n redraw();\n }\n }\n /**\n * Remove resize/afterSetExtremes at chart destroy.\n * @private\n */\n function onChartDestroy() {\n for (let i = 0, iEnd = chartDestroyEvents.length; i < iEnd; ++i) {\n const events = chartDestroyEvents[i];\n if (events[0] === this) {\n events[1].forEach((unbind) => unbind());\n chartDestroyEvents.splice(i, 1);\n return;\n }\n }\n }\n function onChartGetMargins() {\n const rangeSelector = this.rangeSelector;\n if (rangeSelector) {\n const rangeSelectorHeight = rangeSelector.getHeight();\n if (this.extraTopMargin) {\n this.plotTop += rangeSelectorHeight;\n }\n if (this.extraBottomMargin) {\n this.marginBottom += rangeSelectorHeight;\n }\n }\n }\n /**\n * @private\n */\n function onChartRender() {\n const chart = this, rangeSelector = chart.rangeSelector;\n if (rangeSelector && !rangeSelector.options.floating) {\n rangeSelector.render();\n const verticalAlign = rangeSelector.options.verticalAlign;\n if (verticalAlign === 'bottom') {\n this.extraBottomMargin = true;\n }\n else if (verticalAlign !== 'middle') {\n this.extraTopMargin = true;\n }\n }\n }\n /**\n * @private\n */\n function onChartUpdate(e) {\n const chart = this, options = e.options, optionsRangeSelector = options.rangeSelector, extraBottomMarginWas = this.extraBottomMargin, extraTopMarginWas = this.extraTopMargin;\n let rangeSelector = chart.rangeSelector;\n if (optionsRangeSelector &&\n optionsRangeSelector.enabled &&\n !defined(rangeSelector) &&\n this.options.rangeSelector) {\n this.options.rangeSelector.enabled = true;\n this.rangeSelector = rangeSelector = new RangeSelectorConstructor(this);\n }\n this.extraBottomMargin = false;\n this.extraTopMargin = false;\n if (rangeSelector) {\n onChartCallback(this);\n const verticalAlign = (optionsRangeSelector &&\n optionsRangeSelector.verticalAlign) || (rangeSelector.options && rangeSelector.options.verticalAlign);\n if (!rangeSelector.options.floating) {\n if (verticalAlign === 'bottom') {\n this.extraBottomMargin = true;\n }\n else if (verticalAlign !== 'middle') {\n this.extraTopMargin = true;\n }\n }\n if (this.extraBottomMargin !== extraBottomMarginWas ||\n this.extraTopMargin !== extraTopMarginWas) {\n this.isDirtyBox = true;\n }\n }\n }\n /* *\n *\n * Default Export\n *\n * */\n const RangeSelectorComposition = {\n compose\n };\n\n return RangeSelectorComposition;\n });\n _registerModule(_modules, 'Stock/RangeSelector/RangeSelector.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Defaults.js'], _modules['Core/Globals.js'], _modules['Stock/RangeSelector/RangeSelectorComposition.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (Axis, D, H, RangeSelectorComposition, SVGElement, U) {\n /* *\n *\n * (c) 2010-2021 Torstein Honsi\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { defaultOptions } = D;\n const { addEvent, createElement, css, defined, destroyObjectProperties, discardElement, extend, fireEvent, isNumber, merge, objectEach, pad, pick, pInt, splat } = U;\n /* *\n *\n * Functions\n *\n * */\n /**\n * Get the preferred input type based on a date format string.\n *\n * @private\n * @function preferredInputType\n */\n function preferredInputType(format) {\n const ms = format.indexOf('%L') !== -1;\n if (ms) {\n return 'text';\n }\n const date = ['a', 'A', 'd', 'e', 'w', 'b', 'B', 'm', 'o', 'y', 'Y']\n .some((char) => format.indexOf('%' + char) !== -1);\n const time = ['H', 'k', 'I', 'l', 'M', 'S']\n .some((char) => format.indexOf('%' + char) !== -1);\n if (date && time) {\n return 'datetime-local';\n }\n if (date) {\n return 'date';\n }\n if (time) {\n return 'time';\n }\n return 'text';\n }\n /* *\n *\n * Class\n *\n * */\n /**\n * The range selector.\n *\n * @private\n * @class\n * @name Highcharts.RangeSelector\n * @param {Highcharts.Chart} chart\n */\n class RangeSelector {\n /* *\n *\n * Static Functions\n *\n * */\n /**\n * @private\n */\n static compose(AxisClass, ChartClass) {\n RangeSelectorComposition.compose(AxisClass, ChartClass, RangeSelector);\n }\n /* *\n *\n * Constructor\n *\n * */\n constructor(chart) {\n /* *\n *\n * Properties\n *\n * */\n this.buttons = void 0;\n this.buttonOptions = RangeSelector.prototype.defaultButtons;\n this.initialButtonGroupWidth = 0;\n this.options = void 0;\n this.chart = chart;\n this.init(chart);\n }\n /* *\n *\n * Functions\n *\n * */\n /**\n * The method to run when one of the buttons in the range selectors is\n * clicked\n *\n * @private\n * @function Highcharts.RangeSelector#clickButton\n * @param {number} i\n * The index of the button\n * @param {boolean} [redraw]\n */\n clickButton(i, redraw) {\n const rangeSelector = this, chart = rangeSelector.chart, rangeOptions = rangeSelector.buttonOptions[i], baseAxis = chart.xAxis[0], unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || baseAxis || {}, type = rangeOptions.type, dataGrouping = rangeOptions.dataGrouping;\n let dataMin = unionExtremes.dataMin, dataMax = unionExtremes.dataMax, newMin, newMax = baseAxis && Math.round(Math.min(baseAxis.max, pick(dataMax, baseAxis.max))), // #1568\n baseXAxisOptions, range = rangeOptions._range, rangeMin, minSetting, rangeSetting, ctx, ytdExtremes, addOffsetMin = true;\n // chart has no data, base series is removed\n if (dataMin === null || dataMax === null) {\n return;\n }\n // Set the fixed range before range is altered\n chart.fixedRange = range;\n rangeSelector.setSelected(i);\n // Apply dataGrouping associated to button\n if (dataGrouping) {\n this.forcedDataGrouping = true;\n Axis.prototype.setDataGrouping.call(baseAxis || { chart: this.chart }, dataGrouping, false);\n this.frozenStates = rangeOptions.preserveDataGrouping;\n }\n // Apply range\n if (type === 'month' || type === 'year') {\n if (!baseAxis) {\n // This is set to the user options and picked up later when the\n // axis is instantiated so that we know the min and max.\n range = rangeOptions;\n }\n else {\n ctx = {\n range: rangeOptions,\n max: newMax,\n chart: chart,\n dataMin: dataMin,\n dataMax: dataMax\n };\n newMin = baseAxis.minFromRange.call(ctx);\n if (isNumber(ctx.newMax)) {\n newMax = ctx.newMax;\n }\n // #15799: offsetMin is added in minFromRange so that it works\n // with pre-selected buttons as well\n addOffsetMin = false;\n }\n // Fixed times like minutes, hours, days\n }\n else if (range) {\n newMin = Math.max(newMax - range, dataMin);\n newMax = Math.min(newMin + range, dataMax);\n addOffsetMin = false;\n }\n else if (type === 'ytd') {\n // On user clicks on the buttons, or a delayed action running from\n // the beforeRender event (below), the baseAxis is defined.\n if (baseAxis) {\n // When \"ytd\" is the pre-selected button for the initial view,\n // its calculation is delayed and rerun in the beforeRender\n // event (below). When the series are initialized, but before\n // the chart is rendered, we have access to the xData array\n // (#942).\n if (typeof dataMax === 'undefined' ||\n typeof dataMin === 'undefined') {\n dataMin = Number.MAX_VALUE;\n dataMax = Number.MIN_VALUE;\n chart.series.forEach((series) => {\n // reassign it to the last item\n const xData = series.xData;\n if (xData) {\n dataMin = Math.min(xData[0], dataMin);\n dataMax = Math.max(xData[xData.length - 1], dataMax);\n }\n });\n redraw = false;\n }\n ytdExtremes = rangeSelector.getYTDExtremes(dataMax, dataMin, chart.time.useUTC);\n newMin = rangeMin = ytdExtremes.min;\n newMax = ytdExtremes.max;\n // \"ytd\" is pre-selected. We don't yet have access to processed\n // point and extremes data (things like pointStart and pointInterval\n // are missing), so we delay the process (#942)\n }\n else {\n rangeSelector.deferredYTDClick = i;\n return;\n }\n }\n else if (type === 'all' && baseAxis) {\n // If the navigator exist and the axis range is declared reset that\n // range and from now on only use the range set by a user, #14742.\n if (chart.navigator && chart.navigator.baseSeries[0]) {\n chart.navigator.baseSeries[0].xAxis.options.range = void 0;\n }\n newMin = dataMin;\n newMax = dataMax;\n }\n if (addOffsetMin && rangeOptions._offsetMin && defined(newMin)) {\n newMin += rangeOptions._offsetMin;\n }\n if (rangeOptions._offsetMax && defined(newMax)) {\n newMax += rangeOptions._offsetMax;\n }\n if (this.dropdown) {\n this.dropdown.selectedIndex = i + 1;\n }\n // Update the chart\n if (!baseAxis) {\n // Axis not yet instanciated. Temporarily set min and range\n // options and remove them on chart load (#4317).\n baseXAxisOptions = splat(chart.options.xAxis)[0];\n rangeSetting = baseXAxisOptions.range;\n baseXAxisOptions.range = range;\n minSetting = baseXAxisOptions.min;\n baseXAxisOptions.min = rangeMin;\n addEvent(chart, 'load', function resetMinAndRange() {\n baseXAxisOptions.range = rangeSetting;\n baseXAxisOptions.min = minSetting;\n });\n }\n else {\n // Existing axis object. Set extremes after render time.\n baseAxis.setExtremes(newMin, newMax, pick(redraw, true), void 0, // auto animation\n {\n trigger: 'rangeSelectorButton',\n rangeSelectorButton: rangeOptions\n });\n }\n fireEvent(this, 'afterBtnClick');\n }\n /**\n * Set the selected option. This method only sets the internal flag, it\n * doesn't update the buttons or the actual zoomed range.\n *\n * @private\n * @function Highcharts.RangeSelector#setSelected\n * @param {number} [selected]\n */\n setSelected(selected) {\n this.selected = this.options.selected = selected;\n }\n /**\n * Initialize the range selector\n *\n * @private\n * @function Highcharts.RangeSelector#init\n * @param {Highcharts.Chart} chart\n */\n init(chart) {\n const rangeSelector = this, options = chart.options.rangeSelector, buttonOptions = (options.buttons || rangeSelector.defaultButtons.slice()), selectedOption = options.selected, blurInputs = function () {\n const minInput = rangeSelector.minInput, maxInput = rangeSelector.maxInput;\n // #3274 in some case blur is not defined\n if (minInput && !!minInput.blur) {\n fireEvent(minInput, 'blur');\n }\n if (maxInput && !!maxInput.blur) {\n fireEvent(maxInput, 'blur');\n }\n };\n rangeSelector.chart = chart;\n rangeSelector.options = options;\n rangeSelector.buttons = [];\n rangeSelector.buttonOptions = buttonOptions;\n this.eventsToUnbind = [];\n this.eventsToUnbind.push(addEvent(chart.container, 'mousedown', blurInputs));\n this.eventsToUnbind.push(addEvent(chart, 'resize', blurInputs));\n // Extend the buttonOptions with actual range\n buttonOptions.forEach(rangeSelector.computeButtonRange);\n // zoomed range based on a pre-selected button index\n if (typeof selectedOption !== 'undefined' &&\n buttonOptions[selectedOption]) {\n this.clickButton(selectedOption, false);\n }\n this.eventsToUnbind.push(addEvent(chart, 'load', function () {\n // If a data grouping is applied to the current button, release it\n // when extremes change\n if (chart.xAxis && chart.xAxis[0]) {\n addEvent(chart.xAxis[0], 'setExtremes', function (e) {\n if (this.max - this.min !==\n chart.fixedRange &&\n e.trigger !== 'rangeSelectorButton' &&\n e.trigger !== 'updatedData' &&\n rangeSelector.forcedDataGrouping &&\n !rangeSelector.frozenStates) {\n this.setDataGrouping(false, false);\n }\n });\n }\n }));\n }\n /**\n * Dynamically update the range selector buttons after a new range has been\n * set\n *\n * @private\n * @function Highcharts.RangeSelector#updateButtonStates\n */\n updateButtonStates() {\n const rangeSelector = this, chart = this.chart, dropdown = this.dropdown, baseAxis = chart.xAxis[0], actualRange = Math.round(baseAxis.max - baseAxis.min), hasNoData = !baseAxis.hasVisibleSeries, day = 24 * 36e5, // A single day in milliseconds\n unionExtremes = (chart.scroller &&\n chart.scroller.getUnionExtremes()) || baseAxis, dataMin = unionExtremes.dataMin, dataMax = unionExtremes.dataMax, ytdExtremes = rangeSelector.getYTDExtremes(dataMax, dataMin, chart.time.useUTC), ytdMin = ytdExtremes.min, ytdMax = ytdExtremes.max, selected = rangeSelector.selected, allButtonsEnabled = rangeSelector.options.allButtonsEnabled, buttons = rangeSelector.buttons;\n let selectedExists = isNumber(selected);\n rangeSelector.buttonOptions.forEach((rangeOptions, i) => {\n const range = rangeOptions._range, type = rangeOptions.type, count = rangeOptions.count || 1, button = buttons[i], offsetRange = rangeOptions._offsetMax -\n rangeOptions._offsetMin, isSelected = i === selected, \n // Disable buttons where the range exceeds what is allowed in\n // the current view\n isTooGreatRange = range >\n dataMax - dataMin, \n // Disable buttons where the range is smaller than the minimum\n // range\n isTooSmallRange = range < baseAxis.minRange;\n let state = 0, \n // Do not select the YTD button if not explicitly told so\n isYTDButNotSelected = false, \n // Disable the All button if we're already showing all\n isAllButAlreadyShowingAll = false, isSameRange = range === actualRange;\n // Months and years have a variable range so we check the extremes\n if ((type === 'month' || type === 'year') &&\n (actualRange + 36e5 >=\n { month: 28, year: 365 }[type] * day * count - offsetRange) &&\n (actualRange - 36e5 <=\n { month: 31, year: 366 }[type] * day * count + offsetRange)) {\n isSameRange = true;\n }\n else if (type === 'ytd') {\n isSameRange = (ytdMax - ytdMin + offsetRange) === actualRange;\n isYTDButNotSelected = !isSelected;\n }\n else if (type === 'all') {\n isSameRange = (baseAxis.max - baseAxis.min >=\n dataMax - dataMin);\n isAllButAlreadyShowingAll = (!isSelected &&\n selectedExists &&\n isSameRange);\n }\n // The new zoom area happens to match the range for a button - mark\n // it selected. This happens when scrolling across an ordinal gap.\n // It can be seen in the intraday demos when selecting 1h and scroll\n // across the night gap.\n const disable = (!allButtonsEnabled &&\n (isTooGreatRange ||\n isTooSmallRange ||\n isAllButAlreadyShowingAll ||\n hasNoData));\n const select = ((isSelected && isSameRange) ||\n (isSameRange && !selectedExists && !isYTDButNotSelected) ||\n (isSelected && rangeSelector.frozenStates));\n if (disable) {\n state = 3;\n }\n else if (select) {\n selectedExists = true; // Only one button can be selected\n state = 2;\n }\n // If state has changed, update the button\n if (button.state !== state) {\n button.setState(state);\n if (dropdown) {\n dropdown.options[i + 1].disabled = disable;\n if (state === 2) {\n dropdown.selectedIndex = i + 1;\n }\n }\n // Reset (#9209)\n if (state === 0 && selected === i) {\n rangeSelector.setSelected();\n }\n }\n });\n }\n /**\n * Compute and cache the range for an individual button\n *\n * @private\n * @function Highcharts.RangeSelector#computeButtonRange\n * @param {Highcharts.RangeSelectorButtonsOptions} rangeOptions\n */\n computeButtonRange(rangeOptions) {\n const type = rangeOptions.type, count = rangeOptions.count || 1, \n // these time intervals have a fixed number of milliseconds, as\n // opposed to month, ytd and year\n fixedTimes = {\n millisecond: 1,\n second: 1000,\n minute: 60 * 1000,\n hour: 3600 * 1000,\n day: 24 * 3600 * 1000,\n week: 7 * 24 * 3600 * 1000\n };\n // Store the range on the button object\n if (fixedTimes[type]) {\n rangeOptions._range = fixedTimes[type] * count;\n }\n else if (type === 'month' || type === 'year') {\n rangeOptions._range = {\n month: 30,\n year: 365\n }[type] * 24 * 36e5 * count;\n }\n rangeOptions._offsetMin = pick(rangeOptions.offsetMin, 0);\n rangeOptions._offsetMax = pick(rangeOptions.offsetMax, 0);\n rangeOptions._range +=\n rangeOptions._offsetMax - rangeOptions._offsetMin;\n }\n /**\n * Get the unix timestamp of a HTML input for the dates\n *\n * @private\n * @function Highcharts.RangeSelector#getInputValue\n */\n getInputValue(name) {\n const input = name === 'min' ? this.minInput : this.maxInput;\n const options = this.chart.options\n .rangeSelector;\n const time = this.chart.time;\n if (input) {\n return ((input.type === 'text' && options.inputDateParser) ||\n this.defaultInputDateParser)(input.value, time.useUTC, time);\n }\n return 0;\n }\n /**\n * Set the internal and displayed value of a HTML input for the dates\n *\n * @private\n * @function Highcharts.RangeSelector#setInputValue\n */\n setInputValue(name, inputTime) {\n const options = this.options, time = this.chart.time, input = name === 'min' ? this.minInput : this.maxInput, dateBox = name === 'min' ? this.minDateBox : this.maxDateBox;\n if (input) {\n const hcTimeAttr = input.getAttribute('data-hc-time');\n let updatedTime = defined(hcTimeAttr) ? Number(hcTimeAttr) : void 0;\n if (defined(inputTime)) {\n const previousTime = updatedTime;\n if (defined(previousTime)) {\n input.setAttribute('data-hc-time-previous', previousTime);\n }\n input.setAttribute('data-hc-time', inputTime);\n updatedTime = inputTime;\n }\n input.value = time.dateFormat((this.inputTypeFormats[input.type] ||\n options.inputEditDateFormat), updatedTime);\n if (dateBox) {\n dateBox.attr({\n text: time.dateFormat(options.inputDateFormat, updatedTime)\n });\n }\n }\n }\n /**\n * Set the min and max value of a HTML input for the dates\n *\n * @private\n * @function Highcharts.RangeSelector#setInputExtremes\n */\n setInputExtremes(name, min, max) {\n const input = name === 'min' ? this.minInput : this.maxInput;\n if (input) {\n const format = this.inputTypeFormats[input.type];\n const time = this.chart.time;\n if (format) {\n const newMin = time.dateFormat(format, min);\n if (input.min !== newMin) {\n input.min = newMin;\n }\n const newMax = time.dateFormat(format, max);\n if (input.max !== newMax) {\n input.max = newMax;\n }\n }\n }\n }\n /**\n * @private\n * @function Highcharts.RangeSelector#showInput\n * @param {string} name\n */\n showInput(name) {\n const dateBox = name === 'min' ? this.minDateBox : this.maxDateBox, input = name === 'min' ? this.minInput : this.maxInput;\n if (input && dateBox && this.inputGroup) {\n const isTextInput = input.type === 'text', { translateX = 0, translateY = 0 } = this.inputGroup, { x = 0, width = 0, height = 0 } = dateBox, { inputBoxWidth } = this.options;\n css(input, {\n width: isTextInput ?\n ((width + (inputBoxWidth ? -2 : 20)) + 'px') :\n 'auto',\n height: (height - 2) + 'px',\n border: '2px solid silver'\n });\n if (isTextInput && inputBoxWidth) {\n css(input, {\n left: (translateX + x) + 'px',\n top: translateY + 'px'\n });\n // Inputs of types date, time or datetime-local should be centered\n // on top of the dateBox\n }\n else {\n css(input, {\n left: Math.min(Math.round(x +\n translateX -\n (input.offsetWidth - width) / 2), this.chart.chartWidth - input.offsetWidth) + 'px',\n top: (translateY - (input.offsetHeight - height) / 2) + 'px'\n });\n }\n }\n }\n /**\n * @private\n * @function Highcharts.RangeSelector#hideInput\n * @param {string} name\n */\n hideInput(name) {\n const input = name === 'min' ? this.minInput : this.maxInput;\n if (input) {\n css(input, {\n top: '-9999em',\n border: 0,\n width: '1px',\n height: '1px'\n });\n }\n }\n /**\n * @private\n * @function Highcharts.RangeSelector#defaultInputDateParser\n */\n defaultInputDateParser(inputDate, useUTC, time) {\n const hasTimezone = (str) => str.length > 6 &&\n (str.lastIndexOf('-') === str.length - 6 ||\n str.lastIndexOf('+') === str.length - 6);\n let input = inputDate.split('/').join('-').split(' ').join('T');\n if (input.indexOf('T') === -1) {\n input += 'T00:00';\n }\n if (useUTC) {\n input += 'Z';\n }\n else if (H.isSafari && !hasTimezone(input)) {\n const offset = new Date(input).getTimezoneOffset() / 60;\n input += offset <= 0 ? `+${pad(-offset)}:00` : `-${pad(offset)}:00`;\n }\n let date = Date.parse(input);\n // If the value isn't parsed directly to a value by the\n // browser's Date.parse method, try\n // parsing it a different way\n if (!isNumber(date)) {\n const parts = inputDate.split('-');\n date = Date.UTC(pInt(parts[0]), pInt(parts[1]) - 1, pInt(parts[2]));\n }\n if (time && useUTC && isNumber(date)) {\n date += time.getTimezoneOffset(date);\n }\n return date;\n }\n /**\n * Draw either the 'from' or the 'to' HTML input box of the range selector\n *\n * @private\n * @function Highcharts.RangeSelector#drawInput\n */\n drawInput(name) {\n const { chart, div, inputGroup } = this;\n const rangeSelector = this, chartStyle = chart.renderer.style || {}, renderer = chart.renderer, options = chart.options.rangeSelector, lang = defaultOptions.lang, isMin = name === 'min';\n /**\n * @private\n */\n function updateExtremes() {\n const { maxInput, minInput } = rangeSelector, chartAxis = chart.xAxis[0], unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || chartAxis, dataMin = unionExtremes.dataMin, dataMax = unionExtremes.dataMax;\n let value = rangeSelector.getInputValue(name);\n if (value !== Number(input.getAttribute('data-hc-time-previous')) &&\n isNumber(value)) {\n input.setAttribute('data-hc-time-previous', value);\n // Validate the extremes. If it goes beyound the data min or\n // max, use the actual data extreme (#2438).\n if (isMin && maxInput && isNumber(dataMin)) {\n if (value > Number(maxInput.getAttribute('data-hc-time'))) {\n value = void 0;\n }\n else if (value < dataMin) {\n value = dataMin;\n }\n }\n else if (minInput && isNumber(dataMax)) {\n if (value < Number(minInput.getAttribute('data-hc-time'))) {\n value = void 0;\n }\n else if (value > dataMax) {\n value = dataMax;\n }\n }\n // Set the extremes\n if (typeof value !== 'undefined') { // @todo typof undefined\n chartAxis.setExtremes(isMin ? value : chartAxis.min, isMin ? chartAxis.max : value, void 0, void 0, { trigger: 'rangeSelectorInput' });\n }\n }\n }\n // Create the text label\n const text = lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'] || '';\n const label = renderer\n .label(text, 0)\n .addClass('highcharts-range-label')\n .attr({\n padding: text ? 2 : 0,\n height: text ? options.inputBoxHeight : 0\n })\n .add(inputGroup);\n // Create an SVG label that shows updated date ranges and and records\n // click events that bring in the HTML input.\n const dateBox = renderer\n .label('', 0)\n .addClass('highcharts-range-input')\n .attr({\n padding: 2,\n width: options.inputBoxWidth,\n height: options.inputBoxHeight,\n 'text-align': 'center'\n })\n .on('click', function () {\n // If it is already focused, the onfocus event doesn't fire\n // (#3713)\n rangeSelector.showInput(name);\n rangeSelector[name + 'Input'].focus();\n });\n if (!chart.styledMode) {\n dateBox.attr({\n stroke: options.inputBoxBorderColor,\n 'stroke-width': 1\n });\n }\n dateBox.add(inputGroup);\n // Create the HTML input element. This is rendered as 1x1 pixel then set\n // to the right size when focused.\n const input = createElement('input', {\n name: name,\n className: 'highcharts-range-selector'\n }, void 0, div);\n // #14788: Setting input.type to an unsupported type throws in IE, so\n // we need to use setAttribute instead\n input.setAttribute('type', preferredInputType(options.inputDateFormat || '%e %b %Y'));\n if (!chart.styledMode) {\n // Styles\n label.css(merge(chartStyle, options.labelStyle));\n dateBox.css(merge({\n color: \"#333333\" /* Palette.neutralColor80 */\n }, chartStyle, options.inputStyle));\n css(input, extend({\n position: 'absolute',\n border: 0,\n boxShadow: '0 0 15px rgba(0,0,0,0.3)',\n width: '1px',\n height: '1px',\n padding: 0,\n textAlign: 'center',\n fontSize: chartStyle.fontSize,\n fontFamily: chartStyle.fontFamily,\n top: '-9999em' // #4798\n }, options.inputStyle));\n }\n // Blow up the input box\n input.onfocus = () => {\n rangeSelector.showInput(name);\n };\n // Hide away the input box\n input.onblur = () => {\n // update extermes only when inputs are active\n if (input === H.doc.activeElement) { // Only when focused\n // Update also when no `change` event is triggered, like when\n // clicking inside the SVG (#4710)\n updateExtremes();\n }\n // #10404 - move hide and blur outside focus\n rangeSelector.hideInput(name);\n rangeSelector.setInputValue(name);\n input.blur(); // #4606\n };\n let keyDown = false;\n // handle changes in the input boxes\n input.onchange = () => {\n // Update extremes and blur input when clicking date input calendar\n if (!keyDown) {\n updateExtremes();\n rangeSelector.hideInput(name);\n input.blur();\n }\n };\n input.onkeypress = (event) => {\n // IE does not fire onchange on enter\n if (event.keyCode === 13) {\n updateExtremes();\n }\n };\n input.onkeydown = (event) => {\n keyDown = true;\n // Arrow keys\n if (event.keyCode === 38 || event.keyCode === 40) {\n updateExtremes();\n }\n };\n input.onkeyup = () => {\n keyDown = false;\n };\n return { dateBox, input, label };\n }\n /**\n * Get the position of the range selector buttons and inputs. This can be\n * overridden from outside for custom positioning.\n *\n * @private\n * @function Highcharts.RangeSelector#getPosition\n */\n getPosition() {\n const chart = this.chart, options = chart.options.rangeSelector, top = options.verticalAlign === 'top' ?\n chart.plotTop - chart.axisOffset[0] :\n 0; // set offset only for varticalAlign top\n return {\n buttonTop: top + options.buttonPosition.y,\n inputTop: top + options.inputPosition.y - 10\n };\n }\n /**\n * Get the extremes of YTD. Will choose dataMax if its value is lower than\n * the current timestamp. Will choose dataMin if its value is higher than\n * the timestamp for the start of current year.\n *\n * @private\n * @function Highcharts.RangeSelector#getYTDExtremes\n * @return {*}\n * Returns min and max for the YTD\n */\n getYTDExtremes(dataMax, dataMin, useUTC) {\n const time = this.chart.time, now = new time.Date(dataMax), year = time.get('FullYear', now), startOfYear = useUTC ?\n time.Date.UTC(year, 0, 1) : // eslint-disable-line new-cap\n +new time.Date(year, 0, 1), min = Math.max(dataMin, startOfYear), ts = now.getTime();\n return {\n max: Math.min(dataMax || ts, ts),\n min\n };\n }\n /**\n * Render the range selector including the buttons and the inputs. The first\n * time render is called, the elements are created and positioned. On\n * subsequent calls, they are moved and updated.\n *\n * @private\n * @function Highcharts.RangeSelector#render\n * @param {number} [min]\n * X axis minimum\n * @param {number} [max]\n * X axis maximum\n */\n render(min, max) {\n const chart = this.chart, renderer = chart.renderer, container = chart.container, chartOptions = chart.options, options = chartOptions.rangeSelector, \n // Place inputs above the container\n inputsZIndex = pick(chartOptions.chart.style &&\n chartOptions.chart.style.zIndex, 0) + 1, inputEnabled = options.inputEnabled, rendered = this.rendered;\n if (options.enabled === false) {\n return;\n }\n // create the elements\n if (!rendered) {\n this.group = renderer.g('range-selector-group')\n .attr({\n zIndex: 7\n })\n .add();\n this.div = createElement('div', void 0, {\n position: 'relative',\n height: 0,\n zIndex: inputsZIndex\n });\n if (this.buttonOptions.length) {\n this.renderButtons();\n }\n // First create a wrapper outside the container in order to make\n // the inputs work and make export correct\n if (container.parentNode) {\n container.parentNode.insertBefore(this.div, container);\n }\n if (inputEnabled) {\n // Create the group to keep the inputs\n this.inputGroup = renderer.g('input-group').add(this.group);\n const minElems = this.drawInput('min');\n this.minDateBox = minElems.dateBox;\n this.minLabel = minElems.label;\n this.minInput = minElems.input;\n const maxElems = this.drawInput('max');\n this.maxDateBox = maxElems.dateBox;\n this.maxLabel = maxElems.label;\n this.maxInput = maxElems.input;\n }\n }\n if (inputEnabled) {\n // Set or reset the input values\n this.setInputValue('min', min);\n this.setInputValue('max', max);\n const unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || chart.xAxis[0] || {};\n if (defined(unionExtremes.dataMin) &&\n defined(unionExtremes.dataMax)) {\n const minRange = chart.xAxis[0].minRange || 0;\n this.setInputExtremes('min', unionExtremes.dataMin, Math.min(unionExtremes.dataMax, this.getInputValue('max')) - minRange);\n this.setInputExtremes('max', Math.max(unionExtremes.dataMin, this.getInputValue('min')) + minRange, unionExtremes.dataMax);\n }\n // Reflow\n if (this.inputGroup) {\n let x = 0;\n [\n this.minLabel,\n this.minDateBox,\n this.maxLabel,\n this.maxDateBox\n ].forEach((label) => {\n if (label) {\n const { width } = label.getBBox();\n if (width) {\n label.attr({ x });\n x += width + options.inputSpacing;\n }\n }\n });\n }\n }\n this.alignElements();\n this.rendered = true;\n }\n /**\n * Render the range buttons. This only runs the first time, later the\n * positioning is laid out in alignElements.\n *\n * @private\n * @function Highcharts.RangeSelector#renderButtons\n */\n renderButtons() {\n const { buttons, chart, options } = this;\n const lang = defaultOptions.lang;\n const renderer = chart.renderer;\n const buttonTheme = merge(options.buttonTheme);\n const states = buttonTheme && buttonTheme.states;\n // Prevent the button from resetting the width when the button state\n // changes since we need more control over the width when collapsing\n // the buttons\n const width = buttonTheme.width || 28;\n delete buttonTheme.width;\n delete buttonTheme.states;\n this.buttonGroup = renderer.g('range-selector-buttons').add(this.group);\n const dropdown = this.dropdown = createElement('select', void 0, {\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: 0,\n border: 0,\n top: '-9999em',\n cursor: 'pointer',\n opacity: 0.0001\n }, this.div);\n // Prevent page zoom on iPhone\n addEvent(dropdown, 'touchstart', () => {\n dropdown.style.fontSize = '16px';\n });\n // Forward events from select to button\n [\n [H.isMS ? 'mouseover' : 'mouseenter'],\n [H.isMS ? 'mouseout' : 'mouseleave'],\n ['change', 'click']\n ].forEach(([from, to]) => {\n addEvent(dropdown, from, () => {\n const button = buttons[this.currentButtonIndex()];\n if (button) {\n fireEvent(button.element, to || from);\n }\n });\n });\n this.zoomText = renderer\n .label((lang && lang.rangeSelectorZoom) || '', 0)\n .attr({\n padding: options.buttonTheme.padding,\n height: options.buttonTheme.height,\n paddingLeft: 0,\n paddingRight: 0\n })\n .add(this.buttonGroup);\n if (!this.chart.styledMode) {\n this.zoomText.css(options.labelStyle);\n buttonTheme['stroke-width'] = pick(buttonTheme['stroke-width'], 0);\n }\n createElement('option', {\n textContent: this.zoomText.textStr,\n disabled: true\n }, void 0, dropdown);\n this.buttonOptions.forEach((rangeOptions, i) => {\n createElement('option', {\n textContent: rangeOptions.title || rangeOptions.text\n }, void 0, dropdown);\n buttons[i] = renderer\n .button(rangeOptions.text, 0, 0, (e) => {\n // extract events from button object and call\n const buttonEvents = (rangeOptions.events && rangeOptions.events.click);\n let callDefaultEvent;\n if (buttonEvents) {\n callDefaultEvent =\n buttonEvents.call(rangeOptions, e);\n }\n if (callDefaultEvent !== false) {\n this.clickButton(i);\n }\n this.isActive = true;\n }, buttonTheme, states && states.hover, states && states.select, states && states.disabled)\n .attr({\n 'text-align': 'center',\n width\n })\n .add(this.buttonGroup);\n if (rangeOptions.title) {\n buttons[i].attr('title', rangeOptions.title);\n }\n });\n }\n /**\n * Align the elements horizontally and vertically.\n *\n * @private\n * @function Highcharts.RangeSelector#alignElements\n */\n alignElements() {\n const { buttonGroup, buttons, chart, group, inputGroup, options, zoomText } = this;\n const chartOptions = chart.options;\n const navButtonOptions = (chartOptions.exporting &&\n chartOptions.exporting.enabled !== false &&\n chartOptions.navigation &&\n chartOptions.navigation.buttonOptions);\n const { buttonPosition, inputPosition, verticalAlign } = options;\n // Get the X offset required to avoid overlapping with the exporting\n // button. This is is used both by the buttonGroup and the inputGroup.\n const getXOffsetForExportButton = (group, position) => {\n if (navButtonOptions &&\n this.titleCollision(chart) &&\n verticalAlign === 'top' &&\n position.align === 'right' && ((position.y -\n group.getBBox().height - 12) <\n ((navButtonOptions.y || 0) +\n (navButtonOptions.height || 0) +\n chart.spacing[0]))) {\n return -40;\n }\n return 0;\n };\n let plotLeft = chart.plotLeft;\n if (group && buttonPosition && inputPosition) {\n let translateX = buttonPosition.x - chart.spacing[3];\n if (buttonGroup) {\n this.positionButtons();\n if (!this.initialButtonGroupWidth) {\n let width = 0;\n if (zoomText) {\n width += zoomText.getBBox().width + 5;\n }\n buttons.forEach((button, i) => {\n width += button.width || 0;\n if (i !== buttons.length - 1) {\n width += options.buttonSpacing;\n }\n });\n this.initialButtonGroupWidth = width;\n }\n plotLeft -= chart.spacing[3];\n this.updateButtonStates();\n // Detect collision between button group and exporting\n const xOffsetForExportButton = getXOffsetForExportButton(buttonGroup, buttonPosition);\n this.alignButtonGroup(xOffsetForExportButton);\n // Skip animation\n group.placed = buttonGroup.placed = chart.hasLoaded;\n }\n let xOffsetForExportButton = 0;\n if (inputGroup) {\n // Detect collision between the input group and exporting button\n xOffsetForExportButton = getXOffsetForExportButton(inputGroup, inputPosition);\n if (inputPosition.align === 'left') {\n translateX = plotLeft;\n }\n else if (inputPosition.align === 'right') {\n translateX = -Math.max(chart.axisOffset[1], -xOffsetForExportButton);\n }\n // Update the alignment to the updated spacing box\n inputGroup.align({\n y: inputPosition.y,\n width: inputGroup.getBBox().width,\n align: inputPosition.align,\n // fix wrong getBBox() value on right align\n x: inputPosition.x + translateX - 2\n }, true, chart.spacingBox);\n // Skip animation\n inputGroup.placed = chart.hasLoaded;\n }\n this.handleCollision(xOffsetForExportButton);\n // Vertical align\n group.align({\n verticalAlign\n }, true, chart.spacingBox);\n const alignTranslateY = group.alignAttr.translateY;\n // Set position\n let groupHeight = group.getBBox().height + 20; // # 20 padding\n let translateY = 0;\n // Calculate bottom position\n if (verticalAlign === 'bottom') {\n const legendOptions = chart.legend && chart.legend.options;\n const legendHeight = (legendOptions &&\n legendOptions.verticalAlign === 'bottom' &&\n legendOptions.enabled &&\n !legendOptions.floating ?\n (chart.legend.legendHeight +\n pick(legendOptions.margin, 10)) :\n 0);\n groupHeight = groupHeight + legendHeight - 20;\n translateY = (alignTranslateY -\n groupHeight -\n (options.floating ? 0 : options.y) -\n (chart.titleOffset ? chart.titleOffset[2] : 0) -\n 10 // 10 spacing\n );\n }\n if (verticalAlign === 'top') {\n if (options.floating) {\n translateY = 0;\n }\n if (chart.titleOffset && chart.titleOffset[0]) {\n translateY = chart.titleOffset[0];\n }\n translateY += ((chart.margin[0] - chart.spacing[0]) || 0);\n }\n else if (verticalAlign === 'middle') {\n if (inputPosition.y === buttonPosition.y) {\n translateY = alignTranslateY;\n }\n else if (inputPosition.y || buttonPosition.y) {\n if (inputPosition.y < 0 ||\n buttonPosition.y < 0) {\n translateY -= Math.min(inputPosition.y, buttonPosition.y);\n }\n else {\n translateY = alignTranslateY - groupHeight;\n }\n }\n }\n group.translate(options.x, options.y + Math.floor(translateY));\n // Translate HTML inputs\n const { minInput, maxInput, dropdown } = this;\n if (options.inputEnabled && minInput && maxInput) {\n minInput.style.marginTop = group.translateY + 'px';\n maxInput.style.marginTop = group.translateY + 'px';\n }\n if (dropdown) {\n dropdown.style.marginTop = group.translateY + 'px';\n }\n }\n }\n /**\n * Align the button group horizontally and vertically.\n *\n * @private\n * @function Highcharts.RangeSelector#alignButtonGroup\n * @param {number} xOffsetForExportButton\n * @param {number} [width]\n */\n alignButtonGroup(xOffsetForExportButton, width) {\n const { chart, options, buttonGroup, buttons } = this;\n const { buttonPosition } = options;\n const plotLeft = chart.plotLeft - chart.spacing[3];\n let translateX = buttonPosition.x - chart.spacing[3];\n if (buttonPosition.align === 'right') {\n translateX += xOffsetForExportButton - plotLeft; // #13014\n }\n else if (buttonPosition.align === 'center') {\n translateX -= plotLeft / 2;\n }\n if (buttonGroup) {\n // Align button group\n buttonGroup.align({\n y: buttonPosition.y,\n width: pick(width, this.initialButtonGroupWidth),\n align: buttonPosition.align,\n x: translateX\n }, true, chart.spacingBox);\n }\n }\n /**\n * @private\n * @function Highcharts.RangeSelector#positionButtons\n */\n positionButtons() {\n const { buttons, chart, options, zoomText } = this;\n const verb = chart.hasLoaded ? 'animate' : 'attr';\n const { buttonPosition } = options;\n const plotLeft = chart.plotLeft;\n let buttonLeft = plotLeft;\n if (zoomText && zoomText.visibility !== 'hidden') {\n // #8769, allow dynamically updating margins\n zoomText[verb]({\n x: pick(plotLeft + buttonPosition.x, plotLeft)\n });\n // Button start position\n buttonLeft += buttonPosition.x +\n zoomText.getBBox().width + 5;\n }\n for (let i = 0, iEnd = this.buttonOptions.length; i < iEnd; ++i) {\n if (buttons[i].visibility !== 'hidden') {\n buttons[i][verb]({ x: buttonLeft });\n // Increase the button position for the next button\n buttonLeft += (buttons[i].width || 0) + options.buttonSpacing;\n }\n else {\n buttons[i][verb]({ x: plotLeft });\n }\n }\n }\n /**\n * Handle collision between the button group and the input group\n *\n * @private\n * @function Highcharts.RangeSelector#handleCollision\n *\n * @param {number} xOffsetForExportButton\n * The X offset of the group required to make room for the\n * exporting button\n */\n handleCollision(xOffsetForExportButton) {\n const { chart, buttonGroup, inputGroup } = this;\n const { buttonPosition, dropdown, inputPosition } = this.options;\n const maxButtonWidth = () => {\n let buttonWidth = 0;\n this.buttons.forEach((button) => {\n const bBox = button.getBBox();\n if (bBox.width > buttonWidth) {\n buttonWidth = bBox.width;\n }\n });\n return buttonWidth;\n };\n const groupsOverlap = (buttonGroupWidth) => {\n if (inputGroup && buttonGroup) {\n const inputGroupX = (inputGroup.alignAttr.translateX +\n inputGroup.alignOptions.x -\n xOffsetForExportButton +\n // getBBox for detecing left margin\n inputGroup.getBBox().x +\n // 2px padding to not overlap input and label\n 2);\n const inputGroupWidth = inputGroup.alignOptions.width;\n const buttonGroupX = buttonGroup.alignAttr.translateX +\n buttonGroup.getBBox().x;\n return (buttonGroupX + buttonGroupWidth > inputGroupX) &&\n (inputGroupX + inputGroupWidth > buttonGroupX) &&\n (buttonPosition.y <\n (inputPosition.y +\n inputGroup.getBBox().height));\n }\n return false;\n };\n const moveInputsDown = () => {\n if (inputGroup && buttonGroup) {\n inputGroup.attr({\n translateX: inputGroup.alignAttr.translateX + (chart.axisOffset[1] >= -xOffsetForExportButton ?\n 0 :\n -xOffsetForExportButton),\n translateY: inputGroup.alignAttr.translateY +\n buttonGroup.getBBox().height + 10\n });\n }\n };\n if (buttonGroup) {\n if (dropdown === 'always') {\n this.collapseButtons(xOffsetForExportButton);\n if (groupsOverlap(maxButtonWidth())) {\n // Move the inputs down if there is still a collision\n // after collapsing the buttons\n moveInputsDown();\n }\n return;\n }\n if (dropdown === 'never') {\n this.expandButtons();\n }\n }\n // Detect collision\n if (inputGroup && buttonGroup) {\n if ((inputPosition.align === buttonPosition.align) ||\n // 20 is minimal spacing between elements\n groupsOverlap(this.initialButtonGroupWidth + 20)) {\n if (dropdown === 'responsive') {\n this.collapseButtons(xOffsetForExportButton);\n if (groupsOverlap(maxButtonWidth())) {\n moveInputsDown();\n }\n }\n else {\n moveInputsDown();\n }\n }\n else if (dropdown === 'responsive') {\n this.expandButtons();\n }\n }\n else if (buttonGroup && dropdown === 'responsive') {\n if (this.initialButtonGroupWidth > chart.plotWidth) {\n this.collapseButtons(xOffsetForExportButton);\n }\n else {\n this.expandButtons();\n }\n }\n }\n /**\n * Collapse the buttons and put the select element on top.\n *\n * @private\n * @function Highcharts.RangeSelector#collapseButtons\n * @param {number} xOffsetForExportButton\n */\n collapseButtons(xOffsetForExportButton) {\n const { buttons, buttonOptions, chart, dropdown, options, zoomText } = this;\n const userButtonTheme = (chart.userOptions.rangeSelector &&\n chart.userOptions.rangeSelector.buttonTheme) || {};\n const getAttribs = (text) => ({\n text: text ? `${text} ▾` : '▾',\n width: 'auto',\n paddingLeft: pick(options.buttonTheme.paddingLeft, userButtonTheme.padding, 8),\n paddingRight: pick(options.buttonTheme.paddingRight, userButtonTheme.padding, 8)\n });\n if (zoomText) {\n zoomText.hide();\n }\n let hasActiveButton = false;\n buttonOptions.forEach((rangeOptions, i) => {\n const button = buttons[i];\n if (button.state !== 2) {\n button.hide();\n }\n else {\n button.show();\n button.attr(getAttribs(rangeOptions.text));\n hasActiveButton = true;\n }\n });\n if (!hasActiveButton) {\n if (dropdown) {\n dropdown.selectedIndex = 0;\n }\n buttons[0].show();\n buttons[0].attr(getAttribs(this.zoomText && this.zoomText.textStr));\n }\n const { align } = options.buttonPosition;\n this.positionButtons();\n if (align === 'right' || align === 'center') {\n this.alignButtonGroup(xOffsetForExportButton, buttons[this.currentButtonIndex()].getBBox().width);\n }\n this.showDropdown();\n }\n /**\n * Show all the buttons and hide the select element.\n *\n * @private\n * @function Highcharts.RangeSelector#expandButtons\n */\n expandButtons() {\n const { buttons, buttonOptions, options, zoomText } = this;\n this.hideDropdown();\n if (zoomText) {\n zoomText.show();\n }\n buttonOptions.forEach((rangeOptions, i) => {\n const button = buttons[i];\n button.show();\n button.attr({\n text: rangeOptions.text,\n width: options.buttonTheme.width || 28,\n paddingLeft: pick(options.buttonTheme.paddingLeft, 'unset'),\n paddingRight: pick(options.buttonTheme.paddingRight, 'unset')\n });\n if (button.state < 2) {\n button.setState(0);\n }\n });\n this.positionButtons();\n }\n /**\n * Get the index of the visible button when the buttons are collapsed.\n *\n * @private\n * @function Highcharts.RangeSelector#currentButtonIndex\n */\n currentButtonIndex() {\n const { dropdown } = this;\n if (dropdown && dropdown.selectedIndex > 0) {\n return dropdown.selectedIndex - 1;\n }\n return 0;\n }\n /**\n * Position the select element on top of the button.\n *\n * @private\n * @function Highcharts.RangeSelector#showDropdown\n */\n showDropdown() {\n const { buttonGroup, buttons, chart, dropdown } = this;\n if (buttonGroup && dropdown) {\n const { translateX = 0, translateY = 0 } = buttonGroup, bBox = buttons[this.currentButtonIndex()].getBBox();\n css(dropdown, {\n left: (chart.plotLeft + translateX) + 'px',\n top: (translateY + 0.5) + 'px',\n width: bBox.width + 'px',\n height: bBox.height + 'px'\n });\n this.hasVisibleDropdown = true;\n }\n }\n /**\n * @private\n * @function Highcharts.RangeSelector#hideDropdown\n */\n hideDropdown() {\n const { dropdown } = this;\n if (dropdown) {\n css(dropdown, {\n top: '-9999em',\n width: '1px',\n height: '1px'\n });\n this.hasVisibleDropdown = false;\n }\n }\n /**\n * Extracts height of range selector\n *\n * @private\n * @function Highcharts.RangeSelector#getHeight\n * @return {number}\n * Returns rangeSelector height\n */\n getHeight() {\n const rangeSelector = this, options = rangeSelector.options, rangeSelectorGroup = rangeSelector.group, inputPosition = options.inputPosition, buttonPosition = options.buttonPosition, yPosition = options.y, buttonPositionY = buttonPosition.y, inputPositionY = inputPosition.y;\n let rangeSelectorHeight = 0;\n if (options.height) {\n return options.height;\n }\n // Align the elements before we read the height in case we're switching\n // between wrapped and non-wrapped layout\n this.alignElements();\n rangeSelectorHeight = rangeSelectorGroup ?\n // 13px to keep back compatibility\n (rangeSelectorGroup.getBBox(true).height) + 13 +\n yPosition :\n 0;\n const minPosition = Math.min(inputPositionY, buttonPositionY);\n if ((inputPositionY < 0 && buttonPositionY < 0) ||\n (inputPositionY > 0 && buttonPositionY > 0)) {\n rangeSelectorHeight += Math.abs(minPosition);\n }\n return rangeSelectorHeight;\n }\n /**\n * Detect collision with title or subtitle\n *\n * @private\n * @function Highcharts.RangeSelector#titleCollision\n * @return {boolean}\n * Returns collision status\n */\n titleCollision(chart) {\n return !(chart.options.title.text ||\n chart.options.subtitle.text);\n }\n /**\n * Update the range selector with new options\n *\n * @private\n * @function Highcharts.RangeSelector#update\n * @param {Highcharts.RangeSelectorOptions} options\n */\n update(options) {\n const chart = this.chart;\n merge(true, chart.options.rangeSelector, options);\n this.destroy();\n this.init(chart);\n this.render();\n }\n /**\n * Destroys allocated elements.\n *\n * @private\n * @function Highcharts.RangeSelector#destroy\n */\n destroy() {\n const rSelector = this, minInput = rSelector.minInput, maxInput = rSelector.maxInput;\n if (rSelector.eventsToUnbind) {\n rSelector.eventsToUnbind.forEach((unbind) => unbind());\n rSelector.eventsToUnbind = void 0;\n }\n // Destroy elements in collections\n destroyObjectProperties(rSelector.buttons);\n // Clear input element events\n if (minInput) {\n minInput.onfocus = minInput.onblur = minInput.onchange = null;\n }\n if (maxInput) {\n maxInput.onfocus = maxInput.onblur = maxInput.onchange = null;\n }\n // Destroy HTML and SVG elements\n objectEach(rSelector, function (val, key) {\n if (val && key !== 'chart') {\n if (val instanceof SVGElement) {\n // SVGElement\n val.destroy();\n }\n else if (val instanceof window.HTMLElement) {\n // HTML element\n discardElement(val);\n }\n }\n if (val !== RangeSelector.prototype[key]) {\n rSelector[key] = null;\n }\n }, this);\n }\n }\n extend(RangeSelector.prototype, {\n /**\n * The default buttons for pre-selecting time frames.\n * @private\n */\n defaultButtons: [{\n type: 'month',\n count: 1,\n text: '1m',\n title: 'View 1 month'\n }, {\n type: 'month',\n count: 3,\n text: '3m',\n title: 'View 3 months'\n }, {\n type: 'month',\n count: 6,\n text: '6m',\n title: 'View 6 months'\n }, {\n type: 'ytd',\n text: 'YTD',\n title: 'View year to date'\n }, {\n type: 'year',\n count: 1,\n text: '1y',\n title: 'View 1 year'\n }, {\n type: 'all',\n text: 'All',\n title: 'View all'\n }],\n /**\n * The date formats to use when setting min, max and value on date inputs.\n * @private\n */\n inputTypeFormats: {\n 'datetime-local': '%Y-%m-%dT%H:%M:%S',\n 'date': '%Y-%m-%d',\n 'time': '%H:%M:%S'\n }\n });\n /* *\n *\n * Default Export\n *\n * */\n /* *\n *\n * API Options\n *\n * */\n /**\n * Define the time span for the button\n *\n * @typedef {\"all\"|\"day\"|\"hour\"|\"millisecond\"|\"minute\"|\"month\"|\"second\"|\"week\"|\"year\"|\"ytd\"} Highcharts.RangeSelectorButtonTypeValue\n */\n /**\n * Callback function to react on button clicks.\n *\n * @callback Highcharts.RangeSelectorClickCallbackFunction\n *\n * @param {global.Event} e\n * Event arguments.\n *\n * @param {boolean|undefined}\n * Return false to cancel the default button event.\n */\n /**\n * Callback function to parse values entered in the input boxes and return a\n * valid JavaScript time as milliseconds since 1970.\n *\n * @callback Highcharts.RangeSelectorParseCallbackFunction\n *\n * @param {string} value\n * Input value to parse.\n *\n * @return {number}\n * Parsed JavaScript time value.\n */\n (''); // keeps doclets above in JS file\n\n return RangeSelector;\n });\n _registerModule(_modules, 'Accessibility/Components/RangeSelectorComponent.js', [_modules['Stock/RangeSelector/RangeSelector.js'], _modules['Accessibility/AccessibilityComponent.js'], _modules['Accessibility/Utils/ChartUtilities.js'], _modules['Accessibility/Utils/Announcer.js'], _modules['Accessibility/KeyboardNavigationHandler.js'], _modules['Core/Utilities.js']], function (RangeSelector, AccessibilityComponent, ChartUtilities, Announcer, KeyboardNavigationHandler, U) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Accessibility component for the range selector.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { unhideChartElementFromAT, getAxisRangeDescription } = ChartUtilities;\n const { addEvent, attr } = U;\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Do we want date input navigation\n * @private\n */\n function shouldRunInputNavigation(chart) {\n return Boolean(chart.rangeSelector &&\n chart.rangeSelector.inputGroup &&\n chart.rangeSelector.inputGroup.element.style.visibility !== 'hidden' &&\n chart.options.rangeSelector.inputEnabled !== false &&\n chart.rangeSelector.minInput &&\n chart.rangeSelector.maxInput);\n }\n /* *\n *\n * Class\n *\n * */\n /**\n * The RangeSelectorComponent class\n *\n * @private\n * @class\n * @name Highcharts.RangeSelectorComponent\n */\n class RangeSelectorComponent extends AccessibilityComponent {\n constructor() {\n /* *\n *\n * Properties\n *\n * */\n super(...arguments);\n this.announcer = void 0;\n }\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Init the component\n * @private\n */\n init() {\n const chart = this.chart;\n this.announcer = new Announcer(chart, 'polite');\n }\n /**\n * Called on first render/updates to the chart, including options changes.\n */\n onChartUpdate() {\n const chart = this.chart, component = this, rangeSelector = chart.rangeSelector;\n if (!rangeSelector) {\n return;\n }\n this.updateSelectorVisibility();\n this.setDropdownAttrs();\n if (rangeSelector.buttons &&\n rangeSelector.buttons.length) {\n rangeSelector.buttons.forEach((button) => {\n component.setRangeButtonAttrs(button);\n });\n }\n // Make sure input boxes are accessible and focusable\n if (rangeSelector.maxInput && rangeSelector.minInput) {\n ['minInput', 'maxInput'].forEach(function (key, i) {\n const input = rangeSelector[key];\n if (input) {\n unhideChartElementFromAT(chart, input);\n component.setRangeInputAttrs(input, 'accessibility.rangeSelector.' + (i ? 'max' : 'min') +\n 'InputLabel');\n }\n });\n }\n }\n /**\n * Hide buttons from AT when showing dropdown, and vice versa.\n * @private\n */\n updateSelectorVisibility() {\n const chart = this.chart;\n const rangeSelector = chart.rangeSelector;\n const dropdown = (rangeSelector &&\n rangeSelector.dropdown);\n const buttons = (rangeSelector &&\n rangeSelector.buttons ||\n []);\n const hideFromAT = (el) => el.setAttribute('aria-hidden', true);\n if (rangeSelector &&\n rangeSelector.hasVisibleDropdown &&\n dropdown) {\n unhideChartElementFromAT(chart, dropdown);\n buttons.forEach((btn) => hideFromAT(btn.element));\n }\n else {\n if (dropdown) {\n hideFromAT(dropdown);\n }\n buttons.forEach((btn) => unhideChartElementFromAT(chart, btn.element));\n }\n }\n /**\n * Set accessibility related attributes on dropdown element.\n * @private\n */\n setDropdownAttrs() {\n const chart = this.chart;\n const dropdown = (chart.rangeSelector &&\n chart.rangeSelector.dropdown);\n if (dropdown) {\n const label = chart.langFormat('accessibility.rangeSelector.dropdownLabel', { rangeTitle: chart.options.lang.rangeSelectorZoom });\n dropdown.setAttribute('aria-label', label);\n dropdown.setAttribute('tabindex', -1);\n }\n }\n /**\n * Set attrs for a range button\n * @private\n */\n setRangeButtonAttrs(button) {\n attr(button.element, {\n tabindex: -1,\n role: 'button'\n });\n }\n /**\n * Set attrs for a date input\n * @private\n */\n setRangeInputAttrs(input, langKey) {\n const chart = this.chart;\n attr(input, {\n tabindex: -1,\n 'aria-label': chart.langFormat(langKey, { chart: chart })\n });\n }\n /**\n * Handle arrow key nav\n * @private\n */\n onButtonNavKbdArrowKey(keyboardNavigationHandler, keyCode) {\n const response = keyboardNavigationHandler.response, keys = this.keyCodes, chart = this.chart, wrapAround = chart.options.accessibility\n .keyboardNavigation.wrapAround, direction = (keyCode === keys.left || keyCode === keys.up) ? -1 : 1, didHighlight = chart.highlightRangeSelectorButton(chart.highlightedRangeSelectorItemIx + direction);\n if (!didHighlight) {\n if (wrapAround) {\n keyboardNavigationHandler.init(direction);\n return response.success;\n }\n return response[direction > 0 ? 'next' : 'prev'];\n }\n return response.success;\n }\n /**\n * Handle keyboard click\n * @private\n */\n onButtonNavKbdClick(keyboardNavigationHandler) {\n const response = keyboardNavigationHandler.response, chart = this.chart, wasDisabled = chart.oldRangeSelectorItemState === 3;\n if (!wasDisabled) {\n this.fakeClickEvent(chart.rangeSelector.buttons[chart.highlightedRangeSelectorItemIx].element);\n }\n return response.success;\n }\n /**\n * Called whenever a range selector button has been clicked, either by\n * mouse, touch, or kbd/voice/other.\n * @private\n */\n onAfterBtnClick() {\n const chart = this.chart;\n const axisRangeDescription = getAxisRangeDescription(chart.xAxis[0]);\n const announcement = chart.langFormat('accessibility.rangeSelector.clickButtonAnnouncement', { chart, axisRangeDescription });\n if (announcement) {\n this.announcer.announce(announcement);\n }\n }\n /**\n * Handle move between input elements with Tab key\n * @private\n */\n onInputKbdMove(direction) {\n const chart = this.chart;\n const rangeSel = chart.rangeSelector;\n const newIx = chart.highlightedInputRangeIx = (chart.highlightedInputRangeIx || 0) + direction;\n const newIxOutOfRange = newIx > 1 || newIx < 0;\n if (newIxOutOfRange) {\n if (chart.accessibility) {\n // Ignore focus\n chart.accessibility.keyboardNavigation.exiting = true;\n chart.accessibility.keyboardNavigation.tabindexContainer\n .focus();\n return chart.accessibility.keyboardNavigation.move(direction);\n }\n }\n else if (rangeSel) {\n const svgEl = rangeSel[newIx ? 'maxDateBox' : 'minDateBox'];\n const inputEl = rangeSel[newIx ? 'maxInput' : 'minInput'];\n if (svgEl && inputEl) {\n chart.setFocusToElement(svgEl, inputEl);\n }\n }\n return true;\n }\n /**\n * Init date input navigation\n * @private\n */\n onInputNavInit(direction) {\n const component = this;\n const chart = this.chart;\n const buttonIxToHighlight = direction > 0 ? 0 : 1;\n const rangeSel = chart.rangeSelector;\n const svgEl = (rangeSel &&\n rangeSel[buttonIxToHighlight ? 'maxDateBox' : 'minDateBox']);\n const minInput = (rangeSel && rangeSel.minInput);\n const maxInput = (rangeSel && rangeSel.maxInput);\n const inputEl = buttonIxToHighlight ? maxInput : minInput;\n chart.highlightedInputRangeIx = buttonIxToHighlight;\n if (svgEl && minInput && maxInput) {\n chart.setFocusToElement(svgEl, inputEl);\n // Tab-press with the input focused does not propagate to chart\n // automatically, so we manually catch and handle it when relevant.\n if (this.removeInputKeydownHandler) {\n this.removeInputKeydownHandler();\n }\n const keydownHandler = (e) => {\n const isTab = (e.which || e.keyCode) === this.keyCodes.tab;\n if (isTab &&\n component.onInputKbdMove(e.shiftKey ? -1 : 1)) {\n e.preventDefault();\n e.stopPropagation();\n }\n };\n const minRemover = addEvent(minInput, 'keydown', keydownHandler);\n const maxRemover = addEvent(maxInput, 'keydown', keydownHandler);\n this.removeInputKeydownHandler = () => {\n minRemover();\n maxRemover();\n };\n }\n }\n /**\n * Terminate date input nav\n * @private\n */\n onInputNavTerminate() {\n const rangeSel = (this.chart.rangeSelector || {});\n if (rangeSel.maxInput) {\n rangeSel.hideInput('max');\n }\n if (rangeSel.minInput) {\n rangeSel.hideInput('min');\n }\n if (this.removeInputKeydownHandler) {\n this.removeInputKeydownHandler();\n delete this.removeInputKeydownHandler;\n }\n }\n /**\n * Init range selector dropdown nav\n * @private\n */\n initDropdownNav() {\n const chart = this.chart;\n const rangeSelector = chart.rangeSelector;\n const dropdown = (rangeSelector && rangeSelector.dropdown);\n if (rangeSelector && dropdown) {\n chart.setFocusToElement(rangeSelector.buttonGroup, dropdown);\n if (this.removeDropdownKeydownHandler) {\n this.removeDropdownKeydownHandler();\n }\n // Tab-press with dropdown focused does not propagate to chart\n // automatically, so we manually catch and handle it when relevant.\n this.removeDropdownKeydownHandler = addEvent(dropdown, 'keydown', (e) => {\n const isTab = (e.which || e.keyCode) === this.keyCodes.tab, a11y = chart.accessibility;\n if (isTab) {\n e.preventDefault();\n e.stopPropagation();\n if (a11y) {\n a11y.keyboardNavigation.tabindexContainer.focus();\n a11y.keyboardNavigation.move(e.shiftKey ? -1 : 1);\n }\n }\n });\n }\n }\n /**\n * Get navigation for the range selector buttons.\n * @private\n * @return {Highcharts.KeyboardNavigationHandler} The module object.\n */\n getRangeSelectorButtonNavigation() {\n const chart = this.chart;\n const keys = this.keyCodes;\n const component = this;\n return new KeyboardNavigationHandler(chart, {\n keyCodeMap: [\n [\n [keys.left, keys.right, keys.up, keys.down],\n function (keyCode) {\n return component.onButtonNavKbdArrowKey(this, keyCode);\n }\n ],\n [\n [keys.enter, keys.space],\n function () {\n return component.onButtonNavKbdClick(this);\n }\n ]\n ],\n validate: function () {\n return !!(chart.rangeSelector &&\n chart.rangeSelector.buttons &&\n chart.rangeSelector.buttons.length);\n },\n init: function (direction) {\n const rangeSelector = chart.rangeSelector;\n if (rangeSelector && rangeSelector.hasVisibleDropdown) {\n component.initDropdownNav();\n }\n else if (rangeSelector) {\n const lastButtonIx = rangeSelector.buttons.length - 1;\n chart.highlightRangeSelectorButton(direction > 0 ? 0 : lastButtonIx);\n }\n },\n terminate: function () {\n if (component.removeDropdownKeydownHandler) {\n component.removeDropdownKeydownHandler();\n delete component.removeDropdownKeydownHandler;\n }\n }\n });\n }\n /**\n * Get navigation for the range selector input boxes.\n * @private\n * @return {Highcharts.KeyboardNavigationHandler}\n * The module object.\n */\n getRangeSelectorInputNavigation() {\n const chart = this.chart;\n const component = this;\n return new KeyboardNavigationHandler(chart, {\n keyCodeMap: [],\n validate: function () {\n return shouldRunInputNavigation(chart);\n },\n init: function (direction) {\n component.onInputNavInit(direction);\n },\n terminate: function () {\n component.onInputNavTerminate();\n }\n });\n }\n /**\n * Get keyboard navigation handlers for this component.\n * @return {Array}\n * List of module objects.\n */\n getKeyboardNavigation() {\n return [\n this.getRangeSelectorButtonNavigation(),\n this.getRangeSelectorInputNavigation()\n ];\n }\n /**\n * Remove component traces\n */\n destroy() {\n if (this.removeDropdownKeydownHandler) {\n this.removeDropdownKeydownHandler();\n }\n if (this.removeInputKeydownHandler) {\n this.removeInputKeydownHandler();\n }\n if (this.announcer) {\n this.announcer.destroy();\n }\n }\n }\n /* *\n *\n * Class Namespace\n *\n * */\n (function (RangeSelectorComponent) {\n /* *\n *\n * Declarations\n *\n * */\n /* *\n *\n * Constants\n *\n * */\n const composedMembers = [];\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Highlight range selector button by index.\n *\n * @private\n * @function Highcharts.Chart#highlightRangeSelectorButton\n */\n function chartHighlightRangeSelectorButton(ix) {\n const buttons = (this.rangeSelector &&\n this.rangeSelector.buttons ||\n []);\n const curHighlightedIx = this.highlightedRangeSelectorItemIx;\n const curSelectedIx = (this.rangeSelector &&\n this.rangeSelector.selected);\n // Deselect old\n if (typeof curHighlightedIx !== 'undefined' &&\n buttons[curHighlightedIx] &&\n curHighlightedIx !== curSelectedIx) {\n buttons[curHighlightedIx].setState(this.oldRangeSelectorItemState || 0);\n }\n // Select new\n this.highlightedRangeSelectorItemIx = ix;\n if (buttons[ix]) {\n this.setFocusToElement(buttons[ix].box, buttons[ix].element);\n if (ix !== curSelectedIx) {\n this.oldRangeSelectorItemState = buttons[ix].state;\n buttons[ix].setState(1);\n }\n return true;\n }\n return false;\n }\n /**\n * Build compositions\n * @private\n */\n function compose(ChartClass, RangeSelectorClass) {\n if (U.pushUnique(composedMembers, ChartClass)) {\n const chartProto = ChartClass.prototype;\n chartProto.highlightRangeSelectorButton = (chartHighlightRangeSelectorButton);\n }\n if (U.pushUnique(composedMembers, RangeSelectorClass)) {\n addEvent(RangeSelector, 'afterBtnClick', rangeSelectorAfterBtnClick);\n }\n }\n RangeSelectorComponent.compose = compose;\n /**\n * Range selector does not have destroy-setup for class instance events - so\n * we set it on the class and call the component from here.\n * @private\n */\n function rangeSelectorAfterBtnClick() {\n const a11y = this.chart.accessibility;\n if (a11y && a11y.components.rangeSelector) {\n return a11y.components.rangeSelector.onAfterBtnClick();\n }\n }\n })(RangeSelectorComponent || (RangeSelectorComponent = {}));\n /* *\n *\n * Export Default\n *\n * */\n\n return RangeSelectorComponent;\n });\n _registerModule(_modules, 'Accessibility/Components/SeriesComponent/ForcedMarkers.js', [_modules['Core/Utilities.js']], function (U) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Handle forcing series markers.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { addEvent, merge } = U;\n /* *\n *\n * Composition\n *\n * */\n var ForcedMarkersComposition;\n (function (ForcedMarkersComposition) {\n /* *\n *\n * Declarations\n *\n * */\n /* *\n *\n * Compositions\n *\n * */\n const composedMembers = [];\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * @private\n */\n function compose(SeriesClass) {\n if (U.pushUnique(composedMembers, SeriesClass)) {\n addEvent(SeriesClass, 'afterSetOptions', seriesOnAfterSetOptions);\n addEvent(SeriesClass, 'render', seriesOnRender);\n addEvent(SeriesClass, 'afterRender', seriesOnAfterRender);\n }\n }\n ForcedMarkersComposition.compose = compose;\n /**\n * @private\n */\n function forceZeroOpacityMarkerOptions(options) {\n merge(true, options, {\n marker: {\n enabled: true,\n states: {\n normal: {\n opacity: 0\n }\n }\n }\n });\n }\n /**\n * @private\n */\n function getPointMarkerOpacity(pointOptions) {\n return pointOptions.marker.states &&\n pointOptions.marker.states.normal &&\n pointOptions.marker.states.normal.opacity;\n }\n /**\n * @private\n */\n function handleForcePointMarkers(series) {\n let i = series.points.length;\n while (i--) {\n const point = series.points[i];\n const pointOptions = point.options;\n const hadForcedMarker = point.hasForcedA11yMarker;\n delete point.hasForcedA11yMarker;\n if (pointOptions.marker) {\n const isStillForcedMarker = hadForcedMarker &&\n getPointMarkerOpacity(pointOptions) === 0;\n if (pointOptions.marker.enabled && !isStillForcedMarker) {\n unforcePointMarkerOptions(pointOptions);\n point.hasForcedA11yMarker = false;\n }\n else if (pointOptions.marker.enabled === false) {\n forceZeroOpacityMarkerOptions(pointOptions);\n point.hasForcedA11yMarker = true;\n }\n }\n }\n }\n /**\n * @private\n */\n function hasIndividualPointMarkerOptions(series) {\n return !!(series._hasPointMarkers &&\n series.points &&\n series.points.length);\n }\n /**\n * @private\n */\n function isWithinDescriptionThreshold(series) {\n const a11yOptions = series.chart.options.accessibility;\n return series.points.length <\n a11yOptions.series.pointDescriptionEnabledThreshold ||\n a11yOptions.series\n .pointDescriptionEnabledThreshold === false;\n }\n /**\n * Process marker graphics after render\n * @private\n */\n function seriesOnAfterRender() {\n const series = this;\n // For styled mode the rendered graphic does not reflect the style\n // options, and we need to add/remove classes to achieve the same.\n if (series.chart.styledMode) {\n if (series.markerGroup) {\n series.markerGroup[series.a11yMarkersForced ? 'addClass' : 'removeClass']('highcharts-a11y-markers-hidden');\n }\n // Do we need to handle individual points?\n if (hasIndividualPointMarkerOptions(series)) {\n series.points.forEach((point) => {\n if (point.graphic) {\n point.graphic[point.hasForcedA11yMarker ?\n 'addClass' : 'removeClass']('highcharts-a11y-marker-hidden');\n point.graphic[point.hasForcedA11yMarker === false ?\n 'addClass' :\n 'removeClass']('highcharts-a11y-marker-visible');\n }\n });\n }\n }\n }\n /**\n * Keep track of options to reset markers to if no longer forced.\n * @private\n */\n function seriesOnAfterSetOptions(e) {\n this.resetA11yMarkerOptions = merge(e.options.marker || {}, this.userOptions.marker || {});\n }\n /**\n * Keep track of forcing markers.\n * @private\n */\n function seriesOnRender() {\n const series = this, options = series.options;\n if (shouldForceMarkers(series)) {\n if (options.marker && options.marker.enabled === false) {\n series.a11yMarkersForced = true;\n forceZeroOpacityMarkerOptions(series.options);\n }\n if (hasIndividualPointMarkerOptions(series)) {\n handleForcePointMarkers(series);\n }\n }\n else if (series.a11yMarkersForced) {\n delete series.a11yMarkersForced;\n unforceSeriesMarkerOptions(series);\n delete series.resetA11yMarkerOptions;\n }\n }\n /**\n * @private\n */\n function shouldForceMarkers(series) {\n const chart = series.chart, chartA11yEnabled = chart.options.accessibility.enabled, seriesA11yEnabled = (series.options.accessibility &&\n series.options.accessibility.enabled) !== false;\n return (chartA11yEnabled &&\n seriesA11yEnabled &&\n isWithinDescriptionThreshold(series));\n }\n /**\n * @private\n */\n function unforcePointMarkerOptions(pointOptions) {\n merge(true, pointOptions.marker, {\n states: {\n normal: {\n opacity: getPointMarkerOpacity(pointOptions) || 1\n }\n }\n });\n }\n /**\n * Reset markers to normal\n * @private\n */\n function unforceSeriesMarkerOptions(series) {\n const resetMarkerOptions = series.resetA11yMarkerOptions;\n if (resetMarkerOptions) {\n const originalOpactiy = resetMarkerOptions.states &&\n resetMarkerOptions.states.normal &&\n resetMarkerOptions.states.normal.opacity;\n // Temporarily set the old marker options to enabled in order to\n // trigger destruction of the markers in Series.update.\n if (series.userOptions && series.userOptions.marker) {\n series.userOptions.marker.enabled = true;\n }\n series.update({\n marker: {\n enabled: resetMarkerOptions.enabled,\n states: {\n normal: { opacity: originalOpactiy }\n }\n }\n });\n }\n }\n })(ForcedMarkersComposition || (ForcedMarkersComposition = {}));\n /* *\n *\n * Default Export\n *\n * */\n\n return ForcedMarkersComposition;\n });\n _registerModule(_modules, 'Accessibility/Components/SeriesComponent/SeriesKeyboardNavigation.js', [_modules['Core/Series/Point.js'], _modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js'], _modules['Accessibility/KeyboardNavigationHandler.js'], _modules['Accessibility/Utils/EventProvider.js'], _modules['Accessibility/Utils/ChartUtilities.js']], function (Point, Series, SeriesRegistry, H, U, KeyboardNavigationHandler, EventProvider, ChartUtilities) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Handle keyboard navigation for series.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { seriesTypes } = SeriesRegistry;\n const { doc } = H;\n const { defined, fireEvent } = U;\n const { getPointFromXY, getSeriesFromName, scrollAxisToPoint } = ChartUtilities;\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Get the index of a point in a series. This is needed when using e.g. data\n * grouping.\n *\n * @private\n * @function getPointIndex\n * @param {Highcharts.AccessibilityPoint} point\n * The point to find index of.\n * @return {number|undefined}\n * The index in the series.points array of the point.\n */\n function getPointIndex(point) {\n const index = point.index, points = point.series.points;\n let i = points.length;\n if (points[index] !== point) {\n while (i--) {\n if (points[i] === point) {\n return i;\n }\n }\n }\n else {\n return index;\n }\n }\n /**\n * Determine if series navigation should be skipped\n * @private\n */\n function isSkipSeries(series) {\n const a11yOptions = series.chart.options.accessibility, seriesNavOptions = a11yOptions.keyboardNavigation.seriesNavigation, seriesA11yOptions = series.options.accessibility || {}, seriesKbdNavOptions = seriesA11yOptions.keyboardNavigation;\n return seriesKbdNavOptions && seriesKbdNavOptions.enabled === false ||\n seriesA11yOptions.enabled === false ||\n series.options.enableMouseTracking === false || // #8440\n !series.visible ||\n // Skip all points in a series where pointNavigationEnabledThreshold is\n // reached\n (seriesNavOptions.pointNavigationEnabledThreshold &&\n +seriesNavOptions.pointNavigationEnabledThreshold <=\n series.points.length);\n }\n /**\n * Determine if navigation for a point should be skipped\n * @private\n */\n function isSkipPoint(point) {\n const a11yOptions = point.series.chart.options.accessibility;\n const pointA11yDisabled = (point.options.accessibility &&\n point.options.accessibility.enabled === false);\n return point.isNull &&\n a11yOptions.keyboardNavigation.seriesNavigation.skipNullPoints ||\n point.visible === false ||\n point.isInside === false ||\n pointA11yDisabled ||\n isSkipSeries(point.series);\n }\n /**\n * Get the first point that is not a skip point in this series.\n * @private\n */\n function getFirstValidPointInSeries(series) {\n const points = series.points || [], len = points.length;\n for (let i = 0; i < len; ++i) {\n if (!isSkipPoint(points[i])) {\n return points[i];\n }\n }\n return null;\n }\n /**\n * Get the first point that is not a skip point in this chart.\n * @private\n */\n function getFirstValidPointInChart(chart) {\n const series = chart.series || [], len = series.length;\n for (let i = 0; i < len; ++i) {\n if (!isSkipSeries(series[i])) {\n const point = getFirstValidPointInSeries(series[i]);\n if (point) {\n return point;\n }\n }\n }\n return null;\n }\n /**\n * @private\n */\n function highlightLastValidPointInChart(chart) {\n const numSeries = chart.series.length;\n let i = numSeries, res = false;\n while (i--) {\n chart.highlightedPoint = chart.series[i].points[chart.series[i].points.length - 1];\n // Highlight first valid point in the series will also\n // look backwards. It always starts from currently\n // highlighted point.\n res = chart.series[i].highlightNextValidPoint();\n if (res) {\n break;\n }\n }\n return res;\n }\n /**\n * After drilling down/up, we need to set focus to the first point for\n * screen readers and keyboard nav.\n * @private\n */\n function updateChartFocusAfterDrilling(chart) {\n const point = getFirstValidPointInChart(chart);\n if (point) {\n point.highlight(false); // Do not visually highlight\n }\n }\n /**\n * Highlight the first point in chart that is not a skip point\n * @private\n */\n function highlightFirstValidPointInChart(chart) {\n delete chart.highlightedPoint;\n const point = getFirstValidPointInChart(chart);\n return point ? point.highlight() : false;\n }\n /* *\n *\n * Class\n *\n * */\n /**\n * @private\n * @class\n * @name Highcharts.SeriesKeyboardNavigation\n */\n class SeriesKeyboardNavigation {\n /* *\n *\n * Constructor\n *\n * */\n constructor(chart, keyCodes) {\n this.keyCodes = keyCodes;\n this.chart = chart;\n }\n /* *\n *\n * Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * Init the keyboard navigation\n */\n init() {\n const keyboardNavigation = this, chart = this.chart, e = this.eventProvider = new EventProvider();\n e.addEvent(Series, 'destroy', function () {\n return keyboardNavigation.onSeriesDestroy(this);\n });\n e.addEvent(chart, 'afterApplyDrilldown', function () {\n updateChartFocusAfterDrilling(this);\n });\n e.addEvent(chart, 'drilldown', function (e) {\n const point = e.point, series = point.series;\n keyboardNavigation.lastDrilledDownPoint = {\n x: point.x,\n y: point.y,\n seriesName: series ? series.name : ''\n };\n });\n e.addEvent(chart, 'drillupall', function () {\n setTimeout(function () {\n keyboardNavigation.onDrillupAll();\n }, 10);\n });\n // Heatmaps et al. alter z-index in setState, causing elements\n // to lose focus\n e.addEvent(Point, 'afterSetState', function () {\n const point = this;\n const pointEl = point.graphic && point.graphic.element;\n const focusedElement = doc.activeElement;\n // VO brings focus with it to container, causing series nav to run.\n // If then navigating with virtual cursor, it is possible to leave\n // keyboard nav module state on the data points and still activate\n // proxy buttons.\n const focusedElClassName = (focusedElement && focusedElement.getAttribute('class'));\n const isProxyFocused = focusedElClassName &&\n focusedElClassName.indexOf('highcharts-a11y-proxy-element') > -1;\n if (chart.highlightedPoint === point &&\n focusedElement !== pointEl &&\n !isProxyFocused &&\n pointEl &&\n pointEl.focus) {\n pointEl.focus();\n }\n });\n }\n /**\n * After drillup we want to find the point that was drilled down to and\n * highlight it.\n * @private\n */\n onDrillupAll() {\n const last = this.lastDrilledDownPoint, chart = this.chart, series = last && getSeriesFromName(chart, last.seriesName);\n let point;\n if (last && series && defined(last.x) && defined(last.y)) {\n point = getPointFromXY(series, last.x, last.y);\n }\n point = point || getFirstValidPointInChart(chart);\n // Container focus can be lost on drillup due to deleted elements.\n if (chart.container) {\n chart.container.focus();\n }\n if (point && point.highlight) {\n point.highlight(false); // Do not visually highlight\n }\n }\n /**\n * @private\n */\n getKeyboardNavigationHandler() {\n const keyboardNavigation = this, keys = this.keyCodes, chart = this.chart, inverted = chart.inverted;\n return new KeyboardNavigationHandler(chart, {\n keyCodeMap: [\n [inverted ? [keys.up, keys.down] : [keys.left, keys.right],\n function (keyCode) {\n return keyboardNavigation.onKbdSideways(this, keyCode);\n }],\n [inverted ? [keys.left, keys.right] : [keys.up, keys.down],\n function (keyCode) {\n return keyboardNavigation.onKbdVertical(this, keyCode);\n }],\n [[keys.enter, keys.space],\n function (keyCode, event) {\n const point = chart.highlightedPoint;\n if (point) {\n event.point = point;\n fireEvent(point.series, 'click', event);\n point.firePointEvent('click');\n }\n return this.response.success;\n }],\n [[keys.home],\n function () {\n highlightFirstValidPointInChart(chart);\n return this.response.success;\n }],\n [[keys.end],\n function () {\n highlightLastValidPointInChart(chart);\n return this.response.success;\n }],\n [[keys.pageDown, keys.pageUp],\n function (keyCode) {\n chart.highlightAdjacentSeries(keyCode === keys.pageDown);\n return this.response.success;\n }]\n ],\n init: function () {\n return keyboardNavigation.onHandlerInit(this);\n },\n validate: function () {\n return !!getFirstValidPointInChart(chart);\n },\n terminate: function () {\n return keyboardNavigation.onHandlerTerminate();\n }\n });\n }\n /**\n * @private\n * @param {Highcharts.KeyboardNavigationHandler} handler\n * @param {number} keyCode\n * @return {number}\n * response\n */\n onKbdSideways(handler, keyCode) {\n const keys = this.keyCodes, isNext = keyCode === keys.right || keyCode === keys.down;\n return this.attemptHighlightAdjacentPoint(handler, isNext);\n }\n /**\n * When keyboard navigation inits.\n * @private\n * @param {Highcharts.KeyboardNavigationHandler} handler The handler object\n * @return {number}\n * response\n */\n onHandlerInit(handler) {\n const chart = this.chart, kbdNavOptions = chart.options.accessibility.keyboardNavigation;\n if (kbdNavOptions.seriesNavigation.rememberPointFocus &&\n chart.highlightedPoint) {\n chart.highlightedPoint.highlight();\n }\n else {\n highlightFirstValidPointInChart(chart);\n }\n return handler.response.success;\n }\n /**\n * @private\n * @param {Highcharts.KeyboardNavigationHandler} handler\n * @param {number} keyCode\n * @return {number}\n * response\n */\n onKbdVertical(handler, keyCode) {\n const chart = this.chart, keys = this.keyCodes, isNext = keyCode === keys.down || keyCode === keys.right, navOptions = chart.options.accessibility.keyboardNavigation\n .seriesNavigation;\n // Handle serialized mode, act like left/right\n if (navOptions.mode && navOptions.mode === 'serialize') {\n return this.attemptHighlightAdjacentPoint(handler, isNext);\n }\n // Normal mode, move between series\n const highlightMethod = (chart.highlightedPoint &&\n chart.highlightedPoint.series.keyboardMoveVertical) ?\n 'highlightAdjacentPointVertical' :\n 'highlightAdjacentSeries';\n chart[highlightMethod](isNext);\n return handler.response.success;\n }\n /**\n * @private\n */\n onHandlerTerminate() {\n const chart = this.chart, kbdNavOptions = chart.options.accessibility.keyboardNavigation;\n if (chart.tooltip) {\n chart.tooltip.hide(0);\n }\n const hoverSeries = (chart.highlightedPoint && chart.highlightedPoint.series);\n if (hoverSeries && hoverSeries.onMouseOut) {\n hoverSeries.onMouseOut();\n }\n if (chart.highlightedPoint && chart.highlightedPoint.onMouseOut) {\n chart.highlightedPoint.onMouseOut();\n }\n if (!kbdNavOptions.seriesNavigation.rememberPointFocus) {\n delete chart.highlightedPoint;\n }\n }\n /**\n * Function that attempts to highlight next/prev point. Handles wrap around.\n * @private\n */\n attemptHighlightAdjacentPoint(handler, directionIsNext) {\n const chart = this.chart, wrapAround = chart.options.accessibility.keyboardNavigation\n .wrapAround, highlightSuccessful = chart.highlightAdjacentPoint(directionIsNext);\n if (!highlightSuccessful) {\n if (wrapAround && (directionIsNext ?\n highlightFirstValidPointInChart(chart) :\n highlightLastValidPointInChart(chart))) {\n return handler.response.success;\n }\n return handler.response[directionIsNext ? 'next' : 'prev'];\n }\n return handler.response.success;\n }\n /**\n * @private\n */\n onSeriesDestroy(series) {\n const chart = this.chart, currentHighlightedPointDestroyed = chart.highlightedPoint &&\n chart.highlightedPoint.series === series;\n if (currentHighlightedPointDestroyed) {\n delete chart.highlightedPoint;\n if (chart.focusElement) {\n chart.focusElement.removeFocusBorder();\n }\n }\n }\n /**\n * @private\n */\n destroy() {\n this.eventProvider.removeAddedEvents();\n }\n }\n /* *\n *\n * Class Namespace\n *\n * */\n (function (SeriesKeyboardNavigation) {\n /* *\n *\n * Declarations\n *\n * */\n /* *\n *\n * Constants\n *\n * */\n const composedMembers = [];\n /* *\n *\n * Functions\n *\n * */\n /**\n * Function to highlight next/previous point in chart.\n *\n * @private\n * @function Highcharts.Chart#highlightAdjacentPoint\n *\n * @param {boolean} next\n * Flag for the direction.\n *\n * @return {Highcharts.Point|boolean}\n * Returns highlighted point on success, false on failure (no adjacent point\n * to highlight in chosen direction).\n */\n function chartHighlightAdjacentPoint(next) {\n const chart = this, series = chart.series, curPoint = chart.highlightedPoint, curPointIndex = curPoint && getPointIndex(curPoint) || 0, curPoints = curPoint && curPoint.series.points || [], lastSeries = chart.series && chart.series[chart.series.length - 1], lastPoint = lastSeries &&\n lastSeries.points &&\n lastSeries.points[lastSeries.points.length - 1];\n let newSeries, newPoint;\n // If no points, return false\n if (!series[0] || !series[0].points) {\n return false;\n }\n if (!curPoint) {\n // No point is highlighted yet. Try first/last point depending on\n // move direction\n newPoint = next ? series[0].points[0] : lastPoint;\n }\n else {\n // We have a highlighted point. Grab next/prev point & series.\n newSeries = series[curPoint.series.index + (next ? 1 : -1)];\n newPoint = curPoints[curPointIndex + (next ? 1 : -1)];\n if (!newPoint && newSeries) {\n // Done with this series, try next one\n newPoint = newSeries.points[next ? 0 : newSeries.points.length - 1];\n }\n // If there is no adjacent point, we return false\n if (!newPoint) {\n return false;\n }\n }\n // Recursively skip points\n if (isSkipPoint(newPoint)) {\n // If we skip this whole series, move to the end of the series\n // before we recurse, just to optimize\n newSeries = newPoint.series;\n if (isSkipSeries(newSeries)) {\n chart.highlightedPoint = next ?\n newSeries.points[newSeries.points.length - 1] :\n newSeries.points[0];\n }\n else {\n // Otherwise, just move one point\n chart.highlightedPoint = newPoint;\n }\n // Retry\n return chart.highlightAdjacentPoint(next);\n }\n // There is an adjacent point, highlight it\n return newPoint.highlight();\n }\n /**\n * Highlight the closest point vertically.\n * @private\n */\n function chartHighlightAdjacentPointVertical(down) {\n const curPoint = this.highlightedPoint;\n let minDistance = Infinity, bestPoint;\n if (!defined(curPoint.plotX) || !defined(curPoint.plotY)) {\n return false;\n }\n this.series.forEach((series) => {\n if (isSkipSeries(series)) {\n return;\n }\n series.points.forEach((point) => {\n if (!defined(point.plotY) || !defined(point.plotX) ||\n point === curPoint) {\n return;\n }\n let yDistance = point.plotY - curPoint.plotY;\n const width = Math.abs(point.plotX - curPoint.plotX), distance = Math.abs(yDistance) * Math.abs(yDistance) +\n width * width * 4; // Weigh horizontal distance highly\n // Reverse distance number if axis is reversed\n if (series.yAxis && series.yAxis.reversed) {\n yDistance *= -1;\n }\n if (yDistance <= 0 && down || yDistance >= 0 && !down ||\n distance < 5 || // Points in same spot => infinite loop\n isSkipPoint(point)) {\n return;\n }\n if (distance < minDistance) {\n minDistance = distance;\n bestPoint = point;\n }\n });\n });\n return bestPoint ? bestPoint.highlight() : false;\n }\n /**\n * Highlight next/previous series in chart. Returns false if no adjacent\n * series in the direction, otherwise returns new highlighted point.\n * @private\n */\n function chartHighlightAdjacentSeries(down) {\n const chart = this, curPoint = chart.highlightedPoint, lastSeries = chart.series && chart.series[chart.series.length - 1], lastPoint = lastSeries && lastSeries.points &&\n lastSeries.points[lastSeries.points.length - 1];\n let newSeries, newPoint, adjacentNewPoint;\n // If no point is highlighted, highlight the first/last point\n if (!chart.highlightedPoint) {\n newSeries = down ? (chart.series && chart.series[0]) : lastSeries;\n newPoint = down ?\n (newSeries && newSeries.points && newSeries.points[0]) :\n lastPoint;\n return newPoint ? newPoint.highlight() : false;\n }\n newSeries = (chart.series[curPoint.series.index + (down ? -1 : 1)]);\n if (!newSeries) {\n return false;\n }\n // We have a new series in this direction, find the right point\n // Weigh xDistance as counting much higher than Y distance\n newPoint = getClosestPoint(curPoint, newSeries, 4);\n if (!newPoint) {\n return false;\n }\n // New series and point exists, but we might want to skip it\n if (isSkipSeries(newSeries)) {\n // Skip the series\n newPoint.highlight();\n // Try recurse\n adjacentNewPoint = chart.highlightAdjacentSeries(down);\n if (!adjacentNewPoint) {\n // Recurse failed\n curPoint.highlight();\n return false;\n }\n // Recurse succeeded\n return adjacentNewPoint;\n }\n // Highlight the new point or any first valid point back or forwards\n // from it\n newPoint.highlight();\n return newPoint.series.highlightNextValidPoint();\n }\n /**\n * @private\n */\n function compose(ChartClass, PointClass, SeriesClass) {\n if (U.pushUnique(composedMembers, ChartClass)) {\n const chartProto = ChartClass.prototype;\n chartProto.highlightAdjacentPoint = chartHighlightAdjacentPoint;\n chartProto.highlightAdjacentPointVertical = (chartHighlightAdjacentPointVertical);\n chartProto.highlightAdjacentSeries = chartHighlightAdjacentSeries;\n }\n if (U.pushUnique(composedMembers, PointClass)) {\n const pointProto = PointClass.prototype;\n pointProto.highlight = pointHighlight;\n }\n if (U.pushUnique(composedMembers, SeriesClass)) {\n const seriesProto = SeriesClass.prototype;\n /**\n * Set for which series types it makes sense to move to the closest\n * point with up/down arrows, and which series types should just\n * move to next series.\n * @private\n */\n seriesProto.keyboardMoveVertical = true;\n [\n 'column',\n 'gantt',\n 'pie'\n ].forEach((type) => {\n if (seriesTypes[type]) {\n seriesTypes[type].prototype.keyboardMoveVertical = false;\n }\n });\n seriesProto.highlightNextValidPoint = (seriesHighlightNextValidPoint);\n }\n }\n SeriesKeyboardNavigation.compose = compose;\n /**\n * Get the point in a series that is closest (in pixel distance) to a\n * reference point. Optionally supply weight factors for x and y directions.\n * @private\n */\n function getClosestPoint(point, series, xWeight, yWeight) {\n let minDistance = Infinity, dPoint, minIx, distance, i = series.points.length;\n const hasUndefinedPosition = (point) => (!(defined(point.plotX) && defined(point.plotY)));\n if (hasUndefinedPosition(point)) {\n return;\n }\n while (i--) {\n dPoint = series.points[i];\n if (hasUndefinedPosition(dPoint)) {\n continue;\n }\n distance = (point.plotX - dPoint.plotX) *\n (point.plotX - dPoint.plotX) *\n (xWeight || 1) +\n (point.plotY - dPoint.plotY) *\n (point.plotY - dPoint.plotY) *\n (yWeight || 1);\n if (distance < minDistance) {\n minDistance = distance;\n minIx = i;\n }\n }\n return defined(minIx) ? series.points[minIx] : void 0;\n }\n /**\n * Highlights a point (show tooltip, display hover state, focus element).\n *\n * @private\n * @function Highcharts.Point#highlight\n *\n * @return {Highcharts.Point}\n * This highlighted point.\n */\n function pointHighlight(highlightVisually = true) {\n const chart = this.series.chart, tooltipElement = chart.tooltip?.label?.element;\n if (!this.isNull && highlightVisually) {\n this.onMouseOver(); // Show the hover marker and tooltip\n }\n else {\n if (chart.tooltip) {\n chart.tooltip.hide(0);\n }\n // Do not call blur on the element, as it messes up the focus of the\n // div element of the chart\n }\n scrollAxisToPoint(this);\n // We focus only after calling onMouseOver because the state change can\n // change z-index and mess up the element.\n if (this.graphic) {\n chart.setFocusToElement(this.graphic);\n if (!highlightVisually && chart.focusElement) {\n chart.focusElement.removeFocusBorder();\n }\n }\n chart.highlightedPoint = this;\n // Get position of the tooltip.\n const tooltipTop = tooltipElement?.getBoundingClientRect().top;\n if (tooltipElement && tooltipTop && tooltipTop < 0) {\n // Calculate scroll position.\n const scrollTop = window.scrollY, newScrollTop = scrollTop + tooltipTop;\n // Scroll window to new position.\n window.scrollTo({\n behavior: 'smooth',\n top: newScrollTop\n });\n }\n return this;\n }\n /**\n * Highlight first valid point in a series. Returns the point if\n * successfully highlighted, otherwise false. If there is a highlighted\n * point in the series, use that as starting point.\n *\n * @private\n * @function Highcharts.Series#highlightNextValidPoint\n */\n function seriesHighlightNextValidPoint() {\n const curPoint = this.chart.highlightedPoint, start = (curPoint && curPoint.series) === this ?\n getPointIndex(curPoint) :\n 0, points = this.points, len = points.length;\n if (points && len) {\n for (let i = start; i < len; ++i) {\n if (!isSkipPoint(points[i])) {\n return points[i].highlight();\n }\n }\n for (let j = start; j >= 0; --j) {\n if (!isSkipPoint(points[j])) {\n return points[j].highlight();\n }\n }\n }\n return false;\n }\n })(SeriesKeyboardNavigation || (SeriesKeyboardNavigation = {}));\n /* *\n *\n * Default Export\n *\n * */\n\n return SeriesKeyboardNavigation;\n });\n _registerModule(_modules, 'Accessibility/Components/SeriesComponent/SeriesComponent.js', [_modules['Accessibility/AccessibilityComponent.js'], _modules['Accessibility/Utils/ChartUtilities.js'], _modules['Accessibility/Components/SeriesComponent/ForcedMarkers.js'], _modules['Accessibility/Components/SeriesComponent/NewDataAnnouncer.js'], _modules['Accessibility/Components/SeriesComponent/SeriesDescriber.js'], _modules['Accessibility/Components/SeriesComponent/SeriesKeyboardNavigation.js']], function (AccessibilityComponent, ChartUtilities, ForcedMarkers, NewDataAnnouncer, SeriesDescriber, SeriesKeyboardNavigation) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Accessibility component for series and points.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { hideSeriesFromAT } = ChartUtilities;\n const { describeSeries } = SeriesDescriber;\n /* *\n *\n * Class\n *\n * */\n /**\n * The SeriesComponent class\n *\n * @private\n * @class\n * @name Highcharts.SeriesComponent\n */\n class SeriesComponent extends AccessibilityComponent {\n /* *\n *\n * Static Functions\n *\n * */\n /* eslint-disable valid-jsdoc */\n /**\n * @private\n */\n static compose(ChartClass, PointClass, SeriesClass) {\n NewDataAnnouncer.compose(SeriesClass);\n ForcedMarkers.compose(SeriesClass);\n SeriesKeyboardNavigation.compose(ChartClass, PointClass, SeriesClass);\n }\n /* *\n *\n * Functions\n *\n * */\n /**\n * Init the component.\n */\n init() {\n this.newDataAnnouncer = new NewDataAnnouncer(this.chart);\n this.newDataAnnouncer.init();\n this.keyboardNavigation = new SeriesKeyboardNavigation(this.chart, this.keyCodes);\n this.keyboardNavigation.init();\n this.hideTooltipFromATWhenShown();\n this.hideSeriesLabelsFromATWhenShown();\n }\n /**\n * @private\n */\n hideTooltipFromATWhenShown() {\n const component = this;\n if (this.chart.tooltip) {\n this.addEvent(this.chart.tooltip.constructor, 'refresh', function () {\n if (this.chart === component.chart &&\n this.label &&\n this.label.element) {\n this.label.element.setAttribute('aria-hidden', true);\n }\n });\n }\n }\n /**\n * @private\n */\n hideSeriesLabelsFromATWhenShown() {\n this.addEvent(this.chart, 'afterDrawSeriesLabels', function () {\n this.series.forEach(function (series) {\n if (series.labelBySeries) {\n series.labelBySeries.attr('aria-hidden', true);\n }\n });\n });\n }\n /**\n * Called on chart render. It is necessary to do this for render in case\n * markers change on zoom/pixel density.\n */\n onChartRender() {\n const chart = this.chart;\n chart.series.forEach(function (series) {\n const shouldDescribeSeries = (series.options.accessibility &&\n series.options.accessibility.enabled) !== false &&\n series.visible;\n if (shouldDescribeSeries) {\n describeSeries(series);\n }\n else {\n hideSeriesFromAT(series);\n }\n });\n }\n /**\n * Get keyboard navigation handler for this component.\n * @private\n */\n getKeyboardNavigation() {\n return this.keyboardNavigation.getKeyboardNavigationHandler();\n }\n /**\n * Remove traces\n * @private\n */\n destroy() {\n this.newDataAnnouncer.destroy();\n this.keyboardNavigation.destroy();\n }\n }\n /* *\n *\n * Default Export\n *\n * */\n\n return SeriesComponent;\n });\n _registerModule(_modules, 'Accessibility/Components/ZoomComponent.js', [_modules['Accessibility/AccessibilityComponent.js'], _modules['Accessibility/Utils/ChartUtilities.js'], _modules['Accessibility/Utils/HTMLUtilities.js'], _modules['Accessibility/KeyboardNavigationHandler.js'], _modules['Core/Utilities.js']], function (AccessibilityComponent, CU, HU, KeyboardNavigationHandler, U) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Accessibility component for chart zoom.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { unhideChartElementFromAT } = CU;\n const { getFakeMouseEvent } = HU;\n const { attr, pick } = U;\n /* *\n *\n * Functions\n *\n * */\n /**\n * @private\n */\n function chartHasMapZoom(chart) {\n return !!((chart.mapView) &&\n chart.mapNavigation &&\n chart.mapNavigation.navButtons.length);\n }\n /* *\n *\n * Class\n *\n * */\n /**\n * The ZoomComponent class\n *\n * @private\n * @class\n * @name Highcharts.ZoomComponent\n */\n class ZoomComponent extends AccessibilityComponent {\n constructor() {\n /* *\n *\n * Properties\n *\n * */\n super(...arguments);\n this.focusedMapNavButtonIx = -1;\n }\n /* *\n *\n * Functions\n *\n * */\n /**\n * Initialize the component\n */\n init() {\n const component = this, chart = this.chart;\n this.proxyProvider.addGroup('zoom', 'div');\n [\n 'afterShowResetZoom', 'afterApplyDrilldown', 'drillupall'\n ].forEach((eventType) => {\n component.addEvent(chart, eventType, function () {\n component.updateProxyOverlays();\n });\n });\n }\n /**\n * Called when chart is updated\n */\n onChartUpdate() {\n const chart = this.chart, component = this;\n // Make map zoom buttons accessible\n if (chart.mapNavigation) {\n chart.mapNavigation.navButtons.forEach((button, i) => {\n unhideChartElementFromAT(chart, button.element);\n component.setMapNavButtonAttrs(button.element, 'accessibility.zoom.mapZoom' + (i ? 'Out' : 'In'));\n });\n }\n }\n /**\n * @private\n * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} button\n * @param {string} labelFormatKey\n */\n setMapNavButtonAttrs(button, labelFormatKey) {\n const chart = this.chart, label = chart.langFormat(labelFormatKey, { chart: chart });\n attr(button, {\n tabindex: -1,\n role: 'button',\n 'aria-label': label\n });\n }\n /**\n * Update the proxy overlays on every new render to ensure positions are\n * correct.\n */\n onChartRender() {\n this.updateProxyOverlays();\n }\n /**\n * Update proxy overlays, recreating the buttons.\n */\n updateProxyOverlays() {\n const chart = this.chart;\n // Always start with a clean slate\n this.proxyProvider.clearGroup('zoom');\n if (chart.resetZoomButton) {\n this.createZoomProxyButton(chart.resetZoomButton, 'resetZoomProxyButton', chart.langFormat('accessibility.zoom.resetZoomButton', { chart: chart }));\n }\n if (chart.drillUpButton &&\n chart.breadcrumbs &&\n chart.breadcrumbs.list) {\n const lastBreadcrumb = chart.breadcrumbs.list[chart.breadcrumbs.list.length - 1];\n this.createZoomProxyButton(chart.drillUpButton, 'drillUpProxyButton', chart.langFormat('accessibility.drillUpButton', {\n chart: chart,\n buttonText: chart.breadcrumbs.getButtonText(lastBreadcrumb)\n }));\n }\n }\n /**\n * @private\n * @param {Highcharts.SVGElement} buttonEl\n * @param {string} buttonProp\n * @param {string} label\n */\n createZoomProxyButton(buttonEl, buttonProp, label) {\n this[buttonProp] = this.proxyProvider.addProxyElement('zoom', {\n click: buttonEl\n }, 'button', {\n 'aria-label': label,\n tabindex: -1\n });\n }\n /**\n * Get keyboard navigation handler for map zoom.\n * @private\n * @return {Highcharts.KeyboardNavigationHandler} The module object\n */\n getMapZoomNavigation() {\n const keys = this.keyCodes, chart = this.chart, component = this;\n return new KeyboardNavigationHandler(chart, {\n keyCodeMap: [\n [\n [keys.up, keys.down, keys.left, keys.right],\n function (keyCode) {\n return component.onMapKbdArrow(this, keyCode);\n }\n ],\n [\n [keys.tab],\n function (_keyCode, e) {\n return component.onMapKbdTab(this, e);\n }\n ],\n [\n [keys.space, keys.enter],\n function () {\n return component.onMapKbdClick(this);\n }\n ]\n ],\n validate: function () {\n return chartHasMapZoom(chart);\n },\n init: function (direction) {\n return component.onMapNavInit(direction);\n }\n });\n }\n /**\n * Arrow key panning for maps.\n * @private\n * @param {Highcharts.KeyboardNavigationHandler} keyboardNavigationHandler The handler context.\n * @param {number} keyCode Key pressed.\n * @return {number} Response code\n */\n onMapKbdArrow(keyboardNavigationHandler, keyCode) {\n const chart = this.chart, keys = this.keyCodes, target = chart.container, isY = keyCode === keys.up || keyCode === keys.down, stepDirection = (keyCode === keys.left || keyCode === keys.up) ?\n 1 : -1, granularity = 10, diff = (isY ? chart.plotHeight : chart.plotWidth) /\n granularity * stepDirection, \n // Randomize since same mousedown coords twice is ignored in MapView\n r = Math.random() * 10, startPos = {\n x: target.offsetLeft + chart.plotLeft + chart.plotWidth / 2 + r,\n y: target.offsetTop + chart.plotTop + chart.plotHeight / 2 + r\n }, endPos = isY ? { x: startPos.x, y: startPos.y + diff } :\n { x: startPos.x + diff, y: startPos.y };\n [\n getFakeMouseEvent('mousedown', startPos),\n getFakeMouseEvent('mousemove', endPos),\n getFakeMouseEvent('mouseup', endPos)\n ].forEach((e) => target.dispatchEvent(e));\n return keyboardNavigationHandler.response.success;\n }\n /**\n * @private\n * @param {Highcharts.KeyboardNavigationHandler} keyboardNavigationHandler\n * @param {global.KeyboardEvent} event\n * @return {number} Response code\n */\n onMapKbdTab(keyboardNavigationHandler, event) {\n const chart = this.chart;\n const response = keyboardNavigationHandler.response;\n const isBackwards = event.shiftKey;\n const isMoveOutOfRange = isBackwards && !this.focusedMapNavButtonIx ||\n !isBackwards && this.focusedMapNavButtonIx;\n // Deselect old\n chart.mapNavigation.navButtons[this.focusedMapNavButtonIx].setState(0);\n if (isMoveOutOfRange) {\n if (chart.mapView) {\n chart.mapView.zoomBy(); // Reset zoom\n }\n return response[isBackwards ? 'prev' : 'next'];\n }\n // Select other button\n this.focusedMapNavButtonIx += isBackwards ? -1 : 1;\n const button = chart.mapNavigation.navButtons[this.focusedMapNavButtonIx];\n chart.setFocusToElement(button.box, button.element);\n button.setState(2);\n return response.success;\n }\n /**\n * Called on map button click.\n * @private\n * @param {Highcharts.KeyboardNavigationHandler} keyboardNavigationHandler The handler context object\n * @return {number} Response code\n */\n onMapKbdClick(keyboardNavigationHandler) {\n const el = this.chart.mapNavigation.navButtons[this.focusedMapNavButtonIx].element;\n this.fakeClickEvent(el);\n return keyboardNavigationHandler.response.success;\n }\n /**\n * @private\n * @param {number} direction\n */\n onMapNavInit(direction) {\n const chart = this.chart, zoomIn = chart.mapNavigation.navButtons[0], zoomOut = chart.mapNavigation.navButtons[1], initialButton = direction > 0 ? zoomIn : zoomOut;\n chart.setFocusToElement(initialButton.box, initialButton.element);\n initialButton.setState(2);\n this.focusedMapNavButtonIx = direction > 0 ? 0 : 1;\n }\n /**\n * Get keyboard navigation handler for a simple chart button. Provide the\n * button reference for the chart, and a function to call on click.\n *\n * @private\n * @param {string} buttonProp The property on chart referencing the button.\n * @return {Highcharts.KeyboardNavigationHandler} The module object\n */\n simpleButtonNavigation(buttonProp, proxyProp, onClick) {\n const keys = this.keyCodes, component = this, chart = this.chart;\n return new KeyboardNavigationHandler(chart, {\n keyCodeMap: [\n [\n [keys.tab, keys.up, keys.down, keys.left, keys.right],\n function (keyCode, e) {\n const isBackwards = (keyCode === keys.tab && e.shiftKey ||\n keyCode === keys.left ||\n keyCode === keys.up);\n // Arrow/tab => just move\n return this.response[isBackwards ? 'prev' : 'next'];\n }\n ],\n [\n [keys.space, keys.enter],\n function () {\n const res = onClick(this, chart);\n return pick(res, this.response.success);\n }\n ]\n ],\n validate: function () {\n const hasButton = (chart[buttonProp] &&\n chart[buttonProp].box &&\n component[proxyProp].innerElement);\n return hasButton;\n },\n init: function () {\n chart.setFocusToElement(chart[buttonProp].box, component[proxyProp].innerElement);\n }\n });\n }\n /**\n * Get keyboard navigation handlers for this component.\n * @return {Array}\n * List of module objects\n */\n getKeyboardNavigation() {\n return [\n this.simpleButtonNavigation('resetZoomButton', 'resetZoomProxyButton', function (_handler, chart) {\n chart.zoomOut();\n }),\n this.simpleButtonNavigation('drillUpButton', 'drillUpProxyButton', function (handler, chart) {\n chart.drillUp();\n return handler.response.prev;\n }),\n this.getMapZoomNavigation()\n ];\n }\n }\n /* *\n *\n * Default Export\n *\n * */\n\n return ZoomComponent;\n });\n _registerModule(_modules, 'Accessibility/HighContrastMode.js', [_modules['Core/Globals.js']], function (H) {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Handling for Windows High Contrast Mode.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n const { doc, isMS, win } = H;\n /* *\n *\n * Functions\n *\n * */\n /**\n * Detect WHCM in the browser.\n *\n * @function Highcharts#isHighContrastModeActive\n * @private\n * @return {boolean} Returns true if the browser is in High Contrast mode.\n */\n function isHighContrastModeActive() {\n // Use media query on Edge, but not on IE\n const isEdge = /(Edg)/.test(win.navigator.userAgent);\n if (win.matchMedia && isEdge) {\n return win.matchMedia('(-ms-high-contrast: active)').matches;\n }\n // Test BG image for IE\n if (isMS && win.getComputedStyle) {\n const testDiv = doc.createElement('div');\n const imageSrc = 'data:image/gif;base64,' +\n 'R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';\n testDiv.style.backgroundImage = `url(${imageSrc})`; // #13071\n doc.body.appendChild(testDiv);\n const bi = (testDiv.currentStyle ||\n win.getComputedStyle(testDiv)).backgroundImage;\n doc.body.removeChild(testDiv);\n return bi === 'none';\n }\n // Other browsers use the forced-colors standard\n return win.matchMedia && win.matchMedia('(forced-colors: active)').matches;\n }\n /**\n * Force high contrast theme for the chart. The default theme is defined in\n * a separate file.\n *\n * @function Highcharts#setHighContrastTheme\n * @private\n * @param {Highcharts.AccessibilityChart} chart The chart to set the theme of.\n * @return {void}\n */\n function setHighContrastTheme(chart) {\n // We might want to add additional functionality here in the future for\n // storing the old state so that we can reset the theme if HC mode is\n // disabled. For now, the user will have to reload the page.\n chart.highContrastModeActive = true;\n // Apply theme to chart\n const theme = (chart.options.accessibility.highContrastTheme);\n chart.update(theme, false);\n // Force series colors (plotOptions is not enough)\n chart.series.forEach(function (s) {\n const plotOpts = theme.plotOptions[s.type] || {};\n s.update({\n color: plotOpts.color || 'windowText',\n colors: [plotOpts.color || 'windowText'],\n borderColor: plotOpts.borderColor || 'window'\n });\n // Force point colors if existing\n s.points.forEach(function (p) {\n if (p.options && p.options.color) {\n p.update({\n color: plotOpts.color || 'windowText',\n borderColor: plotOpts.borderColor || 'window'\n }, false);\n }\n });\n });\n // The redraw for each series and after is required for 3D pie\n // (workaround)\n chart.redraw();\n }\n /* *\n *\n * Default Export\n *\n * */\n const whcm = {\n isHighContrastModeActive,\n setHighContrastTheme\n };\n\n return whcm;\n });\n _registerModule(_modules, 'Accessibility/HighContrastTheme.js', [], function () {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Default theme for Windows High Contrast Mode.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n /* *\n *\n * Theme\n *\n * */\n const theme = {\n chart: {\n backgroundColor: 'window'\n },\n title: {\n style: {\n color: 'windowText'\n }\n },\n subtitle: {\n style: {\n color: 'windowText'\n }\n },\n colorAxis: {\n minColor: 'windowText',\n maxColor: 'windowText',\n stops: []\n },\n colors: ['windowText'],\n xAxis: {\n gridLineColor: 'windowText',\n labels: {\n style: {\n color: 'windowText'\n }\n },\n lineColor: 'windowText',\n minorGridLineColor: 'windowText',\n tickColor: 'windowText',\n title: {\n style: {\n color: 'windowText'\n }\n }\n },\n yAxis: {\n gridLineColor: 'windowText',\n labels: {\n style: {\n color: 'windowText'\n }\n },\n lineColor: 'windowText',\n minorGridLineColor: 'windowText',\n tickColor: 'windowText',\n title: {\n style: {\n color: 'windowText'\n }\n }\n },\n tooltip: {\n backgroundColor: 'window',\n borderColor: 'windowText',\n style: {\n color: 'windowText'\n }\n },\n plotOptions: {\n series: {\n lineColor: 'windowText',\n fillColor: 'window',\n borderColor: 'windowText',\n edgeColor: 'windowText',\n borderWidth: 1,\n dataLabels: {\n connectorColor: 'windowText',\n color: 'windowText',\n style: {\n color: 'windowText',\n textOutline: 'none'\n }\n },\n marker: {\n lineColor: 'windowText',\n fillColor: 'windowText'\n }\n },\n pie: {\n color: 'window',\n colors: ['window'],\n borderColor: 'windowText',\n borderWidth: 1\n },\n boxplot: {\n fillColor: 'window'\n },\n candlestick: {\n lineColor: 'windowText',\n fillColor: 'window'\n },\n errorbar: {\n fillColor: 'window'\n }\n },\n legend: {\n backgroundColor: 'window',\n itemStyle: {\n color: 'windowText'\n },\n itemHoverStyle: {\n color: 'windowText'\n },\n itemHiddenStyle: {\n color: '#555'\n },\n title: {\n style: {\n color: 'windowText'\n }\n }\n },\n credits: {\n style: {\n color: 'windowText'\n }\n },\n drilldown: {\n activeAxisLabelStyle: {\n color: 'windowText'\n },\n activeDataLabelStyle: {\n color: 'windowText'\n }\n },\n navigation: {\n buttonOptions: {\n symbolStroke: 'windowText',\n theme: {\n fill: 'window'\n }\n }\n },\n rangeSelector: {\n buttonTheme: {\n fill: 'window',\n stroke: 'windowText',\n style: {\n color: 'windowText'\n },\n states: {\n hover: {\n fill: 'window',\n stroke: 'windowText',\n style: {\n color: 'windowText'\n }\n },\n select: {\n fill: '#444',\n stroke: 'windowText',\n style: {\n color: 'windowText'\n }\n }\n }\n },\n inputBoxBorderColor: 'windowText',\n inputStyle: {\n backgroundColor: 'window',\n color: 'windowText'\n },\n labelStyle: {\n color: 'windowText'\n }\n },\n navigator: {\n handles: {\n backgroundColor: 'window',\n borderColor: 'windowText'\n },\n outlineColor: 'windowText',\n maskFill: 'transparent',\n series: {\n color: 'windowText',\n lineColor: 'windowText'\n },\n xAxis: {\n gridLineColor: 'windowText'\n }\n },\n scrollbar: {\n barBackgroundColor: '#444',\n barBorderColor: 'windowText',\n buttonArrowColor: 'windowText',\n buttonBackgroundColor: 'window',\n buttonBorderColor: 'windowText',\n rifleColor: 'windowText',\n trackBackgroundColor: 'window',\n trackBorderColor: 'windowText'\n }\n };\n /* *\n *\n * Default Export\n *\n * */\n\n return theme;\n });\n _registerModule(_modules, 'Accessibility/Options/A11yDefaults.js', [], function () {\n /* *\n *\n * (c) 2009-2021 Øystein Moseng\n *\n * Default options for accessibility.\n *\n * License: www.highcharts.com/license\n *\n * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!\n *\n * */\n /* *\n *\n * API Options\n *\n * */\n /**\n * Formatter callback for the accessibility announcement.\n *\n * @callback Highcharts.AccessibilityAnnouncementFormatter\n *\n * @param {Array} updatedSeries\n * Array of all series that received updates. If an announcement is already\n * queued, the series that received updates for that announcement are also\n * included in this array.\n *\n * @param {Highcharts.Series} [addedSeries]\n * This is provided if {@link Highcharts.Chart#addSeries} was called, and there\n * is a new series. In that case, this argument is a reference to the new\n * series.\n *\n * @param {Highcharts.Point} [addedPoint]\n * This is provided if {@link Highcharts.Series#addPoint} was called, and there\n * is a new point. In that case, this argument is a reference to the new point.\n *\n * @return {false|string}\n * The function should return a string with the text to announce to the user.\n * Return empty string to not announce anything. Return `false` to use the\n * default announcement format.\n */\n /**\n * @interface Highcharts.PointAccessibilityOptionsObject\n */ /**\n * Provide a description of the data point, announced to screen readers.\n * @name Highcharts.PointAccessibilityOptionsObject#description\n * @type {string|undefined}\n * @requires modules/accessibility\n * @since 7.1.0\n */ /**\n * Enable or disable exposing the point to assistive technology\n * @name Highcharts.PointAccessibilityOptionsObject#enabled\n * @type {boolean|undefined}\n * @requires modules/accessibility\n * @since 9.0.1\n */\n /* *\n * @interface Highcharts.PointOptionsObject in parts/Point.ts\n */ /**\n * @name Highcharts.PointOptionsObject#accessibility\n * @type {Highcharts.PointAccessibilityOptionsObject|undefined}\n * @requires modules/accessibility\n * @since 7.1.0\n */\n /**\n * @callback Highcharts.ScreenReaderClickCallbackFunction\n *\n * @param {global.MouseEvent} evt\n * Mouse click event\n *\n * @return {void}\n */\n /**\n * Creates a formatted string for the screen reader module.\n *\n * @callback Highcharts.ScreenReaderFormatterCallbackFunction\n *\n * @param {T} context\n * Context to format\n *\n * @return {string}\n * Formatted string for the screen reader module.\n */\n const Options = {\n /**\n * Options for configuring accessibility for the chart. Requires the\n * [accessibility module](https://code.highcharts.com/modules/accessibility.js)\n * to be loaded. For a description of the module and information\n * on its features, see\n * [Highcharts Accessibility](https://www.highcharts.com/docs/accessibility/accessibility-module).\n *\n * @since 5.0.0\n * @requires modules/accessibility\n * @optionparent accessibility\n */\n accessibility: {\n /**\n * Enable accessibility functionality for the chart. For more\n * information on how to include these features, and why this is\n * recommended, see [Highcharts Accessibility](https://www.highcharts.com/docs/accessibility/accessibility-module).\n *\n * Highcharts will by default emit a warning to the console if\n * the [accessibility module](https://code.highcharts.com/modules/accessibility.js)\n * is not loaded. Setting this option to `false` will override\n * and silence the warning.\n *\n * Once the module is loaded, setting this option to `false`\n * will disable the module for this chart.\n *\n * @since 5.0.0\n */\n enabled: true,\n /**\n * Accessibility options for the screen reader information sections\n * added before and after the chart.\n *\n * @since 8.0.0\n */\n screenReaderSection: {\n /**\n * Function to run upon clicking the \"View as Data Table\" link in\n * the screen reader region.\n *\n * By default Highcharts will insert and set focus to a data table\n * representation of the chart.\n *\n * @type {Highcharts.ScreenReaderClickCallbackFunction}\n * @since 8.0.0\n * @apioption accessibility.screenReaderSection.onViewDataTableClick\n */\n /**\n * Function to run upon clicking the \"Play as sound\" button in\n * the screen reader region.\n *\n * By default Highcharts will call the `chart.sonify` function.\n *\n * @type {Highcharts.ScreenReaderClickCallbackFunction}\n * @since 8.0.1\n * @apioption accessibility.screenReaderSection.onPlayAsSoundClick\n */\n /**\n * A formatter function to create the HTML contents of the hidden\n * screen reader information region before the chart. Receives one\n * argument, `chart`, referring to the chart object. Should return a\n * string with the HTML content of the region. By default this\n * returns an automatic description of the chart based on\n * [beforeChartFormat](#accessibility.screenReaderSection.beforeChartFormat).\n *\n * @type {Highcharts.ScreenReaderFormatterCallbackFunction}\n * @since 8.0.0\n * @apioption accessibility.screenReaderSection.beforeChartFormatter\n */\n /**\n * Format for the screen reader information region before the chart.\n * Supported HTML tags are ``, `

    `, `

    `, ``, `