/* global gtag */
// These are the core menu options

// Language, Year and ToC select switcher (mobile)
function handleSelectSwitchers() {
  let languageYearSwitchers = document.querySelectorAll('.language-switcher select, .year-switcher select, .table-of-contents-switcher select');
  for (let i = 0; i < languageYearSwitchers.length; i++) {
    languageYearSwitchers[i].addEventListener('change', function (e) {

      let selectedOption = this.options[this.selectedIndex];

      if (e.target.dataset.label && selectedOption.dataset.event) {
        gtag('event', selectedOption.dataset.event, {
          'event_category': 'clicks',
          'event_label': e.target.dataset.label,
          'transport_type': 'beacon',
          'value': 1
        })
      }

      //Reset the selector back in case user uses Back button
      let selectedValue = e.target.value;
      if (selectedValue && selectedValue !== window.location.pathname) {
        e.target.value = window.location.pathname;
        window.location = selectedValue;
      }
    });
  }
}

// Search, Language, Year and ToC menus (desktop)
function handleNavMenu() {

  function closeAnyOtherOpenDropdown(e) {

    // If the click was in a menu that's already open then ignore as just been opened.
    if (e.target.classList.contains('dropdown-open')) {
      return
    }

    // If the click was in search nav, then ignore as don't want to close search menu.
    let searchNavs = document.querySelectorAll('.search-nav ul:not(hidden)');
    for (let i = 0; i < searchNavs.length; i++) {
      if (searchNavs[i].contains(e.target)) {
        return
      }
    }

    // Else a click elsewhere so close all the menus
    let openDropdownBtn = document.querySelector('.nav-dropdown-btn.dropdown-open');
    openDropdownBtn && openDropdownBtn.click();
  }

  function trapFocusInList(e) {
    let list = e.currentTarget;
    let isInFooter = list.classList.contains('footer-list');
    if (e.key === "ArrowDown") {
      let siblingElem = isInFooter ? e.target.parentElement.previousElementSibling : e.target.parentElement.nextElementSibling;
      let focusableElem = siblingElem ? siblingElem.querySelector('a') : (isInFooter ? lastFocusableElementInList : firstFocusableElementInList);
      e.preventDefault();
      focusableElem.focus();
    } else if (e.key === "ArrowUp") {
      let siblingElem = isInFooter ? e.target.parentElement.nextElementSibling : e.target.parentElement.previousElementSibling;
      let focusableElem = siblingElem ? siblingElem.querySelector('a') : (isInFooter ? firstFocusableElementInList : lastFocusableElementInList);
      e.preventDefault();
      focusableElem.focus();
    } else if (e.key === "Escape") {
      let navDropDown = e.currentTarget.closest('.nav-dropdown');
      let navDropDownBtn = navDropDown.querySelector('.nav-dropdown-btn');
      navDropDownBtn.click();
      navDropDownBtn.focus();
    }
  }

  let firstFocusableElementInList, lastFocusableElementInList;
  function toggleDropdownVisibility(e) {
    let dropdownBtn = e.currentTarget;
    let dropdown = dropdownBtn.closest('.nav-dropdown');
    let list = dropdown.querySelector('.nav-dropdown-list');
    let isListVisible = !list.classList.toggle('hidden');
    let dropdownOpen = dropdownBtn.classList.toggle('dropdown-open');
    dropdownBtn.setAttribute('aria-expanded', dropdownOpen);

    if (isListVisible) {
      let btnBoundingRect = dropdownBtn.getBoundingClientRect();
      let listBoundingRect = list.getBoundingClientRect();
      if (listBoundingRect.width <= btnBoundingRect.width) {
        list.classList.add("align-center");
      } else if (btnBoundingRect.left + listBoundingRect.width > window.innerWidth) {
        list.classList.add("align-right");
      }
      document.body.addEventListener('click', closeAnyOtherOpenDropdown, true);
      let navItems = list.querySelectorAll('a');
      firstFocusableElementInList = navItems[0];
      lastFocusableElementInList = navItems[navItems.length - 1];
      list.addEventListener('keydown', trapFocusInList);

      if (e.currentTarget.classList.contains('search-button')) {
        e.currentTarget.parentNode.querySelector('input').focus();
      }

    } else {
      list.removeEventListener('keydown', trapFocusInList);
      document.body.removeEventListener('click', closeAnyOtherOpenDropdown, true);
    }
  }

  // Might need to change menu hanging direction on window resize to avoid overflow
  function checkNavDropdown() {
    let list = window.document.querySelector('.dropdown-open ~ .nav-dropdown-list');
    let dropdownBtn = window.document.querySelector('button.dropdown-open');
    // If no open menu, then we're done
    if (!list || !dropdownBtn) {
      return;
    }
    let btnBoundingRect = dropdownBtn.getBoundingClientRect();
    let listBoundingRect = list.getBoundingClientRect();
    if (listBoundingRect.width <= btnBoundingRect.width) {
      list.classList.remove("align-right");
      list.classList.add("align-center");
    } else if (btnBoundingRect.left + listBoundingRect.width > window.innerWidth) {
      list.classList.remove("align-center");
      list.classList.add("align-right");
    } else {
      list.classList.remove("align-center");
      list.classList.remove("align-right");
    }
  }
  window.onresize = checkNavDropdown;

  function navBtnKeyDownHandler(e) {
    let dropdownList = e.currentTarget.nextElementSibling;
    let isDropdownOpen = e.currentTarget.classList.contains('dropdown-open');
    let isInFooter = dropdownList.classList.contains('footer-list');
    if (e.key === "Escape") {
      e.currentTarget.click();
    } else if (e.key === "ArrowDown") {
      e.preventDefault();
      !isDropdownOpen && e.currentTarget.click();
      (isInFooter ? dropdownList.lastElementChild : dropdownList.firstElementChild).querySelector('a').focus();
    } else if (isInFooter && e.key === "ArrowUp") {
      e.preventDefault();
      !isDropdownOpen && e.currentTarget.click();
      dropdownList.firstElementChild.querySelector('a').focus();
    }
  }

  let navDropdownButtons = document.querySelectorAll('.nav-dropdown-btn');
  for (let i = 0; i < navDropdownButtons.length; i++) {
    navDropdownButtons[i].addEventListener('click', toggleDropdownVisibility);
    navDropdownButtons[i].addEventListener('keydown', navBtnKeyDownHandler);
  }
}

// The main mobile hamburger menu
function handleMobileMenu() {
  let menuBtn = document.querySelector('.menu-btn');
  let menuNav = document.querySelector('#menu');
  let main = document.querySelector('main');
  let footer = document.querySelector('footer');

  function toggleNavMenu() {
    let menuOpen = document.body.classList.toggle('menu-open');
    menuBtn.classList.toggle("menu-btn-active");
    menuBtn.setAttribute('aria-expanded', menuOpen);
    let ariaLabel = menuOpen ? menuBtn.getAttribute('data-close-text') : menuBtn.getAttribute('data-open-text');
    menuBtn.setAttribute('aria-label', ariaLabel);

    // Toogle inert to keep focus in the menu and header
    main.toggleAttribute('inert');
    footer.toggleAttribute('inert');

    /* When you open the menu, add an event listener to close it when clicking outside the menu area */
    /* Remove it on closing the menu */
    if (menuBtn.getAttribute('aria-expanded') === 'true') {
      document.body.addEventListener('click', toggleNavMenu, false);
    } else {
      document.body.removeEventListener('click', toggleNavMenu, false);
    }
  }

  menuBtn.addEventListener('click', function (event) {
    toggleNavMenu();
    event.stopPropagation();
  });

  /* Add a click listener to menu so when it's open it swallows click to avoid above click closing it */
  menuNav.addEventListener('click', function (event) {
    event.stopPropagation();
  });

  menuNav.addEventListener('keydown', function (event) {
    if (event.key === 'Escape') {
      if (menuBtn.getAttribute('aria-expanded') === 'true') {
        toggleNavMenu();
        menuBtn.focus();
      }
    }
  });
}

// We can add click events to elements (e.g. ebook) and get event label from data-event attribute
function handleDataEvents() {
  document.querySelectorAll('[data-event]').forEach(trackableElement => {
    trackableElement.addEventListener('click', function (event) {
      gtag('event', event.target.dataset.event, {
        'event_category': 'clicks',
        'event_label': event.target.dataset.label,
        'transport_type': 'beacon',
        'value': 1
      })
    });
  });
}

// Now we're all set up, activate all the elements that depend on JavaScript.
// We do this in inline JS in the page after header loads to get those items displaying
// correctly and avoid the initial CLS (even before above runs which is a bit cheeky to be honest!)
// but need to do it again as other JS-elements will now have loaded further down the page
function activateJavaScriptElements() {
  document.querySelectorAll('.js-hide').forEach(element => {
    // Don't just hide it - delete it completely to avoid any specifity issues
    element.parentNode.removeChild(element);
  });
  document.querySelectorAll('.js-enable').forEach(element => {
    element.classList.remove('js-enable');
    element.classList.remove('hidden');
    element.disabled = false;
    element.hidden = false;
  });
}

//This function removes the lazy-loading attributes from all img and iframe tags
//Useful for print view for example (https://bugs.chromium.org/p/chromium/issues/detail?id=875403)
function removeLazyLoading() {

  //If no Array.from then pretty sure there will be no native lazy-loading support to remove!
  if (Array.from) {
    console.log("Removing lazy loading...");

    Array.from(document.querySelectorAll('img[loading], iframe[loading]')).forEach(function (element) {
      element.removeAttribute('loading');
    });
  }
}

//Add an event handler to remove LazyLoading when entering print mode
function removeLazyLoadingOnPrint() {
  if ("onbeforeprint" in window) {
    window.onbeforeprint = removeLazyLoading;
  }

}

//Check if in print mode so we can remove lazy loading and block interactive visuals
function isInPrintMode() {
  let printMode = false;

  if (window.URL && window.URLSearchParams) {
    let url = new URL(window.location);
    printMode = url.searchParams.has('print');
  }
  if (printMode) {
    console.log("Print Mode");
    removeLazyLoading();
  }
  gtag('event', 'print-mode', { 'event_category': 'user', 'event_label': '' + printMode, 'value': +printMode })
  return printMode;

}

//Check if the screen meets minimum size requirements for Interactive figures
//At the moment we base it on 600px break point matching CSS but it does not need to be the same
function bigEnoughForInteractiveFigures() {
  if (!(window.matchMedia('(max-width: 600px)').matches)) {
    gtag('event', 'min-sheets-width', { 'event_category': 'user', 'event_label': 'true', 'value': 1 });
    return true;
  }
  gtag('event', 'min-sheets-width', { 'event_category': 'user', 'event_label': 'false', 'value': 0 });
  console.log('Screen too small for interactive visuals');
  return false;
}

//Data Save can be set to on, so let's check it
function dataSaverEnabled() {
  let dataSaver = false;
  if ('connection' in navigator) {
    dataSaver = navigator.connection.saveData;
    if (dataSaver) {
      console.log('DataSaver is enabled');
      gtag('event', 'data-saver', { 'event_category': 'user', 'event_label': 'enabled', 'value': 1 });
    } else {
      gtag('event', 'data-saver', { 'event_category': 'user', 'event_label': 'not-enabled', 'value': 0 });
    }
  } else {
    gtag('event', 'data-saver', { 'event_category': 'user', 'event_label': 'not-reported', 'value': 0 });
  }

  return dataSaver;
}

//Check if network API states this is a high bandwidth connection
//Assume it is for those browsers who do not support this (e.g. Safari and IE)
function highBandwidthConnection() {
  let highBandwidth = true;
  if ('connection' in navigator) {
    const effectiveType = navigator.connection.effectiveType;
    if (effectiveType == 'slow-2g' || effectiveType == '2g' || effectiveType == '3g') {
      highBandwidth = false;
      console.log('effectiveType ' + effectiveType + ' is low BandWidth');
      gtag('event', 'connection-type', { 'event_category': 'user', 'event_label': effectiveType, 'value': 0 });
    } else {
      gtag('event', 'connection-type', { 'event_category': 'user', 'event_label': effectiveType, 'value': 1 });
    }
  } else {
    gtag('event', 'connection-type', { 'event_category': 'user', 'event_label': 'not-reported', 'value': 1 });
  }

  return highBandwidth;
}

//iOS causes Google Sheets to create a 6000 by 3700 canvas, which annoyingly isn't supported by iOS!
//Let's check if we have large Canvas support (annoyingly no API for this!)
function highResolutionCanvasSupported() {

  let largeCanvasSupported = false;

  try {
    // Set large sized canvas dimensions and draw test rectangle
    let cvs = document ? document.createElement('canvas') : null;
    let ctx = cvs && cvs.getContext ? cvs.getContext('2d') : null;
    let scale = window.devicePixelRatio || 1;
    if (scale > 1) {
      cvs.width = 6000;
      cvs.height = 3700;
      ctx.fillRect(5999, 3699, 1, 1);
      largeCanvasSupported = Boolean(ctx.getImageData(5999, 3699, 1, 1).data[3]) == 1;
    } else {
      cvs.width = 1200;
      cvs.height = 742;
      ctx.fillRect(1199, 741, 1, 1);
      largeCanvasSupported = Boolean(ctx.getImageData(1199, 741, 1, 1).data[3]) == 1;
    }
  }
  catch (e) {
    largeCanvasSupported = false;
  }

  if (largeCanvasSupported) {
    gtag('event', 'hi-res-canvas', { 'event_category': 'user', 'event_label': 'supported', 'value': 1 });
  } else {
    console.log('High resolution canvas images are not supported');
    gtag('event', 'hi-res-canvas', { 'event_category': 'user', 'event_label': 'not-supported', 'value': 0 });
  }

  return largeCanvasSupported;

}

//If google sheets test pixel works, then can assume interactive sheets work and can remove it
function googleSheetsPixelLoaded() {
  this.parentElement.removeChild(this);
  gtag('event', 'sheets-access', { 'event_category': 'user', 'event_label': 'successful', 'value': 1 });
  gtag('event', 'interactive-figures', { 'event_category': 'user', 'event_label': 'enabled', 'value': 1 });
}

//If google sheets test pixel doesn't work, then revert back to static images
function googleSheetsPixelNotLoaded() {
  console.error('Google Sheets cannot be loaded');

  this.parentElement.removeChild(this);

  let all_fig_imgs = document.querySelectorAll('figure .fig-mobile');
  for (let index = 0; index < all_fig_imgs.length; ++index) {
    let fig_img = all_fig_imgs[index];
    fig_img.classList.remove("fig-mobile");
  }

  let all_fig_iframes = document.querySelectorAll('figure .fig-iframe');
  for (let index = 0; index < all_fig_iframes.length; ++index) {
    let fig_iframe = all_fig_iframes[index];
    fig_iframe.parentElement.removeChild(fig_iframe);
  }
  gtag('event', 'sheets-access', { 'event_category': 'user', 'event_label': 'blocked', 'value': 0 });
  gtag('event', 'interactive-figures', { 'event_category': 'user', 'event_label': 'not-enabled', 'value': 0 });

}

