var timeouts = {};
tableFilters = {};
tableSorts = {};
$(document).on("selectedServerChange", async function() {
// Clear filters and sorts when changing server
tableFilters = {};
tableSorts = {};
})
function setTableFilter(tableId, key, value) {
if (!tableFilters[tableId])
tableFilters[tableId] = {}
tableFilters[tableId][key] = value;
}
function setTableSorts(tableId, value) {
tableSorts[tableId] = value;
}
function ensureTablePagination(tableId) {
if (!tableFilters[tableId])
throw new Error(`${tableId} isn't a valid table ID`);
if (!tableFilters[tableId]["page"])
setTableFilter(tableId, "page", "1");
if (!tableFilters[tableId]["page_size"])
setTableFilter(tableId, "page_size", $(`#${tableId}`).closest(".tab-pane").find(".table-page-sizer").val());
}
async function initTable(containingSelector, tableId, loadDataFunc, newRowFunc, deleteSelectedFunc, options=null) {
let pageSizeId = tableId + "PageSize";
searchId = tableId + "SearchBar";
sortDropdownId = tableId + "SortDropdown";
filterDropdownId = tableId + "FilterDropdown";
createSearchRow(containingSelector, searchId, sortDropdownId, filterDropdownId, options, newRowFunc, deleteSelectedFunc);
createTable(containingSelector, tableId);
createTableControls(containingSelector, pageSizeId);
await bindSearchBar(tableId, searchId, loadDataFunc);
await bindSortDropdown(tableId, sortDropdownId, loadDataFunc);
await bindFilterDropdown(tableId, filterDropdownId, loadDataFunc);
await bindTablePagination(tableId, `${containingSelector} .table-pagination`, loadDataFunc);
await bindTablePaginationResizer(tableId, `${containingSelector} .table-page-sizer`, loadDataFunc);
}
function createSearchRow(containingSelector, searchId, sortDropdownId, filterDropdownId, options, newRowFunc, deleteSelectedFunc) {
$(containingSelector).append(`
`);
// If there is a function that allows the creation of new rows, create a button for it
if (newRowFunc) {
$(`${containingSelector} .table-search-row .table-search-buttons`).prepend(`
`)
$(`${containingSelector} .table-search-row .table-search-buttons .table-new-btn`).on("click", async () => {await newRowFunc(-1)});
}
// Show the sort dropdown
if (options.sort && options.actions.GET) {
$(`${containingSelector} .table-search-row .table-search-buttons`).append(`
Sort By
`);
populateSortDropdown(sortDropdownId, options.actions.GET, options.sort);
}
// Show the filters dropdown
if (options.filter && options.actions.GET) {
$(`${containingSelector} .table-search-row .table-search-buttons`).append(`
Filter By
`);
populateFilterDropdown(filterDropdownId, options.actions.GET, options.filter);
}
// If there is a function for deleting selected rows, create a button for it
if (deleteSelectedFunc) {
$(`${containingSelector} .table-search-row .table-search-buttons`).append(`
`)
$(`${containingSelector} .table-search-row .table-search-buttons .table-del-btn`).on("click", async () => {await deleteSelectedFunc()});
}
}
function populateSortDropdown(sortDropdownId, options, sortKeys) {
for (key in options) {
if (!sortKeys.includes(key))
continue;
let label = options[key].label;
$elem = null;
$elem = $(`
`);
bindSortBoolean($elem);
$(`#${sortDropdownId} .dropdown-menu`).append($elem);
updateSortCheckboxState($elem);
}
}
function populateFilterDropdown(filterDropdownId, options, filterKeys) {
for (key in options) {
if (!filterKeys.includes(key))
continue;
let label = options[key].label;
id = key + "FilterOption";
$elem = null;
switch (options[key].type) {
case ("boolean"):
$elem = $(`
`);
bindFilterBoolean($elem);
$elem.find('input[type="checkbox"]').prop("checked", options[key].default ? true : false).trigger("change");
updateFilterCheckboxState($elem);
break
default:
continue;
}
$(`#${filterDropdownId} .dropdown-menu`).append($elem);
}
// Reset the controls
$(document).on("selectedServerChange", async function() {
$(`#${filterDropdownId} .dropdown-menu input[type="checkbox"]`).each(function() {
$(this).data("state", null);
updateFilterCheckboxState($(this).parent());
});
});
}
function bindSortBoolean($elem) {
$elem.find("button").on("click", function() {
// Reset sibling buttons
$(this).parent().siblings("li").each(function() {
$(this).find("button").data("state", null);
updateSortCheckboxState($(this));
});
let state = $(this).data("state");
state = determineState(state);
$(this).data("state", state);
updateSortCheckboxState($elem)
});
}
function bindFilterBoolean($elem) {
$elem.find('label').on("click", function() {
let $input = $elem.find('input[type="checkbox"]');
let state = $input.data("state");
state = determineState(state);
$input.data("state", state);
updateFilterCheckboxState($elem);
});
}
function determineState(state) {
switch (state) {
case true:
return false;
case false:
return null;
case null:
default:
return true;
}
}
function updateSortCheckboxState($elem) {
let state = $elem.find("button").data("state");
let $icon = $elem.find(".bi");
$icon.removeClass();
switch (state) {
case true:
$icon.addClass("bi bi-sort-alpha-up");
break;
case false:
$icon.addClass("bi bi-sort-alpha-down-alt");
break;
case null:
default:
$icon.addClass("bi bi-slash invisible");
}
}
function updateFilterCheckboxState($elem) {
let state = $elem.find('input[type="checkbox"]').data("state");
let $icon = $elem.find(".bi");
switch (state) {
case true:
$elem.find('input[type="checkbox"]').prop("checked", true);
$icon.removeClass().addClass("bi bi-check");
break;
case false:
$elem.find('input[type="checkbox"]').prop("checked", false);
$icon.removeClass().addClass("bi bi-x");
break;
case null:
default:
$elem.find('input[type="checkbox"]').prop("checked", false);
$icon.removeClass().addClass("bi bi-dash invisible");
break;
}
}
async function bindSearchBar(tableId, searchBarSelector, loadDataFunc) {
searchBar = $("#" + searchBarSelector)
searchBar.on("input", async function() {
clearTimeout(timeouts[searchBarSelector]);
var searchString = $(this).val();
setTableFilter(tableId, "search", searchString);
timeouts[searchBarSelector] = setTimeout(async function() {
await loadDataFunc(getCurrentlyActiveServer().guild_id);
}, 300);
});
}
async function bindFilterDropdown(tableId, filterDropdownId, loadDataFunc) {
$(`#${filterDropdownId} .dropdown-menu`).on("change", "input", async function() {
setTableFilter(tableId, $(this).attr("data-key"), $(this).data("state"));
setTableFilter(tableId, "page", "1");
await loadDataFunc(getCurrentlyActiveServer().guild_id);
});
}
async function bindSortDropdown(tableId, sortDropdownId, loadDataFunc) {
$(`#${sortDropdownId} .dropdown-menu`).on("click", "button", async function() {
let state = $(this).data("state");
sortKey = "";
switch(state) {
case true:
sortKey = $(this).attr("data-sortkey")
break;
case false:
sortKey = "-" + $(this).attr("data-sortkey")
break;
case null:
default:
sortKey = ""
}
setTableSorts(tableId, sortKey);
await loadDataFunc(getCurrentlyActiveServer().guild_id);
});
}
function createTable(containingSelector, tableId) {
$(containingSelector).append(`
`);
}
function createTableControls(containingSelector, pageSizeId) {
$(containingSelector).append(`
`);
$("#" + pageSizeId).select2({
theme: "bootstrap",
minimumResultsForSearch: 10,
});
}
function updateTableContainer(containerId, page, pageSize, itemsCount, totalItemsCount, nextExists, prevExists) {
if (!page || !pageSize)
throw new Error(`Missing page data '${containerId}': page=${page} pageSize=${pageSize}`);
updateTablePagination(
`#${containerId} .table-pagination`,
page,
pageSize,
totalItemsCount,
nextExists,
prevExists
);
updateTablePaginationInfo(
`#${containerId} .table-page-info`,
itemsCount,
totalItemsCount
);
}
// Updates the pagination text for a given pageInfoId
function updateTablePaginationInfo(pageInfoId, showing, total) {
$(`${pageInfoId} .pageinfo-showing`).text(showing);
$(`${pageInfoId} .pageinfo-total`).text(`${total} Result${total > 1 ? "s" : ""}`);
}
// Updates the pagination buttons for a given pageControlsId
function updateTablePagination(pageControlsId, currentPage, pageSize, totalItems, nextExists, prevExists) {
$(pageControlsId).attr("data-page", currentPage);
// Remove existing page specific buttons
$(`${pageControlsId} .page-pick`).remove();
// Determine states of 'previous page' 'next page' buttons
$(`${pageControlsId} .page-prev`).toggleClass("disabled", !prevExists).attr("tabindex", prevExists ? "" : "-1");
$(`${pageControlsId} .page-next`).toggleClass("disabled", !nextExists).attr("tabindex", nextExists ? "" : "-1");
// Calculate amount of pages to account for
const pages = Math.max(Math.ceil(totalItems / pageSize), 1);
const maxVisiblePages = 10;
let startPage, endPage;
currentPage = parseInt(currentPage);
pageSize = parseInt(pageSize);
if (pages <= maxVisiblePages) {
// If total pages are less than or equal to max visible pages, show all
startPage = 1;
endPage = pages;
} else {
// Determine the start and end pages
const halfVisible = Math.floor(maxVisiblePages / 2);
if (currentPage <= halfVisible) {
startPage = 1;
endPage = maxVisiblePages;
} else if (currentPage + halfVisible >= pages) {
startPage = pages - maxVisiblePages + 1;
endPage = pages;
} else {
startPage = currentPage - halfVisible;
endPage = currentPage + halfVisible;
}
}
if (startPage > 1) {
addPageButton(pageControlsId, 1);
if (startPage > 2) {
addEllipsis(pageControlsId);
}
}
for (let i = startPage; i <= endPage; i++) {
addPageButton(pageControlsId, i, currentPage);
}
if (endPage < pages) {
if (endPage < pages - 1) {
addEllipsis(pageControlsId);
}
addPageButton(pageControlsId, pages);
}
}
function addPageButton(pageControlsId, pageNumber, currentPage) {
let pageItem = $("