Greasy Fork is available in English.

Color Picker

Color picker for Sketchful

Tính đến 30-06-2020. Xem phiên bản mới nhất.

// ==UserScript==
// @name        Color Picker
// @namespace   https://greasyfork.org/users/281093
// @match       https://sketchful.io/*
// @grant       none
// @version     0.5.1
// @author      Bell
// @description Color picker for Sketchful 
// @run-at      document-end
// jshint esversion: 6
// ==/UserScript==

let defaultPalettes = [
    [   
        "#ffffff","#d3d1d2","#f70f0f","#ff7200","#fce700","#02cb00","#01fe94","#05b0ff","#221ecd","#a300bd","#cc7fad","#fdad88","#9e5425",
        "#514f54","#a9a7a8","#ae0b00","#c84706","#ec9e06","#007612","#049d6f","#00579d","#0f0b96","#6e0083","#a65673","#e38a5e","#5e320d",
        "#000000","#827c80","#57060c","#8b2500","#9e6600","#003f00","#00766a","#003b75","#0e0151","#3c0350","#73314d","#d1754e","#421e06"
    ], 

    [
        "#3a3a3c","#8e8e93","#f8f9fa","#ffadad","#ffd6a5","#fdffb6","#caffbf","#9bf6ff","#a0c4ff","#bdb2ff","#ffc6ff","#fdad88","#9e5425",
        "#2c2c2e","#636366","#e0e0e0","#ff7070","#f3a220","#f9e079","#049d6f","#92ddea","#6dafe0","#ab87ff","#ff87ab","#e38a5e","#5e320d",
        "#1c1c1e","#48484a","#c2c2c2","#f54d4d","#dc8700","#f0c808","#00766a","#219bc3","#548bbc","#715aff","#ff5d8f","#d1754e","#421e06"
    ],

    [
        "#081c15","#1b4332","#2d6a4f","#40916c","#52b788","#74c69d","#95d5b2","#b7e4c7","#d8f3dc","#000000","#faf9f9","#ffd6ba","#fec89a",
        "#774936","#8a5a44","#9d6b53","#b07d62","#c38e70","#cd9777","#d69f7e","#deab90","#e6b8a2","#edc4b3","#ffb5a7","#fcd5ce","#f8edeb",
        "#cb997e","#eddcd2","#fff1e6","#f0efeb","#ddbea9","#a5a58d","#b7b7a4","#6d6875","#b5838d","#e5989b","#ffb4a2","#ffcdb2","#f9dcc4"
    ],

    [
        "#10002b","#240046","#3c096c","#5a189a","#7b2cbf","#9d4edd","#c77dff","#e0aaff","#efcefa","#d4b2d8","#a88fac","#826c7f","#5d4e60",
        "#7c6f93","#886f93","#a967ad","#ad6789","#db81ad","#ff6c91","#ff736c","#ff9e46","#faa275","#ff8c61","#ce6a85","#985277","#5c374c",
        "#721b65","#b80d57","#f8615a","#ffd868","#bb596b","#f96d80","#ff9a76","#ffc4a3","#00e0ff","#74f9ff","#a6fff2","#e8ffe8","#ffffff"
    ],

    [
        "#007f5f","#2b9348","#55a630","#80b918","#aacc00","#bfd200","#d4d700","#dddf00","#eeef20","#ffff3f","#03045e","#0077b6","#00b4d8",
        "#ff4800","#ff5400","#ff6000","#ff6d00","#ff7900","#ff8500","#ff9100","#ff9e00","#ffaa00","#ffb600","#90e0ef","#caf0f8","#000000",
        "#143642","#263c41","#38413f","#4a473e","#5c4d3c","#6f523b","#815839","#935e38","#a56336","#b76935","#000000","#ffffff","#ffffff"
    ]
];
let palettes = JSON.parse(localStorage.getItem('palettes')) || defaultPalettes;
let paletteIndex = parseInt(localStorage.getItem("paletteIndex")) || 0;
let lockedPalettes = JSON.parse(localStorage.getItem('lockedPalettes')) || [0];
let activeColor = {
    node: null,
    index: null
};

const canvas = document.querySelector("#canvas");
const ctx = canvas.getContext("2d");
const gameTools = document.querySelector("#gameTools");
const chatBox = document.querySelector("#gameChat");
const colorButtons = document.querySelectorAll(".gameToolsColor");
const colorsDiv = document.querySelector("#gameToolsColors");
const colorButton = document.querySelector("#gameToolsColors > div:nth-child(1) > div:nth-child(1)");

const colorPickerWrapper = document.createElement("div");
const colorInput = document.createElement("input");
const colorPicker = document.createElement("input");
const inputStyle = `margin: 5px 0; height: 20px; width: 35%; text-align: center; border: none;font-weight: 800; border-radius: 5px; background-color: #CBCBCB;`;
const wrapperStyle = `position: absolute; margin: 5px 35%; height: 20px; width: 37px; border-radius: 5px;`;

(function init() { 
    addPicker();
    updatePageStyle();
    addObservers();
    addListeners();
    changePalette(paletteIndex);
})();