//We use Google Sheets for detailed visualisations
//Check for support and switch out images if supported
function upgradeInteractiveFigures() {
  try {
    if (!isInPrintMode() && bigEnoughForInteractiveFigures() && !dataSaverEnabled() && highBandwidthConnection() && highResolutionCanvasSupported()) {

      //Find each image and create the iframe
      let all_fig_imgs = document.querySelectorAll('figure img[data-iframe]');

      //If no figures with a data-iframe, then we're done
      if (all_fig_imgs.length == 0) {
        return;
      }

      console.log('Upgrading to interactive figures');

      for (let index = 0; index < all_fig_imgs.length; ++index) {
        let fig_img = all_fig_imgs[index];

        if (fig_img.getAttribute('data-iframe')) {

          let iframe = document.createElement('iframe');

          //Set up some default attributes
          iframe.setAttribute('title', fig_img.getAttribute('alt'));
          iframe.setAttribute('class', 'fig-iframe');
          iframe.setAttribute('tabindex', '-1'); // Google embeds are currently not keyboard interactive so disable tabindex
          if (fig_img.getAttribute('aria-labelledby')) {
            iframe.setAttribute('aria-labelledby', fig_img.getAttribute('aria-labelledby'));
          }
          if (fig_img.getAttribute('aria-describedby')) {
            iframe.setAttribute('aria-describedby', fig_img.getAttribute('aria-describedby'));
          }
          iframe.setAttribute('width', fig_img.dataset.width || "600");
          iframe.setAttribute('height', fig_img.dataset.height || '371');
          iframe.setAttribute('seamless', fig_img.dataset.seamless || '');
          iframe.setAttribute('frameborder', fig_img.dataset.frameborder || '0');
          iframe.setAttribute('scrolling', fig_img.dataset.scrolling || 'no');
          iframe.setAttribute('loading', fig_img.dataset.loading || 'lazy');
          iframe.setAttribute('src', fig_img.dataset.iframe);
          // Set it to credentialless to avoid issues when it tries to embed the login screen
          iframe.setAttribute('credentialless', true);

          //The figure should have a link
          let parentLink = fig_img.parentNode;
          if (parentLink.nodeName == "A") {

            //Insert the iframe before the link.
            parentLink.parentNode.insertBefore(iframe, parentLink);

            //Add the fig-mobile class to hide the img in desktop view
            parentLink.classList.add("fig-mobile");
          }

        }
      }

      //Add a test image to check we can actually access Google Sheets
      //as it's sometimes blocked by corporate proxies and the like
      //have a fallback function to revert if this is the case
      let google_sheets_pixel = document.createElement('img');
      google_sheets_pixel.setAttribute('src', 'https://docs.google.com/favicon.ico');
      google_sheets_pixel.setAttribute('height', '1');
      google_sheets_pixel.setAttribute('width', '1');
      google_sheets_pixel.addEventListener('load', googleSheetsPixelLoaded);
      google_sheets_pixel.addEventListener('error', googleSheetsPixelNotLoaded);
      window.document.body.appendChild(google_sheets_pixel);

    } else {
      gtag('event', 'interactive-figures', { 'event_category': 'user', 'event_label': 'not-enabled', 'value': 0 });
    }
  } catch (err) {
    console.error('Error' + err);
    gtag('event', 'interactive-figures', { 'event_category': 'user', 'event_label': 'not-enabled', 'value': 0 });
  }
}

function setDiscussionCount() {
  try {
    if (window.discussion_url && window.fetch) {
      fetch(window.discussion_url)
        .then(function (response) { return response.json(); })
        .then(function (response) {
          if (!response) {
            return;
          }

          let comments = + response.posts_count - 1;
          if (isNaN(comments)) {
            return;
          }
          document.querySelectorAll('.num-comments').forEach(el => {
            el.textContent = comments;
          });

          if (comments === 1) {
            document.querySelectorAll('.comment-singular').forEach(el => {
              el.removeAttribute('data-translation');
            });
          } else {
            document.querySelectorAll('.comment-plural').forEach(el => {
              el.removeAttribute('data-translation');
            });
          }
          document.querySelector('#cta-container').classList.remove('invisible');
          document.querySelector('.discuss-cta').classList.remove('hidden');
          gtag('event', 'discussion-count', { 'event_category': 'user', 'event_label': 'enabled', 'value': 1 });
        })
        .catch(function (err) {
          console.error(err);
          gtag('event', 'discussion-count', { 'event_category': 'user', 'event_label': 'not-enabled', 'value': 0 });
        });
    }
  } catch (err) {
    console.error('Error' + err);
    gtag('event', 'discussion-count', { 'event_category': 'user', 'event_label': 'not-enabled', 'value': 0 });
  }
}

function indexHighlighter() {

  // Don't implement this on mobile as won't be used
  // Note: do show on tablet in case needed when rotating into landscape
  if (window.matchMedia('(max-width: 37.5em)').matches) {
    return;
  }

  //Only activate this if IntersectionObserver is supported
  if (!('IntersectionObserver' in window)) {
    gtag('event', 'index-highlighter', { 'event_category': 'user', 'event_label': 'not-enabled', 'value': 0 });
    return;
  }

  let chapterIndex = document.querySelector('.index-box');

  // If no index - then nothing to do!
  if (!chapterIndex) {
    return;
  }

  // Check if user has set reduced motion and only continue if not
  let hasOSReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  if (hasOSReducedMotion) {
    console.log('User has set prefers-reduced-motion to ' + hasOSReducedMotion + ' so not highlighting the current section in chapter index');
    gtag('event', 'prefers-reduced-motion', { 'event_category': 'user', 'event_label': 'reduce', 'value': 0 });
    gtag('event', 'index-highlighter', { 'event_category': 'user', 'event_label': 'not-enabled', 'value': 0 });
    return;
  }

  // Check if 'position:sticky' is supported (as this is not great UX when not so don't bother)
  // Add the sticky class (which sets 'position:sticky') and then test if that stuck :-)
  // Also use endsWith to support vendor prefixes (Safari v12 needs this)
  chapterIndex.classList.add('sticky');
  let chapterIndexStyles = getComputedStyle(chapterIndex);
  if (!chapterIndexStyles || !chapterIndexStyles.position || !chapterIndexStyles.position.endsWith('sticky')) {
    gtag('event', 'index-highlighter', { 'event_category': 'user', 'event_label': 'not-enabled', 'value': 0 });
    return;
  }

  // Restrict the page height of the index to the page-height, as we're going to scroll this.
  chapterIndex.classList.add('page-height');

  // Create a function to handle highlighting a new index item
  // that will be called by the IntersectionObserver
  function highlightIndexEntry(link) {

    let indexLink = document.querySelector('.index-box a[href="#' + link + '"]');
    let oldIndexLink = document.querySelector('.index-box .active');

    if (!indexLink || indexLink.isEqualNode(oldIndexLink)) {
      return;
    }

    if (oldIndexLink) {
      oldIndexLink.classList.remove('active');
    }
    indexLink.parentNode.classList.add('active');

    // If the index is displayed in full then we're done!
    if (chapterIndex.scrollHeight <= chapterIndex.clientHeight) {
      return;
    }
    // Otherwise if too large to display in full then scroll to this element
    // We'd love to use scrollIntoView but unfortunately won't work if user
    // is still scrolling in main doc, so do it the old fashioned way
    let currentPosition = indexLink.offsetTop;
    let currentNode = indexLink;
    // Walk the node back up to the index-scroller to get the total offset
    // of this entry, relative to the full Index
    while (currentNode && currentNode.parentNode != chapterIndex) {
      currentPosition = currentPosition + currentNode.offsetTop;
      currentNode = currentNode.parentNode;
    }
    // Show the current image in the middle of the screen
    chapterIndex.scrollTop = currentPosition - (chapterIndex.clientHeight / 2);
  }

  // Set up a new Interstection Observer for when the title is 80% from the bottom of the page
  let options = {
    root: null,
    rootMargin: "0px 0px -80% 0px",
    threshold: null
  };
  let observer = new IntersectionObserver(function (entries) {
    for (let index = 0; index < entries.length; ++index) {
      let entry = entries[index];

      if (entry.isIntersecting && entry.target && entry.target.id) {
        highlightIndexEntry(entry.target.id);
      }
    }
  }, options);

  // Add an intersection observer to each heading
  let all_headings = document.querySelectorAll('article h1, article h2, article h3, article h4, article h5, article h6');
  for (let index = 0; index < all_headings.length; ++index) {
    let heading = all_headings[index];
    observer.observe(heading);
  }

  gtag('event', 'index-highlighter', { 'event_category': 'user', 'event_label': 'enabled', 'value': 0 });

}

function toggleDescription(event) {
  let event_button = event.target;
  if (!event_button) {
    return;
  }
  let description_id = event_button.getAttribute('aria-controls');
  if (!description_id) {
    return;
  }

  let description = document.querySelector('#' + description_id);
  if (!description) {
    return;
  }

  description.hidden = !description.hidden;
  event_button.setAttribute('aria-expanded', event_button.getAttribute('aria-expanded') == 'true' ? 'false' : 'true');
  event_button.textContent = event_button.getAttribute('aria-expanded') == 'true' ? event_button.getAttribute('data-hide-text') : event_button.getAttribute('data-show-text');

}

