(function () { function getTableHeaderText(headerCell) { return String(headerCell && headerCell.textContent ? headerCell.textContent : '').trim().toLowerCase(); } function cellHasButtonContent(cell) { return Boolean(cell && cell.querySelector && cell.querySelector('button, form, input, select, textarea')); } function getCellSortValue(cell) { if (!cell) { return ''; } var sortValue = cell.getAttribute && cell.getAttribute('data-sort-value'); if (sortValue !== null && sortValue !== undefined && sortValue !== '') { return String(sortValue).trim(); } return String(cell.textContent || '').replace(/\s+/g, ' ').trim(); } function getComparableSortValue(rawValue) { var value = String(rawValue || '').trim(); if (!value) { return { type: 'empty', value: '' }; } var numericValue = Number(value.replace(/,/g, '')); if (!Number.isNaN(numericValue) && value !== '') { return { type: 'number', value: numericValue }; } var dateValue = Date.parse(value); if (!Number.isNaN(dateValue)) { return { type: 'date', value: dateValue }; } return { type: 'string', value: value.toLowerCase() }; } function compareSortValues(leftValue, rightValue) { if (leftValue.type === 'empty' && rightValue.type === 'empty') { return 0; } if (leftValue.type === 'empty') { return 1; } if (rightValue.type === 'empty') { return -1; } if (leftValue.type === rightValue.type) { if (leftValue.value < rightValue.value) { return -1; } if (leftValue.value > rightValue.value) { return 1; } return 0; } return String(leftValue.value).localeCompare(String(rightValue.value), undefined, { numeric: true, sensitivity: 'base' }); } function getSortableTableState(table) { if (!table._sortableState) { table._sortableState = { columnIndex: null, direction: 'asc' }; } return table._sortableState; } function ensureSortableHeaderIndicator(headerCell) { var indicator = headerCell.querySelector && headerCell.querySelector('.table-sort-indicator'); if (indicator) { return indicator; } indicator = document.createElement('i'); indicator.className = 'table-sort-indicator bi bi-arrow-down-up ms-1'; indicator.setAttribute('aria-hidden', 'true'); headerCell.appendChild(indicator); return indicator; } function updateSortableHeaderIndicator(headerCell, isSortable, isActive, direction) { if (!isSortable) { var hiddenIndicator = headerCell.querySelector && headerCell.querySelector('.table-sort-indicator'); if (hiddenIndicator) { hiddenIndicator.style.display = 'none'; } return; } var indicator = ensureSortableHeaderIndicator(headerCell); indicator.style.display = ''; indicator.className = 'table-sort-indicator bi ms-1'; if (isActive && direction === 'desc') { indicator.classList.add('bi-caret-down-fill'); return; } if (isActive && direction === 'asc') { indicator.classList.add('bi-caret-up-fill'); return; } indicator.classList.add('bi-arrow-down-up'); } function isSortableTableColumn(table, columnIndex) { var headerCell = table.tHead && table.tHead.rows && table.tHead.rows.length ? table.tHead.rows[0].cells[columnIndex] : null; if (!headerCell) { return false; } if (headerCell.getAttribute && headerCell.getAttribute('data-sortable') === 'false') { return false; } if (/actions?|buttons?/i.test(getTableHeaderText(headerCell))) { return false; } var bodies = table.tBodies ? Array.prototype.slice.call(table.tBodies) : []; for (var i = 0; i < bodies.length; i += 1) { var rows = Array.prototype.slice.call(bodies[i].rows || []); for (var j = 0; j < rows.length; j += 1) { var cell = rows[j].cells ? rows[j].cells[columnIndex] : null; if (cell && cellHasButtonContent(cell)) { return false; } } } return true; } function updateSortableHeaderState(table) { var state = getSortableTableState(table); var headerCells = table.tHead && table.tHead.rows && table.tHead.rows.length ? Array.prototype.slice.call(table.tHead.rows[0].cells || []) : []; headerCells.forEach(function (headerCell, index) { if (!headerCell) { return; } var sortable = isSortableTableColumn(table, index); headerCell.classList.remove('sort-asc', 'sort-desc', 'sortable', 'unsortable'); headerCell.removeAttribute('aria-sort'); headerCell.removeAttribute('role'); headerCell.removeAttribute('tabindex'); if (sortable) { headerCell.classList.add('sortable'); updateSortableHeaderIndicator(headerCell, true, state.columnIndex === index, state.direction); headerCell.setAttribute('role', 'button'); headerCell.setAttribute('tabindex', '0'); if (state.columnIndex === index) { headerCell.classList.add(state.direction === 'desc' ? 'sort-desc' : 'sort-asc'); headerCell.setAttribute('aria-sort', state.direction === 'desc' ? 'descending' : 'ascending'); } else { headerCell.setAttribute('aria-sort', 'none'); } } else { headerCell.classList.add('unsortable'); updateSortableHeaderIndicator(headerCell, false); } }); } function sortTable(table, columnIndex, direction) { var tbody = table.tBodies && table.tBodies[0] ? table.tBodies[0] : null; if (!tbody) { return; } var rows = Array.prototype.slice.call(tbody.rows || []); if (!rows.length) { return; } var multiplier = direction === 'desc' ? -1 : 1; rows.sort(function (leftRow, rightRow) { var leftCell = leftRow.cells ? leftRow.cells[columnIndex] : null; var rightCell = rightRow.cells ? rightRow.cells[columnIndex] : null; var leftComparable = getComparableSortValue(getCellSortValue(leftCell)); var rightComparable = getComparableSortValue(getCellSortValue(rightCell)); return compareSortValues(leftComparable, rightComparable) * multiplier; }); rows.forEach(function (row) { tbody.appendChild(row); }); } function applyTableSort(table) { var state = getSortableTableState(table); if (state.columnIndex === null || state.columnIndex === undefined) { return; } sortTable(table, state.columnIndex, state.direction); updateSortableHeaderState(table); } function initSortableTables() { var tables = Array.prototype.slice.call(document.querySelectorAll('table')); tables.forEach(function (table) { var headerRow = table.tHead && table.tHead.rows && table.tHead.rows.length ? table.tHead.rows[0] : null; if (!headerRow) { return; } Array.prototype.forEach.call(headerRow.cells, function (headerCell, index) { if (!isSortableTableColumn(table, index)) { return; } ensureSortableHeaderIndicator(headerCell); headerCell.addEventListener('click', function () { var state = getSortableTableState(table); var nextDirection = state.columnIndex === index && state.direction === 'asc' ? 'desc' : 'asc'; state.columnIndex = index; state.direction = nextDirection; sortTable(table, index, nextDirection); updateSortableHeaderState(table); }); headerCell.addEventListener('keydown', function (event) { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); headerCell.click(); } }); }); updateSortableHeaderState(table); }); } window.applyTableSort = applyTableSort; window.initSortableTables = initSortableTables; }());