|
|
|
@ -17,7 +17,7 @@
|
|
|
|
|
import {
|
|
|
|
|
dom, COLUMN, COLUMN_LITERAL, COMPARISON, ATTRIBUTES,
|
|
|
|
|
columnAscendingByDefault, columnIsSortable, COLUMN_TRANSFORMATION,
|
|
|
|
|
element, JOIN_URL_PASTE, communityQRCodeURL, STAFF_ID_PASTE, IDENTIFIER_PASTE, DETAILS_LINK_PASTE
|
|
|
|
|
element, JOIN_URL_PASTE, communityQRCodeURL, STAFF_ID_PASTE, IDENTIFIER_PASTE, DETAILS_LINK_PASTE, CLASSES, flagToLanguageAscii
|
|
|
|
|
} from './js/constants.js';
|
|
|
|
|
|
|
|
|
|
// Hidden communities for transparency.
|
|
|
|
@ -129,16 +129,26 @@ function onLoad() {
|
|
|
|
|
hideBadCommunities();
|
|
|
|
|
// Sort by server to show off new feature & align colors.
|
|
|
|
|
sortTable(COLUMN.SERVER_ICON);
|
|
|
|
|
initializeSearch();
|
|
|
|
|
createJoinLinkButtons();
|
|
|
|
|
markSortableColumns();
|
|
|
|
|
addQRModalHandlers();
|
|
|
|
|
addServerIconInteractions();
|
|
|
|
|
addSearchInteractions();
|
|
|
|
|
preloadImages();
|
|
|
|
|
setInterval(() => {
|
|
|
|
|
preloadImages();
|
|
|
|
|
}, 60 * 60E3);
|
|
|
|
|
reactToURLParameters();
|
|
|
|
|
addInformativeInteractions();
|
|
|
|
|
Array.from(document.querySelectorAll('.enter-clicks')).forEach(element => {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
element.addEventListener('keydown', (/** @type {KeyboardEvent} */ ev) => {
|
|
|
|
|
if (ev.key == "Enter") {
|
|
|
|
|
ev.currentTarget.click();
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -200,7 +210,7 @@ function displayQRModal(communityID, pane = 0) {
|
|
|
|
|
tagContainer.innerHTML = "";
|
|
|
|
|
|
|
|
|
|
tagContainer.append(
|
|
|
|
|
...JSON.parse(rowInfo.tags).map(tag => tagBody(tag))
|
|
|
|
|
...rowInfo.tags.map(tag => tagBody(tag))
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
dom.details_modal_qr_code().src = communityQRCodeURL(communityID);
|
|
|
|
@ -438,6 +448,46 @@ function addServerIconInteractions() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function addSearchInteractions() {
|
|
|
|
|
dom.btn_toggle_search()?.addEventListener('click', function (ev) {
|
|
|
|
|
location.hash="#";
|
|
|
|
|
const container = dom.search_container();
|
|
|
|
|
container?.classList.toggle(CLASSES.COMPONENTS.COLLAPSED);
|
|
|
|
|
if (!container?.classList.contains(CLASSES.COMPONENTS.COLLAPSED)) {
|
|
|
|
|
const searchBar = dom.search_bar();
|
|
|
|
|
searchBar?.focus();
|
|
|
|
|
// Inconsistent; attempt to align search bar to top to make more space for results.
|
|
|
|
|
searchBar?.scrollIntoView({ behavior: 'smooth', inline: 'start' });
|
|
|
|
|
} else {
|
|
|
|
|
useSearchTerm("");
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
dom.search_bar()?.addEventListener('keydown', function () {
|
|
|
|
|
setTimeout(() => useSearchTerm(this.value), 0);
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
dom.search_bar()?.addEventListener('keyup', function (ev) {
|
|
|
|
|
if (ev.key === "Enter") {
|
|
|
|
|
this.blur();
|
|
|
|
|
}
|
|
|
|
|
useSearchTerm(this.value);
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
dom.btn_clear_search()?.addEventListener('click', function () {
|
|
|
|
|
useSearchTerm("");
|
|
|
|
|
const searchBar = dom.search_bar();
|
|
|
|
|
searchBar?.focus();
|
|
|
|
|
searchBar.value = "";
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
Array.from(dom.sample_searches()).forEach(button => button.addEventListener('click', function() {
|
|
|
|
|
const targetSearch = button.getAttribute(ATTRIBUTES.SEARCH.TARGET_SEARCH);
|
|
|
|
|
useSearchTerm(targetSearch);
|
|
|
|
|
dom.search_bar().value = targetSearch;
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Function comparing two elements.
|
|
|
|
|
*
|
|
|
|
@ -562,25 +612,84 @@ function markSortableColumns() {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @type {HTMLTableRowElement[]}
|
|
|
|
|
*/
|
|
|
|
|
const communityFullRowCache = [];
|
|
|
|
|
|
|
|
|
|
function initializeSearch() {
|
|
|
|
|
communityFullRowCache.push(...dom.tbl_communities_content_rows());
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param {string} [rawTerm]
|
|
|
|
|
*/
|
|
|
|
|
function useSearchTerm(rawTerm) {
|
|
|
|
|
if (!rawTerm) {
|
|
|
|
|
replaceRowsWith(communityFullRowCache);
|
|
|
|
|
dom.search_bar()?.classList.remove(CLASSES.SEARCH.NO_RESULTS);
|
|
|
|
|
} else {
|
|
|
|
|
const term = rawTerm.toLowerCase().replace(/lang:(\S+)/g, "").trim();
|
|
|
|
|
const termTags = Array.from(rawTerm.matchAll(/#[^#\s]+/g)).map(match => match[0].slice(1).toLowerCase());
|
|
|
|
|
const termLanguage = rawTerm.match(/lang:(\S+)/)?.[1];
|
|
|
|
|
const newRows = communityFullRowCache.filter(
|
|
|
|
|
row => {
|
|
|
|
|
const rowInfo = dom.row_info(row);
|
|
|
|
|
const langAscii = rowInfo.language_flag && flagToLanguageAscii(rowInfo.language_flag).toLowerCase();
|
|
|
|
|
const rowName = rowInfo.name.toLowerCase();
|
|
|
|
|
const rowDesc = rowInfo.description.toLowerCase();
|
|
|
|
|
const rowTags = rowInfo.tags.map(({text}) => text.replace(/\s+/g, "-"));
|
|
|
|
|
if (termLanguage && !langAscii.includes(termLanguage.toLowerCase())) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (termTags.length >= 1) {
|
|
|
|
|
if (termTags.some(tag => rowTags.some(rowTag => rowTag.includes(tag)))) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return rowName.includes(term) || rowDesc.includes(term);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
if (newRows.length === 0) {
|
|
|
|
|
dom.search_bar()?.classList.add(CLASSES.SEARCH.NO_RESULTS);
|
|
|
|
|
} else {
|
|
|
|
|
dom.search_bar()?.classList.remove(CLASSES.SEARCH.NO_RESULTS);
|
|
|
|
|
}
|
|
|
|
|
replaceRowsWith(newRows);
|
|
|
|
|
}
|
|
|
|
|
sortTable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function replaceRowsWith(rows) {
|
|
|
|
|
dom.tbl_communities_content_rows().forEach(row => row.remove());
|
|
|
|
|
dom.tbl_communities().querySelector("tbody").append(...rows);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sorts the default communities table according the given column.
|
|
|
|
|
* Sort direction is determined by defaults; successive sorts
|
|
|
|
|
* on the same column reverse the sort direction.
|
|
|
|
|
* @param {number} column - Numeric ID of column being sorted.
|
|
|
|
|
* @param {number} [column] - Numeric ID of column being sorted. Re-applies last sort if absent.
|
|
|
|
|
*/
|
|
|
|
|
function sortTable(column) {
|
|
|
|
|
const table = dom.tbl_communities();
|
|
|
|
|
const sortState = getSortState(table);
|
|
|
|
|
const sortingAsBefore = column === undefined;
|
|
|
|
|
const sortingNewColumn = column !== sortState?.column;
|
|
|
|
|
const ascending = sortingNewColumn
|
|
|
|
|
? columnAscendingByDefault(column)
|
|
|
|
|
: !sortState.ascending;
|
|
|
|
|
const compare = makeRowComparer(column, ascending);
|
|
|
|
|
const sortedColumn = column ?? sortState?.column;
|
|
|
|
|
const ascending =
|
|
|
|
|
sortingAsBefore ?
|
|
|
|
|
sortState.ascending : (
|
|
|
|
|
sortingNewColumn
|
|
|
|
|
? columnAscendingByDefault(column)
|
|
|
|
|
: !sortState.ascending
|
|
|
|
|
);
|
|
|
|
|
const compare = makeRowComparer(sortedColumn, ascending);
|
|
|
|
|
const rows = Array.from(table.rows).slice(1);
|
|
|
|
|
rows.sort(compare);
|
|
|
|
|
rows.forEach((row) => row.remove());
|
|
|
|
|
table.querySelector("tbody").append(...rows);
|
|
|
|
|
setSortState(table, { ascending, column });
|
|
|
|
|
replaceRowsWith(rows);
|
|
|
|
|
setSortState(table, { ascending, column: sortedColumn });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// `html.js` selector for styling purposes
|
|
|
|
|