78 lines
2.3 KiB
JavaScript
78 lines
2.3 KiB
JavaScript
(function () {
|
|
"use strict";
|
|
|
|
function getCellText(row, index) {
|
|
var cell = row.children[index];
|
|
return cell ? cell.textContent.trim() : "";
|
|
}
|
|
|
|
function isEmptyValue(text) {
|
|
return text === "" || text === "—";
|
|
}
|
|
|
|
function isPureNumber(text) {
|
|
return /^-?\d+(\.\d+)?$/.test(text);
|
|
}
|
|
|
|
function columnIsNumeric(rows, index) {
|
|
var sawValue = false;
|
|
for (var i = 0; i < rows.length; i++) {
|
|
var text = getCellText(rows[i], index);
|
|
if (isEmptyValue(text)) continue;
|
|
sawValue = true;
|
|
if (!isPureNumber(text)) return false;
|
|
}
|
|
return sawValue;
|
|
}
|
|
|
|
function compareRows(a, b, index, numeric, dir) {
|
|
var textA = getCellText(a, index);
|
|
var textB = getCellText(b, index);
|
|
var emptyA = isEmptyValue(textA);
|
|
var emptyB = isEmptyValue(textB);
|
|
if (emptyA && emptyB) return 0;
|
|
if (emptyA) return 1;
|
|
if (emptyB) return -1;
|
|
|
|
var result = numeric
|
|
? parseFloat(textA) - parseFloat(textB)
|
|
: textA.localeCompare(textB, undefined, { numeric: true, sensitivity: "base" });
|
|
return dir === "desc" ? -result : result;
|
|
}
|
|
|
|
function sortTable(table, th) {
|
|
var thead = table.tHead;
|
|
var tbody = table.tBodies[0];
|
|
if (!thead || !tbody) return;
|
|
|
|
var headerRow = thead.rows[0];
|
|
var index = Array.prototype.indexOf.call(headerRow.children, th);
|
|
if (index === -1) return;
|
|
|
|
var rows = Array.prototype.slice.call(tbody.rows);
|
|
if (rows.length < 2) return;
|
|
|
|
var nextDir = th.getAttribute("data-sort-dir") === "asc" ? "desc" : "asc";
|
|
Array.prototype.forEach.call(headerRow.children, function (cell) {
|
|
cell.removeAttribute("data-sort-dir");
|
|
cell.removeAttribute("aria-sort");
|
|
});
|
|
th.setAttribute("data-sort-dir", nextDir);
|
|
th.setAttribute("aria-sort", nextDir === "asc" ? "ascending" : "descending");
|
|
|
|
var numeric = columnIsNumeric(rows, index);
|
|
rows.sort(function (a, b) {
|
|
return compareRows(a, b, index, numeric, nextDir);
|
|
});
|
|
rows.forEach(function (row) {
|
|
tbody.appendChild(row);
|
|
});
|
|
}
|
|
|
|
document.addEventListener("click", function (event) {
|
|
var th = event.target.closest("table.sortable thead th");
|
|
if (!th || th.hasAttribute("data-sort-off") || th.textContent.trim() === "") return;
|
|
sortTable(th.closest("table"), th);
|
|
});
|
|
})();
|