function addShowDescription() {
  let all_desc_buttons = document.querySelectorAll('.fig-description-button');

  for (let index = 0; index < all_desc_buttons.length; ++index) {
    let desc_button = all_desc_buttons[index];
    desc_button.addEventListener('click', toggleDescription);
    desc_button.classList.remove('novisibility-until-js');
    let description = document.querySelector('#' + desc_button.getAttribute('aria-controls'));
    if (description) {
      description.classList.remove('hidden');
      description.classList.add('fig-description');
      description.hidden = true;
    }
  }

}

function addKeyboardScollableRegions() {
  // If a table or code block is overflowing then should allow keyboard focus
  // More details - https://adrianroselli.com/2020/11/under-engineered-responsive-tables.html

  // Handle tables that have overflowed
  let all_table_containers = document.querySelectorAll('.table-wrap-container');
  for (let index = 0; index < all_table_containers.length; ++index) {
    let table_container = all_table_containers[index];

    if (table_container.scrollWidth > table_container.clientWidth) {
      let figure = table_container.parentElement.parentElement;
      if (figure && figure.nodeName == "FIGURE") {
        let figid = figure.id;
        let figcaption = figure.querySelector('figcaption');

        if (figid && figcaption) {
          figcaption.setAttribute('id', figid + '-caption');
          table_container.setAttribute('tabindex', '0');
          table_container.setAttribute('role', 'region');
          table_container.setAttribute('aria-labelledby', figid + '-caption');
        }
      }
    }
  }

  // Handle code blocks that have overflowed
  let all_pre_elements = document.querySelectorAll('pre');
  for (let index = 0; index < all_pre_elements.length; ++index) {
    let pre_element = all_pre_elements[index];

    if (pre_element.scrollWidth > pre_element.clientWidth) {
      pre_element.setAttribute('tabindex', '0');
      pre_element.setAttribute('role', 'region');
      pre_element.setAttribute('aria-label', `Code ${index}`);

    }
  }

}

function addShortKeyEventListers() {
  document.addEventListener("keyup", function onPress(event) {

    // Prevent the search box and other inputs from processing these listeners.
    if (event.target.nodeName == 'INPUT' || event.target.nodeName == 'TEXTAREA' || event.target.nodeName == 'SELECT') {
      return;
    }

    if (event.key === 'p' || event.key === 'P' || event.key === ',' || event.key === '<') {
      let previous = document.getElementById('previous-chapter');
      if (previous) {
        previous.click();
      }
    }
    if (event.key === 'n' || event.key === 'N' || event.key === '.' || event.key === '>') {
      let next = document.getElementById('next-chapter');
      if (next) {
        next.click();
      }
    }
    if (event.key === 'd' || event.key === 'D') {
      document.querySelectorAll('.fig-description-button').forEach(descButton => {
        descButton.click();
      });
    }
  });
}

function indexMenu() {
  let indexBox = document.querySelector('.index-box');
  let indexBoxTitle = document.querySelector('.index .index-btn');

  if (!indexBox || !indexBoxTitle) {
    return;
  }

  indexBoxTitle.addEventListener('click', function () {
    let indexOpen = indexBox.classList.toggle('show');
    indexBoxTitle.setAttribute('aria-expanded', indexOpen);
    let ariaLabel = indexOpen ? indexBoxTitle.getAttribute('data-close-text') : indexBoxTitle.getAttribute('data-open-text');
    indexBoxTitle.setAttribute('aria-label', ariaLabel);
  });

  indexBox.addEventListener("keydown", function onPress(event) {
    if (event.key === 'Escape') {
      if (indexBoxTitle.getAttribute('aria-expanded') === 'true') {
        indexBoxTitle.click();
        indexBoxTitle.focus();
      }
    }
  });
}

handleSelectSwitchers();
handleMobileMenu();
handleNavMenu();
handleDataEvents();
activateJavaScriptElements();
indexMenu();
indexHighlighter();
addShowDescription();
removeLazyLoadingOnPrint();
upgradeInteractiveFigures();
addKeyboardScollableRegions();
setDiscussionCount();
addShortKeyEventListers();