User:B700465189a9/Gadget-Palette.js

From the Super Mario Wiki, the Mario encyclopedia
Jump to navigationJump to search

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.
$(function() {
    const paletteData = JSON.parse("[{\"colors\":[4278190080,4281545984,4283833600,4285269760,4278994944,4278210312,4278211072,4285230336,4285399104,4284219518,4278206541,4283387727,4284900966,4290064672,4288237056,4290190971,4279079680,4281894656,4278226738,4284277808,4290559488,4293565986,4287158272,4294934896,4278200968,4279505575,4282056868,4278221965,4285868030,4279590873,4282532095,4288682700,4294930124,4291196671,4294077183,4282769538,4284788991,4282961374,4289572269,4290295992,4290639019,4291817366,4293191060,4294432933,4287795455,4289983436,4290112498,4294954181,4290830335,4292072191,4294952170,4293445887,4294623999,4294967295],\"name\":\"15° Canonical\"}]");

    const palettes = new Set();
    paletteData.forEach(palette => palettes.add({
        colors: new Set(palette.colors),
        deprecation: palette.deprecation,
        name: palette.name,
    }));

    function checkData(data) {
        if (data.width > 0 && data.height > 0) {
            const possiblePalettes = new Set(palettes);
            let alpha = false;

            for (let y = 0; y < data.height; y++) {
                for (let x = 0; x < data.width; x++) {
                    const index = (y * data.width + x) * 4;

                    const r = data.data[index];
                    const g = data.data[index + 1];
                    const b = data.data[index + 2];
                    const a = data.data[index + 3];

                    if (a === 0xFF) {
                        alpha = true;
                    } else {
                        const color = ((a << 24) | (r << 16) | (g << 8) | b) >>> 0;

                        for (const palette of possiblePalettes) {
                            if (!palette.colors.has(color)) {
                                possiblePalettes.delete(palette);
                            }
                        }

                        if (possiblePalettes.size === 0) {
                            return { possiblePalettes: false, alpha: false };
                        }
                    }
                }
            }

            return { possiblePalettes, alpha };
        }

        return { possiblePalettes: false, alpha: false };
    }

    function hover(text, hoverText) {
        const span = document.createElement("span");
        span.textContent = text;
        span.className = "explain";
        span.title = hoverText;
        return span;
    }

    function check(url, cell) {
        const image = new Image();

        image.addEventListener("error", function () {
            cell.textContent = "Error";
            URL.revokeObjectURL(url);
        });

        image.addEventListener("load", function () {
            const canvas = new OffscreenCanvas(image.width, image.height);
            const context = canvas.getContext("2d");
            context.drawImage(image, 0, 0);

            const data = context.getImageData(0, 0, image.width, image.height);
            const { possiblePalettes, alpha } = checkData(data);
            cell.textContent = "";

            if (!possiblePalettes) {
                cell.append(hover("Unknown", "Image uses a color that isn't in the list of known palettes."));
            } else {
                let index = 0;
                for (const palette of possiblePalettes) {
                    if (palette.deprecation) {
                        cell.append(hover(palette.name, palette.deprecation));
                    } else {
                        cell.append(palette.name);
                    }

                    if ((index + 1) < possiblePalettes.length) {
                        cell.append(", ");
                    }
                    index += 1;
                }

                if (alpha) {
                    cell.append(" (+ transparency)")
                }
            }

            URL.revokeObjectURL(url);
        });

        image.crossOrigin = "Anonymous";
        image.src = url;

        cell.textContent = "Loading...";
    }

    function checkRow(row, cell) {
        const originalImage = row.querySelector("td:nth-child(2) a");
        if (!originalImage) return;

        const url = new URL(originalImage.href);

        if (!url.pathname.endsWith(".png")) {
            cell.textContent = "";
            cell.append(hover("Unsupported", "Only PNG files can be checked for palette."));
            return;
        }

        check(url, cell);
    }

    for (const table of document.querySelectorAll("table.filehistory")) {
        const dimensions = [...table.querySelectorAll("th")].findIndex(cell => cell.textContent === "Dimensions");

        for (const row of table.rows) {
            const header = row.cells[0].tagName === "TH";
            const cell = document.createElement(header ? "th" : "td");
            cell.textContent = header ? "Palette" : "-";
            row.insertBefore(cell, row.cells[dimensions].nextSibling);

            if (!header) {
                checkRow(row, cell);
            }
        }
    }

    const upload = document.querySelector("#wpUploadFile");

    if (upload) {
        const row = document.createElement("tr");
        const cell = document.createElement("td");
        row.textContent = "Palette: ";
        const text = document.createElement("span");
        text.textContent = "-";
        cell.append(text);
        row.append(cell);
        document.querySelector("#mw-htmlform-source tbody").append(row);

        upload.addEventListener("change", () => {
            if (upload.files && upload.files[0]) {
                const url = URL.createObjectURL(upload.files[0]);
                check(url, text);
            } else {
                text.textContent = "-";
            }
        });
    }
});