[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/media/plg_system_jooa11y/js/ -> jooa11y.js (source)

   1  var top = 'top';
   2  var bottom = 'bottom';
   3  var right = 'right';
   4  var left = 'left';
   5  var auto = 'auto';
   6  var basePlacements = [top, bottom, right, left];
   7  var start = 'start';
   8  var end = 'end';
   9  var clippingParents = 'clippingParents';
  10  var viewport = 'viewport';
  11  var popper = 'popper';
  12  var reference = 'reference';
  13  var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) {
  14    return acc.concat([placement + "-" + start, placement + "-" + end]);
  15  }, []);
  16  var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) {
  17    return acc.concat([placement, placement + "-" + start, placement + "-" + end]);
  18  }, []); // modifiers that need to read the DOM
  19  
  20  var beforeRead = 'beforeRead';
  21  var read = 'read';
  22  var afterRead = 'afterRead'; // pure-logic modifiers
  23  
  24  var beforeMain = 'beforeMain';
  25  var main = 'main';
  26  var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)
  27  
  28  var beforeWrite = 'beforeWrite';
  29  var write = 'write';
  30  var afterWrite = 'afterWrite';
  31  var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];
  32  
  33  function getNodeName(element) {
  34    return element ? (element.nodeName || '').toLowerCase() : null;
  35  }
  36  
  37  function getWindow(node) {
  38    if (node == null) {
  39      return window;
  40    }
  41  
  42    if (node.toString() !== '[object Window]') {
  43      var ownerDocument = node.ownerDocument;
  44      return ownerDocument ? ownerDocument.defaultView || window : window;
  45    }
  46  
  47    return node;
  48  }
  49  
  50  function isElement$1(node) {
  51    var OwnElement = getWindow(node).Element;
  52    return node instanceof OwnElement || node instanceof Element;
  53  }
  54  
  55  function isHTMLElement(node) {
  56    var OwnElement = getWindow(node).HTMLElement;
  57    return node instanceof OwnElement || node instanceof HTMLElement;
  58  }
  59  
  60  function isShadowRoot(node) {
  61    // IE 11 has no ShadowRoot
  62    if (typeof ShadowRoot === 'undefined') {
  63      return false;
  64    }
  65  
  66    var OwnElement = getWindow(node).ShadowRoot;
  67    return node instanceof OwnElement || node instanceof ShadowRoot;
  68  } // and applies them to the HTMLElements such as popper and arrow
  69  
  70  
  71  function applyStyles(_ref) {
  72    var state = _ref.state;
  73    Object.keys(state.elements).forEach(function (name) {
  74      var style = state.styles[name] || {};
  75      var attributes = state.attributes[name] || {};
  76      var element = state.elements[name]; // arrow is optional + virtual elements
  77  
  78      if (!isHTMLElement(element) || !getNodeName(element)) {
  79        return;
  80      } // Flow doesn't support to extend this property, but it's the most
  81      // effective way to apply styles to an HTMLElement
  82      // $FlowFixMe[cannot-write]
  83  
  84  
  85      Object.assign(element.style, style);
  86      Object.keys(attributes).forEach(function (name) {
  87        var value = attributes[name];
  88  
  89        if (value === false) {
  90          element.removeAttribute(name);
  91        } else {
  92          element.setAttribute(name, value === true ? '' : value);
  93        }
  94      });
  95    });
  96  }
  97  
  98  function effect$2(_ref2) {
  99    var state = _ref2.state;
 100    var initialStyles = {
 101      popper: {
 102        position: state.options.strategy,
 103        left: '0',
 104        top: '0',
 105        margin: '0'
 106      },
 107      arrow: {
 108        position: 'absolute'
 109      },
 110      reference: {}
 111    };
 112    Object.assign(state.elements.popper.style, initialStyles.popper);
 113    state.styles = initialStyles;
 114  
 115    if (state.elements.arrow) {
 116      Object.assign(state.elements.arrow.style, initialStyles.arrow);
 117    }
 118  
 119    return function () {
 120      Object.keys(state.elements).forEach(function (name) {
 121        var element = state.elements[name];
 122        var attributes = state.attributes[name] || {};
 123        var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them
 124  
 125        var style = styleProperties.reduce(function (style, property) {
 126          style[property] = '';
 127          return style;
 128        }, {}); // arrow is optional + virtual elements
 129  
 130        if (!isHTMLElement(element) || !getNodeName(element)) {
 131          return;
 132        }
 133  
 134        Object.assign(element.style, style);
 135        Object.keys(attributes).forEach(function (attribute) {
 136          element.removeAttribute(attribute);
 137        });
 138      });
 139    };
 140  } // eslint-disable-next-line import/no-unused-modules
 141  
 142  
 143  var applyStyles$1 = {
 144    name: 'applyStyles',
 145    enabled: true,
 146    phase: 'write',
 147    fn: applyStyles,
 148    effect: effect$2,
 149    requires: ['computeStyles']
 150  };
 151  
 152  function getBasePlacement$1(placement) {
 153    return placement.split('-')[0];
 154  }
 155  
 156  var max = Math.max;
 157  var min = Math.min;
 158  var round = Math.round;
 159  
 160  function getBoundingClientRect(element, includeScale) {
 161    if (includeScale === void 0) {
 162      includeScale = false;
 163    }
 164  
 165    var rect = element.getBoundingClientRect();
 166    var scaleX = 1;
 167    var scaleY = 1;
 168  
 169    if (isHTMLElement(element) && includeScale) {
 170      var offsetHeight = element.offsetHeight;
 171      var offsetWidth = element.offsetWidth; // Do not attempt to divide by 0, otherwise we get `Infinity` as scale
 172      // Fallback to 1 in case both values are `0`
 173  
 174      if (offsetWidth > 0) {
 175        scaleX = round(rect.width) / offsetWidth || 1;
 176      }
 177  
 178      if (offsetHeight > 0) {
 179        scaleY = round(rect.height) / offsetHeight || 1;
 180      }
 181    }
 182  
 183    return {
 184      width: rect.width / scaleX,
 185      height: rect.height / scaleY,
 186      top: rect.top / scaleY,
 187      right: rect.right / scaleX,
 188      bottom: rect.bottom / scaleY,
 189      left: rect.left / scaleX,
 190      x: rect.left / scaleX,
 191      y: rect.top / scaleY
 192    };
 193  } // means it doesn't take into account transforms.
 194  
 195  
 196  function getLayoutRect(element) {
 197    var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed.
 198    // Fixes https://github.com/popperjs/popper-core/issues/1223
 199  
 200    var width = element.offsetWidth;
 201    var height = element.offsetHeight;
 202  
 203    if (Math.abs(clientRect.width - width) <= 1) {
 204      width = clientRect.width;
 205    }
 206  
 207    if (Math.abs(clientRect.height - height) <= 1) {
 208      height = clientRect.height;
 209    }
 210  
 211    return {
 212      x: element.offsetLeft,
 213      y: element.offsetTop,
 214      width: width,
 215      height: height
 216    };
 217  }
 218  
 219  function contains(parent, child) {
 220    var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method
 221  
 222    if (parent.contains(child)) {
 223      return true;
 224    } // then fallback to custom implementation with Shadow DOM support
 225    else if (rootNode && isShadowRoot(rootNode)) {
 226      var next = child;
 227  
 228      do {
 229        if (next && parent.isSameNode(next)) {
 230          return true;
 231        } // $FlowFixMe[prop-missing]: need a better way to handle this...
 232  
 233  
 234        next = next.parentNode || next.host;
 235      } while (next);
 236    } // Give up, the result is false
 237  
 238  
 239    return false;
 240  }
 241  
 242  function getComputedStyle$1(element) {
 243    return getWindow(element).getComputedStyle(element);
 244  }
 245  
 246  function isTableElement(element) {
 247    return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;
 248  }
 249  
 250  function getDocumentElement(element) {
 251    // $FlowFixMe[incompatible-return]: assume body is always available
 252    return ((isElement$1(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]
 253    element.document) || window.document).documentElement;
 254  }
 255  
 256  function getParentNode(element) {
 257    if (getNodeName(element) === 'html') {
 258      return element;
 259    }
 260  
 261    return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle
 262      // $FlowFixMe[incompatible-return]
 263      // $FlowFixMe[prop-missing]
 264      element.assignedSlot || // step into the shadow DOM of the parent of a slotted node
 265      element.parentNode || ( // DOM Element detected
 266      isShadowRoot(element) ? element.host : null) || // ShadowRoot detected
 267      // $FlowFixMe[incompatible-call]: HTMLElement is a Node
 268      getDocumentElement(element) // fallback
 269  
 270    );
 271  }
 272  
 273  function getTrueOffsetParent(element) {
 274    if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837
 275    getComputedStyle$1(element).position === 'fixed') {
 276      return null;
 277    }
 278  
 279    return element.offsetParent;
 280  } // `.offsetParent` reports `null` for fixed elements, while absolute elements
 281  // return the containing block
 282  
 283  
 284  function getContainingBlock(element) {
 285    var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') !== -1;
 286    var isIE = navigator.userAgent.indexOf('Trident') !== -1;
 287  
 288    if (isIE && isHTMLElement(element)) {
 289      // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport
 290      var elementCss = getComputedStyle$1(element);
 291  
 292      if (elementCss.position === 'fixed') {
 293        return null;
 294      }
 295    }
 296  
 297    var currentNode = getParentNode(element);
 298  
 299    while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {
 300      var css = getComputedStyle$1(currentNode); // This is non-exhaustive but covers the most common CSS properties that
 301      // create a containing block.
 302      // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
 303  
 304      if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') {
 305        return currentNode;
 306      } else {
 307        currentNode = currentNode.parentNode;
 308      }
 309    }
 310  
 311    return null;
 312  } // Gets the closest ancestor positioned element. Handles some edge cases,
 313  // such as table ancestors and cross browser bugs.
 314  
 315  
 316  function getOffsetParent(element) {
 317    var window = getWindow(element);
 318    var offsetParent = getTrueOffsetParent(element);
 319  
 320    while (offsetParent && isTableElement(offsetParent) && getComputedStyle$1(offsetParent).position === 'static') {
 321      offsetParent = getTrueOffsetParent(offsetParent);
 322    }
 323  
 324    if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle$1(offsetParent).position === 'static')) {
 325      return window;
 326    }
 327  
 328    return offsetParent || getContainingBlock(element) || window;
 329  }
 330  
 331  function getMainAxisFromPlacement(placement) {
 332    return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y';
 333  }
 334  
 335  function within(min$1, value, max$1) {
 336    return max(min$1, min(value, max$1));
 337  }
 338  
 339  function withinMaxClamp(min, value, max) {
 340    var v = within(min, value, max);
 341    return v > max ? max : v;
 342  }
 343  
 344  function getFreshSideObject() {
 345    return {
 346      top: 0,
 347      right: 0,
 348      bottom: 0,
 349      left: 0
 350    };
 351  }
 352  
 353  function mergePaddingObject(paddingObject) {
 354    return Object.assign({}, getFreshSideObject(), paddingObject);
 355  }
 356  
 357  function expandToHashMap(value, keys) {
 358    return keys.reduce(function (hashMap, key) {
 359      hashMap[key] = value;
 360      return hashMap;
 361    }, {});
 362  }
 363  
 364  var toPaddingObject = function toPaddingObject(padding, state) {
 365    padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, {
 366      placement: state.placement
 367    })) : padding;
 368    return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));
 369  };
 370  
 371  function arrow(_ref) {
 372    var _state$modifiersData$;
 373  
 374    var state = _ref.state,
 375        name = _ref.name,
 376        options = _ref.options;
 377    var arrowElement = state.elements.arrow;
 378    var popperOffsets = state.modifiersData.popperOffsets;
 379    var basePlacement = getBasePlacement$1(state.placement);
 380    var axis = getMainAxisFromPlacement(basePlacement);
 381    var isVertical = [left, right].indexOf(basePlacement) >= 0;
 382    var len = isVertical ? 'height' : 'width';
 383  
 384    if (!arrowElement || !popperOffsets) {
 385      return;
 386    }
 387  
 388    var paddingObject = toPaddingObject(options.padding, state);
 389    var arrowRect = getLayoutRect(arrowElement);
 390    var minProp = axis === 'y' ? top : left;
 391    var maxProp = axis === 'y' ? bottom : right;
 392    var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len];
 393    var startDiff = popperOffsets[axis] - state.rects.reference[axis];
 394    var arrowOffsetParent = getOffsetParent(arrowElement);
 395    var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;
 396    var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is
 397    // outside of the popper bounds
 398  
 399    var min = paddingObject[minProp];
 400    var max = clientSize - arrowRect[len] - paddingObject[maxProp];
 401    var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference;
 402    var offset = within(min, center, max); // Prevents breaking syntax highlighting...
 403  
 404    var axisProp = axis;
 405    state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$);
 406  }
 407  
 408  function effect$1(_ref2) {
 409    var state = _ref2.state,
 410        options = _ref2.options;
 411    var _options$element = options.element,
 412        arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element;
 413  
 414    if (arrowElement == null) {
 415      return;
 416    } // CSS selector
 417  
 418  
 419    if (typeof arrowElement === 'string') {
 420      arrowElement = state.elements.popper.querySelector(arrowElement);
 421  
 422      if (!arrowElement) {
 423        return;
 424      }
 425    }
 426  
 427    if (!contains(state.elements.popper, arrowElement)) {
 428      return;
 429    }
 430  
 431    state.elements.arrow = arrowElement;
 432  } // eslint-disable-next-line import/no-unused-modules
 433  
 434  
 435  var arrow$1 = {
 436    name: 'arrow',
 437    enabled: true,
 438    phase: 'main',
 439    fn: arrow,
 440    effect: effect$1,
 441    requires: ['popperOffsets'],
 442    requiresIfExists: ['preventOverflow']
 443  };
 444  
 445  function getVariation(placement) {
 446    return placement.split('-')[1];
 447  }
 448  
 449  var unsetSides = {
 450    top: 'auto',
 451    right: 'auto',
 452    bottom: 'auto',
 453    left: 'auto'
 454  }; // Round the offsets to the nearest suitable subpixel based on the DPR.
 455  // Zooming can change the DPR, but it seems to report a value that will
 456  // cleanly divide the values into the appropriate subpixels.
 457  
 458  function roundOffsetsByDPR(_ref) {
 459    var x = _ref.x,
 460        y = _ref.y;
 461    var win = window;
 462    var dpr = win.devicePixelRatio || 1;
 463    return {
 464      x: round(x * dpr) / dpr || 0,
 465      y: round(y * dpr) / dpr || 0
 466    };
 467  }
 468  
 469  function mapToStyles(_ref2) {
 470    var _Object$assign2;
 471  
 472    var popper = _ref2.popper,
 473        popperRect = _ref2.popperRect,
 474        placement = _ref2.placement,
 475        variation = _ref2.variation,
 476        offsets = _ref2.offsets,
 477        position = _ref2.position,
 478        gpuAcceleration = _ref2.gpuAcceleration,
 479        adaptive = _ref2.adaptive,
 480        roundOffsets = _ref2.roundOffsets,
 481        isFixed = _ref2.isFixed;
 482    var _offsets$x = offsets.x,
 483        x = _offsets$x === void 0 ? 0 : _offsets$x,
 484        _offsets$y = offsets.y,
 485        y = _offsets$y === void 0 ? 0 : _offsets$y;
 486  
 487    var _ref3 = typeof roundOffsets === 'function' ? roundOffsets({
 488      x: x,
 489      y: y
 490    }) : {
 491      x: x,
 492      y: y
 493    };
 494  
 495    x = _ref3.x;
 496    y = _ref3.y;
 497    var hasX = offsets.hasOwnProperty('x');
 498    var hasY = offsets.hasOwnProperty('y');
 499    var sideX = left;
 500    var sideY = top;
 501    var win = window;
 502  
 503    if (adaptive) {
 504      var offsetParent = getOffsetParent(popper);
 505      var heightProp = 'clientHeight';
 506      var widthProp = 'clientWidth';
 507  
 508      if (offsetParent === getWindow(popper)) {
 509        offsetParent = getDocumentElement(popper);
 510  
 511        if (getComputedStyle$1(offsetParent).position !== 'static' && position === 'absolute') {
 512          heightProp = 'scrollHeight';
 513          widthProp = 'scrollWidth';
 514        }
 515      } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it
 516  
 517  
 518      offsetParent = offsetParent;
 519  
 520      if (placement === top || (placement === left || placement === right) && variation === end) {
 521        sideY = bottom;
 522        var offsetY = isFixed && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing]
 523        offsetParent[heightProp];
 524        y -= offsetY - popperRect.height;
 525        y *= gpuAcceleration ? 1 : -1;
 526      }
 527  
 528      if (placement === left || (placement === top || placement === bottom) && variation === end) {
 529        sideX = right;
 530        var offsetX = isFixed && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing]
 531        offsetParent[widthProp];
 532        x -= offsetX - popperRect.width;
 533        x *= gpuAcceleration ? 1 : -1;
 534      }
 535    }
 536  
 537    var commonStyles = Object.assign({
 538      position: position
 539    }, adaptive && unsetSides);
 540  
 541    var _ref4 = roundOffsets === true ? roundOffsetsByDPR({
 542      x: x,
 543      y: y
 544    }) : {
 545      x: x,
 546      y: y
 547    };
 548  
 549    x = _ref4.x;
 550    y = _ref4.y;
 551  
 552    if (gpuAcceleration) {
 553      var _Object$assign;
 554  
 555      return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? "translate(" + x + "px, " + y + "px)" : "translate3d(" + x + "px, " + y + "px, 0)", _Object$assign));
 556    }
 557  
 558    return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + "px" : '', _Object$assign2[sideX] = hasX ? x + "px" : '', _Object$assign2.transform = '', _Object$assign2));
 559  }
 560  
 561  function computeStyles(_ref5) {
 562    var state = _ref5.state,
 563        options = _ref5.options;
 564    var _options$gpuAccelerat = options.gpuAcceleration,
 565        gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat,
 566        _options$adaptive = options.adaptive,
 567        adaptive = _options$adaptive === void 0 ? true : _options$adaptive,
 568        _options$roundOffsets = options.roundOffsets,
 569        roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets;
 570    var commonStyles = {
 571      placement: getBasePlacement$1(state.placement),
 572      variation: getVariation(state.placement),
 573      popper: state.elements.popper,
 574      popperRect: state.rects.popper,
 575      gpuAcceleration: gpuAcceleration,
 576      isFixed: state.options.strategy === 'fixed'
 577    };
 578  
 579    if (state.modifiersData.popperOffsets != null) {
 580      state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, {
 581        offsets: state.modifiersData.popperOffsets,
 582        position: state.options.strategy,
 583        adaptive: adaptive,
 584        roundOffsets: roundOffsets
 585      })));
 586    }
 587  
 588    if (state.modifiersData.arrow != null) {
 589      state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, {
 590        offsets: state.modifiersData.arrow,
 591        position: 'absolute',
 592        adaptive: false,
 593        roundOffsets: roundOffsets
 594      })));
 595    }
 596  
 597    state.attributes.popper = Object.assign({}, state.attributes.popper, {
 598      'data-popper-placement': state.placement
 599    });
 600  } // eslint-disable-next-line import/no-unused-modules
 601  
 602  
 603  var computeStyles$1 = {
 604    name: 'computeStyles',
 605    enabled: true,
 606    phase: 'beforeWrite',
 607    fn: computeStyles,
 608    data: {}
 609  };
 610  var passive = {
 611    passive: true
 612  };
 613  
 614  function effect(_ref) {
 615    var state = _ref.state,
 616        instance = _ref.instance,
 617        options = _ref.options;
 618    var _options$scroll = options.scroll,
 619        scroll = _options$scroll === void 0 ? true : _options$scroll,
 620        _options$resize = options.resize,
 621        resize = _options$resize === void 0 ? true : _options$resize;
 622    var window = getWindow(state.elements.popper);
 623    var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper);
 624  
 625    if (scroll) {
 626      scrollParents.forEach(function (scrollParent) {
 627        scrollParent.addEventListener('scroll', instance.update, passive);
 628      });
 629    }
 630  
 631    if (resize) {
 632      window.addEventListener('resize', instance.update, passive);
 633    }
 634  
 635    return function () {
 636      if (scroll) {
 637        scrollParents.forEach(function (scrollParent) {
 638          scrollParent.removeEventListener('scroll', instance.update, passive);
 639        });
 640      }
 641  
 642      if (resize) {
 643        window.removeEventListener('resize', instance.update, passive);
 644      }
 645    };
 646  } // eslint-disable-next-line import/no-unused-modules
 647  
 648  
 649  var eventListeners = {
 650    name: 'eventListeners',
 651    enabled: true,
 652    phase: 'write',
 653    fn: function fn() {},
 654    effect: effect,
 655    data: {}
 656  };
 657  var hash$1 = {
 658    left: 'right',
 659    right: 'left',
 660    bottom: 'top',
 661    top: 'bottom'
 662  };
 663  
 664  function getOppositePlacement(placement) {
 665    return placement.replace(/left|right|bottom|top/g, function (matched) {
 666      return hash$1[matched];
 667    });
 668  }
 669  
 670  var hash = {
 671    start: 'end',
 672    end: 'start'
 673  };
 674  
 675  function getOppositeVariationPlacement(placement) {
 676    return placement.replace(/start|end/g, function (matched) {
 677      return hash[matched];
 678    });
 679  }
 680  
 681  function getWindowScroll(node) {
 682    var win = getWindow(node);
 683    var scrollLeft = win.pageXOffset;
 684    var scrollTop = win.pageYOffset;
 685    return {
 686      scrollLeft: scrollLeft,
 687      scrollTop: scrollTop
 688    };
 689  }
 690  
 691  function getWindowScrollBarX(element) {
 692    // If <html> has a CSS width greater than the viewport, then this will be
 693    // incorrect for RTL.
 694    // Popper 1 is broken in this case and never had a bug report so let's assume
 695    // it's not an issue. I don't think anyone ever specifies width on <html>
 696    // anyway.
 697    // Browsers where the left scrollbar doesn't cause an issue report `0` for
 698    // this (e.g. Edge 2019, IE11, Safari)
 699    return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;
 700  }
 701  
 702  function getViewportRect(element) {
 703    var win = getWindow(element);
 704    var html = getDocumentElement(element);
 705    var visualViewport = win.visualViewport;
 706    var width = html.clientWidth;
 707    var height = html.clientHeight;
 708    var x = 0;
 709    var y = 0; // NB: This isn't supported on iOS <= 12. If the keyboard is open, the popper
 710    // can be obscured underneath it.
 711    // Also, `html.clientHeight` adds the bottom bar height in Safari iOS, even
 712    // if it isn't open, so if this isn't available, the popper will be detected
 713    // to overflow the bottom of the screen too early.
 714  
 715    if (visualViewport) {
 716      width = visualViewport.width;
 717      height = visualViewport.height; // Uses Layout Viewport (like Chrome; Safari does not currently)
 718      // In Chrome, it returns a value very close to 0 (+/-) but contains rounding
 719      // errors due to floating point numbers, so we need to check precision.
 720      // Safari returns a number <= 0, usually < -1 when pinch-zoomed
 721      // Feature detection fails in mobile emulation mode in Chrome.
 722      // Math.abs(win.innerWidth / visualViewport.scale - visualViewport.width) <
 723      // 0.001
 724      // Fallback here: "Not Safari" userAgent
 725  
 726      if (!/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
 727        x = visualViewport.offsetLeft;
 728        y = visualViewport.offsetTop;
 729      }
 730    }
 731  
 732    return {
 733      width: width,
 734      height: height,
 735      x: x + getWindowScrollBarX(element),
 736      y: y
 737    };
 738  } // of the `<html>` and `<body>` rect bounds if horizontally scrollable
 739  
 740  
 741  function getDocumentRect(element) {
 742    var _element$ownerDocumen;
 743  
 744    var html = getDocumentElement(element);
 745    var winScroll = getWindowScroll(element);
 746    var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;
 747    var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);
 748    var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);
 749    var x = -winScroll.scrollLeft + getWindowScrollBarX(element);
 750    var y = -winScroll.scrollTop;
 751  
 752    if (getComputedStyle$1(body || html).direction === 'rtl') {
 753      x += max(html.clientWidth, body ? body.clientWidth : 0) - width;
 754    }
 755  
 756    return {
 757      width: width,
 758      height: height,
 759      x: x,
 760      y: y
 761    };
 762  }
 763  
 764  function isScrollParent(element) {
 765    // Firefox wants us to check `-x` and `-y` variations as well
 766    var _getComputedStyle = getComputedStyle$1(element),
 767        overflow = _getComputedStyle.overflow,
 768        overflowX = _getComputedStyle.overflowX,
 769        overflowY = _getComputedStyle.overflowY;
 770  
 771    return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);
 772  }
 773  
 774  function getScrollParent(node) {
 775    if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {
 776      // $FlowFixMe[incompatible-return]: assume body is always available
 777      return node.ownerDocument.body;
 778    }
 779  
 780    if (isHTMLElement(node) && isScrollParent(node)) {
 781      return node;
 782    }
 783  
 784    return getScrollParent(getParentNode(node));
 785  }
 786  /*

 787  given a DOM element, return the list of all scroll parents, up the list of ancesors

 788  until we get to the top window object. This list is what we attach scroll listeners

 789  to, because if any of these parent elements scroll, we'll need to re-calculate the

 790  reference element's position.

 791  */
 792  
 793  
 794  function listScrollParents(element, list) {
 795    var _element$ownerDocumen;
 796  
 797    if (list === void 0) {
 798      list = [];
 799    }
 800  
 801    var scrollParent = getScrollParent(element);
 802    var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body);
 803    var win = getWindow(scrollParent);
 804    var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;
 805    var updatedList = list.concat(target);
 806    return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here
 807    updatedList.concat(listScrollParents(getParentNode(target)));
 808  }
 809  
 810  function rectToClientRect(rect) {
 811    return Object.assign({}, rect, {
 812      left: rect.x,
 813      top: rect.y,
 814      right: rect.x + rect.width,
 815      bottom: rect.y + rect.height
 816    });
 817  }
 818  
 819  function getInnerBoundingClientRect(element) {
 820    var rect = getBoundingClientRect(element);
 821    rect.top = rect.top + element.clientTop;
 822    rect.left = rect.left + element.clientLeft;
 823    rect.bottom = rect.top + element.clientHeight;
 824    rect.right = rect.left + element.clientWidth;
 825    rect.width = element.clientWidth;
 826    rect.height = element.clientHeight;
 827    rect.x = rect.left;
 828    rect.y = rect.top;
 829    return rect;
 830  }
 831  
 832  function getClientRectFromMixedType(element, clippingParent) {
 833    return clippingParent === viewport ? rectToClientRect(getViewportRect(element)) : isElement$1(clippingParent) ? getInnerBoundingClientRect(clippingParent) : rectToClientRect(getDocumentRect(getDocumentElement(element)));
 834  } // A "clipping parent" is an overflowable container with the characteristic of
 835  // clipping (or hiding) overflowing elements with a position different from
 836  // `initial`
 837  
 838  
 839  function getClippingParents(element) {
 840    var clippingParents = listScrollParents(getParentNode(element));
 841    var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle$1(element).position) >= 0;
 842    var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;
 843  
 844    if (!isElement$1(clipperElement)) {
 845      return [];
 846    } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414
 847  
 848  
 849    return clippingParents.filter(function (clippingParent) {
 850      return isElement$1(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body';
 851    });
 852  } // Gets the maximum area that the element is visible in due to any number of
 853  // clipping parents
 854  
 855  
 856  function getClippingRect(element, boundary, rootBoundary) {
 857    var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);
 858    var clippingParents = [].concat(mainClippingParents, [rootBoundary]);
 859    var firstClippingParent = clippingParents[0];
 860    var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {
 861      var rect = getClientRectFromMixedType(element, clippingParent);
 862      accRect.top = max(rect.top, accRect.top);
 863      accRect.right = min(rect.right, accRect.right);
 864      accRect.bottom = min(rect.bottom, accRect.bottom);
 865      accRect.left = max(rect.left, accRect.left);
 866      return accRect;
 867    }, getClientRectFromMixedType(element, firstClippingParent));
 868    clippingRect.width = clippingRect.right - clippingRect.left;
 869    clippingRect.height = clippingRect.bottom - clippingRect.top;
 870    clippingRect.x = clippingRect.left;
 871    clippingRect.y = clippingRect.top;
 872    return clippingRect;
 873  }
 874  
 875  function computeOffsets(_ref) {
 876    var reference = _ref.reference,
 877        element = _ref.element,
 878        placement = _ref.placement;
 879    var basePlacement = placement ? getBasePlacement$1(placement) : null;
 880    var variation = placement ? getVariation(placement) : null;
 881    var commonX = reference.x + reference.width / 2 - element.width / 2;
 882    var commonY = reference.y + reference.height / 2 - element.height / 2;
 883    var offsets;
 884  
 885    switch (basePlacement) {
 886      case top:
 887        offsets = {
 888          x: commonX,
 889          y: reference.y - element.height
 890        };
 891        break;
 892  
 893      case bottom:
 894        offsets = {
 895          x: commonX,
 896          y: reference.y + reference.height
 897        };
 898        break;
 899  
 900      case right:
 901        offsets = {
 902          x: reference.x + reference.width,
 903          y: commonY
 904        };
 905        break;
 906  
 907      case left:
 908        offsets = {
 909          x: reference.x - element.width,
 910          y: commonY
 911        };
 912        break;
 913  
 914      default:
 915        offsets = {
 916          x: reference.x,
 917          y: reference.y
 918        };
 919    }
 920  
 921    var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null;
 922  
 923    if (mainAxis != null) {
 924      var len = mainAxis === 'y' ? 'height' : 'width';
 925  
 926      switch (variation) {
 927        case start:
 928          offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2);
 929          break;
 930  
 931        case end:
 932          offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2);
 933          break;
 934      }
 935    }
 936  
 937    return offsets;
 938  }
 939  
 940  function detectOverflow(state, options) {
 941    if (options === void 0) {
 942      options = {};
 943    }
 944  
 945    var _options = options,
 946        _options$placement = _options.placement,
 947        placement = _options$placement === void 0 ? state.placement : _options$placement,
 948        _options$boundary = _options.boundary,
 949        boundary = _options$boundary === void 0 ? clippingParents : _options$boundary,
 950        _options$rootBoundary = _options.rootBoundary,
 951        rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary,
 952        _options$elementConte = _options.elementContext,
 953        elementContext = _options$elementConte === void 0 ? popper : _options$elementConte,
 954        _options$altBoundary = _options.altBoundary,
 955        altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary,
 956        _options$padding = _options.padding,
 957        padding = _options$padding === void 0 ? 0 : _options$padding;
 958    var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));
 959    var altContext = elementContext === popper ? reference : popper;
 960    var popperRect = state.rects.popper;
 961    var element = state.elements[altBoundary ? altContext : elementContext];
 962    var clippingClientRect = getClippingRect(isElement$1(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary);
 963    var referenceClientRect = getBoundingClientRect(state.elements.reference);
 964    var popperOffsets = computeOffsets({
 965      reference: referenceClientRect,
 966      element: popperRect,
 967      strategy: 'absolute',
 968      placement: placement
 969    });
 970    var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets));
 971    var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect
 972    // 0 or negative = within the clipping rect
 973  
 974    var overflowOffsets = {
 975      top: clippingClientRect.top - elementClientRect.top + paddingObject.top,
 976      bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom,
 977      left: clippingClientRect.left - elementClientRect.left + paddingObject.left,
 978      right: elementClientRect.right - clippingClientRect.right + paddingObject.right
 979    };
 980    var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element
 981  
 982    if (elementContext === popper && offsetData) {
 983      var offset = offsetData[placement];
 984      Object.keys(overflowOffsets).forEach(function (key) {
 985        var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1;
 986        var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x';
 987        overflowOffsets[key] += offset[axis] * multiply;
 988      });
 989    }
 990  
 991    return overflowOffsets;
 992  }
 993  
 994  function computeAutoPlacement(state, options) {
 995    if (options === void 0) {
 996      options = {};
 997    }
 998  
 999    var _options = options,
1000        placement = _options.placement,
1001        boundary = _options.boundary,
1002        rootBoundary = _options.rootBoundary,
1003        padding = _options.padding,
1004        flipVariations = _options.flipVariations,
1005        _options$allowedAutoP = _options.allowedAutoPlacements,
1006        allowedAutoPlacements = _options$allowedAutoP === void 0 ? placements : _options$allowedAutoP;
1007    var variation = getVariation(placement);
1008    var placements$1 = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) {
1009      return getVariation(placement) === variation;
1010    }) : basePlacements;
1011    var allowedPlacements = placements$1.filter(function (placement) {
1012      return allowedAutoPlacements.indexOf(placement) >= 0;
1013    });
1014  
1015    if (allowedPlacements.length === 0) {
1016      allowedPlacements = placements$1;
1017    } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions...
1018  
1019  
1020    var overflows = allowedPlacements.reduce(function (acc, placement) {
1021      acc[placement] = detectOverflow(state, {
1022        placement: placement,
1023        boundary: boundary,
1024        rootBoundary: rootBoundary,
1025        padding: padding
1026      })[getBasePlacement$1(placement)];
1027      return acc;
1028    }, {});
1029    return Object.keys(overflows).sort(function (a, b) {
1030      return overflows[a] - overflows[b];
1031    });
1032  }
1033  
1034  function getExpandedFallbackPlacements(placement) {
1035    if (getBasePlacement$1(placement) === auto) {
1036      return [];
1037    }
1038  
1039    var oppositePlacement = getOppositePlacement(placement);
1040    return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)];
1041  }
1042  
1043  function flip(_ref) {
1044    var state = _ref.state,
1045        options = _ref.options,
1046        name = _ref.name;
1047  
1048    if (state.modifiersData[name]._skip) {
1049      return;
1050    }
1051  
1052    var _options$mainAxis = options.mainAxis,
1053        checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,
1054        _options$altAxis = options.altAxis,
1055        checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis,
1056        specifiedFallbackPlacements = options.fallbackPlacements,
1057        padding = options.padding,
1058        boundary = options.boundary,
1059        rootBoundary = options.rootBoundary,
1060        altBoundary = options.altBoundary,
1061        _options$flipVariatio = options.flipVariations,
1062        flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio,
1063        allowedAutoPlacements = options.allowedAutoPlacements;
1064    var preferredPlacement = state.options.placement;
1065    var basePlacement = getBasePlacement$1(preferredPlacement);
1066    var isBasePlacement = basePlacement === preferredPlacement;
1067    var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement));
1068    var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) {
1069      return acc.concat(getBasePlacement$1(placement) === auto ? computeAutoPlacement(state, {
1070        placement: placement,
1071        boundary: boundary,
1072        rootBoundary: rootBoundary,
1073        padding: padding,
1074        flipVariations: flipVariations,
1075        allowedAutoPlacements: allowedAutoPlacements
1076      }) : placement);
1077    }, []);
1078    var referenceRect = state.rects.reference;
1079    var popperRect = state.rects.popper;
1080    var checksMap = new Map();
1081    var makeFallbackChecks = true;
1082    var firstFittingPlacement = placements[0];
1083  
1084    for (var i = 0; i < placements.length; i++) {
1085      var placement = placements[i];
1086  
1087      var _basePlacement = getBasePlacement$1(placement);
1088  
1089      var isStartVariation = getVariation(placement) === start;
1090      var isVertical = [top, bottom].indexOf(_basePlacement) >= 0;
1091      var len = isVertical ? 'width' : 'height';
1092      var overflow = detectOverflow(state, {
1093        placement: placement,
1094        boundary: boundary,
1095        rootBoundary: rootBoundary,
1096        altBoundary: altBoundary,
1097        padding: padding
1098      });
1099      var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top;
1100  
1101      if (referenceRect[len] > popperRect[len]) {
1102        mainVariationSide = getOppositePlacement(mainVariationSide);
1103      }
1104  
1105      var altVariationSide = getOppositePlacement(mainVariationSide);
1106      var checks = [];
1107  
1108      if (checkMainAxis) {
1109        checks.push(overflow[_basePlacement] <= 0);
1110      }
1111  
1112      if (checkAltAxis) {
1113        checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0);
1114      }
1115  
1116      if (checks.every(function (check) {
1117        return check;
1118      })) {
1119        firstFittingPlacement = placement;
1120        makeFallbackChecks = false;
1121        break;
1122      }
1123  
1124      checksMap.set(placement, checks);
1125    }
1126  
1127    if (makeFallbackChecks) {
1128      // `2` may be desired in some cases – research later
1129      var numberOfChecks = flipVariations ? 3 : 1;
1130  
1131      var _loop = function _loop(_i) {
1132        var fittingPlacement = placements.find(function (placement) {
1133          var checks = checksMap.get(placement);
1134  
1135          if (checks) {
1136            return checks.slice(0, _i).every(function (check) {
1137              return check;
1138            });
1139          }
1140        });
1141  
1142        if (fittingPlacement) {
1143          firstFittingPlacement = fittingPlacement;
1144          return "break";
1145        }
1146      };
1147  
1148      for (var _i = numberOfChecks; _i > 0; _i--) {
1149        var _ret = _loop(_i);
1150  
1151        if (_ret === "break") break;
1152      }
1153    }
1154  
1155    if (state.placement !== firstFittingPlacement) {
1156      state.modifiersData[name]._skip = true;
1157      state.placement = firstFittingPlacement;
1158      state.reset = true;
1159    }
1160  } // eslint-disable-next-line import/no-unused-modules
1161  
1162  
1163  var flip$1 = {
1164    name: 'flip',
1165    enabled: true,
1166    phase: 'main',
1167    fn: flip,
1168    requiresIfExists: ['offset'],
1169    data: {
1170      _skip: false
1171    }
1172  };
1173  
1174  function getSideOffsets(overflow, rect, preventedOffsets) {
1175    if (preventedOffsets === void 0) {
1176      preventedOffsets = {
1177        x: 0,
1178        y: 0
1179      };
1180    }
1181  
1182    return {
1183      top: overflow.top - rect.height - preventedOffsets.y,
1184      right: overflow.right - rect.width + preventedOffsets.x,
1185      bottom: overflow.bottom - rect.height + preventedOffsets.y,
1186      left: overflow.left - rect.width - preventedOffsets.x
1187    };
1188  }
1189  
1190  function isAnySideFullyClipped(overflow) {
1191    return [top, right, bottom, left].some(function (side) {
1192      return overflow[side] >= 0;
1193    });
1194  }
1195  
1196  function hide(_ref) {
1197    var state = _ref.state,
1198        name = _ref.name;
1199    var referenceRect = state.rects.reference;
1200    var popperRect = state.rects.popper;
1201    var preventedOffsets = state.modifiersData.preventOverflow;
1202    var referenceOverflow = detectOverflow(state, {
1203      elementContext: 'reference'
1204    });
1205    var popperAltOverflow = detectOverflow(state, {
1206      altBoundary: true
1207    });
1208    var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect);
1209    var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets);
1210    var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets);
1211    var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets);
1212    state.modifiersData[name] = {
1213      referenceClippingOffsets: referenceClippingOffsets,
1214      popperEscapeOffsets: popperEscapeOffsets,
1215      isReferenceHidden: isReferenceHidden,
1216      hasPopperEscaped: hasPopperEscaped
1217    };
1218    state.attributes.popper = Object.assign({}, state.attributes.popper, {
1219      'data-popper-reference-hidden': isReferenceHidden,
1220      'data-popper-escaped': hasPopperEscaped
1221    });
1222  } // eslint-disable-next-line import/no-unused-modules
1223  
1224  
1225  var hide$1 = {
1226    name: 'hide',
1227    enabled: true,
1228    phase: 'main',
1229    requiresIfExists: ['preventOverflow'],
1230    fn: hide
1231  };
1232  
1233  function distanceAndSkiddingToXY(placement, rects, offset) {
1234    var basePlacement = getBasePlacement$1(placement);
1235    var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1;
1236  
1237    var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, {
1238      placement: placement
1239    })) : offset,
1240        skidding = _ref[0],
1241        distance = _ref[1];
1242  
1243    skidding = skidding || 0;
1244    distance = (distance || 0) * invertDistance;
1245    return [left, right].indexOf(basePlacement) >= 0 ? {
1246      x: distance,
1247      y: skidding
1248    } : {
1249      x: skidding,
1250      y: distance
1251    };
1252  }
1253  
1254  function offset(_ref2) {
1255    var state = _ref2.state,
1256        options = _ref2.options,
1257        name = _ref2.name;
1258    var _options$offset = options.offset,
1259        offset = _options$offset === void 0 ? [0, 0] : _options$offset;
1260    var data = placements.reduce(function (acc, placement) {
1261      acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset);
1262      return acc;
1263    }, {});
1264    var _data$state$placement = data[state.placement],
1265        x = _data$state$placement.x,
1266        y = _data$state$placement.y;
1267  
1268    if (state.modifiersData.popperOffsets != null) {
1269      state.modifiersData.popperOffsets.x += x;
1270      state.modifiersData.popperOffsets.y += y;
1271    }
1272  
1273    state.modifiersData[name] = data;
1274  } // eslint-disable-next-line import/no-unused-modules
1275  
1276  
1277  var offset$1 = {
1278    name: 'offset',
1279    enabled: true,
1280    phase: 'main',
1281    requires: ['popperOffsets'],
1282    fn: offset
1283  };
1284  
1285  function popperOffsets(_ref) {
1286    var state = _ref.state,
1287        name = _ref.name; // Offsets are the actual position the popper needs to have to be
1288    // properly positioned near its reference element
1289    // This is the most basic placement, and will be adjusted by
1290    // the modifiers in the next step
1291  
1292    state.modifiersData[name] = computeOffsets({
1293      reference: state.rects.reference,
1294      element: state.rects.popper,
1295      strategy: 'absolute',
1296      placement: state.placement
1297    });
1298  } // eslint-disable-next-line import/no-unused-modules
1299  
1300  
1301  var popperOffsets$1 = {
1302    name: 'popperOffsets',
1303    enabled: true,
1304    phase: 'read',
1305    fn: popperOffsets,
1306    data: {}
1307  };
1308  
1309  function getAltAxis(axis) {
1310    return axis === 'x' ? 'y' : 'x';
1311  }
1312  
1313  function preventOverflow(_ref) {
1314    var state = _ref.state,
1315        options = _ref.options,
1316        name = _ref.name;
1317    var _options$mainAxis = options.mainAxis,
1318        checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,
1319        _options$altAxis = options.altAxis,
1320        checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis,
1321        boundary = options.boundary,
1322        rootBoundary = options.rootBoundary,
1323        altBoundary = options.altBoundary,
1324        padding = options.padding,
1325        _options$tether = options.tether,
1326        tether = _options$tether === void 0 ? true : _options$tether,
1327        _options$tetherOffset = options.tetherOffset,
1328        tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset;
1329    var overflow = detectOverflow(state, {
1330      boundary: boundary,
1331      rootBoundary: rootBoundary,
1332      padding: padding,
1333      altBoundary: altBoundary
1334    });
1335    var basePlacement = getBasePlacement$1(state.placement);
1336    var variation = getVariation(state.placement);
1337    var isBasePlacement = !variation;
1338    var mainAxis = getMainAxisFromPlacement(basePlacement);
1339    var altAxis = getAltAxis(mainAxis);
1340    var popperOffsets = state.modifiersData.popperOffsets;
1341    var referenceRect = state.rects.reference;
1342    var popperRect = state.rects.popper;
1343    var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, {
1344      placement: state.placement
1345    })) : tetherOffset;
1346    var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? {
1347      mainAxis: tetherOffsetValue,
1348      altAxis: tetherOffsetValue
1349    } : Object.assign({
1350      mainAxis: 0,
1351      altAxis: 0
1352    }, tetherOffsetValue);
1353    var offsetModifierState = state.modifiersData.offset ? state.modifiersData.offset[state.placement] : null;
1354    var data = {
1355      x: 0,
1356      y: 0
1357    };
1358  
1359    if (!popperOffsets) {
1360      return;
1361    }
1362  
1363    if (checkMainAxis) {
1364      var _offsetModifierState$;
1365  
1366      var mainSide = mainAxis === 'y' ? top : left;
1367      var altSide = mainAxis === 'y' ? bottom : right;
1368      var len = mainAxis === 'y' ? 'height' : 'width';
1369      var offset = popperOffsets[mainAxis];
1370      var min$1 = offset + overflow[mainSide];
1371      var max$1 = offset - overflow[altSide];
1372      var additive = tether ? -popperRect[len] / 2 : 0;
1373      var minLen = variation === start ? referenceRect[len] : popperRect[len];
1374      var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go
1375      // outside the reference bounds
1376  
1377      var arrowElement = state.elements.arrow;
1378      var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : {
1379        width: 0,
1380        height: 0
1381      };
1382      var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject();
1383      var arrowPaddingMin = arrowPaddingObject[mainSide];
1384      var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want
1385      // to include its full size in the calculation. If the reference is small
1386      // and near the edge of a boundary, the popper can overflow even if the
1387      // reference is not overflowing as well (e.g. virtual elements with no
1388      // width or height)
1389  
1390      var arrowLen = within(0, referenceRect[len], arrowRect[len]);
1391      var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis;
1392      var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis;
1393      var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow);
1394      var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0;
1395      var offsetModifierValue = (_offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState[mainAxis]) != null ? _offsetModifierState$ : 0;
1396      var tetherMin = offset + minOffset - offsetModifierValue - clientOffset;
1397      var tetherMax = offset + maxOffset - offsetModifierValue;
1398      var preventedOffset = within(tether ? min(min$1, tetherMin) : min$1, offset, tether ? max(max$1, tetherMax) : max$1);
1399      popperOffsets[mainAxis] = preventedOffset;
1400      data[mainAxis] = preventedOffset - offset;
1401    }
1402  
1403    if (checkAltAxis) {
1404      var _offsetModifierState$2;
1405  
1406      var _mainSide = mainAxis === 'x' ? top : left;
1407  
1408      var _altSide = mainAxis === 'x' ? bottom : right;
1409  
1410      var _offset = popperOffsets[altAxis];
1411  
1412      var _len = altAxis === 'y' ? 'height' : 'width';
1413  
1414      var _min = _offset + overflow[_mainSide];
1415  
1416      var _max = _offset - overflow[_altSide];
1417  
1418      var isOriginSide = [top, left].indexOf(basePlacement) !== -1;
1419  
1420      var _offsetModifierValue = (_offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState[altAxis]) != null ? _offsetModifierState$2 : 0;
1421  
1422      var _tetherMin = isOriginSide ? _min : _offset - referenceRect[_len] - popperRect[_len] - _offsetModifierValue + normalizedTetherOffsetValue.altAxis;
1423  
1424      var _tetherMax = isOriginSide ? _offset + referenceRect[_len] + popperRect[_len] - _offsetModifierValue - normalizedTetherOffsetValue.altAxis : _max;
1425  
1426      var _preventedOffset = tether && isOriginSide ? withinMaxClamp(_tetherMin, _offset, _tetherMax) : within(tether ? _tetherMin : _min, _offset, tether ? _tetherMax : _max);
1427  
1428      popperOffsets[altAxis] = _preventedOffset;
1429      data[altAxis] = _preventedOffset - _offset;
1430    }
1431  
1432    state.modifiersData[name] = data;
1433  } // eslint-disable-next-line import/no-unused-modules
1434  
1435  
1436  var preventOverflow$1 = {
1437    name: 'preventOverflow',
1438    enabled: true,
1439    phase: 'main',
1440    fn: preventOverflow,
1441    requiresIfExists: ['offset']
1442  };
1443  
1444  function getHTMLElementScroll(element) {
1445    return {
1446      scrollLeft: element.scrollLeft,
1447      scrollTop: element.scrollTop
1448    };
1449  }
1450  
1451  function getNodeScroll(node) {
1452    if (node === getWindow(node) || !isHTMLElement(node)) {
1453      return getWindowScroll(node);
1454    } else {
1455      return getHTMLElementScroll(node);
1456    }
1457  }
1458  
1459  function isElementScaled(element) {
1460    var rect = element.getBoundingClientRect();
1461    var scaleX = round(rect.width) / element.offsetWidth || 1;
1462    var scaleY = round(rect.height) / element.offsetHeight || 1;
1463    return scaleX !== 1 || scaleY !== 1;
1464  } // Returns the composite rect of an element relative to its offsetParent.
1465  // Composite means it takes into account transforms as well as layout.
1466  
1467  
1468  function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) {
1469    if (isFixed === void 0) {
1470      isFixed = false;
1471    }
1472  
1473    var isOffsetParentAnElement = isHTMLElement(offsetParent);
1474    var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent);
1475    var documentElement = getDocumentElement(offsetParent);
1476    var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled);
1477    var scroll = {
1478      scrollLeft: 0,
1479      scrollTop: 0
1480    };
1481    var offsets = {
1482      x: 0,
1483      y: 0
1484    };
1485  
1486    if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {
1487      if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078
1488      isScrollParent(documentElement)) {
1489        scroll = getNodeScroll(offsetParent);
1490      }
1491  
1492      if (isHTMLElement(offsetParent)) {
1493        offsets = getBoundingClientRect(offsetParent, true);
1494        offsets.x += offsetParent.clientLeft;
1495        offsets.y += offsetParent.clientTop;
1496      } else if (documentElement) {
1497        offsets.x = getWindowScrollBarX(documentElement);
1498      }
1499    }
1500  
1501    return {
1502      x: rect.left + scroll.scrollLeft - offsets.x,
1503      y: rect.top + scroll.scrollTop - offsets.y,
1504      width: rect.width,
1505      height: rect.height
1506    };
1507  }
1508  
1509  function order(modifiers) {
1510    var map = new Map();
1511    var visited = new Set();
1512    var result = [];
1513    modifiers.forEach(function (modifier) {
1514      map.set(modifier.name, modifier);
1515    }); // On visiting object, check for its dependencies and visit them recursively
1516  
1517    function sort(modifier) {
1518      visited.add(modifier.name);
1519      var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []);
1520      requires.forEach(function (dep) {
1521        if (!visited.has(dep)) {
1522          var depModifier = map.get(dep);
1523  
1524          if (depModifier) {
1525            sort(depModifier);
1526          }
1527        }
1528      });
1529      result.push(modifier);
1530    }
1531  
1532    modifiers.forEach(function (modifier) {
1533      if (!visited.has(modifier.name)) {
1534        // check for visited object
1535        sort(modifier);
1536      }
1537    });
1538    return result;
1539  }
1540  
1541  function orderModifiers(modifiers) {
1542    // order based on dependencies
1543    var orderedModifiers = order(modifiers); // order based on phase
1544  
1545    return modifierPhases.reduce(function (acc, phase) {
1546      return acc.concat(orderedModifiers.filter(function (modifier) {
1547        return modifier.phase === phase;
1548      }));
1549    }, []);
1550  }
1551  
1552  function debounce$1(fn) {
1553    var pending;
1554    return function () {
1555      if (!pending) {
1556        pending = new Promise(function (resolve) {
1557          Promise.resolve().then(function () {
1558            pending = undefined;
1559            resolve(fn());
1560          });
1561        });
1562      }
1563  
1564      return pending;
1565    };
1566  }
1567  
1568  function mergeByName(modifiers) {
1569    var merged = modifiers.reduce(function (merged, current) {
1570      var existing = merged[current.name];
1571      merged[current.name] = existing ? Object.assign({}, existing, current, {
1572        options: Object.assign({}, existing.options, current.options),
1573        data: Object.assign({}, existing.data, current.data)
1574      }) : current;
1575      return merged;
1576    }, {}); // IE11 does not support Object.values
1577  
1578    return Object.keys(merged).map(function (key) {
1579      return merged[key];
1580    });
1581  }
1582  
1583  var DEFAULT_OPTIONS = {
1584    placement: 'bottom',
1585    modifiers: [],
1586    strategy: 'absolute'
1587  };
1588  
1589  function areValidElements() {
1590    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
1591      args[_key] = arguments[_key];
1592    }
1593  
1594    return !args.some(function (element) {
1595      return !(element && typeof element.getBoundingClientRect === 'function');
1596    });
1597  }
1598  
1599  function popperGenerator(generatorOptions) {
1600    if (generatorOptions === void 0) {
1601      generatorOptions = {};
1602    }
1603  
1604    var _generatorOptions = generatorOptions,
1605        _generatorOptions$def = _generatorOptions.defaultModifiers,
1606        defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,
1607        _generatorOptions$def2 = _generatorOptions.defaultOptions,
1608        defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;
1609    return function createPopper(reference, popper, options) {
1610      if (options === void 0) {
1611        options = defaultOptions;
1612      }
1613  
1614      var state = {
1615        placement: 'bottom',
1616        orderedModifiers: [],
1617        options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions),
1618        modifiersData: {},
1619        elements: {
1620          reference: reference,
1621          popper: popper
1622        },
1623        attributes: {},
1624        styles: {}
1625      };
1626      var effectCleanupFns = [];
1627      var isDestroyed = false;
1628      var instance = {
1629        state: state,
1630        setOptions: function setOptions(setOptionsAction) {
1631          var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction;
1632          cleanupModifierEffects();
1633          state.options = Object.assign({}, defaultOptions, state.options, options);
1634          state.scrollParents = {
1635            reference: isElement$1(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],
1636            popper: listScrollParents(popper)
1637          }; // Orders the modifiers based on their dependencies and `phase`
1638          // properties
1639  
1640          var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers
1641  
1642          state.orderedModifiers = orderedModifiers.filter(function (m) {
1643            return m.enabled;
1644          }); // Validate the provided modifiers so that the consumer will get warned
1645  
1646          runModifierEffects();
1647          return instance.update();
1648        },
1649        // Sync update – it will always be executed, even if not necessary. This
1650        // is useful for low frequency updates where sync behavior simplifies the
1651        // logic.
1652        // For high frequency updates (e.g. `resize` and `scroll` events), always
1653        // prefer the async Popper#update method
1654        forceUpdate: function forceUpdate() {
1655          if (isDestroyed) {
1656            return;
1657          }
1658  
1659          var _state$elements = state.elements,
1660              reference = _state$elements.reference,
1661              popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements
1662          // anymore
1663  
1664          if (!areValidElements(reference, popper)) {
1665            return;
1666          } // Store the reference and popper rects to be read by modifiers
1667  
1668  
1669          state.rects = {
1670            reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),
1671            popper: getLayoutRect(popper)
1672          }; // Modifiers have the ability to reset the current update cycle. The
1673          // most common use case for this is the `flip` modifier changing the
1674          // placement, which then needs to re-run all the modifiers, because the
1675          // logic was previously ran for the previous placement and is therefore
1676          // stale/incorrect
1677  
1678          state.reset = false;
1679          state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier
1680          // is filled with the initial data specified by the modifier. This means
1681          // it doesn't persist and is fresh on each update.
1682          // To ensure persistent data, use `${name}#persistent`
1683  
1684          state.orderedModifiers.forEach(function (modifier) {
1685            return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);
1686          });
1687  
1688          for (var index = 0; index < state.orderedModifiers.length; index++) {
1689            if (state.reset === true) {
1690              state.reset = false;
1691              index = -1;
1692              continue;
1693            }
1694  
1695            var _state$orderedModifie = state.orderedModifiers[index],
1696                fn = _state$orderedModifie.fn,
1697                _state$orderedModifie2 = _state$orderedModifie.options,
1698                _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,
1699                name = _state$orderedModifie.name;
1700  
1701            if (typeof fn === 'function') {
1702              state = fn({
1703                state: state,
1704                options: _options,
1705                name: name,
1706                instance: instance
1707              }) || state;
1708            }
1709          }
1710        },
1711        // Async and optimistically optimized update – it will not be executed if
1712        // not necessary (debounced to run at most once-per-tick)
1713        update: debounce$1(function () {
1714          return new Promise(function (resolve) {
1715            instance.forceUpdate();
1716            resolve(state);
1717          });
1718        }),
1719        destroy: function destroy() {
1720          cleanupModifierEffects();
1721          isDestroyed = true;
1722        }
1723      };
1724  
1725      if (!areValidElements(reference, popper)) {
1726        return instance;
1727      }
1728  
1729      instance.setOptions(options).then(function (state) {
1730        if (!isDestroyed && options.onFirstUpdate) {
1731          options.onFirstUpdate(state);
1732        }
1733      }); // Modifiers have the ability to execute arbitrary code before the first
1734      // update cycle runs. They will be executed in the same order as the update
1735      // cycle. This is useful when a modifier adds some persistent data that
1736      // other modifiers need to use, but the modifier is run after the dependent
1737      // one.
1738  
1739      function runModifierEffects() {
1740        state.orderedModifiers.forEach(function (_ref3) {
1741          var name = _ref3.name,
1742              _ref3$options = _ref3.options,
1743              options = _ref3$options === void 0 ? {} : _ref3$options,
1744              effect = _ref3.effect;
1745  
1746          if (typeof effect === 'function') {
1747            var cleanupFn = effect({
1748              state: state,
1749              name: name,
1750              instance: instance,
1751              options: options
1752            });
1753  
1754            var noopFn = function noopFn() {};
1755  
1756            effectCleanupFns.push(cleanupFn || noopFn);
1757          }
1758        });
1759      }
1760  
1761      function cleanupModifierEffects() {
1762        effectCleanupFns.forEach(function (fn) {
1763          return fn();
1764        });
1765        effectCleanupFns = [];
1766      }
1767  
1768      return instance;
1769    };
1770  }
1771  
1772  var defaultModifiers = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1, offset$1, flip$1, preventOverflow$1, arrow$1, hide$1];
1773  var createPopper = /*#__PURE__*/popperGenerator({
1774    defaultModifiers: defaultModifiers
1775  }); // eslint-disable-next-line import/no-unused-modules
1776  
1777  /**!

1778  * tippy.js v6.3.7

1779  * (c) 2017-2021 atomiks

1780  * MIT License

1781  */
1782  
1783  var BOX_CLASS = "tippy-box";
1784  var CONTENT_CLASS = "tippy-content";
1785  var BACKDROP_CLASS = "tippy-backdrop";
1786  var ARROW_CLASS = "tippy-arrow";
1787  var SVG_ARROW_CLASS = "tippy-svg-arrow";
1788  var TOUCH_OPTIONS = {
1789    passive: true,
1790    capture: true
1791  };
1792  
1793  var TIPPY_DEFAULT_APPEND_TO = function TIPPY_DEFAULT_APPEND_TO() {
1794    return document.body;
1795  };
1796  
1797  function getValueAtIndexOrReturn(value, index, defaultValue) {
1798    if (Array.isArray(value)) {
1799      var v = value[index];
1800      return v == null ? Array.isArray(defaultValue) ? defaultValue[index] : defaultValue : v;
1801    }
1802  
1803    return value;
1804  }
1805  
1806  function isType(value, type) {
1807    var str = {}.toString.call(value);
1808    return str.indexOf('[object') === 0 && str.indexOf(type + "]") > -1;
1809  }
1810  
1811  function invokeWithArgsOrReturn(value, args) {
1812    return typeof value === 'function' ? value.apply(void 0, args) : value;
1813  }
1814  
1815  function debounce(fn, ms) {
1816    // Avoid wrapping in `setTimeout` if ms is 0 anyway
1817    if (ms === 0) {
1818      return fn;
1819    }
1820  
1821    var timeout;
1822    return function (arg) {
1823      clearTimeout(timeout);
1824      timeout = setTimeout(function () {
1825        fn(arg);
1826      }, ms);
1827    };
1828  }
1829  
1830  function splitBySpaces(value) {
1831    return value.split(/\s+/).filter(Boolean);
1832  }
1833  
1834  function normalizeToArray(value) {
1835    return [].concat(value);
1836  }
1837  
1838  function pushIfUnique(arr, value) {
1839    if (arr.indexOf(value) === -1) {
1840      arr.push(value);
1841    }
1842  }
1843  
1844  function unique(arr) {
1845    return arr.filter(function (item, index) {
1846      return arr.indexOf(item) === index;
1847    });
1848  }
1849  
1850  function getBasePlacement(placement) {
1851    return placement.split('-')[0];
1852  }
1853  
1854  function arrayFrom(value) {
1855    return [].slice.call(value);
1856  }
1857  
1858  function removeUndefinedProps(obj) {
1859    return Object.keys(obj).reduce(function (acc, key) {
1860      if (obj[key] !== undefined) {
1861        acc[key] = obj[key];
1862      }
1863  
1864      return acc;
1865    }, {});
1866  }
1867  
1868  function div() {
1869    return document.createElement('div');
1870  }
1871  
1872  function isElement(value) {
1873    return ['Element', 'Fragment'].some(function (type) {
1874      return isType(value, type);
1875    });
1876  }
1877  
1878  function isNodeList(value) {
1879    return isType(value, 'NodeList');
1880  }
1881  
1882  function isMouseEvent(value) {
1883    return isType(value, 'MouseEvent');
1884  }
1885  
1886  function isReferenceElement(value) {
1887    return !!(value && value._tippy && value._tippy.reference === value);
1888  }
1889  
1890  function getArrayOfElements(value) {
1891    if (isElement(value)) {
1892      return [value];
1893    }
1894  
1895    if (isNodeList(value)) {
1896      return arrayFrom(value);
1897    }
1898  
1899    if (Array.isArray(value)) {
1900      return value;
1901    }
1902  
1903    return arrayFrom(document.querySelectorAll(value));
1904  }
1905  
1906  function setTransitionDuration(els, value) {
1907    els.forEach(function (el) {
1908      if (el) {
1909        el.style.transitionDuration = value + "ms";
1910      }
1911    });
1912  }
1913  
1914  function setVisibilityState(els, state) {
1915    els.forEach(function (el) {
1916      if (el) {
1917        el.setAttribute('data-state', state);
1918      }
1919    });
1920  }
1921  
1922  function getOwnerDocument(elementOrElements) {
1923    var _element$ownerDocumen;
1924  
1925    var _normalizeToArray = normalizeToArray(elementOrElements),
1926        element = _normalizeToArray[0]; // Elements created via a <template> have an ownerDocument with no reference to the body
1927  
1928  
1929    return element != null && (_element$ownerDocumen = element.ownerDocument) != null && _element$ownerDocumen.body ? element.ownerDocument : document;
1930  }
1931  
1932  function isCursorOutsideInteractiveBorder(popperTreeData, event) {
1933    var clientX = event.clientX,
1934        clientY = event.clientY;
1935    return popperTreeData.every(function (_ref) {
1936      var popperRect = _ref.popperRect,
1937          popperState = _ref.popperState,
1938          props = _ref.props;
1939      var interactiveBorder = props.interactiveBorder;
1940      var basePlacement = getBasePlacement(popperState.placement);
1941      var offsetData = popperState.modifiersData.offset;
1942  
1943      if (!offsetData) {
1944        return true;
1945      }
1946  
1947      var topDistance = basePlacement === 'bottom' ? offsetData.top.y : 0;
1948      var bottomDistance = basePlacement === 'top' ? offsetData.bottom.y : 0;
1949      var leftDistance = basePlacement === 'right' ? offsetData.left.x : 0;
1950      var rightDistance = basePlacement === 'left' ? offsetData.right.x : 0;
1951      var exceedsTop = popperRect.top - clientY + topDistance > interactiveBorder;
1952      var exceedsBottom = clientY - popperRect.bottom - bottomDistance > interactiveBorder;
1953      var exceedsLeft = popperRect.left - clientX + leftDistance > interactiveBorder;
1954      var exceedsRight = clientX - popperRect.right - rightDistance > interactiveBorder;
1955      return exceedsTop || exceedsBottom || exceedsLeft || exceedsRight;
1956    });
1957  }
1958  
1959  function updateTransitionEndListener(box, action, listener) {
1960    var method = action + "EventListener"; // some browsers apparently support `transition` (unprefixed) but only fire
1961    // `webkitTransitionEnd`...
1962  
1963    ['transitionend', 'webkitTransitionEnd'].forEach(function (event) {
1964      box[method](event, listener);
1965    });
1966  }
1967  /**

1968   * Compared to xxx.contains, this function works for dom structures with shadow

1969   * dom

1970   */
1971  
1972  
1973  function actualContains(parent, child) {
1974    var target = child;
1975  
1976    while (target) {
1977      var _target$getRootNode;
1978  
1979      if (parent.contains(target)) {
1980        return true;
1981      }
1982  
1983      target = target.getRootNode == null ? void 0 : (_target$getRootNode = target.getRootNode()) == null ? void 0 : _target$getRootNode.host;
1984    }
1985  
1986    return false;
1987  }
1988  
1989  var currentInput = {
1990    isTouch: false
1991  };
1992  var lastMouseMoveTime = 0;
1993  /**

1994   * When a `touchstart` event is fired, it's assumed the user is using touch

1995   * input. We'll bind a `mousemove` event listener to listen for mouse input in

1996   * the future. This way, the `isTouch` property is fully dynamic and will handle

1997   * hybrid devices that use a mix of touch + mouse input.

1998   */
1999  
2000  function onDocumentTouchStart() {
2001    if (currentInput.isTouch) {
2002      return;
2003    }
2004  
2005    currentInput.isTouch = true;
2006  
2007    if (window.performance) {
2008      document.addEventListener('mousemove', onDocumentMouseMove);
2009    }
2010  }
2011  /**

2012   * When two `mousemove` event are fired consecutively within 20ms, it's assumed

2013   * the user is using mouse input again. `mousemove` can fire on touch devices as

2014   * well, but very rarely that quickly.

2015   */
2016  
2017  
2018  function onDocumentMouseMove() {
2019    var now = performance.now();
2020  
2021    if (now - lastMouseMoveTime < 20) {
2022      currentInput.isTouch = false;
2023      document.removeEventListener('mousemove', onDocumentMouseMove);
2024    }
2025  
2026    lastMouseMoveTime = now;
2027  }
2028  /**

2029   * When an element is in focus and has a tippy, leaving the tab/window and

2030   * returning causes it to show again. For mouse users this is unexpected, but

2031   * for keyboard use it makes sense.

2032   * TODO: find a better technique to solve this problem

2033   */
2034  
2035  
2036  function onWindowBlur() {
2037    var activeElement = document.activeElement;
2038  
2039    if (isReferenceElement(activeElement)) {
2040      var instance = activeElement._tippy;
2041  
2042      if (activeElement.blur && !instance.state.isVisible) {
2043        activeElement.blur();
2044      }
2045    }
2046  }
2047  
2048  function bindGlobalEventListeners() {
2049    document.addEventListener('touchstart', onDocumentTouchStart, TOUCH_OPTIONS);
2050    window.addEventListener('blur', onWindowBlur);
2051  }
2052  
2053  var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
2054  var isIE11 = isBrowser ? // @ts-ignore
2055  !!window.msCrypto : false;
2056  var pluginProps = {
2057    animateFill: false,
2058    followCursor: false,
2059    inlinePositioning: false,
2060    sticky: false
2061  };
2062  var renderProps = {
2063    allowHTML: false,
2064    animation: 'fade',
2065    arrow: true,
2066    content: '',
2067    inertia: false,
2068    maxWidth: 350,
2069    role: 'tooltip',
2070    theme: '',
2071    zIndex: 9999
2072  };
2073  var defaultProps = Object.assign({
2074    appendTo: TIPPY_DEFAULT_APPEND_TO,
2075    aria: {
2076      content: 'auto',
2077      expanded: 'auto'
2078    },
2079    delay: 0,
2080    duration: [300, 250],
2081    getReferenceClientRect: null,
2082    hideOnClick: true,
2083    ignoreAttributes: false,
2084    interactive: false,
2085    interactiveBorder: 2,
2086    interactiveDebounce: 0,
2087    moveTransition: '',
2088    offset: [0, 10],
2089    onAfterUpdate: function onAfterUpdate() {},
2090    onBeforeUpdate: function onBeforeUpdate() {},
2091    onCreate: function onCreate() {},
2092    onDestroy: function onDestroy() {},
2093    onHidden: function onHidden() {},
2094    onHide: function onHide() {},
2095    onMount: function onMount() {},
2096    onShow: function onShow() {},
2097    onShown: function onShown() {},
2098    onTrigger: function onTrigger() {},
2099    onUntrigger: function onUntrigger() {},
2100    onClickOutside: function onClickOutside() {},
2101    placement: 'top',
2102    plugins: [],
2103    popperOptions: {},
2104    render: null,
2105    showOnCreate: false,
2106    touch: true,
2107    trigger: 'mouseenter focus',
2108    triggerTarget: null
2109  }, pluginProps, renderProps);
2110  var defaultKeys = Object.keys(defaultProps);
2111  
2112  var setDefaultProps = function setDefaultProps(partialProps) {
2113    var keys = Object.keys(partialProps);
2114    keys.forEach(function (key) {
2115      defaultProps[key] = partialProps[key];
2116    });
2117  };
2118  
2119  function getExtendedPassedProps(passedProps) {
2120    var plugins = passedProps.plugins || [];
2121    var pluginProps = plugins.reduce(function (acc, plugin) {
2122      var name = plugin.name,
2123          defaultValue = plugin.defaultValue;
2124  
2125      if (name) {
2126        var _name;
2127  
2128        acc[name] = passedProps[name] !== undefined ? passedProps[name] : (_name = defaultProps[name]) != null ? _name : defaultValue;
2129      }
2130  
2131      return acc;
2132    }, {});
2133    return Object.assign({}, passedProps, pluginProps);
2134  }
2135  
2136  function getDataAttributeProps(reference, plugins) {
2137    var propKeys = plugins ? Object.keys(getExtendedPassedProps(Object.assign({}, defaultProps, {
2138      plugins: plugins
2139    }))) : defaultKeys;
2140    var props = propKeys.reduce(function (acc, key) {
2141      var valueAsString = (reference.getAttribute("data-tippy-" + key) || '').trim();
2142  
2143      if (!valueAsString) {
2144        return acc;
2145      }
2146  
2147      if (key === 'content') {
2148        acc[key] = valueAsString;
2149      } else {
2150        try {
2151          acc[key] = JSON.parse(valueAsString);
2152        } catch (e) {
2153          acc[key] = valueAsString;
2154        }
2155      }
2156  
2157      return acc;
2158    }, {});
2159    return props;
2160  }
2161  
2162  function evaluateProps(reference, props) {
2163    var out = Object.assign({}, props, {
2164      content: invokeWithArgsOrReturn(props.content, [reference])
2165    }, props.ignoreAttributes ? {} : getDataAttributeProps(reference, props.plugins));
2166    out.aria = Object.assign({}, defaultProps.aria, out.aria);
2167    out.aria = {
2168      expanded: out.aria.expanded === 'auto' ? props.interactive : out.aria.expanded,
2169      content: out.aria.content === 'auto' ? props.interactive ? null : 'describedby' : out.aria.content
2170    };
2171    return out;
2172  }
2173  
2174  var innerHTML = function innerHTML() {
2175    return 'innerHTML';
2176  };
2177  
2178  function dangerouslySetInnerHTML(element, html) {
2179    element[innerHTML()] = html;
2180  }
2181  
2182  function createArrowElement(value) {
2183    var arrow = div();
2184  
2185    if (value === true) {
2186      arrow.className = ARROW_CLASS;
2187    } else {
2188      arrow.className = SVG_ARROW_CLASS;
2189  
2190      if (isElement(value)) {
2191        arrow.appendChild(value);
2192      } else {
2193        dangerouslySetInnerHTML(arrow, value);
2194      }
2195    }
2196  
2197    return arrow;
2198  }
2199  
2200  function setContent(content, props) {
2201    if (isElement(props.content)) {
2202      dangerouslySetInnerHTML(content, '');
2203      content.appendChild(props.content);
2204    } else if (typeof props.content !== 'function') {
2205      if (props.allowHTML) {
2206        dangerouslySetInnerHTML(content, props.content);
2207      } else {
2208        content.textContent = props.content;
2209      }
2210    }
2211  }
2212  
2213  function getChildren(popper) {
2214    var box = popper.firstElementChild;
2215    var boxChildren = arrayFrom(box.children);
2216    return {
2217      box: box,
2218      content: boxChildren.find(function (node) {
2219        return node.classList.contains(CONTENT_CLASS);
2220      }),
2221      arrow: boxChildren.find(function (node) {
2222        return node.classList.contains(ARROW_CLASS) || node.classList.contains(SVG_ARROW_CLASS);
2223      }),
2224      backdrop: boxChildren.find(function (node) {
2225        return node.classList.contains(BACKDROP_CLASS);
2226      })
2227    };
2228  }
2229  
2230  function render(instance) {
2231    var popper = div();
2232    var box = div();
2233    box.className = BOX_CLASS;
2234    box.setAttribute('data-state', 'hidden');
2235    box.setAttribute('tabindex', '-1');
2236    var content = div();
2237    content.className = CONTENT_CLASS;
2238    content.setAttribute('data-state', 'hidden');
2239    setContent(content, instance.props);
2240    popper.appendChild(box);
2241    box.appendChild(content);
2242    onUpdate(instance.props, instance.props);
2243  
2244    function onUpdate(prevProps, nextProps) {
2245      var _getChildren = getChildren(popper),
2246          box = _getChildren.box,
2247          content = _getChildren.content,
2248          arrow = _getChildren.arrow;
2249  
2250      if (nextProps.theme) {
2251        box.setAttribute('data-theme', nextProps.theme);
2252      } else {
2253        box.removeAttribute('data-theme');
2254      }
2255  
2256      if (typeof nextProps.animation === 'string') {
2257        box.setAttribute('data-animation', nextProps.animation);
2258      } else {
2259        box.removeAttribute('data-animation');
2260      }
2261  
2262      if (nextProps.inertia) {
2263        box.setAttribute('data-inertia', '');
2264      } else {
2265        box.removeAttribute('data-inertia');
2266      }
2267  
2268      box.style.maxWidth = typeof nextProps.maxWidth === 'number' ? nextProps.maxWidth + "px" : nextProps.maxWidth;
2269  
2270      if (nextProps.role) {
2271        box.setAttribute('role', nextProps.role);
2272      } else {
2273        box.removeAttribute('role');
2274      }
2275  
2276      if (prevProps.content !== nextProps.content || prevProps.allowHTML !== nextProps.allowHTML) {
2277        setContent(content, instance.props);
2278      }
2279  
2280      if (nextProps.arrow) {
2281        if (!arrow) {
2282          box.appendChild(createArrowElement(nextProps.arrow));
2283        } else if (prevProps.arrow !== nextProps.arrow) {
2284          box.removeChild(arrow);
2285          box.appendChild(createArrowElement(nextProps.arrow));
2286        }
2287      } else if (arrow) {
2288        box.removeChild(arrow);
2289      }
2290    }
2291  
2292    return {
2293      popper: popper,
2294      onUpdate: onUpdate
2295    };
2296  } // Runtime check to identify if the render function is the default one; this
2297  // way we can apply default CSS transitions logic and it can be tree-shaken away
2298  
2299  
2300  render.$$tippy = true;
2301  var idCounter = 1;
2302  var mouseMoveListeners = []; // Used by `hideAll()`
2303  
2304  var mountedInstances = [];
2305  
2306  function createTippy(reference, passedProps) {
2307    var props = evaluateProps(reference, Object.assign({}, defaultProps, getExtendedPassedProps(removeUndefinedProps(passedProps)))); // ===========================================================================
2308    // 🔒 Private members
2309    // ===========================================================================
2310  
2311    var showTimeout;
2312    var hideTimeout;
2313    var scheduleHideAnimationFrame;
2314    var isVisibleFromClick = false;
2315    var didHideDueToDocumentMouseDown = false;
2316    var didTouchMove = false;
2317    var ignoreOnFirstUpdate = false;
2318    var lastTriggerEvent;
2319    var currentTransitionEndListener;
2320    var onFirstUpdate;
2321    var listeners = [];
2322    var debouncedOnMouseMove = debounce(onMouseMove, props.interactiveDebounce);
2323    var currentTarget; // ===========================================================================
2324    // 🔑 Public members
2325    // ===========================================================================
2326  
2327    var id = idCounter++;
2328    var popperInstance = null;
2329    var plugins = unique(props.plugins);
2330    var state = {
2331      // Is the instance currently enabled?
2332      isEnabled: true,
2333      // Is the tippy currently showing and not transitioning out?
2334      isVisible: false,
2335      // Has the instance been destroyed?
2336      isDestroyed: false,
2337      // Is the tippy currently mounted to the DOM?
2338      isMounted: false,
2339      // Has the tippy finished transitioning in?
2340      isShown: false
2341    };
2342    var instance = {
2343      // properties
2344      id: id,
2345      reference: reference,
2346      popper: div(),
2347      popperInstance: popperInstance,
2348      props: props,
2349      state: state,
2350      plugins: plugins,
2351      // methods
2352      clearDelayTimeouts: clearDelayTimeouts,
2353      setProps: setProps,
2354      setContent: setContent,
2355      show: show,
2356      hide: hide,
2357      hideWithInteractivity: hideWithInteractivity,
2358      enable: enable,
2359      disable: disable,
2360      unmount: unmount,
2361      destroy: destroy
2362    }; // TODO: Investigate why this early return causes a TDZ error in the tests —
2363    // it doesn't seem to happen in the browser
2364  
2365    /* istanbul ignore if */
2366  
2367    if (!props.render) {
2368      return instance;
2369    } // ===========================================================================
2370    // Initial mutations
2371    // ===========================================================================
2372  
2373  
2374    var _props$render = props.render(instance),
2375        popper = _props$render.popper,
2376        onUpdate = _props$render.onUpdate;
2377  
2378    popper.setAttribute('data-tippy-root', '');
2379    popper.id = "tippy-" + instance.id;
2380    instance.popper = popper;
2381    reference._tippy = instance;
2382    popper._tippy = instance;
2383    var pluginsHooks = plugins.map(function (plugin) {
2384      return plugin.fn(instance);
2385    });
2386    var hasAriaExpanded = reference.hasAttribute('aria-expanded');
2387    addListeners();
2388    handleAriaExpandedAttribute();
2389    handleStyles();
2390    invokeHook('onCreate', [instance]);
2391  
2392    if (props.showOnCreate) {
2393      scheduleShow();
2394    } // Prevent a tippy with a delay from hiding if the cursor left then returned
2395    // before it started hiding
2396  
2397  
2398    popper.addEventListener('mouseenter', function () {
2399      if (instance.props.interactive && instance.state.isVisible) {
2400        instance.clearDelayTimeouts();
2401      }
2402    });
2403    popper.addEventListener('mouseleave', function () {
2404      if (instance.props.interactive && instance.props.trigger.indexOf('mouseenter') >= 0) {
2405        getDocument().addEventListener('mousemove', debouncedOnMouseMove);
2406      }
2407    });
2408    return instance; // ===========================================================================
2409    // 🔒 Private methods
2410    // ===========================================================================
2411  
2412    function getNormalizedTouchSettings() {
2413      var touch = instance.props.touch;
2414      return Array.isArray(touch) ? touch : [touch, 0];
2415    }
2416  
2417    function getIsCustomTouchBehavior() {
2418      return getNormalizedTouchSettings()[0] === 'hold';
2419    }
2420  
2421    function getIsDefaultRenderFn() {
2422      var _instance$props$rende; // @ts-ignore
2423  
2424  
2425      return !!((_instance$props$rende = instance.props.render) != null && _instance$props$rende.$$tippy);
2426    }
2427  
2428    function getCurrentTarget() {
2429      return currentTarget || reference;
2430    }
2431  
2432    function getDocument() {
2433      var parent = getCurrentTarget().parentNode;
2434      return parent ? getOwnerDocument(parent) : document;
2435    }
2436  
2437    function getDefaultTemplateChildren() {
2438      return getChildren(popper);
2439    }
2440  
2441    function getDelay(isShow) {
2442      // For touch or keyboard input, force `0` delay for UX reasons
2443      // Also if the instance is mounted but not visible (transitioning out),
2444      // ignore delay
2445      if (instance.state.isMounted && !instance.state.isVisible || currentInput.isTouch || lastTriggerEvent && lastTriggerEvent.type === 'focus') {
2446        return 0;
2447      }
2448  
2449      return getValueAtIndexOrReturn(instance.props.delay, isShow ? 0 : 1, defaultProps.delay);
2450    }
2451  
2452    function handleStyles(fromHide) {
2453      if (fromHide === void 0) {
2454        fromHide = false;
2455      }
2456  
2457      popper.style.pointerEvents = instance.props.interactive && !fromHide ? '' : 'none';
2458      popper.style.zIndex = "" + instance.props.zIndex;
2459    }
2460  
2461    function invokeHook(hook, args, shouldInvokePropsHook) {
2462      if (shouldInvokePropsHook === void 0) {
2463        shouldInvokePropsHook = true;
2464      }
2465  
2466      pluginsHooks.forEach(function (pluginHooks) {
2467        if (pluginHooks[hook]) {
2468          pluginHooks[hook].apply(pluginHooks, args);
2469        }
2470      });
2471  
2472      if (shouldInvokePropsHook) {
2473        var _instance$props;
2474  
2475        (_instance$props = instance.props)[hook].apply(_instance$props, args);
2476      }
2477    }
2478  
2479    function handleAriaContentAttribute() {
2480      var aria = instance.props.aria;
2481  
2482      if (!aria.content) {
2483        return;
2484      }
2485  
2486      var attr = "aria-" + aria.content;
2487      var id = popper.id;
2488      var nodes = normalizeToArray(instance.props.triggerTarget || reference);
2489      nodes.forEach(function (node) {
2490        var currentValue = node.getAttribute(attr);
2491  
2492        if (instance.state.isVisible) {
2493          node.setAttribute(attr, currentValue ? currentValue + " " + id : id);
2494        } else {
2495          var nextValue = currentValue && currentValue.replace(id, '').trim();
2496  
2497          if (nextValue) {
2498            node.setAttribute(attr, nextValue);
2499          } else {
2500            node.removeAttribute(attr);
2501          }
2502        }
2503      });
2504    }
2505  
2506    function handleAriaExpandedAttribute() {
2507      if (hasAriaExpanded || !instance.props.aria.expanded) {
2508        return;
2509      }
2510  
2511      var nodes = normalizeToArray(instance.props.triggerTarget || reference);
2512      nodes.forEach(function (node) {
2513        if (instance.props.interactive) {
2514          node.setAttribute('aria-expanded', instance.state.isVisible && node === getCurrentTarget() ? 'true' : 'false');
2515        } else {
2516          node.removeAttribute('aria-expanded');
2517        }
2518      });
2519    }
2520  
2521    function cleanupInteractiveMouseListeners() {
2522      getDocument().removeEventListener('mousemove', debouncedOnMouseMove);
2523      mouseMoveListeners = mouseMoveListeners.filter(function (listener) {
2524        return listener !== debouncedOnMouseMove;
2525      });
2526    }
2527  
2528    function onDocumentPress(event) {
2529      // Moved finger to scroll instead of an intentional tap outside
2530      if (currentInput.isTouch) {
2531        if (didTouchMove || event.type === 'mousedown') {
2532          return;
2533        }
2534      }
2535  
2536      var actualTarget = event.composedPath && event.composedPath()[0] || event.target; // Clicked on interactive popper
2537  
2538      if (instance.props.interactive && actualContains(popper, actualTarget)) {
2539        return;
2540      } // Clicked on the event listeners target
2541  
2542  
2543      if (normalizeToArray(instance.props.triggerTarget || reference).some(function (el) {
2544        return actualContains(el, actualTarget);
2545      })) {
2546        if (currentInput.isTouch) {
2547          return;
2548        }
2549  
2550        if (instance.state.isVisible && instance.props.trigger.indexOf('click') >= 0) {
2551          return;
2552        }
2553      } else {
2554        invokeHook('onClickOutside', [instance, event]);
2555      }
2556  
2557      if (instance.props.hideOnClick === true) {
2558        instance.clearDelayTimeouts();
2559        instance.hide(); // `mousedown` event is fired right before `focus` if pressing the
2560        // currentTarget. This lets a tippy with `focus` trigger know that it
2561        // should not show
2562  
2563        didHideDueToDocumentMouseDown = true;
2564        setTimeout(function () {
2565          didHideDueToDocumentMouseDown = false;
2566        }); // The listener gets added in `scheduleShow()`, but this may be hiding it
2567        // before it shows, and hide()'s early bail-out behavior can prevent it
2568        // from being cleaned up
2569  
2570        if (!instance.state.isMounted) {
2571          removeDocumentPress();
2572        }
2573      }
2574    }
2575  
2576    function onTouchMove() {
2577      didTouchMove = true;
2578    }
2579  
2580    function onTouchStart() {
2581      didTouchMove = false;
2582    }
2583  
2584    function addDocumentPress() {
2585      var doc = getDocument();
2586      doc.addEventListener('mousedown', onDocumentPress, true);
2587      doc.addEventListener('touchend', onDocumentPress, TOUCH_OPTIONS);
2588      doc.addEventListener('touchstart', onTouchStart, TOUCH_OPTIONS);
2589      doc.addEventListener('touchmove', onTouchMove, TOUCH_OPTIONS);
2590    }
2591  
2592    function removeDocumentPress() {
2593      var doc = getDocument();
2594      doc.removeEventListener('mousedown', onDocumentPress, true);
2595      doc.removeEventListener('touchend', onDocumentPress, TOUCH_OPTIONS);
2596      doc.removeEventListener('touchstart', onTouchStart, TOUCH_OPTIONS);
2597      doc.removeEventListener('touchmove', onTouchMove, TOUCH_OPTIONS);
2598    }
2599  
2600    function onTransitionedOut(duration, callback) {
2601      onTransitionEnd(duration, function () {
2602        if (!instance.state.isVisible && popper.parentNode && popper.parentNode.contains(popper)) {
2603          callback();
2604        }
2605      });
2606    }
2607  
2608    function onTransitionedIn(duration, callback) {
2609      onTransitionEnd(duration, callback);
2610    }
2611  
2612    function onTransitionEnd(duration, callback) {
2613      var box = getDefaultTemplateChildren().box;
2614  
2615      function listener(event) {
2616        if (event.target === box) {
2617          updateTransitionEndListener(box, 'remove', listener);
2618          callback();
2619        }
2620      } // Make callback synchronous if duration is 0
2621      // `transitionend` won't fire otherwise
2622  
2623  
2624      if (duration === 0) {
2625        return callback();
2626      }
2627  
2628      updateTransitionEndListener(box, 'remove', currentTransitionEndListener);
2629      updateTransitionEndListener(box, 'add', listener);
2630      currentTransitionEndListener = listener;
2631    }
2632  
2633    function on(eventType, handler, options) {
2634      if (options === void 0) {
2635        options = false;
2636      }
2637  
2638      var nodes = normalizeToArray(instance.props.triggerTarget || reference);
2639      nodes.forEach(function (node) {
2640        node.addEventListener(eventType, handler, options);
2641        listeners.push({
2642          node: node,
2643          eventType: eventType,
2644          handler: handler,
2645          options: options
2646        });
2647      });
2648    }
2649  
2650    function addListeners() {
2651      if (getIsCustomTouchBehavior()) {
2652        on('touchstart', onTrigger, {
2653          passive: true
2654        });
2655        on('touchend', onMouseLeave, {
2656          passive: true
2657        });
2658      }
2659  
2660      splitBySpaces(instance.props.trigger).forEach(function (eventType) {
2661        if (eventType === 'manual') {
2662          return;
2663        }
2664  
2665        on(eventType, onTrigger);
2666  
2667        switch (eventType) {
2668          case 'mouseenter':
2669            on('mouseleave', onMouseLeave);
2670            break;
2671  
2672          case 'focus':
2673            on(isIE11 ? 'focusout' : 'blur', onBlurOrFocusOut);
2674            break;
2675  
2676          case 'focusin':
2677            on('focusout', onBlurOrFocusOut);
2678            break;
2679        }
2680      });
2681    }
2682  
2683    function removeListeners() {
2684      listeners.forEach(function (_ref) {
2685        var node = _ref.node,
2686            eventType = _ref.eventType,
2687            handler = _ref.handler,
2688            options = _ref.options;
2689        node.removeEventListener(eventType, handler, options);
2690      });
2691      listeners = [];
2692    }
2693  
2694    function onTrigger(event) {
2695      var _lastTriggerEvent;
2696  
2697      var shouldScheduleClickHide = false;
2698  
2699      if (!instance.state.isEnabled || isEventListenerStopped(event) || didHideDueToDocumentMouseDown) {
2700        return;
2701      }
2702  
2703      var wasFocused = ((_lastTriggerEvent = lastTriggerEvent) == null ? void 0 : _lastTriggerEvent.type) === 'focus';
2704      lastTriggerEvent = event;
2705      currentTarget = event.currentTarget;
2706      handleAriaExpandedAttribute();
2707  
2708      if (!instance.state.isVisible && isMouseEvent(event)) {
2709        // If scrolling, `mouseenter` events can be fired if the cursor lands
2710        // over a new target, but `mousemove` events don't get fired. This
2711        // causes interactive tooltips to get stuck open until the cursor is
2712        // moved
2713        mouseMoveListeners.forEach(function (listener) {
2714          return listener(event);
2715        });
2716      } // Toggle show/hide when clicking click-triggered tooltips
2717  
2718  
2719      if (event.type === 'click' && (instance.props.trigger.indexOf('mouseenter') < 0 || isVisibleFromClick) && instance.props.hideOnClick !== false && instance.state.isVisible) {
2720        shouldScheduleClickHide = true;
2721      } else {
2722        scheduleShow(event);
2723      }
2724  
2725      if (event.type === 'click') {
2726        isVisibleFromClick = !shouldScheduleClickHide;
2727      }
2728  
2729      if (shouldScheduleClickHide && !wasFocused) {
2730        scheduleHide(event);
2731      }
2732    }
2733  
2734    function onMouseMove(event) {
2735      var target = event.target;
2736      var isCursorOverReferenceOrPopper = getCurrentTarget().contains(target) || popper.contains(target);
2737  
2738      if (event.type === 'mousemove' && isCursorOverReferenceOrPopper) {
2739        return;
2740      }
2741  
2742      var popperTreeData = getNestedPopperTree().concat(popper).map(function (popper) {
2743        var _instance$popperInsta;
2744  
2745        var instance = popper._tippy;
2746        var state = (_instance$popperInsta = instance.popperInstance) == null ? void 0 : _instance$popperInsta.state;
2747  
2748        if (state) {
2749          return {
2750            popperRect: popper.getBoundingClientRect(),
2751            popperState: state,
2752            props: props
2753          };
2754        }
2755  
2756        return null;
2757      }).filter(Boolean);
2758  
2759      if (isCursorOutsideInteractiveBorder(popperTreeData, event)) {
2760        cleanupInteractiveMouseListeners();
2761        scheduleHide(event);
2762      }
2763    }
2764  
2765    function onMouseLeave(event) {
2766      var shouldBail = isEventListenerStopped(event) || instance.props.trigger.indexOf('click') >= 0 && isVisibleFromClick;
2767  
2768      if (shouldBail) {
2769        return;
2770      }
2771  
2772      if (instance.props.interactive) {
2773        instance.hideWithInteractivity(event);
2774        return;
2775      }
2776  
2777      scheduleHide(event);
2778    }
2779  
2780    function onBlurOrFocusOut(event) {
2781      if (instance.props.trigger.indexOf('focusin') < 0 && event.target !== getCurrentTarget()) {
2782        return;
2783      } // If focus was moved to within the popper
2784  
2785  
2786      if (instance.props.interactive && event.relatedTarget && popper.contains(event.relatedTarget)) {
2787        return;
2788      }
2789  
2790      scheduleHide(event);
2791    }
2792  
2793    function isEventListenerStopped(event) {
2794      return currentInput.isTouch ? getIsCustomTouchBehavior() !== event.type.indexOf('touch') >= 0 : false;
2795    }
2796  
2797    function createPopperInstance() {
2798      destroyPopperInstance();
2799      var _instance$props2 = instance.props,
2800          popperOptions = _instance$props2.popperOptions,
2801          placement = _instance$props2.placement,
2802          offset = _instance$props2.offset,
2803          getReferenceClientRect = _instance$props2.getReferenceClientRect,
2804          moveTransition = _instance$props2.moveTransition;
2805      var arrow = getIsDefaultRenderFn() ? getChildren(popper).arrow : null;
2806      var computedReference = getReferenceClientRect ? {
2807        getBoundingClientRect: getReferenceClientRect,
2808        contextElement: getReferenceClientRect.contextElement || getCurrentTarget()
2809      } : reference;
2810      var tippyModifier = {
2811        name: '$$tippy',
2812        enabled: true,
2813        phase: 'beforeWrite',
2814        requires: ['computeStyles'],
2815        fn: function fn(_ref2) {
2816          var state = _ref2.state;
2817  
2818          if (getIsDefaultRenderFn()) {
2819            var _getDefaultTemplateCh = getDefaultTemplateChildren(),
2820                box = _getDefaultTemplateCh.box;
2821  
2822            ['placement', 'reference-hidden', 'escaped'].forEach(function (attr) {
2823              if (attr === 'placement') {
2824                box.setAttribute('data-placement', state.placement);
2825              } else {
2826                if (state.attributes.popper["data-popper-" + attr]) {
2827                  box.setAttribute("data-" + attr, '');
2828                } else {
2829                  box.removeAttribute("data-" + attr);
2830                }
2831              }
2832            });
2833            state.attributes.popper = {};
2834          }
2835        }
2836      };
2837      var modifiers = [{
2838        name: 'offset',
2839        options: {
2840          offset: offset
2841        }
2842      }, {
2843        name: 'preventOverflow',
2844        options: {
2845          padding: {
2846            top: 2,
2847            bottom: 2,
2848            left: 5,
2849            right: 5
2850          }
2851        }
2852      }, {
2853        name: 'flip',
2854        options: {
2855          padding: 5
2856        }
2857      }, {
2858        name: 'computeStyles',
2859        options: {
2860          adaptive: !moveTransition
2861        }
2862      }, tippyModifier];
2863  
2864      if (getIsDefaultRenderFn() && arrow) {
2865        modifiers.push({
2866          name: 'arrow',
2867          options: {
2868            element: arrow,
2869            padding: 3
2870          }
2871        });
2872      }
2873  
2874      modifiers.push.apply(modifiers, (popperOptions == null ? void 0 : popperOptions.modifiers) || []);
2875      instance.popperInstance = createPopper(computedReference, popper, Object.assign({}, popperOptions, {
2876        placement: placement,
2877        onFirstUpdate: onFirstUpdate,
2878        modifiers: modifiers
2879      }));
2880    }
2881  
2882    function destroyPopperInstance() {
2883      if (instance.popperInstance) {
2884        instance.popperInstance.destroy();
2885        instance.popperInstance = null;
2886      }
2887    }
2888  
2889    function mount() {
2890      var appendTo = instance.props.appendTo;
2891      var parentNode; // By default, we'll append the popper to the triggerTargets's parentNode so
2892      // it's directly after the reference element so the elements inside the
2893      // tippy can be tabbed to
2894      // If there are clipping issues, the user can specify a different appendTo
2895      // and ensure focus management is handled correctly manually
2896  
2897      var node = getCurrentTarget();
2898  
2899      if (instance.props.interactive && appendTo === TIPPY_DEFAULT_APPEND_TO || appendTo === 'parent') {
2900        parentNode = node.parentNode;
2901      } else {
2902        parentNode = invokeWithArgsOrReturn(appendTo, [node]);
2903      } // The popper element needs to exist on the DOM before its position can be
2904      // updated as Popper needs to read its dimensions
2905  
2906  
2907      if (!parentNode.contains(popper)) {
2908        parentNode.appendChild(popper);
2909      }
2910  
2911      instance.state.isMounted = true;
2912      createPopperInstance();
2913    }
2914  
2915    function getNestedPopperTree() {
2916      return arrayFrom(popper.querySelectorAll('[data-tippy-root]'));
2917    }
2918  
2919    function scheduleShow(event) {
2920      instance.clearDelayTimeouts();
2921  
2922      if (event) {
2923        invokeHook('onTrigger', [instance, event]);
2924      }
2925  
2926      addDocumentPress();
2927      var delay = getDelay(true);
2928  
2929      var _getNormalizedTouchSe = getNormalizedTouchSettings(),
2930          touchValue = _getNormalizedTouchSe[0],
2931          touchDelay = _getNormalizedTouchSe[1];
2932  
2933      if (currentInput.isTouch && touchValue === 'hold' && touchDelay) {
2934        delay = touchDelay;
2935      }
2936  
2937      if (delay) {
2938        showTimeout = setTimeout(function () {
2939          instance.show();
2940        }, delay);
2941      } else {
2942        instance.show();
2943      }
2944    }
2945  
2946    function scheduleHide(event) {
2947      instance.clearDelayTimeouts();
2948      invokeHook('onUntrigger', [instance, event]);
2949  
2950      if (!instance.state.isVisible) {
2951        removeDocumentPress();
2952        return;
2953      } // For interactive tippies, scheduleHide is added to a document.body handler
2954      // from onMouseLeave so must intercept scheduled hides from mousemove/leave
2955      // events when trigger contains mouseenter and click, and the tip is
2956      // currently shown as a result of a click.
2957  
2958  
2959      if (instance.props.trigger.indexOf('mouseenter') >= 0 && instance.props.trigger.indexOf('click') >= 0 && ['mouseleave', 'mousemove'].indexOf(event.type) >= 0 && isVisibleFromClick) {
2960        return;
2961      }
2962  
2963      var delay = getDelay(false);
2964  
2965      if (delay) {
2966        hideTimeout = setTimeout(function () {
2967          if (instance.state.isVisible) {
2968            instance.hide();
2969          }
2970        }, delay);
2971      } else {
2972        // Fixes a `transitionend` problem when it fires 1 frame too
2973        // late sometimes, we don't want hide() to be called.
2974        scheduleHideAnimationFrame = requestAnimationFrame(function () {
2975          instance.hide();
2976        });
2977      }
2978    } // ===========================================================================
2979    // 🔑 Public methods
2980    // ===========================================================================
2981  
2982  
2983    function enable() {
2984      instance.state.isEnabled = true;
2985    }
2986  
2987    function disable() {
2988      // Disabling the instance should also hide it
2989      // https://github.com/atomiks/tippy.js-react/issues/106
2990      instance.hide();
2991      instance.state.isEnabled = false;
2992    }
2993  
2994    function clearDelayTimeouts() {
2995      clearTimeout(showTimeout);
2996      clearTimeout(hideTimeout);
2997      cancelAnimationFrame(scheduleHideAnimationFrame);
2998    }
2999  
3000    function setProps(partialProps) {
3001      if (instance.state.isDestroyed) {
3002        return;
3003      }
3004  
3005      invokeHook('onBeforeUpdate', [instance, partialProps]);
3006      removeListeners();
3007      var prevProps = instance.props;
3008      var nextProps = evaluateProps(reference, Object.assign({}, prevProps, removeUndefinedProps(partialProps), {
3009        ignoreAttributes: true
3010      }));
3011      instance.props = nextProps;
3012      addListeners();
3013  
3014      if (prevProps.interactiveDebounce !== nextProps.interactiveDebounce) {
3015        cleanupInteractiveMouseListeners();
3016        debouncedOnMouseMove = debounce(onMouseMove, nextProps.interactiveDebounce);
3017      } // Ensure stale aria-expanded attributes are removed
3018  
3019  
3020      if (prevProps.triggerTarget && !nextProps.triggerTarget) {
3021        normalizeToArray(prevProps.triggerTarget).forEach(function (node) {
3022          node.removeAttribute('aria-expanded');
3023        });
3024      } else if (nextProps.triggerTarget) {
3025        reference.removeAttribute('aria-expanded');
3026      }
3027  
3028      handleAriaExpandedAttribute();
3029      handleStyles();
3030  
3031      if (onUpdate) {
3032        onUpdate(prevProps, nextProps);
3033      }
3034  
3035      if (instance.popperInstance) {
3036        createPopperInstance(); // Fixes an issue with nested tippies if they are all getting re-rendered,
3037        // and the nested ones get re-rendered first.
3038        // https://github.com/atomiks/tippyjs-react/issues/177
3039        // TODO: find a cleaner / more efficient solution(!)
3040  
3041        getNestedPopperTree().forEach(function (nestedPopper) {
3042          // React (and other UI libs likely) requires a rAF wrapper as it flushes
3043          // its work in one
3044          requestAnimationFrame(nestedPopper._tippy.popperInstance.forceUpdate);
3045        });
3046      }
3047  
3048      invokeHook('onAfterUpdate', [instance, partialProps]);
3049    }
3050  
3051    function setContent(content) {
3052      instance.setProps({
3053        content: content
3054      });
3055    }
3056  
3057    function show() {
3058      var isAlreadyVisible = instance.state.isVisible;
3059      var isDestroyed = instance.state.isDestroyed;
3060      var isDisabled = !instance.state.isEnabled;
3061      var isTouchAndTouchDisabled = currentInput.isTouch && !instance.props.touch;
3062      var duration = getValueAtIndexOrReturn(instance.props.duration, 0, defaultProps.duration);
3063  
3064      if (isAlreadyVisible || isDestroyed || isDisabled || isTouchAndTouchDisabled) {
3065        return;
3066      } // Normalize `disabled` behavior across browsers.
3067      // Firefox allows events on disabled elements, but Chrome doesn't.
3068      // Using a wrapper element (i.e. <span>) is recommended.
3069  
3070  
3071      if (getCurrentTarget().hasAttribute('disabled')) {
3072        return;
3073      }
3074  
3075      invokeHook('onShow', [instance], false);
3076  
3077      if (instance.props.onShow(instance) === false) {
3078        return;
3079      }
3080  
3081      instance.state.isVisible = true;
3082  
3083      if (getIsDefaultRenderFn()) {
3084        popper.style.visibility = 'visible';
3085      }
3086  
3087      handleStyles();
3088      addDocumentPress();
3089  
3090      if (!instance.state.isMounted) {
3091        popper.style.transition = 'none';
3092      } // If flipping to the opposite side after hiding at least once, the
3093      // animation will use the wrong placement without resetting the duration
3094  
3095  
3096      if (getIsDefaultRenderFn()) {
3097        var _getDefaultTemplateCh2 = getDefaultTemplateChildren(),
3098            box = _getDefaultTemplateCh2.box,
3099            content = _getDefaultTemplateCh2.content;
3100  
3101        setTransitionDuration([box, content], 0);
3102      }
3103  
3104      onFirstUpdate = function onFirstUpdate() {
3105        var _instance$popperInsta2;
3106  
3107        if (!instance.state.isVisible || ignoreOnFirstUpdate) {
3108          return;
3109        }
3110  
3111        ignoreOnFirstUpdate = true; // reflow
3112  
3113        void popper.offsetHeight;
3114        popper.style.transition = instance.props.moveTransition;
3115  
3116        if (getIsDefaultRenderFn() && instance.props.animation) {
3117          var _getDefaultTemplateCh3 = getDefaultTemplateChildren(),
3118              _box = _getDefaultTemplateCh3.box,
3119              _content = _getDefaultTemplateCh3.content;
3120  
3121          setTransitionDuration([_box, _content], duration);
3122          setVisibilityState([_box, _content], 'visible');
3123        }
3124  
3125        handleAriaContentAttribute();
3126        handleAriaExpandedAttribute();
3127        pushIfUnique(mountedInstances, instance); // certain modifiers (e.g. `maxSize`) require a second update after the
3128        // popper has been positioned for the first time
3129  
3130        (_instance$popperInsta2 = instance.popperInstance) == null ? void 0 : _instance$popperInsta2.forceUpdate();
3131        invokeHook('onMount', [instance]);
3132  
3133        if (instance.props.animation && getIsDefaultRenderFn()) {
3134          onTransitionedIn(duration, function () {
3135            instance.state.isShown = true;
3136            invokeHook('onShown', [instance]);
3137          });
3138        }
3139      };
3140  
3141      mount();
3142    }
3143  
3144    function hide() {
3145      var isAlreadyHidden = !instance.state.isVisible;
3146      var isDestroyed = instance.state.isDestroyed;
3147      var isDisabled = !instance.state.isEnabled;
3148      var duration = getValueAtIndexOrReturn(instance.props.duration, 1, defaultProps.duration);
3149  
3150      if (isAlreadyHidden || isDestroyed || isDisabled) {
3151        return;
3152      }
3153  
3154      invokeHook('onHide', [instance], false);
3155  
3156      if (instance.props.onHide(instance) === false) {
3157        return;
3158      }
3159  
3160      instance.state.isVisible = false;
3161      instance.state.isShown = false;
3162      ignoreOnFirstUpdate = false;
3163      isVisibleFromClick = false;
3164  
3165      if (getIsDefaultRenderFn()) {
3166        popper.style.visibility = 'hidden';
3167      }
3168  
3169      cleanupInteractiveMouseListeners();
3170      removeDocumentPress();
3171      handleStyles(true);
3172  
3173      if (getIsDefaultRenderFn()) {
3174        var _getDefaultTemplateCh4 = getDefaultTemplateChildren(),
3175            box = _getDefaultTemplateCh4.box,
3176            content = _getDefaultTemplateCh4.content;
3177  
3178        if (instance.props.animation) {
3179          setTransitionDuration([box, content], duration);
3180          setVisibilityState([box, content], 'hidden');
3181        }
3182      }
3183  
3184      handleAriaContentAttribute();
3185      handleAriaExpandedAttribute();
3186  
3187      if (instance.props.animation) {
3188        if (getIsDefaultRenderFn()) {
3189          onTransitionedOut(duration, instance.unmount);
3190        }
3191      } else {
3192        instance.unmount();
3193      }
3194    }
3195  
3196    function hideWithInteractivity(event) {
3197      getDocument().addEventListener('mousemove', debouncedOnMouseMove);
3198      pushIfUnique(mouseMoveListeners, debouncedOnMouseMove);
3199      debouncedOnMouseMove(event);
3200    }
3201  
3202    function unmount() {
3203      if (instance.state.isVisible) {
3204        instance.hide();
3205      }
3206  
3207      if (!instance.state.isMounted) {
3208        return;
3209      }
3210  
3211      destroyPopperInstance(); // If a popper is not interactive, it will be appended outside the popper
3212      // tree by default. This seems mainly for interactive tippies, but we should
3213      // find a workaround if possible
3214  
3215      getNestedPopperTree().forEach(function (nestedPopper) {
3216        nestedPopper._tippy.unmount();
3217      });
3218  
3219      if (popper.parentNode) {
3220        popper.parentNode.removeChild(popper);
3221      }
3222  
3223      mountedInstances = mountedInstances.filter(function (i) {
3224        return i !== instance;
3225      });
3226      instance.state.isMounted = false;
3227      invokeHook('onHidden', [instance]);
3228    }
3229  
3230    function destroy() {
3231      if (instance.state.isDestroyed) {
3232        return;
3233      }
3234  
3235      instance.clearDelayTimeouts();
3236      instance.unmount();
3237      removeListeners();
3238      delete reference._tippy;
3239      instance.state.isDestroyed = true;
3240      invokeHook('onDestroy', [instance]);
3241    }
3242  }
3243  
3244  function tippy(targets, optionalProps) {
3245    if (optionalProps === void 0) {
3246      optionalProps = {};
3247    }
3248  
3249    var plugins = defaultProps.plugins.concat(optionalProps.plugins || []);
3250    bindGlobalEventListeners();
3251    var passedProps = Object.assign({}, optionalProps, {
3252      plugins: plugins
3253    });
3254    var elements = getArrayOfElements(targets);
3255    var instances = elements.reduce(function (acc, reference) {
3256      var instance = reference && createTippy(reference, passedProps);
3257  
3258      if (instance) {
3259        acc.push(instance);
3260      }
3261  
3262      return acc;
3263    }, []);
3264    return isElement(targets) ? instances[0] : instances;
3265  }
3266  
3267  tippy.defaultProps = defaultProps;
3268  tippy.setDefaultProps = setDefaultProps;
3269  tippy.currentInput = currentInput; // every time the popper is destroyed (i.e. a new target), removing the styles
3270  // and causing transitions to break for singletons when the console is open, but
3271  // most notably for non-transform styles being used, `gpuAcceleration: false`.
3272  
3273  Object.assign({}, applyStyles$1, {
3274    effect: function effect(_ref) {
3275      var state = _ref.state;
3276      var initialStyles = {
3277        popper: {
3278          position: state.options.strategy,
3279          left: '0',
3280          top: '0',
3281          margin: '0'
3282        },
3283        arrow: {
3284          position: 'absolute'
3285        },
3286        reference: {}
3287      };
3288      Object.assign(state.elements.popper.style, initialStyles.popper);
3289      state.styles = initialStyles;
3290  
3291      if (state.elements.arrow) {
3292        Object.assign(state.elements.arrow.style, initialStyles.arrow);
3293      } // intentionally return no cleanup function
3294      // return () => { ... }
3295  
3296    }
3297  });
3298  tippy.setDefaultProps({
3299    render: render
3300  }); // import 'tippy.js/dist/tippy.css';
3301  
3302  /**

3303   * Utility methods

3304   */
3305  // Determine element visibility
3306  
3307  const isElementHidden = $el => {
3308    if ($el.getAttribute('hidden') || $el.offsetWidth === 0 && $el.offsetHeight === 0) {
3309      return true;
3310    } else {
3311      const compStyles = getComputedStyle($el);
3312      return compStyles.getPropertyValue('display') === 'none';
3313    }
3314  }; // Escape HTML, encode HTML symbols
3315  
3316  
3317  const escapeHTML = text => {
3318    const $div = document.createElement('div');
3319    $div.textContent = text;
3320    return $div.innerHTML.replaceAll('"', '&quot;').replaceAll("'", '&#039;').replaceAll("`", '&#x60;');
3321  };
3322  /**

3323   * Jooa11y Translation object

3324   */
3325  
3326  
3327  const Lang = {
3328    langStrings: {},
3329    addI18n: function (strings) {
3330      this.langStrings = strings;
3331    },
3332    _: function (string) {
3333      return this.translate(string);
3334    },
3335    sprintf: function (string, ...args) {
3336      let transString = this._(string);
3337  
3338      if (args && args.length) {
3339        args.forEach(arg => {
3340          transString = transString.replace(/%\([a-zA-z]+\)/, arg);
3341        });
3342      }
3343  
3344      return transString;
3345    },
3346    translate: function (string) {
3347      return this.langStrings[string] || string;
3348    }
3349  };
3350  /**

3351   * Jooa11y default options

3352   */
3353  
3354  const defaultOptions = {
3355    langCode: 'en',
3356    // Target area to scan.
3357    checkRoot: 'main',
3358    // A content container
3359    // Readability configuration.
3360    readabilityRoot: 'main',
3361    readabilityLang: 'en',
3362    // Inclusions and exclusions. Use commas to seperate classes or elements.
3363    containerIgnore: '.jooa11y-ignore',
3364    // Ignore specific regions.
3365    outlineIgnore: '',
3366    // Exclude headings from outline panel.
3367    headerIgnore: '',
3368    // Ignore specific headings. E.g. "h1.jumbotron-heading"
3369    imageIgnore: '',
3370    // Ignore specific images.
3371    linkIgnore: '',
3372    // Ignore specific links.
3373    linkIgnoreSpan: 'noscript, span.sr-only-example',
3374    // Ignore specific classes within links. Example: <a href="#">learn more <span class="sr-only-example">(opens new tab)</span></a>.
3375    linksToFlag: '',
3376    // Links you don't want your content editors pointing to (e.g. development environments).
3377    // Embedded content.
3378    videoContent: "video, [src*='youtube.com'], [src*='vimeo.com'], [src*='yuja.com'], [src*='panopto.com']",
3379    audioContent: "audio, [src*='soundcloud.com'], [src*='simplecast.com'], [src*='podbean.com'], [src*='buzzsprout.com'], [src*='blubrry.com'], [src*='transistor.fm'], [src*='fusebox.fm'], [src*='libsyn.com']",
3380    embeddedContent: '',
3381    // Alt Text stop words.
3382    suspiciousAltWords: ['image', 'graphic', 'picture', 'photo'],
3383    placeholderAltStopWords: ['alt', 'image', 'photo', 'decorative', 'photo', 'placeholder', 'placeholder image', 'spacer', '.'],
3384    // Link Text stop words
3385    partialAltStopWords: ['click', 'click here', 'click here for more', 'click here to learn more', 'click here to learn more.', 'check out', 'download', 'download here', 'download here.', 'find out', 'find out more', 'find out more.', 'form', 'here', 'here.', 'info', 'information', 'link', 'learn', 'learn more', 'learn more.', 'learn to', 'more', 'page', 'paper', 'read more', 'read', 'read this', 'this', 'this page', 'this page.', 'this website', 'this website.', 'view', 'view our', 'website', '.'],
3386    warningAltWords: ['< ', ' >', 'click here'],
3387    // Link Text (Advanced)
3388    newWindowPhrases: ['external', 'new tab', 'new window', 'pop-up', 'pop up'],
3389    // Link Text (Advanced). Only some items in list would need to be translated.
3390    fileTypePhrases: ['document', 'pdf', 'doc', 'docx', 'word', 'mp3', 'ppt', 'text', 'pptx', 'powerpoint', 'txt', 'exe', 'dmg', 'rtf', 'install', 'windows', 'macos', 'spreadsheet', 'worksheet', 'csv', 'xls', 'xlsx', 'video', 'mp4', 'mov', 'avi']
3391  };
3392  defaultOptions.embeddedContent = `$defaultOptions.videoContent}, $defaultOptions.audioContent}`;
3393  /**

3394   * Load and validate options

3395   *

3396   * @param {Jooa11y}  instance

3397   * @param {Object} customOptions

3398   * @returns {Object}

3399   */
3400  
3401  const loadOptions = (instance, customOptions) => {
3402    const options = customOptions ? Object.assign(defaultOptions, customOptions) : defaultOptions; // Check required options
3403  
3404    ['langCode', 'checkRoot'].forEach(option => {
3405      if (!options[option]) {
3406        throw new Error(`Option [$option}] is required`);
3407      }
3408    });
3409  
3410    if (!options.readabilityRoot) {
3411      options.readabilityRoot = options.checkRoot;
3412    } // Container ignores apply to self and children.
3413  
3414  
3415    if (options.containerIgnore) {
3416      let containerSelectors = options.containerIgnore.split(',').map(el => {
3417        return `$el} *, $el}`;
3418      });
3419      options.containerIgnore = '[aria-hidden="true"], #jooa11y-container *, .jooa11y-instance *, ' + containerSelectors.join(', ');
3420    } else {
3421      options.containerIgnore = '[aria-hidden="true"], #jooa11y-container *, .jooa11y-instance *';
3422    }
3423  
3424    instance.containerIgnore = options.containerIgnore; // Images ignore
3425  
3426    instance.imageIgnore = instance.containerIgnore + ', [role="presentation"], [src^="https://trck.youvisit.com"]';
3427  
3428    if (options.imageIgnore) {
3429      instance.imageIgnore = options.imageIgnore + ',' + instance.imageIgnore;
3430    } // Ignore specific headings
3431  
3432  
3433    instance.headerIgnore = options.containerIgnore;
3434  
3435    if (options.headerIgnore) {
3436      instance.headerIgnore = options.headerIgnore + ',' + instance.headerIgnore;
3437    } // Links ignore defaults plus jooa11y links.
3438  
3439  
3440    instance.linkIgnore = instance.containerIgnore + ', [aria-hidden="true"], .anchorjs-link';
3441  
3442    if (options.linkIgnore) {
3443      instance.linkIgnore = options.linkIgnore + ',' + instance.linkIgnore;
3444    }
3445  
3446    return options;
3447  };
3448  /**

3449   * Jooa11y class

3450   */
3451  
3452  
3453  class Jooa11y {
3454    constructor(options) {
3455      this.checkAll = async () => {
3456        this.errorCount = 0;
3457        this.warningCount = 0;
3458        this.$root = document.querySelector(this.options.checkRoot);
3459        this.findElements(); //Ruleset checks
3460  
3461        this.checkHeaders();
3462        this.checkLinkText();
3463        this.checkUnderline();
3464        this.checkAltText();
3465  
3466        if (localStorage.getItem("jooa11y-remember-contrast") === "On") {
3467          this.checkContrast();
3468        }
3469  
3470        if (localStorage.getItem("jooa11y-remember-labels") === "On") {
3471          this.checkLabels();
3472        }
3473  
3474        if (localStorage.getItem("jooa11y-remember-links-advanced") === "On") {
3475          this.checkLinksAdvanced();
3476        }
3477  
3478        if (localStorage.getItem("jooa11y-remember-readability") === "On") {
3479          this.checkReadability();
3480        }
3481  
3482        this.checkEmbeddedContent();
3483        this.checkQA(); //Update panel
3484  
3485        if (this.panelActive) {
3486          this.resetAll();
3487        } else {
3488          this.updatePanel();
3489        }
3490  
3491        this.initializeTooltips();
3492        this.detectOverflow();
3493        this.nudge(); //Don't show badge when panel is opened.
3494  
3495        if (!document.getElementsByClassName('jooa11y-on').length) {
3496          this.updateBadge();
3497        }
3498      };
3499  
3500      this.nudge = () => {
3501        const jooa11yInstance = document.querySelectorAll('.jooa11y-instance, .jooa11y-instance-inline');
3502        jooa11yInstance.forEach($el => {
3503          const sibling = $el.nextElementSibling;
3504  
3505          if (sibling !== null && (sibling.classList.contains("jooa11y-instance") || sibling.classList.contains("jooa11y-instance-inline"))) {
3506            sibling.querySelector("button").setAttribute("style", "margin: -10px -20px !important;");
3507          }
3508        });
3509      };
3510  
3511      this.buildPanel = () => {
3512        const $outlineToggle = document.getElementById("jooa11y-outline-toggle");
3513        const $outlinePanel = document.getElementById("jooa11y-outline-panel");
3514        const $outlineList = document.getElementById("jooa11y-outline-list");
3515        const $settingsToggle = document.getElementById("jooa11y-settings-toggle");
3516        const $settingsPanel = document.getElementById("jooa11y-settings-panel");
3517        const $settingsContent = document.getElementById("jooa11y-settings-content");
3518        const $headingAnnotations = document.querySelectorAll(".jooa11y-heading-label"); //Show outline panel
3519  
3520        $outlineToggle.addEventListener('click', () => {
3521          if ($outlineToggle.getAttribute("aria-expanded") === "true") {
3522            $outlineToggle.classList.remove("jooa11y-outline-active");
3523            $outlinePanel.classList.remove("jooa11y-active");
3524            $outlineToggle.textContent = Lang._('SHOW_OUTLINE');
3525            $outlineToggle.setAttribute("aria-expanded", "false");
3526            localStorage.setItem("jooa11y-remember-outline", "Closed");
3527          } else {
3528            $outlineToggle.classList.add("jooa11y-outline-active");
3529            $outlinePanel.classList.add("jooa11y-active");
3530            $outlineToggle.textContent = Lang._('HIDE_OUTLINE');
3531            $outlineToggle.setAttribute("aria-expanded", "true");
3532            localStorage.setItem("jooa11y-remember-outline", "Opened");
3533          } //Set focus on Page Outline heading for accessibility.
3534  
3535  
3536          document.querySelector("#jooa11y-outline-header > h2").focus(); //Show heading level annotations.
3537  
3538          $headingAnnotations.forEach($el => $el.classList.toggle("jooa11y-label-visible")); //Close Settings panel when Show Outline is active.
3539  
3540          $settingsPanel.classList.remove("jooa11y-active");
3541          $settingsToggle.classList.remove("jooa11y-settings-active");
3542          $settingsToggle.setAttribute("aria-expanded", "false");
3543          $settingsToggle.textContent = Lang._('SHOW_SETTINGS'); //Keyboard accessibility fix for scrollable panel content.
3544  
3545          if ($outlineList.clientHeight > 250) {
3546            $outlineList.setAttribute("tabindex", "0");
3547          }
3548        }); //Remember to leave outline open
3549  
3550        if (localStorage.getItem("jooa11y-remember-outline") === "Opened") {
3551          $outlineToggle.classList.add("jooa11y-outline-active");
3552          $outlinePanel.classList.add("jooa11y-active");
3553          $outlineToggle.textContent = Lang._('HIDE_OUTLINE');
3554          $outlineToggle.setAttribute("aria-expanded", "true");
3555          $headingAnnotations.forEach($el => $el.classList.toggle("jooa11y-label-visible")); //Keyboard accessibility fix for scrollable panel content.
3556  
3557          if ($outlineList.clientHeight > 250) {
3558            $outlineList.setAttribute("tabindex", "0");
3559          }
3560        } //Show settings panel
3561  
3562  
3563        $settingsToggle.addEventListener('click', () => {
3564          if ($settingsToggle.getAttribute("aria-expanded") === "true") {
3565            $settingsToggle.classList.remove("jooa11y-settings-active");
3566            $settingsPanel.classList.remove("jooa11y-active");
3567            $settingsToggle.textContent = Lang._('SHOW_SETTINGS');
3568            $settingsToggle.setAttribute("aria-expanded", "false");
3569          } else {
3570            $settingsToggle.classList.add("jooa11y-settings-active");
3571            $settingsPanel.classList.add("jooa11y-active");
3572            $settingsToggle.textContent = Lang._('HIDE_SETTINGS');
3573            $settingsToggle.setAttribute("aria-expanded", "true");
3574          } //Set focus on Settings heading for accessibility.
3575  
3576  
3577          document.querySelector("#jooa11y-settings-header > h2").focus(); //Close Show Outline panel when Settings is active.
3578  
3579          $outlinePanel.classList.remove("jooa11y-active");
3580          $outlineToggle.classList.remove("jooa11y-outline-active");
3581          $outlineToggle.setAttribute("aria-expanded", "false");
3582          $outlineToggle.textContent = Lang._('SHOW_OUTLINE');
3583          $headingAnnotations.forEach($el => $el.classList.remove("jooa11y-label-visible"));
3584          localStorage.setItem("jooa11y-remember-outline", "Closed"); //Keyboard accessibility fix for scrollable panel content.
3585  
3586          if ($settingsContent.clientHeight > 350) {
3587            $settingsContent.setAttribute("tabindex", "0");
3588          }
3589        }); //Enhanced keyboard accessibility for panel.
3590  
3591        document.getElementById('jooa11y-panel-controls').addEventListener('keydown', function (e) {
3592          const $tab = document.querySelectorAll('#jooa11y-outline-toggle[role=tab], #jooa11y-settings-toggle[role=tab]');
3593  
3594          if (e.key === 'ArrowRight') {
3595            for (let i = 0; i < $tab.length; i++) {
3596              if ($tab[i].getAttribute('aria-expanded') === "true" || $tab[i].getAttribute('aria-expanded') === "false") {
3597                $tab[i + 1].focus();
3598                e.preventDefault();
3599                break;
3600              }
3601            }
3602          }
3603  
3604          if (e.key === 'ArrowDown') {
3605            for (let i = 0; i < $tab.length; i++) {
3606              if ($tab[i].getAttribute('aria-expanded') === "true" || $tab[i].getAttribute('aria-expanded') === "false") {
3607                $tab[i + 1].focus();
3608                e.preventDefault();
3609                break;
3610              }
3611            }
3612          }
3613  
3614          if (e.key === 'ArrowLeft') {
3615            for (let i = $tab.length - 1; i > 0; i--) {
3616              if ($tab[i].getAttribute('aria-expanded') === "true" || $tab[i].getAttribute('aria-expanded') === "false") {
3617                $tab[i - 1].focus();
3618                e.preventDefault();
3619                break;
3620              }
3621            }
3622          }
3623  
3624          if (e.key === 'ArrowUp') {
3625            for (let i = $tab.length - 1; i > 0; i--) {
3626              if ($tab[i].getAttribute('aria-expanded') === "true" || $tab[i].getAttribute('aria-expanded') === "false") {
3627                $tab[i - 1].focus();
3628                e.preventDefault();
3629                break;
3630              }
3631            }
3632          }
3633        });
3634        const $closeAlertToggle = document.getElementById("jooa11y-close-alert");
3635        const $alertPanel = document.getElementById("jooa11y-panel-alert");
3636        const $alertText = document.getElementById("jooa11y-panel-alert-text");
3637        const $jooa11ySkipBtn = document.getElementById("jooa11y-cycle-toggle");
3638        $closeAlertToggle.addEventListener('click', () => {
3639          $alertPanel.classList.remove("jooa11y-active");
3640  
3641          while ($alertText.firstChild) $alertText.removeChild($alertText.firstChild);
3642  
3643          document.querySelectorAll('.jooa11y-pulse-border').forEach(el => el.classList.remove('jooa11y-pulse-border'));
3644          $jooa11ySkipBtn.focus();
3645        });
3646      };
3647  
3648      this.skipToIssue = () => {
3649        /* Polyfill for scrollTo. scrollTo instead of .animate(), so Jooa11y could use jQuery slim build. Credit: https://stackoverflow.com/a/67108752 & https://github.com/iamdustan/smoothscroll */
3650        //let reducedMotionQuery = false;
3651        //let scrollBehavior = 'smooth';
3652  
3653        /*

3654        if (!('scrollBehavior' in document.documentElement.style)) {

3655            var js = document.createElement('script');

3656            js.src = "https://cdn.jsdelivr.net/npm/[email protected]/dist/smoothscroll.min.js";

3657            document.head.appendChild(js);

3658        }

3659        if (!(document.documentMode)) {

3660            if (typeof window.matchMedia === "function") {

3661                reducedMotionQuery = window.matchMedia("(prefers-reduced-motion: reduce)");

3662            }

3663            if (!reducedMotionQuery || reducedMotionQuery.matches) {

3664                scrollBehavior = "auto";

3665            }

3666        }

3667        */
3668        let jooa11yBtnLocation = 0;
3669        const findJooa11yBtn = document.querySelectorAll('.jooa11y-btn').length; //Jump to issue using keyboard shortcut.
3670  
3671        document.addEventListener('keyup', e => {
3672          if (e.altKey && e.code === "Period" || e.code == "KeyS") {
3673            skipToIssueToggle();
3674            e.preventDefault();
3675          }
3676        }); //Jump to issue using click.
3677  
3678        const $skipToggle = document.getElementById("jooa11y-cycle-toggle");
3679        $skipToggle.addEventListener('click', e => {
3680          skipToIssueToggle();
3681          e.preventDefault();
3682        });
3683  
3684        const skipToIssueToggle = function () {
3685          //Calculate location of both visible and hidden buttons.
3686          const $findButtons = document.querySelectorAll('.jooa11y-btn');
3687          const $alertPanel = document.getElementById("jooa11y-panel-alert");
3688          const $alertText = document.getElementById("jooa11y-panel-alert-text");
3689          const $alertPanelPreview = document.getElementById("jooa11y-panel-alert-preview"); //const $closeAlertToggle = document.getElementById("jooa11y-close-alert");
3690          //Mini function: Find visibible parent of hidden element.
3691  
3692          const findVisibleParent = ($el, property, value) => {
3693            while ($el !== null) {
3694              const style = window.getComputedStyle($el);
3695              const propValue = style.getPropertyValue(property);
3696  
3697              if (propValue === value) {
3698                return $el;
3699              }
3700  
3701              $el = $el.parentElement;
3702            }
3703  
3704            return null;
3705          }; //Mini function: Calculate top of element.
3706  
3707  
3708          const offset = $el => {
3709            let rect = $el.getBoundingClientRect(),
3710                scrollTop = window.pageYOffset || document.documentElement.scrollTop;
3711            return {
3712              top: rect.top + scrollTop
3713            };
3714          }; //'offsetTop' will always return 0 if element is hidden. We rely on offsetTop to determine if element is hidden, although we use 'getBoundingClientRect' to set the scroll position.
3715  
3716  
3717          let scrollPosition;
3718          let offsetTopPosition = $findButtons[jooa11yBtnLocation].offsetTop;
3719  
3720          if (offsetTopPosition === 0) {
3721            let visiblePosition = findVisibleParent($findButtons[jooa11yBtnLocation], 'display', 'none');
3722            scrollPosition = offset(visiblePosition.previousElementSibling).top - 50;
3723          } else {
3724            scrollPosition = offset($findButtons[jooa11yBtnLocation]).top - 50;
3725          } //Scroll to element if offsetTop is less than or equal to 0.
3726  
3727  
3728          if (offsetTopPosition >= 0) {
3729            setTimeout(function () {
3730              window.scrollTo({
3731                top: scrollPosition,
3732                behavior: 'smooth'
3733              });
3734            }, 1); //Add pulsing border to visible parent of hidden element.
3735  
3736            $findButtons.forEach(function ($el) {
3737              const overflowing = findVisibleParent($el, 'display', 'none');
3738  
3739              if (overflowing !== null) {
3740                let hiddenparent = overflowing.previousElementSibling;
3741                hiddenparent.classList.add("jooa11y-pulse-border");
3742              }
3743            });
3744            $findButtons[jooa11yBtnLocation].focus();
3745          } else {
3746            $findButtons[jooa11yBtnLocation].focus();
3747          } //Alert if element is hidden.
3748  
3749  
3750          if (offsetTopPosition === 0) {
3751            $alertPanel.classList.add("jooa11y-active");
3752            $alertText.textContent = `$Lang._('PANEL_STATUS_HIDDEN')}`;
3753            $alertPanelPreview.innerHTML = $findButtons[jooa11yBtnLocation].getAttribute('data-tippy-content');
3754          } else if (offsetTopPosition < 1) {
3755            $alertPanel.classList.remove("jooa11y-active");
3756            document.querySelectorAll('.jooa11y-pulse-border').forEach($el => $el.classList.remove('jooa11y-pulse-border'));
3757          } //Reset index so it scrolls back to top of page.
3758  
3759  
3760          jooa11yBtnLocation += 1;
3761  
3762          if (jooa11yBtnLocation >= findJooa11yBtn) {
3763            jooa11yBtnLocation = 0;
3764          }
3765        };
3766      };
3767  
3768      this.containerIgnore = '';
3769      this.imageIgnore = '';
3770      this.headerIgnore = '';
3771      this.linkIgnore = ''; // Load options
3772  
3773      this.options = loadOptions(this, options); //Icon on the main toggle. Easy to replace.
3774  
3775      const MainToggleIcon = "<svg role='img' focusable='false' width='35px' height='35px' aria-hidden='true' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><path fill='#ffffff' d='M256 48c114.953 0 208 93.029 208 208 0 114.953-93.029 208-208 208-114.953 0-208-93.029-208-208 0-114.953 93.029-208 208-208m0-40C119.033 8 8 119.033 8 256s111.033 248 248 248 248-111.033 248-248S392.967 8 256 8zm0 56C149.961 64 64 149.961 64 256s85.961 192 192 192 192-85.961 192-192S362.039 64 256 64zm0 44c19.882 0 36 16.118 36 36s-16.118 36-36 36-36-16.118-36-36 16.118-36 36-36zm117.741 98.023c-28.712 6.779-55.511 12.748-82.14 15.807.851 101.023 12.306 123.052 25.037 155.621 3.617 9.26-.957 19.698-10.217 23.315-9.261 3.617-19.699-.957-23.316-10.217-8.705-22.308-17.086-40.636-22.261-78.549h-9.686c-5.167 37.851-13.534 56.208-22.262 78.549-3.615 9.255-14.05 13.836-23.315 10.217-9.26-3.617-13.834-14.056-10.217-23.315 12.713-32.541 24.185-54.541 25.037-155.621-26.629-3.058-53.428-9.027-82.141-15.807-8.6-2.031-13.926-10.648-11.895-19.249s10.647-13.926 19.249-11.895c96.686 22.829 124.283 22.783 220.775 0 8.599-2.03 17.218 3.294 19.249 11.895 2.029 8.601-3.297 17.219-11.897 19.249z'/></svg>";
3776      const jooa11ycontainer = document.createElement("div");
3777      jooa11ycontainer.setAttribute("id", "jooa11y-container");
3778      jooa11ycontainer.setAttribute("role", "region");
3779      jooa11ycontainer.setAttribute("lang", this.options.langCode);
3780      jooa11ycontainer.setAttribute("aria-label", Lang._('CONTAINER_LABEL'));
3781      let loadContrastPreference = localStorage.getItem("jooa11y-remember-contrast") === "On";
3782      let loadLabelsPreference = localStorage.getItem("jooa11y-remember-labels") === "On";
3783      let loadChangeRequestPreference = localStorage.getItem("jooa11y-remember-links-advanced") === "On";
3784      let loadReadabilityPreference = localStorage.getItem("jooa11y-remember-readability") === "On";
3785      jooa11ycontainer.innerHTML = //Main toggle button.
3786      `<button type="button" aria-expanded="false" id="jooa11y-toggle" aria-describedby="jooa11y-notification-badge" aria-label="$Lang._('MAIN_TOGGLE_LABEL')}">
3787                      $MainToggleIcon}
3788                      <div id="jooa11y-notification-badge">
3789                          <span id="jooa11y-notification-count"></span>
3790                      </div>
3791                  </button>` + //Start of main container.
3792      `<div id="jooa11y-panel">` + //Page Outline tab.
3793      `<div id="jooa11y-outline-panel" role="tabpanel" aria-labelledby="jooa11y-outline-header">
3794                  <div id="jooa11y-outline-header" class="jooa11y-header-text">
3795                      <h2 tabindex="-1">$Lang._('PAGE_OUTLINE')}</h2>
3796                  </div>
3797                  <div id="jooa11y-outline-content">
3798                      <ul id="jooa11y-outline-list"></ul>
3799                  </div>` + //Readability tab.
3800      `<div id="jooa11y-readability-panel">
3801                      <div id="jooa11y-readability-content">
3802                          <h2 class="jooa11y-header-text-inline">$Lang._('READABILITY')}</h2>
3803                          <p id="jooa11y-readability-info"></p>
3804                          <ul id="jooa11y-readability-details"></ul>
3805                      </div>
3806                  </div>
3807              </div>` + //End of Page Outline tab.
3808      //Settings tab.
3809      `<div id="jooa11y-settings-panel" role="tabpanel" aria-labelledby="jooa11y-settings-header">
3810                  <div id="jooa11y-settings-header" class="jooa11y-header-text">
3811                      <h2 tabindex="-1">$Lang._('SETTINGS')}</h2>
3812                  </div>
3813                  <div id="jooa11y-settings-content">
3814                      <ul id="jooa11y-settings-options">
3815                          <li>
3816                              <label id="check-contrast" for="jooa11y-contrast-toggle">$Lang._('CONTRAST')}</label>
3817                              <button id="jooa11y-contrast-toggle"
3818                              aria-labelledby="check-contrast"
3819                              class="jooa11y-settings-switch"
3820                              aria-pressed="$loadContrastPreference ? "true" : "false"}">$loadContrastPreference ? Lang._('ON') : Lang._('OFF')}</button>
3821                          </li>
3822                          <li>
3823                              <label id="check-labels" for="jooa11y-labels-toggle">$Lang._('FORM_LABELS')}</label>
3824                              <button id="jooa11y-labels-toggle" aria-labelledby="check-labels" class="jooa11y-settings-switch"
3825                              aria-pressed="$loadLabelsPreference ? "true" : "false"}">$loadLabelsPreference ? Lang._('ON') : Lang._('OFF')}</button>
3826                          </li>
3827                          <li>
3828                              <label id="check-changerequest" for="jooa11y-links-advanced-toggle">$Lang._('LINKS_ADVANCED')}<span class="jooa11y-badge">AAA</span></label>
3829                              <button id="jooa11y-links-advanced-toggle" aria-labelledby="check-changerequest" class="jooa11y-settings-switch"
3830                              aria-pressed="$loadChangeRequestPreference ? "true" : "false"}">$loadChangeRequestPreference ? Lang._('ON') : Lang._('OFF')}</button>
3831                          </li>
3832                          <li>
3833                              <label id="check-readability" for="jooa11y-readability-toggle">$Lang._('READABILITY')}<span class="jooa11y-badge">AAA</span></label>
3834                              <button id="jooa11y-readability-toggle" aria-labelledby="check-readability" class="jooa11y-settings-switch"
3835                              aria-pressed="$loadReadabilityPreference ? "true" : "false"}">$loadReadabilityPreference ? Lang._('ON') : Lang._('OFF')}</button>
3836                          </li>
3837                          <li>
3838                              <label id="dark-mode" for="jooa11y-theme-toggle">$Lang._('DARK_MODE')}</label>
3839                              <button id="jooa11y-theme-toggle" aria-labelledby="dark-mode" class="jooa11y-settings-switch"></button>
3840                          </li>
3841                      </ul>
3842                  </div>
3843              </div>` + //Console warning messages.
3844      `<div id="jooa11y-panel-alert">
3845                  <div class="jooa11y-header-text">
3846                      <button id="jooa11y-close-alert" class="jooa11y-close-btn" aria-label="$Lang._('ALERT_CLOSE')}" aria-describedby="jooa11y-alert-heading jooa11y-panel-alert-text"></button>
3847                      <h2 id="jooa11y-alert-heading">$Lang._('ALERT_TEXT')}</h2>
3848                  </div>
3849                  <p id="jooa11y-panel-alert-text"></p>
3850                  <div id="jooa11y-panel-alert-preview"></div>
3851              </div>` + //Main panel that conveys state of page.
3852      `<div id="jooa11y-panel-content">
3853                  <button id="jooa11y-cycle-toggle" type="button" aria-label="$Lang._('SHORTCUT_SR')}">
3854                      <div class="jooa11y-panel-icon"></div>
3855                  </button>
3856                  <div id="jooa11y-panel-text"><p id="jooa11y-status" aria-live="polite"></p></div>
3857              </div>` + //Show Outline & Show Settings button.
3858      `<div id="jooa11y-panel-controls" role="tablist" aria-orientation="horizontal">
3859                  <button type="button" role="tab" aria-expanded="false" id="jooa11y-outline-toggle" aria-controls="jooa11y-outline-panel">
3860                      $Lang._('SHOW_OUTLINE')}
3861                  </button>
3862                  <button type="button" role="tab" aria-expanded="false" id="jooa11y-settings-toggle" aria-controls="jooa11y-settings-panel">
3863                      $Lang._('SHOW_SETTINGS')}
3864                  </button>
3865                  <div style="width:35px"></div>
3866              </div>` + //End of main container.
3867      `</div>`;
3868      document.body.append(jooa11ycontainer); //Put before document.ready because of CSS flicker when dark mode is enabled.
3869  
3870      this.settingPanelToggles(); // Preload before CheckAll function.
3871  
3872      this.jooa11yMainToggle();
3873      this.sanitizeHTMLandComputeARIA();
3874      this.initializeJumpToIssueTooltip();
3875    } //----------------------------------------------------------------------
3876    // Main toggle button
3877    //----------------------------------------------------------------------
3878  
3879  
3880    jooa11yMainToggle() {
3881      //Keeps checker active when navigating between pages until it is toggled off.
3882      const jooa11yToggle = document.getElementById("jooa11y-toggle");
3883      jooa11yToggle.addEventListener('click', e => {
3884        if (localStorage.getItem("jooa11y-remember-panel") === "Opened") {
3885          localStorage.setItem("jooa11y-remember-panel", "Closed");
3886          jooa11yToggle.classList.remove("jooa11y-on");
3887          jooa11yToggle.setAttribute("aria-expanded", "false");
3888          this.resetAll();
3889          this.updateBadge();
3890          e.preventDefault();
3891        } else {
3892          localStorage.setItem("jooa11y-remember-panel", "Opened");
3893          jooa11yToggle.classList.add("jooa11y-on");
3894          jooa11yToggle.setAttribute("aria-expanded", "true");
3895          this.checkAll(); //Don't show badge when panel is opened.
3896  
3897          document.getElementById("jooa11y-notification-badge").style.display = 'none';
3898          e.preventDefault();
3899        }
3900      }); //Remember to leave it open
3901  
3902      if (localStorage.getItem("jooa11y-remember-panel") === "Opened") {
3903        jooa11yToggle.classList.add("jooa11y-on");
3904        jooa11yToggle.setAttribute("aria-expanded", "true");
3905      } //Crudely give a little time to load any other content or slow post-rendered JS, iFrames, etc.
3906  
3907  
3908      if (jooa11yToggle.classList.contains("jooa11y-on")) {
3909        jooa11yToggle.classList.toggle("loading-jooa11y");
3910        jooa11yToggle.setAttribute("aria-expanded", "true");
3911        setTimeout(this.checkAll, 800);
3912      } //Keyboard commands
3913  
3914  
3915      document.onkeydown = evt => {
3916        evt = evt || window.event; //Escape key to close accessibility checker panel
3917  
3918        var isEscape = false;
3919  
3920        if ("key" in evt) {
3921          isEscape = evt.key === "Escape" || evt.key === "Esc";
3922        } else {
3923          isEscape = evt.keyCode === 27;
3924        }
3925  
3926        if (isEscape && document.getElementById("jooa11y-panel").classList.contains("jooa11y-active")) {
3927          jooa11yToggle.setAttribute("aria-expanded", "false");
3928          jooa11yToggle.classList.remove("jooa11y-on");
3929          jooa11yToggle.click();
3930          this.resetAll();
3931        } //Alt + A to open accessibility checker panel
3932  
3933  
3934        if (evt.altKey && evt.code == "KeyA") {
3935          const jooa11yToggle = document.getElementById("jooa11y-toggle");
3936          jooa11yToggle.click();
3937          jooa11yToggle.focus();
3938          evt.preventDefault();
3939        }
3940      };
3941    } // ============================================================
3942    // Helpers: Sanitize HTML and compute ARIA for hyperlinks
3943    // ============================================================
3944  
3945  
3946    sanitizeHTMLandComputeARIA() {
3947      //Helper: Compute alt text on images within a text node.
3948      this.computeTextNodeWithImage = function ($el) {
3949        const imgArray = Array.from($el.querySelectorAll("img"));
3950        let returnText = ""; //No image, has text.
3951  
3952        if (imgArray.length === 0 && $el.textContent.trim().length > 1) {
3953          returnText = $el.textContent.trim();
3954        } //Has image, no text.
3955        else if (imgArray.length && $el.textContent.trim().length === 0) {
3956          let imgalt = imgArray[0].getAttribute("alt");
3957  
3958          if (!imgalt || imgalt === " ") {
3959            returnText = " ";
3960          } else if (imgalt !== undefined) {
3961            returnText = imgalt;
3962          }
3963        } //Has image and text.
3964        //To-do: This is a hack? Any way to do this better?
3965        else if (imgArray.length && $el.textContent.trim().length) {
3966          imgArray.forEach(element => {
3967            element.insertAdjacentHTML("afterend", " <span class='jooa11y-clone-image-text' aria-hidden='true'>" + imgArray[0].getAttribute("alt") + "</span> ");
3968          });
3969          returnText = $el.textContent.trim();
3970        }
3971  
3972        return returnText;
3973      }; //Helper: Handle ARIA labels for Link Text module.
3974  
3975  
3976      this.computeAriaLabel = function (el) {
3977        if (el.matches("[aria-label]")) {
3978          return el.getAttribute("aria-label");
3979        } else if (el.matches("[aria-labelledby]")) {
3980          let target = el.getAttribute("aria-labelledby").split(/\s+/);
3981  
3982          if (target.length > 0) {
3983            let returnText = "";
3984            target.forEach(x => {
3985              if (document.querySelector("#" + x) === null) {
3986                returnText += " ";
3987              } else {
3988                returnText += document.querySelector("#" + x).firstChild.nodeValue + " ";
3989              }
3990            });
3991            return returnText;
3992          } else {
3993            return "";
3994          }
3995        } //Children of element.
3996        else if (Array.from(el.children).filter(x => x.matches("[aria-label]")).length > 0) {
3997          return Array.from(el.children)[0].getAttribute("aria-label");
3998        } else if (Array.from(el.children).filter(x => x.matches("[title]")).length > 0) {
3999          return Array.from(el.children)[0].getAttribute("title");
4000        } else if (Array.from(el.children).filter(x => x.matches("[aria-labelledby]")).length > 0) {
4001          let target = Array.from(el.children)[0].getAttribute("aria-labelledby").split(/\s+/);
4002  
4003          if (target.length > 0) {
4004            let returnText = "";
4005            target.forEach(x => {
4006              if (document.querySelector("#" + x) === null) {
4007                returnText += " ";
4008              } else {
4009                returnText += document.querySelector("#" + x).firstChild.nodeValue + " ";
4010              }
4011            });
4012            return returnText;
4013          } else {
4014            return "";
4015          }
4016        } else {
4017          return "noAria";
4018        }
4019      };
4020    } //----------------------------------------------------------------------
4021    // Setting's panel: Additional ruleset toggles.
4022    //----------------------------------------------------------------------
4023  
4024  
4025    settingPanelToggles() {
4026      //Toggle: Contrast
4027      const $jooa11yContrastCheck = document.getElementById("jooa11y-contrast-toggle");
4028  
4029      $jooa11yContrastCheck.onclick = async () => {
4030        if (localStorage.getItem("jooa11y-remember-contrast") === "On") {
4031          localStorage.setItem("jooa11y-remember-contrast", "Off");
4032          $jooa11yContrastCheck.textContent = Lang._('OFF');
4033          $jooa11yContrastCheck.setAttribute("aria-pressed", "false");
4034          this.resetAll(false);
4035          await this.checkAll();
4036        } else {
4037          localStorage.setItem("jooa11y-remember-contrast", "On");
4038          $jooa11yContrastCheck.textContent = Lang._('ON');
4039          $jooa11yContrastCheck.setAttribute("aria-pressed", "true");
4040          this.resetAll(false);
4041          await this.checkAll();
4042        }
4043      }; //Toggle: Form labels
4044  
4045  
4046      const $jooa11yLabelsCheck = document.getElementById("jooa11y-labels-toggle");
4047  
4048      $jooa11yLabelsCheck.onclick = async () => {
4049        if (localStorage.getItem("jooa11y-remember-labels") === "On") {
4050          localStorage.setItem("jooa11y-remember-labels", "Off");
4051          $jooa11yLabelsCheck.textContent = Lang._('OFF');
4052          $jooa11yLabelsCheck.setAttribute("aria-pressed", "false");
4053          this.resetAll(false);
4054          await this.checkAll();
4055        } else {
4056          localStorage.setItem("jooa11y-remember-labels", "On");
4057          $jooa11yLabelsCheck.textContent = Lang._('ON');
4058          $jooa11yLabelsCheck.setAttribute("aria-pressed", "true");
4059          this.resetAll(false);
4060          await this.checkAll();
4061        }
4062      }; //Toggle: Links (Advanced)
4063  
4064  
4065      const $jooa11yChangeRequestCheck = document.getElementById("jooa11y-links-advanced-toggle");
4066  
4067      $jooa11yChangeRequestCheck.onclick = async () => {
4068        if (localStorage.getItem("jooa11y-remember-links-advanced") === "On") {
4069          localStorage.setItem("jooa11y-remember-links-advanced", "Off");
4070          $jooa11yChangeRequestCheck.textContent = Lang._('OFF');
4071          $jooa11yChangeRequestCheck.setAttribute("aria-pressed", "false");
4072          this.resetAll(false);
4073          await this.checkAll();
4074        } else {
4075          localStorage.setItem("jooa11y-remember-links-advanced", "On");
4076          $jooa11yChangeRequestCheck.textContent = Lang._('ON');
4077          $jooa11yChangeRequestCheck.setAttribute("aria-pressed", "true");
4078          this.resetAll(false);
4079          await this.checkAll();
4080        }
4081      }; //Toggle: Readability
4082  
4083  
4084      const $jooa11yReadabilityCheck = document.getElementById("jooa11y-readability-toggle");
4085  
4086      $jooa11yReadabilityCheck.onclick = async () => {
4087        if (localStorage.getItem("jooa11y-remember-readability") === "On") {
4088          localStorage.setItem("jooa11y-remember-readability", "Off");
4089          $jooa11yReadabilityCheck.textContent = Lang._('OFF');
4090          $jooa11yReadabilityCheck.setAttribute("aria-pressed", "false");
4091          document.getElementById("jooa11y-readability-panel").classList.remove("jooa11y-active");
4092          this.resetAll(false);
4093          await this.checkAll();
4094        } else {
4095          localStorage.setItem("jooa11y-remember-readability", "On");
4096          $jooa11yReadabilityCheck.textContent = Lang._('ON');
4097          $jooa11yReadabilityCheck.setAttribute("aria-pressed", "true");
4098          document.getElementById("jooa11y-readability-panel").classList.add("jooa11y-active");
4099          this.resetAll(false);
4100          await this.checkAll();
4101        }
4102      };
4103  
4104      if (localStorage.getItem("jooa11y-remember-readability") === "On") {
4105        document.getElementById("jooa11y-readability-panel").classList.add("jooa11y-active");
4106      } //Toggle: Dark mode. (Credits: https://derekkedziora.com/blog/dark-mode-revisited)
4107  
4108  
4109      let systemInitiatedDark = window.matchMedia("(prefers-color-scheme: dark)");
4110      const $jooa11yTheme = document.getElementById("jooa11y-theme-toggle");
4111      const html = document.querySelector("html");
4112      const theme = localStorage.getItem("jooa11y-remember-theme");
4113  
4114      if (systemInitiatedDark.matches) {
4115        $jooa11yTheme.textContent = Lang._('ON');
4116        $jooa11yTheme.setAttribute("aria-pressed", "true");
4117      } else {
4118        $jooa11yTheme.textContent = Lang._('OFF');
4119        $jooa11yTheme.setAttribute("aria-pressed", "false");
4120      }
4121  
4122      function prefersColorTest(systemInitiatedDark) {
4123        if (systemInitiatedDark.matches) {
4124          html.setAttribute("data-jooa11y-theme", "dark");
4125          $jooa11yTheme.textContent = Lang._('ON');
4126          $jooa11yTheme.setAttribute("aria-pressed", "true");
4127          localStorage.setItem("jooa11y-remember-theme", "");
4128        } else {
4129          html.setAttribute("data-jooa11y-theme", "light");
4130          $jooa11yTheme.textContent = Lang._('OFF');
4131          $jooa11yTheme.setAttribute("aria-pressed", "false");
4132          localStorage.setItem("jooa11y-remember-theme", "");
4133        }
4134      }
4135  
4136      systemInitiatedDark.addEventListener('change', prefersColorTest);
4137  
4138      $jooa11yTheme.onclick = async () => {
4139        const theme = localStorage.getItem("jooa11y-remember-theme");
4140  
4141        if (theme === "dark") {
4142          html.setAttribute("data-jooa11y-theme", "light");
4143          localStorage.setItem("jooa11y-remember-theme", "light");
4144          $jooa11yTheme.textContent = Lang._('OFF');
4145          $jooa11yTheme.setAttribute("aria-pressed", "false");
4146        } else if (theme === "light") {
4147          html.setAttribute("data-jooa11y-theme", "dark");
4148          localStorage.setItem("jooa11y-remember-theme", "dark");
4149          $jooa11yTheme.textContent = Lang._('ON');
4150          $jooa11yTheme.setAttribute("aria-pressed", "true");
4151        } else if (systemInitiatedDark.matches) {
4152          html.setAttribute("data-jooa11y-theme", "light");
4153          localStorage.setItem("jooa11y-remember-theme", "light");
4154          $jooa11yTheme.textContent = Lang._('OFF');
4155          $jooa11yTheme.setAttribute("aria-pressed", "false");
4156        } else {
4157          html.setAttribute("data-jooa11y-theme", "dark");
4158          localStorage.setItem("jooa11y-remember-theme", "dark");
4159          $jooa11yTheme.textContent = Lang._('OFF');
4160          $jooa11yTheme.setAttribute("aria-pressed", "true");
4161        }
4162      };
4163  
4164      if (theme === "dark") {
4165        html.setAttribute("data-jooa11y-theme", "dark");
4166        localStorage.setItem("jooa11y-remember-theme", "dark");
4167        $jooa11yTheme.textContent = Lang._('ON');
4168        $jooa11yTheme.setAttribute("aria-pressed", "true");
4169      } else if (theme === "light") {
4170        html.setAttribute("data-jooa11y-theme", "light");
4171        localStorage.setItem("jooa11y-remember-theme", "light");
4172        $jooa11yTheme.textContent = Lang._('OFF');
4173        $jooa11yTheme.setAttribute("aria-pressed", "false");
4174      }
4175    } //----------------------------------------------------------------------
4176    // Tooltip for Jump-to-Issue button.
4177    //----------------------------------------------------------------------
4178  
4179  
4180    initializeJumpToIssueTooltip() {
4181      tippy('#jooa11y-cycle-toggle', {
4182        content: `<div style="text-align:center">$Lang._('SHORTCUT_TOOLTIP')} &raquo;<br><span class="jooa11y-shortcut-icon"></span></div>`,
4183        allowHTML: true,
4184        delay: [900, 0],
4185        trigger: "mouseenter focusin",
4186        arrow: true,
4187        placement: 'top',
4188        theme: "jooa11y-theme",
4189        aria: {
4190          content: null,
4191          expanded: false
4192        },
4193        appendTo: document.body
4194      });
4195    } // ----------------------------------------------------------------------
4196    // Do Initial check
4197    // ----------------------------------------------------------------------
4198  
4199  
4200    doInitialCheck() {
4201      if (localStorage.getItem("jooa11y-remember-panel") === "Closed" || !localStorage.getItem("jooa11y-remember-panel")) {
4202        this.panelActive = true; // Prevent panel popping up after initial check
4203  
4204        this.checkAll();
4205      }
4206    } // ----------------------------------------------------------------------
4207    // Check all
4208    // ----------------------------------------------------------------------
4209  
4210  
4211    // ============================================================
4212    // Reset all
4213    // ============================================================
4214    resetAll(restartPanel = true) {
4215      this.panelActive = false;
4216      this.clearEverything(); //Remove eventListeners on the Show Outline and Show Panel toggles.
4217  
4218      const $outlineToggle = document.getElementById("jooa11y-outline-toggle");
4219      const resetOutline = $outlineToggle.cloneNode(true);
4220      $outlineToggle.parentNode.replaceChild(resetOutline, $outlineToggle);
4221      const $settingsToggle = document.getElementById("jooa11y-settings-toggle");
4222      const resetSettings = $settingsToggle.cloneNode(true);
4223      $settingsToggle.parentNode.replaceChild(resetSettings, $settingsToggle); //Errors
4224  
4225      document.querySelectorAll('.jooa11y-error-border').forEach(el => el.classList.remove('jooa11y-error-border'));
4226      document.querySelectorAll('.jooa11y-error-text').forEach(el => el.classList.remove('jooa11y-error-text')); //Warnings
4227  
4228      document.querySelectorAll('.jooa11y-warning-border').forEach(el => el.classList.remove('jooa11y-warning-border'));
4229      document.querySelectorAll('.jooa11y-warning-text').forEach(el => el.classList.remove('jooa11y-warning-text'));
4230      document.querySelectorAll('p').forEach(el => el.classList.remove('jooa11y-fake-list'));
4231      let allcaps = document.querySelectorAll('.jooa11y-warning-uppercase');
4232      allcaps.forEach(el => el.outerHTML = el.innerHTML); //Good
4233  
4234      document.querySelectorAll('.jooa11y-good-border').forEach(el => el.classList.remove('jooa11y-good-border'));
4235      document.querySelectorAll('.jooa11y-good-text').forEach(el => el.classList.remove('jooa11y-good-text')); //Remove
4236  
4237      document.querySelectorAll(`
4238                  .jooa11y-instance,
4239                  .jooa11y-instance-inline,
4240                  .jooa11y-heading-label,
4241                  #jooa11y-outline-list li,
4242                  .jooa11y-readability-period,
4243                  #jooa11y-readability-info span,
4244                  #jooa11y-readability-details li,
4245                  .jooa11y-clone-image-text
4246              `).forEach(el => el.parentNode.removeChild(el)); //Etc
4247  
4248      document.querySelectorAll('.jooa11y-overflow').forEach(el => el.classList.remove('jooa11y-overflow'));
4249      document.querySelectorAll('.jooa11y-fake-heading').forEach(el => el.classList.remove('jooa11y-fake-heading'));
4250      document.querySelectorAll('.jooa11y-pulse-border').forEach(el => el.classList.remove('jooa11y-pulse-border'));
4251      document.querySelector('#jooa11y-panel-alert').classList.remove("jooa11y-active");
4252      var empty = document.querySelector('#jooa11y-panel-alert-text');
4253  
4254      while (empty.firstChild) empty.removeChild(empty.firstChild);
4255  
4256      var clearStatus = document.querySelector('#jooa11y-status');
4257  
4258      while (clearStatus.firstChild) clearStatus.removeChild(clearStatus.firstChild);
4259  
4260      if (restartPanel) {
4261        document.querySelector('#jooa11y-panel').classList.remove("jooa11y-active");
4262      }
4263    }
4264  
4265    clearEverything() {}
4266  
4267    // ============================================================
4268    // Initialize tooltips for error/warning/pass buttons: (Tippy.js)
4269    // Although you can also swap this with Bootstrap's tooltip library for example.
4270    // ============================================================
4271    initializeTooltips() {
4272      tippy(".jooa11y-btn", {
4273        interactive: true,
4274        trigger: "mouseenter click focusin",
4275        //Focusin trigger to ensure "Jump to issue" button displays tooltip.
4276        arrow: true,
4277        delay: [200, 0],
4278        //Slight delay to ensure mouse doesn't quickly trigger and hide tooltip.
4279        theme: "jooa11y-theme",
4280        placement: 'bottom',
4281        allowHTML: true,
4282        aria: {
4283          content: 'describedby'
4284        },
4285        appendTo: document.body
4286      });
4287    } // ============================================================
4288    // Detect parent containers that have hidden overflow.
4289    // ============================================================
4290  
4291  
4292    detectOverflow() {
4293      const findParentWithOverflow = ($el, property, value) => {
4294        while ($el !== null) {
4295          const style = window.getComputedStyle($el);
4296          const propValue = style.getPropertyValue(property);
4297  
4298          if (propValue === value) {
4299            return $el;
4300          }
4301  
4302          $el = $el.parentElement;
4303        }
4304  
4305        return null;
4306      };
4307  
4308      const $findButtons = document.querySelectorAll('.jooa11y-btn');
4309      $findButtons.forEach(function ($el) {
4310        const overflowing = findParentWithOverflow($el, 'overflow', 'hidden');
4311  
4312        if (overflowing !== null) {
4313          overflowing.classList.add('jooa11y-overflow');
4314        }
4315      });
4316    } // ============================================================
4317    // Nudge buttons if they overlap.
4318    // ============================================================
4319  
4320  
4321    // ============================================================
4322    // Update iOS style notification badge on icon.
4323    // ============================================================
4324    updateBadge() {
4325      let totalCount = this.errorCount + this.warningCount;
4326      const notifBadge = document.getElementById("jooa11y-notification-badge");
4327  
4328      if (totalCount === 0) {
4329        notifBadge.style.display = "none";
4330      } else {
4331        notifBadge.style.display = "flex";
4332        document.getElementById('jooa11y-notification-count').innerHTML = Lang.sprintf('PANEL_STATUS_ICON', totalCount);
4333      }
4334    } // ----------------------------------------------------------------------
4335    // Main panel: Display and update panel.
4336    // ----------------------------------------------------------------------
4337  
4338  
4339    updatePanel() {
4340      this.panelActive = true;
4341      this.errorCount + this.warningCount;
4342      this.buildPanel();
4343      this.skipToIssue();
4344      const $jooa11ySkipBtn = document.getElementById("jooa11y-cycle-toggle");
4345      $jooa11ySkipBtn.disabled = false;
4346      $jooa11ySkipBtn.setAttribute("style", "cursor: pointer !important;");
4347      const $jooa11yPanel = document.getElementById("jooa11y-panel");
4348      $jooa11yPanel.classList.add("jooa11y-active");
4349      const $panelContent = document.getElementById("jooa11y-panel-content");
4350      const $jooa11yStatus = document.getElementById("jooa11y-status");
4351      const $findButtons = document.querySelectorAll('.jooa11y-btn');
4352  
4353      if (this.errorCount > 0 && this.warningCount > 0) {
4354        $panelContent.setAttribute("class", "jooa11y-errors");
4355        $jooa11yStatus.textContent = Lang.sprintf('PANEL_STATUS_BOTH', this.errorCount, this.warningCount);
4356      } else if (this.errorCount > 0) {
4357        $panelContent.setAttribute("class", "jooa11y-errors");
4358        $jooa11yStatus.textContent = Lang.sprintf('PANEL_STATUS_ERRORS', this.errorCount);
4359      } else if (this.warningCount > 0) {
4360        $panelContent.setAttribute("class", "jooa11y-warnings");
4361        $jooa11yStatus.textContent = Lang.sprintf('PANEL_STATUS_WARNINGS', this.warningCount);
4362      } else {
4363        $panelContent.setAttribute("class", "jooa11y-good");
4364        $jooa11yStatus.textContent = Lang._('PANEL_STATUS_NONE');
4365  
4366        if ($findButtons.length === 0) {
4367          $jooa11ySkipBtn.disabled = true;
4368          $jooa11ySkipBtn.setAttribute("style", "cursor: default !important;");
4369        }
4370      }
4371    }
4372  
4373    // ============================================================
4374    // Finds all elements and caches them
4375    // ============================================================
4376    findElements() {
4377      const allHeadings = Array.from(this.$root.querySelectorAll("h1, h2, h3, h4, h5, h6, [role='heading'][aria-level]"));
4378      const allPs = Array.from(this.$root.querySelectorAll("p"));
4379      this.$containerExclusions = Array.from(document.querySelectorAll(this.containerIgnore));
4380      this.$h = allHeadings.filter(heading => !this.$containerExclusions.includes(heading));
4381      this.$p = allPs.filter(p => !this.$containerExclusions.includes(p));
4382    }
4383  
4384    // ============================================================
4385    // Rulesets: Check Headings
4386    // ============================================================
4387    checkHeaders() {
4388      let prevLevel;
4389      this.$h.forEach((el, i) => {
4390        let text = this.computeTextNodeWithImage(el);
4391        let htext = escapeHTML(text);
4392        let level;
4393  
4394        if (el.getAttribute("aria-level")) {
4395          level = +el.getAttribute("aria-level");
4396        } else {
4397          level = +el.tagName.slice(1);
4398        }
4399  
4400        let headingLength = el.textContent.trim().length;
4401        let error = null;
4402        let warning = null;
4403  
4404        if (level - prevLevel > 1 && i !== 0) {
4405          error = Lang.sprintf('HEADING_NON_CONSECUTIVE_LEVEL', prevLevel, level);
4406        } else if (el.textContent.trim().length === 0) {
4407          if (el.querySelectorAll("img").length) {
4408            const imgalt = el.querySelector("img").getAttribute("alt");
4409  
4410            if (imgalt === undefined || imgalt === " " || imgalt === "") {
4411              error = Lang.sprintf('HEADING_EMPTY_WITH_IMAGE', level);
4412              el.classList.add("jooa11y-error-text");
4413            }
4414          } else {
4415            error = Lang.sprintf('HEADING_EMPTY', level);
4416            el.classList.add("jooa11y-error-text");
4417          }
4418        } else if (i === 0 && level !== 1 && level !== 2) {
4419          error = Lang._('HEADING_FIRST');
4420        } else if (el.textContent.trim().length > 170) {
4421          warning = `$Lang._('HEADING_LONG')} . $Lang.sprintf('HEADING_LONG_INFO', headingLength)}`;
4422        }
4423  
4424        prevLevel = level;
4425        let li = `<li class='jooa11y-outline-$level}'>
4426                  <span class='jooa11y-badge'>$level}</span>
4427                  <span class='jooa11y-outline-list-item'>$htext}</span>
4428              </li>`;
4429        let liError = `<li class='jooa11y-outline-$level}'>
4430                  <span class='jooa11y-badge jooa11y-error-badge'>
4431                  <span aria-hidden='true'>&#10007;</span>
4432                  <span class='jooa11y-visually-hidden'>$Lang._('ERROR')}</span> $level}</span>
4433                  <span class='jooa11y-outline-list-item jooa11y-red-text jooa11y-bold'>$htext}</span>
4434              </li>`;
4435        let liWarning = `<li class='jooa11y-outline-$level}'>
4436                  <span class='jooa11y-badge jooa11y-warning-badge'>
4437                  <span aria-hidden='true'>&#x3f;</span>
4438                  <span class='jooa11y-visually-hidden'>$Lang._('WARNING')}</span> $level}</span>
4439                  <span class='jooa11y-outline-list-item jooa11y-yellow-text jooa11y-bold'>$htext}</span>
4440              </li>`;
4441        let ignoreArray = [];
4442  
4443        if (this.options.outlineIgnore) {
4444          ignoreArray = Array.from(document.querySelectorAll(this.options.outlineIgnore));
4445        }
4446  
4447        if (!ignoreArray.includes(el)) {
4448          //Append heading labels.
4449          el.insertAdjacentHTML("beforeend", `<span class='jooa11y-heading-label'>H$level}</span>`); //Heading errors
4450  
4451          if (error != null && el.closest("a")) {
4452            this.errorCount++;
4453            el.classList.add("jooa11y-error-border");
4454            el.closest("a").insertAdjacentHTML("afterend", this.annotate(Lang._('ERROR'), error, true));
4455            document.querySelector("#jooa11y-outline-list").insertAdjacentHTML("beforeend", liError);
4456          } else if (error != null) {
4457            this.errorCount++;
4458            el.classList.add("jooa11y-error-border");
4459            el.insertAdjacentHTML("beforebegin", this.annotate(Lang._('ERROR'), error));
4460            document.querySelector("#jooa11y-outline-list").insertAdjacentHTML("beforeend", liError);
4461          } //Heading warnings
4462          else if (warning != null && el.closest("a")) {
4463            this.warningCount++;
4464            el.closest("a").insertAdjacentHTML("afterend", this.annotate(Lang._('WARNING'), warning));
4465            document.querySelector("#jooa11y-outline-list").insertAdjacentHTML("beforeend", liWarning);
4466          } else if (warning != null) {
4467            el.insertAdjacentHTML("beforebegin", this.annotate(Lang._('WARNING'), warning));
4468            document.querySelector("#jooa11y-outline-list").insertAdjacentHTML("beforeend", liWarning);
4469          } //Not an error or warning
4470          else if (error == null || warning == null) {
4471            document.querySelector("#jooa11y-outline-list").insertAdjacentHTML("beforeend", li);
4472          }
4473        }
4474      }); //Check to see there is at least one H1 on the page.
4475  
4476      const $h1 = Array.from(this.$root.querySelectorAll('h1, [role="heading"][aria-level="1"]')).filter($h => !this.$containerExclusions.includes($h));
4477  
4478      if ($h1.length === 0) {
4479        this.errorCount++;
4480        document.querySelector('#jooa11y-outline-header').insertAdjacentHTML('afterend', `<div class='jooa11y-instance jooa11y-missing-h1'>
4481                      <span class='jooa11y-badge jooa11y-error-badge'><span aria-hidden='true'>&#10007;</span><span class='jooa11y-visually-hidden'>$Lang._('ERROR')}</span></span>
4482                      <span class='jooa11y-red-text jooa11y-bold'>$Lang._('PANEL_HEADING_MISSING_ONE')}</span>
4483                  </div>`);
4484        document.querySelector("#jooa11y-container").insertAdjacentHTML('afterend', this.annotateBanner(Lang._('ERROR'), Lang._('HEADING_MISSING_ONE')));
4485      }
4486    }
4487  
4488    // ============================================================
4489    // Rulesets: Link text
4490    // ============================================================
4491    checkLinkText() {
4492      const containsLinkTextStopWords = textContent => {
4493        let urlText = ["http", ".asp", ".htm", ".php", ".edu/", ".com/", ".net/", ".org/", ".us/", ".ca/", ".de/", ".icu/", ".uk/", ".ru/", ".info/", ".top/", ".xyz/", ".tk/", ".cn/", ".ga/", ".cf/", ".nl/", ".io/"];
4494        let hit = [null, null, null]; // Flag partial stop words.
4495  
4496        this.options.partialAltStopWords.forEach(word => {
4497          if (textContent.length === word.length && textContent.toLowerCase().indexOf(word) >= 0) {
4498            hit[0] = word;
4499            return false;
4500          }
4501        }); // Other warnings we want to add.
4502  
4503        this.options.warningAltWords.forEach(word => {
4504          if (textContent.toLowerCase().indexOf(word) >= 0) {
4505            hit[1] = word;
4506            return false;
4507          }
4508        }); // Flag link text containing URLs.
4509  
4510        urlText.forEach(word => {
4511          if (textContent.toLowerCase().indexOf(word) >= 0) {
4512            hit[2] = word;
4513            return false;
4514          }
4515        });
4516        return hit;
4517      };
4518      /* Mini function if you need to exclude any text contained with a span. We created this function to ignore automatically appended sr-only text for external links and document filetypes.

4519        $.fn.ignore = function(sel){

4520          return this.clone().find(sel||">*").remove().end();

4521      };

4522        $el.ignore("span.sr-only").text().trim();

4523        Example: <a href="#">learn more <span class="sr-only">(external)</span></a>

4524        This function will ignore the text "(external)", and correctly flag this link as an error for non descript link text. */
4525  
4526  
4527      const fnIgnore = (element, selector) => {
4528        const $clone = element.cloneNode(true);
4529        const $excluded = Array.from(selector ? $clone.querySelectorAll(selector) : $clone.children);
4530        $excluded.forEach($c => {
4531          $c.parentElement.removeChild($c);
4532        });
4533        return $clone;
4534      };
4535  
4536      const $linkIgnore = Array.from(this.$root.querySelectorAll(this.linkIgnore));
4537      const $links = Array.from(this.$root.querySelectorAll('a[href]')).filter($a => !$linkIgnore.includes($a));
4538      $links.forEach(el => {
4539        let linkText = this.computeAriaLabel(el);
4540        let hasAriaLabelledBy = el.getAttribute('aria-labelledby');
4541        let hasAriaLabel = el.getAttribute('aria-label');
4542        let hasTitle = el.getAttribute('title');
4543        let childAriaLabelledBy = null;
4544        let childAriaLabel = null;
4545        let childTitle = null;
4546  
4547        if (el.children.length) {
4548          let $firstChild = el.children[0];
4549          childAriaLabelledBy = $firstChild.getAttribute('aria-labelledby');
4550          childAriaLabel = $firstChild.getAttribute('aria-label');
4551          childTitle = $firstChild.getAttribute('title');
4552        }
4553  
4554        let error = containsLinkTextStopWords(fnIgnore(el, this.options.linkIgnoreSpan).textContent.trim());
4555  
4556        if (linkText === 'noAria') {
4557          linkText = el.textContent;
4558        } //Flag empty hyperlinks
4559  
4560  
4561        if (el.getAttribute('href') && !el.textContent.trim()) {
4562          if (el.querySelectorAll('img').length) ;else if (hasAriaLabelledBy || hasAriaLabel) {
4563            el.classList.add("jooa11y-good-border");
4564            el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('GOOD'), Lang.sprintf('LINK_LABEL', linkText), true));
4565          } else if (hasTitle) {
4566            let linkText = hasTitle;
4567            el.classList.add("jooa11y-good-border");
4568            el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('GOOD'), Lang.sprintf('LINK_LABEL', linkText), true));
4569          } else if (el.children.length) {
4570            if (childAriaLabelledBy || childAriaLabel || childTitle) {
4571              el.classList.add("jooa11y-good-border");
4572              el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('GOOD'), Lang.sprintf('LINK_LABEL', linkText), true));
4573            } else {
4574              this.errorCount++;
4575              el.classList.add("jooa11y-error-border");
4576              el.insertAdjacentHTML('afterend', this.annotate(Lang._('ERROR'), Lang.sprintf('LINK_EMPTY_LINK_NO_LABEL'), true));
4577            }
4578          } else {
4579            this.errorCount++;
4580            el.classList.add("jooa11y-error-border");
4581            el.insertAdjacentHTML('afterend', this.annotate(Lang._('ERROR'), Lang._('LINK_EMPTY'), true));
4582          }
4583        } else if (error[0] !== null) {
4584          if (hasAriaLabelledBy) {
4585            el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('GOOD'), Lang.sprintf('LINK_LABEL', linkText), true));
4586          } else if (hasAriaLabel) {
4587            el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('GOOD'), Lang.sprintf('LINK_LABEL', hasAriaLabel), true));
4588          } else if (el.getAttribute('aria-hidden') === 'true' && el.getAttribute('tabindex') === '-1') ;else {
4589            this.errorCount++;
4590            el.classList.add("jooa11y-error-text");
4591            el.insertAdjacentHTML('afterend', this.annotate(Lang._('ERROR'), `$Lang.sprintf('LINK_STOPWORD', error[0])} <hr aria-hidden="true"> $Lang._('LINK_STOPWORD_TIP')}`, true));
4592          }
4593        } else if (error[1] !== null) {
4594          this.warningCount++;
4595          el.classList.add("jooa11y-warning-text");
4596          el.insertAdjacentHTML('afterend', this.annotate(Lang._('WARNING'), `$Lang.sprintf('LINK_BEST_PRACTICES', error[1])} <hr aria-hidden="true"> $Lang._('LINK_BEST_PRACTICES_DETAILS')}`, true));
4597        } else if (error[2] != null) {
4598          if (linkText.length > 40) {
4599            this.warningCount++;
4600            el.classList.add("jooa11y-warning-text");
4601            el.insertAdjacentHTML('afterend', this.annotate(Lang._('WARNING'), `$Lang._('LINK_URL')} <hr aria-hidden="true"> $Lang._('LINK_URL_TIP')}`, true));
4602          }
4603        }
4604      });
4605    }
4606  
4607    // ============================================================
4608    // Rulesets: Links (Advanced)
4609    // ============================================================
4610    checkLinksAdvanced() {
4611      const $linkIgnore = Array.from(this.$root.querySelectorAll(this.linkIgnore + ', #jooa11y-container a, .jooa11y-exclude'));
4612      const $linksTargetBlank = Array.from(this.$root.querySelectorAll('a[href]')).filter($a => !$linkIgnore.includes($a));
4613      let seen = {};
4614      $linksTargetBlank.forEach(el => {
4615        let linkText = this.computeAriaLabel(el);
4616  
4617        if (linkText === 'noAria') {
4618          linkText = el.textContent;
4619        }
4620  
4621        const fileTypeMatch = el.matches(`
4622                      a[href$='.pdf'],
4623                      a[href$='.doc'],
4624                      a[href$='.zip'],
4625                      a[href$='.mp3'],
4626                      a[href$='.txt'],
4627                      a[href$='.exe'],
4628                      a[href$='.dmg'],
4629                      a[href$='.rtf'],
4630                      a[href$='.pptx'],
4631                      a[href$='.ppt'],
4632                      a[href$='.xls'],
4633                      a[href$='.xlsx'],
4634                      a[href$='.csv'],
4635                      a[href$='.mp4'],
4636                      a[href$='.mov'],
4637                      a[href$='.avi']
4638                  `); //Links with identical accessible names have equivalent purpose.
4639        //If link has an image, process alt attribute,
4640        //To-do: Kinda hacky. Doesn't return accessible name of link in correct order.
4641  
4642        const $img = el.querySelector('img');
4643        let alt = $img ? $img.getAttribute('alt') || '' : ''; //Return link text and image's alt text.
4644  
4645        let linkTextTrimmed = linkText.trim().toLowerCase() + " " + alt;
4646        let href = el.getAttribute("href");
4647  
4648        if (linkText.length !== 0) {
4649          if (seen[linkTextTrimmed] && linkTextTrimmed.length !== 0) {
4650            if (seen[href]) ;else {
4651              this.warningCount++;
4652              el.classList.add("jooa11y-warning-text");
4653              el.insertAdjacentHTML('afterend', this.annotate(Lang._('WARNING'), `$Lang._('LINK_IDENTICAL_NAME')} <hr aria-hidden="true"> $Lang.sprintf('LINK_IDENTICAL_NAME_TIP', linkText)}`, true));
4654            }
4655          } else {
4656            seen[linkTextTrimmed] = true;
4657            seen[href] = true;
4658          }
4659        } //New tab or new window.
4660  
4661  
4662        const containsNewWindowPhrases = this.options.newWindowPhrases.some(function (pass) {
4663          return linkText.toLowerCase().indexOf(pass) >= 0;
4664        }); //Link that points to a file type indicates that it does.
4665  
4666        const containsFileTypePhrases = this.options.fileTypePhrases.some(function (pass) {
4667          return linkText.toLowerCase().indexOf(pass) >= 0;
4668        });
4669  
4670        if (el.getAttribute("target") === "_blank" && !fileTypeMatch && !containsNewWindowPhrases) {
4671          this.warningCount++;
4672          el.classList.add("jooa11y-warning-text");
4673          el.insertAdjacentHTML('afterend', this.annotate(Lang._('WARNING'), `$Lang._('NEW_TAB_WARNING')} <hr aria-hidden="true"> $Lang._('NEW_TAB_WARNING_TIP')}`, true));
4674        }
4675  
4676        if (fileTypeMatch && !containsFileTypePhrases) {
4677          this.warningCount++;
4678          el.classList.add("jooa11y-warning-text");
4679          el.insertAdjacentHTML('afterend', this.annotate(Lang._('WARNING'), `$Lang._('FILE_TYPE_WARNING')} <hr aria-hidden="true"> $Lang._('FILE_TYPE_WARNING_TIP')}`, true));
4680        }
4681      });
4682    } // ============================================================
4683    // Ruleset: Underlined text
4684    // ============================================================
4685    // check text for <u>  tags
4686  
4687  
4688    checkUnderline() {
4689      const underline = Array.from(this.$root.querySelectorAll('u'));
4690      underline.forEach($el => {
4691        this.warningCount++;
4692        $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), `$Lang._('TEXT_UNDERLINE_WARNING')} <hr aria-hidden="true"> $Lang._('TEXT_UNDERLINE_WARNING_TIP')}`, true));
4693      }); // check for text-decoration-line: underline
4694  
4695      const computed = Array.from(this.$root.querySelectorAll('h1, h2, h3, h4, h5, h6, p, div, span, li, blockquote'));
4696      computed.forEach($el => {
4697        let style = getComputedStyle($el),
4698            decoration = style.textDecorationLine;
4699  
4700        if (decoration === 'underline') {
4701          this.warningCount++;
4702          $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), `$Lang._('TEXT_UNDERLINE_WARNING')} <hr aria-hidden="true"> $Lang._('TEXT_UNDERLINE_WARNING_TIP')}`, true));
4703        }
4704      });
4705    } // ============================================================
4706    // Ruleset: Alternative text
4707    // ============================================================
4708  
4709  
4710    checkAltText() {
4711      const containsAltTextStopWords = alt => {
4712        const altUrl = [".png", ".jpg", ".jpeg", ".gif", ".tiff", ".svg"];
4713        let hit = [null, null, null];
4714        altUrl.forEach(word => {
4715          if (alt.toLowerCase().indexOf(word) >= 0) {
4716            hit[0] = word;
4717          }
4718        });
4719        this.options.suspiciousAltWords.forEach(word => {
4720          if (alt.toLowerCase().indexOf(word) >= 0) {
4721            hit[1] = word;
4722          }
4723        });
4724        this.options.placeholderAltStopWords.forEach(word => {
4725          if (alt.length === word.length && alt.toLowerCase().indexOf(word) >= 0) {
4726            hit[2] = word;
4727          }
4728        });
4729        return hit;
4730      }; // Stores the corresponding issue text to alternative text
4731  
4732  
4733      const images = Array.from(this.$root.querySelectorAll("img"));
4734      const excludeimages = Array.from(this.$root.querySelectorAll(this.imageIgnore));
4735      const $img = images.filter($el => !excludeimages.includes($el));
4736      $img.forEach($el => {
4737        let alt = $el.getAttribute("alt");
4738  
4739        if (alt === null) {
4740          if ($el.closest('a[href]')) {
4741            if ($el.closest('a[href]').textContent.trim().length > 1) {
4742              $el.classList.add("jooa11y-error-border");
4743              $el.closest('a[href]').insertAdjacentHTML('beforebegin', this.annotate(Lang._('ERROR'), Lang._('MISSING_ALT_LINK_BUT_HAS_TEXT_MESSAGE'), false, true));
4744            } else if ($el.closest('a[href]').textContent.trim().length === 0) {
4745              $el.classList.add("jooa11y-error-border");
4746              $el.closest('a[href]').insertAdjacentHTML('beforebegin', this.annotate(Lang._('ERROR'), Lang._('MISSING_ALT_LINK_MESSAGE'), false, true));
4747            }
4748          } // General failure message if image is missing alt.
4749          else {
4750            $el.classList.add("jooa11y-error-border");
4751            $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('ERROR'), Lang._('MISSING_ALT_MESSAGE'), false, true));
4752          }
4753        } // If alt attribute is present, further tests are done.
4754        else {
4755          let altText = escapeHTML(alt); //Prevent tooltip from breaking.
4756  
4757          let error = containsAltTextStopWords(altText);
4758          let altLength = alt.length; // Image fails if a stop word was found.
4759  
4760          if (error[0] != null && $el.closest("a[href]")) {
4761            this.errorCount++;
4762            $el.classList.add("jooa11y-error-border");
4763            $el.closest("a[href]").insertAdjacentHTML('beforebegin', this.annotate(Lang._('ERROR'), `$Lang.sprintf('LINK_IMAGE_BAD_ALT_MESSAGE', altText, error[0])} <hr aria-hidden="true"> $Lang._('LINK_IMAGE_BAD_ALT_MESSAGE_INFO')}`, false));
4764          } else if (error[2] != null && $el.closest("a[href]")) {
4765            this.errorCount++;
4766            $el.classList.add("jooa11y-error-border");
4767            $el.closest("a[href]").insertAdjacentHTML('beforebegin', this.annotate(Lang._('ERROR'), Lang.sprintf('LINK_IMAGE_PLACEHOLDER_ALT_MESSAGE', altText), false, true));
4768          } else if (error[1] != null && $el.closest("a[href]")) {
4769            this.warningCount++;
4770            $el.classList.add("jooa11y-warning-border");
4771            $el.closest("a[href]").insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), `$Lang.sprintf('LINK_IMAGE_SUS_ALT_MESSAGE', altText, error[1])} <hr aria-hidden="true"> $Lang.sprintf('LINK_IMAGE_SUS_ALT_MESSAGE_INFO', altText)}`, false));
4772          } else if (error[0] != null) {
4773            this.errorCount++;
4774            $el.classList.add("jooa11y-error-border");
4775            $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('ERROR'), `$Lang._('LINK_ALT_HAS_BAD_WORD_MESSAGE')} <hr aria-hidden="true"> $Lang.sprintf('LINK_ALT_HAS_BAD_WORD_MESSAGE_INFO', error[0], altText)}`, false));
4776          } else if (error[2] != null) {
4777            this.errorCount++;
4778            $el.classList.add("jooa11y-error-border");
4779            $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('ERROR'), Lang.sprintf('LINK_ALT_PLACEHOLDER_MESSAGE', altText), false));
4780          } else if (error[1] != null) {
4781            this.warningCount++;
4782            $el.classList.add("jooa11y-warning-border");
4783            $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), `$Lang.sprintf('LINK_ALT_HAS_SUS_WORD_MESSAGE', altText, error[1])} <hr aria-hidden="true"> $Lang.sprintf('LINK_ALT_HAS_SUS_WORD_MESSAGE_INFO', altText)}`, false));
4784          } else if ((alt === "" || alt === " ") && $el.closest("a[href]")) {
4785            if ($el.closest("a[href]").getAttribute("tabindex") === "-1" && $el.closest("a[href]").getAttribute("aria-hidden") === "true") ;else if ($el.closest("a[href]").getAttribute("aria-hidden") === "true") {
4786              this.errorCount++;
4787              $el.classList.add("jooa11y-error-border");
4788              $el.closest("a[href]").insertAdjacentHTML('beforebegin', this.annotate(Lang._('ERROR'), Lang._('LINK_HYPERLINKED_IMAGE_ARIA_HIDDEN'), false, true));
4789            } else if ($el.closest("a[href]").textContent.trim().length === 0) {
4790              this.errorCount++;
4791              $el.classList.add("jooa11y-error-border");
4792              $el.closest("a[href]").insertAdjacentHTML('beforebegin', this.annotate(Lang._('ERROR'), Lang._('LINK_IMAGE_LINK_NULL_ALT_NO_TEXT_MESSAGE'), false, true));
4793            } else {
4794              $el.closest("a[href]").insertAdjacentHTML('beforebegin', this.annotate(Lang._('GOOD'), Lang._('LINK_LINK_HAS_ALT_MESSAGE'), false, true));
4795            }
4796          } //Link and contains alt text.
4797          else if (alt.length > 250 && $el.closest("a[href]")) {
4798            this.warningCount++;
4799            $el.classList.add("jooa11y-warning-border");
4800            $el.closest("a[href]").insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), `$Lang._('HYPERLINK_ALT_LENGTH_MESSAGE')} <hr aria-hidden="true"> $Lang.sprintf('HYPERLINK_ALT_LENGTH_MESSAGE_INFO', altText, altLength)}`, false));
4801          } //Link and contains an alt text.
4802          else if (alt !== "" && $el.closest("a[href]") && $el.closest("a[href]").textContent.trim().length === 0) {
4803            this.warningCount++;
4804            $el.classList.add("jooa11y-warning-border");
4805            $el.closest("a[href]").insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), `$Lang._('LINK_IMAGE_LINK_ALT_TEXT_MESSAGE')} <hr aria-hidden="true"> $Lang.sprintf('LINK_IMAGE_LINK_ALT_TEXT_MESSAGE_INFO', altText)}`, false));
4806          } //Contains alt text & surrounding link text.
4807          else if (alt !== "" && $el.closest("a[href]") && $el.closest("a[href]").textContent.trim().length > 1) {
4808            this.warningCount++;
4809            $el.classList.add("jooa11y-warning-border");
4810            $el.closest("a[href]").insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), `$Lang._('LINK_ANCHOR_LINK_AND_ALT_MESSAGE')} <hr aria-hidden="true"> $Lang.sprintf('LINK_ANCHOR_LINK_AND_ALT_MESSAGE_INFO', altText)}`, false));
4811          } //Decorative alt and not a link.
4812          else if (alt === "" || alt === " ") {
4813            if ($el.closest("figure")) {
4814              const figcaption = $el.closest("figure").querySelector("figcaption");
4815  
4816              if (figcaption !== null && figcaption.textContent.trim().length >= 1) {
4817                this.warningCount++;
4818                $el.classList.add("jooa11y-warning-border");
4819                $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), `$Lang._('IMAGE_FIGURE_DECORATIVE')} <hr aria-hidden="true"> $Lang._('IMAGE_FIGURE_DECORATIVE_INFO')}`, false, true));
4820              }
4821            } else {
4822              this.warningCount++;
4823              $el.classList.add("jooa11y-warning-border");
4824              $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), Lang._('LINK_DECORATIVE_MESSAGE'), false, true));
4825            }
4826          } else if (alt.length > 250) {
4827            this.warningCount++;
4828            $el.classList.add("jooa11y-warning-border");
4829            $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), `$Lang._('LINK_ALT_TOO_LONG_MESSAGE')} <hr aria-hidden="true"> $Lang.sprintf('LINK_ALT_TOO_LONG_MESSAGE_INFO', altText, altLength)}`, false));
4830          } else if (alt !== "") {
4831            //Figure element has same alt and caption text.
4832            if ($el.closest("figure")) {
4833              const figcaption = $el.closest("figure").querySelector("figcaption");
4834  
4835              if (figcaption !== null && figcaption.textContent.trim().toLowerCase === altText.trim().toLowerCase) {
4836                this.warningCount++;
4837                $el.classList.add("jooa11y-warning-border");
4838                $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), `$Lang.sprintf('IMAGE_FIGURE_DUPLICATE_ALT', altText)} <hr aria-hidden="true"> $Lang._('IMAGE_FIGURE_DECORATIVE_INFO')}`, false, true));
4839              }
4840            } //If image has alt text - pass!
4841            else {
4842              $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('GOOD'), `$Lang.sprintf('LINK_PASS_ALT', altText)}`, false, true));
4843            }
4844          }
4845        }
4846      });
4847    }
4848  
4849    // ============================================================
4850    // Rulesets: Labels
4851    // ============================================================
4852    checkLabels() {
4853      const $inputs = Array.from(this.$root.querySelectorAll('input, select, textarea')).filter($i => {
4854        return !this.$containerExclusions.includes($i) && !isElementHidden($i);
4855      });
4856      $inputs.forEach(el => {
4857        let ariaLabel = this.computeAriaLabel(el);
4858        const type = el.getAttribute('type'); //If button type is submit or button: pass
4859  
4860        if (type === "submit" || type === "button" || type === "hidden") ; //Inputs where type="image".
4861        else if (type === "image") {
4862          let imgalt = el.getAttribute("alt");
4863  
4864          if (!imgalt || imgalt === ' ') {
4865            if (el.getAttribute("aria-label")) ;else {
4866              this.errorCount++;
4867              el.classList.add("jooa11y-error-border");
4868              el.insertAdjacentHTML('afterend', this.annotate(Lang._('ERROR'), Lang._('LABELS_MISSING_IMAGE_INPUT_MESSAGE'), true));
4869            }
4870          }
4871        } //Recommendation to remove reset buttons.
4872        else if (type === "reset") {
4873          this.warningCount++;
4874          el.classList.add("jooa11y-warning-border");
4875          el.insertAdjacentHTML('afterend', this.annotate(Lang._('WARNING'), `$Lang._('LABELS_INPUT_RESET_MESSAGE')} <hr aria-hidden="true"> $Lang._('LABELS_INPUT_RESET_MESSAGE_TIP')}`, true));
4876        } //Uses ARIA. Warn them to ensure there's a visible label.
4877        else if (el.getAttribute("aria-label") || el.getAttribute("aria-labelledby") || el.getAttribute("title")) {
4878          if (el.getAttribute("title")) {
4879            let ariaLabel = el.getAttribute("title");
4880            this.warningCount++;
4881            el.classList.add("jooa11y-warning-border");
4882            el.insertAdjacentHTML('afterend', this.annotate(Lang._('WARNING'), `$Lang._('LABELS_ARIA_LABEL_INPUT_MESSAGE')} <hr aria-hidden="true"> $Lang.sprintf('LABELS_ARIA_LABEL_INPUT_MESSAGE_INFO', ariaLabel)}`, true));
4883          } else {
4884            this.warningCount++;
4885            el.classList.add("jooa11y-warning-border");
4886            el.insertAdjacentHTML('afterend', this.annotate(Lang._('WARNING'), `$Lang._('LABELS_ARIA_LABEL_INPUT_MESSAGE')} <hr aria-hidden="true"> $Lang.sprintf('LABELS_ARIA_LABEL_INPUT_MESSAGE_INFO', ariaLabel)}`, true));
4887          }
4888        } //Implicit labels.
4889        else if (el.closest('label') && el.closest('label').textContent.trim()) ; //Has an ID but doesn't have a matching FOR attribute.
4890        else if (el.getAttribute("id") && Array.from(el.parentElement.children).filter($c => $c.nodeName === 'LABEL').length) {
4891          const $labels = Array.from(el.parentElement.children).filter($c => $c.nodeName === 'LABEL');
4892          let hasFor = false;
4893          $labels.forEach($l => {
4894            if (hasFor) return;
4895  
4896            if ($l.getAttribute('for') === el.getAttribute('id')) {
4897              hasFor = true;
4898            }
4899          });
4900  
4901          if (!hasFor) {
4902            this.errorCount++;
4903            el.classList.add("jooa11y-error-border");
4904            el.insertAdjacentHTML('afterend', this.annotate(Lang._('ERROR'), `$Lang._('LABELS_NO_FOR_ATTRIBUTE_MESSAGE')} <hr aria-hidden="true"> $Lang.sprintf('LABELS_NO_FOR_ATTRIBUTE_MESSAGE_INFO', el.getAttribute('id'))}`, true));
4905          }
4906        } else {
4907          this.errorCount++;
4908          el.classList.add("jooa11y-error-border");
4909          el.insertAdjacentHTML('afterend', this.annotate(Lang._('ERROR'), Lang._('LABELS_MISSING_LABEL_MESSAGE'), true));
4910        }
4911      });
4912    }
4913  
4914    // ============================================================
4915    // Rulesets: Embedded content.
4916    // ============================================================
4917    checkEmbeddedContent() {
4918      const $findiframes = Array.from(this.$root.querySelectorAll("iframe, audio, video"));
4919      const $iframes = $findiframes.filter($el => !this.$containerExclusions.includes($el)); //Warning: Video content.
4920  
4921      const $videos = $iframes.filter($el => $el.matches(this.options.videoContent));
4922      $videos.forEach($el => {
4923        let track = $el.getElementsByTagName('TRACK');
4924        if ($el.tagName === "VIDEO" && track.length) ;else {
4925          this.warningCount++;
4926          $el.classList.add("jooa11y-warning-border");
4927          $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), Lang._('EMBED_VIDEO')));
4928        }
4929      }); //Warning: Audio content.
4930  
4931      const $audio = $iframes.filter($el => $el.matches(this.options.audioContent));
4932      $audio.forEach($el => {
4933        this.warningCount++;
4934        $el.classList.add("jooa11y-warning-border");
4935        $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), Lang._('EMBED_AUDIO')));
4936      }); //Error: iFrame is missing accessible name.
4937  
4938      $iframes.forEach($el => {
4939        if ($el.tagName === "VIDEO" || $el.tagName === "AUDIO" || $el.getAttribute("aria-hidden") === "true" || $el.getAttribute("hidden") !== null || $el.style.display === 'none' || $el.getAttribute("role") === "presentation") ;else if ($el.getAttribute("title") === null || $el.getAttribute("title") === '') {
4940          if ($el.getAttribute("aria-label") === null || $el.getAttribute("aria-label") === '') {
4941            if ($el.getAttribute("aria-labelledby") === null) {
4942              //Make sure red error border takes precedence
4943              if ($el.classList.contains("jooa11y-warning-border")) {
4944                $el.classList.remove("jooa11y-warning-border");
4945              }
4946  
4947              this.errorCount++;
4948              $el.classList.add("jooa11y-error-border");
4949              $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('ERROR'), Lang._('EMBED_MISSING_TITLE')));
4950            }
4951          }
4952        } else ;
4953      });
4954      const $embeddedcontent = $iframes.filter($el => !$el.matches(this.options.embeddedContent));
4955      $embeddedcontent.forEach($el => {
4956        if ($el.tagName === "VIDEO" || $el.tagName === "AUDIO" || $el.getAttribute("aria-hidden") === "true" || $el.getAttribute("hidden") !== null || $el.style.display === 'none' || $el.getAttribute("role") === "presentation" || $el.getAttribute("tabindex") === "-1") ;else {
4957          this.warningCount++;
4958          $el.classList.add("jooa11y-warning-border");
4959          $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), Lang._('EMBED_GENERAL_WARNING')));
4960        }
4961      });
4962    } // ============================================================
4963    // Rulesets: QA
4964    // ============================================================
4965  
4966  
4967    checkQA() {
4968      //Error: Find all links pointing to development environment.
4969      const $findbadDevLinks = this.options.linksToFlag ? Array.from(this.$root.querySelectorAll(this.options.linksToFlag)) : [];
4970      const $badDevLinks = $findbadDevLinks.filter($el => !this.$containerExclusions.includes($el));
4971      $badDevLinks.forEach($el => {
4972        this.errorCount++;
4973        $el.classList.add("jooa11y-error-text");
4974        $el.insertAdjacentHTML('afterend', this.annotate(Lang._('ERROR'), Lang.sprintf('QA_BAD_LINK', $el.getAttribute('href')), true));
4975      }); //Warning: Find all PDFs. Although only append warning icon to first PDF on page.
4976  
4977      let checkPDF = Array.from(this.$root.querySelectorAll('a[href$=".pdf"]')).filter(p => !this.$containerExclusions.includes(p));
4978      let firstPDF = checkPDF[0];
4979      let pdfCount = checkPDF.length;
4980  
4981      if (checkPDF.length > 0) {
4982        this.warningCount++;
4983        checkPDF.forEach($pdf => {
4984          $pdf.classList.add('jooa11y-warning-text');
4985  
4986          if ($pdf.querySelector('img')) {
4987            $pdf.classList.remove('jooa11y-warning-text');
4988          }
4989        });
4990        firstPDF.insertAdjacentHTML('afterend', this.annotate(Lang._('WARNING'), Lang.sprintf('QA_PDF_COUNT', pdfCount), true));
4991      } //Warning: Detect uppercase.
4992  
4993  
4994      const $findallcaps = Array.from(this.$root.querySelectorAll("h1, h2, h3, h4, h5, h6, p, li:not([class^='jooa11y']), blockquote"));
4995      const $allcaps = $findallcaps.filter($el => !this.$containerExclusions.includes($el));
4996      $allcaps.forEach(function ($el) {
4997        let uppercasePattern = /(?!<a[^>]*?>)(\b[A-Z][',!:A-Z\s]{15,}|\b[A-Z]{15,}\b)(?![^<]*?<\/a>)/g;
4998        let html = $el.innerHTML;
4999        $el.innerHTML = html.replace(uppercasePattern, "<span class='jooa11y-warning-uppercase'>$1</span>");
5000      });
5001      const $warningUppercase = document.querySelectorAll(".jooa11y-warning-uppercase");
5002      $warningUppercase.forEach($el => {
5003        $el.insertAdjacentHTML('afterend', this.annotate(Lang._('WARNING'), Lang._('QA_UPPERCASE_WARNING'), true));
5004      });
5005  
5006      if ($warningUppercase.length > 0) {
5007        this.warningCount++;
5008      } //Tables check.
5009  
5010  
5011      const $findtables = Array.from(this.$root.querySelectorAll("table:not([role='presentation'])"));
5012      const $tables = $findtables.filter($el => !this.$containerExclusions.includes($el));
5013      $tables.forEach($el => {
5014        let findTHeaders = $el.querySelectorAll("th");
5015        let findHeadingTags = $el.querySelectorAll("h1, h2, h3, h4, h5, h6");
5016  
5017        if (findTHeaders.length === 0) {
5018          this.errorCount++;
5019          $el.classList.add("jooa11y-error-border");
5020          $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('ERROR'), Lang._('TABLES_MISSING_HEADINGS')));
5021        }
5022  
5023        if (findHeadingTags.length > 0) {
5024          this.errorCount++;
5025          findHeadingTags.forEach($el => {
5026            $el.classList.add("jooa11y-error-border");
5027            $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('ERROR'), `$Lang._('TABLES_SEMANTIC_HEADING')} <hr aria-hidden="true"> $Lang._('TABLES_SEMANTIC_HEADING_INFO')}`));
5028          });
5029        }
5030  
5031        findTHeaders.forEach($el => {
5032          if ($el.textContent.trim().length === 0) {
5033            this.errorCount++;
5034            $el.classList.add("jooa11y-error-border");
5035            $el.innerHTML = this.annotate(Lang._('ERROR'), `$Lang._('TABLES_EMPTY_HEADING')} <hr aria-hidden="true"> $Lang._('TABLES_EMPTY_HEADING_INFO')}`);
5036          }
5037        });
5038      }); //Error: Missing language tag. Lang should be at least 2 characters.
5039  
5040      const lang = document.querySelector("html").getAttribute("lang");
5041  
5042      if (!lang || lang.length < 2) {
5043        this.errorCount++;
5044        const jooa11yContainer = document.getElementById("jooa11y-container");
5045        jooa11yContainer.insertAdjacentHTML('afterend', this.annotateBanner(Lang._('ERROR'), Lang._('QA_PAGE_LANGUAGE_MESSAGE')));
5046      } //Excessive bolding or italics.
5047  
5048  
5049      const $findstrongitalics = Array.from(this.$root.querySelectorAll("strong, em"));
5050      const $strongitalics = $findstrongitalics.filter($el => !this.$containerExclusions.includes($el));
5051      $strongitalics.forEach($el => {
5052        if ($el.textContent.trim().length > 200) {
5053          this.warningCount++;
5054          $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), Lang._('QA_BAD_ITALICS')));
5055        }
5056      }); //Find blockquotes used as headers.
5057  
5058      const $findblockquotes = Array.from(this.$root.querySelectorAll("blockquote"));
5059      const $blockquotes = $findblockquotes.filter($el => !this.$containerExclusions.includes($el));
5060      $blockquotes.forEach($el => {
5061        let bqHeadingText = $el.textContent;
5062  
5063        if (bqHeadingText.trim().length < 25) {
5064          this.warningCount++;
5065          $el.classList.add("jooa11y-warning-border");
5066          $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), `$Lang.sprintf('QA_BLOCKQUOTE_MESSAGE', bqHeadingText)} <hr aria-hidden="true"> $Lang._('QA_BLOCKQUOTE_MESSAGE_TIP')}`));
5067        }
5068      }); // Warning: Detect fake headings.
5069  
5070      this.$p.forEach($el => {
5071        let brAfter = $el.innerHTML.indexOf("</strong><br>");
5072        let brBefore = $el.innerHTML.indexOf("<br></strong>"); //Check paragraphs greater than x characters.
5073  
5074        if ($el && $el.textContent.trim().length >= 300) {
5075          let firstChild = $el.firstChild; //If paragraph starts with <strong> tag and ends with <br>.
5076  
5077          if (firstChild.tagName === "STRONG" && (brBefore !== -1 || brAfter !== -1)) {
5078            let boldtext = firstChild.textContent;
5079  
5080            if (!$el.closest("table") && boldtext.length <= 120) {
5081              firstChild.classList.add("jooa11y-fake-heading", "jooa11y-warning-border");
5082              $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), `$Lang.sprintf('QA_FAKE_HEADING', boldtext)} <hr aria-hidden="true"> $Lang._('QA_FAKE_HEADING_INFO')}`));
5083            }
5084          }
5085        } // If paragraph only contains <p><strong>...</strong></p>.
5086  
5087  
5088        if (/^<(strong)>.+<\/\1>$/.test($el.innerHTML.trim())) {
5089          //Although only flag if it:
5090          // 1) Has less than 120 characters (typical heading length).
5091          // 2) The previous element is not a heading.
5092          const prevElement = $el.previousElementSibling;
5093          let tagName = "";
5094  
5095          if (prevElement !== null) {
5096            tagName = prevElement.tagName;
5097          }
5098  
5099          if (!$el.closest("table") && $el.textContent.length <= 120 && tagName.charAt(0) !== "H") {
5100            let boldtext = $el.textContent;
5101            $el.classList.add("jooa11y-fake-heading", "jooa11y-warning-border");
5102            $el.firstChild.insertAdjacentHTML("afterend", this.annotate(Lang._('WARNING'), `$Lang.sprintf('QA_FAKE_HEADING', boldtext)} <hr aria-hidden="true"> $Lang._('QA_FAKE_HEADING_INFO')}`));
5103          }
5104        }
5105      });
5106  
5107      if (this.$root.querySelectorAll(".jooa11y-fake-heading").length > 0) {
5108        this.warningCount++;
5109      } // Check duplicate ID
5110  
5111  
5112      const ids = this.$root.querySelectorAll('[id]');
5113      let allIds = {};
5114      ids.forEach($el => {
5115        let id = $el.id;
5116  
5117        if (id) {
5118          if (allIds[id] === undefined) {
5119            allIds[id] = 1;
5120          } else {
5121            $el.classList.add("sa11y-error-border");
5122            $el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), `$Lang._('QA_DUPLICATE_ID')}
5123                                  <hr aria-hidden="true">
5124                                  $Lang.sprintf('QA_DUPLICATE_ID_TIP', id)}`, true));
5125          }
5126        }
5127      });
5128      /* Thanks to John Jameson from PrincetonU for this ruleset! */
5129      // Detect paragraphs that should be lists.
5130  
5131      let activeMatch = "";
5132      let prefixDecrement = {
5133        b: "a",
5134        B: "A",
5135        2: "1"
5136      };
5137      let prefixMatch = /a\.|a\)|A\.|A\)|1\.|1\)|\*\s|-\s|--|•\s|→\s|✓\s|✔\s|✗\s|✖\s|✘\s|❯\s|›\s|»\s/;
5138  
5139      let decrement = function (el) {
5140        return el.replace(/^b|^B|^2/, function (match) {
5141          return prefixDecrement[match];
5142        });
5143      };
5144  
5145      this.$p.forEach(el => {
5146        let hit = false; // Grab first two characters.
5147  
5148        let firstPrefix = el.textContent.substring(0, 2);
5149  
5150        if (firstPrefix.trim().length > 0 && firstPrefix !== activeMatch && firstPrefix.match(prefixMatch)) {
5151          // We have a prefix and a possible hit
5152          // Split p by carriage return if present and compare.
5153          let hasBreak = el.innerHTML.indexOf("<br>");
5154  
5155          if (hasBreak !== -1) {
5156            let subParagraph = el.innerHTML.substring(hasBreak + 4).trim();
5157            let subPrefix = subParagraph.substring(0, 2);
5158  
5159            if (firstPrefix === decrement(subPrefix)) {
5160              hit = true;
5161            }
5162          } // Decrement the second p prefix and compare .
5163  
5164  
5165          if (!hit) {
5166            let $second = el.nextElementSibling.nodeName === 'P' ? el.nextElementSibling : null;
5167  
5168            if ($second) {
5169              let secondPrefix = decrement(el.nextElementSibling.textContent.substring(0, 2));
5170  
5171              if (firstPrefix === secondPrefix) {
5172                hit = true;
5173              }
5174            }
5175          }
5176  
5177          if (hit) {
5178            this.warningCount++;
5179            el.insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), `$Lang.sprintf('QA_SHOULD_BE_LIST', firstPrefix)} <hr aria-hidden="true"> $Lang._('QA_SHOULD_BE_LIST_TIP')}`));
5180            el.classList.add("jooa11y-fake-list");
5181            activeMatch = firstPrefix;
5182          } else {
5183            activeMatch = "";
5184          }
5185        } else {
5186          activeMatch = "";
5187        }
5188      });
5189  
5190      if (this.$root.querySelectorAll('.jooa11y-fake-list').length > 0) {
5191        this.warningCount++;
5192      }
5193    }
5194  
5195    // ============================================================
5196    // Rulesets: Contrast
5197    // Color contrast plugin by jasonday: https://github.com/jasonday/color-contrast
5198    // ============================================================
5199    checkContrast() {
5200      const $findcontrast = Array.from(this.$root.querySelectorAll("* > :not(.jooa11y-heading-label)"));
5201      const $contrast = $findcontrast.filter($el => !this.$containerExclusions.includes($el));
5202      var contrastErrors = {
5203        errors: [],
5204        warnings: []
5205      };
5206      let elements = $contrast;
5207      let contrast = {
5208        // Parse rgb(r, g, b) and rgba(r, g, b, a) strings into an array.
5209        // Adapted from https://github.com/gka/chroma.js
5210        parseRgb: function (css) {
5211          let i, m, rgb, _i, _j;
5212  
5213          if (m = css.match(/rgb\(\s*(\-?\d+),\s*(\-?\d+)\s*,\s*(\-?\d+)\s*\)/)) {
5214            rgb = m.slice(1, 4);
5215  
5216            for (i = _i = 0; _i <= 2; i = ++_i) {
5217              rgb[i] = +rgb[i];
5218            }
5219  
5220            rgb[3] = 1;
5221          } else if (m = css.match(/rgba\(\s*(\-?\d+),\s*(\-?\d+)\s*,\s*(\-?\d+)\s*,\s*([01]|[01]?\.\d+)\)/)) {
5222            rgb = m.slice(1, 5);
5223  
5224            for (i = _j = 0; _j <= 3; i = ++_j) {
5225              rgb[i] = +rgb[i];
5226            }
5227          }
5228  
5229          return rgb;
5230        },
5231        // Based on http://www.w3.org/TR/WCAG20/#relativeluminancedef
5232        relativeLuminance: function (c) {
5233          let lum = [];
5234  
5235          for (let i = 0; i < 3; i++) {
5236            let v = c[i] / 255;
5237            lum.push(v < 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4));
5238          }
5239  
5240          return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
5241        },
5242        // Based on http://www.w3.org/TR/WCAG20/#contrast-ratiodef
5243        contrastRatio: function (x, y) {
5244          let l1 = contrast.relativeLuminance(contrast.parseRgb(x));
5245          let l2 = contrast.relativeLuminance(contrast.parseRgb(y));
5246          return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
5247        },
5248        getBackground: function (el) {
5249          let styles = getComputedStyle(el),
5250              bgColor = styles.backgroundColor,
5251              bgImage = styles.backgroundImage,
5252              rgb = contrast.parseRgb(bgColor) + '',
5253              alpha = rgb.split(','); // if background has alpha transparency, flag manual check
5254  
5255          if (alpha[3] < 1 && alpha[3] > 0) {
5256            return "alpha";
5257          } // if element has no background image, or transparent background (alpha == 0) return bgColor
5258  
5259  
5260          if (bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent' && bgImage === "none" && alpha[3] !== '0') {
5261            return bgColor;
5262          } else if (bgImage !== "none") {
5263            return "image";
5264          } // retest if not returned above
5265  
5266  
5267          if (el.tagName === 'HTML') {
5268            return 'rgb(255, 255, 255)';
5269          } else {
5270            return contrast.getBackground(el.parentNode);
5271          }
5272        },
5273        // check visibility - based on jQuery method
5274        // isVisible: function (el) {
5275        //     return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
5276        // },
5277        check: function () {
5278          // resets results
5279          contrastErrors = {
5280            errors: [],
5281            warnings: []
5282          };
5283  
5284          for (let i = 0; i < elements.length; i++) {
5285            (function (elem) {
5286              // Test if visible. Although we want invisible too.
5287              if (contrast
5288              /* .isVisible(elem) */
5289              ) {
5290                let style = getComputedStyle(elem),
5291                    color = style.color,
5292                    fill = style.fill,
5293                    fontSize = parseInt(style.fontSize),
5294                    pointSize = fontSize * 3 / 4,
5295                    fontWeight = style.fontWeight,
5296                    htmlTag = elem.tagName,
5297                    background = contrast.getBackground(elem),
5298                    textString = [].reduce.call(elem.childNodes, function (a, b) {
5299                  return a + (b.nodeType === 3 ? b.textContent : '');
5300                }, ''),
5301                    text = textString.trim(),
5302                    ratio,
5303                    error,
5304                    warning;
5305  
5306                if (htmlTag === "SVG") {
5307                  ratio = Math.round(contrast.contrastRatio(fill, background) * 100) / 100;
5308  
5309                  if (ratio < 3) {
5310                    error = {
5311                      elem: elem,
5312                      ratio: ratio + ':1'
5313                    };
5314                    contrastErrors.errors.push(error);
5315                  }
5316                } else if (text.length || htmlTag === "INPUT" || htmlTag === "SELECT" || htmlTag === "TEXTAREA") {
5317                  // does element have a background image - needs to be manually reviewed
5318                  if (background === "image") {
5319                    warning = {
5320                      elem: elem
5321                    };
5322                    contrastErrors.warnings.push(warning);
5323                  } else if (background === "alpha") {
5324                    warning = {
5325                      elem: elem
5326                    };
5327                    contrastErrors.warnings.push(warning);
5328                  } else {
5329                    ratio = Math.round(contrast.contrastRatio(color, background) * 100) / 100;
5330  
5331                    if (pointSize >= 18 || pointSize >= 14 && fontWeight >= 700) {
5332                      if (ratio < 3) {
5333                        error = {
5334                          elem: elem,
5335                          ratio: ratio + ':1'
5336                        };
5337                        contrastErrors.errors.push(error);
5338                      }
5339                    } else {
5340                      if (ratio < 4.5) {
5341                        error = {
5342                          elem: elem,
5343                          ratio: ratio + ':1'
5344                        };
5345                        contrastErrors.errors.push(error);
5346                      }
5347                    }
5348                  }
5349                }
5350              }
5351            })(elements[i]);
5352          }
5353  
5354          return contrastErrors;
5355        }
5356      };
5357      contrast.check(); //const {errorMessage, warningMessage} = jooa11yIM["contrast"];
5358  
5359      contrastErrors.errors.forEach(item => {
5360        let name = item.elem;
5361        let cratio = item.ratio;
5362        let clone = name.cloneNode(true);
5363        let removeJooa11yHeadingLabel = clone.querySelectorAll('.jooa11y-heading-label');
5364  
5365        for (let i = 0; i < removeJooa11yHeadingLabel.length; i++) {
5366          clone.removeChild(removeJooa11yHeadingLabel[i]);
5367        }
5368  
5369        let nodetext = clone.textContent;
5370        this.errorCount++;
5371  
5372        if (name.tagName === "INPUT") {
5373          name.insertAdjacentHTML('beforebegin', this.annotate(Lang._('ERROR'), `$Lang._('CONTRAST_ERROR_INPUT_MESSAGE')}
5374                           <hr aria-hidden="true">
5375                           $Lang.sprintf('CONTRAST_ERROR_INPUT_MESSAGE_INFO', cratio)}`, true));
5376        } else {
5377          name.insertAdjacentHTML('beforebegin', this.annotate(Lang._('ERROR'), `$Lang.sprintf('CONTRAST_ERROR_MESSAGE', cratio, nodetext)}
5378                          <hr aria-hidden="true">
5379                          $Lang.sprintf('CONTRAST_ERROR_MESSAGE_INFO', cratio, nodetext)}`, true));
5380        }
5381      });
5382      contrastErrors.warnings.forEach(item => {
5383        let name = item.elem;
5384        let clone = name.cloneNode(true);
5385        let removeJooa11yHeadingLabel = clone.querySelectorAll('.jooa11y-heading-label');
5386  
5387        for (let i = 0; i < removeJooa11yHeadingLabel.length; i++) {
5388          clone.removeChild(removeJooa11yHeadingLabel[i]);
5389        }
5390  
5391        let nodetext = clone.textContent;
5392        this.warningCount++;
5393        name.insertAdjacentHTML('beforebegin', this.annotate(Lang._('WARNING'), `$Lang._('CONTRAST_WARNING_MESSAGE')} <hr aria-hidden="true"> $Lang.sprintf('CONTRAST_WARNING_MESSAGE_INFO', nodetext)}`, true));
5394      });
5395    }
5396  
5397    // ============================================================
5398    // Rulesets: Readability
5399    // Adapted from Greg Kraus' readability script: https://accessibility.oit.ncsu.edu/it-accessibility-at-nc-state/developers/tools/readability-bookmarklet/
5400    // ============================================================
5401    checkReadability() {
5402      const container = document.querySelector(this.options.readabilityRoot);
5403      const $findreadability = Array.from(container.querySelectorAll("p, li"));
5404      const $readability = $findreadability.filter($el => !this.$containerExclusions.includes($el)); //Crude hack to add a period to the end of list items to make a complete sentence.
5405  
5406      $readability.forEach($el => {
5407        let listText = $el.textContent;
5408  
5409        if (listText.length >= 120) {
5410          if (listText.charAt(listText.length - 1) !== ".") {
5411            $el.insertAdjacentHTML("beforeend", "<span class='jooa11y-readability-period jooa11y-visually-hidden'>.</span>");
5412          }
5413        }
5414      }); // Compute syllables: http://stackoverflow.com/questions/5686483/how-to-compute-number-of-syllables-in-a-word-in-javascript
5415  
5416      function number_of_syllables(wordCheck) {
5417        wordCheck = wordCheck.toLowerCase().replace('.', '').replace('\n', '');
5418  
5419        if (wordCheck.length <= 3) {
5420          return 1;
5421        }
5422  
5423        wordCheck = wordCheck.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, '');
5424        wordCheck = wordCheck.replace(/^y/, '');
5425        let syllable_string = wordCheck.match(/[aeiouy]{1,2}/g);
5426        let syllables = 0;
5427  
5428        if (!!syllable_string) {
5429          syllables = syllable_string.length;
5430        }
5431  
5432        return syllables;
5433      }
5434  
5435      let readabilityarray = [];
5436  
5437      for (let i = 0; i < $readability.length; i++) {
5438        var current = $readability[i];
5439  
5440        if (current.textContent.replace(/ |\n/g, '') !== '') {
5441          readabilityarray.push(current.textContent);
5442        }
5443      }
5444  
5445      let paragraphtext = readabilityarray.join(' ').trim().toString();
5446      let words_raw = paragraphtext.replace(/[.!?-]+/g, ' ').split(' ');
5447      let words = 0;
5448  
5449      for (let i = 0; i < words_raw.length; i++) {
5450        if (words_raw[i] != 0) {
5451          words = words + 1;
5452        }
5453      }
5454  
5455      let sentences_raw = paragraphtext.split(/[.!?]+/);
5456      let sentences = 0;
5457  
5458      for (let i = 0; i < sentences_raw.length; i++) {
5459        if (sentences_raw[i] !== '') {
5460          sentences = sentences + 1;
5461        }
5462      }
5463  
5464      let total_syllables = 0;
5465      let syllables1 = 0;
5466      let syllables2 = 0;
5467  
5468      for (let i = 0; i < words_raw.length; i++) {
5469        if (words_raw[i] != 0) {
5470          var syllable_count = number_of_syllables(words_raw[i]);
5471  
5472          if (syllable_count === 1) {
5473            syllables1 = syllables1 + 1;
5474          }
5475  
5476          if (syllable_count === 2) {
5477            syllables2 = syllables2 + 1;
5478          }
5479  
5480          total_syllables = total_syllables + syllable_count;
5481        }
5482      } //var characters = paragraphtext.replace(/[.!?|\s]+/g, '').length;
5483      //Reference: https://core.ac.uk/download/pdf/6552422.pdf
5484      //Reference: https://github.com/Yoast/YoastSEO.js/issues/267
5485  
5486  
5487      let flesch_reading_ease;
5488  
5489      if (this.options.readabilityLang === 'en') {
5490        flesch_reading_ease = 206.835 - 1.015 * words / sentences - 84.6 * total_syllables / words;
5491      } else if (this.options.readabilityLang === 'fr') {
5492        //French (Kandel & Moles)
5493        flesch_reading_ease = 207 - 1.015 * words / sentences - 73.6 * total_syllables / words;
5494      } else if (this.options.readabilityLang === 'es') {
5495        flesch_reading_ease = 206.84 - 1.02 * words / sentences - 0.60 * (100 * total_syllables / words);
5496      }
5497  
5498      if (flesch_reading_ease > 100) {
5499        flesch_reading_ease = 100;
5500      } else if (flesch_reading_ease < 0) {
5501        flesch_reading_ease = 0;
5502      }
5503  
5504      const $readabilityinfo = document.getElementById("jooa11y-readability-info");
5505  
5506      if (paragraphtext.length === 0) {
5507        $readabilityinfo.innerHTML = Lang._('READABILITY_NO_P_OR_LI_MESSAGE');
5508      } else if (words > 30) {
5509        let fleschScore = flesch_reading_ease.toFixed(1);
5510        let avgWordsPerSentence = (words / sentences).toFixed(1);
5511        let complexWords = Math.round(100 * ((words - (syllables1 + syllables2)) / words)); //WCAG AAA pass if greater than 60
5512  
5513        if (fleschScore >= 0 && fleschScore < 30) {
5514          $readabilityinfo.innerHTML = `<span>$fleschScore}</span> <span class="jooa11y-readability-score">$Lang._('VERY_DIFFICULT_READABILITY')}</span>`;
5515        } else if (fleschScore > 31 && fleschScore < 49) {
5516          $readabilityinfo.innerHTML = `<span>$fleschScore}</span> <span class="jooa11y-readability-score">$Lang._('DIFFICULT_READABILITY')}</span>`;
5517        } else if (fleschScore > 50 && fleschScore < 60) {
5518          $readabilityinfo.innerHTML = `<span>$fleschScore}</span> <span class="jooa11y-readability-score">$Lang._('FAIRLY_DIFFICULT_READABILITY')}</span>`;
5519        } else {
5520          $readabilityinfo.innerHTML = `<span>$fleschScore}</span> <span class="jooa11y-readability-score">$Lang._('GOOD_READABILITY')}</span>`;
5521        }
5522  
5523        document.getElementById("jooa11y-readability-details").innerHTML = `<li><span class='jooa11y-bold'>$Lang._('AVG_WORD_PER_SENTENCE')}</span> $avgWordsPerSentence}</li>
5524                  <li><span class='jooa11y-bold'>$Lang._('COMPLEX_WORDS')}</span> $complexWords}%</li>
5525                  <li><span class='jooa11y-bold'>$Lang._('TOTAL_WORDS')}</span> $words}</li>`;
5526      } else {
5527        $readabilityinfo.textContent = Lang._('READABILITY_NOT_ENOUGH_CONTENT_MESSAGE');
5528      }
5529    }
5530  
5531    //----------------------------------------------------------------------
5532    // Templating for Error, Warning and Pass buttons.
5533    //----------------------------------------------------------------------
5534    annotate(type, content, inline = false) {
5535      const validTypes = [Lang._('ERROR'), Lang._('WARNING'), Lang._('GOOD')];
5536  
5537      if (validTypes.indexOf(type) === -1) {
5538        throw Error(`Invalid type [$type}] for annotation`);
5539      }
5540  
5541      const CSSName = {
5542        [validTypes[0]]: "error",
5543        [validTypes[1]]: "warning",
5544        [validTypes[2]]: "good"
5545      }; // Check if content is a function
5546  
5547      if (content && {}.toString.call(content) === "[object Function]") {
5548        // if it is, call it and get the value.
5549        content = content();
5550      } // Escape content, it is need because it used inside data-tippy-content=""
5551  
5552  
5553      content = escapeHTML(content);
5554      return `
5555          <div class=$inline ? "jooa11y-instance-inline" : "jooa11y-instance"}>
5556              <button
5557              type="button"
5558              aria-label="${[type]}"
5559              class="jooa11y-btn jooa11y-$CSSName[type]}-btn$inline ? "-text" : ""}"
5560              data-tippy-content="<div lang='$this.options.langCode}'>
5561                  <div class='jooa11y-header-text'>${[type]}</div>
5562                  $content}
5563              </div>
5564          ">
5565          </button>
5566          </div>`;
5567    }
5568  
5569    //----------------------------------------------------------------------
5570    // Templating for full-width banners.
5571    //----------------------------------------------------------------------
5572    annotateBanner(type, content) {
5573      const validTypes = [Lang._('ERROR'), Lang._('WARNING'), Lang._('GOOD')];
5574  
5575      if (validTypes.indexOf(type) === -1) {
5576        throw Error(`Invalid type [$type}] for annotation`);
5577      }
5578  
5579      const CSSName = {
5580        [validTypes[0]]: "error",
5581        [validTypes[1]]: "warning",
5582        [validTypes[2]]: "good"
5583      }; // Check if content is a function
5584  
5585      if (content && {}.toString.call(content) === "[object Function]") {
5586        // if it is, call it and get the value.
5587        content = content();
5588      }
5589  
5590      return `<div class="jooa11y-instance jooa11y-$CSSName[type]}-message-container">
5591        <div role="region" aria-label="${[type]}" class="jooa11y-$CSSName[type]}-message" lang="$this.options.langCode}">
5592            $content}
5593        </div>
5594    </div>`;
5595    }
5596  
5597  }
5598  
5599  if (!Joomla) {
5600    throw new Error('Joomla API is not properly initialised');
5601  }
5602  
5603  const stringPrefix = 'PLG_SYSTEM_JOOA11Y_';
5604  
5605  Lang.translate = string => Joomla.Text._(stringPrefix + string, string);
5606  
5607  const options = Joomla.getOptions('jooa11yOptions');
5608  window.addEventListener('load', () => {
5609    // Instantiate
5610    const checker = new Jooa11y(options);
5611    checker.doInitialCheck();
5612  });


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer