table-sort-js
Advanced tools
Comparing version 1.16.0 to 1.17.0
{ | ||
"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.", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
@@ -6,2 +6,3 @@ ![npm version](https://img.shields.io/npm/v/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) | ||
@@ -21,36 +22,28 @@ | ||
- <b>Option 1:</b> Install from npm: | ||
- <b>Option 1</b>: Load as script from a Content Delivery Network (CDN): | ||
```javascript | ||
npm install table-sort-js | ||
<script src="https://cdn.jsdelivr.net/npm/table-sort-js/table-sort.js"></script> | ||
``` | ||
Or Minified (smaller size, but harder to debug!): | ||
```javascript | ||
import tableSort from "table-sort-js/table-sort.js"; | ||
<script src="https://cdn.jsdelivr.net/npm/table-sort-js/table-sort.min.js"></script> | ||
``` | ||
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) | ||
- <b>Option 2</b>: Load as script from a Content Delivery Network (CDN): | ||
- <b>Option 2:</b> Install from npm: | ||
```javascript | ||
<script src="https://cdn.jsdelivr.net/npm/table-sort-js@latest/table-sort.js"></script> | ||
npm install table-sort-js | ||
``` | ||
Or Minified (smaller size, but harder to debug!): | ||
```javascript | ||
<script src="https://cdn.jsdelivr.net/npm/table-sort-js@latest/table-sort.min.js"></script> | ||
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) | ||
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) | ||
- <b>Option 3:</b> 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 | ||
<script src="table-sort.js"></script> | ||
``` | ||
## To make tables sortable: | ||
@@ -69,2 +62,3 @@ | ||
| "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. | | ||
@@ -71,0 +65,0 @@ <br> |
@@ -31,3 +31,2 @@ /* | ||
const columnIndexAndTableRow = {}; | ||
const fileSizeColumnTextAndRow = {}; | ||
for (let table of getTagTable) { | ||
@@ -50,12 +49,14 @@ if (table.classList.contains("table-sort")) { | ||
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 <tr> or <td> exists below <thead> the browser will make <tbody> | ||
return sortableTable.querySelectorAll("tbody"); | ||
} | ||
@@ -113,26 +114,42 @@ } | ||
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"); | ||
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")); | ||
} | ||
let columnIndexesClicked = []; | ||
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"), | ||
}; | ||
const isNoSortClassInference = | ||
sortableTable.classList.contains("no-class-infer"); | ||
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 | ||
); | ||
} | ||
@@ -142,347 +159,396 @@ } | ||
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!"; | ||
function cellsOrRows(table, tr) { | ||
if (table.hasClass.cellsSort) { | ||
return tr.innerHTML; | ||
} else { | ||
return tr.outerHTML; | ||
} | ||
} | ||
if (desc && tableArrows) { | ||
th.insertAdjacentText("beforeend", arrowDown); | ||
} else if (tableArrows) { | ||
th.insertAdjacentText("beforeend", arrowUp); | ||
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 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 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 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); | ||
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) { | ||
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 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 [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]; | ||
} | ||
const regexMinutes = match[2]; | ||
if (regexMinutes) { | ||
minutesInSeconds = Number(regexMinutes.replace("m", "")) * 60; | ||
} | ||
const regexSeconds = match[3]; | ||
if (regexSeconds) { | ||
seconds = Number(regexSeconds.replace("s", "")); | ||
} | ||
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; | ||
} | ||
column.toBeSorted.push(`${numberToSort}#${i}`); | ||
columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML; | ||
const regexSeconds = match[3]; | ||
if (regexSeconds) { | ||
seconds = Number(regexSeconds.replace("s", "")); | ||
} | ||
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 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 getColumn(tr, colSpanSum, colSpanData) { | ||
return tr | ||
.querySelectorAll("td") | ||
.item( | ||
colSpanData[columnIndex] === 1 | ||
? colSpanSum[columnIndex] - 1 | ||
: colSpanSum[columnIndex] - colSpanData[columnIndex] | ||
); | ||
function strLocaleCompare(str1, str2) { | ||
return str1.localeCompare( | ||
str2, | ||
navigator.languages[0] || navigator.language, | ||
{ numeric: !isAlphaSort, ignorePunctuation: !isPunctSort } | ||
); | ||
} | ||
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 handleNumbers(str1, str2) { | ||
let num1, num2; | ||
num1 = parseNumberFromString(str1); | ||
num2 = parseNumberFromString(str2); | ||
const isPunctSort = th.classList.contains("punct-sort"); | ||
const isAlphaSort = th.classList.contains("alpha-sort"); | ||
const isNumericSort = th.classList.contains("numeric-sort"); | ||
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) => { | ||
@@ -493,7 +559,8 @@ 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 | ||
); | ||
} | ||
@@ -509,9 +576,9 @@ timesClickedColumn += 1; | ||
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); | ||
} | ||
@@ -526,16 +593,23 @@ | ||
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); | ||
@@ -542,0 +616,0 @@ }); |
29265
584
92