/*! skipto - v4.1.3 - 2021-10-08
* https://github.com/paypal/skipto
* Copyright (c) 2021 PayPal Accessibility Team and University of Illinois; Licensed BSD */
 /*@cc_on @*/
/*@if (@_jscript_version >= 5.8) @*/
/* ========================================================================
* Copyright (c) <2021> PayPal and University of Illinois
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of PayPal or any of its subsidiaries or affiliates nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ======================================================================== */

(function() {
  'use strict';
  var SkipTo = {
    skipToId: 'id-skip-to-js-4',
    skipToMenuId: 'id-skip-to-menu-4',
    domNode: null,
    buttonNode: null,
    menuNode: null,
    tooltipNode: null,
    menuitemNodes: [],
    firstMenuitem: false,
    lastMenuitem: false,
    firstChars: [],
    headingLevels: [],
    skipToIdIndex: 1,
    showAllLandmarksSelector: 'main, [role=main], [role=search], nav, [role=navigation], section[aria-label], section[aria-labelledby], section[title], [role=region][aria-label], [role=region][aria-labelledby], [role=region][title], form[aria-label], form[aria-labelledby], aside, [role=complementary], body > header, [role=banner], body > footer, [role=contentinfo]',
    showAllHeadingsSelector: 'h1, h2, h3, h4, h5, h6',
    showTooltipFocus: false,
    showTooltipHover: false,
    tooltipTimerDelay: 500,  // in milliseconds
    // Default configuration values
    config: {
      // Feature switches
      enableActions: false,
      enableMofN: true,
      enableHeadingLevelShortcuts: true,
      enableHelp: true,
      enableTooltip: true,
      // Customization of button and menu
      accesskey: '0', // default is the number zero
      attachElement: 'header',
      displayOption: 'static', // options: static (default), popup
      // container element, use containerClass for custom styling
      containerElement: 'div',
      containerRole: '',
      customClass: '',

      // Button labels and messages
      buttonTitle: '',   // deprecated in favor of buttonTooltip
      buttonTitleWithAccesskey: '',  // deprecated in favor of buttonTooltipAccesskey
      buttonTooltip: '',
      buttonTooltipAccesskey: 'Shortcut Key: $key',
      buttonLabel: 'Skip To Content',

      // Menu labels and messages
      menuLabel: 'Landmarks and Headings',
      landmarkGroupLabel: 'Landmarks',
      headingGroupLabel: 'Headings',
      mofnGroupLabel: ' ($m of $n)',
      headingLevelLabel: 'Heading level',
      mainLabel: 'main',
      searchLabel: 'search',
      navLabel: 'navigation',
      regionLabel: 'region',
      asideLabel: 'complementary',
      footerLabel: 'contentinfo',
      headerLabel: 'banner',
      formLabel: 'form',
      msgNoLandmarksFound: 'No landmarks found',
      msgNoHeadingsFound: 'No headings found',

      // Action labels and messages
      actionGroupLabel: 'Actions',
      actionShowHeadingsHelp: 'Toggles between showing "All" and "Selected" Headings.',
      actionShowSelectedHeadingsLabel: 'Show Selected Headings ($num)',
      actionShowAllHeadingsLabel: 'Show All Headings ($num)',
      actionShowLandmarksHelp: 'Toggles between showing "All" and "Selected" Landmarks.',
      actionShowSelectedLandmarksLabel: 'Show Selected Landmarks ($num)',
      actionShowAllLandmarksLabel: 'Show All Landmarks ($num)',

      actionShowSelectedHeadingsAriaLabel: 'Show $num selected headings',
      actionShowAllHeadingsAriaLabel: 'Show all $num headings',
      actionShowSelectedLandmarksAriaLabel: 'Show $num selected landmarks',
      actionShowAllLandmarksAriaLabel: 'Show all $num landmarks',

      // Selectors for landmark and headings sections
      landmarks: 'main, [role="main"], [role="search"], nav, [role="navigation"], aside, [role="complementary"]',
      headings: 'main h1, [role="main"] h1, main h2, [role="main"] h2',

      // Custom CSS position and colors
      colorTheme: '',
      fontFamily: '',
      fontSize: '',
      positionLeft: '',
      menuTextColor: '',
      menuBackgroundColor: '',
      menuitemFocusTextColor: '',
      menuitemFocusBackgroundColor: '',
      focusBorderColor: '',
      buttonTextColor: '',
      buttonBackgroundColor: '',
    },
    colorThemes: {
      'default': {
        fontFamily: 'inherit',
        fontSize: 'inherit',
        positionLeft: '46%',
        menuTextColor: '#1a1a1a',
        menuBackgroundColor: '#dcdcdc',
        menuitemFocusTextColor: '#eeeeee',
        menuitemFocusBackgroundColor: '#1a1a1a',
        focusBorderColor: '#1a1a1a',
        buttonTextColor: '#1a1a1a',
        buttonBackgroundColor: '#eeeeee',
      },
      'illinois': {
        fontFamily: 'inherit',
        fontSize: 'inherit',
        positionLeft: '46%',
        menuTextColor: '#00132c',
        menuBackgroundColor: '#cad9ef',
        menuitemFocusTextColor: '#eeeeee',
        menuitemFocusBackgroundColor: '#00132c',
        focusBorderColor: '#ff552e',
        buttonTextColor: '#444444',
        buttonBackgroundColor: '#dddede',
      },
      'aria': {
        fontFamily: 'sans-serif',
        fontSize: '10pt',
        positionLeft: '7%',
        menuTextColor: '#000',
        menuBackgroundColor: '#def',
        menuitemFocusTextColor: '#fff',
        menuitemFocusBackgroundColor: '#005a9c',
        focusBorderColor: '#005a9c',
        buttonTextColor: '#005a9c',
        buttonBackgroundColor: '#ddd',
      }
    },
    defaultCSS: '.skip-to.popup{position:absolute;top:-30em;left:0}.skip-to,.skip-to.popup.focus{position:absolute;top:0;left:$positionLeft;font-family:$fontFamily;font-size:$fontSize}.skip-to.fixed{position:fixed}.skip-to button{position:relative;margin:0;padding:6px 8px 6px 8px;border-width:0 1px 1px 1px;border-style:solid;border-radius:0 0 6px 6px;border-color:$buttonBackgroundColor;color:$menuTextColor;background-color:$buttonBackgroundColor;z-index:200;font-family:$fontFamily;font-size:$fontSize}.skip-to .skip-to-tooltip{position:absolute;top:2.25em;left:8em;margin:1px;padding:4px;border:1px solid #ccc;box-shadow:2px 3px 5px #ddd;background-color:#eee;color:#000;font-family:Helvetica,Arial,Sans-Serif;font-variant-numeric:slashed-zero;font-size:9pt;width:auto;display:none;white-space:nowrap;z-index:201}.skip-to .skip-to-tooltip.skip-to-show-tooltip{display:block}.skip-to [aria-expanded=true]+.skip-to-tooltip.skip-to-show-tooltip{display:none}.skip-to [role=menu]{position:absolute;min-width:17em;display:none;margin:0;padding:.25rem;background-color:$menuBackgroundColor;border-width:2px;border-style:solid;border-color:$focusBorderColor;border-radius:5px;z-index:1000}.skip-to [role=group]{display:grid;grid-auto-rows:min-content;grid-row-gap:1px}.skip-to [role=separator]:first-child{border-radius:5px 5px 0 0}.skip-to [role=menuitem]{padding:3px;width:auto;border-width:0;border-style:solid;color:$menuTextColor;background-color:$menuBackgroundColor;z-index:1000;display:grid;overflow-y:auto;grid-template-columns:repeat(6,1.2rem) 1fr;grid-column-gap:2px;font-size:1em}.skip-to [role=menuitem] .label,.skip-to [role=menuitem] .level{font-size:100%;font-weight:400;color:$menuTextColor;display:inline-block;background-color:$menuBackgroundColor;line-height:inherit;display:inline-block}.skip-to [role=menuitem] .level{text-align:right;padding-right:4px}.skip-to [role=menuitem] .label{text-align:left;margin:0;padding:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.skip-to [role=menuitem] .label:first-letter,.skip-to [role=menuitem] .level:first-letter{text-decoration:underline;text-transform:uppercase}.skip-to [role=menuitem].skip-to-h1 .level{grid-column:1}.skip-to [role=menuitem].skip-to-h2 .level{grid-column:2}.skip-to [role=menuitem].skip-to-h3 .level{grid-column:3}.skip-to [role=menuitem].skip-to-h4 .level{grid-column:4}.skip-to [role=menuitem].skip-to-h5 .level{grid-column:5}.skip-to [role=menuitem].skip-to-h6 .level{grid-column:8}.skip-to [role=menuitem].skip-to-h1 .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-h2 .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-h3 .label{grid-column:4/8}.skip-to [role=menuitem].skip-to-h4 .label{grid-column:5/8}.skip-to [role=menuitem].skip-to-h5 .label{grid-column:6/8}.skip-to [role=menuitem].skip-to-h6 .label{grid-column:7/8}.skip-to [role=menuitem].skip-to-h1.no-level .label{grid-column:1/8}.skip-to [role=menuitem].skip-to-h2.no-level .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-h3.no-level .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-h4.no-level .label{grid-column:4/8}.skip-to [role=menuitem].skip-to-h5.no-level .label{grid-column:5/8}.skip-to [role=menuitem].skip-to-h6.no-level .label{grid-column:6/8}.skip-to [role=menuitem].skip-to-nesting-level-1 .nesting{grid-column:1}.skip-to [role=menuitem].skip-to-nesting-level-2 .nesting{grid-column:2}.skip-to [role=menuitem].skip-to-nesting-level-3 .nesting{grid-column:3}.skip-to [role=menuitem].skip-to-nesting-level-0 .label{grid-column:1/8}.skip-to [role=menuitem].skip-to-nesting-level-1 .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-nesting-level-2 .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-nesting-level-3 .label{grid-column:4/8}.skip-to [role=menuitem].action .label,.skip-to [role=menuitem].no-items .label{grid-column:1/8}.skip-to [role=separator]{margin:1px 0 1px 0;padding:3px;display:block;width:auto;font-weight:700;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:$menuTextColor;background-color:$menuBackgroundColor;color:$menuTextColor;z-index:1000}.skip-to [role=separator] .mofn{font-weight:400;font-size:85%}.skip-to [role=separator]:first-child{border-radius:5px 5px 0 0}.skip-to [role=menuitem].last{border-radius:0 0 5px 5px}.skip-to.focus{display:block}.skip-to button:focus,.skip-to button:hover{background-color:$menuBackgroundColor;color:$menuTextColor;outline:0}.skip-to button:focus{padding:6px 7px 5px 7px;border-width:0 2px 2px 2px;border-color:$focusBorderColor}.skip-to [role=menuitem]:focus{padding:1px;border-width:2px;border-style:solid;border-color:$focusBorderColor;background-color:$menuitemFocusBackgroundColor;color:$menuitemFocusTextColor;outline:0}.skip-to [role=menuitem]:focus .label,.skip-to [role=menuitem]:focus .level{background-color:$menuitemFocusBackgroundColor;color:$menuitemFocusTextColor}',

    //
    // Functions related to configuring the features
    // of skipTo
    //
    isNotEmptyString: function(str) {
      return (typeof str === 'string') && str.length;
    },
    isEmptyString: function(str) {
      return (typeof str !== 'string') || str.length === 0;
    },
    init: function(config) {
      var node;
      // Check if skipto is already loaded

      if (document.querySelector('style#' + this.skipToId)) {
        return;
      }

      var attachElement = document.body;
      if (config) {
        this.setUpConfig(config);
      }
      if (typeof this.config.attachElement === 'string') {
        node = document.querySelector(this.config.attachElement);
        if (node && node.nodeType === Node.ELEMENT_NODE) {
          attachElement = node;
        }
      }
      this.addCSSColors();
      this.renderStyleElement(this.defaultCSS);
      var elem = this.config.containerElement.toLowerCase().trim();
      if (!this.isNotEmptyString(elem)) {
        elem = 'div';
      }
      this.domNode = document.createElement(elem);
      this.domNode.classList.add('skip-to');
      if (this.isNotEmptyString(this.config.customClass)) {
        this.domNode.classList.add(this.config.customClass);
      }
      if (this.isNotEmptyString(this.config.containerRole)) {
        this.domNode.setAttribute('role', this.config.containerRole);
      }
      var displayOption = this.config.displayOption;
      if (typeof displayOption === 'string') {
        displayOption = displayOption.trim().toLowerCase();
        if (displayOption.length) {
          switch (this.config.displayOption) {
            case 'fixed':
              this.domNode.classList.add('fixed');
              break;
            case 'onfocus':  // Legacy option
            case 'popup':
              this.domNode.classList.add('popup');
              break;
            default:
              break;
          }
        }
      }
      // Place skip to at the beginning of the document
      if (attachElement.firstElementChild) {
        attachElement.insertBefore(this.domNode, attachElement.firstElementChild);
      } else {
        attachElement.appendChild(this.domNode);
      }
      this.buttonNode = document.createElement('button');
      this.buttonNode.textContent = this.config.buttonLabel;
      this.buttonNode.setAttribute('aria-haspopup', 'true');
      this.buttonNode.setAttribute('aria-expanded', 'false');
      this.buttonNode.setAttribute('aria-controls', this.skipToMenuId);
      this.buttonNode.setAttribute('accesskey', this.config.accesskey);

      this.domNode.appendChild(this.buttonNode);

      this.renderTooltip(this.domNode, this.buttonNode);

      this.menuNode = document.createElement('div');
      this.menuNode.setAttribute('role', 'menu');
      this.menuNode.setAttribute('aria-busy', 'true');
      this.menuNode.setAttribute('id', this.skipToMenuId);
      this.domNode.appendChild(this.menuNode);
      this.buttonNode.addEventListener('keydown', this.handleButtonKeydown.bind(this));
      this.buttonNode.addEventListener('click', this.handleButtonClick.bind(this));
      this.buttonNode.addEventListener('focus', this.handleButtonFocus.bind(this));
      this.buttonNode.addEventListener('blur', this.handleButtonBlur.bind(this));
      this.buttonNode.addEventListener('pointerenter', this.handleButtonPointerenter.bind(this));
      this.buttonNode.addEventListener('pointerout', this.handleButtonPointerout.bind(this));
      this.domNode.addEventListener('focusin', this.handleFocusin.bind(this));
      this.domNode.addEventListener('focusout', this.handleFocusout.bind(this));
      window.addEventListener('pointerdown', this.handleBackgroundPointerdown.bind(this), true);

    },
    renderTooltip: function(attachNode, buttonNode) {
      var id = 'id-skip-to-tooltip';
      var accesskey = this.getBrowserSpecificAccesskey(this.config.accesskey);

      var tooltip = this.config.buttonTooltip;
      // for backward compatibility, support 'this.config.buttonTitle' if defined
      if (this.isNotEmptyString(this.config.buttonTitle)) {
        tooltip = this.config.buttonTitle;
      }

      this.tooltipLeft = buttonNode.getBoundingClientRect().width;
      this.tooltipTop  = buttonNode.getBoundingClientRect().height;

      this.tooltipNode = document.createElement('div');
      this.tooltipNode.setAttribute('role', 'tooltip');
      this.tooltipNode.id = id;
      this.tooltipNode.classList.add('skip-to-tooltip');

      if (this.isNotEmptyString(accesskey)) {
        tooltip = this.config.buttonTooltipAccesskey.replace('$key', accesskey);
        // for backward compatibility support 'buttonTitleWithAccesskey' if defined
        if (this.isNotEmptyString(this.config.buttonTitleWithAccesskey)) {
          tooltip = this.config.buttonTitleWithAccesskey.replace('$key', accesskey);
        }
      }

      if (this.isEmptyString(tooltip)) {
        // if there is no tooltip information
        // do not display tooltip
        this.config.enableTooltip = false;
      } else {
        this.tooltipNode.textContent = tooltip;
      }

      attachNode.appendChild(this.tooltipNode);
      this.tooltipNode.style.left = this.tooltipLeft + 'px';
      this.tooltipNode.style.top = this.tooltipTop + 'px';

      // Temporarily show the tooltip to get rendered height
      this.tooltipNode.classList.add('skip-to-show-tooltip');
      this.tooltipHeight = this.tooltipNode.getBoundingClientRect().height;
      this.tooltipNode.classList.remove('skip-to-show-tooltip');
    },

    updateStyle: function(stylePlaceholder, value, defaultValue) {
      if (typeof value !== 'string' || value.length === 0) {
        value = defaultValue;
      }
      var index1 = this.defaultCSS.indexOf(stylePlaceholder);
      var index2 = index1 + stylePlaceholder.length;
      while (index1 >= 0 && index2 < this.defaultCSS.length) {
        this.defaultCSS = this.defaultCSS.substring(0, index1) + value + this.defaultCSS.substring(index2);
        index1 = this.defaultCSS.indexOf(stylePlaceholder, index2);
        index2 = index1 + stylePlaceholder.length;
      }
    },
    addCSSColors: function() {
      var theme = this.colorThemes['default'];
      if (typeof this.colorThemes[this.config.colorTheme] === 'object') {
        theme = this.colorThemes[this.config.colorTheme];
      }
      this.updateStyle('$fontFamily', this.config.fontFamily, theme.fontFamily);
      this.updateStyle('$fontSize', this.config.fontSize, theme.fontSize);

      this.updateStyle('$positionLeft', this.config.positionLeft, theme.positionLeft);

      this.updateStyle('$menuTextColor', this.config.menuTextColor, theme.menuTextColor);
      this.updateStyle('$menuBackgroundColor', this.config.menuBackgroundColor, theme.menuBackgroundColor);

      this.updateStyle('$menuitemFocusTextColor', this.config.menuitemFocusTextColor, theme.menuitemFocusTextColor);
      this.updateStyle('$menuitemFocusBackgroundColor', this.config.menuitemFocusBackgroundColor, theme.menuitemFocusBackgroundColor);

      this.updateStyle('$focusBorderColor', this.config.focusBorderColor, theme.focusBorderColor);

      this.updateStyle('$buttonTextColor', this.config.buttonTextColor, theme.buttonTextColor);
      this.updateStyle('$buttonBackgroundColor', this.config.buttonBackgroundColor, theme.buttonBackgroundColor);
    },

    getBrowserSpecificAccesskey: function (accesskey) {
      var userAgent = navigator.userAgent.toLowerCase();
      var platform =  navigator.platform.toLowerCase();

      var hasWin    = platform.indexOf('win') >= 0;
      var hasMac    = platform.indexOf('mac') >= 0;
      var hasLinux  = platform.indexOf('linux') >= 0 || platform.indexOf('bsd') >= 0;

      var hasAndroid = userAgent.indexOf('android') >= 0;
      var hasFirefox = userAgent.indexOf('firefox') >= 0;
      var hasChrome = userAgent.indexOf('chrome') >= 0;
      var hasOpera = userAgent.indexOf('opr') >= 0;

      if (typeof accesskey !== 'string' || accesskey.length === 0) {
        return '';
      }

      if (hasWin || (hasLinux && !hasAndroid)) {
        if (hasFirefox) {
          return "Shift + Alt + " + accesskey;
        } else {
          if (hasChrome || hasOpera) {
            return "Alt + " + accesskey;
          }
        }
      }

      if (hasMac) {
        return "Ctrl + Option + " + accesskey;
      }

      return '';
    },
    setUpConfig: function(appConfig) {
      var localConfig = this.config,
        name,
        appConfigSettings = typeof appConfig.settings !== 'undefined' ? appConfig.settings.skipTo : {};
      for (name in appConfigSettings) {
        //overwrite values of our local config, based on the external config
        if ((typeof localConfig[name] !== 'undefined') &&
           ((typeof appConfigSettings[name] === 'string') &&
            (appConfigSettings[name].length > 0 ) ||
           typeof appConfigSettings[name] === 'boolean')
          ) {
          localConfig[name] = appConfigSettings[name];
        } else {
          throw new Error('** SkipTo Problem with user configuration option "' + name + '".');
        }
      }
    },
    renderStyleElement: function(cssString) {
      var styleNode = document.createElement('style');
      var headNode = document.getElementsByTagName('head')[0];
      var css = document.createTextNode(cssString);

      styleNode.setAttribute("type", "text/css");
      // ID is used to test whether skipto is already loaded
      styleNode.id = this.skipToId;
      styleNode.appendChild(css);
      headNode.appendChild(styleNode);
    },

    //
    // Functions related to creating and populating the
    // the popup menu
    //

    getFirstChar: function(menuitem) {
      var c = '';
      var label = menuitem.querySelector('.label');
      if (label && this.isNotEmptyString(label.textContent)) {
        c = label.textContent.trim()[0].toLowerCase();
      }
      return c;
    },

    getHeadingLevelFromAttribute: function(menuitem) {
      var level = '';
      if (menuitem.hasAttribute('data-level')) {
        level = menuitem.getAttribute('data-level');
      }
      return level;
    },

    updateKeyboardShortCuts: function () {
      var mi;
      this.firstChars = [];
      this.headingLevels = [];

      for(var i = 0; i < this.menuitemNodes.length; i += 1) {
        mi = this.menuitemNodes[i];
        this.firstChars.push(this.getFirstChar(mi));
        this.headingLevels.push(this.getHeadingLevelFromAttribute(mi));
      }
    },

    updateMenuitems: function () {
      var menuitemNodes = this.menuNode.querySelectorAll('[role=menuitem');

      this.menuitemNodes = [];
      for(var i = 0; i < menuitemNodes.length; i += 1) {
        this.menuitemNodes.push(menuitemNodes[i]);
      }

      this.firstMenuitem = this.menuitemNodes[0];
      this.lastMenuitem = this.menuitemNodes[this.menuitemNodes.length-1];
      this.lastMenuitem.classList.add('last');
      this.updateKeyboardShortCuts();
    },

    renderMenuitemToGroup: function (groupNode, mi) {
      var tagNode, tagNodeChild, labelNode, nestingNode;

      var menuitemNode = document.createElement('div');
      menuitemNode.setAttribute('role', 'menuitem');
      menuitemNode.classList.add(mi.class);
      if (this.isNotEmptyString(mi.tagName)) {
        menuitemNode.classList.add('skip-to-' + mi.tagName.toLowerCase());
      }
      menuitemNode.setAttribute('data-id', mi.dataId);
      menuitemNode.tabIndex = -1;
      if (this.isNotEmptyString(mi.ariaLabel)) {
        menuitemNode.setAttribute('aria-label', mi.ariaLabel);
      }

      // add event handlers
      menuitemNode.addEventListener('keydown', this.handleMenuitemKeydown.bind(this));
      menuitemNode.addEventListener('click', this.handleMenuitemClick.bind(this));
      menuitemNode.addEventListener('pointerenter', this.handleMenuitemPointerenter.bind(this));

      groupNode.appendChild(menuitemNode);

      // add heading level and label
      if (mi.class.includes('heading')) {
        if (this.config.enableHeadingLevelShortcuts) {
          tagNode = document.createElement('span');
          tagNodeChild = document.createElement('span');
          tagNodeChild.appendChild(document.createTextNode(mi.level));
          tagNode.append(tagNodeChild);
          tagNode.appendChild(document.createTextNode(')'));
          tagNode.classList.add('level');
          menuitemNode.append(tagNode);
        } else {
          menuitemNode.classList.add('no-level');
        }
        menuitemNode.setAttribute('data-level', mi.level);
        if (this.isNotEmptyString(mi.tagName)) {
          menuitemNode.classList.add('skip-to-' + mi.tagName);
        }
      }

      // add nesting level for landmarks
      if (mi.class.includes('landmark')) {
        menuitemNode.setAttribute('data-nesting', mi.nestingLevel);
        menuitemNode.classList.add('skip-to-nesting-level-' + mi.nestingLevel);

        if (mi.nestingLevel > 0 && (mi.nestingLevel > this.lastNestingLevel)) {
          nestingNode = document.createElement('span');
          nestingNode.classList.add('nesting');
          menuitemNode.append(nestingNode);
        }
        this.lastNestingLevel = mi.nestingLevel;
      }

      labelNode = document.createElement('span');
      labelNode.appendChild(document.createTextNode(mi.name));
      labelNode.classList.add('label');
      menuitemNode.append(labelNode);

      return menuitemNode;
    },

    renderGroupLabel: function (groupLabelId, title, m, n) {
      var titleNode, mofnNode, s;
      var groupLabelNode = document.getElementById(groupLabelId);

      titleNode = groupLabelNode.querySelector('.title');
      mofnNode = groupLabelNode.querySelector('.mofn');

      titleNode.textContent = title;

      if (this.config.enableActions && this.config.enableMofN) {
        if ((typeof m === 'number') && (typeof n === 'number')) {
          s = this.config.mofnGroupLabel;
          s = s.replace('$m', m);
          s = s.replace('$n', n);
          mofnNode.textContent = s;
        }
      }
    },

    renderMenuitemGroup: function(groupId, title) {
      var labelNode, groupNode, spanNode;
      var menuNode = this.menuNode;
      if (this.isNotEmptyString(title)) {
        labelNode = document.createElement('div');
        labelNode.id = groupId + "-label";
        labelNode.setAttribute('role', 'separator');
        menuNode.appendChild(labelNode);

        spanNode = document.createElement('span');
        spanNode.classList.add('title');
        spanNode.textContent = title;
        labelNode.append(spanNode);

        spanNode = document.createElement('span');
        spanNode.classList.add('mofn');
        labelNode.append(spanNode);

        groupNode = document.createElement('div');
        groupNode.setAttribute('role', 'group');
        groupNode.setAttribute('aria-labelledby', labelNode.id);
        groupNode.id = groupId;
        menuNode.appendChild(groupNode);
        menuNode = groupNode;
      }
      return groupNode;
    },

    removeMenuitemGroup: function(groupId) {
      var node = document.getElementById(groupId);
      this.menuNode.removeChild(node);
      node = document.getElementById(groupId + "-label");
      this.menuNode.removeChild(node);
    },

    renderMenuitemsToGroup: function(groupNode, menuitems, msgNoItemsFound) {
      groupNode.innerHTML = '';
      this.lastNestingLevel = 0;

      if (menuitems.length === 0) {
        var item = {};
        item.name = msgNoItemsFound;
        item.tagName = '';
        item.class = 'no-items';
        item.dataId = '';
        this.renderMenuitemToGroup(groupNode, item);
      }
      else {
        for (var i = 0; i < menuitems.length; i += 1) {
          this.renderMenuitemToGroup(groupNode, menuitems[i]);
        }
      }
    },

    getShowMoreHeadingsSelector: function(option) {
      if (option === 'all') {
        return this.showAllHeadingsSelector;
      }
      return this.config.headings;
    },

    getShowMoreHeadingsLabel: function(option, n) {
      var label = this.config.actionShowSelectedHeadingsLabel;
      if (option === 'all') {
        label = this.config.actionShowAllHeadingsLabel;
      }
      return label.replace('$num', n);
    },

    getShowMoreHeadingsAriaLabel: function(option, n) {
      var label = this.config.actionShowSelectedHeadingsAriaLabel;

      if (option === 'all') {
        label = this.config.actionShowAllHeadingsAriaLabel;
      }

      return label.replace('$num', n);
    },

    renderActionMoreHeadings: function(groupNode) {
      var item, menuitemNode;
      var option = 'all';

      var selectedHeadingsLen = this.getHeadings(this.getShowMoreHeadingsSelector('selected')).length;
      var allHeadingsLen = this.getHeadings(this.getShowMoreHeadingsSelector('all')).length;
      var noAction = selectedHeadingsLen === allHeadingsLen;
      var headingsLen = allHeadingsLen;

      if (option !== 'all') {
        headingsLen = selectedHeadingsLen;
      }

      if (!noAction) {
        item = {};
        item.tagName = '';
        item.role = 'menuitem';
        item.class = 'action';
        item.dataId = 'skip-to-more-headings';
        item.name = this.getShowMoreHeadingsLabel(option, headingsLen);
        item.ariaLabel = this.getShowMoreHeadingsAriaLabel(option, headingsLen);

        menuitemNode = this.renderMenuitemToGroup(groupNode, item);
        menuitemNode.setAttribute('data-show-heading-option', option);
        menuitemNode.title = this.config.actionShowHeadingsHelp;
      }
      return noAction;
    },

    updateHeadingGroupMenuitems: function(option) {
      var headings, headingsLen, labelNode, groupNode;

      var selectedHeadings = this.getHeadings(this.getShowMoreHeadingsSelector('selected'));
      var selectedHeadingsLen = selectedHeadings.length;
      var allHeadings = this.getHeadings(this.getShowMoreHeadingsSelector('all'));
      var allHeadingsLen = allHeadings.length;

      // Update list of headings
      if ( option === 'all' ) {
        headings = allHeadings;
      }
      else {
        headings = selectedHeadings;
      }

      this.renderGroupLabel('id-skip-to-group-headings-label', this.config.headingGroupLabel, headings.length, allHeadings.length);

      groupNode = document.getElementById('id-skip-to-group-headings');
      this.renderMenuitemsToGroup(groupNode, headings, this.config.msgNoHeadingsFound);
      this.updateMenuitems();

      // Move focus to first heading menuitem
      if (groupNode.firstElementChild) {
        groupNode.firstElementChild.focus();
      }

      // Update heading action menuitem
      if (option === 'all') {
        option = 'selected';
        headingsLen = selectedHeadingsLen;
      } else {
        option = 'all';
        headingsLen = allHeadingsLen;
      }

      var menuitemNode = this.menuNode.querySelector('[data-id=skip-to-more-headings]');
      menuitemNode.setAttribute('data-show-heading-option', option);
      menuitemNode.setAttribute('aria-label', this.getShowMoreHeadingsAriaLabel(option, headingsLen));

      labelNode = menuitemNode.querySelector('span.label');
      labelNode.textContent = this.getShowMoreHeadingsLabel(option, headingsLen);
    },

    getShowMoreLandmarksSelector: function(option) {
      if (option === 'all') {
        return this.showAllLandmarksSelector;
      }
      return this.config.landmarks;
    },

    getShowMoreLandmarksLabel: function(option, n) {
      var label = this.config.actionShowSelectedLandmarksLabel;

      if (option === 'all') {
        label = this.config.actionShowAllLandmarksLabel;
      }
      return label.replace('$num', n);
    },

    getShowMoreLandmarksAriaLabel: function(option, n) {
      var label = this.config.actionShowSelectedLandmarksAriaLabel;

      if (option === 'all') {
        label = this.config.actionShowAllLandmarksAriaLabel;
      }

      return label.replace('$num', n);
    },

    renderActionMoreLandmarks: function(groupNode) {
      var item, menuitemNode;
      var option = 'all';

      var selectedLandmarksLen = this.getLandmarks(this.getShowMoreLandmarksSelector('selected')).length;
      var allLandmarksLen = this.getLandmarks(this.getShowMoreLandmarksSelector('all')).length;
      var noAction = selectedLandmarksLen === allLandmarksLen;
      var landmarksLen = allLandmarksLen;

      if (option !== 'all') {
        landmarksLen = selectedLandmarksLen;
      }

      if (!noAction) {
        item = {};
        item.tagName = '';
        item.role = 'menuitem';
        item.class = 'action';
        item.dataId = 'skip-to-more-landmarks';
        item.name = this.getShowMoreLandmarksLabel(option, landmarksLen);
        item.ariaLabel =  this.getShowMoreLandmarksAriaLabel(option, landmarksLen);

        menuitemNode = this.renderMenuitemToGroup(groupNode, item);

        menuitemNode.setAttribute('data-show-landmark-option', option);
        menuitemNode.title = this.config.actionShowLandmarksHelp;
      }
      return noAction;
    },

    updateLandmarksGroupMenuitems: function(option) {
      var landmarks, landmarksLen, labelNode, groupNode;
      var selectedLandmarks = this.getLandmarks(this.getShowMoreLandmarksSelector('selected'));
      var selectedLandmarksLen = selectedLandmarks.length;
      var allLandmarks = this.getLandmarks(this.getShowMoreLandmarksSelector('all'), true);
      var allLandmarksLen = allLandmarks.length;

      // Update landmark menu items
      if ( option === 'all' ) {
        landmarks = allLandmarks;
      }
      else {
        landmarks = selectedLandmarks;
      }

      this.renderGroupLabel('id-skip-to-group-landmarks-label', this.config.landmarkGroupLabel, landmarks.length, allLandmarks.length);

      groupNode = document.getElementById('id-skip-to-group-landmarks');
      this.renderMenuitemsToGroup(groupNode, landmarks, this.config.msgNoLandmarksFound);
      this.updateMenuitems();

      // Move focus to first landmark menuitem
      if (groupNode.firstElementChild) {
        groupNode.firstElementChild.focus();
      }

      // Update landmark action menuitem
      if (option === 'all') {
        option = 'selected';
        landmarksLen = selectedLandmarksLen;
      } else {
        option = 'all';
        landmarksLen = allLandmarksLen;
      }

      var menuitemNode = this.menuNode.querySelector('[data-id=skip-to-more-landmarks]');
      menuitemNode.setAttribute('data-show-landmark-option', option);
      menuitemNode.setAttribute('aria-label', this.getShowMoreLandmarksAriaLabel(option, landmarksLen));

      labelNode = menuitemNode.querySelector('span.label');
      labelNode.textContent = this.getShowMoreLandmarksLabel(option, landmarksLen);
    },

    renderMenu: function() {
      var groupNode,
      selectedLandmarks,
      allLandmarks,
      landmarkElements,
      selectedHeadings,
      allHeadings,
      headingElements,
      selector,
      option,
      hasNoAction1,
      hasNoAction2;
      // remove current menu items from menu
      while (this.menuNode.lastElementChild) {
        this.menuNode.removeChild(this.menuNode.lastElementChild);
      }

      option = 'selected';
      // Create landmarks group
      selector = this.getShowMoreLandmarksSelector('all');
      allLandmarks = this.getLandmarks(selector, true);
      selector = this.getShowMoreLandmarksSelector('selected');
      selectedLandmarks = this.getLandmarks(selector);
      landmarkElements = selectedLandmarks;

      if (option === 'all') {
        landmarkElements = allLandmarks;
      }

      groupNode = this.renderMenuitemGroup('id-skip-to-group-landmarks', this.config.landmarkGroupLabel);
      this.renderMenuitemsToGroup(groupNode, landmarkElements, this.config.msgNoLandmarksFound);
      this.renderGroupLabel('id-skip-to-group-landmarks-label', this.config.landmarkGroupLabel, landmarkElements.length, allLandmarks.length);

      // Create headings group
      selector = this.getShowMoreHeadingsSelector('all');
      allHeadings = this.getHeadings(selector);
      selector = this.getShowMoreHeadingsSelector('selected');
      selectedHeadings = this.getHeadings(selector);
      headingElements = selectedHeadings;

      if (option === 'all') {
        headingElements = allHeadings;
      }

      groupNode = this.renderMenuitemGroup('id-skip-to-group-headings', this.config.headingGroupLabel);
      this.renderMenuitemsToGroup(groupNode, headingElements, this.config.msgNoHeadingsFound);
      this.renderGroupLabel('id-skip-to-group-headings-label', this.config.headingGroupLabel, headingElements.length, allHeadings.length);

      // Create actions, if enabled
      if (this.config.enableActions) {
        groupNode = this.renderMenuitemGroup('id-skip-to-group-actions', this.config.actionGroupLabel);
        hasNoAction1 = this.renderActionMoreLandmarks(groupNode);
        hasNoAction2 = this.renderActionMoreHeadings(groupNode);
        // Remove action label if no actions are available
        if (hasNoAction1 && hasNoAction2) {
          this.removeMenuitemGroup('id-skip-to-group-actions');
        }
      }

      // Update list of menuitems
      this.updateMenuitems();
    },

    //
    // Menu scripting event functions and utilities
    //

    setFocusToMenuitem: function(menuitem) {
      if (menuitem) {
        menuitem.focus();
      }
    },

    setFocusToFirstMenuitem: function() {
      this.setFocusToMenuitem(this.firstMenuitem);
    },

    setFocusToLastMenuitem: function() {
      this.setFocusToMenuitem(this.lastMenuitem);
    },

    setFocusToPreviousMenuitem: function(menuitem) {
      var newMenuitem, index;
      if (menuitem === this.firstMenuitem) {
        newMenuitem = this.lastMenuitem;
      } else {
        index = this.menuitemNodes.indexOf(menuitem);
        newMenuitem = this.menuitemNodes[index - 1];
      }
      this.setFocusToMenuitem(newMenuitem);
      return newMenuitem;
    },

    setFocusToNextMenuitem: function(menuitem) {
      var newMenuitem, index;
      if (menuitem === this.lastMenuitem) {
        newMenuitem = this.firstMenuitem;
      } else {
        index = this.menuitemNodes.indexOf(menuitem);
        newMenuitem = this.menuitemNodes[index + 1];
      }
      this.setFocusToMenuitem(newMenuitem);
      return newMenuitem;
    },

    setFocusByFirstCharacter: function(menuitem, char) {
      var start, index;
      if (char.length > 1) {
        return;
      }
      char = char.toLowerCase();

      // Get start index for search based on position of currentItem
      start = this.menuitemNodes.indexOf(menuitem) + 1;
      if (start >= this.menuitemNodes.length) {
        start = 0;
      }

      // Check remaining items in the menu
      index = this.firstChars.indexOf(char, start);

      // If not found in remaining items, check headings
      if (index === -1) {
        index = this.headingLevels.indexOf(char, start);
      }

      // If not found in remaining items, check from beginning
      if (index === -1) {
        index = this.firstChars.indexOf(char, 0);
      }

      // If not found in remaining items, check headings from beginning
      if (index === -1) {
        index = this.headingLevels.indexOf(char, 0);
      }

      // If match was found...
      if (index > -1) {
        this.setFocusToMenuitem(this.menuitemNodes[index]);
      }
    },

    // Utilities
    getIndexFirstChars: function(startIndex, char) {
      for (var i = startIndex; i < this.firstChars.length; i += 1) {
        if (char === this.firstChars[i]) {
          return i;
        }
      }
      return -1;
    },
    // Popup menu methods
    openPopup: function() {
      this.menuNode.setAttribute('aria-busy', 'true');
      this.renderMenu();
      this.menuNode.style.display = 'block';
      this.menuNode.removeAttribute('aria-busy');
      this.buttonNode.setAttribute('aria-expanded', 'true');
    },

    closePopup: function() {
      if (this.isOpen()) {
        this.buttonNode.setAttribute('aria-expanded', 'false');
        this.menuNode.style.display = 'none';
      }
    },
    isOpen: function() {
      return this.buttonNode.getAttribute('aria-expanded') === 'true';
    },
    // Menu event handlers
    handleFocusin: function() {
      this.domNode.classList.add('focus');
    },
    handleFocusout: function() {
      this.domNode.classList.remove('focus');
    },
    handleButtonKeydown: function(event) {
      var key = event.key,
        flag = false;
      switch (key) {
        case ' ':
        case 'Enter':
        case 'ArrowDown':
        case 'Down':
          this.openPopup();
          this.setFocusToFirstMenuitem();
          flag = true;
          break;
        case 'Esc':
        case 'Escape':
          this.closePopup();
          this.buttonNode.focus();
          this.hideTooltip();
          flag = true;
          break;
        case 'Up':
        case 'ArrowUp':
          this.openPopup();
          this.setFocusToLastMenuitem();
          flag = true;
          break;
        default:
          break;
      }
      if (flag) {
        event.stopPropagation();
        event.preventDefault();
      }
    },
    handleButtonClick: function(event) {
      if (this.isOpen()) {
        this.closePopup();
        this.buttonNode.focus();
      } else {
        this.openPopup();
        this.setFocusToFirstMenuitem();
      }
      event.stopPropagation();
      event.preventDefault();
    },
    isTooltipHidden: function() {
      return this.tooltipNode.className.indexOf('skip-to-show-tooltip') < 0;
    },
    displayTooltip: function() {
      if (this.showTooltipFocus || this.showTooltipHover) {
        this.tooltipNode.classList.add('skip-to-show-tooltip');
      }
    },
    showTooltip: function() {
      this.showTooltipFocus = true;
      if (this.config.enableTooltip && this.isTooltipHidden()) {
        this.tooltipNode.style.left = this.tooltipLeft + 'px';
        this.tooltipNode.style.top = this.tooltipTop + 'px';
        setTimeout(this.displayTooltip.bind(this), this.tooltipTimerDelay);
      }
    },
    hideTooltip: function() {
      this.showTooltipFocus = false;
      if(this.config.enableTooltip) {
        this.tooltipNode.classList.remove('skip-to-show-tooltip');
      }
    },
    handleButtonFocus: function() {
      this.showTooltip();
    },
    handleButtonBlur: function() {
      this.hideTooltip();
    },
    handleButtonPointerenter: function(event) {
      this.showTooltipHover = true;
      if (this.config.enableTooltip && this.isTooltipHidden()) {
        var rect = this.buttonNode.getBoundingClientRect();
        var left = Math.min(this.tooltipLeft, event.pageX - rect.left + this.tooltipHeight);
        this.tooltipNode.style.left = left + 'px';
        var top = event.pageY - rect.top;
        this.tooltipNode.style.top = top + 'px';
        setTimeout(this.showTooltip. bind(this), this.tooltipTimerDelay);
      }
    },
    handleButtonPointerout: function() {
      this.showTooltipHover = false;
      if(this.config.enableTooltip) {
        this.tooltipNode.classList.remove('skip-to-show-tooltip');
      }
    },
    skipToElement: function(menuitem) {

      var isVisible = this.isVisible;
      var focusNode = false;
      var scrollNode = false;
      var elem;

      function findVisibleElement(e, selectors) {
        if (e) {
          for (var j = 0; j < selectors.length; j += 1) {
            var elems = e.querySelectorAll(selectors[j]);
            for(var i = 0; i < elems.length; i +=1) {
              if (isVisible(elems[i])) {
                return elems[i];
              }
            }
          }
        }
        return e;
      }

      var searchSelectors = ['input', 'button', 'input[type=button]', 'input[type=submit]', 'a'];
      var navigationSelectors = ['a', 'input', 'button', 'input[type=button]', 'input[type=submit]'];
      var landmarkSelectors = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'section', 'article', 'p', 'li', 'a'];

      var isLandmark = menuitem.classList.contains('landmark');
      var isSearch = menuitem.classList.contains('skip-to-search');
      var isNav = menuitem.classList.contains('skip-to-nav');

      elem = document.querySelector('[data-skip-to-id="' + menuitem.getAttribute('data-id') + '"]');

      if (elem) {
        if (isSearch) {
          focusNode = findVisibleElement(elem, searchSelectors);
        }
        if (isNav) {
          focusNode = findVisibleElement(elem, navigationSelectors);
        }
        if (focusNode && this.isVisible(focusNode)) {
          focusNode.focus();
          focusNode.scrollIntoView({block: 'nearest'});
        }
        else {
          if (isLandmark) {
            scrollNode = findVisibleElement(elem, landmarkSelectors);
            if (scrollNode) {
              elem = scrollNode;
            }
          }
          elem.tabIndex = -1;
          elem.focus();
          elem.scrollIntoView({block: 'center'});
        }
      }
    },
    handleMenuitemAction: function(tgt) {
      var option;
      switch (tgt.getAttribute('data-id')) {
        case '':
          // this means there were no headings or landmarks in the list
          break;

        case 'skip-to-more-headings':
          option = tgt.getAttribute('data-show-heading-option');
          this.updateHeadingGroupMenuitems(option);
          break;

        case 'skip-to-more-landmarks':
          option = tgt.getAttribute('data-show-landmark-option');
          this.updateLandmarksGroupMenuitems(option);
          break;

        default:
          this.closePopup();
          this.skipToElement(tgt);
          break;
      }
    },
    handleMenuitemKeydown: function(event) {
      var tgt = event.currentTarget,
        key = event.key,
        flag = false;

      function isPrintableCharacter(str) {
        return str.length === 1 && str.match(/\S/);
      }
      if (event.ctrlKey || event.altKey || event.metaKey) {
        return;
      }
      if (event.shiftKey) {
        if (isPrintableCharacter(key)) {
          this.setFocusByFirstCharacter(tgt, key);
          flag = true;
        }
        if (event.key === 'Tab') {
          this.buttonNode.focus();
          this.closePopup();
          flag = true;
        }
      } else {
        switch (key) {
          case 'Enter':
          case ' ':
            this.handleMenuitemAction(tgt);
            flag = true;
            break;
          case 'Esc':
          case 'Escape':
            this.closePopup();
            this.buttonNode.focus();
            flag = true;
            break;
          case 'Up':
          case 'ArrowUp':
            this.setFocusToPreviousMenuitem(tgt);
            flag = true;
            break;
          case 'ArrowDown':
          case 'Down':
            this.setFocusToNextMenuitem(tgt);
            flag = true;
            break;
          case 'Home':
          case 'PageUp':
            this.setFocusToFirstMenuitem();
            flag = true;
            break;
          case 'End':
          case 'PageDown':
            this.setFocusToLastMenuitem();
            flag = true;
            break;
          case 'Tab':
            this.closePopup();
            break;
          default:
            if (isPrintableCharacter(key)) {
              this.setFocusByFirstCharacter(tgt, key);
              flag = true;
            }
            break;
        }
      }
      if (flag) {
        event.stopPropagation();
        event.preventDefault();
      }
    },
    handleMenuitemClick: function(event) {
      this.handleMenuitemAction(event.currentTarget);
      event.stopPropagation();
      event.preventDefault();
    },
    handleMenuitemPointerenter: function(event) {
      var tgt = event.currentTarget;
      tgt.focus();
    },
    handleBackgroundPointerdown: function(event) {
      if (!this.domNode.contains(event.target)) {
        if (this.isOpen()) {
          this.closePopup();
          this.buttonNode.focus();
        }
      }
    },
    // methods to extract landmarks, headings and ids
    normalizeName: function(name) {
      if (typeof name === 'string') return name.replace(/\w\S*/g, function(txt) {
        return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
      });
      return "";
    },
    getTextContent: function(elem) {
      function getText(e, strings) {
        // If text node get the text and return
        if (e.nodeType === Node.TEXT_NODE) {
          strings.push(e.data);
        } else {
          // if an element for through all the children elements looking for text
          if (e.nodeType === Node.ELEMENT_NODE) {
            // check to see if IMG or AREA element and to use ALT content if defined
            var tagName = e.tagName.toLowerCase();
            if ((tagName === 'img') || (tagName === 'area')) {
              if (e.alt) {
                strings.push(e.alt);
              }
            } else {
              var c = e.firstChild;
              while (c) {
                getText(c, strings);
                c = c.nextSibling;
              } // end loop
            }
          }
        }
      } // end function getStrings
      // Create return object
      var str = "Test",
        strings = [];
      getText(elem, strings);
      if (strings.length) str = strings.join(" ");
      return str;
    },
    getAccessibleName: function(elem) {
      var labelledbyIds = elem.getAttribute('aria-labelledby'),
        label = elem.getAttribute('aria-label'),
        title = elem.getAttribute('title'),
        name = "";
      if (labelledbyIds && labelledbyIds.length) {
        var str,
          strings = [],
          ids = labelledbyIds.split(' ');
        if (!ids.length) ids = [labelledbyIds];
        for (var i = 0, l = ids.length; i < l; i += 1) {
          var e = document.getElementById(ids[i]);
          if (e) str = this.getTextContent(e);
          if (str && str.length) strings.push(str);
        }
        name = strings.join(" ");
      } else {
        if (this.isNotEmptyString(label)) {
          name = label;
        } else {
          if (this.isNotEmptyString(title)) {
            name = title;
          }
        }
      }
      return name;
    },
    isVisible: function(element) {
      function isVisibleRec(el) {
        if (el.nodeType === 9) return true; /*IE8 does not support Node.DOCUMENT_NODE*/
        var computedStyle = window.getComputedStyle(el);
        var display = computedStyle.getPropertyValue('display');
        var visibility = computedStyle.getPropertyValue('visibility');
        var hidden = el.getAttribute('hidden');
        if ((display === 'none') ||
          (visibility === 'hidden') ||
          (hidden !== null)) {
          return false;
        }
        return isVisibleRec(el.parentNode);
      }
      return isVisibleRec(element);
    },
    getHeadings: function(targets) {
      var dataId, level;
      if (typeof targets !== 'string') {
        targets = this.config.headings;
      }
      var headingElementsArr = [];
      if (typeof targets !== 'string' || targets.length === 0) return;
      var headings = document.querySelectorAll(targets);
      for (var i = 0, len = headings.length; i < len; i += 1) {
        var heading = headings[i];
        var role = heading.getAttribute('role');
        if ((typeof role === 'string') && (role === 'presentation')) continue;
        if (this.isVisible(heading)) {
          if (heading.hasAttribute('data-skip-to-id')) {
            dataId = heading.getAttribute('data-skip-to-id');
          } else {
            heading.setAttribute('data-skip-to-id', this.skipToIdIndex);
            dataId = this.skipToIdIndex;
          }
          level = heading.tagName.substring(1);
          var headingItem = {};
          headingItem.dataId = dataId.toString();
          headingItem.class = 'heading';
          headingItem.name = this.getTextContent(heading);
          headingItem.ariaLabel = headingItem.name + ', ';
          headingItem.ariaLabel += this.config.headingLevelLabel + ' ' + level;
          headingItem.tagName = heading.tagName.toLowerCase();
          headingItem.role = 'heading';
          headingItem.level = level;
          headingElementsArr.push(headingItem);
          this.skipToIdIndex += 1;
        }
      }
      return headingElementsArr;
    },
    getLocalizedLandmarkName: function(tagName, name) {
      var n;
      switch (tagName) {
        case 'aside':
          n = this.config.asideLabel;
          break;
        case 'footer':
          n = this.config.footerLabel;
          break;
        case 'form':
          n = this.config.formLabel;
          break;
        case 'header':
          n = this.config.headerLabel;
          break;
        case 'main':
          n = this.config.mainLabel;
          break;
        case 'nav':
          n = this.config.navLabel;
          break;
        case 'section':
        case 'region':
          n = this.config.regionLabel;
          break;
        case 'search':
          n = this.config.searchLabel;
          break;
          // When an ID is used as a selector, assume for main content
        default:
          n = tagName;
          break;
      }
      if (this.isNotEmptyString(name)) {
        n += ': ' + name;
      }
      return n;
    },
    getNestingLevel: function(landmark, landmarks) {
      var nestingLevel = 0;
      var parentNode = landmark.parentNode;
      while (parentNode) {
        for (var i = 0; i < landmarks.length; i += 1) {
          if (landmarks[i] === parentNode) {
            nestingLevel += 1;
            // no more than 3 levels of nesting supported
            if (nestingLevel === 3) {
              return 3;
            }
            continue;
          }
        }
        parentNode = parentNode.parentNode;
      }
      return nestingLevel;
    },
    getLandmarks: function(targets, allFlag) {
      if (typeof allFlag !== 'boolean') {
        allFlag = false;
      }
      if (typeof targets !== 'string') {
        targets = this.config.landmarks;
      }
      var landmarks = document.querySelectorAll(targets);
      var mainElements = [];
      var searchElements = [];
      var navElements = [];
      var asideElements = [];
      var footerElements = [];
      var regionElements = [];
      var otherElements = [];
      var allLandmarks = [];
      var dataId = '';
      for (var i = 0, len = landmarks.length; i < len; i += 1) {
        var landmark = landmarks[i];
        // if skipto is a landmark don't include it in the list
        if (landmark === this.domNode) {
          continue;
        }
        var role = landmark.getAttribute('role');
        var tagName = landmark.tagName.toLowerCase();
        if ((typeof role === 'string') && (role === 'presentation')) continue;
        if (this.isVisible(landmark)) {
          if (!role) role = tagName;
          var name = this.getAccessibleName(landmark);
          if (typeof name !== 'string') {
            name = '';
          }
          // normalize tagNames
          switch (role) {
            case 'banner':
              tagName = 'header';
              break;
            case 'complementary':
              tagName = 'aside';
              break;
            case 'contentinfo':
              tagName = 'footer';
              break;
            case 'form':
              tagName = 'form';
              break;
            case 'main':
              tagName = 'main';
              break;
            case 'navigation':
              tagName = 'nav';
              break;
            case 'region':
              tagName = 'section';
              break;
            case 'search':
              tagName = 'search';
              break;
            default:
              break;
          }
          // if using ID for selectQuery give tagName as main
          if (['aside', 'footer', 'form', 'header', 'main', 'nav', 'section', 'search'].indexOf(tagName) < 0) {
            tagName = 'main';
          }
          if (landmark.hasAttribute('aria-roledescription')) {
            tagName = landmark.getAttribute('aria-roledescription').trim().replace(' ', '-');
          }
          if (landmark.hasAttribute('data-skip-to-id')) {
            dataId = landmark.getAttribute('data-skip-to-id');
          } else {
            landmark.setAttribute('data-skip-to-id', this.skipToIdIndex);
            dataId =  this.skipToIdIndex;
          }
          var landmarkItem = {};
          landmarkItem.dataId = dataId.toString();
          landmarkItem.class = 'landmark';
          landmarkItem.hasName = name.length > 0;
          landmarkItem.name = this.getLocalizedLandmarkName(tagName, name);
          landmarkItem.tagName = tagName;
          landmarkItem.nestingLevel = 0;
          if (allFlag) {
            landmarkItem.nestingLevel = this.getNestingLevel(landmark, landmarks);
          }
          this.skipToIdIndex += 1;
          allLandmarks.push(landmarkItem);

          // For sorting landmarks into groups
          switch (tagName) {
            case 'main':
              mainElements.push(landmarkItem);
              break;
            case 'search':
              searchElements.push(landmarkItem);
              break;
            case 'nav':
              navElements.push(landmarkItem);
              break;
            case 'aside':
              asideElements.push(landmarkItem);
              break;
            case 'footer':
              footerElements.push(landmarkItem);
              break;
            case 'section':
              // Regions must have accessible name to be included
              if (landmarkItem.hasName) {
                regionElements.push(landmarkItem);
              }
              break;
            default:
              otherElements.push(landmarkItem);
              break;
          }
        }
      }
      if (allFlag) {
        return allLandmarks;
      }
      return [].concat(mainElements, searchElements, navElements, asideElements, regionElements, footerElements, otherElements);
    }
  };
  // Initialize skipto menu button with onload event
  window.addEventListener('load', function() {
    SkipTo.init(window.SkipToConfig ||
                ((typeof window.Joomla === 'object' && typeof window.Joomla.getOptions === 'function') ? window.Joomla.getOptions('skipto-settings', {}) : {})
                );
  });
})();
/*@end @*/
