Common.js: Difference between revisions
From SUALEX
Created page with "→Any JavaScript here will be loaded for all users on every page load.: mw.loader.using(['jquery'], function () { function initColumnSearch($table) { var $thead = $table.find('thead'); var $rows = $table.find('tbody tr'); 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')...." |
No edit summary |
||
| Line 1: | Line 1: | ||
/* Any JavaScript here will be loaded for all users on every page load. */ | /* Any JavaScript here will be loaded for all users on every page load. */ | ||
mw.loader.using(['jquery'], function () { | 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 | function initColumnSearchAndPager($table) { | ||
var $thead = $table.find('thead'); | var $thead = $table.find('thead'); | ||
// Ensure a thead exists (clone first row into thead if necessary) | |||
if ($thead.length === 0) { | if ($thead.length === 0) { | ||
var $firstRow = $table.find('tr').first(); | var $firstRow = $table.find('tr').first(); | ||
| Line 13: | Line 24: | ||
} | } | ||
var $headerRow = $thead.find('tr').last(); | var $headerRow = $thead.find('tr').last(); | ||
var $filterRow = $('<tr class="searchable-row">'); | var $filterRow = $('<tr class="searchable-row">'); | ||
$headerRow.find('th'). | var colCount = $headerRow.find('th').length; | ||
for (var colIndex = 0; colIndex < colCount; colIndex++) { | |||
var $input = $('<input>', { | var $input = $('<input>', { | ||
type: 'search', | type: 'search', | ||
placeholder: 'Search…' | placeholder: 'Search…', | ||
'data-col-index': colIndex | |||
}).css({ | }).css({ | ||
width: '95%', | width: '95%', | ||
| Line 26: | Line 40: | ||
$filterRow.append($('<th>').append($input)); | $filterRow.append($('<th>').append($input)); | ||
} | } | ||
$headerRow.before($filterRow); | $headerRow.before($filterRow); | ||
$ | // Pager UI (placed above the table) | ||
var $pager = $('<div class="table-pager" style="margin:0.5em 0; display:flex; align-items:center; gap:0.5em;">'); | |||
var $prev = $('<button type="button" class="pager-prev">Prev</button>'); | |||
var $next = $('<button type="button" class="pager-next">Next</button>'); | |||
var $pageInfo = $('<span class="pager-pageinfo">'); | |||
var $status = $('<span class="pager-status">'); | |||
$pager.append($prev, $next, $pageInfo, $status); | |||
$table.before($pager); | |||
// Page size fixed per request | |||
var pageSize = 100; | |||
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); | |||
$pageInfo.text('Page ' + currentPage + ' of ' + totalPages); | |||
if (totalMatches === 0) { | |||
$status.text('Showing 0 entries'); | |||
} else { | |||
$status.text('Showing ' + start + '–' + end + ' of ' + totalMatches + ' entries'); | |||
} | |||
$prev.prop('disabled', currentPage <= 1); | |||
$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); | |||
} | |||
// Prev / Next handlers | |||
$prev.on('click', function () { | |||
if (currentPage > 1) { | |||
currentPage--; | |||
renderPage(); | |||
} | |||
}); | |||
$next.on('click', function () { | |||
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(); | |||
} | |||
}); | |||
// Filtering logic (applies across all rows) | |||
function applyFilters() { | |||
var filters = []; | var filters = []; | ||
$filterRow.find('input').each(function () { | $filterRow.find('input').each(function () { | ||
filters.push($(this).val().toLowerCase()); | filters.push($(this).val().toLowerCase()); | ||
}); | }); | ||
$ | // Recompute rows set | ||
$allRows = getAllRows(); | |||
$allRows.each(function () { | |||
var $cells = $(this).find('td'); | var $cells = $(this).find('td'); | ||
var visible = true; | var visible = true; | ||
filters. | for (var i = 0; i < filters.length; i++) { | ||
if (!text) | 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; | visible = false; | ||
break; | |||
} | } | ||
} | } | ||
$(this). | $(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 () { | $(function () { | ||
$('table.searchable.sortable').each(function () { | $('table.searchable.sortable').each(function () { | ||
initColumnSearchAndPager($(this)); | |||
}); | }); | ||
}); | }); | ||
}); | }); | ||
Revision as of 00:14, 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 (placed above the table)
var $pager = $('<div class="table-pager" style="margin:0.5em 0; display:flex; align-items:center; gap:0.5em;">');
var $prev = $('<button type="button" class="pager-prev">Prev</button>');
var $next = $('<button type="button" class="pager-next">Next</button>');
var $pageInfo = $('<span class="pager-pageinfo">');
var $status = $('<span class="pager-status">');
$pager.append($prev, $next, $pageInfo, $status);
$table.before($pager);
// Page size fixed per request
var pageSize = 100;
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);
$pageInfo.text('Page ' + currentPage + ' of ' + totalPages);
if (totalMatches === 0) {
$status.text('Showing 0 entries');
} else {
$status.text('Showing ' + start + '–' + end + ' of ' + totalMatches + ' entries');
}
$prev.prop('disabled', currentPage <= 1);
$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);
}
// Prev / Next handlers
$prev.on('click', function () {
if (currentPage > 1) {
currentPage--;
renderPage();
}
});
$next.on('click', function () {
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();
}
});
// 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));
});
});
});