// Global javascript (loaded on all pages in Pattern Lab and Drupal)
// Should be used sparingly because javascript files can be used in components.

// JavaScript should be made compatible with libraries other than jQuery by
// wrapping it with an "anonymous closure". See:
// - https://drupal.org/node/1446420
// - http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth

/**
 * Rocketship UI JS
 *
 * Contains: triggers for functions.
 * Functions themselves are split off and grouped below each behavior.
 */

(function (Drupal, once, window, document) {
  "use strict";

  // Set namespace for frontend UI javascript.
  if (typeof window.rocketshipUI === 'undefined') {
    window.rocketshipUI = {};
  }

  var self = window.rocketshipUI;

  ///////////////////////////////////////////////////////////////////////
  // Behavior for Tabs: triggers
  ///////////////////////////////////////////////////////////////////////

  Drupal.behaviors.rocketshipUITables = {
    attach: (context, settings) => {
      // !!! IF YOU DON'T WANT A SPECIFIC TABLE TO BE RESPONSIVE, SET THE 'RESPONSIVE' ATTRIBUTE TO FALSE
      //     VIA A THEME FUNCTION OR IN AN OVERRIDDEN TWIG TEMPLATE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      //
      // (except for CKE tables, those are purely done via the JS below)

      var tables = context.querySelectorAll('table');

      // Make responsive tables
      // in the Sass: choose between 2 types: 'reformatted' or 'scroll'.
      if (tables.length) {
        self.setResponsiveTables(tables);
      }
    }
  };

  ///////////////////////////////////////////////////////////////////////
  // Behavior for Tabs: functions
  ///////////////////////////////////////////////////////////////////////

  /**
   * Make tables responsive.
   */
  self.setResponsiveTables = (tables) => {
    // Add responsive functionality to tables added via WYSIWYG
    // or otherwise inserted in the page (e.g. from Commerce).

    // If coming from CKE.
    const ckeClass = '.cke_show_borders, .text-long';

    once('set-responsive-tables', tables).forEach((tableElement) => {
      const table = tableElement;
      const pageTable = table.closest('.page') ?? table.closest('.sb-show-main');

      if (pageTable) {
        // If already has a responsive class, define the type
        // based on if it's coming from CKE or not.

        // If table in CKE, we need to take into account
        // the style that the user has set on it:
        if (table.closest(ckeClass)) {
          // Set extra attributes to help us with restyling for mobile.
          self.tableSetAttributes(table);

          // Unlikely situation, but possible:
          // user has set a class that prevents the 'has-scroll' behavior
          // so don't trigger that function and do another behavior
          if (table.classList.contains('table--reformatted')) {
            if (!table.parentNode.classList.contains('table-responsive')) {
              const wrapper = document.createElement('div');
              wrapper.className = 'table-responsive is-reformatted';
              table.parentNode.insertBefore(wrapper, table);
              wrapper.appendChild(table);
            }
            // Default CKE table behavior we want:
          } else {
            if (!table.parentNode.classList.contains('table-responsive')) {
              const wrapper = document.createElement('div');
              wrapper.className = 'table-responsive has-scroll';
              table.parentNode.insertBefore(wrapper, table);
              wrapper.appendChild(table);
            }

            // Set a bunch of stuff using JS to make the table scrollable in
            // different ways based on the th structure.
            self.tableScroll(table, table.closest('.table-responsive'));
          }

          // If not in CKE,
          // your table will either have classes coming from Twig & theme
          // functions, or won't have any at all.
        } else {
          // We only set tables to be responsive, if they have the correct
          // wrapper class which was set in Twig based on the 'responsive'
          // attribute which can be set or overridden using a theme hook.
          if (table?.closest('.table-responsive')?.classList.contains('has-scroll')) {
            self.tableScroll(table, table.closest('.table-responsive'));
          }
        }
      }
    });
  };

  /**
   * Modifications to table classes to make responsive styling easier.
   *
   * - Check various combinations of th's in thead, tfoot or tbody - as first
   * or last row/column and add classes accordingly.
   * @param {HTMLElement} table The table element.
   * @param {HTMLElement} wrapper The wrapper element.
   */
  self.tableScroll = (table, wrapper) => {
    // If th in thead OR tfoot (no combo)
    // make all cells go under each other + space on left (or right, if tfoot).

    // Else, if th in tbody + all in 1 column (diff rows but first OR last
    // index of each row).
    // No combo of first or last.
    // make the first (or last) cell fixed, with padding on the correct side so
    // user can scroll.

    // Move the caption
    const caption = table.querySelector('caption');
    if (caption) {
      wrapper.parentNode.insertBefore(caption, wrapper);
    }

    var trs = table.querySelectorAll('tr');
    var thRow = false;
    var thHead = false;
    var thFoot = false;
    var thCol = false;
    var thLeft = false;
    var thRight = false;

    once('check-th-position', trs).forEach(() => {
      var ths = table.querySelectorAll('th');
      Array.prototype.forEach.call(ths, (thElement, j) => {
        var th = thElement;

        // We have th's in the left column of the table body.
        if (j === 0 && th.parentNode.parentNode.nodeName === 'TBODY') {
          thCol = true;
          thLeft = true;
        }

        // We have th's in the right column of the table body.
        if (j === (ths.length - 1) && th.parentNode.parentNode.nodeName === 'TBODY') {
          thCol = true;
          thRight = true;
        }

        // We have th's in the head row.
        if (th.parentNode.parentNode.nodeName === 'THEAD' ) {
          thRow = true;
          thHead = true;
        }

        // We have th's in the head row.
        if (th.parentNode.parentNode.nodeName === 'TFOOT' ) {
          thRow = true;
          thFoot = true;
        }
      });
    });

    // Th's in thead or tfoot => row becomes a fixed column.
    if (thRow) {
      wrapper.classList.add('js-table--th-row');

      if (thHead) {
        wrapper.classList.add('js-table--th-top');
      } else {
        wrapper.classList.add('js-table--th-bottom');
      }
      if (window.innerWidth < 768) {
        sizeCells(table);
      }

      window.rocketshipUI.optimizedResize().add(() => {
        sizeCells(table);
      });
    } else if (thCol) {
      // Th's in body on left or right (but only without th's in thead or tfoot)
      // column cells become fixed to left or right.
      wrapper.classList.add('js-table--th-col');

      if (thLeft) {
        wrapper.classList.add('js-table--th-left');
      } else {
        wrapper.classList.add('js-table--th-right');
      }

      // Wrap th's in divs to be able to position them.
      var ths = table.querySelectorAll('th');
      Array.prototype.forEach.call(ths, (th) => {
        var thHtml = th.innerHTML;
        var div = document.createElement('div');
        div.className = 'th__content';
        div.innerHTML = thHtml;
        th.innerHTML = '';
        th.appendChild(div);
      });

      if (window.innerWidth < 768) {
        sizeTh(ths);
      }

      window.rocketshipUI.optimizedResize().add(function() {
        sizeTh(ths);
      });
    } else {
      wrapper.classList.add('js-table--no-th');
    }

    /**
     * Resize cells in a table to match content.
     *
     * @param {HTMLElement} table The table element.
     */
    function sizeCells(table) {
      var cells = table.querySelectorAll('th, td');
      var heightMax = 0;

      Array.prototype.forEach.call(cells, (cell) => {
        cell.style.height = 'auto';
        cell.style.minHeight = '0';

        var cellHeight = cell.offsetHeight;

        if (cellHeight > heightMax) {
          heightMax = cellHeight;
        }
      });

      Array.prototype.forEach.call(cells, (cell) => {
        if (window.innerWidth < 768) {
          cell.style.minHeight = heightMax + 'px';
        } else {
          cell.style.minHeight = 'auto';
        }
      });
    }

    /**
     * Resize th content based on tr.
     *
     * @param {NodeListOf<HTMLElement>} ths The th elements.
     */
    function sizeTh(ths) {
      Array.prototype.forEach.call(ths, (th) => {
        var thContent = th.querySelector('.th__content');
        if (window.innerWidth < 768) {
          thContent.style.minHeight = th.parentNode.offsetHeight + 'px';
        } else {
          thContent.style.minHeight = 'auto';
        }
      });
    }
  };

  /**
   * Modifications to table attributes to make responsive styling easier.
   *
   * Normally, this stuff is handled by the default Twig templates for tables
   * but some modules or custom stuff has their own Twig and so they don't get
   * the same treatment. This JS should modify those tables so they are in line
   * with what is expected.
   *
   * - Add some data-attributes or other elements
   * to make responsive styling easier
   * e.g. data-title on each cell, that matches a th's text
   * @param {HTMLElement} table The table element.
   */
  self.tableSetAttributes = (table) => {
    // Make data-attributes for CSS to use as headings
    // If th's in thead.
    const tableHead = table.querySelector('thead');
    if (tableHead) {
      tableHead.setAttribute('tabindex', '0');
      let headings = [];
      let ths = table.querySelectorAll('th');
      Array.prototype.forEach.call(ths, (th) => {
        headings.push(th.textContent);
      });
      let rows = table.querySelectorAll('tr');
      Array.prototype.forEach.call(rows, (row) => {
        let cells = row.querySelectorAll('td');
        let count = 0;
        Array.prototype.forEach.call(cells, (cell)  => {
          cell.setAttribute('data-title', headings[count]);
          count++;
        });
      });
    } else {
      // If th's in tbody.
      let tableBody = table.querySelector('tbody');
      if(tableBody) {
        tableBody.setAttribute('tabindex', '0');
      }
      let ths = table.querySelectorAll('th');
      if(ths.length) {
        let rows = table.querySelectorAll('tr');
        Array.prototype.forEach.call(rows, (row) => {
          let heading = row.querySelector('th').textContent;
          let cells = row.querySelectorAll('td');
          Array.prototype.forEach.call(cells, (cell) => {
            cell.setAttribute('data-title', heading);
          });
        });
      } else {
        // If no th's at all, don't need certain styling on mobile.
        table.classList.add('no-th');
      }
    }
  };

})(Drupal, once, window, document);
