Common.js: Difference between revisions
From SUALEX
No edit summary |
No edit summary |
||
| (One intermediate revision by the same user not shown) | |||
| Line 44: | Line 44: | ||
$headerRow.before($filterRow); | $headerRow.before($filterRow); | ||
// Pager UI ( | // Pager UI | ||
function createPager() { | |||
var $pager = $('<div class="table-pager">').css({ | |||
margin: '0.5em 0', | |||
display: 'flex', | |||
justifyContent: 'space-between', | |||
alignItems: 'center', | |||
gap: '0.5em' | |||
}); | |||
var $left = $('<div class="pager-left">').css({ display: 'flex', alignItems: 'center', gap: '0.5em' }); | |||
var pageSize = | var $right = $('<div class="pager-right">').css({ display: 'flex', alignItems: 'center', gap: '0.5em' }); | ||
var $status = $('<span class="pager-status">'); | |||
var $pageInfo = $('<span class="pager-pageinfo">'); | |||
var $prev = $('<button type="button" class="pager-prev">◀</button>'); | |||
var $next = $('<button type="button" class="pager-next">▶</button>'); | |||
$left.append($status); | |||
$right.append($pageInfo, $prev, $next); | |||
$pager.append($left, $right); | |||
return { | |||
$pager: $pager, | |||
$status: $status, | |||
$pageInfo: $pageInfo, | |||
$prev: $prev, | |||
$next: $next | |||
}; | |||
} | |||
var topPager = createPager(); | |||
var bottomPager = createPager(); | |||
$table.before(topPager.$pager); | |||
$table.after(bottomPager.$pager); | |||
var pageSize = 50; | |||
var currentPage = 1; | var currentPage = 1; | ||
| Line 61: | Line 89: | ||
var $rows = $table.find('tbody tr'); | var $rows = $table.find('tbody tr'); | ||
if ($rows.length === 0) { | if ($rows.length === 0) { | ||
// fallback: all tr under table excluding thead rows | |||
$rows = $table.find('tr').not($thead.find('tr')); | $rows = $table.find('tr').not($thead.find('tr')); | ||
} | } | ||
| Line 76: | Line 105: | ||
var end = Math.min(currentPage * pageSize, totalMatches); | var end = Math.min(currentPage * pageSize, totalMatches); | ||
var pageText = 'Page ' + currentPage + ' of ' + totalPages; | |||
var statusText = (totalMatches === 0) | |||
? 'Showing 0 entries' | |||
: 'Showing ' + start + '–' + end + ' of ' + totalMatches + ' entries'; | |||
// Update both pagers | |||
topPager.$pageInfo.text(pageText); | |||
bottomPager.$pageInfo.text(pageText); | |||
topPager.$status.text(statusText); | |||
bottomPager.$status.text(statusText); | |||
$prev.prop('disabled', currentPage <= 1); | // enable/disable both prev/next buttons | ||
$next.prop('disabled', currentPage >= totalPages); | topPager.$prev.prop('disabled', currentPage <= 1); | ||
bottomPager.$prev.prop('disabled', currentPage <= 1); | |||
topPager.$next.prop('disabled', currentPage >= totalPages); | |||
bottomPager.$next.prop('disabled', currentPage >= totalPages); | |||
} | } | ||
| Line 109: | Line 145: | ||
} | } | ||
// | // Navigation button handlers | ||
function goPrev() { | |||
if (currentPage > 1) { | if (currentPage > 1) { | ||
currentPage--; | currentPage--; | ||
renderPage(); | renderPage(); | ||
} | } | ||
} | } | ||
function goNext() { | |||
var totalMatches = $allRows.filter(function () { return $(this).data('matched') === true; }).length; | var totalMatches = $allRows.filter(function () { return $(this).data('matched') === true; }).length; | ||
var totalPages = Math.max(1, Math.ceil(totalMatches / pageSize)); | var totalPages = Math.max(1, Math.ceil(totalMatches / pageSize)); | ||
| Line 123: | Line 159: | ||
renderPage(); | renderPage(); | ||
} | } | ||
}); | } | ||
// Wire handlers for both pagers | |||
topPager.$prev.on('click', goPrev); | |||
bottomPager.$prev.on('click', goPrev); | |||
topPager.$next.on('click', goNext); | |||
bottomPager.$next.on('click', goNext); | |||
// Filtering logic (applies across all rows) | // Filtering logic (applies across all rows) | ||
Latest revision as of 01:39, 31 January 2026
/* Any JavaScript here will be loaded for all users on every page load. */
mw.loader.using(['jquery'], function () {
// Debounce helper to avoid too-frequent re-evaluations while typing
function debounce(fn, wait) {
var timeout;
return function () {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function () {
fn.apply(context, args);
}, wait);
};
}
function initColumnSearchAndPager($table) {
var $thead = $table.find('thead');
// Ensure a thead exists (clone first row into thead if necessary)
if ($thead.length === 0) {
var $firstRow = $table.find('tr').first();
$thead = $('<thead>').append($firstRow.clone());
$firstRow.remove();
$table.prepend($thead);
}
var $headerRow = $thead.find('tr').last();
var $filterRow = $('<tr class="searchable-row">');
var colCount = $headerRow.find('th').length;
for (var colIndex = 0; colIndex < colCount; colIndex++) {
var $input = $('<input>', {
type: 'search',
placeholder: 'Search…',
'data-col-index': colIndex
}).css({
width: '95%',
boxSizing: 'border-box'
});
$filterRow.append($('<th>').append($input));
}
$headerRow.before($filterRow);
// Pager UI
function createPager() {
var $pager = $('<div class="table-pager">').css({
margin: '0.5em 0',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
gap: '0.5em'
});
var $left = $('<div class="pager-left">').css({ display: 'flex', alignItems: 'center', gap: '0.5em' });
var $right = $('<div class="pager-right">').css({ display: 'flex', alignItems: 'center', gap: '0.5em' });
var $status = $('<span class="pager-status">');
var $pageInfo = $('<span class="pager-pageinfo">');
var $prev = $('<button type="button" class="pager-prev">◀</button>');
var $next = $('<button type="button" class="pager-next">▶</button>');
$left.append($status);
$right.append($pageInfo, $prev, $next);
$pager.append($left, $right);
return {
$pager: $pager,
$status: $status,
$pageInfo: $pageInfo,
$prev: $prev,
$next: $next
};
}
var topPager = createPager();
var bottomPager = createPager();
$table.before(topPager.$pager);
$table.after(bottomPager.$pager);
var pageSize = 50;
var currentPage = 1;
// Helper to fetch current candidate rows (recomputed when needed)
function getAllRows() {
var $rows = $table.find('tbody tr');
if ($rows.length === 0) {
// fallback: all tr under table excluding thead rows
$rows = $table.find('tr').not($thead.find('tr'));
}
return $rows;
}
// Initialize matched flag
var $allRows = getAllRows();
$allRows.each(function () { $(this).data('matched', true); });
function updatePagerUI(totalMatches) {
var totalPages = Math.max(1, Math.ceil(totalMatches / pageSize));
// compute shown range
var start = (totalMatches === 0) ? 0 : (currentPage - 1) * pageSize + 1;
var end = Math.min(currentPage * pageSize, totalMatches);
var pageText = 'Page ' + currentPage + ' of ' + totalPages;
var statusText = (totalMatches === 0)
? 'Showing 0 entries'
: 'Showing ' + start + '–' + end + ' of ' + totalMatches + ' entries';
// Update both pagers
topPager.$pageInfo.text(pageText);
bottomPager.$pageInfo.text(pageText);
topPager.$status.text(statusText);
bottomPager.$status.text(statusText);
// enable/disable both prev/next buttons
topPager.$prev.prop('disabled', currentPage <= 1);
bottomPager.$prev.prop('disabled', currentPage <= 1);
topPager.$next.prop('disabled', currentPage >= totalPages);
bottomPager.$next.prop('disabled', currentPage >= totalPages);
}
function renderPage() {
// Recompute rows set (in case table changed)
$allRows = getAllRows();
// Hide everything first
$allRows.hide();
// Filtered rows (preserve order)
var $matched = $allRows.filter(function () { return $(this).data('matched') === true; });
var totalMatches = $matched.length;
var totalPages = Math.max(1, Math.ceil(totalMatches / pageSize));
if (currentPage > totalPages) currentPage = totalPages;
var startIndex = (currentPage - 1) * pageSize;
var endIndex = startIndex + pageSize;
$matched.slice(startIndex, endIndex).show();
updatePagerUI(totalMatches);
}
// Navigation button handlers
function goPrev() {
if (currentPage > 1) {
currentPage--;
renderPage();
}
}
function goNext() {
var totalMatches = $allRows.filter(function () { return $(this).data('matched') === true; }).length;
var totalPages = Math.max(1, Math.ceil(totalMatches / pageSize));
if (currentPage < totalPages) {
currentPage++;
renderPage();
}
}
// Wire handlers for both pagers
topPager.$prev.on('click', goPrev);
bottomPager.$prev.on('click', goPrev);
topPager.$next.on('click', goNext);
bottomPager.$next.on('click', goNext);
// Filtering logic (applies across all rows)
function applyFilters() {
var filters = [];
$filterRow.find('input').each(function () {
filters.push($(this).val().toLowerCase());
});
// Recompute rows set
$allRows = getAllRows();
$allRows.each(function () {
var $cells = $(this).find('td');
var visible = true;
for (var i = 0; i < filters.length; i++) {
var text = filters[i];
if (!text) continue;
var cell = $cells.eq(i);
var cellText = cell.length ? cell.text().toLowerCase() : '';
if (cellText.indexOf(text) === -1) {
visible = false;
break;
}
}
$(this).data('matched', visible);
});
// Reset to first page when filters change
currentPage = 1;
renderPage();
}
// Debounce input handler for better UX on large tables
var debouncedApply = debounce(applyFilters, 150);
$filterRow.find('input').on('input', debouncedApply);
// Expose a reapply function for external code that mutates or sorts the table
$table.data('reapplySearchAndPaging', function () {
applyFilters();
});
// Initial render
renderPage();
}
// Initialize every matching table on DOM ready
$(function () {
$('table.searchable.sortable').each(function () {
initColumnSearchAndPager($(this));
});
});
});