text: /* 
  table-sort-js
  Author: Lee Wannacott
  Licence: MIT License Copyright (c) 2021 Lee Wannacott 
      
  GitHub Repository: https://github.com/LeeWannacott/table-sort-js
  npm package: https://www.npmjs.com/package/table-sort-js
  Demo: https://leewannacott.github.io/Portfolio/#/GitHub
  Install:
  Frontend: <script src="https://leewannacott.github.io/table-sort-js/table-sort.js"></script> or
  Download this file and add <script src="table-sort.js"></script> to your HTML 
  Backend: npm install table-sort-js and use require("../node_modules/table-sort-js/table-sort.js") 
  Instructions:
    Add class="table-sort" to tables you'd like to make sortable
    Click on the table headers to sort them.
  */
  
  function tableSortJs(test = false, domDocumentWindow = document) {
    function checkIfTesting() {
      if (test === true) {
        const getTagTable = domDocumentWindow.getElementsByTagName("table");
        const createTableHead = domDocumentWindow.createElement("thead");
        return [getTagTable, createTableHead];
      } else {
        const getTagTable = document.getElementsByTagName("table");
        const createTableHead = document.createElement("thead");
        return [getTagTable, createTableHead];
      }
    }
    const [getTagTable, createTableHead] = checkIfTesting();
    const columnIndexAndTableRow = {};
    const fileSizeColumnTextAndRow = {};
    for (let table of getTagTable) {
      if (table.classList.contains("table-sort")) {
        makeTableSortable(table);
      }
    }
  
    function makeTableSortable(sortableTable) {
      if (sortableTable.getElementsByTagName("thead").length === 0) {
        createTableHead;
        the.appendChild(sortableTable.rows[0]);
        sortableTable.insertBefore(the, sortableTable.firstChild);
      }
  
      const tableHead = sortableTable.querySelector("thead");
      const tableBody = sortableTable.querySelector("tbody");
      const tableHeadHeaders = tableHead.querySelectorAll("th");
      tableHead.style.cursor = "pointer";
  
      let columnIndexesClicked = [];
      for (let [columnIndex, th] of tableHeadHeaders.entries()) {
        let timesClickedColumn = 0;
  
        th.addEventListener("click", function () {
          const tableRows = tableBody.querySelectorAll("tr");
          const columnData = [];
  
          let isDataAttribute = th.classList.contains("data-sort");
          if(isDataAttribute){
            for (let [i, tr] of tableRows.entries()) {
              const dataAttributeTd = tr.querySelectorAll("td").item(columnIndex).dataset.sort
              columnData.push(`${dataAttributeTd}#${i}`)
              columnIndexAndTableRow[columnData[i]] = tr.innerHTML;
            }
          }
  
          let isDayOfWeek = th.classList.contains("days-of-week");
          if (isDayOfWeek) {
            const day = /(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday|Mon|Tue|Wed|Thur|Fri|Sat|Sun)/i;
            const dayOfWeek = {
              Monday: 1,
              Tuesday: 2,
              Wednesday: 3,
              Thursday: 4,
              Friday: 5,
              Saturday: 6,
              Sunday: 7,
            };
            for (let [i, tr] of tableRows.entries()) {
              const dayOfWeekTd = tr.querySelectorAll("td").item(columnIndex)
                .textContent;
              if (dayOfWeekTd.match(day)) {
                if (dayOfWeekTd.match(/Monday|Mon/i)) {
                  columnData.push(`${dayOfWeek.Monday}#${i}`);
                } else if (dayOfWeekTd.match(/Tuesday|Tue/i)) {
                  columnData.push(`${dayOfWeek.Tuesday}#${i}`);
                } else if (dayOfWeekTd.match(/Wednesday|Wed/i)) {
                  columnData.push(`${dayOfWeek.Wednesday}#${i}`);
                } else if (dayOfWeekTd.match(/Thursday|Thur/i)) {
                  columnData.push(`${dayOfWeek.Thursday}#${i}`);
                } else if (dayOfWeekTd.match(/Friday|Fri/i)) {
                  columnData.push(`${dayOfWeek.Friday}#${i}`);
                } else if (dayOfWeekTd.match(/Saturday|Sat/i)) {
                  columnData.push(`${dayOfWeek.Saturday}#${i}`);
                } else if (dayOfWeekTd.match(/Sunday|Sun/i)) {
                  columnData.push(`${dayOfWeek.Sunday}#${i}`);
                }
              } else {
                columnData.push(`!X!Y!Z!#${i}`);
              }
            }
          }
  
          // Handle filesize sorting (e.g KB, MB, GB, TB) - Turns data into KiB.
          let isFileSize = th.classList.contains("file-size");
          if (isFileSize) {
            const numberWithUnitType = /[.0-9]+(\s?B|\s?KB|\s?KiB|\s?MB|\s?MiB|\s?GB|\s?GiB|T\s?B|\s?TiB)/i;
            const unitType = /(\s?B|\s?KB|\s?KiB|\s?MB|\s?MiB|\s?GB|G\s?iB|\s?TB|\s?TiB)/i;
            const fileSizes = {
              Kibibyte: 1024,
              Mebibyte: 1.049e6,
              Gibibyte: 1.074e9,
              Tebibyte: 1.1e12,
              Pebibyte: 1.126e15,
              Kilobyte: 1000,
              Megabyte: 1e6,
              Gigabyte: 1e9,
              Terabyte: 1e12,
            };
  
            function removeUnitTypeConvertToBytes(fileSizeTd, _replace) {
              fileSizeTd = fileSizeTd.replace(unitType, "");
              fileSizeTd = fileSizeTd.replace(
                fileSizeTd,
                fileSizeTd * fileSizes[_replace]
              );
              return fileSizeTd;
            }
  
            for (let [i, tr] of tableRows.entries()) {
              let fileSizeTd = tr.querySelectorAll("td").item(columnIndex)
                .textContent;
              if (fileSizeTd.match(numberWithUnitType)) {
                if (fileSizeTd.match(/\s?KB/i)) {
                  fileSizeTd = removeUnitTypeConvertToBytes(fileSizeTd, 'Kilobyte')
                  columnData.push(`${fileSizeTd}#${i}`);
                } else if (fileSizeTd.match(/\s?KiB/i)) {
                  fileSizeTd = removeUnitTypeConvertToBytes(fileSizeTd, 'Kibibyte')
                  columnData.push(`${fileSizeTd}#${i}`);
                } else if (fileSizeTd.match(/\s?MB/i)) {
                  fileSizeTd = removeUnitTypeConvertToBytes(fileSizeTd, 'Megabyte')
                  columnData.push(`${fileSizeTd}#${i}`);
                } else if (fileSizeTd.match(/\s?MiB/i)) {
                  fileSizeTd = removeUnitTypeConvertToBytes(fileSizeTd, 'Mebibyte')
                  columnData.push(`${fileSizeTd}#${i}`);
                } else if (fileSizeTd.match(/\s?GB/i)) {
                  fileSizeTd = removeUnitTypeConvertToBytes(fileSizeTd, 'Gigabyte')
                  columnData.push(`${fileSizeTd}#${i}`);
                } else if (fileSizeTd.match(/\s?GiB/i)) {
                  fileSizeTd = removeUnitTypeConvertToBytes(fileSizeTd, 'Gibibyte')
                  columnData.push(`${fileSizeTd}#${i}`);
                } else if (fileSizeTd.match(/\s?TB/i)) {
                  fileSizeTd = removeUnitTypeConvertToBytes(fileSizeTd, 'Terabyte')
                  columnData.push(`${fileSizeTd}#${i}`);
                } else if (fileSizeTd.match(/\s?TiB/i)) {
                  fileSizeTd = removeUnitTypeConvertToBytes(fileSizeTd, 'Tebibyte')
                  columnData.push(`${fileSizeTd}#${i}`);
                } else if (fileSizeTd.match(/\s?B/i)) {
                  fileSizeTd = fileSizeTd.replace(unitType, "");
                  columnData.push(`${fileSizeTd}#${i}`);
                }
              } else {
                columnData.push(`!X!Y!Z!#${i}`);
              }
            }
          }
  
          let isRememberSort = sortableTable.classList.contains("remember-sort");
          // Checking if user has clicked different column from the first column if yes reset times clicked.
          if (!isRememberSort) {
            columnIndexesClicked.push(columnIndex);
            if (timesClickedColumn === 1 && columnIndexesClicked.length > 1) {
              const lastColumnClicked =
                columnIndexesClicked[columnIndexesClicked.length - 1];
              const secondLastColumnClicked =
                columnIndexesClicked[columnIndexesClicked.length - 2];
              if (lastColumnClicked !== secondLastColumnClicked) {
                timesClickedColumn = 0;
                columnIndexesClicked.shift();
              }
            }
          }
  
          timesClickedColumn += 1;
  
          getTableData();
          updateTable();
  
          function updateTable() {
            for (let [i, tr] of tableRows.entries()) {
              if (isFileSize) {
                tr.innerHTML = fileSizeColumnTextAndRow[columnData[i]];
                let fileSizeInBytesHTML = tr
                  .querySelectorAll("td")
                  .item(columnIndex).innerHTML;
                let fileSizeInBytesText = tr
                  .querySelectorAll("td")
                  .item(columnIndex).textContent;
                const fileSizes = {
                  Kibibyte: 1024,
                  Mebibyte: 1.049e6,
                  Gibibyte: 1.074e9,
                  Tebibyte: 1.1e12,
                  Pebibyte: 1.126e15,
                };
                // Remove the unique identifyer for duplicate values(#number).
                columnData[i] = columnData[i].replace(/#[0-9]*/, "");
                if (columnData[i] < fileSizes.Kibibyte) {
                  fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
                    fileSizeInBytesText,
                    `${parseFloat(columnData[i]).toFixed(2)} B`
                  );
                } else if (
                  columnData[i] >= fileSizes.Kibibyte &&
                  columnData[i] < fileSizes.Mebibyte
                ) {
                  fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
                    fileSizeInBytesText,
                    `${(columnData[i] / fileSizes.Kibibyte).toFixed(2)} KiB`
                  );
                } else if (
                  columnData[i] >= fileSizes.Mebibyte &&
                  columnData[i] < fileSizes.Gibibyte
                ) {
                  fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
                    fileSizeInBytesText,
                    `${(columnData[i] / fileSizes.Mebibyte).toFixed(2)} MiB`
                  );
                } else if (
                  columnData[i] >= fileSizes.Gibibyte &&
                  columnData[i] < fileSizes.Tebibyte
                ) {
                  fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
                    fileSizeInBytesText,
                    `${(columnData[i] / fileSizes.Gibibyte).toFixed(2)} GiB`
                  );
                } else if (
                  columnData[i] >= fileSizes.Tebibyte &&
                  columnData[i] < fileSizes.Pebibyte
                ) {
                  fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
                    fileSizeInBytesText,
                    `${(columnData[i] / fileSizes.Tebibyte).toFixed(2)} TiB`
                  );
                } else {
                  fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
                    fileSizeInBytesText,
                    "NaN"
                  );
                }
                tr
                  .querySelectorAll("td")
                  .item(columnIndex).innerHTML = fileSizeInBytesHTML;
              } else if (!isFileSize) {
                tr.innerHTML = columnIndexAndTableRow[columnData[i]];
              }
            }
          }
  
          function getTableData() {
            for (let [i, tr] of tableRows.entries()) {
              // inner text for column we click on
              let tdTextContent = tr.querySelectorAll("td").item(columnIndex)
                .textContent;
              if (tdTextContent.length === 0) {
                tdTextContent = "";
              }
              if (tdTextContent.trim() !== "") {
                if (isFileSize) {
                  fileSizeColumnTextAndRow[columnData[i]] = tr.innerHTML;
                }
                if (isDayOfWeek) {
                  columnIndexAndTableRow[columnData[i]] = tr.innerHTML;
                }
                if (!isFileSize && !isDayOfWeek && !isDataAttribute) {
                  columnData.push(`${tdTextContent}#${i}`);
                  columnIndexAndTableRow[`${tdTextContent}#${i}`] = tr.innerHTML;
                }
              } else {
                // Fill in blank table cells dict key with filler value.
                columnData.push(`!X!Y!Z!#${i}`);
                columnIndexAndTableRow[`!X!Y!Z!#${i}`] = tr.innerHTML;
              }
            }
  
            function naturalSortAescending(a, b) {
              if (a.includes("X!Y!Z!#")) {
                return 1;
              } else if (b.includes("X!Y!Z!#")) {
                return -1;
              } else {
                return a.localeCompare(
                  b,
                  navigator.languages[0] || navigator.language,
                  { numeric: true, ignorePunctuation: true }
                );
              }
            }
  
            function naturalSortDescending(a, b) {
              return naturalSortAescending(b, a);
            }
  
            function clearArrows(arrowUp = "▲", arrowDown = "▼") {
              th.innerText = th.innerText.replace(arrowUp, "");
              th.innerText = th.innerText.replace(arrowDown, "");
            }
  
            let arrowUp = " ▲";
            let arrowDown = " ▼";
  
            // Sort naturally; default aescending unless th contains 'order-by-desc' as className.
            if (columnData[0] === undefined) {
              return;
            }
  
            let desc = th.classList.contains("order-by-desc");
            let tableArrows = sortableTable.classList.contains("table-arrows");
  
            if (timesClickedColumn === 1) {
              if (desc) {
                if (tableArrows) {
                  clearArrows(arrowUp, arrowDown);
                  th.insertAdjacentText("beforeend", arrowDown);
                }
                columnData.sort(naturalSortDescending, {
                  numeric: true,
                  ignorePunctuation: true,
                });
              } else {
                if (tableArrows) {
                  clearArrows(arrowUp, arrowDown);
                  th.insertAdjacentText("beforeend", arrowUp);
                }
                columnData.sort(naturalSortAescending);
              }
            } else if (timesClickedColumn === 2) {
              timesClickedColumn = 0;
              if (desc) {
                if (tableArrows) {
                  clearArrows(arrowUp, arrowDown);
                  th.insertAdjacentText("beforeend", arrowUp);
                }
                columnData.sort(naturalSortAescending, {
                  numeric: true,
                  ignorePunctuation: true,
                });
              } else {
                if (tableArrows) {
                  clearArrows(arrowUp, arrowDown);
                  th.insertAdjacentText("beforeend", arrowDown);
                }
                columnData.sort(naturalSortDescending);
              }
            }
          }
        });
      }
    }
  }
  
  if (
    document.readyState === "complete" ||
    document.readyState === "interactive"
  ) {
    tableSortJs();
  } else if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", tableSortJs, false);
  }
  if (typeof module == "object") {
    module.exports = tableSortJs;
  }
  