function addPicker() {
    colorPicker.type = "color";
    colorPicker.setAttribute("style", "opacity: 0; width: 37px; cursor: pointer;");
    colorPicker.oninput = updatePicker; 

    colorPickerWrapper.setAttribute("style", wrapperStyle);
    colorPickerWrapper.style.backgroundColor = colorPicker.value;
    colorPickerWrapper.appendChild(colorPicker);
    gameTools.appendChild(colorPickerWrapper);

    colorInput.oninput = updateInput;
    colorInput.addEventListener("click", selectInputText, false);
    colorInput.setAttribute("style", inputStyle);
    colorInput.setAttribute("spellcheck", "false");
    colorInput.value = colorPicker.value;
    gameTools.appendChild(colorInput);
    addButtons();
}

function addObservers() {
    const heightObserver = new MutationObserver(adjustChatSize);
    const config = {
        attributes: true,
        childList: false,
        subtree: false
    };
    heightObserver.observe(gameTools, config);
    heightObserver.observe(chatBox, config);
}

function addListeners() {
    canvas.addEventListener('pointerdown', pickCanvasColor, false);
    colorsDiv.addEventListener("click", editColor, false);
    let saveBtn = document.querySelector("#savePalette");
    saveBtn.addEventListener("dragenter", highlight, false);
    saveBtn.addEventListener("dragleave", unhighlight, false);
    saveBtn.addEventListener("drop", handleDrop, false);
    saveBtn.addEventListener("dragover", e => { e.preventDefault(); }, false);
}

function updatePageStyle() {
    document.querySelector("#gameToolsSlider").style.top = "77px";
    gameTools.style.height = "200px";
}

function toggleLock() {
    let lockBtn = document.querySelector("#lockButton");
    if (lockBtn.getAttribute("state") === "unlocked") {
        lockPalette(lockBtn);
    } else {
        unlockPalette(lockBtn);
    }
    updateLock();
}

function lockPalette(lockBtn) {
    lockedPalettes.push(paletteIndex);
    localStorage.setItem("lockedPalettes", JSON.stringify(lockedPalettes));
}

function unlockPalette(lockBtn) {
    let index = lockedPalettes.indexOf(paletteIndex);
    if (index < 0) return;  
    lockedPalettes.splice(index, 1);
    localStorage.setItem("lockedPalettes", JSON.stringify(lockedPalettes));
}

function updateLock() {
    let lockBtn = document.querySelector("#lockButton");
    if (isPaletteLocked()) {
        lockBtn.classList.remove("fa-unlock-alt");
        lockBtn.classList.add("fa-lock");
        lockBtn.setAttribute("state", "locked");
    } else {
        lockBtn.classList.add("fa-unlock-alt");
        lockBtn.classList.remove("fa-lock");
        lockBtn.setAttribute("state", "unlocked");
    }
    resetActiveColor();
}

function addButtons() {
    let prevPaletteBtn = document.createElement("button");
    let saveColorBtn = document.createElement("button"); 
    let nextPaletteBtn = document.createElement("button");
    let lockBtn = document.createElement("button");
    
    addButton(prevPaletteBtn, "arrow-left", "5px 5px 5px 45px;");
    addButton(saveColorBtn, "save", "5px 5px 5px 75px;", "savePalette");
    addButton(nextPaletteBtn, "arrow-right", "5px 5px 5px 105px;");
    addButton(lockBtn, "unlock-alt", "5px 5px 5px 135px;", "lockButton");
    lockBtn.setAttribute("state", "unlocked");

    prevPaletteBtn.addEventListener("click", prevPalette, false);
    saveColorBtn.addEventListener("click", saveColor, false);
    nextPaletteBtn.addEventListener("click", nextPalette, false);
    lockBtn.addEventListener("click", toggleLock, false);
}

function nextPalette() {
    paletteIndex < (palettes.length - 1) && paletteIndex++;
    localStorage.setItem("paletteIndex", paletteIndex);
    changePalette();
}

function prevPalette() {
    paletteIndex > 0 && paletteIndex--;
    localStorage.setItem("paletteIndex", paletteIndex);
    changePalette();
}

function saveColor(e) {
    if (e.shiftKey) {
        downloadPalettes();
        return;
    }
    let currentPalette = palettes[paletteIndex];
    if (activeColor.index) {
        currentPalette[activeColor.index] = colorPicker.value;
    } else {
        while (currentPalette.length >= 39 || isPaletteLocked()) {
            paletteIndex++;
            if (paletteIndex === palettes.length) {
                palettes.push([]);
            }
            currentPalette = palettes[paletteIndex];
        }
        currentPalette.push(colorPicker.value);
    }
    changePalette();
    savePalettes();
}

function savePalettes() {
    localStorage.setItem("palettes", JSON.stringify(palettes));
}

function downloadPalettes() {
    let formattedPaletteData = JSON.stringify(palettes).replace(/\]\,/g, "],\n\n");
    download("palettes.txt", formattedPaletteData);
}

