| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608 | 
							- /**
 
-  * Accessibility module - Keyboard navigation
 
-  *
 
-  * (c) 2010-2017 Highsoft AS
 
-  * Author: Oystein Moseng
 
-  *
 
-  * License: www.highcharts.com/license
 
-  */
 
- 'use strict';
 
- import H from '../parts/Globals.js';
 
- import '../parts/Utilities.js';
 
- import '../parts/Chart.js';
 
- import '../parts/Series.js';
 
- import '../parts/Point.js';
 
- import '../parts/Tooltip.js';
 
- import '../parts/SvgRenderer.js';
 
- var win = H.win,
 
-     doc = win.document,
 
-     addEvent = H.addEvent,
 
-     fireEvent = H.fireEvent,
 
-     merge = H.merge,
 
-     pick = H.pick;
 
- /*
 
-  * Add focus border functionality to SVGElements. Draws a new rect on top of
 
-  * element around its bounding box.
 
-  */
 
- H.extend(H.SVGElement.prototype, {
 
-     /**
 
-      * @private
 
-      * @function Highcharts.SVGElement#addFocusBorder
 
-      *
 
-      * @param {number} margin
 
-      *
 
-      * @param {Higcharts.CSSObject} style
 
-      */
 
-     addFocusBorder: function (margin, style) {
 
-         // Allow updating by just adding new border
 
-         if (this.focusBorder) {
 
-             this.removeFocusBorder();
 
-         }
 
-         // Add the border rect
 
-         var bb = this.getBBox(),
 
-             pad = pick(margin, 3);
 
-         this.focusBorder = this.renderer.rect(
 
-             bb.x - pad,
 
-             bb.y - pad,
 
-             bb.width + 2 * pad,
 
-             bb.height + 2 * pad,
 
-             style && style.borderRadius
 
-         )
 
-             .addClass('highcharts-focus-border')
 
-             .attr({
 
-                 zIndex: 99
 
-             })
 
-             .add(this.parentGroup);
 
-         if (!this.renderer.styledMode) {
 
-             this.focusBorder.attr({
 
-                 stroke: style && style.stroke,
 
-                 'stroke-width': style && style.strokeWidth
 
-             });
 
-         }
 
-     },
 
-     /**
 
-      * @private
 
-      * @function Highcharts.SVGElement#removeFocusBorder
 
-      */
 
-     removeFocusBorder: function () {
 
-         if (this.focusBorder) {
 
-             this.focusBorder.destroy();
 
-             delete this.focusBorder;
 
-         }
 
-     }
 
- });
 
- /*
 
-  * Set for which series types it makes sense to move to the closest point with
 
-  * up/down arrows, and which series types should just move to next series.
 
-  */
 
- H.Series.prototype.keyboardMoveVertical = true;
 
- ['column', 'pie'].forEach(function (type) {
 
-     if (H.seriesTypes[type]) {
 
-         H.seriesTypes[type].prototype.keyboardMoveVertical = false;
 
-     }
 
- });
 
- /**
 
-  * Strip HTML tags away from a string. Used for aria-label attributes, painting
 
-  * on a canvas will fail if the text contains tags.
 
-  *
 
-  * @private
 
-  * @function stripTags
 
-  *
 
-  * @param  {string} s
 
-  *         The input string
 
-  *
 
-  * @return {string}
 
-  *         The filtered string
 
-  */
 
- function stripTags(s) {
 
-     return typeof s === 'string' ? s.replace(/<\/?[^>]+(>|$)/g, '') : s;
 
- }
 
- /**
 
-  * Get the index of a point in a series. This is needed when using e.g. data
 
-  * grouping.
 
-  *
 
-  * @private
 
-  * @function getPointIndex
 
-  *
 
-  * @param {Highcharts.Point} point
 
-  *        The point to find index of.
 
-  *
 
-  * @return {number}
 
-  *         The index in the series.points array of the point.
 
-  */
 
- function getPointIndex(point) {
 
-     var index = point.index,
 
-         points = point.series.points,
 
-         i = points.length;
 
-     if (points[index] !== point) {
 
-         while (i--) {
 
-             if (points[i] === point) {
 
-                 return i;
 
-             }
 
-         }
 
-     } else {
 
-         return index;
 
-     }
 
- }
 
- // Set default keyboard navigation options
 
- H.setOptions({
 
-     /**
 
-      * @since        5.0.0
 
-      * @optionparent accessibility
 
-      */
 
-     accessibility: {
 
-         /**
 
-          * Options for keyboard navigation.
 
-          *
 
-          * @since 5.0.0
 
-          */
 
-         keyboardNavigation: {
 
-             /**
 
-              * Enable keyboard navigation for the chart.
 
-              *
 
-              * @since 5.0.0
 
-              */
 
-             enabled: true,
 
-             /**
 
-              * Options for the focus border drawn around elements while
 
-              * navigating through them.
 
-              *
 
-              * @sample highcharts/accessibility/custom-focus
 
-              *         Custom focus ring
 
-              *
 
-              * @since 6.0.3
 
-              */
 
-             focusBorder: {
 
-                 /**
 
-                  * Enable/disable focus border for chart.
 
-                  *
 
-                  * @since 6.0.3
 
-                  */
 
-                 enabled: true,
 
-                 /**
 
-                  * Hide the browser's default focus indicator.
 
-                  *
 
-                  * @since 6.0.4
 
-                  */
 
-                 hideBrowserFocusOutline: true,
 
-                 /**
 
-                  * Style options for the focus border drawn around elements
 
-                  * while navigating through them. Note that some browsers in
 
-                  * addition draw their own borders for focused elements. These
 
-                  * automatic borders can not be styled by Highcharts.
 
-                  *
 
-                  * In styled mode, the border is given the
 
-                  * `.highcharts-focus-border` class.
 
-                  *
 
-                  * @type    {Highcharts.CSSObject}
 
-                  * @default {"color": "#335cad", "lineWidth": 2, "borderRadius": 3}
 
-                  * @since   6.0.3
 
-                  */
 
-                 style: {
 
-                     /** @ignore-option */
 
-                     color: '#335cad',
 
-                     /** @ignore-option */
 
-                     lineWidth: 2,
 
-                     /** @ignore-option */
 
-                     borderRadius: 3
 
-                 },
 
-                 /**
 
-                  * Focus border margin around the elements.
 
-                  *
 
-                  * @since 6.0.3
 
-                  */
 
-                 margin: 2
 
-             },
 
-             /**
 
-              * Set the keyboard navigation mode for the chart. Can be "normal"
 
-              * or "serialize". In normal mode, left/right arrow keys move
 
-              * between points in a series, while up/down arrow keys move between
 
-              * series. Up/down navigation acts intelligently to figure out which
 
-              * series makes sense to move to from any given point.
 
-              *
 
-              * In "serialize" mode, points are instead navigated as a single
 
-              * list. Left/right behaves as in "normal" mode. Up/down arrow keys
 
-              * will behave like left/right. This is useful for unifying
 
-              * navigation behavior with/without screen readers enabled.
 
-              *
 
-              * @type       {string}
 
-              * @default    normal
 
-              * @since      6.0.4
 
-              * @validvalue ["normal", "serialize"]
 
-              * @apioption  accessibility.keyboardNavigation.mode
 
-              */
 
-             /**
 
-              * Skip null points when navigating through points with the
 
-              * keyboard.
 
-              *
 
-              * @since 5.0.0
 
-              */
 
-             skipNullPoints: true
 
-         }
 
-     }
 
- });
 
