// ==UserScript== // @name Chess.com Piece Numbers // @namespace https://theusaf.org // @version 0.0.5 // @description Adds piece number data to pieces in the chess board // @author theusaf // @match https://www.chess.com/** // @icon https://www.google.com/s2/favicons?domain=chess.com // @grant none // @license MIT // @downloadURL none // ==/UserScript== let tries = 0; function init() { const board = document.querySelector("chess-board"), pieces = { black: { p: 2 // to fix promotion stuff }, white: { p: 2 } }; if (board) { const observer = new MutationObserver((mutations, observer) => { const attributeMutations = new Set(); for (const mutation of mutations) { if (mutation.type === "childList") { const {addedNodes, removedNodes} = mutation; for (const node of addedNodes) { if (node.nodeName !== "DIV") {continue;} if (Array.from(node.classList)?.includes("piece")) { addPiece(node); } } for (const node of removedNodes) { if (node.nodeName !== "DIV") {continue;} if (Array.from(node.classList)?.includes("piece")) { removePiece(node); } } } else { const filter = (mutation) => { return (/piece/.test(mutation.oldValue) || /piece/.test(mutation.target.className)) && (!/dragging/.test(mutation.target.className) && !/dragging/.test(mutation.oldValue) && mutation.oldValue !== null); }; if (filter(mutation)) { attributeMutations.add(mutation); } } } const out = [], moveSquaresProcessed = new Set(), toSwap = new Set(); for (const mutation of attributeMutations) { out.push(mutation); const piecePosition = getPieceSquare(mutation.target.className), [pieceTeam, pieceType] = getPieceType(mutation.target.className), [originalPieceTeam, originalPieceType] = getPieceType(mutation.oldValue); if (piecePosition in moveSquaresProcessed) {continue;} moveSquaresProcessed.add(piecePosition); let shouldContinue = false; // console.log(piecePosition, pieceTeam, pieceType, originalPieceTeam, originalPieceType) for (const swap of toSwap) { const {team, type, swapWith} = swap; if (pieceTeam === team && pieceType === type) { console.log("swapping"); swapPieces(mutation.target, swapWith); toSwap.delete(swap); shouldContinue = true; break; } } if (shouldContinue) {continue;} if (pieceTeam === originalPieceTeam && pieceType === originalPieceType) { // no change required } else { // uh oh, need to swap! toSwap.add({ team: originalPieceTeam, type: originalPieceType, swapWith: mutation.target }); } } if (out.length) { /*console.log(out.map(mutation => { return [`${mutation.oldValue} --> ${mutation.target.className}`, mutation.target]; }));*/ // Check for any abnormalities and fix issues moveSquaresProcessed.clear(); for (const mutation of attributeMutations) { const piecePosition = getPieceSquare(mutation.target.className), currentPiece = getPieceType(mutation.target.className).join(""), originalPiece = mutation.target.getAttribute("original-piece"); if (piecePosition in moveSquaresProcessed) {continue;} moveSquaresProcessed.add(piecePosition); if (!currentPiece || !piecePosition) {continue;} if (currentPiece !== originalPiece) { const pieces = [...document.querySelectorAll(`[original-piece=${currentPiece}]`)]; for (const piece of pieces) { const pieceOrignalPiece = piece.getAttribute("original-piece"), pieceCurrentPiece = getPieceType(piece.className).join(""); if (pieceOrignalPiece !== pieceCurrentPiece) { console.log("Swapping #2", mutation.target, piece); swapPieces(piece, mutation.target); // swap original pieces also mutation.target.setAttribute("original-piece", pieceOrignalPiece); piece.setAttribute("original-piece", originalPiece); break; } } } } } }); observer.observe(board, { childList: true, subtree: true, attributeFilter: ["class"], attributeOldValue: true }); const existingPieces = document.querySelectorAll("chess-board .piece"); for (const piece of existingPieces) { addPiece(piece); } } else { if (++tries > 10) {return;} setTimeout(init, 500); } function swapPieces(a, b) { const myNumber = a.getAttribute("piece-number"), myOriginalPiece = a.getAttribute("original-piece"); a.setAttribute("piece-number", b.getAttribute("piece-number")); a.setAttribute("original-piece", b.getAttribute("original-piece")); b.setAttribute("piece-number", myNumber); b.setAttribute("original-piece", myOriginalPiece); } function getPieceSquare(name) { const piece = name.split(" ").find(word => /^square/.test(word)); return piece?.split("-")[1]; } function getPieceType(name) { const piece = name.split(" ").find(word => word.length === 2 && /^[wb]/.test(word)); return piece?.split("") || []; } function addPiece(piece) { const colorPieces = piece.classList[1][0] === "w" ? pieces.white : pieces.black, actualPiece = piece.classList[1][1]; if (typeof colorPieces[actualPiece] !== "number") {colorPieces[actualPiece] = 0;} colorPieces[actualPiece]++; piece.setAttribute("piece-number", colorPieces[actualPiece]); piece.setAttribute("original-piece", getPieceType(piece.className).join("")); } function removePiece(piece) { const colorPieces = piece.classList[1][0] === "w" ? pieces.white : pieces.black, actualPiece = piece.classList[1][1]; if (typeof colorPieces[actualPiece] !== "number") {colorPieces[actualPiece] = 0;} colorPieces[actualPiece]--; } } init();