You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
339 lines
10 KiB
JavaScript
339 lines
10 KiB
JavaScript
let page = "Padliography";
|
|
let endpoint = `https://pzwiki.wdka.nl/mw-mediadesign/api.php?action=parse&format=json&origin=*&page=${page}&prop=text`;
|
|
|
|
const container = document.getElementById("wiki-contents");
|
|
const filtersContainer = document.getElementById("filters-container");
|
|
const activeAllButton = document.getElementById("active-all");
|
|
|
|
// We use a set and not an array in order to avoid repetition for the categories
|
|
let filters = new Set();
|
|
|
|
// Variables for the filter system
|
|
let allActive = true;
|
|
let tagList;
|
|
|
|
// Global variable for the generated table
|
|
let table;
|
|
|
|
// Request the content to the Wiki API and parse it as a JSON file
|
|
fetch(endpoint)
|
|
.then((response) => response.json())
|
|
.then((data) => {
|
|
// TODO: custom class in the table to call the element
|
|
// Create a new element to parse the response from the wiki and get an HTML table element out of it
|
|
let section = document.createElement("div");
|
|
section.innerHTML = data.parse.text["*"];
|
|
|
|
// Find the table with the padliography custom class (remember to set it up in the wiki table)
|
|
let wikiTable = section.getElementsByClassName("padliography")[0];
|
|
|
|
// Create a new table using the data in the Wiki table
|
|
createTable(wikiTable);
|
|
|
|
// Fill the filter section with all the tags gathered in the filters Set
|
|
// We don't need any argument because the filters set is defined as global
|
|
createFilters();
|
|
|
|
// Initially show all the categories
|
|
showAllTags();
|
|
|
|
// Sort by date, last first (oke srry for this, the sorting function is a toggle atm)
|
|
sortTable(headers["Date"]);
|
|
sortTable(headers["Date"]);
|
|
});
|
|
|
|
// ---
|
|
// Contents Generation
|
|
// ---
|
|
|
|
function createTable(data) {
|
|
table = document.createElement("table");
|
|
|
|
table.appendChild(createHeaders());
|
|
|
|
// Traverse the rows collection as an array
|
|
[...data.rows].forEach((row, index) => {
|
|
// Avoid first row that contains column titles
|
|
if (index > 0) {
|
|
// Create a row element
|
|
let tableRow = document.createElement("tr");
|
|
|
|
// Create a list of tags from the categories cell
|
|
let categoryTags = row.cells[3].innerText.split(",").map((category) => category.trim());
|
|
// Set the categories as classes for future filtering
|
|
categoryTags.forEach((tag) => tableRow.classList.add(tag));
|
|
|
|
// --- --- ---
|
|
// --- Beginning of the row
|
|
// Columns order could be modified simply by reordering the next 4 sections
|
|
|
|
// Create a cell with date
|
|
let date = document.createElement("td");
|
|
date.classList.add("date");
|
|
date.innerHTML = createDate(row.cells[4].innerText);
|
|
tableRow.appendChild(date);
|
|
|
|
// Create a cell with title + link to the pad
|
|
let title = document.createElement("td");
|
|
title.classList.add("title");
|
|
title.appendChild(createTitleLink(row));
|
|
tableRow.appendChild(title);
|
|
|
|
// Create a cell with categories
|
|
let categories = document.createElement("td");
|
|
categories.classList.add("categories");
|
|
categories.appendChild(createTags(categoryTags));
|
|
tableRow.appendChild(categories);
|
|
|
|
// Create a cell with the overview
|
|
let overview = document.createElement("td");
|
|
overview.classList.add("overview");
|
|
overview.innerHTML = row.cells[2].innerText;
|
|
tableRow.appendChild(overview);
|
|
|
|
// --- End of the row
|
|
// --- --- ---
|
|
|
|
// Insert the row in the table
|
|
table.appendChild(tableRow);
|
|
}
|
|
});
|
|
// Insert the table in the container
|
|
container.appendChild(table);
|
|
}
|
|
|
|
let headers = {};
|
|
|
|
function createHeaders() {
|
|
let firstRow = document.createElement("tr");
|
|
firstRow.classList.add("header");
|
|
let titles = ["Date", "Title", "Categories", "Overview"];
|
|
|
|
titles.forEach((title) => {
|
|
let th = document.createElement("th");
|
|
th.innerHTML = title;
|
|
// Add sorting callback for Date and Title
|
|
// Do we need to sort also the categories? im not sure!
|
|
if (title === "Date" || title === "Title")
|
|
th.addEventListener("click", () => sortTable(th));
|
|
firstRow.appendChild(th);
|
|
|
|
// Add header to the headers object in order to use the sort later
|
|
headers[title] = th;
|
|
});
|
|
|
|
return firstRow;
|
|
}
|
|
|
|
function createTitleLink(row) {
|
|
// Take the first cell of the wiki (link)
|
|
let url = row.cells[0].innerText;
|
|
// Take the second cell of the wiki (title)
|
|
let title = row.cells[1].innerText;
|
|
|
|
// Create a link element
|
|
let link = document.createElement("a");
|
|
link.setAttribute("target", "_blank");
|
|
|
|
// TODO: stretched link for making all the row clickabe?
|
|
link.classList.add("stretched-link");
|
|
|
|
// Use the link as href
|
|
link.href = url;
|
|
// Use the title as text
|
|
link.innerHTML = title;
|
|
|
|
return link;
|
|
}
|
|
|
|
function createDate(string) {
|
|
let parsed = string.replaceAll("/", ".");
|
|
|
|
let date = new Date(string);
|
|
if (date) {
|
|
const options = { year: "numeric", month: "short", day: "numeric" };
|
|
parsed = date.toLocaleDateString("en-EN", options);
|
|
if (parsed === "Invalid Date") parsed = "???";
|
|
}
|
|
return parsed;
|
|
}
|
|
|
|
// Take a list of categories and create an unordered list with them
|
|
// + store every category to the filters Set
|
|
function createTags(categories) {
|
|
// Create a list to contain all the tags
|
|
let tagsContainer = document.createElement("ul");
|
|
|
|
// For each category create a li element
|
|
categories
|
|
.sort((a, b) => a.localeCompare(b))
|
|
.forEach((item) => {
|
|
let tag = document.createElement("li");
|
|
tag.classList.add("tag");
|
|
tag.innerHTML = item;
|
|
tagsContainer.appendChild(tag);
|
|
|
|
// add the category to the filters Set to use it later
|
|
filters.add(item);
|
|
});
|
|
|
|
return tagsContainer;
|
|
}
|
|
|
|
// ---
|
|
// Filtering
|
|
// ---
|
|
|
|
// Parse the filters Set to create a list of interactive list elements
|
|
function createFilters() {
|
|
Array.from(filters)
|
|
.sort((a, b) => a.localeCompare(b))
|
|
.forEach((item) => {
|
|
let tag = document.createElement("li");
|
|
// Store the filter value in the markup and use it for filtering later
|
|
tag.setAttribute("data-tag", item);
|
|
// Tab index is for accessibility via keyboard
|
|
tag.setAttribute("tabindex", 0);
|
|
tag.setAttribute("role", "button");
|
|
tag.classList.add("tag");
|
|
tag.innerHTML = item;
|
|
filtersContainer.appendChild(tag);
|
|
});
|
|
// Build a list of the tag for managing the filter
|
|
tagList = document.querySelectorAll("[data-tag]");
|
|
}
|
|
|
|
// Manage all the combinations when a filter is clicked or selected
|
|
function conditionalToggle(element) {
|
|
// If all the filters are active disable them all except the one selected
|
|
if (allActive) {
|
|
tagList.forEach((tag) => {
|
|
filters.delete(tag.getAttribute("data-tag"));
|
|
tag.classList.remove("active");
|
|
tag.setAttribute("aria-expanded", "false");
|
|
});
|
|
activeAllButton.classList.remove("active");
|
|
allActive = false;
|
|
}
|
|
|
|
// If the filter is active turn it off, else the contrary
|
|
if (element.classList.contains("active")) {
|
|
element.classList.remove("active");
|
|
element.setAttribute("aria-expanded", "false");
|
|
filters.delete(element.getAttribute("data-tag"));
|
|
} else {
|
|
element.classList.add("active");
|
|
element.setAttribute("aria-expanded", "true");
|
|
filters.add(element.getAttribute("data-tag"));
|
|
}
|
|
|
|
// Check if every filter is active or not
|
|
// use it to apply the correct style when all the filters are active
|
|
allActive = tagList.length == filters.size;
|
|
if (allActive) {
|
|
activeAllButton.classList.add("active");
|
|
activeAllButton.setAttribute("aria-expanded", "true");
|
|
}
|
|
|
|
// If there are no filters active enable them all
|
|
if (filters.size === 0) {
|
|
showAllTags();
|
|
}
|
|
}
|
|
|
|
// Display the rows that have their categories in the filters Set
|
|
function showSelectedRows() {
|
|
[...table.rows].forEach((row) => {
|
|
row.classList.remove("active");
|
|
});
|
|
|
|
filters.forEach((filter) => {
|
|
let selectedRows = [...table.rows].filter((el) => {
|
|
filter = filter.replaceAll(" ", "-");
|
|
return el.classList.contains(filter);
|
|
});
|
|
selectedRows.forEach((row) => row.classList.add("active"));
|
|
});
|
|
|
|
allActiveCheck();
|
|
}
|
|
|
|
function showAllTags() {
|
|
tagList.forEach((tag) => {
|
|
filters.add(tag.getAttribute("data-tag"));
|
|
tag.classList.add("active");
|
|
tag.setAttribute("aria-expanded", "true");
|
|
});
|
|
allActive = true;
|
|
showSelectedRows();
|
|
}
|
|
|
|
function allActiveCheck() {
|
|
if (tagList.length === filters.size) {
|
|
allActive = true;
|
|
tagList.forEach((tag) => tag.classList.add("all"));
|
|
filtersContainer.firstElementChild.classList.add("active");
|
|
} else {
|
|
allActive = false;
|
|
tagList.forEach((tag) => tag.classList.remove("all"));
|
|
filtersContainer.firstElementChild.classList.remove("active");
|
|
}
|
|
}
|
|
|
|
// ---
|
|
// Sorting table
|
|
// kindly adapted from https://stackoverflow.com/questions/14267781/sorting-html-table-with-javascript
|
|
// TODO: Sorting UI
|
|
// ---
|
|
|
|
const getCellValue = (tr, idx) => {
|
|
if (tr.children[idx].classList.contains("date")) return new Date(tr.children[idx].innerText);
|
|
else return tr.children[idx].innerText || tr.children[idx].textContent;
|
|
};
|
|
// wow this is really obscure wtf not happy with it at all
|
|
const comparer = (idx, asc) => (a, b) =>
|
|
((v1, v2) =>
|
|
v1 !== "" && v2 !== "" && !isNaN(v1) && !isNaN(v2)
|
|
? v1 - v2
|
|
: v1.toString().localeCompare(v2))(
|
|
getCellValue(asc ? a : b, idx),
|
|
getCellValue(asc ? b : a, idx)
|
|
);
|
|
|
|
// The function is added as callback in the createHeaders function
|
|
const sortTable = (th) => {
|
|
const table = th.closest("table");
|
|
Array.from(table.querySelectorAll("tr:nth-child(n+2)"))
|
|
.sort(comparer(Array.from(th.parentNode.children).indexOf(th), (this.asc = !this.asc)))
|
|
.forEach((tr) => table.appendChild(tr));
|
|
};
|
|
|
|
// ---
|
|
// Event Listener
|
|
// ---
|
|
|
|
filtersContainer.addEventListener("click", (e) => {
|
|
if (e.target.tagName === "LI") {
|
|
conditionalToggle(e.target);
|
|
showSelectedRows();
|
|
}
|
|
});
|
|
|
|
activeAllButton.addEventListener("click", (e) => {
|
|
activeAllButton.classList.add("active");
|
|
activeAllButton.setAttribute("aria-expanded", "true");
|
|
showAllTags();
|
|
});
|
|
|
|
document.addEventListener("keydown", (event) => {
|
|
if (event.isComposing || event.keyCode === 229) {
|
|
return;
|
|
}
|
|
if (event.keyCode === 32) {
|
|
if (document.activeElement.tagName === "LI") {
|
|
conditionalToggle(document.activeElement);
|
|
showSelectedRows();
|
|
}
|
|
}
|
|
});
|