- /**
 
-  * Keyboard navigation for the legend. Requires the Accessibility module.
 
-  *
 
-  * @since     5.0.14
 
-  * @apioption legend.keyboardNavigation
 
-  */
 
- /**
 
-  * Enable/disable keyboard navigation for the legend. Requires the Accessibility
 
-  * module.
 
-  *
 
-  * @see [accessibility.keyboardNavigation](
 
-  *      #accessibility.keyboardNavigation.enabled)
 
-  *
 
-  * @type      {boolean}
 
-  * @default   true
 
-  * @since     5.0.13
 
-  * @apioption legend.keyboardNavigation.enabled
 
-  */
 
- /**
 
-  * Abstraction layer for keyboard navigation. Keep a map of keyCodes to handler
 
-  * functions, and a next/prev move handler for tab order. The module's keyCode
 
-  * handlers determine when to move to another module. Validate holds a function
 
-  * to determine if there are prerequisites for this module to run that are not
 
-  * met. Init holds a function to run once before any keyCodes are interpreted.
 
-  * Terminate holds a function to run once before moving to next/prev module.
 
-  *
 
-  * @private
 
-  * @class
 
-  * @name KeyboardNavigationModule
 
-  *
 
-  * @param {Highcharts.Chart} chart
 
-  *        The chart object keeps track of a list of KeyboardNavigationModules.
 
-  *
 
-  * @param {*} options
 
-  */
 
- function KeyboardNavigationModule(chart, options) {
 
-     this.chart = chart;
 
-     this.id = options.id;
 
-     this.keyCodeMap = options.keyCodeMap;
 
-     this.validate = options.validate;
 
-     this.init = options.init;
 
-     this.terminate = options.terminate;
 
- }
 
- KeyboardNavigationModule.prototype = {
 
-     /**
 
-      * Find handler function(s) for key code in the keyCodeMap and run it.
 
-      *
 
-      * @private
 
-      * @function KeyboardNavigationModule#run
 
-      *
 
-      * @param {global.Event} e
 
-      *
 
-      * @return {boolean}
 
-      */
 
-     run: function (e) {
 
-         var navModule = this,
 
-             keyCode = e.which || e.keyCode,
 
-             found = false,
 
-             handled = false;
 
-         this.keyCodeMap.forEach(function (codeSet) {
 
-             if (codeSet[0].indexOf(keyCode) > -1) {
 
-                 found = true;
 
-                 handled = codeSet[1].call(navModule, keyCode, e) !== false;
 
-             }
 
-         });
 
-         // Default tab handler, move to next/prev module
 
-         if (!found && keyCode === 9) {
 
-             handled = this.move(e.shiftKey ? -1 : 1);
 
-         }
 
-         return handled;
 
-     },
 
-     /**
 
-      * Move to next/prev valid module, or undefined if none, and init it.
 
-      * Returns true on success and false if there is no valid module to move to.
 
-      *
 
-      * @private
 
-      * @function KeyboardNavigationModule#move
 
-      *
 
-      * @param {number} direction
 
-      *
 
-      * @return {boolean}
 
-      */
 
-     move: function (direction) {
 
-         var chart = this.chart;
 
-         if (this.terminate) {
 
-             this.terminate(direction);
 
-         }
 
-         chart.keyboardNavigationModuleIndex += direction;
 
-         var newModule = chart.keyboardNavigationModules[
 
-             chart.keyboardNavigationModuleIndex
 
-         ];
 
-         // Remove existing focus border if any
 
-         if (chart.focusElement) {
 
-             chart.focusElement.removeFocusBorder();
 
-         }
 
-         // Verify new module
 
-         if (newModule) {
 
-             if (newModule.validate && !newModule.validate()) {
 
-                 return this.move(direction); // Invalid module, recurse
 
-             }
 
-             if (newModule.init) {
 
-                 newModule.init(direction); // Valid module, init it
 
-                 return true;
 
-             }
 
-         }
 
-         // No module
 
-         chart.keyboardNavigationModuleIndex = 0; // Reset counter
 
-         // Set focus to chart or exit anchor depending on direction
 
-         if (direction > 0) {
 
-             this.chart.exiting = true;
 
-             this.chart.tabExitAnchor.focus();
 
-         } else {
 
-             this.chart.renderTo.focus();
 
-         }
 
-         return false;
 
-     }
 
- };
 
- /**
 
-  * Utility function to attempt to fake a click event on an element.
 
-  *
 
-  * @private
 
-  * @function fakeClickEvent
 
-  *
 
-  * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement}
 
-  */
 
- function fakeClickEvent(element) {
 
-     var fakeEvent;
 
-     if (element && element.onclick && doc.createEvent) {
 
-         fakeEvent = doc.createEvent('Events');
 
-         fakeEvent.initEvent('click', true, false);
 
-         element.onclick(fakeEvent);
 
-     }
 
- }
 
- /**
 
-  * Determine if a series should be skipped
 
-  *
 
-  * @private
 
-  * @function isSkipSeries
 
-  *
 
-  * @param {Highcharts.Series} series
 
-  *
 
-  * @return {boolean}
 
-  */
 
- function isSkipSeries(series) {
 
-     var a11yOptions = series.chart.options.accessibility;
 
-     return series.options.skipKeyboardNavigation ||
 
-         series.options.enableMouseTracking === false || // #8440
 
-         !series.visible ||
 
-         // Skip all points in a series where pointDescriptionThreshold is
 
-         // reached
 
-         (a11yOptions.pointDescriptionThreshold &&
 
-         a11yOptions.pointDescriptionThreshold <= series.points.length);
 
- }
 
- /**
 
-  * Determine if a point should be skipped
 
-  *
 
-  * @private
 
-  * @function isSkipPoint
 
-  *
 
-  * @param {Highcharts.Point} point
 
-  *
 
-  * @return {boolean}
 
-  */
 
- function isSkipPoint(point) {
 
-     var a11yOptions = point.series.chart.options.accessibility;
 
-     return point.isNull && a11yOptions.keyboardNavigation.skipNullPoints ||
 
-         point.visible === false ||
 
-         isSkipSeries(point.series);
 
- }
 
