diff --git a/README.md b/README.md
index 10648b7..37ca0b9 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,6 @@ Or Minified (smaller size, but harder to debug!):
Example on how to use table-sort-js with [HTML](https://leewannacott.github.io/table-sort-js/docs/html5.html)
-
- Option 2: Install from npm:
```javascript
diff --git a/browser-extensions/chrome/manifest.json b/browser-extensions/chrome/manifest.json
index aa5aedc..251c72e 100644
--- a/browser-extensions/chrome/manifest.json
+++ b/browser-extensions/chrome/manifest.json
@@ -2,7 +2,7 @@
"manifest_version": 3,
"author": "Lee Wannacott",
"name": "table-sort-js",
- "version": "1.16.0",
+ "version": "1.17.0",
"description": "Makes tables sortable using table-sort-js: https://github.com/LeeWannacott/table-sort-js",
"icons": { "48": "icons/t.png" },
"browser_action": {
diff --git a/browser-extensions/chrome/table-sort-js.zip b/browser-extensions/chrome/table-sort-js.zip
index ed4be8a..43adff3 100644
Binary files a/browser-extensions/chrome/table-sort-js.zip and b/browser-extensions/chrome/table-sort-js.zip differ
diff --git a/browser-extensions/chrome/table-sort.js b/browser-extensions/chrome/table-sort.js
index 4b19ea0..a9a7e31 100644
--- a/browser-extensions/chrome/table-sort.js
+++ b/browser-extensions/chrome/table-sort.js
@@ -28,7 +28,6 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
const [getTagTable] = getHTMLTables();
const columnIndexAndTableRow = {};
- const fileSizeColumnTextAndRow = {};
for (let table of getTagTable) {
if (table.classList.contains("table-sort")) {
makeTableSortable(table);
@@ -46,16 +45,18 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
sortableTable.insertBefore(createTableHead, sortableTable.firstChild);
}
- function getTableBody(sortableTable) {
+ function getTableBodies(sortableTable) {
if (sortableTable.getElementsByTagName("thead").length === 0) {
createMissingTableHead(sortableTable);
if (sortableTable.querySelectorAll("tbody").length > 1) {
+ // Why index 1?; I don't remember
return sortableTable.querySelectorAll("tbody")[1];
} else {
- return sortableTable.querySelector("tbody");
+ return sortableTable.querySelectorAll("tbody");
}
} else {
- return sortableTable.querySelector("tbody");
+ // if
or exists below the browser will make |
+ return sortableTable.querySelectorAll("tbody");
}
}
@@ -108,389 +109,455 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
function makeTableSortable(sortableTable) {
const table = {
- body: getTableBody(sortableTable),
- head: sortableTable.querySelector("thead"),
+ bodies: getTableBodies(sortableTable),
+ theads: sortableTable.querySelectorAll("thead"),
+ rows: [],
+ headers: [],
};
- table.headers = table.head.querySelectorAll("th");
- table.rows = table.body.querySelectorAll("tr");
-
- let columnIndexesClicked = [];
+ for (let index of table.bodies.keys()) {
+ if (table.bodies.item(index) == null) {
+ return;
+ }
+ table.headers.push(table.theads.item(index).querySelectorAll("th"));
+ table.rows.push(table.bodies.item(index).querySelectorAll("tr"));
+ }
- const isNoSortClassInference =
- sortableTable.classList.contains("no-class-infer");
+ table.hasClass = {
+ noClassInfer: sortableTable.classList.contains("no-class-infer"),
+ cellsSort: sortableTable.classList.contains("cells-sort"),
+ tableArrows: sortableTable.classList.contains("table-arrows"),
+ rememberSort: sortableTable.classList.contains("remember-sort"),
+ };
- for (let [columnIndex, th] of table.headers.entries()) {
- if (!th.classList.contains("disable-sort")) {
- th.style.cursor = "pointer";
- if (!isNoSortClassInference) {
- inferSortClasses(table.rows, columnIndex, th);
+ for (
+ let headerIndex = 0;
+ headerIndex < table.theads.length;
+ headerIndex++
+ ) {
+ let columnIndexesClicked = [];
+ for (let [columnIndex, th] of table.headers[headerIndex].entries()) {
+ if (!th.classList.contains("disable-sort")) {
+ th.style.cursor = "pointer";
+ if (!table.hasClass.noClassInfer) {
+ inferSortClasses(table.rows[headerIndex], columnIndex, th);
+ }
+ makeEachColumnSortable(
+ th,
+ headerIndex,
+ columnIndex,
+ table,
+ columnIndexesClicked
+ );
}
- makeEachColumnSortable(
- th,
- columnIndex,
- table,
- sortableTable,
- columnIndexesClicked
- );
}
}
}
- function makeEachColumnSortable(
- th,
- columnIndex,
- table,
- sortableTable,
- columnIndexesClicked
- ) {
- const desc = th.classList.contains("order-by-desc");
- const tableArrows = sortableTable.classList.contains("table-arrows");
- const [arrowUp, arrowDown] = [" ▲", " ▼"];
- const fillValue = "!X!Y!Z!";
-
- if (desc && tableArrows) {
- th.insertAdjacentText("beforeend", arrowDown);
- } else if (tableArrows) {
- th.insertAdjacentText("beforeend", arrowUp);
+ function cellsOrRows(table, tr) {
+ if (table.hasClass.cellsSort) {
+ return tr.innerHTML;
+ } else {
+ return tr.outerHTML;
}
+ }
- function sortDataAttributes(tableRows, column) {
- for (let [i, tr] of tableRows.entries()) {
- let dataAttributeTd = getColumn(tr, column.spanSum, column.span).dataset
- .sort;
- column.toBeSorted.push(`${dataAttributeTd}#${i}`);
- columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML;
- }
+ function sortDataAttributes(table, column) {
+ for (let [i, tr] of table.visibleRows.entries()) {
+ let dataAttributeTd = column.getColumn(tr, column.spanSum, column.span)
+ .dataset.sort;
+ column.toBeSorted.push(`${dataAttributeTd}#${i}`);
+ columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr);
}
+ }
- function sortFileSize(tableRows, column) {
- let unitToMultiplier = {
- b: 1,
- kb: 1000,
- kib: 2 ** 10,
- mb: 1e6,
- mib: 2 ** 20,
- gb: 1e9,
- gib: 2 ** 30,
- tb: 1e12,
- tib: 2 ** 40,
- };
- const numberWithUnitType = /([.0-9]+)\s?(B|KB|KiB|MB|MiB|GB|GiB|TB|TiB)/i;
- for (let [i, tr] of tableRows.entries()) {
- let fileSizeTd = tr
- .querySelectorAll("td")
- .item(columnIndex).textContent;
- let match = fileSizeTd.match(numberWithUnitType);
- if (match) {
- let number = parseFloat(match[1]);
- let unit = match[2].toLowerCase();
- let multiplier = unitToMultiplier[unit];
- column.toBeSorted.push(`${number * multiplier}#${i}`);
- } else {
- column.toBeSorted.push(`${fillValue}#${i}`);
- }
+ function sortFileSize(table, column, columnIndex) {
+ let unitToMultiplier = {
+ b: 1,
+ kb: 1000,
+ kib: 2 ** 10,
+ mb: 1e6,
+ mib: 2 ** 20,
+ gb: 1e9,
+ gib: 2 ** 30,
+ tb: 1e12,
+ tib: 2 ** 40,
+ };
+ const numberWithUnitType = /([.0-9]+)\s?(B|KB|KiB|MB|MiB|GB|GiB|TB|TiB)/i;
+ for (let [i, tr] of table.visibleRows.entries()) {
+ let fileSizeTd = tr.querySelectorAll("td").item(columnIndex).textContent;
+ let match = fileSizeTd.match(numberWithUnitType);
+ if (match) {
+ let number = parseFloat(match[1]);
+ let unit = match[2].toLowerCase();
+ let multiplier = unitToMultiplier[unit];
+ column.toBeSorted.push(`${number * multiplier}#${i}`);
+ columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr);
}
}
+ }
- function sortByRuntime(tableRows, column) {
- try {
- for (let [i, tr] of tableRows.entries()) {
- const regexMinutesAndSeconds = /^(\d+h)?\s?(\d+m)?\s?(\d+s)?$/i;
- let columnOfTd = "";
- // TODO: github actions runtime didn't like textContent, tests didn't like innerText?
- if (testingTableSortJS) {
- columnOfTd = getColumn(tr, column.spanSum, column.span).textContent;
- } else {
- columnOfTd = getColumn(tr, column.spanSum, column.span).innerText;
- }
- let match = columnOfTd.match(regexMinutesAndSeconds);
- let [minutesInSeconds, hours, seconds] = [0, 0, 0];
- let timeinSeconds = columnOfTd;
- if (match) {
- const regexHours = match[1];
- if (regexHours) {
- hours = Number(regexHours.replace("h", "")) * 60 * 60;
- }
- const regexMinutes = match[2];
- if (regexMinutes) {
- minutesInSeconds = Number(regexMinutes.replace("m", "")) * 60;
- }
- const regexSeconds = match[3];
- if (regexSeconds) {
- seconds = Number(regexSeconds.replace("s", ""));
+ function sortDates(datesFormat, table, column) {
+ try {
+ for (let [i, tr] of table.visibleRows.entries()) {
+ let columnOfTd, datesRegex;
+ if (datesFormat === "mdy" || datesFormat === "dmy") {
+ datesRegex = /^(\d\d?)[./-](\d\d?)[./-]((\d\d)?\d\d)/;
+ } else if (datesFormat === "ymd") {
+ datesRegex = /^(\d\d\d\d)[./-](\d\d?)[./-](\d\d?)/;
+ }
+ columnOfTd = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).textContent;
+ let match = columnOfTd.match(datesRegex);
+ let [years, days, months] = [0, 0, 0];
+ let numberToSort = columnOfTd;
+ if (match) {
+ const [regPos1, regPos2, regPos3] = [match[1], match[2], match[3]];
+ if (regPos1 && regPos2 && regPos3) {
+ if (datesFormat === "mdy") {
+ [months, days, years] = [regPos1, regPos2, regPos3];
+ } else if (datesFormat === "ymd") {
+ [years, months, days] = [regPos1, regPos2, regPos3];
+ } else {
+ [days, months, years] = [regPos1, regPos2, regPos3];
}
- timeinSeconds = hours + minutesInSeconds + seconds;
}
- column.toBeSorted.push(`${timeinSeconds}#${i}`);
- columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML;
+ numberToSort = Number(
+ years +
+ String(months).padStart(2, "0") +
+ String(days).padStart(2, "0")
+ );
}
- } catch (e) {
- console.log(e);
+ column.toBeSorted.push(`${numberToSort}#${i}`);
+ columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr);
}
+ } catch (e) {
+ console.log(e);
}
+ }
- function sortDates(datesFormat, tableRows, column) {
- try {
- for (let [i, tr] of tableRows.entries()) {
- let columnOfTd, datesRegex;
- if (datesFormat === "mdy" || datesFormat === "dmy") {
- datesRegex = /^(\d\d?)[./-](\d\d?)[./-]((\d\d)?\d\d)/;
- } else if (datesFormat === "ymd") {
- datesRegex = /^(\d\d\d\d)[./-](\d\d?)[./-](\d\d?)/;
+ function sortByRuntime(table, column) {
+ try {
+ for (let [i, tr] of table.visibleRows.entries()) {
+ const regexMinutesAndSeconds = /^(\d+h)?\s?(\d+m)?\s?(\d+s)?$/i;
+ let columnOfTd = "";
+ // TODO: github actions runtime didn't like textContent, tests didn't like innerText?
+ if (testingTableSortJS) {
+ columnOfTd = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).textContent;
+ } else {
+ columnOfTd = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).innerText;
+ }
+ let match = columnOfTd.match(regexMinutesAndSeconds);
+ let [minutesInSeconds, hours, seconds] = [0, 0, 0];
+ let timeinSeconds = columnOfTd;
+ if (match) {
+ const regexHours = match[1];
+ if (regexHours) {
+ hours = Number(regexHours.replace("h", "")) * 60 * 60;
}
- columnOfTd = getColumn(tr, column.spanSum, column.span).textContent;
- let match = columnOfTd.match(datesRegex);
- let [years, days, months] = [0, 0, 0];
- let numberToSort = columnOfTd;
- if (match) {
- const [regPos1, regPos2, regPos3] = [match[1], match[2], match[3]];
- if (regPos1 && regPos2 && regPos3) {
- if (datesFormat === "mdy") {
- [months, days, years] = [regPos1, regPos2, regPos3];
- } else if (datesFormat === "ymd") {
- [years, months, days] = [regPos1, regPos2, regPos3];
- } else {
- [days, months, years] = [regPos1, regPos2, regPos3];
- }
- }
- numberToSort = Number(
- years +
- String(months).padStart(2, "0") +
- String(days).padStart(2, "0")
- );
+ const regexMinutes = match[2];
+ if (regexMinutes) {
+ minutesInSeconds = Number(regexMinutes.replace("m", "")) * 60;
+ }
+ const regexSeconds = match[3];
+ if (regexSeconds) {
+ seconds = Number(regexSeconds.replace("s", ""));
}
- column.toBeSorted.push(`${numberToSort}#${i}`);
- columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML;
+ timeinSeconds = hours + minutesInSeconds + seconds;
}
- } catch (e) {
- console.log(e);
+ column.toBeSorted.push(`${timeinSeconds}#${i}`);
+ columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr);
}
+ } catch (e) {
+ console.log(e);
}
+ }
- function rememberSort() {
- // if user clicked different column from first column reset times clicked.
- columnIndexesClicked.push(columnIndex);
- if (timesClickedColumn === 1 && columnIndexesClicked.length > 1) {
- const lastColumnClicked =
- columnIndexesClicked[columnIndexesClicked.length - 1];
- const secondLastColumnClicked =
- columnIndexesClicked[columnIndexesClicked.length - 2];
- if (lastColumnClicked !== secondLastColumnClicked) {
- columnIndexesClicked.shift();
- timesClickedColumn = 0;
+ function getTableData(tableProperties, timesClickedColumn) {
+ const {
+ table,
+ tableRows,
+ fillValue,
+ column,
+ th,
+ hasThClass,
+ isSortDates,
+ desc,
+ arrow,
+ } = tableProperties;
+ for (let [i, tr] of tableRows.entries()) {
+ let tdTextContent = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).textContent;
+ if (tdTextContent.length === 0) {
+ tdTextContent = "";
+ }
+ if (tdTextContent.trim() !== "") {
+ if (
+ !hasThClass.fileSize &&
+ !hasThClass.dataSort &&
+ !hasThClass.runtime &&
+ !hasThClass.filesize &&
+ !isSortDates.dayMonthYear &&
+ !isSortDates.yearMonthDay &&
+ !isSortDates.monthDayYear
+ ) {
+ column.toBeSorted.push(`${tdTextContent}#${i}`);
+ columnIndexAndTableRow[`${tdTextContent}#${i}`] = cellsOrRows(
+ table,
+ tr
+ );
}
+ } else {
+ // Fill in blank table cells dict key with filler value.
+ column.toBeSorted.push(`${fillValue}#${i}`);
+ columnIndexAndTableRow[`${fillValue}#${i}`] = cellsOrRows(table, tr);
}
- return timesClickedColumn;
}
- function getColSpanData(headers, column) {
- headers.forEach((th, index) => {
- column.span[index] = th.colSpan;
- if (index === 0) column.spanSum[index] = th.colSpan;
- else column.spanSum[index] = column.spanSum[index - 1] + th.colSpan;
- });
- }
+ const isPunctSort = th.classList.contains("punct-sort");
+ const isAlphaSort = th.classList.contains("alpha-sort");
+ const isNumericSort = th.classList.contains("numeric-sort");
- function getColumn(tr, colSpanSum, colSpanData) {
- return tr
- .querySelectorAll("td")
- .item(
- colSpanData[columnIndex] === 1
- ? colSpanSum[columnIndex] - 1
- : colSpanSum[columnIndex] - colSpanData[columnIndex]
- );
+ function parseNumberFromString(str) {
+ let num;
+ str = str.slice(0, str.indexOf("#"));
+ if (str.match(/^\((\d+(?:\.\d+)?)\)$/)) {
+ num = -1 * Number(str.slice(1, -1));
+ } else {
+ num = Number(str);
+ }
+ return num;
}
- function getTableData(tableProperties) {
- const { tableRows, column, hasThClass, isSortDates } = tableProperties;
- for (let [i, tr] of tableRows.entries()) {
- let tdTextContent = getColumn(
- tr,
- column.spanSum,
- column.span
- ).textContent;
- if (tdTextContent.length === 0) {
- tdTextContent = "";
- }
- if (tdTextContent.trim() !== "") {
- if (hasThClass.fileSize) {
- fileSizeColumnTextAndRow[column.toBeSorted[i]] = tr.outerHTML;
- }
- // These classes already handle pushing to column and setting the tr html.
- if (
- !hasThClass.fileSize &&
- !hasThClass.dataSort &&
- !hasThClass.runtime &&
- !isSortDates.dayMonthYear &&
- !isSortDates.yearMonthDay &&
- !isSortDates.monthDayYear
- ) {
- column.toBeSorted.push(`${tdTextContent}#${i}`);
- columnIndexAndTableRow[`${tdTextContent}#${i}`] = tr.outerHTML;
- }
- } else {
- // Fill in blank table cells dict key with filler value.
- column.toBeSorted.push(`${fillValue}#${i}`);
- columnIndexAndTableRow[`${fillValue}#${i}`] = tr.outerHTML;
- }
- }
+ function strLocaleCompare(str1, str2) {
+ return str1.localeCompare(
+ str2,
+ navigator.languages[0] || navigator.language,
+ { numeric: !isAlphaSort, ignorePunctuation: !isPunctSort }
+ );
+ }
- const isPunctSort = th.classList.contains("punct-sort");
- const isAlphaSort = th.classList.contains("alpha-sort");
- const isNumericSort = th.classList.contains("numeric-sort");
+ function handleNumbers(str1, str2) {
+ let num1, num2;
+ num1 = parseNumberFromString(str1);
+ num2 = parseNumberFromString(str2);
- function parseNumberFromString(str) {
- let num;
- str = str.slice(0, str.indexOf("#"));
- if (str.match(/^\((\d+(?:\.\d+)?)\)$/)) {
- num = -1 * Number(str.slice(1, -1));
- } else {
- num = Number(str);
- }
- return num;
+ if (!isNaN(num1) && !isNaN(num2)) {
+ return num1 - num2;
+ } else {
+ return strLocaleCompare(str1, str2);
}
+ }
- function strLocaleCompare(str1, str2) {
- return str1.localeCompare(
- str2,
- navigator.languages[0] || navigator.language,
- { numeric: !isAlphaSort, ignorePunctuation: !isPunctSort }
- );
+ function sortAscending(a, b) {
+ if (a.includes(`${fillValue}#`)) {
+ return 1;
+ } else if (b.includes(`${fillValue}#`)) {
+ return -1;
+ } else if (isNumericSort) {
+ return handleNumbers(a, b);
+ } else {
+ return strLocaleCompare(a, b);
}
+ }
- function handleNumbers(str1, str2) {
- let num1, num2;
- num1 = parseNumberFromString(str1);
- num2 = parseNumberFromString(str2);
+ function sortDescending(a, b) {
+ return sortAscending(b, a);
+ }
- if (!isNaN(num1) && !isNaN(num2)) {
- return num1 - num2;
- } else {
- return strLocaleCompare(str1, str2);
- }
- }
+ function clearArrows(arrowUp = "▲", arrowDown = "▼") {
+ th.innerHTML = th.innerHTML.replace(arrowUp, "");
+ th.innerHTML = th.innerHTML.replace(arrowDown, "");
+ }
- function sortAscending(a, b) {
- if (a.includes(`${fillValue}#`)) {
- return 1;
- } else if (b.includes(`${fillValue}#`)) {
- return -1;
- } else if (isNumericSort) {
- return handleNumbers(a, b);
- } else {
- return strLocaleCompare(a, b);
- }
- }
+ if (column.toBeSorted[0] === undefined) {
+ return;
+ }
- function sortDescending(a, b) {
- return sortAscending(b, a);
+ function changeTableArrow(arrowDirection) {
+ if (table.hasClass.tableArrows) {
+ clearArrows(arrow.up, arrow.down);
+ th.insertAdjacentText("beforeend", arrowDirection);
}
+ }
- function clearArrows(arrowUp = "▲", arrowDown = "▼") {
- th.innerHTML = th.innerHTML.replace(arrowUp, "");
- th.innerHTML = th.innerHTML.replace(arrowDown, "");
- }
+ function sortColumn(sortDirection) {
+ column.toBeSorted.sort(sortDirection, {
+ numeric: !isAlphaSort,
+ ignorePunctuation: !isPunctSort,
+ });
+ }
- if (column.toBeSorted[0] === undefined) {
- return;
+ if (timesClickedColumn === 1) {
+ if (desc) {
+ changeTableArrow(arrow.down);
+ sortColumn(sortDescending);
+ } else {
+ changeTableArrow(arrow.up);
+ sortColumn(sortAscending);
}
-
- function changeTableArrow(arrowDirection) {
- if (tableArrows) {
- clearArrows(arrowUp, arrowDown);
- th.insertAdjacentText("beforeend", arrowDirection);
- }
+ } else if (timesClickedColumn === 2) {
+ timesClickedColumn = 0;
+ if (desc) {
+ changeTableArrow(arrow.up);
+ sortColumn(sortAscending);
+ } else {
+ changeTableArrow(arrow.down);
+ sortColumn(sortDescending);
}
+ }
+ return timesClickedColumn;
+ }
- function sortColumn(sortDirection) {
- column.toBeSorted.sort(sortDirection, {
- numeric: !isAlphaSort,
- ignorePunctuation: !isPunctSort,
- });
+ function updateFilesize(i, table, tr, column, columnIndex) {
+ if (table.hasClass.cellsSort) {
+ tr.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]];
+ } else {
+ // We do this to sort rows rather than cells:
+ const template = document.createElement("template");
+ template.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]];
+ tr = template.content.firstChild;
+ }
+ let fileSizeInBytesHTML = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).outerHTML;
+ const fileSizeInBytesText = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).textContent;
+
+ const fileSize = column.toBeSorted[i].replace(/#[0-9]*/, "");
+ let prefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi"];
+ let replaced = false;
+ for (let i = 0; i < prefixes.length; ++i) {
+ let nextPrefixMultiplier = 2 ** (10 * (i + 1));
+ if (fileSize < nextPrefixMultiplier) {
+ let prefixMultiplier = 2 ** (10 * i);
+ fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
+ fileSizeInBytesText,
+ `${(fileSize / prefixMultiplier).toFixed(2)} ${prefixes[i]}B`
+ );
+ replaced = true;
+ break;
}
+ }
+ if (!replaced) {
+ fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
+ fileSizeInBytesText,
+ "NaN"
+ );
+ }
+ tr.querySelectorAll("td").item(columnIndex).innerHTML = fileSizeInBytesHTML;
+ return table.hasClass.cellsSort ? tr.innerHTML : tr.outerHTML;
+ }
- if (timesClickedColumn === 1) {
- if (desc) {
- changeTableArrow(arrowDown);
- sortColumn(sortDescending);
+ function updateTable(tableProperties) {
+ const { column, table, columnIndex, hasThClass } = tableProperties;
+ for (let [i, tr] of table.visibleRows.entries()) {
+ if (hasThClass.fileSize) {
+ if (table.hasClass.cellsSort) {
+ tr.innerHTML = updateFilesize(i, table, tr, column, columnIndex);
} else {
- changeTableArrow(arrowUp);
- sortColumn(sortAscending);
+ tr.outerHTML = updateFilesize(i, table, tr, column, columnIndex);
}
- } else if (timesClickedColumn === 2) {
- timesClickedColumn = 0;
- if (desc) {
- changeTableArrow(arrowUp);
- sortColumn(sortAscending);
+ } else if (!hasThClass.fileSize) {
+ if (table.hasClass.cellsSort) {
+ tr.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]];
} else {
- changeTableArrow(arrowDown);
- sortColumn(sortDescending);
+ tr.outerHTML = columnIndexAndTableRow[column.toBeSorted[i]];
}
}
}
+ }
- function updateTable(tableProperties) {
- const { tableRows, column, hasThClass } = tableProperties;
- for (let [i, tr] of tableRows.entries()) {
- if (hasThClass.fileSize) {
- tr.innerHTML = fileSizeColumnTextAndRow[column.toBeSorted[i]];
- let fileSizeInBytesHTML = tr
- .querySelectorAll("td")
- .item(columnIndex).innerHTML;
- const fileSizeInBytesText = tr
- .querySelectorAll("td")
- .item(columnIndex).textContent;
- // Remove the unique identifyer for duplicate values(#number).
- column.toBeSorted[i] = column.toBeSorted[i].replace(/#[0-9]*/, "");
- const fileSize = parseFloat(column.toBeSorted[i]);
- let prefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi"];
- let replaced = false;
- for (let i = 0; i < prefixes.length; ++i) {
- let nextPrefixMultiplier = 2 ** (10 * (i + 1));
- if (fileSize < nextPrefixMultiplier) {
- let prefixMultiplier = 2 ** (10 * i);
- fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
- fileSizeInBytesText,
- `${(fileSize / prefixMultiplier).toFixed(2)} ${prefixes[i]}B`
- );
- replaced = true;
- break;
- }
- }
- if (!replaced) {
- fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
- fileSizeInBytesText,
- "NaN"
- );
- }
- tr.querySelectorAll("td").item(columnIndex).innerHTML =
- fileSizeInBytesHTML;
- } else if (!hasThClass.fileSize) {
- tr.outerHTML = columnIndexAndTableRow[column.toBeSorted[i]];
- }
+ function getColSpanData(headers, column) {
+ headers.forEach((th, index) => {
+ column.span[index] = th.colSpan;
+ if (index === 0) column.spanSum[index] = th.colSpan;
+ else column.spanSum[index] = column.spanSum[index - 1] + th.colSpan;
+ });
+ }
+
+ function rememberSort(columnIndexesClicked, timesClickedColumn, columnIndex) {
+ // if user clicked different column from first column reset times clicked.
+ columnIndexesClicked.push(columnIndex);
+ if (timesClickedColumn === 1 && columnIndexesClicked.length > 1) {
+ const lastColumnClicked =
+ columnIndexesClicked[columnIndexesClicked.length - 1];
+ const secondLastColumnClicked =
+ columnIndexesClicked[columnIndexesClicked.length - 2];
+ if (lastColumnClicked !== secondLastColumnClicked) {
+ columnIndexesClicked.shift();
+ timesClickedColumn = 0;
}
}
+ return timesClickedColumn;
+ }
+
+ function makeEachColumnSortable(
+ th,
+ headerIndex,
+ columnIndex,
+ table,
+ columnIndexesClicked
+ ) {
+ const desc = th.classList.contains("order-by-desc");
+ const arrow = { up: " ▲", down: " ▼" };
+ const fillValue = "!X!Y!Z!";
+
+ if (desc && table.hasClass.tableArrows) {
+ th.insertAdjacentText("beforeend", arrow.down);
+ } else if (table.hasClass.tableArrows) {
+ th.insertAdjacentText("beforeend", arrow.up);
+ }
let timesClickedColumn = 0;
+ const column = {
+ getColumn: function getColumn(tr, colSpanSum, colSpanData) {
+ return tr
+ .querySelectorAll("td")
+ .item(
+ colSpanData[columnIndex] === 1
+ ? colSpanSum[columnIndex] - 1
+ : colSpanSum[columnIndex] - colSpanData[columnIndex]
+ );
+ },
+ };
th.addEventListener("click", function () {
- const column = {
- toBeSorted: [],
- span: {},
- spanSum: {},
- };
+ column.toBeSorted = [];
+ column.span = {};
+ column.spanSum = {};
+ getColSpanData(table.headers[headerIndex], column);
table.visibleRows = Array.prototype.filter.call(
- table.body.querySelectorAll("tr"),
+ table.bodies.item(headerIndex).querySelectorAll("tr"),
(tr) => {
return tr.style.display !== "none";
}
);
- getColSpanData(table.headers, column);
-
- const isRememberSort = sortableTable.classList.contains("remember-sort");
- if (!isRememberSort) {
- timesClickedColumn = rememberSort();
+ if (!table.hasClass.rememberSort) {
+ timesClickedColumn = rememberSort(
+ columnIndexesClicked,
+ timesClickedColumn,
+ columnIndex
+ );
}
timesClickedColumn += 1;
@@ -501,13 +568,13 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
};
if (hasThClass.dataSort) {
- sortDataAttributes(table.visibleRows, column);
+ sortDataAttributes(table, column);
}
if (hasThClass.fileSize) {
- sortFileSize(table.visibleRows, column);
+ sortFileSize(table, column, columnIndex, fillValue);
}
if (hasThClass.runtime) {
- sortByRuntime(table.visibleRows, column);
+ sortByRuntime(table, column);
}
const isSortDates = {
@@ -517,20 +584,27 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
};
// pick mdy first to override the inferred default class which is dmy.
if (isSortDates.monthDayYear) {
- sortDates("mdy", table.visibleRows, column);
+ sortDates("mdy", table, column);
} else if (isSortDates.yearMonthDay) {
- sortDates("ymd", table.visibleRows, column);
+ sortDates("ymd", table, column);
} else if (isSortDates.dayMonthYear) {
- sortDates("dmy", table.visibleRows, column);
+ sortDates("dmy", table, column);
}
const tableProperties = {
+ table,
tableRows: table.visibleRows,
+ fillValue,
column,
+ columnIndex,
+ th,
hasThClass,
isSortDates,
+ desc,
+ timesClickedColumn,
+ arrow,
};
- getTableData(tableProperties);
+ timesClickedColumn = getTableData(tableProperties, timesClickedColumn);
updateTable(tableProperties);
});
diff --git a/browser-extensions/firefox/manifest.json b/browser-extensions/firefox/manifest.json
index 5dbff19..5efa850 100644
--- a/browser-extensions/firefox/manifest.json
+++ b/browser-extensions/firefox/manifest.json
@@ -2,7 +2,7 @@
"manifest_version": 2,
"author": "Lee Wannacott",
"name": "table-sort-js",
- "version": "1.16.0",
+ "version": "1.17.0",
"description": "Makes tables sortable using table-sort-js: https://github.com/LeeWannacott/table-sort-js",
"icons": { "48": "icons/t.png" },
"browser_action": {
diff --git a/browser-extensions/firefox/table-sort-js.zip b/browser-extensions/firefox/table-sort-js.zip
index fc637c6..91c195d 100644
Binary files a/browser-extensions/firefox/table-sort-js.zip and b/browser-extensions/firefox/table-sort-js.zip differ
diff --git a/browser-extensions/firefox/table-sort.js b/browser-extensions/firefox/table-sort.js
index 4b19ea0..a9a7e31 100644
--- a/browser-extensions/firefox/table-sort.js
+++ b/browser-extensions/firefox/table-sort.js
@@ -28,7 +28,6 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
const [getTagTable] = getHTMLTables();
const columnIndexAndTableRow = {};
- const fileSizeColumnTextAndRow = {};
for (let table of getTagTable) {
if (table.classList.contains("table-sort")) {
makeTableSortable(table);
@@ -46,16 +45,18 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
sortableTable.insertBefore(createTableHead, sortableTable.firstChild);
}
- function getTableBody(sortableTable) {
+ function getTableBodies(sortableTable) {
if (sortableTable.getElementsByTagName("thead").length === 0) {
createMissingTableHead(sortableTable);
if (sortableTable.querySelectorAll("tbody").length > 1) {
+ // Why index 1?; I don't remember
return sortableTable.querySelectorAll("tbody")[1];
} else {
- return sortableTable.querySelector("tbody");
+ return sortableTable.querySelectorAll("tbody");
}
} else {
- return sortableTable.querySelector("tbody");
+ // if or exists below the browser will make |
+ return sortableTable.querySelectorAll("tbody");
}
}
@@ -108,389 +109,455 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
function makeTableSortable(sortableTable) {
const table = {
- body: getTableBody(sortableTable),
- head: sortableTable.querySelector("thead"),
+ bodies: getTableBodies(sortableTable),
+ theads: sortableTable.querySelectorAll("thead"),
+ rows: [],
+ headers: [],
};
- table.headers = table.head.querySelectorAll("th");
- table.rows = table.body.querySelectorAll("tr");
-
- let columnIndexesClicked = [];
+ for (let index of table.bodies.keys()) {
+ if (table.bodies.item(index) == null) {
+ return;
+ }
+ table.headers.push(table.theads.item(index).querySelectorAll("th"));
+ table.rows.push(table.bodies.item(index).querySelectorAll("tr"));
+ }
- const isNoSortClassInference =
- sortableTable.classList.contains("no-class-infer");
+ table.hasClass = {
+ noClassInfer: sortableTable.classList.contains("no-class-infer"),
+ cellsSort: sortableTable.classList.contains("cells-sort"),
+ tableArrows: sortableTable.classList.contains("table-arrows"),
+ rememberSort: sortableTable.classList.contains("remember-sort"),
+ };
- for (let [columnIndex, th] of table.headers.entries()) {
- if (!th.classList.contains("disable-sort")) {
- th.style.cursor = "pointer";
- if (!isNoSortClassInference) {
- inferSortClasses(table.rows, columnIndex, th);
+ for (
+ let headerIndex = 0;
+ headerIndex < table.theads.length;
+ headerIndex++
+ ) {
+ let columnIndexesClicked = [];
+ for (let [columnIndex, th] of table.headers[headerIndex].entries()) {
+ if (!th.classList.contains("disable-sort")) {
+ th.style.cursor = "pointer";
+ if (!table.hasClass.noClassInfer) {
+ inferSortClasses(table.rows[headerIndex], columnIndex, th);
+ }
+ makeEachColumnSortable(
+ th,
+ headerIndex,
+ columnIndex,
+ table,
+ columnIndexesClicked
+ );
}
- makeEachColumnSortable(
- th,
- columnIndex,
- table,
- sortableTable,
- columnIndexesClicked
- );
}
}
}
- function makeEachColumnSortable(
- th,
- columnIndex,
- table,
- sortableTable,
- columnIndexesClicked
- ) {
- const desc = th.classList.contains("order-by-desc");
- const tableArrows = sortableTable.classList.contains("table-arrows");
- const [arrowUp, arrowDown] = [" ▲", " ▼"];
- const fillValue = "!X!Y!Z!";
-
- if (desc && tableArrows) {
- th.insertAdjacentText("beforeend", arrowDown);
- } else if (tableArrows) {
- th.insertAdjacentText("beforeend", arrowUp);
+ function cellsOrRows(table, tr) {
+ if (table.hasClass.cellsSort) {
+ return tr.innerHTML;
+ } else {
+ return tr.outerHTML;
}
+ }
- function sortDataAttributes(tableRows, column) {
- for (let [i, tr] of tableRows.entries()) {
- let dataAttributeTd = getColumn(tr, column.spanSum, column.span).dataset
- .sort;
- column.toBeSorted.push(`${dataAttributeTd}#${i}`);
- columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML;
- }
+ function sortDataAttributes(table, column) {
+ for (let [i, tr] of table.visibleRows.entries()) {
+ let dataAttributeTd = column.getColumn(tr, column.spanSum, column.span)
+ .dataset.sort;
+ column.toBeSorted.push(`${dataAttributeTd}#${i}`);
+ columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr);
}
+ }
- function sortFileSize(tableRows, column) {
- let unitToMultiplier = {
- b: 1,
- kb: 1000,
- kib: 2 ** 10,
- mb: 1e6,
- mib: 2 ** 20,
- gb: 1e9,
- gib: 2 ** 30,
- tb: 1e12,
- tib: 2 ** 40,
- };
- const numberWithUnitType = /([.0-9]+)\s?(B|KB|KiB|MB|MiB|GB|GiB|TB|TiB)/i;
- for (let [i, tr] of tableRows.entries()) {
- let fileSizeTd = tr
- .querySelectorAll("td")
- .item(columnIndex).textContent;
- let match = fileSizeTd.match(numberWithUnitType);
- if (match) {
- let number = parseFloat(match[1]);
- let unit = match[2].toLowerCase();
- let multiplier = unitToMultiplier[unit];
- column.toBeSorted.push(`${number * multiplier}#${i}`);
- } else {
- column.toBeSorted.push(`${fillValue}#${i}`);
- }
+ function sortFileSize(table, column, columnIndex) {
+ let unitToMultiplier = {
+ b: 1,
+ kb: 1000,
+ kib: 2 ** 10,
+ mb: 1e6,
+ mib: 2 ** 20,
+ gb: 1e9,
+ gib: 2 ** 30,
+ tb: 1e12,
+ tib: 2 ** 40,
+ };
+ const numberWithUnitType = /([.0-9]+)\s?(B|KB|KiB|MB|MiB|GB|GiB|TB|TiB)/i;
+ for (let [i, tr] of table.visibleRows.entries()) {
+ let fileSizeTd = tr.querySelectorAll("td").item(columnIndex).textContent;
+ let match = fileSizeTd.match(numberWithUnitType);
+ if (match) {
+ let number = parseFloat(match[1]);
+ let unit = match[2].toLowerCase();
+ let multiplier = unitToMultiplier[unit];
+ column.toBeSorted.push(`${number * multiplier}#${i}`);
+ columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr);
}
}
+ }
- function sortByRuntime(tableRows, column) {
- try {
- for (let [i, tr] of tableRows.entries()) {
- const regexMinutesAndSeconds = /^(\d+h)?\s?(\d+m)?\s?(\d+s)?$/i;
- let columnOfTd = "";
- // TODO: github actions runtime didn't like textContent, tests didn't like innerText?
- if (testingTableSortJS) {
- columnOfTd = getColumn(tr, column.spanSum, column.span).textContent;
- } else {
- columnOfTd = getColumn(tr, column.spanSum, column.span).innerText;
- }
- let match = columnOfTd.match(regexMinutesAndSeconds);
- let [minutesInSeconds, hours, seconds] = [0, 0, 0];
- let timeinSeconds = columnOfTd;
- if (match) {
- const regexHours = match[1];
- if (regexHours) {
- hours = Number(regexHours.replace("h", "")) * 60 * 60;
- }
- const regexMinutes = match[2];
- if (regexMinutes) {
- minutesInSeconds = Number(regexMinutes.replace("m", "")) * 60;
- }
- const regexSeconds = match[3];
- if (regexSeconds) {
- seconds = Number(regexSeconds.replace("s", ""));
+ function sortDates(datesFormat, table, column) {
+ try {
+ for (let [i, tr] of table.visibleRows.entries()) {
+ let columnOfTd, datesRegex;
+ if (datesFormat === "mdy" || datesFormat === "dmy") {
+ datesRegex = /^(\d\d?)[./-](\d\d?)[./-]((\d\d)?\d\d)/;
+ } else if (datesFormat === "ymd") {
+ datesRegex = /^(\d\d\d\d)[./-](\d\d?)[./-](\d\d?)/;
+ }
+ columnOfTd = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).textContent;
+ let match = columnOfTd.match(datesRegex);
+ let [years, days, months] = [0, 0, 0];
+ let numberToSort = columnOfTd;
+ if (match) {
+ const [regPos1, regPos2, regPos3] = [match[1], match[2], match[3]];
+ if (regPos1 && regPos2 && regPos3) {
+ if (datesFormat === "mdy") {
+ [months, days, years] = [regPos1, regPos2, regPos3];
+ } else if (datesFormat === "ymd") {
+ [years, months, days] = [regPos1, regPos2, regPos3];
+ } else {
+ [days, months, years] = [regPos1, regPos2, regPos3];
}
- timeinSeconds = hours + minutesInSeconds + seconds;
}
- column.toBeSorted.push(`${timeinSeconds}#${i}`);
- columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML;
+ numberToSort = Number(
+ years +
+ String(months).padStart(2, "0") +
+ String(days).padStart(2, "0")
+ );
}
- } catch (e) {
- console.log(e);
+ column.toBeSorted.push(`${numberToSort}#${i}`);
+ columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr);
}
+ } catch (e) {
+ console.log(e);
}
+ }
- function sortDates(datesFormat, tableRows, column) {
- try {
- for (let [i, tr] of tableRows.entries()) {
- let columnOfTd, datesRegex;
- if (datesFormat === "mdy" || datesFormat === "dmy") {
- datesRegex = /^(\d\d?)[./-](\d\d?)[./-]((\d\d)?\d\d)/;
- } else if (datesFormat === "ymd") {
- datesRegex = /^(\d\d\d\d)[./-](\d\d?)[./-](\d\d?)/;
+ function sortByRuntime(table, column) {
+ try {
+ for (let [i, tr] of table.visibleRows.entries()) {
+ const regexMinutesAndSeconds = /^(\d+h)?\s?(\d+m)?\s?(\d+s)?$/i;
+ let columnOfTd = "";
+ // TODO: github actions runtime didn't like textContent, tests didn't like innerText?
+ if (testingTableSortJS) {
+ columnOfTd = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).textContent;
+ } else {
+ columnOfTd = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).innerText;
+ }
+ let match = columnOfTd.match(regexMinutesAndSeconds);
+ let [minutesInSeconds, hours, seconds] = [0, 0, 0];
+ let timeinSeconds = columnOfTd;
+ if (match) {
+ const regexHours = match[1];
+ if (regexHours) {
+ hours = Number(regexHours.replace("h", "")) * 60 * 60;
}
- columnOfTd = getColumn(tr, column.spanSum, column.span).textContent;
- let match = columnOfTd.match(datesRegex);
- let [years, days, months] = [0, 0, 0];
- let numberToSort = columnOfTd;
- if (match) {
- const [regPos1, regPos2, regPos3] = [match[1], match[2], match[3]];
- if (regPos1 && regPos2 && regPos3) {
- if (datesFormat === "mdy") {
- [months, days, years] = [regPos1, regPos2, regPos3];
- } else if (datesFormat === "ymd") {
- [years, months, days] = [regPos1, regPos2, regPos3];
- } else {
- [days, months, years] = [regPos1, regPos2, regPos3];
- }
- }
- numberToSort = Number(
- years +
- String(months).padStart(2, "0") +
- String(days).padStart(2, "0")
- );
+ const regexMinutes = match[2];
+ if (regexMinutes) {
+ minutesInSeconds = Number(regexMinutes.replace("m", "")) * 60;
+ }
+ const regexSeconds = match[3];
+ if (regexSeconds) {
+ seconds = Number(regexSeconds.replace("s", ""));
}
- column.toBeSorted.push(`${numberToSort}#${i}`);
- columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML;
+ timeinSeconds = hours + minutesInSeconds + seconds;
}
- } catch (e) {
- console.log(e);
+ column.toBeSorted.push(`${timeinSeconds}#${i}`);
+ columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr);
}
+ } catch (e) {
+ console.log(e);
}
+ }
- function rememberSort() {
- // if user clicked different column from first column reset times clicked.
- columnIndexesClicked.push(columnIndex);
- if (timesClickedColumn === 1 && columnIndexesClicked.length > 1) {
- const lastColumnClicked =
- columnIndexesClicked[columnIndexesClicked.length - 1];
- const secondLastColumnClicked =
- columnIndexesClicked[columnIndexesClicked.length - 2];
- if (lastColumnClicked !== secondLastColumnClicked) {
- columnIndexesClicked.shift();
- timesClickedColumn = 0;
+ function getTableData(tableProperties, timesClickedColumn) {
+ const {
+ table,
+ tableRows,
+ fillValue,
+ column,
+ th,
+ hasThClass,
+ isSortDates,
+ desc,
+ arrow,
+ } = tableProperties;
+ for (let [i, tr] of tableRows.entries()) {
+ let tdTextContent = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).textContent;
+ if (tdTextContent.length === 0) {
+ tdTextContent = "";
+ }
+ if (tdTextContent.trim() !== "") {
+ if (
+ !hasThClass.fileSize &&
+ !hasThClass.dataSort &&
+ !hasThClass.runtime &&
+ !hasThClass.filesize &&
+ !isSortDates.dayMonthYear &&
+ !isSortDates.yearMonthDay &&
+ !isSortDates.monthDayYear
+ ) {
+ column.toBeSorted.push(`${tdTextContent}#${i}`);
+ columnIndexAndTableRow[`${tdTextContent}#${i}`] = cellsOrRows(
+ table,
+ tr
+ );
}
+ } else {
+ // Fill in blank table cells dict key with filler value.
+ column.toBeSorted.push(`${fillValue}#${i}`);
+ columnIndexAndTableRow[`${fillValue}#${i}`] = cellsOrRows(table, tr);
}
- return timesClickedColumn;
}
- function getColSpanData(headers, column) {
- headers.forEach((th, index) => {
- column.span[index] = th.colSpan;
- if (index === 0) column.spanSum[index] = th.colSpan;
- else column.spanSum[index] = column.spanSum[index - 1] + th.colSpan;
- });
- }
+ const isPunctSort = th.classList.contains("punct-sort");
+ const isAlphaSort = th.classList.contains("alpha-sort");
+ const isNumericSort = th.classList.contains("numeric-sort");
- function getColumn(tr, colSpanSum, colSpanData) {
- return tr
- .querySelectorAll("td")
- .item(
- colSpanData[columnIndex] === 1
- ? colSpanSum[columnIndex] - 1
- : colSpanSum[columnIndex] - colSpanData[columnIndex]
- );
+ function parseNumberFromString(str) {
+ let num;
+ str = str.slice(0, str.indexOf("#"));
+ if (str.match(/^\((\d+(?:\.\d+)?)\)$/)) {
+ num = -1 * Number(str.slice(1, -1));
+ } else {
+ num = Number(str);
+ }
+ return num;
}
- function getTableData(tableProperties) {
- const { tableRows, column, hasThClass, isSortDates } = tableProperties;
- for (let [i, tr] of tableRows.entries()) {
- let tdTextContent = getColumn(
- tr,
- column.spanSum,
- column.span
- ).textContent;
- if (tdTextContent.length === 0) {
- tdTextContent = "";
- }
- if (tdTextContent.trim() !== "") {
- if (hasThClass.fileSize) {
- fileSizeColumnTextAndRow[column.toBeSorted[i]] = tr.outerHTML;
- }
- // These classes already handle pushing to column and setting the tr html.
- if (
- !hasThClass.fileSize &&
- !hasThClass.dataSort &&
- !hasThClass.runtime &&
- !isSortDates.dayMonthYear &&
- !isSortDates.yearMonthDay &&
- !isSortDates.monthDayYear
- ) {
- column.toBeSorted.push(`${tdTextContent}#${i}`);
- columnIndexAndTableRow[`${tdTextContent}#${i}`] = tr.outerHTML;
- }
- } else {
- // Fill in blank table cells dict key with filler value.
- column.toBeSorted.push(`${fillValue}#${i}`);
- columnIndexAndTableRow[`${fillValue}#${i}`] = tr.outerHTML;
- }
- }
+ function strLocaleCompare(str1, str2) {
+ return str1.localeCompare(
+ str2,
+ navigator.languages[0] || navigator.language,
+ { numeric: !isAlphaSort, ignorePunctuation: !isPunctSort }
+ );
+ }
- const isPunctSort = th.classList.contains("punct-sort");
- const isAlphaSort = th.classList.contains("alpha-sort");
- const isNumericSort = th.classList.contains("numeric-sort");
+ function handleNumbers(str1, str2) {
+ let num1, num2;
+ num1 = parseNumberFromString(str1);
+ num2 = parseNumberFromString(str2);
- function parseNumberFromString(str) {
- let num;
- str = str.slice(0, str.indexOf("#"));
- if (str.match(/^\((\d+(?:\.\d+)?)\)$/)) {
- num = -1 * Number(str.slice(1, -1));
- } else {
- num = Number(str);
- }
- return num;
+ if (!isNaN(num1) && !isNaN(num2)) {
+ return num1 - num2;
+ } else {
+ return strLocaleCompare(str1, str2);
}
+ }
- function strLocaleCompare(str1, str2) {
- return str1.localeCompare(
- str2,
- navigator.languages[0] || navigator.language,
- { numeric: !isAlphaSort, ignorePunctuation: !isPunctSort }
- );
+ function sortAscending(a, b) {
+ if (a.includes(`${fillValue}#`)) {
+ return 1;
+ } else if (b.includes(`${fillValue}#`)) {
+ return -1;
+ } else if (isNumericSort) {
+ return handleNumbers(a, b);
+ } else {
+ return strLocaleCompare(a, b);
}
+ }
- function handleNumbers(str1, str2) {
- let num1, num2;
- num1 = parseNumberFromString(str1);
- num2 = parseNumberFromString(str2);
+ function sortDescending(a, b) {
+ return sortAscending(b, a);
+ }
- if (!isNaN(num1) && !isNaN(num2)) {
- return num1 - num2;
- } else {
- return strLocaleCompare(str1, str2);
- }
- }
+ function clearArrows(arrowUp = "▲", arrowDown = "▼") {
+ th.innerHTML = th.innerHTML.replace(arrowUp, "");
+ th.innerHTML = th.innerHTML.replace(arrowDown, "");
+ }
- function sortAscending(a, b) {
- if (a.includes(`${fillValue}#`)) {
- return 1;
- } else if (b.includes(`${fillValue}#`)) {
- return -1;
- } else if (isNumericSort) {
- return handleNumbers(a, b);
- } else {
- return strLocaleCompare(a, b);
- }
- }
+ if (column.toBeSorted[0] === undefined) {
+ return;
+ }
- function sortDescending(a, b) {
- return sortAscending(b, a);
+ function changeTableArrow(arrowDirection) {
+ if (table.hasClass.tableArrows) {
+ clearArrows(arrow.up, arrow.down);
+ th.insertAdjacentText("beforeend", arrowDirection);
}
+ }
- function clearArrows(arrowUp = "▲", arrowDown = "▼") {
- th.innerHTML = th.innerHTML.replace(arrowUp, "");
- th.innerHTML = th.innerHTML.replace(arrowDown, "");
- }
+ function sortColumn(sortDirection) {
+ column.toBeSorted.sort(sortDirection, {
+ numeric: !isAlphaSort,
+ ignorePunctuation: !isPunctSort,
+ });
+ }
- if (column.toBeSorted[0] === undefined) {
- return;
+ if (timesClickedColumn === 1) {
+ if (desc) {
+ changeTableArrow(arrow.down);
+ sortColumn(sortDescending);
+ } else {
+ changeTableArrow(arrow.up);
+ sortColumn(sortAscending);
}
-
- function changeTableArrow(arrowDirection) {
- if (tableArrows) {
- clearArrows(arrowUp, arrowDown);
- th.insertAdjacentText("beforeend", arrowDirection);
- }
+ } else if (timesClickedColumn === 2) {
+ timesClickedColumn = 0;
+ if (desc) {
+ changeTableArrow(arrow.up);
+ sortColumn(sortAscending);
+ } else {
+ changeTableArrow(arrow.down);
+ sortColumn(sortDescending);
}
+ }
+ return timesClickedColumn;
+ }
- function sortColumn(sortDirection) {
- column.toBeSorted.sort(sortDirection, {
- numeric: !isAlphaSort,
- ignorePunctuation: !isPunctSort,
- });
+ function updateFilesize(i, table, tr, column, columnIndex) {
+ if (table.hasClass.cellsSort) {
+ tr.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]];
+ } else {
+ // We do this to sort rows rather than cells:
+ const template = document.createElement("template");
+ template.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]];
+ tr = template.content.firstChild;
+ }
+ let fileSizeInBytesHTML = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).outerHTML;
+ const fileSizeInBytesText = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).textContent;
+
+ const fileSize = column.toBeSorted[i].replace(/#[0-9]*/, "");
+ let prefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi"];
+ let replaced = false;
+ for (let i = 0; i < prefixes.length; ++i) {
+ let nextPrefixMultiplier = 2 ** (10 * (i + 1));
+ if (fileSize < nextPrefixMultiplier) {
+ let prefixMultiplier = 2 ** (10 * i);
+ fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
+ fileSizeInBytesText,
+ `${(fileSize / prefixMultiplier).toFixed(2)} ${prefixes[i]}B`
+ );
+ replaced = true;
+ break;
}
+ }
+ if (!replaced) {
+ fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
+ fileSizeInBytesText,
+ "NaN"
+ );
+ }
+ tr.querySelectorAll("td").item(columnIndex).innerHTML = fileSizeInBytesHTML;
+ return table.hasClass.cellsSort ? tr.innerHTML : tr.outerHTML;
+ }
- if (timesClickedColumn === 1) {
- if (desc) {
- changeTableArrow(arrowDown);
- sortColumn(sortDescending);
+ function updateTable(tableProperties) {
+ const { column, table, columnIndex, hasThClass } = tableProperties;
+ for (let [i, tr] of table.visibleRows.entries()) {
+ if (hasThClass.fileSize) {
+ if (table.hasClass.cellsSort) {
+ tr.innerHTML = updateFilesize(i, table, tr, column, columnIndex);
} else {
- changeTableArrow(arrowUp);
- sortColumn(sortAscending);
+ tr.outerHTML = updateFilesize(i, table, tr, column, columnIndex);
}
- } else if (timesClickedColumn === 2) {
- timesClickedColumn = 0;
- if (desc) {
- changeTableArrow(arrowUp);
- sortColumn(sortAscending);
+ } else if (!hasThClass.fileSize) {
+ if (table.hasClass.cellsSort) {
+ tr.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]];
} else {
- changeTableArrow(arrowDown);
- sortColumn(sortDescending);
+ tr.outerHTML = columnIndexAndTableRow[column.toBeSorted[i]];
}
}
}
+ }
- function updateTable(tableProperties) {
- const { tableRows, column, hasThClass } = tableProperties;
- for (let [i, tr] of tableRows.entries()) {
- if (hasThClass.fileSize) {
- tr.innerHTML = fileSizeColumnTextAndRow[column.toBeSorted[i]];
- let fileSizeInBytesHTML = tr
- .querySelectorAll("td")
- .item(columnIndex).innerHTML;
- const fileSizeInBytesText = tr
- .querySelectorAll("td")
- .item(columnIndex).textContent;
- // Remove the unique identifyer for duplicate values(#number).
- column.toBeSorted[i] = column.toBeSorted[i].replace(/#[0-9]*/, "");
- const fileSize = parseFloat(column.toBeSorted[i]);
- let prefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi"];
- let replaced = false;
- for (let i = 0; i < prefixes.length; ++i) {
- let nextPrefixMultiplier = 2 ** (10 * (i + 1));
- if (fileSize < nextPrefixMultiplier) {
- let prefixMultiplier = 2 ** (10 * i);
- fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
- fileSizeInBytesText,
- `${(fileSize / prefixMultiplier).toFixed(2)} ${prefixes[i]}B`
- );
- replaced = true;
- break;
- }
- }
- if (!replaced) {
- fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
- fileSizeInBytesText,
- "NaN"
- );
- }
- tr.querySelectorAll("td").item(columnIndex).innerHTML =
- fileSizeInBytesHTML;
- } else if (!hasThClass.fileSize) {
- tr.outerHTML = columnIndexAndTableRow[column.toBeSorted[i]];
- }
+ function getColSpanData(headers, column) {
+ headers.forEach((th, index) => {
+ column.span[index] = th.colSpan;
+ if (index === 0) column.spanSum[index] = th.colSpan;
+ else column.spanSum[index] = column.spanSum[index - 1] + th.colSpan;
+ });
+ }
+
+ function rememberSort(columnIndexesClicked, timesClickedColumn, columnIndex) {
+ // if user clicked different column from first column reset times clicked.
+ columnIndexesClicked.push(columnIndex);
+ if (timesClickedColumn === 1 && columnIndexesClicked.length > 1) {
+ const lastColumnClicked =
+ columnIndexesClicked[columnIndexesClicked.length - 1];
+ const secondLastColumnClicked =
+ columnIndexesClicked[columnIndexesClicked.length - 2];
+ if (lastColumnClicked !== secondLastColumnClicked) {
+ columnIndexesClicked.shift();
+ timesClickedColumn = 0;
}
}
+ return timesClickedColumn;
+ }
+
+ function makeEachColumnSortable(
+ th,
+ headerIndex,
+ columnIndex,
+ table,
+ columnIndexesClicked
+ ) {
+ const desc = th.classList.contains("order-by-desc");
+ const arrow = { up: " ▲", down: " ▼" };
+ const fillValue = "!X!Y!Z!";
+
+ if (desc && table.hasClass.tableArrows) {
+ th.insertAdjacentText("beforeend", arrow.down);
+ } else if (table.hasClass.tableArrows) {
+ th.insertAdjacentText("beforeend", arrow.up);
+ }
let timesClickedColumn = 0;
+ const column = {
+ getColumn: function getColumn(tr, colSpanSum, colSpanData) {
+ return tr
+ .querySelectorAll("td")
+ .item(
+ colSpanData[columnIndex] === 1
+ ? colSpanSum[columnIndex] - 1
+ : colSpanSum[columnIndex] - colSpanData[columnIndex]
+ );
+ },
+ };
th.addEventListener("click", function () {
- const column = {
- toBeSorted: [],
- span: {},
- spanSum: {},
- };
+ column.toBeSorted = [];
+ column.span = {};
+ column.spanSum = {};
+ getColSpanData(table.headers[headerIndex], column);
table.visibleRows = Array.prototype.filter.call(
- table.body.querySelectorAll("tr"),
+ table.bodies.item(headerIndex).querySelectorAll("tr"),
(tr) => {
return tr.style.display !== "none";
}
);
- getColSpanData(table.headers, column);
-
- const isRememberSort = sortableTable.classList.contains("remember-sort");
- if (!isRememberSort) {
- timesClickedColumn = rememberSort();
+ if (!table.hasClass.rememberSort) {
+ timesClickedColumn = rememberSort(
+ columnIndexesClicked,
+ timesClickedColumn,
+ columnIndex
+ );
}
timesClickedColumn += 1;
@@ -501,13 +568,13 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
};
if (hasThClass.dataSort) {
- sortDataAttributes(table.visibleRows, column);
+ sortDataAttributes(table, column);
}
if (hasThClass.fileSize) {
- sortFileSize(table.visibleRows, column);
+ sortFileSize(table, column, columnIndex, fillValue);
}
if (hasThClass.runtime) {
- sortByRuntime(table.visibleRows, column);
+ sortByRuntime(table, column);
}
const isSortDates = {
@@ -517,20 +584,27 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
};
// pick mdy first to override the inferred default class which is dmy.
if (isSortDates.monthDayYear) {
- sortDates("mdy", table.visibleRows, column);
+ sortDates("mdy", table, column);
} else if (isSortDates.yearMonthDay) {
- sortDates("ymd", table.visibleRows, column);
+ sortDates("ymd", table, column);
} else if (isSortDates.dayMonthYear) {
- sortDates("dmy", table.visibleRows, column);
+ sortDates("dmy", table, column);
}
const tableProperties = {
+ table,
tableRows: table.visibleRows,
+ fillValue,
column,
+ columnIndex,
+ th,
hasThClass,
isSortDates,
+ desc,
+ timesClickedColumn,
+ arrow,
};
- getTableData(tableProperties);
+ timesClickedColumn = getTableData(tableProperties, timesClickedColumn);
updateTable(tableProperties);
});
diff --git a/npm/README.md b/npm/README.md
index 6e0d26f..37ca0b9 100644
--- a/npm/README.md
+++ b/npm/README.md
@@ -3,6 +3,7 @@
[![jsDeliver downloads](https://data.jsdelivr.com/v1/package/npm/table-sort-js/badge)](https://www.jsdelivr.com/package/npm/table-sort-js)
![repo size](https://img.shields.io/github/repo-size/leewannacott/table-sort-js)
![MIT licence](https://img.shields.io/github/license/LeeWannacott/table-sort-js)
+[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
![build status](https://img.shields.io/github/actions/workflow/status/leewannacott/table-sort-js/jest.yml?branch=master)
# TABLE-SORT-JS.
@@ -17,39 +18,31 @@
## Install instructions.
-- Option 1: Install from npm:
+- Option 1: Load as script from a Content Delivery Network (CDN):
```javascript
-npm install table-sort-js
+
```
+Or Minified (smaller size, but harder to debug!):
+
```javascript
-import tableSort from "table-sort-js/table-sort.js";
+
```
-Examples on using table-sort-js with frontend frameworks such as [React.js](https://leewannacott.github.io/table-sort-js/docs/react.html) and [Vue.js](https://leewannacott.github.io/table-sort-js/docs/vue.html)
+Example on how to use table-sort-js with [HTML](https://leewannacott.github.io/table-sort-js/docs/html5.html)
-- Option 2: Load as script from a Content Delivery Network (CDN):
+- Option 2: Install from npm:
```javascript
-
+npm install table-sort-js
```
-Or Minified (smaller size, but harder to debug!):
-
```javascript
-
+import tableSort from "table-sort-js/table-sort.js";
```
-Refer to the documenation for examples on how to use table-sort-js with [HTML](https://leewannacott.github.io/table-sort-js/docs/html5.html)
-
-- Option 3: Download [table-sort.js](https://cdn.jsdelivr.net/npm/table-sort-js@latest/table-sort.js) (Select save as.), or download a [minified version](https://cdn.jsdelivr.net/npm/table-sort-js@latest/table-sort.min.js) (~5kB)
-
-Then rename and add the following script before your HTML table:
-
-```html
-
-```
+Examples on using table-sort-js with frontend frameworks such as [React.js](https://leewannacott.github.io/table-sort-js/docs/react.html) and [Vue.js](https://leewannacott.github.io/table-sort-js/docs/vue.html)
## To make tables sortable:
@@ -64,6 +57,7 @@ Then rename and add the following script before your HTML table:
| "no-class-infer" | Turns off inference for adding sort classes automatically i.e (file-size-sort, runtime-sort, dates-dmy-sort). |
| "table-arrows" | Display ascending or descending triangles. |
| "remember-sort" | If clicking on different columns remembers sort of the original column. |
+| "cells-sort" | sort cells (td) rather than table rows (tr); useful for keeping table rows with classes/attributes in place. |
diff --git a/npm/package.json b/npm/package.json
index 4c8819d..4c9ce6b 100644
--- a/npm/package.json
+++ b/npm/package.json
@@ -1,6 +1,6 @@
{
"name": "table-sort-js",
- "version": "1.16.0",
+ "version": "1.17.0",
"description": "A JavaScript client-side HTML table sorting library with no dependencies required.",
"license": "MIT",
"repository": "LeeWannacott/table-sort-js",
diff --git a/npm/table-sort.js b/npm/table-sort.js
index 4b19ea0..a9a7e31 100644
--- a/npm/table-sort.js
+++ b/npm/table-sort.js
@@ -28,7 +28,6 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
const [getTagTable] = getHTMLTables();
const columnIndexAndTableRow = {};
- const fileSizeColumnTextAndRow = {};
for (let table of getTagTable) {
if (table.classList.contains("table-sort")) {
makeTableSortable(table);
@@ -46,16 +45,18 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
sortableTable.insertBefore(createTableHead, sortableTable.firstChild);
}
- function getTableBody(sortableTable) {
+ function getTableBodies(sortableTable) {
if (sortableTable.getElementsByTagName("thead").length === 0) {
createMissingTableHead(sortableTable);
if (sortableTable.querySelectorAll("tbody").length > 1) {
+ // Why index 1?; I don't remember
return sortableTable.querySelectorAll("tbody")[1];
} else {
- return sortableTable.querySelector("tbody");
+ return sortableTable.querySelectorAll("tbody");
}
} else {
- return sortableTable.querySelector("tbody");
+ // if or exists below the browser will make |
+ return sortableTable.querySelectorAll("tbody");
}
}
@@ -108,389 +109,455 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
function makeTableSortable(sortableTable) {
const table = {
- body: getTableBody(sortableTable),
- head: sortableTable.querySelector("thead"),
+ bodies: getTableBodies(sortableTable),
+ theads: sortableTable.querySelectorAll("thead"),
+ rows: [],
+ headers: [],
};
- table.headers = table.head.querySelectorAll("th");
- table.rows = table.body.querySelectorAll("tr");
-
- let columnIndexesClicked = [];
+ for (let index of table.bodies.keys()) {
+ if (table.bodies.item(index) == null) {
+ return;
+ }
+ table.headers.push(table.theads.item(index).querySelectorAll("th"));
+ table.rows.push(table.bodies.item(index).querySelectorAll("tr"));
+ }
- const isNoSortClassInference =
- sortableTable.classList.contains("no-class-infer");
+ table.hasClass = {
+ noClassInfer: sortableTable.classList.contains("no-class-infer"),
+ cellsSort: sortableTable.classList.contains("cells-sort"),
+ tableArrows: sortableTable.classList.contains("table-arrows"),
+ rememberSort: sortableTable.classList.contains("remember-sort"),
+ };
- for (let [columnIndex, th] of table.headers.entries()) {
- if (!th.classList.contains("disable-sort")) {
- th.style.cursor = "pointer";
- if (!isNoSortClassInference) {
- inferSortClasses(table.rows, columnIndex, th);
+ for (
+ let headerIndex = 0;
+ headerIndex < table.theads.length;
+ headerIndex++
+ ) {
+ let columnIndexesClicked = [];
+ for (let [columnIndex, th] of table.headers[headerIndex].entries()) {
+ if (!th.classList.contains("disable-sort")) {
+ th.style.cursor = "pointer";
+ if (!table.hasClass.noClassInfer) {
+ inferSortClasses(table.rows[headerIndex], columnIndex, th);
+ }
+ makeEachColumnSortable(
+ th,
+ headerIndex,
+ columnIndex,
+ table,
+ columnIndexesClicked
+ );
}
- makeEachColumnSortable(
- th,
- columnIndex,
- table,
- sortableTable,
- columnIndexesClicked
- );
}
}
}
- function makeEachColumnSortable(
- th,
- columnIndex,
- table,
- sortableTable,
- columnIndexesClicked
- ) {
- const desc = th.classList.contains("order-by-desc");
- const tableArrows = sortableTable.classList.contains("table-arrows");
- const [arrowUp, arrowDown] = [" ▲", " ▼"];
- const fillValue = "!X!Y!Z!";
-
- if (desc && tableArrows) {
- th.insertAdjacentText("beforeend", arrowDown);
- } else if (tableArrows) {
- th.insertAdjacentText("beforeend", arrowUp);
+ function cellsOrRows(table, tr) {
+ if (table.hasClass.cellsSort) {
+ return tr.innerHTML;
+ } else {
+ return tr.outerHTML;
}
+ }
- function sortDataAttributes(tableRows, column) {
- for (let [i, tr] of tableRows.entries()) {
- let dataAttributeTd = getColumn(tr, column.spanSum, column.span).dataset
- .sort;
- column.toBeSorted.push(`${dataAttributeTd}#${i}`);
- columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML;
- }
+ function sortDataAttributes(table, column) {
+ for (let [i, tr] of table.visibleRows.entries()) {
+ let dataAttributeTd = column.getColumn(tr, column.spanSum, column.span)
+ .dataset.sort;
+ column.toBeSorted.push(`${dataAttributeTd}#${i}`);
+ columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr);
}
+ }
- function sortFileSize(tableRows, column) {
- let unitToMultiplier = {
- b: 1,
- kb: 1000,
- kib: 2 ** 10,
- mb: 1e6,
- mib: 2 ** 20,
- gb: 1e9,
- gib: 2 ** 30,
- tb: 1e12,
- tib: 2 ** 40,
- };
- const numberWithUnitType = /([.0-9]+)\s?(B|KB|KiB|MB|MiB|GB|GiB|TB|TiB)/i;
- for (let [i, tr] of tableRows.entries()) {
- let fileSizeTd = tr
- .querySelectorAll("td")
- .item(columnIndex).textContent;
- let match = fileSizeTd.match(numberWithUnitType);
- if (match) {
- let number = parseFloat(match[1]);
- let unit = match[2].toLowerCase();
- let multiplier = unitToMultiplier[unit];
- column.toBeSorted.push(`${number * multiplier}#${i}`);
- } else {
- column.toBeSorted.push(`${fillValue}#${i}`);
- }
+ function sortFileSize(table, column, columnIndex) {
+ let unitToMultiplier = {
+ b: 1,
+ kb: 1000,
+ kib: 2 ** 10,
+ mb: 1e6,
+ mib: 2 ** 20,
+ gb: 1e9,
+ gib: 2 ** 30,
+ tb: 1e12,
+ tib: 2 ** 40,
+ };
+ const numberWithUnitType = /([.0-9]+)\s?(B|KB|KiB|MB|MiB|GB|GiB|TB|TiB)/i;
+ for (let [i, tr] of table.visibleRows.entries()) {
+ let fileSizeTd = tr.querySelectorAll("td").item(columnIndex).textContent;
+ let match = fileSizeTd.match(numberWithUnitType);
+ if (match) {
+ let number = parseFloat(match[1]);
+ let unit = match[2].toLowerCase();
+ let multiplier = unitToMultiplier[unit];
+ column.toBeSorted.push(`${number * multiplier}#${i}`);
+ columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr);
}
}
+ }
- function sortByRuntime(tableRows, column) {
- try {
- for (let [i, tr] of tableRows.entries()) {
- const regexMinutesAndSeconds = /^(\d+h)?\s?(\d+m)?\s?(\d+s)?$/i;
- let columnOfTd = "";
- // TODO: github actions runtime didn't like textContent, tests didn't like innerText?
- if (testingTableSortJS) {
- columnOfTd = getColumn(tr, column.spanSum, column.span).textContent;
- } else {
- columnOfTd = getColumn(tr, column.spanSum, column.span).innerText;
- }
- let match = columnOfTd.match(regexMinutesAndSeconds);
- let [minutesInSeconds, hours, seconds] = [0, 0, 0];
- let timeinSeconds = columnOfTd;
- if (match) {
- const regexHours = match[1];
- if (regexHours) {
- hours = Number(regexHours.replace("h", "")) * 60 * 60;
- }
- const regexMinutes = match[2];
- if (regexMinutes) {
- minutesInSeconds = Number(regexMinutes.replace("m", "")) * 60;
- }
- const regexSeconds = match[3];
- if (regexSeconds) {
- seconds = Number(regexSeconds.replace("s", ""));
+ function sortDates(datesFormat, table, column) {
+ try {
+ for (let [i, tr] of table.visibleRows.entries()) {
+ let columnOfTd, datesRegex;
+ if (datesFormat === "mdy" || datesFormat === "dmy") {
+ datesRegex = /^(\d\d?)[./-](\d\d?)[./-]((\d\d)?\d\d)/;
+ } else if (datesFormat === "ymd") {
+ datesRegex = /^(\d\d\d\d)[./-](\d\d?)[./-](\d\d?)/;
+ }
+ columnOfTd = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).textContent;
+ let match = columnOfTd.match(datesRegex);
+ let [years, days, months] = [0, 0, 0];
+ let numberToSort = columnOfTd;
+ if (match) {
+ const [regPos1, regPos2, regPos3] = [match[1], match[2], match[3]];
+ if (regPos1 && regPos2 && regPos3) {
+ if (datesFormat === "mdy") {
+ [months, days, years] = [regPos1, regPos2, regPos3];
+ } else if (datesFormat === "ymd") {
+ [years, months, days] = [regPos1, regPos2, regPos3];
+ } else {
+ [days, months, years] = [regPos1, regPos2, regPos3];
}
- timeinSeconds = hours + minutesInSeconds + seconds;
}
- column.toBeSorted.push(`${timeinSeconds}#${i}`);
- columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML;
+ numberToSort = Number(
+ years +
+ String(months).padStart(2, "0") +
+ String(days).padStart(2, "0")
+ );
}
- } catch (e) {
- console.log(e);
+ column.toBeSorted.push(`${numberToSort}#${i}`);
+ columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr);
}
+ } catch (e) {
+ console.log(e);
}
+ }
- function sortDates(datesFormat, tableRows, column) {
- try {
- for (let [i, tr] of tableRows.entries()) {
- let columnOfTd, datesRegex;
- if (datesFormat === "mdy" || datesFormat === "dmy") {
- datesRegex = /^(\d\d?)[./-](\d\d?)[./-]((\d\d)?\d\d)/;
- } else if (datesFormat === "ymd") {
- datesRegex = /^(\d\d\d\d)[./-](\d\d?)[./-](\d\d?)/;
+ function sortByRuntime(table, column) {
+ try {
+ for (let [i, tr] of table.visibleRows.entries()) {
+ const regexMinutesAndSeconds = /^(\d+h)?\s?(\d+m)?\s?(\d+s)?$/i;
+ let columnOfTd = "";
+ // TODO: github actions runtime didn't like textContent, tests didn't like innerText?
+ if (testingTableSortJS) {
+ columnOfTd = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).textContent;
+ } else {
+ columnOfTd = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).innerText;
+ }
+ let match = columnOfTd.match(regexMinutesAndSeconds);
+ let [minutesInSeconds, hours, seconds] = [0, 0, 0];
+ let timeinSeconds = columnOfTd;
+ if (match) {
+ const regexHours = match[1];
+ if (regexHours) {
+ hours = Number(regexHours.replace("h", "")) * 60 * 60;
}
- columnOfTd = getColumn(tr, column.spanSum, column.span).textContent;
- let match = columnOfTd.match(datesRegex);
- let [years, days, months] = [0, 0, 0];
- let numberToSort = columnOfTd;
- if (match) {
- const [regPos1, regPos2, regPos3] = [match[1], match[2], match[3]];
- if (regPos1 && regPos2 && regPos3) {
- if (datesFormat === "mdy") {
- [months, days, years] = [regPos1, regPos2, regPos3];
- } else if (datesFormat === "ymd") {
- [years, months, days] = [regPos1, regPos2, regPos3];
- } else {
- [days, months, years] = [regPos1, regPos2, regPos3];
- }
- }
- numberToSort = Number(
- years +
- String(months).padStart(2, "0") +
- String(days).padStart(2, "0")
- );
+ const regexMinutes = match[2];
+ if (regexMinutes) {
+ minutesInSeconds = Number(regexMinutes.replace("m", "")) * 60;
+ }
+ const regexSeconds = match[3];
+ if (regexSeconds) {
+ seconds = Number(regexSeconds.replace("s", ""));
}
- column.toBeSorted.push(`${numberToSort}#${i}`);
- columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML;
+ timeinSeconds = hours + minutesInSeconds + seconds;
}
- } catch (e) {
- console.log(e);
+ column.toBeSorted.push(`${timeinSeconds}#${i}`);
+ columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr);
}
+ } catch (e) {
+ console.log(e);
}
+ }
- function rememberSort() {
- // if user clicked different column from first column reset times clicked.
- columnIndexesClicked.push(columnIndex);
- if (timesClickedColumn === 1 && columnIndexesClicked.length > 1) {
- const lastColumnClicked =
- columnIndexesClicked[columnIndexesClicked.length - 1];
- const secondLastColumnClicked =
- columnIndexesClicked[columnIndexesClicked.length - 2];
- if (lastColumnClicked !== secondLastColumnClicked) {
- columnIndexesClicked.shift();
- timesClickedColumn = 0;
+ function getTableData(tableProperties, timesClickedColumn) {
+ const {
+ table,
+ tableRows,
+ fillValue,
+ column,
+ th,
+ hasThClass,
+ isSortDates,
+ desc,
+ arrow,
+ } = tableProperties;
+ for (let [i, tr] of tableRows.entries()) {
+ let tdTextContent = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).textContent;
+ if (tdTextContent.length === 0) {
+ tdTextContent = "";
+ }
+ if (tdTextContent.trim() !== "") {
+ if (
+ !hasThClass.fileSize &&
+ !hasThClass.dataSort &&
+ !hasThClass.runtime &&
+ !hasThClass.filesize &&
+ !isSortDates.dayMonthYear &&
+ !isSortDates.yearMonthDay &&
+ !isSortDates.monthDayYear
+ ) {
+ column.toBeSorted.push(`${tdTextContent}#${i}`);
+ columnIndexAndTableRow[`${tdTextContent}#${i}`] = cellsOrRows(
+ table,
+ tr
+ );
}
+ } else {
+ // Fill in blank table cells dict key with filler value.
+ column.toBeSorted.push(`${fillValue}#${i}`);
+ columnIndexAndTableRow[`${fillValue}#${i}`] = cellsOrRows(table, tr);
}
- return timesClickedColumn;
}
- function getColSpanData(headers, column) {
- headers.forEach((th, index) => {
- column.span[index] = th.colSpan;
- if (index === 0) column.spanSum[index] = th.colSpan;
- else column.spanSum[index] = column.spanSum[index - 1] + th.colSpan;
- });
- }
+ const isPunctSort = th.classList.contains("punct-sort");
+ const isAlphaSort = th.classList.contains("alpha-sort");
+ const isNumericSort = th.classList.contains("numeric-sort");
- function getColumn(tr, colSpanSum, colSpanData) {
- return tr
- .querySelectorAll("td")
- .item(
- colSpanData[columnIndex] === 1
- ? colSpanSum[columnIndex] - 1
- : colSpanSum[columnIndex] - colSpanData[columnIndex]
- );
+ function parseNumberFromString(str) {
+ let num;
+ str = str.slice(0, str.indexOf("#"));
+ if (str.match(/^\((\d+(?:\.\d+)?)\)$/)) {
+ num = -1 * Number(str.slice(1, -1));
+ } else {
+ num = Number(str);
+ }
+ return num;
}
- function getTableData(tableProperties) {
- const { tableRows, column, hasThClass, isSortDates } = tableProperties;
- for (let [i, tr] of tableRows.entries()) {
- let tdTextContent = getColumn(
- tr,
- column.spanSum,
- column.span
- ).textContent;
- if (tdTextContent.length === 0) {
- tdTextContent = "";
- }
- if (tdTextContent.trim() !== "") {
- if (hasThClass.fileSize) {
- fileSizeColumnTextAndRow[column.toBeSorted[i]] = tr.outerHTML;
- }
- // These classes already handle pushing to column and setting the tr html.
- if (
- !hasThClass.fileSize &&
- !hasThClass.dataSort &&
- !hasThClass.runtime &&
- !isSortDates.dayMonthYear &&
- !isSortDates.yearMonthDay &&
- !isSortDates.monthDayYear
- ) {
- column.toBeSorted.push(`${tdTextContent}#${i}`);
- columnIndexAndTableRow[`${tdTextContent}#${i}`] = tr.outerHTML;
- }
- } else {
- // Fill in blank table cells dict key with filler value.
- column.toBeSorted.push(`${fillValue}#${i}`);
- columnIndexAndTableRow[`${fillValue}#${i}`] = tr.outerHTML;
- }
- }
+ function strLocaleCompare(str1, str2) {
+ return str1.localeCompare(
+ str2,
+ navigator.languages[0] || navigator.language,
+ { numeric: !isAlphaSort, ignorePunctuation: !isPunctSort }
+ );
+ }
- const isPunctSort = th.classList.contains("punct-sort");
- const isAlphaSort = th.classList.contains("alpha-sort");
- const isNumericSort = th.classList.contains("numeric-sort");
+ function handleNumbers(str1, str2) {
+ let num1, num2;
+ num1 = parseNumberFromString(str1);
+ num2 = parseNumberFromString(str2);
- function parseNumberFromString(str) {
- let num;
- str = str.slice(0, str.indexOf("#"));
- if (str.match(/^\((\d+(?:\.\d+)?)\)$/)) {
- num = -1 * Number(str.slice(1, -1));
- } else {
- num = Number(str);
- }
- return num;
+ if (!isNaN(num1) && !isNaN(num2)) {
+ return num1 - num2;
+ } else {
+ return strLocaleCompare(str1, str2);
}
+ }
- function strLocaleCompare(str1, str2) {
- return str1.localeCompare(
- str2,
- navigator.languages[0] || navigator.language,
- { numeric: !isAlphaSort, ignorePunctuation: !isPunctSort }
- );
+ function sortAscending(a, b) {
+ if (a.includes(`${fillValue}#`)) {
+ return 1;
+ } else if (b.includes(`${fillValue}#`)) {
+ return -1;
+ } else if (isNumericSort) {
+ return handleNumbers(a, b);
+ } else {
+ return strLocaleCompare(a, b);
}
+ }
- function handleNumbers(str1, str2) {
- let num1, num2;
- num1 = parseNumberFromString(str1);
- num2 = parseNumberFromString(str2);
+ function sortDescending(a, b) {
+ return sortAscending(b, a);
+ }
- if (!isNaN(num1) && !isNaN(num2)) {
- return num1 - num2;
- } else {
- return strLocaleCompare(str1, str2);
- }
- }
+ function clearArrows(arrowUp = "▲", arrowDown = "▼") {
+ th.innerHTML = th.innerHTML.replace(arrowUp, "");
+ th.innerHTML = th.innerHTML.replace(arrowDown, "");
+ }
- function sortAscending(a, b) {
- if (a.includes(`${fillValue}#`)) {
- return 1;
- } else if (b.includes(`${fillValue}#`)) {
- return -1;
- } else if (isNumericSort) {
- return handleNumbers(a, b);
- } else {
- return strLocaleCompare(a, b);
- }
- }
+ if (column.toBeSorted[0] === undefined) {
+ return;
+ }
- function sortDescending(a, b) {
- return sortAscending(b, a);
+ function changeTableArrow(arrowDirection) {
+ if (table.hasClass.tableArrows) {
+ clearArrows(arrow.up, arrow.down);
+ th.insertAdjacentText("beforeend", arrowDirection);
}
+ }
- function clearArrows(arrowUp = "▲", arrowDown = "▼") {
- th.innerHTML = th.innerHTML.replace(arrowUp, "");
- th.innerHTML = th.innerHTML.replace(arrowDown, "");
- }
+ function sortColumn(sortDirection) {
+ column.toBeSorted.sort(sortDirection, {
+ numeric: !isAlphaSort,
+ ignorePunctuation: !isPunctSort,
+ });
+ }
- if (column.toBeSorted[0] === undefined) {
- return;
+ if (timesClickedColumn === 1) {
+ if (desc) {
+ changeTableArrow(arrow.down);
+ sortColumn(sortDescending);
+ } else {
+ changeTableArrow(arrow.up);
+ sortColumn(sortAscending);
}
-
- function changeTableArrow(arrowDirection) {
- if (tableArrows) {
- clearArrows(arrowUp, arrowDown);
- th.insertAdjacentText("beforeend", arrowDirection);
- }
+ } else if (timesClickedColumn === 2) {
+ timesClickedColumn = 0;
+ if (desc) {
+ changeTableArrow(arrow.up);
+ sortColumn(sortAscending);
+ } else {
+ changeTableArrow(arrow.down);
+ sortColumn(sortDescending);
}
+ }
+ return timesClickedColumn;
+ }
- function sortColumn(sortDirection) {
- column.toBeSorted.sort(sortDirection, {
- numeric: !isAlphaSort,
- ignorePunctuation: !isPunctSort,
- });
+ function updateFilesize(i, table, tr, column, columnIndex) {
+ if (table.hasClass.cellsSort) {
+ tr.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]];
+ } else {
+ // We do this to sort rows rather than cells:
+ const template = document.createElement("template");
+ template.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]];
+ tr = template.content.firstChild;
+ }
+ let fileSizeInBytesHTML = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).outerHTML;
+ const fileSizeInBytesText = column.getColumn(
+ tr,
+ column.spanSum,
+ column.span
+ ).textContent;
+
+ const fileSize = column.toBeSorted[i].replace(/#[0-9]*/, "");
+ let prefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi"];
+ let replaced = false;
+ for (let i = 0; i < prefixes.length; ++i) {
+ let nextPrefixMultiplier = 2 ** (10 * (i + 1));
+ if (fileSize < nextPrefixMultiplier) {
+ let prefixMultiplier = 2 ** (10 * i);
+ fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
+ fileSizeInBytesText,
+ `${(fileSize / prefixMultiplier).toFixed(2)} ${prefixes[i]}B`
+ );
+ replaced = true;
+ break;
}
+ }
+ if (!replaced) {
+ fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
+ fileSizeInBytesText,
+ "NaN"
+ );
+ }
+ tr.querySelectorAll("td").item(columnIndex).innerHTML = fileSizeInBytesHTML;
+ return table.hasClass.cellsSort ? tr.innerHTML : tr.outerHTML;
+ }
- if (timesClickedColumn === 1) {
- if (desc) {
- changeTableArrow(arrowDown);
- sortColumn(sortDescending);
+ function updateTable(tableProperties) {
+ const { column, table, columnIndex, hasThClass } = tableProperties;
+ for (let [i, tr] of table.visibleRows.entries()) {
+ if (hasThClass.fileSize) {
+ if (table.hasClass.cellsSort) {
+ tr.innerHTML = updateFilesize(i, table, tr, column, columnIndex);
} else {
- changeTableArrow(arrowUp);
- sortColumn(sortAscending);
+ tr.outerHTML = updateFilesize(i, table, tr, column, columnIndex);
}
- } else if (timesClickedColumn === 2) {
- timesClickedColumn = 0;
- if (desc) {
- changeTableArrow(arrowUp);
- sortColumn(sortAscending);
+ } else if (!hasThClass.fileSize) {
+ if (table.hasClass.cellsSort) {
+ tr.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]];
} else {
- changeTableArrow(arrowDown);
- sortColumn(sortDescending);
+ tr.outerHTML = columnIndexAndTableRow[column.toBeSorted[i]];
}
}
}
+ }
- function updateTable(tableProperties) {
- const { tableRows, column, hasThClass } = tableProperties;
- for (let [i, tr] of tableRows.entries()) {
- if (hasThClass.fileSize) {
- tr.innerHTML = fileSizeColumnTextAndRow[column.toBeSorted[i]];
- let fileSizeInBytesHTML = tr
- .querySelectorAll("td")
- .item(columnIndex).innerHTML;
- const fileSizeInBytesText = tr
- .querySelectorAll("td")
- .item(columnIndex).textContent;
- // Remove the unique identifyer for duplicate values(#number).
- column.toBeSorted[i] = column.toBeSorted[i].replace(/#[0-9]*/, "");
- const fileSize = parseFloat(column.toBeSorted[i]);
- let prefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi"];
- let replaced = false;
- for (let i = 0; i < prefixes.length; ++i) {
- let nextPrefixMultiplier = 2 ** (10 * (i + 1));
- if (fileSize < nextPrefixMultiplier) {
- let prefixMultiplier = 2 ** (10 * i);
- fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
- fileSizeInBytesText,
- `${(fileSize / prefixMultiplier).toFixed(2)} ${prefixes[i]}B`
- );
- replaced = true;
- break;
- }
- }
- if (!replaced) {
- fileSizeInBytesHTML = fileSizeInBytesHTML.replace(
- fileSizeInBytesText,
- "NaN"
- );
- }
- tr.querySelectorAll("td").item(columnIndex).innerHTML =
- fileSizeInBytesHTML;
- } else if (!hasThClass.fileSize) {
- tr.outerHTML = columnIndexAndTableRow[column.toBeSorted[i]];
- }
+ function getColSpanData(headers, column) {
+ headers.forEach((th, index) => {
+ column.span[index] = th.colSpan;
+ if (index === 0) column.spanSum[index] = th.colSpan;
+ else column.spanSum[index] = column.spanSum[index - 1] + th.colSpan;
+ });
+ }
+
+ function rememberSort(columnIndexesClicked, timesClickedColumn, columnIndex) {
+ // if user clicked different column from first column reset times clicked.
+ columnIndexesClicked.push(columnIndex);
+ if (timesClickedColumn === 1 && columnIndexesClicked.length > 1) {
+ const lastColumnClicked =
+ columnIndexesClicked[columnIndexesClicked.length - 1];
+ const secondLastColumnClicked =
+ columnIndexesClicked[columnIndexesClicked.length - 2];
+ if (lastColumnClicked !== secondLastColumnClicked) {
+ columnIndexesClicked.shift();
+ timesClickedColumn = 0;
}
}
+ return timesClickedColumn;
+ }
+
+ function makeEachColumnSortable(
+ th,
+ headerIndex,
+ columnIndex,
+ table,
+ columnIndexesClicked
+ ) {
+ const desc = th.classList.contains("order-by-desc");
+ const arrow = { up: " ▲", down: " ▼" };
+ const fillValue = "!X!Y!Z!";
+
+ if (desc && table.hasClass.tableArrows) {
+ th.insertAdjacentText("beforeend", arrow.down);
+ } else if (table.hasClass.tableArrows) {
+ th.insertAdjacentText("beforeend", arrow.up);
+ }
let timesClickedColumn = 0;
+ const column = {
+ getColumn: function getColumn(tr, colSpanSum, colSpanData) {
+ return tr
+ .querySelectorAll("td")
+ .item(
+ colSpanData[columnIndex] === 1
+ ? colSpanSum[columnIndex] - 1
+ : colSpanSum[columnIndex] - colSpanData[columnIndex]
+ );
+ },
+ };
th.addEventListener("click", function () {
- const column = {
- toBeSorted: [],
- span: {},
- spanSum: {},
- };
+ column.toBeSorted = [];
+ column.span = {};
+ column.spanSum = {};
+ getColSpanData(table.headers[headerIndex], column);
table.visibleRows = Array.prototype.filter.call(
- table.body.querySelectorAll("tr"),
+ table.bodies.item(headerIndex).querySelectorAll("tr"),
(tr) => {
return tr.style.display !== "none";
}
);
- getColSpanData(table.headers, column);
-
- const isRememberSort = sortableTable.classList.contains("remember-sort");
- if (!isRememberSort) {
- timesClickedColumn = rememberSort();
+ if (!table.hasClass.rememberSort) {
+ timesClickedColumn = rememberSort(
+ columnIndexesClicked,
+ timesClickedColumn,
+ columnIndex
+ );
}
timesClickedColumn += 1;
@@ -501,13 +568,13 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
};
if (hasThClass.dataSort) {
- sortDataAttributes(table.visibleRows, column);
+ sortDataAttributes(table, column);
}
if (hasThClass.fileSize) {
- sortFileSize(table.visibleRows, column);
+ sortFileSize(table, column, columnIndex, fillValue);
}
if (hasThClass.runtime) {
- sortByRuntime(table.visibleRows, column);
+ sortByRuntime(table, column);
}
const isSortDates = {
@@ -517,20 +584,27 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
};
// pick mdy first to override the inferred default class which is dmy.
if (isSortDates.monthDayYear) {
- sortDates("mdy", table.visibleRows, column);
+ sortDates("mdy", table, column);
} else if (isSortDates.yearMonthDay) {
- sortDates("ymd", table.visibleRows, column);
+ sortDates("ymd", table, column);
} else if (isSortDates.dayMonthYear) {
- sortDates("dmy", table.visibleRows, column);
+ sortDates("dmy", table, column);
}
const tableProperties = {
+ table,
tableRows: table.visibleRows,
+ fillValue,
column,
+ columnIndex,
+ th,
hasThClass,
isSortDates,
+ desc,
+ timesClickedColumn,
+ arrow,
};
- getTableData(tableProperties);
+ timesClickedColumn = getTableData(tableProperties, timesClickedColumn);
updateTable(tableProperties);
});
diff --git a/public/docs/about.html b/public/docs/about.html
index d2f10e5..e089683 100644
--- a/public/docs/about.html
+++ b/public/docs/about.html
@@ -72,7 +72,8 @@ Objectives of table-sort-js:
Having no dependencies keeps the library size down and avoids the
- left-pad fiasco. (for example jquery which datatables relies on is around 80KB).
+ left-pad fiasco. (for example jquery which datatables relies on is
+ around 80KB).
Be versatile; sorts dates, numbers (in a natural order),
diff --git a/public/docs/demo.html b/public/docs/demo.html
index 1f4c92f..d3e3b24 100644
--- a/public/docs/demo.html
+++ b/public/docs/demo.html
@@ -61,167 +61,172 @@ table-sort-js
-
+
-
-
-
- Last Name |
- First Name |
- Birth Date |
- Employee ID |
- Department |
- Runtime |
- File Size |
- data-sort days |
- dates in dd/mm/yyyy |
-
-
-
- Franklin |
- Benjamin |
- 1706-1-17 |
- 1 |
- k-level |
- 1h 1m 17s |
- 10b |
- Tuesday |
- 17/6/1978 |
-
-
- da Vinci |
- Zarlo |
- 1452-4-15 |
- 13000 |
- |
- 1m 45s |
- 192038998987021b |
- Wednesday |
- 18/10/2027 |
-
-
- Statham |
- Jason |
- 1967-7-26 |
- |
- HR |
- 11m 40s |
- 134809b |
- Friday |
- 4/9/2008 |
-
-
- Micheal |
- Angelo |
- 1958-8-21 |
- 54 |
- Marketing |
- 29s |
- 30980980b |
- Thursday |
- 2/3/1879 |
-
-
-
- Ben |
- |
- 1994/9/23 |
- 134 |
- Marketing |
- 41s |
- 902938402398b |
- Monday |
- 8/6/1978 |
-
-
-
-
Demo of colspan and data-sort class to sort fractions:
-
-
-
- |
- Category |
- Show |
- Overall |
- On Our Dates |
- First Sold Out |
-
-
-
-
- |
- Comedy |
- Show 1 |
- 18/25 |
- 72% |
- 3/4 |
- 75% |
- 1999-07-30 |
-
-
- |
- Music |
- Show 2 |
- 6/10 |
- 60% |
- 3/4 |
- 75% |
- 1999-08-04 |
-
-
- |
- Theatre |
- Show 3 |
- 7/15 |
- 47% |
- 3/4 |
- 75% |
- 1999-07-19 |
-
-
- |
- Comedy |
- Show 4 |
- 10/15 |
- 67% |
- 2/3 |
- 67% |
- 1999-07-19 |
-
-
- |
- Comedy |
- Show 5 |
- 9/12 |
- 75% |
- 1/2 |
- 50% |
- 1999-07-29 |
-
-
- |
- Comedy |
- Show 6 |
- 16/24 |
- 67% |
- 2/4 |
- 50% |
- 1999-07-26 |
-
-
- |
- Comedy |
- Show 7 |
- 16/26 |
- 62% |
- 2/4 |
- 50% |
- 2022-07-31 |
-
-
-
+
+
+
+ Last Name |
+ First Name |
+ Birth Date |
+ Employee ID |
+ Department |
+ Runtime |
+ File Size |
+ data-sort days |
+ dates in dd/mm/yyyy |
+
+
+
+ Franklin |
+ Benjamin |
+ 1706-1-17 |
+ 1 |
+ k-level |
+ 1h 1m 17s |
+ 10b |
+ Tuesday |
+ 17/6/1978 |
+
+
+ da Vinci |
+ Zarlo |
+ 1452-4-15 |
+ 13000 |
+ |
+ 1m 45s |
+ 192038998987021b |
+ Wednesday |
+ 18/10/2027 |
+
+
+ Statham |
+ Jason |
+ 1967-7-26 |
+ |
+ HR |
+ 11m 40s |
+ 134809b |
+ Friday |
+ 4/9/2008 |
+
+
+ Micheal |
+ Angelo |
+ 1958-8-21 |
+ 54 |
+ Marketing |
+ 29s |
+ 30980980b |
+ Thursday |
+ 2/3/1879 |
+
+
+ Ben |
+ |
+ 1994/9/23 |
+ 134 |
+ Marketing |
+ 41s |
+ 902938402398b |
+ Monday |
+ 8/6/1978 |
+
+
+
+
+ Demo of colspan and data-sort class to sort fractions:
+
+
+
+
+ |
+ Category |
+ Show |
+ Overall |
+ On Our Dates |
+ First Sold Out |
+
+
+
+
+ |
+ Comedy |
+ Show 1 |
+ 18/25 |
+ 72% |
+ 3/4 |
+ 75% |
+ 1999-07-30 |
+
+
+ |
+ Music |
+ Show 2 |
+ 6/10 |
+ 60% |
+ 3/4 |
+ 75% |
+ 1999-08-04 |
+
+
+ |
+ Theatre |
+ Show 3 |
+ 7/15 |
+ 47% |
+ 3/4 |
+ 75% |
+ 1999-07-19 |
+
+
+ |
+ Comedy |
+ Show 4 |
+ 10/15 |
+ 67% |
+ 2/3 |
+ 67% |
+ 1999-07-19 |
+
+
+ |
+ Comedy |
+ Show 5 |
+ 9/12 |
+ 75% |
+ 1/2 |
+ 50% |
+ 1999-07-29 |
+
+
+ |
+ Comedy |
+ Show 6 |
+ 16/24 |
+ 67% |
+ 2/4 |
+ 50% |
+ 1999-07-26 |
+
+
+ |
+ Comedy |
+ Show 7 |
+ 16/26 |
+ 62% |
+ 2/4 |
+ 50% |
+ 2022-07-31 |
+
+
+
diff --git a/public/docs/development.html b/public/docs/development.html
index 2abf4bb..12a7c9a 100644
--- a/public/docs/development.html
+++ b/public/docs/development.html
@@ -102,7 +102,8 @@ How to contribute to table-sort-js:
note: table-sort-js currently uses node version 16; I would
- recommend using node version manager (nvm) and running the command nvm 16 to select node version 16.
+ recommend using node version manager (nvm) and running the command
+ nvm 16 to select node version 16.
Testing table-sort-js: npm run test (currently uses Jest
diff --git a/public/docs/html5.html b/public/docs/html5.html
index aab4210..da7031e 100644
--- a/public/docs/html5.html
+++ b/public/docs/html5.html
@@ -65,7 +65,7 @@ table-sort-js
-
+
<script src="https://cdn.jsdelivr.net/npm/table-sort-js/table-sort.js"></script>
<table class="table-sort">
<thead>
diff --git a/public/table-sort.js b/public/table-sort.js
index dbf75bd..a9a7e31 100644
--- a/public/table-sort.js
+++ b/public/table-sort.js
@@ -129,9 +129,12 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
rememberSort: sortableTable.classList.contains("remember-sort"),
};
-
- for ( let headerIndex = 0; headerIndex < table.theads.length; headerIndex++) {
- let columnIndexesClicked = [];
+ for (
+ let headerIndex = 0;
+ headerIndex < table.theads.length;
+ headerIndex++
+ ) {
+ let columnIndexesClicked = [];
for (let [columnIndex, th] of table.headers[headerIndex].entries()) {
if (!th.classList.contains("disable-sort")) {
th.style.cursor = "pointer";
diff --git a/test/table.js b/test/table.js
index bd40711..0ce6eb3 100644
--- a/test/table.js
+++ b/test/table.js
@@ -105,7 +105,7 @@ function createTestTable(
for (let [i, tr] of tableRows.entries()) {
if (tr.style.display !== "none") {
for (let i = 0; i < numberOfTableColumns; i++)
- if (props.tableTags === "cells-sort" || props.tableTags ==="tr-sort") {
+ if (props.tableTags === "cells-sort" || props.tableTags === "tr-sort") {
testIfSortedList[`col${i}`].push(tr.outerHTML);
} else {
testIfSortedList[`col${i}`].push(