function download(filename, text) {
    let pom = document.createElement('a');
    pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
    pom.setAttribute('download', filename);
    
    if (document.createEvent) {
        let event = document.createEvent('MouseEvents');
        event.initEvent('click', true, true);
        pom.dispatchEvent(event);
    }
    else {
        pom.click();
    }
}

function isPaletteLocked() {
    return lockedPalettes.includes(paletteIndex);
}

function editColor(event) {
    if (isPaletteLocked() || !event.target.classList.contains("gameToolsColor")) return;
    event.preventDefault();
    let color = {
        node: event.target,
        index: Array.prototype.indexOf.call(colorButtons, event.target)
    };
    if (event.altKey) {
        if (color.index >= 0 && paletteIndex >= 0) {
            let palette = palettes[paletteIndex];
            palette.splice(color.index, 1);
            if (isPaletteEmpty(palette)) {
                palettes.splice(paletteIndex, 1);
                paletteIndex--;
            } 
            changePalette();
            savePalettes();
        }
    } else if (event.shiftKey) {
        setActiveColor(color);
    }
}

function changePalette() {
    if (paletteIndex < 0 || paletteIndex >= palettes.length) {
        paletteIndex = 0;
        localStorage.setItem("paletteIndex", paletteIndex); 
    }
    colorButtons.forEach((button, idx) => {
        let color = palettes[paletteIndex][idx] || "#fff";
        button.style.backgroundColor = color;
    });
    resetActiveColor();
    updateLock();
}

function isPaletteEmpty(palette) {
    if (!palette) return true;
    let empty = true;
    for (let color of palette) {
        if (color) {
            empty = false;
            break;
        }
    }
    return empty;
}

function setActiveColor(color) {
    resetActiveColor();
    activeColor = color;
    console.log(activeColor);
    activeColor.node.style.border = "solid 2px red";
} 

function resetActiveColor() {
    if (activeColor.node) {
        activeColor.node.style.border = "";
        activeColor = {
            node: null,
            index: null
        };
    }
}

function addButton(button, icon, pos, id = "") {
    let buttonStyle = `margin: ${pos}; position: absolute; height: 20px; border: none; background-color: #CBCBCB; border-radius: 5px;`;
    button.setAttribute("style", buttonStyle);
    button.setAttribute("class", `fas fa-${icon}`);
    button.id = id;
    gameTools.appendChild(button);
}

function updatePicker(event) {
    let color = event.target.value;
    colorPickerWrapper.style.backgroundColor = color;
    colorInput.value = color;
    setColor(color);
}

function updateInput(event) {
    let color = event.target.value;
    colorPickerWrapper.style.backgroundColor = color;
    colorPicker.value = color;
    setColor(color);
}

function setColor(color) {
    let prevColor = colorButton.style.backgroundColor;
    colorButton.style.backgroundColor = color;
    colorButton.dispatchEvent(new Event("pointerdown"));
    colorButton.style.backgroundColor = prevColor;
}

function selectInputText() {
    colorInput.select();
}

function pickCanvasColor(event) {
    if (!event.altKey) return;
    event.stopImmediatePropagation();
    let pos = getPos(event);
    let [r, g, b, a] = ctx.getImageData(pos.x, pos.y, 1, 1).data;
    let color = `rgb(${r}, ${g}, ${b})`;
    setColor(color);
}

function getPos(event) {
    let canvasRect = canvas.getBoundingClientRect();
    let canvasScale = canvas.width / canvasRect.width;
    return {
        x: (event.clientX - canvasRect.left) * canvasScale,
        y: (event.clientY - canvasRect.top) * canvasScale
    };
}

function handleDrop(e) {
    e.preventDefault();
    colorsDiv.style.filter = "";
    let file = e.dataTransfer.files[0];
    if (file) {
        let reader = new FileReader();
        reader.readAsText(file);
        reader.onload = (e) => {
            const csvRegex = /CSV \*\/\s*(\S+)/;
            let match = e.target.result.match(csvRegex);
            let palette = match ? match[1].split(",").map(color => `#${color}`) : JSON.parse(e.target.result);
            loadPalette(palette);
            resetPaletteState();
        };
    }
}

function addPalette(palette) {
    palettes.push(palette);
    paletteIndex = palettes.length - 1;
}

function loadPalette(palette) {
    if (!Array.isArray(palette[0])) {
        addPalette(palette);
        return;
    }
    palettes = palette;
    paletteIndex = 0;
    lockedPalettes = [];
}

function resetPaletteState() {
    resetActiveColor();
    updateLock();
    changePalette();
    savePalettes();
}

function highlight(e) {
    e.preventDefault();
    colorsDiv.style.filter = "brightness(0.8)";
}

function unhighlight(e) {
    e.preventDefault();
    colorsDiv.style.filter = "";
}

function isDrawing() {
    return document.querySelector("#gameTools").style.display !== "none";
}

function adjustChatSize() {
    chatBox.style.height = isDrawing() ? "calc(100% - 200px)" : "calc(100% - 180px)";
}