- /**
 
-  * Get the point in a series that is closest (in distance) to a reference point.
 
-  * Optionally supply weight factors for x and y directions.
 
-  *
 
-  * @private
 
-  * @function getClosestPoint
 
-  *
 
-  * @param {Highcharts.Point} point
 
-  *
 
-  * @param {Highcharts.Series} series
 
-  *
 
-  * @param {number} [xWeight]
 
-  *
 
-  * @param {number} [yWeight]
 
-  *
 
-  * @return {Highcharts.Point|undefined}
 
-  */
 
- function getClosestPoint(point, series, xWeight, yWeight) {
 
-     var minDistance = Infinity,
 
-         dPoint,
 
-         minIx,
 
-         distance,
 
-         i = series.points.length;
 
-     if (point.plotX === undefined || point.plotY === undefined) {
 
-         return;
 
-     }
 
-     while (i--) {
 
-         dPoint = series.points[i];
 
-         if (dPoint.plotX === undefined || dPoint.plotY === undefined) {
 
-             continue;
 
-         }
 
-         distance = (point.plotX - dPoint.plotX) *
 
-                 (point.plotX - dPoint.plotX) * (xWeight || 1) +
 
-                 (point.plotY - dPoint.plotY) *
 
-                 (point.plotY - dPoint.plotY) * (yWeight || 1);
 
-         if (distance < minDistance) {
 
-             minDistance = distance;
 
-             minIx = i;
 
-         }
 
-     }
 
-     return minIx !== undefined && series.points[minIx];
 
- }
 
- /**
 
-  * Pan along axis in a direction (1 or -1), optionally with a defined
 
-  * granularity (number of steps it takes to walk across current view)
 
-  *
 
-  * @private
 
-  * @function Highcharts.Axis#panStep
 
-  *
 
-  * @param {number} direction
 
-  *
 
-  * @param {number} [granularity]
 
-  */
 
- H.Axis.prototype.panStep = function (direction, granularity) {
 
-     var gran = granularity || 3,
 
-         extremes = this.getExtremes(),
 
-         step = (extremes.max - extremes.min) / gran * direction,
 
-         newMax = extremes.max + step,
 
-         newMin = extremes.min + step,
 
-         size = newMax - newMin;
 
-     if (direction < 0 && newMin < extremes.dataMin) {
 
-         newMin = extremes.dataMin;
 
-         newMax = newMin + size;
 
-     } else if (direction > 0 && newMax > extremes.dataMax) {
 
-         newMax = extremes.dataMax;
 
-         newMin = newMax - size;
 
-     }
 
-     this.setExtremes(newMin, newMax);
 
- };
 
- /**
 
-  * Set chart's focus to an SVGElement. Calls focus() on it, and draws the focus
 
-  * border.
 
-  *
 
-  * @private
 
-  * @function Highcharts.Chart#setFocusToElement
 
-  *
 
-  * @param {Highcharts.SVGElement} svgElement
 
-  *        Element to draw the border around.
 
-  *
 
-  * @param {Highcharts.SVGElement} [focusElement]
 
-  *        If supplied, it draws the border around svgElement and sets the focus
 
-  *        to focusElement.
 
-  */
 
- H.Chart.prototype.setFocusToElement = function (svgElement, focusElement) {
 
-     var focusBorderOptions = this.options.accessibility
 
-             .keyboardNavigation.focusBorder,
 
-         browserFocusElement = focusElement || svgElement;
 
-     // Set browser focus if possible
 
-     if (
 
-         browserFocusElement.element &&
 
-         browserFocusElement.element.focus
 
-     ) {
 
-         browserFocusElement.element.focus();
 
-         // Hide default focus ring
 
-         if (focusBorderOptions.hideBrowserFocusOutline) {
 
-             browserFocusElement.css({ outline: 'none' });
 
-         }
 
-     }
 
-     if (focusBorderOptions.enabled) {
 
-         // Remove old focus border
 
-         if (this.focusElement) {
 
-             this.focusElement.removeFocusBorder();
 
-         }
 
-         // Draw focus border (since some browsers don't do it automatically)
 
-         svgElement.addFocusBorder(focusBorderOptions.margin, {
 
-             stroke: focusBorderOptions.style.color,
 
-             strokeWidth: focusBorderOptions.style.lineWidth,
 
-             borderRadius: focusBorderOptions.style.borderRadius
 
-         });
 
-         this.focusElement = svgElement;
 
-     }
 
- };
 
- /**
 
-  * Highlights a point (show tooltip and display hover state).
 
-  *
 
-  * @private
 
-  * @function Highcharts.Point#highlight
 
-  *
 
-  * @return {Highcharts.Point}
 
-  *         This highlighted point.
 
-  */
 
- H.Point.prototype.highlight = function () {
 
-     var chart = this.series.chart;
 
-     if (!this.isNull) {
 
-         this.onMouseOver(); // Show the hover marker and tooltip
 
-     } else {
 
-         if (chart.tooltip) {
 
-             chart.tooltip.hide(0);
 
-         }
 
-         // Don't call blur on the element, as it messes up the chart div's focus
 
-     }
 
-     // We focus only after calling onMouseOver because the state change can
 
-     // change z-index and mess up the element.
 
-     if (this.graphic) {
 
-         chart.setFocusToElement(this.graphic);
 
-     }
 
-     chart.highlightedPoint = this;
 
-     return this;
 
- };
 
- /**
 
-  * Function to highlight next/previous point in chart.
 
-  *
 
-  * @private
 
-  * @function Highcharts.Chart#highlightAdjacentPoint
 
-  *
 
-  * @param {boolean} next
 
-  *        Flag for the direction.
 
-  *
 
-  * @return {Highcharts.Point|false}
 
-  *         Returns highlighted point on success, false on failure (no adjacent
 
-  *         point to highlight in chosen direction).
 
-  */
 
- H.Chart.prototype.highlightAdjacentPoint = function (next) {
 
-     var 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 && lastSeries.points &&
 
-                     lastSeries.points[lastSeries.points.length - 1],
 
-         newSeries,
 
-         newPoint;
 
-     // If no points, return false
 
-     if (!series[0] || !series[0].points) {
 
-         return false;
 
-     }
 
-     if (!curPoint) {
 
-         // No point is highlighted yet. Try first/last point depending on move
 
-         // direction
 
-         newPoint = next ? series[0].points[0] : lastPoint;
 
-     } else {
 
-         // We have a highlighted point.
 
-         // Grab next/prev point & series
 
-         newSeries = series[curPoint.series.index + (next ? 1 : -1)];
 
-         newPoint = curPoints[curPointIndex + (next ? 1 : -1)];
 
-         if (!newPoint && newSeries) {
 
-             // Done with this series, try next one
 
-             newPoint = newSeries.points[next ? 0 : newSeries.points.length - 1];
 
-         }
 
-         // If there is no adjacent point, we return false
 
-         if (!newPoint) {
 
-             return false;
 
-         }
 
-     }
 
-     // Recursively skip points
 
-     if (isSkipPoint(newPoint)) {
 
-         // If we skip this whole series, move to the end of the series before we
 
-         // recurse, just to optimize
 
-         newSeries = newPoint.series;
 
-         if (isSkipSeries(newSeries)) {
 
-             chart.highlightedPoint = next ?
 
-                 newSeries.points[newSeries.points.length - 1] :
 
-                 newSeries.points[0];
 
-         } else {
 
-             // Otherwise, just move one point
 
-             chart.highlightedPoint = newPoint;
 
-         }
 
-         // Retry
 
-         return chart.highlightAdjacentPoint(next);
 
-     }
 
-     // There is an adjacent point, highlight it
 
-     return newPoint.highlight();
 
- };
 
