MediaWiki:TeamBingo.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
mw.hook('wikipage.content').add(function () {
const JSON_PAGE = "Module:TeamBingo/data.json"; // wiki page where JSON is stored
const POLL_INTERVAL = 50000; // 5 seconds
const username = mw.config.get("wgUserName");
const infoBox = document.getElementById("teamBingoInfoBox");
const containers = document.querySelectorAll(".teamBingoContainer");
if (!containers.length) return;
let boardData = {}; // current state of tiles for all teams
// Initialize boards
containers.forEach(container => {
container.innerHTML = "";
const team = container.id;
const captain = container.getAttribute("data-captain");
const itemsAttr = container.getAttribute("data-items");
const items = itemsAttr.split("|").filter(i => i.trim() !== "");
const parsedItems = items.map((entry, idx) => {
const parts = entry.split("::");
return { name: parts[0].trim(), tooltip: parts[1] ? parts[1].trim() : parts[0].trim(), index: idx };
});
const table = document.createElement("table");
table.style.width = "100%";
table.style.tableLayout = "fixed";
table.style.border = "2px solid #596e96";
table.style.borderCollapse = "collapse";
container.appendChild(table);
const rows = Math.ceil(Math.sqrt(parsedItems.length));
const cols = Math.ceil(parsedItems.length / rows);
const allTemplates = parsedItems.map(item => `{{plinkp|${item.name}}}`).join("\n");
// Fetch images via API
$.getJSON("/api.php", { action: "parse", text: allTemplates, format: "json" }).done(function(data){
if(!data.parse || !data.parse.text) return;
const tempDiv = document.createElement("div");
tempDiv.innerHTML = data.parse.text["*"];
const imgs = tempDiv.querySelectorAll("img");
for (let r = 0; r < rows; r++){
const row = table.insertRow();
for (let c = 0; c < cols; c++){
const idx = r*cols + c;
if(idx >= parsedItems.length) break;
const item = parsedItems[idx];
const cell = row.insertCell();
cell.style.position = "relative";
cell.style.textAlign = "center";
cell.style.background = "#313e59";
cell.style.border = "1px solid #596e96";
cell.style.cursor = "pointer";
cell.style.transition = "all 0.2s";
cell.style.padding = "10px";
const cellContainer = document.createElement("div");
cellContainer.style.position = "relative";
// Image
if(imgs[idx]){
const img = document.createElement("img");
img.src = imgs[idx].src;
img.alt = item.name;
img.style.width = "40px";
img.style.display = "block";
img.style.margin = "0 auto";
cellContainer.appendChild(img);
}
// Name
const nameDiv = document.createElement("div");
nameDiv.textContent = item.name;
nameDiv.style.marginTop = "5px";
cellContainer.appendChild(nameDiv);
// Checkmark
const checkmark = document.createElement("div");
checkmark.textContent = "✔";
checkmark.style.position = "absolute";
checkmark.style.top = "2px";
checkmark.style.right = "2px";
checkmark.style.color = "#4CAF50";
checkmark.style.fontSize = "20px";
checkmark.style.display = "none";
cellContainer.appendChild(checkmark);
cell.appendChild(cellContainer);
// Hover info
cell.addEventListener("mouseenter", ()=>{
infoBox.innerHTML = "";
const header = document.createElement("div");
header.style.fontWeight = "bold";
header.style.marginBottom = "5px";
header.textContent = item.name;
infoBox.appendChild(header);
if(imgs[idx]){
const img = document.createElement("img");
img.src = imgs[idx].src;
img.style.width = "40px";
img.style.display = "block";
img.style.marginBottom = "5px";
infoBox.appendChild(img);
}
item.tooltip.split("%%").forEach(line=>{
const div = document.createElement("div");
div.innerHTML = line.trim();
infoBox.appendChild(div);
});
});
cell.addEventListener("mouseleave", ()=>{
infoBox.innerHTML = "Hover over a tile to see its info here.";
});
// Click only if captain
cell.addEventListener("click", function(){
if(username !== captain) return;
const key = `${team}_tile_${idx}`;
boardData[key] = !boardData[key];
saveBoardData();
renderBoard();
});
}
}
});
});
// Fetch board state from JSON page
function fetchBoardData(){
$.getJSON(`/api.php?action=parse&page=${JSON_PAGE}&format=json`).done(data=>{
try{
const text = data.parse.text["*"];
boardData = JSON.parse(text);
renderBoard();
}catch(e){ console.error(e);}
});
}
// Save board state to JSON page
function saveBoardData(){
const content = JSON.stringify(boardData);
$.post("/api.php", {
action:"edit",
title: JSON_PAGE,
token: mw.user.tokens.get("csrfToken"),
format: "json",
text: content,
summary: "Update bingo board"
});
}
// Update checkmarks based on boardData
function renderBoard(){
containers.forEach(container=>{
const team = container.id;
const cells = container.querySelectorAll("td");
cells.forEach((cell, idx)=>{
const key = `${team}_tile_${idx}`;
const check = cell.querySelector(".bingoCheckmark");
if(check) check.style.display = boardData[key] ? "block" : "none";
cell.style.background = boardData[key] ? "rgba(76,175,80,0.5)" : "#313e59";
});
});
}
fetchBoardData();
setInterval(fetchBoardData, 5000); // poll every 5s
});