text: /* *
   *
   *  (c) 2009-2021 Øystein Moseng
   *
   *  Extend SVG and Chart classes with focus border capabilities.
   *
   *  License: www.highcharts.com/license
   *
   *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
   *
   * */
  'use strict';
  import H from '../Core/Globals.js';
  import SVGElement from '../Core/Renderer/SVG/SVGElement.js';
  import SVGLabel from '../Core/Renderer/SVG/SVGLabel.js';
  import U from '../Core/Utilities.js';
  var addEvent = U.addEvent, extend = U.extend, pick = U.pick;
  /* eslint-disable no-invalid-this, valid-jsdoc */
  // Attributes that trigger a focus border update
  var svgElementBorderUpdateTriggers = [
      'x', 'y', 'transform', 'width', 'height', 'r', 'd', 'stroke-width'
  ];
  /**
   * Add hook to destroy focus border if SVG element is destroyed, unless
   * hook already exists.
   * @private
   * @param el Element to add destroy hook to
   */
  function addDestroyFocusBorderHook(el) {
      if (el.focusBorderDestroyHook) {
          return;
      }
      var origDestroy = el.destroy;
      el.destroy = function () {
          if (el.focusBorder && el.focusBorder.destroy) {
              el.focusBorder.destroy();
          }
          return origDestroy.apply(el, arguments);
      };
      el.focusBorderDestroyHook = origDestroy;
  }
  /**
   * Remove hook from SVG element added by addDestroyFocusBorderHook, if
   * existing.
   * @private
   * @param el Element to remove destroy hook from
   */
  function removeDestroyFocusBorderHook(el) {
      if (!el.focusBorderDestroyHook) {
          return;
      }
      el.destroy = el.focusBorderDestroyHook;
      delete el.focusBorderDestroyHook;
  }
  /**
   * Add hooks to update the focus border of an element when the element
   * size/position is updated, unless already added.
   * @private
   * @param el Element to add update hooks to
   * @param updateParams Parameters to pass through to addFocusBorder when updating.
   */
  function addUpdateFocusBorderHooks(el) {
      var updateParams = [];
      for (var _i = 1; _i < arguments.length; _i++) {
          updateParams[_i - 1] = arguments[_i];
      }
      if (el.focusBorderUpdateHooks) {
          return;
      }
      el.focusBorderUpdateHooks = {};
      svgElementBorderUpdateTriggers.forEach(function (trigger) {
          var setterKey = trigger + 'Setter';
          var origSetter = el[setterKey] || el._defaultSetter;
          el.focusBorderUpdateHooks[setterKey] = origSetter;
          el[setterKey] = function () {
              var ret = origSetter.apply(el, arguments);
              el.addFocusBorder.apply(el, updateParams);
              return ret;
          };
      });
  }
  /**
   * Remove hooks from SVG element added by addUpdateFocusBorderHooks, if
   * existing.
   * @private
   * @param el Element to remove update hooks from
   */
  function removeUpdateFocusBorderHooks(el) {
      if (!el.focusBorderUpdateHooks) {
          return;
      }
      Object.keys(el.focusBorderUpdateHooks).forEach(function (setterKey) {
          var origSetter = el.focusBorderUpdateHooks[setterKey];
          if (origSetter === el._defaultSetter) {
              delete el[setterKey];
          }
          else {
              el[setterKey] = origSetter;
          }
      });
      delete el.focusBorderUpdateHooks;
  }
  /*
   * Add focus border functionality to SVGElements. Draws a new rect on top of
   * element around its bounding box. This is used by multiple components.
   */
  extend(SVGElement.prototype, {
      /**
       * @private
       * @function highcharts.SVGElement#addFocusBorder
       *
       * @param {number} margin
       *
       * @param {SVGAttributes} attribs
       */
      addFocusBorder: function (margin, attribs) {
          // Allow updating by just adding new border
          if (this.focusBorder) {
              this.removeFocusBorder();
          }
          // Add the border rect
          var bb = this.getBBox(), pad = pick(margin, 3);
          bb.x += this.translateX ? this.translateX : 0;
          bb.y += this.translateY ? this.translateY : 0;
          var borderPosX = bb.x - pad, borderPosY = bb.y - pad, borderWidth = bb.width + 2 * pad, borderHeight = bb.height + 2 * pad;
          // For text elements, apply x and y offset, #11397.
          /**
           * @private
           * @function
           *
           * @param {Highcharts.SVGElement} text
           *
           * @return {TextAnchorCorrectionObject}
           */
          function getTextAnchorCorrection(text) {
              var posXCorrection = 0, posYCorrection = 0;
              if (text.attr('text-anchor') === 'middle') {
                  posXCorrection = H.isFirefox && text.rotation ? 0.25 : 0.5;
                  posYCorrection = H.isFirefox && !text.rotation ? 0.75 : 0.5;
              }
              else if (!text.rotation) {
                  posYCorrection = 0.75;
              }
              else {
                  posXCorrection = 0.25;
              }
              return {
                  x: posXCorrection,
                  y: posYCorrection
              };
          }
          var isLabel = this instanceof SVGLabel;
          if (this.element.nodeName === 'text' || isLabel) {
              var isRotated = !!this.rotation;
              var correction = !isLabel ? getTextAnchorCorrection(this) :
                  {
                      x: isRotated ? 1 : 0,
                      y: 0
                  };
              var attrX = +this.attr('x');
              var attrY = +this.attr('y');
              if (!isNaN(attrX)) {
                  borderPosX = attrX - (bb.width * correction.x) - pad;
              }
              if (!isNaN(attrY)) {
                  borderPosY = attrY - (bb.height * correction.y) - pad;
              }
              if (isLabel && isRotated) {
                  var temp = borderWidth;
                  borderWidth = borderHeight;
                  borderHeight = temp;
                  if (!isNaN(attrX)) {
                      borderPosX = attrX - (bb.height * correction.x) - pad;
                  }
                  if (!isNaN(attrY)) {
                      borderPosY = attrY - (bb.width * correction.y) - pad;
                  }
              }
          }
          this.focusBorder = this.renderer.rect(borderPosX, borderPosY, borderWidth, borderHeight, parseInt((attribs && attribs.r || 0).toString(), 10))
              .addClass('highcharts-focus-border')
              .attr({
              zIndex: 99
          })
              .add(this.parentGroup);
          if (!this.renderer.styledMode) {
              this.focusBorder.attr({
                  stroke: attribs && attribs.stroke,
                  'stroke-width': attribs && attribs.strokeWidth
              });
          }
          addUpdateFocusBorderHooks(this, margin, attribs);
          addDestroyFocusBorderHook(this);
      },
      /**
       * @private
       * @function highcharts.SVGElement#removeFocusBorder
       */
      removeFocusBorder: function () {
          removeUpdateFocusBorderHooks(this);
          removeDestroyFocusBorderHook(this);
          if (this.focusBorder) {
              this.focusBorder.destroy();
              delete this.focusBorder;
          }
      }
  });
  /**
   * Redraws the focus border on the currently focused element.
   *
   * @private
   * @function highcharts.Chart#renderFocusBorder
   */
  H.Chart.prototype.renderFocusBorder = function () {
      var focusElement = this.focusElement, focusBorderOptions = this.options.accessibility.keyboardNavigation.focusBorder;
      if (focusElement) {
          focusElement.removeFocusBorder();
          if (focusBorderOptions.enabled) {
              focusElement.addFocusBorder(focusBorderOptions.margin, {
                  stroke: focusBorderOptions.style.color,
                  strokeWidth: focusBorderOptions.style.lineWidth,
                  r: focusBorderOptions.style.borderRadius
              });
          }
      }
  };
  /**
   * Set chart's focus to an SVGElement. Calls focus() on it, and draws the focus
   * border. This is used by multiple components.
   *
   * @private
   * @function highcharts.Chart#setFocusToElement
   *
   * @param {Highcharts.SVGElement} svgElement
   *        Element to draw the border around.
   *
   * @param {SVGDOMElement|HTMLDOMElement} [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.element;
      // Set browser focus if possible
      if (browserFocusElement &&
          browserFocusElement.focus) {
          // If there is no focusin-listener, add one to work around Edge issue
          // where Narrator is not reading out points despite calling focus().
          if (!(browserFocusElement.hcEvents &&
              browserFocusElement.hcEvents.focusin)) {
              addEvent(browserFocusElement, 'focusin', function () { });
          }
          browserFocusElement.focus();
          // Hide default focus ring
          if (focusBorderOptions.hideBrowserFocusOutline) {
              browserFocusElement.style.outline = 'none';
          }
      }
      if (this.focusElement) {
          this.focusElement.removeFocusBorder();
      }
      this.focusElement = svgElement;
      this.renderFocusBorder();
  };
  