- /**
 
-  * Highlight first valid point in a series. Returns the point if successfully
 
-  * highlighted, otherwise false. If there is a highlighted point in the series,
 
-  * use that as starting point.
 
-  *
 
-  * @private
 
-  * @function Highcharts.Series#highlightFirstValidPoint
 
-  *
 
-  * @return {Highcharts.Point|false}
 
-  */
 
- H.Series.prototype.highlightFirstValidPoint = function () {
 
-     var curPoint = this.chart.highlightedPoint,
 
-         start = (curPoint && curPoint.series) === this ?
 
-             getPointIndex(curPoint) :
 
-             0,
 
-         points = this.points;
 
-     if (points) {
 
-         for (var i = start, len = points.length; i < len; ++i) {
 
-             if (!isSkipPoint(points[i])) {
 
-                 return points[i].highlight();
 
-             }
 
-         }
 
-         for (var j = start; j >= 0; --j) {
 
-             if (!isSkipPoint(points[j])) {
 
-                 return points[j].highlight();
 
-             }
 
-         }
 
-     }
 
-     return false;
 
- };
 
- /**
 
-  * Highlight next/previous series in chart. Returns false if no adjacent series
 
-  * in the direction, otherwise returns new highlighted point.
 
-  *
 
-  * @private
 
-  * @function Highcharts.Chart#highlightAdjacentSeries
 
-  *
 
-  * @param {boolean} down
 
-  *
 
-  * @return {Highcharts.Point|false}
 
-  */
 
- H.Chart.prototype.highlightAdjacentSeries = function (down) {
 
-     var chart = this,
 
-         newSeries,
 
-         newPoint,
 
-         adjacentNewPoint,
 
-         curPoint = chart.highlightedPoint,
 
-         lastSeries = chart.series && chart.series[chart.series.length - 1],
 
-         lastPoint = lastSeries && lastSeries.points &&
 
-                     lastSeries.points[lastSeries.points.length - 1];
 
-     // If no point is highlighted, highlight the first/last point
 
-     if (!chart.highlightedPoint) {
 
-         newSeries = down ? (chart.series && chart.series[0]) : lastSeries;
 
-         newPoint = down ?
 
-             (newSeries && newSeries.points && newSeries.points[0]) : lastPoint;
 
-         return newPoint ? newPoint.highlight() : false;
 
-     }
 
-     newSeries = chart.series[curPoint.series.index + (down ? -1 : 1)];
 
-     if (!newSeries) {
 
-         return false;
 
-     }
 
-     // We have a new series in this direction, find the right point
 
-     // Weigh xDistance as counting much higher than Y distance
 
-     newPoint = getClosestPoint(curPoint, newSeries, 4);
 
-     if (!newPoint) {
 
-         return false;
 
-     }
 
-     // New series and point exists, but we might want to skip it
 
-     if (isSkipSeries(newSeries)) {
 
-         // Skip the series
 
-         newPoint.highlight();
 
-         adjacentNewPoint = chart.highlightAdjacentSeries(down); // Try recurse
 
-         if (!adjacentNewPoint) {
 
-             // Recurse failed
 
-             curPoint.highlight();
 
-             return false;
 
-         }
 
-         // Recurse succeeded
 
-         return adjacentNewPoint;
 
-     }
 
-     // Highlight the new point or any first valid point back or forwards from it
 
-     newPoint.highlight();
 
-     return newPoint.series.highlightFirstValidPoint();
 
- };
 
- /**
 
-  * Highlight the closest point vertically.
 
-  *
 
-  * @private
 
-  * @function Highcharts.Chart#highlightAdjacentPointVertical
 
-  *
 
-  * @param {boolean} down
 
-  *
 
-  * @return {Highcharts.Point|false}
 
-  */
 
- H.Chart.prototype.highlightAdjacentPointVertical = function (down) {
 
-     var curPoint = this.highlightedPoint,
 
-         minDistance = Infinity,
 
-         bestPoint;
 
-     if (curPoint.plotX === undefined || curPoint.plotY === undefined) {
 
-         return false;
 
-     }
 
-     this.series.forEach(function (series) {
 
-         if (isSkipSeries(series)) {
 
-             return;
 
-         }
 
-         series.points.forEach(function (point) {
 
-             if (point.plotY === undefined || point.plotX === undefined ||
 
-                 point === curPoint) {
 
-                 return;
 
-             }
 
-             var yDistance = point.plotY - curPoint.plotY,
 
-                 width = Math.abs(point.plotX - curPoint.plotX),
 
-                 distance = Math.abs(yDistance) * Math.abs(yDistance) +
 
-                     width * width * 4; // Weigh horizontal distance highly
 
-             // Reverse distance number if axis is reversed
 
-             if (series.yAxis.reversed) {
 
-                 yDistance *= -1;
 
-             }
 
-             if (
 
-                 yDistance < 0 && down || yDistance > 0 && !down || // Wrong dir
 
-                 distance < 5 || // Points in same spot => infinite loop
 
-                 isSkipPoint(point)
 
-             ) {
 
-                 return;
 
-             }
 
-             if (distance < minDistance) {
 
-                 minDistance = distance;
 
-                 bestPoint = point;
 
-             }
 
-         });
 
-     });
 
-     return bestPoint ? bestPoint.highlight() : false;
 
- };
 
- /**
 
-  * Show the export menu and focus the first item (if exists).
 
-  *
 
-  * @private
 
-  * @function Highcharts.Chart#showExportMenu
 
-  */
 
- H.Chart.prototype.showExportMenu = function () {
 
-     if (this.exportSVGElements && this.exportSVGElements[0]) {
 
-         this.exportSVGElements[0].element.onclick();
 
-         this.highlightExportItem(0);
 
-     }
 
- };
 
- /**
 
-  * Hide export menu.
 
-  *
 
-  * @private
 
-  * @function Highcharts.Chart#hideExportMenu
 
-  */
 
- H.Chart.prototype.hideExportMenu = function () {
 
-     var chart = this,
 
-         exportList = chart.exportDivElements;
 
-     if (exportList && chart.exportContextMenu) {
 
-         // Reset hover states etc.
 
-         exportList.forEach(function (el) {
 
-             if (el.className === 'highcharts-menu-item' && el.onmouseout) {
 
-                 el.onmouseout();
 
-             }
 
-         });
 
-         chart.highlightedExportItem = 0;
 
-         // Hide the menu div
 
-         chart.exportContextMenu.hideMenu();
 
-         // Make sure the chart has focus and can capture keyboard events
 
-         chart.container.focus();
 
-     }
 
- };
 
- /**
 
-  * Highlight export menu item by index.
 
-  *
 
-  * @private
 
-  * @function Highcharts.Chart#highlightExportItem
 
-  *
 
-  * @param {number} ix
 
-  *
 
-  * @return {true|undefined}
 
-  */
 
- H.Chart.prototype.highlightExportItem = function (ix) {
 
-     var listItem = this.exportDivElements && this.exportDivElements[ix],
 
-         curHighlighted =
 
-             this.exportDivElements &&
 
-             this.exportDivElements[this.highlightedExportItem],
 
-         hasSVGFocusSupport;
 
-     if (
 
-         listItem &&
 
-         listItem.tagName === 'DIV' &&
 
-         !(listItem.children && listItem.children.length)
 
-     ) {
 
-         // Test if we have focus support for SVG elements
 
-         hasSVGFocusSupport = !!(
 
-             this.renderTo.getElementsByTagName('g')[0] || {}
 
-         ).focus;
 
-         // Only focus if we can set focus back to the elements after
 
-         // destroying the menu (#7422)
 
-         if (listItem.focus && hasSVGFocusSupport) {
 
-             listItem.focus();
 
-         }
 
-         if (curHighlighted && curHighlighted.onmouseout) {
 
-             curHighlighted.onmouseout();
 
-         }
 
-         if (listItem.onmouseover) {
 
-             listItem.onmouseover();
 
-         }
 
-         this.highlightedExportItem = ix;
 
-         return true;
 
-     }
 
- };
 
- /**
 
-  * Try to highlight the last valid export menu item.
 
-  *
 
-  * @private
 
-  * @function Highcharts.Chart#highlightLastExportItem
 
-  */
 
- H.Chart.prototype.highlightLastExportItem = function () {
 
-     var chart = this,
 
-         i;
 
-     if (chart.exportDivElements) {
 
-         i = chart.exportDivElements.length;
 
-         while (i--) {
 
-             if (chart.highlightExportItem(i)) {
 
-                 break;
 
-             }
 
-         }
 
-     }
 
- };
 
- /**
 
-  * Highlight range selector button by index.
 
-  *
 
-  * @private
 
-  * @function Highcharts.Chart#highlightRangeSelectorButton
 
-  *
 
-  * @param {number} ix
 
-  *
 
-  * @return {boolean}
 
-  */
 
- H.Chart.prototype.highlightRangeSelectorButton = function (ix) {
 
-     var buttons = this.rangeSelector.buttons;
 
-     // Deselect old
 
-     if (buttons[this.highlightedRangeSelectorItemIx]) {
 
-         buttons[this.highlightedRangeSelectorItemIx].setState(
 
-             this.oldRangeSelectorItemState || 0
 
-         );
 
-     }
 
-     // Select new
 
-     this.highlightedRangeSelectorItemIx = ix;
 
-     if (buttons[ix]) {
 
-         this.setFocusToElement(buttons[ix].box, buttons[ix]);
 
-         this.oldRangeSelectorItemState = buttons[ix].state;
 
-         buttons[ix].setState(2);
 
-         return true;
 
-     }
 
-     return false;
 
- };
 
- /**
 
-  * Highlight legend item by index.
 
-  *
 
-  * @private
 
-  * @function Highcharts.Chart#highlightLegendItem
 
-  *
 
-  * @param {number} ix
 
-  *
 
-  * @return {boolean}
 
-  */
 
- H.Chart.prototype.highlightLegendItem = function (ix) {
 
-     var items = this.legend.allItems,
 
-         oldIx = this.highlightedLegendItemIx;
 
-     if (items[ix]) {
 
-         if (items[oldIx]) {
 
-             fireEvent(
 
-                 items[oldIx].legendGroup.element,
 
-                 'mouseout'
 
-             );
 
-         }
 
-         // Scroll if we have to
 
-         if (items[ix].pageIx !== undefined &&
 
-             items[ix].pageIx + 1 !== this.legend.currentPage) {
 
-             this.legend.scroll(1 + items[ix].pageIx - this.legend.currentPage);
 
-         }
 
-         // Focus
 
-         this.highlightedLegendItemIx = ix;
 
-         this.setFocusToElement(items[ix].legendItem, items[ix].legendGroup);
 
-         fireEvent(items[ix].legendGroup.element, 'mouseover');
 
-         return true;
 
-     }
 
-     return false;
 
- };
 
- /**
 
-  * Add keyboard navigation handling modules to chart.
 
-  *
 
-  * @private
 
-  * @function Highcharts.Chart#addKeyboardNavigationModules
 
-  */
 
- H.Chart.prototype.addKeyboardNavigationModules = function () {
 
-     var chart = this;
 
-     /**
 
-      * @private
 
-      * @function navModuleFactory
 
-      *
 
-      * @param {string} id
 
-      *
 
-      * @param {Array<Array<number>,Function>} keyMap
 
-      *
 
-      * @param {Highcharts.Dictionary<Function>} options
 
-      *
 
-      * @return {KeyboardNavigationModule}
 
-      */
 
-     function navModuleFactory(id, keyMap, options) {
 
-         return new KeyboardNavigationModule(chart, merge({
 
-             keyCodeMap: keyMap
 
-         }, { id: id }, options));
 
-     }
 
-     /**
 
-      * List of the different keyboard handling modes we use depending on where
 
-      * we are in the chart. Each mode has a set of handling functions mapped to
 
-      * key codes. Each mode determines when to move to the next/prev mode.
 
-      *
 
-      * @private
 
-      * @name Highcharts.Chart#keyboardNavigationModules
 
-      * @type {Array<KeyboardNavigationModule>}
 
-      */
 
-     chart.keyboardNavigationModules = [
 
-         // Entry point catching the first tab, allowing users to tab into points
 
-         // more intuitively.
 
-         navModuleFactory('entry', []),
 
-         // Points
 
-         navModuleFactory('points', [
 
-             // Left/Right
 
-             [[37, 39], function (keyCode) {
 
-                 var right = keyCode === 39;
 
-                 if (!chart.highlightAdjacentPoint(right)) {
 
-                     // Failed to highlight next, wrap to last/first
 
-                     return this.init(right ? 1 : -1);
 
-                 }
 
-                 return true;
 
-             }],
 
-             // Up/Down
 
-             [[38, 40], function (keyCode) {
 
-                 var down = keyCode !== 38,
 
-                     navOptions = chart.options.accessibility.keyboardNavigation;
 
-                 if (navOptions.mode && navOptions.mode === 'serialize') {
 
-                     // Act like left/right
 
-                     if (!chart.highlightAdjacentPoint(down)) {
 
-                         return this.init(down ? 1 : -1);
 
-                     }
 
-                     return true;
 
-                 }
 
-                 // Normal mode, move between series
 
-                 var highlightMethod = chart.highlightedPoint &&
 
-                         chart.highlightedPoint.series.keyboardMoveVertical ?
 
-                     'highlightAdjacentPointVertical' :
 
-                     'highlightAdjacentSeries';
 
-                 chart[highlightMethod](down);
 
-                 return true;
 
-             }],
 
-             // Enter/Spacebar
 
-             [[13, 32], function () {
 
-                 if (chart.highlightedPoint) {
 
-                     chart.highlightedPoint.firePointEvent('click');
 
-                 }
 
-             }]
 
-         ], {
 
-             // Always start highlighting from scratch when entering this module
 
-             init: function (dir) {
 
-                 var numSeries = chart.series.length,
 
-                     i = dir > 0 ? 0 : numSeries,
 
-                     res;
 
-                 if (dir > 0) {
 
-                     delete chart.highlightedPoint;
 
-                     // Find first valid point to highlight
 
-                     while (i < numSeries) {
 
-                         res = chart.series[i].highlightFirstValidPoint();
 
-                         if (res) {
 
-                             return res;
 
-                         }
 
-                         ++i;
 
-                     }
 
-                 } else {
 
-                     // Find last valid point to highlight
 
-                     while (i--) {
 
-                         chart.highlightedPoint = chart.series[i].points[
 
-                             chart.series[i].points.length - 1
 
-                         ];
 
-                         // Highlight first valid point in the series will also
 
-                         // look backwards. It always starts from currently
 
-                         // highlighted point.
 
-                         res = chart.series[i].highlightFirstValidPoint();
 
-                         if (res) {
 
-                             return res;
 
-                         }
 
-                     }
 
-                 }
 
-             },
 
-             // If leaving points, don't show tooltip anymore
 
-             terminate: function () {
 
-                 if (chart.tooltip) {
 
-                     chart.tooltip.hide(0);
 
-                 }
 
-                 delete chart.highlightedPoint;
 
-             }
 
-         }),
 
-         // Exporting
 
-         navModuleFactory('exporting', [
 
-             // Left/Up
 
-             [[37, 38], function () {
 
-                 var i = chart.highlightedExportItem || 0,
 
-                     reachedEnd = true;
 
-                 // Try to highlight prev item in list. Highlighting e.g.
 
-                 // separators will fail.
 
-                 while (i--) {
 
-                     if (chart.highlightExportItem(i)) {
 
-                         reachedEnd = false;
 
-                         break;
 
-                     }
 
-                 }
 
-                 if (reachedEnd) {
 
-                     chart.highlightLastExportItem();
 
-                     return true;
 
-                 }
 
-             }],
 
-             // Right/Down
 
-             [[39, 40], function () {
 
-                 var highlightedExportItem = chart.highlightedExportItem || 0,
 
-                     reachedEnd = true;
 
-                 // Try to highlight next item in list. Highlighting e.g.
 
-                 // separators will fail.
 
-                 for (
 
-                     var i = highlightedExportItem + 1;
 
-                     i < chart.exportDivElements.length;
 
-                     ++i
 
-                 ) {
 
-                     if (chart.highlightExportItem(i)) {
 
-                         reachedEnd = false;
 
-                         break;
 
-                     }
 
-                 }
 
-                 if (reachedEnd) {
 
-                     chart.highlightExportItem(0);
 
-                     return true;
 
-                 }
 
-             }],
 
-             // Enter/Spacebar
 
-             [[13, 32], function () {
 
-                 fakeClickEvent(
 
-                     chart.exportDivElements[chart.highlightedExportItem]
 
-                 );
 
-             }]
 
-         ], {
 
-             // Only run exporting navigation if exporting support exists and is
 
-             // enabled on chart
 
-             validate: function () {
 
-                 return (
 
-                     chart.exportChart &&
 
-                     !(
 
-                         chart.options.exporting &&
 
-                         chart.options.exporting.enabled === false
 
-                     )
 
-                 );
 
-             },
 
-             // Show export menu
 
-             init: function (direction) {
 
-                 chart.highlightedPoint = null;
 
-                 chart.showExportMenu();
 
-                 // If coming back to export menu from other module, try to
 
-                 // highlight last item in menu
 
-                 if (direction < 0) {
 
-                     chart.highlightLastExportItem();
 
-                 }
 
-             },
 
-             // Hide the menu
 
-             terminate: function () {
 
-                 chart.hideExportMenu();
 
-             }
 
-         }),
 
-         // Map zoom
 
-         navModuleFactory('mapZoom', [
 
-             // Up/down/left/right
 
-             [[38, 40, 37, 39], function (keyCode) {
 
-                 chart[keyCode === 38 || keyCode === 40 ? 'yAxis' : 'xAxis'][0]
 
-                     .panStep(keyCode < 39 ? -1 : 1);
 
-             }],
 
-             // Tabs
 
-             [[9], function (keyCode, e) {
 
-                 var button;
 
-                 // Deselect old
 
-                 chart.mapNavButtons[chart.focusedMapNavButtonIx].setState(0);
 
-                 if (
 
-                     e.shiftKey && !chart.focusedMapNavButtonIx ||
 
-                     !e.shiftKey && chart.focusedMapNavButtonIx
 
-                 ) { // trying to go somewhere we can't?
 
-                     chart.mapZoom(); // Reset zoom
 
-                     // Nowhere to go, go to prev/next module
 
-                     return this.move(e.shiftKey ? -1 : 1);
 
-                 }
 
-                 chart.focusedMapNavButtonIx += e.shiftKey ? -1 : 1;
 
-                 button = chart.mapNavButtons[chart.focusedMapNavButtonIx];
 
-                 chart.setFocusToElement(button.box, button);
 
-                 button.setState(2);
 
-             }],
 
-             // Enter/Spacebar
 
-             [[13, 32], function () {
 
-                 fakeClickEvent(
 
-                     chart.mapNavButtons[chart.focusedMapNavButtonIx].element
 
-                 );
 
-             }]
 
-         ], {
 
-             // Only run this module if we have map zoom on the chart
 
-             validate: function () {
 
-                 return (
 
-                     chart.mapZoom &&
 
-                     chart.mapNavButtons &&
 
-                     chart.mapNavButtons.length === 2
 
-                 );
 
-             },
 
-             // Make zoom buttons do their magic
 
-             init: function (direction) {
 
-                 var zoomIn = chart.mapNavButtons[0],
 
-                     zoomOut = chart.mapNavButtons[1],
 
-                     initialButton = direction > 0 ? zoomIn : zoomOut;
 
-                 chart.mapNavButtons.forEach(function (button, i) {
 
-                     button.element.setAttribute('tabindex', -1);
 
-                     button.element.setAttribute('role', 'button');
 
-                     button.element.setAttribute(
 
-                         'aria-label',
 
-                         chart.langFormat(
 
-                             'accessibility.mapZoom' + (i ? 'Out' : 'In'),
 
-                             { chart: chart }
 
-                         )
 
-                     );
 
-                 });
 
-                 chart.setFocusToElement(initialButton.box, initialButton);
 
-                 initialButton.setState(2);
 
-                 chart.focusedMapNavButtonIx = direction > 0 ? 0 : 1;
 
-             }
 
-         }),
 
-         // Highstock range selector (minus input boxes)
 
-         navModuleFactory('rangeSelector', [
 
-             // Left/Right/Up/Down
 
-             [[37, 39, 38, 40], function (keyCode) {
 
-                 var direction = (keyCode === 37 || keyCode === 38) ? -1 : 1;
 
-                 // Try to highlight next/prev button
 
-                 if (
 
-                     !chart.highlightRangeSelectorButton(
 
-                         chart.highlightedRangeSelectorItemIx + direction
 
-                     )
 
-                 ) {
 
-                     return this.move(direction);
 
-                 }
 
-             }],
 
-             // Enter/Spacebar
 
-             [[13, 32], function () {
 
-                 // Don't allow click if button used to be disabled
 
-                 if (chart.oldRangeSelectorItemState !== 3) {
 
-                     fakeClickEvent(
 
-                         chart.rangeSelector.buttons[
 
-                             chart.highlightedRangeSelectorItemIx
 
-                         ].element
 
-                     );
 
-                 }
 
-             }]
 
-         ], {
 
-             // Only run this module if we have range selector
 
-             validate: function () {
 
-                 return (
 
-                     chart.rangeSelector &&
 
-                     chart.rangeSelector.buttons &&
 
-                     chart.rangeSelector.buttons.length
 
-                 );
 
-             },
 
-             // Make elements focusable and accessible
 
-             init: function (direction) {
 
-                 chart.rangeSelector.buttons.forEach(function (button) {
 
-                     button.element.setAttribute('tabindex', '-1');
 
-                     button.element.setAttribute('role', 'button');
 
-                     button.element.setAttribute(
 
-                         'aria-label',
 
-                         chart.langFormat(
 
-                             'accessibility.rangeSelectorButton',
 
-                             {
 
-                                 chart: chart,
 
-                                 buttonText: button.text && button.text.textStr
 
-                             }
 
-                         )
 
-                     );
 
-                 });
 
-                 // Focus first/last button
 
-                 chart.highlightRangeSelectorButton(
 
-                     direction > 0 ? 0 : chart.rangeSelector.buttons.length - 1
 
-                 );
 
-             }
 
-         }),
 
-         // Highstock range selector, input boxes
 
-         navModuleFactory('rangeSelectorInput', [
 
-             // Tab/Up/Down
 
-             [[9, 38, 40], function (keyCode, e) {
 
-                 var direction =
 
-                     (keyCode === 9 && e.shiftKey || keyCode === 38) ? -1 : 1,
 
-                     newIx = chart.highlightedInputRangeIx =
 
-                         chart.highlightedInputRangeIx + direction;
 
-                 // Try to highlight next/prev item in list.
 
-                 if (newIx > 1 || newIx < 0) { // Out of range
 
-                     return this.move(direction);
 
-                 }
 
-                 chart.rangeSelector[newIx ? 'maxInput' : 'minInput'].focus();
 
-             }]
 
-         ], {
 
-             // Only run if we have range selector with input boxes
 
-             validate: function () {
 
-                 var inputVisible = (
 
-                     chart.rangeSelector &&
 
-                     chart.rangeSelector.inputGroup &&
 
-                     chart.rangeSelector.inputGroup.element
 
-                         .getAttribute('visibility') !== 'hidden'
 
-                 );
 
-                 return (
 
-                     inputVisible &&
 
-                     chart.options.rangeSelector.inputEnabled !== false &&
 
-                     chart.rangeSelector.minInput &&
 
-                     chart.rangeSelector.maxInput
 
-                 );
 
-             },
 
-             // Highlight first/last input box
 
-             init: function (direction) {
 
-                 chart.highlightedInputRangeIx = direction > 0 ? 0 : 1;
 
-                 chart.rangeSelector[
 
-                     chart.highlightedInputRangeIx ? 'maxInput' : 'minInput'
 
-                 ].focus();
 
-             }
 
-         }),
 
-         // Legend navigation
 
-         navModuleFactory('legend', [
 
-             // Left/Right/Up/Down
 
-             [[37, 39, 38, 40], function (keyCode) {
 
-                 var direction = (keyCode === 37 || keyCode === 38) ? -1 : 1;
 
-                 // Try to highlight next/prev legend item
 
-                 if (!chart.highlightLegendItem(
 
-                     chart.highlightedLegendItemIx + direction
 
-                 ) && chart.legend.allItems.length > 1) {
 
-                     // Wrap around if more than 1 item
 
-                     this.init(direction);
 
-                 }
 
-             }],
 
-             // Enter/Spacebar
 
-             [[13, 32], function () {
 
-                 var legendElement = chart.legend.allItems[
 
-                     chart.highlightedLegendItemIx
 
-                 ].legendItem.element;
 
-                 fakeClickEvent(
 
-                     !chart.legend.options.useHTML ? // #8561
 
-                         legendElement.parentNode : legendElement
 
-                 );
 
-             }]
 
-         ], {
 
-             // Only run this module if we have at least one legend - wait for
 
-             // it - item. Don't run if the legend is populated by a colorAxis.
 
-             // Don't run if legend navigation is disabled.
 
-             validate: function () {
 
-                 return chart.legend && chart.legend.allItems &&
 
-                     chart.legend.display &&
 
-                     !(chart.colorAxis && chart.colorAxis.length) &&
 
-                     (chart.options.legend &&
 
-                     chart.options.legend.keyboardNavigation &&
 
-                     chart.options.legend.keyboardNavigation.enabled) !== false;
 
-             },
 
-             // Make elements focusable and accessible
 
-             init: function (direction) {
 
-                 chart.legend.allItems.forEach(function (item) {
 
-                     item.legendGroup.element.setAttribute('tabindex', '-1');
 
-                     item.legendGroup.element.setAttribute('role', 'button');
 
-                     item.legendGroup.element.setAttribute(
 
-                         'aria-label',
 
-                         chart.langFormat(
 
-                             'accessibility.legendItem',
 
-                             {
 
-                                 chart: chart,
 
-                                 itemName: stripTags(item.name)
 
-                             }
 
-                         )
 
-                     );
 
-                 });
 
-                 // Focus first/last item
 
-                 chart.highlightLegendItem(
 
-                     direction > 0 ? 0 : chart.legend.allItems.length - 1
 
-                 );
 
-             }
 
-         })
 
-     ];
 
- };
 
- /**
 
-  * Add exit anchor to the chart. We use this to move focus out of chart whenever
 
-  * we want, by setting focus to this div and not preventing the default tab
 
-  * action. We also use this when users come back into the chart by tabbing back,
 
-  * in order to navigate from the end of the chart.
 
-  *
 
-  * @private
 
-  * @function Highcharts.Chart#addExitAnchor
 
-  *
 
-  * @return {Function}
 
-  *         Returns the unbind function for the exit anchor's event handler.
 
-  */
 
- H.Chart.prototype.addExitAnchor = function () {
 
-     var chart = this;
 
-     chart.tabExitAnchor = doc.createElement('div');
 
-     chart.tabExitAnchor.setAttribute('tabindex', '0');
 
-     // Hide exit anchor
 
-     merge(true, chart.tabExitAnchor.style, {
 
-         position: 'absolute',
 
-         left: '-9999px',
 
-         top: 'auto',
 
-         width: '1px',
 
-         height: '1px',
 
-         overflow: 'hidden'
 
-     });
 
-     chart.renderTo.appendChild(chart.tabExitAnchor);
 
-     return addEvent(
 
-         chart.tabExitAnchor,
 
-         'focus',
 
-         function (ev) {
 
-             var e = ev || win.event,
 
-                 curModule;
 
-             // If focusing and we are exiting, do nothing once.
 
-             if (!chart.exiting) {
 
-                 // Not exiting, means we are coming in backwards
 
-                 chart.renderTo.focus();
 
-                 e.preventDefault();
 
-                 // Move to last valid keyboard nav module
 
-                 // Note the we don't run it, just set the index
 
-                 chart.keyboardNavigationModuleIndex =
 
-                     chart.keyboardNavigationModules.length - 1;
 
-                 curModule = chart.keyboardNavigationModules[
 
-                     chart.keyboardNavigationModuleIndex
 
-                 ];
 
-                 // Validate the module
 
-                 if (curModule.validate && !curModule.validate()) {
 
-                     // Invalid.
 
-                     // Move inits next valid module in direction
 
-                     curModule.move(-1);
 
-                 } else {
 
-                     // We have a valid module, init it
 
-                     curModule.init(-1);
 
-                 }
 
-             } else {
 
-                 // Don't skip the next focus, we only skip once.
 
-                 chart.exiting = false;
 
-             }
 
-         }
 
-     );
 
- };
 
- /**
 
-  * Clear the chart and reset the navigation state.
 
-  *
 
-  * @private
 
-  * @function Highcharts.Chart#resetKeyboardNavigation
 
-  */
 
- H.Chart.prototype.resetKeyboardNavigation = function () {
 
-     var chart = this,
 
-         curMod = (
 
-             chart.keyboardNavigationModules &&
 
-             chart.keyboardNavigationModules[
 
-                 chart.keyboardNavigationModuleIndex || 0
 
-             ]
 
-         );
 
-     if (curMod && curMod.terminate) {
 
-         curMod.terminate();
 
-     }
 
-     if (chart.focusElement) {
 
-         chart.focusElement.removeFocusBorder();
 
-     }
 
-     chart.keyboardNavigationModuleIndex = 0;
 
-     chart.keyboardReset = true;
 
- };
 
- // On destroy, we need to clean up the focus border and the state.
 
- H.addEvent(H.Series, 'destroy', function () {
 
-     var chart = this.chart;
 
-     if (chart.highlightedPoint && chart.highlightedPoint.series === this) {
 
-         delete chart.highlightedPoint;
 
-         if (chart.focusElement) {
 
-             chart.focusElement.removeFocusBorder();
 
-         }
 
-     }
 
- });
 
- // Add keyboard navigation events on chart load.
 
- H.Chart.prototype.callbacks.push(function (chart) {
 
-     var a11yOptions = chart.options.accessibility;
 
-     if (a11yOptions.enabled && a11yOptions.keyboardNavigation.enabled) {
 
-         // Init nav modules. We start at the first module, and as the user
 
-         // navigates through the chart the index will increase to use different
 
-         // handler modules.
 
-         chart.addKeyboardNavigationModules();
 
-         chart.keyboardNavigationModuleIndex = 0;
 
-         // Make chart container reachable by tab
 
-         if (
 
-             chart.container.hasAttribute &&
 
-             !chart.container.hasAttribute('tabIndex')
 
-         ) {
 
-             chart.container.setAttribute('tabindex', '0');
 
-         }
 
-         // Add tab exit anchor
 
-         if (!chart.tabExitAnchor) {
 
-             chart.unbindExitAnchorFocus = chart.addExitAnchor();
 
-         }
 
-         // Handle keyboard events by routing them to active keyboard nav module
 
-         chart.unbindKeydownHandler = addEvent(chart.renderTo, 'keydown',
 
-             function (ev) {
 
-                 var e = ev || win.event,
 
-                     curNavModule = chart.keyboardNavigationModules[
 
-                         chart.keyboardNavigationModuleIndex
 
-                     ];
 
-                 chart.keyboardReset = false;
 
-                 // If there is a nav module for the current index, run it.
 
-                 // Otherwise, we are outside of the chart in some direction.
 
-                 if (curNavModule) {
 
-                     if (curNavModule.run(e)) {
 
-                         // Successfully handled this key event, stop default
 
-                         e.preventDefault();
 
-                     }
 
-                 }
 
-             });
 
-         // Reset chart navigation state if we click outside the chart and it's
 
-         // not already reset
 
-         chart.unbindBlurHandler = addEvent(doc, 'mouseup', function () {
 
-             if (
 
-                 !chart.keyboardReset &&
 
-                 !(chart.pointer && chart.pointer.chartPosition)
 
-             ) {
 
-                 chart.resetKeyboardNavigation();
 
-             }
 
-         });
 
-         // Add cleanup handlers
 
-         addEvent(chart, 'destroy', function () {
 
-             chart.resetKeyboardNavigation();
 
-             if (chart.unbindExitAnchorFocus && chart.tabExitAnchor) {
 
-                 chart.unbindExitAnchorFocus();
 
-             }
 
-             if (chart.unbindKeydownHandler && chart.renderTo) {
 
-                 chart.unbindKeydownHandler();
 
-             }
 
-             if (chart.unbindBlurHandler) {
 
-                 chart.unbindBlurHandler();
 
-             }
 
-         });
 
-     }
 
- });
 
 
  |