您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Redecorate florr.io with your style.
当前为
// ==UserScript== // @name florr.io | Text & Color Changer // @namespace Hai // @version 2.1.1 // @description Redecorate florr.io with your style. // @author Furaken // @match https://florr.io/* // @license MIT // @grant GM_addStyle // ==/UserScript== function addAlpha(color, opacity) { opacity = Math.round(Math.min(Math.max(opacity ?? 1, 0), 1) * 255); return color + opacity.toString(16).toUpperCase(); } function componentToHex(c) { let hex = c.toString(16); return hex.length == 1 ? "0" + hex : hex; } function rgbToHex(r, g, b) { return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b); } function hexToRgb(hex) { let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null; } function createEle(type, parent, style, innerHTML, id, className) { let ele = document.createElement(type); if (type) ele.type = type if (style) ele.style = style if (innerHTML) ele.innerHTML = innerHTML if (id) ele.id = id if (className) ele.className = className parent.appendChild(ele) return ele } let ls = localStorage.customizer || JSON.stringify({ color: { from: [], to: [], }, text: [] }) function errorMessage(error) { console.log(error) alert(`${error}\n\n${JSON.stringify(ls, null, 4)}`) } function syntaxHighlight(json) { // https://stackoverflow.com/questions/4810841/pretty-print-json-using-javascript if (typeof json != 'string') { json = JSON.stringify(json, undefined, 2); } json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { var cls = 'number'; if (/^"/.test(match)) { if (/:$/.test(match)) { cls = 'key'; } else { cls = 'string'; } } else if (/true|false/.test(match)) { cls = 'boolean'; } else if (/null/.test(match)) { cls = 'null'; } return '<span class="' + cls + '">' + match + '</span>'; }); } try {ls = JSON.parse(ls)} catch (error) {errorMessage(error)} let closeButtonSvg = "data:image/svg+xml;base64,PCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KDTwhLS0gVXBsb2FkZWQgdG86IFNWRyBSZXBvLCB3d3cuc3ZncmVwby5jb20sIFRyYW5zZm9ybWVkIGJ5OiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCAxMDI0IDEwMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbD0iIzAwMDAwMCI+Cg08ZyBpZD0iU1ZHUmVwb19iZ0NhcnJpZXIiIHN0cm9rZS13aWR0aD0iMCIvPgoNPGcgaWQ9IlNWR1JlcG9fdHJhY2VyQ2FycmllciIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cg08ZyBpZD0iU1ZHUmVwb19pY29uQ2FycmllciI+Cg08cGF0aCBmaWxsPSIjZmZmZmZmY2MiIGQ9Ik0xOTUuMiAxOTUuMmE2NCA2NCAwIDAgMSA5MC40OTYgMEw1MTIgNDIxLjUwNCA3MzguMzA0IDE5NS4yYTY0IDY0IDAgMCAxIDkwLjQ5NiA5MC40OTZMNjAyLjQ5NiA1MTIgODI4LjggNzM4LjMwNGE2NCA2NCAwIDAgMS05MC40OTYgOTAuNDk2TDUxMiA2MDIuNDk2IDI4NS42OTYgODI4LjhhNjQgNjQgMCAwIDEtOTAuNDk2LTkwLjQ5Nkw0MjEuNTA0IDUxMiAxOTUuMiAyODUuNjk2YTY0IDY0IDAgMCAxIDAtOTAuNDk2eiIvPgoNPC9nPgoNPC9zdmc+" let ctx = document.getElementById("canvas").getContext("2d") let message = `This script is made by Furaken (discord username: <w style="color: #15b1d6">samerkizi</w>) Toggle this menu with keybind: <w style="color: #f5945d">Shift \`</w> Join my discord server: <w style="color: #5567f1; cursor:pointer;" onclick='window.open("https://discord.gg/tmWUfg4FR9")'>https://discord.gg/tmWUfg4FR9</w>` let container = createEle( "div", document.querySelector("body"), ` margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(1); width: 80%; height: 80%; display: flex; z-index: 999; transition: all 0.4s ease-in-out; font-family: 'Ubuntu'; color: white; text-shadow: rgb(0 0 0) 2px 0px 0px, rgb(0 0 0) 1.75517px 0.958851px 0px, rgb(0 0 0) 1.0806px 1.68294px 0px, rgb(0 0 0) 0.141474px 1.99499px 0px, rgb(0 0 0) -0.832294px 1.81859px 0px, rgb(0 0 0) -1.60229px 1.19694px 0px, rgb(0 0 0) -1.97998px 0.28224px 0px, rgb(0 0 0) -1.87291px -0.701566px 0px, rgb(0 0 0) -1.30729px -1.5136px 0px, rgb(0 0 0) -0.421592px -1.95506px 0px, rgb(0 0 0) 0.567324px -1.91785px 0px, rgb(0 0 0) 1.41734px -1.41108px 0px, rgb(0 0 0) 1.92034px -0.558831px 0px; `, ` <div style="background: #333;border-radius: 10px;box-shadow: 5px 5px rgba(0, 0, 0, 0.3);padding: 15px;display: flex; width: 100%;"> <div style="display: flex; flex-direction: column; width: 75%;"> <div style="height: 100%; background: #00000050; border-radius: 10px; position: sticky; display: flex; flex-direction: column;"> <div id="editLabel" style="text-align:center;background: #75ba75;padding: 10px;top: 0;left: 0;border-radius: 10px 10px 0 0;position: sticky;z-index: 1;">COLOR CHANGER</div> <div id="con_edit" style="white-space: break-spaces; background: #00000030; padding: 15px; border-radius:0 0 5px 5px;font-family:'Space Mono', monospace;overflow: hidden auto;word-wrap: break-word; padding: 15px; position: relative; height: 100%;">${message}<br><br><br>${syntaxHighlight(JSON.stringify(ls, null, 4))}</div> <div style="padding: 10px;position: sticky;z-index: 1;height: 40px;display: flex;font-size: 12px;text-align: center;line-height: 27px;"> <div id="con_edit_button_text" class="button" style="overflow:hidden;position: relative;height: 100%;width: fit-content;padding: 0 15px; border-radius: 3px;border: solid #ce6529 4px;box-sizing: border-box;background: #f5945d;">Text changer</div> <div id="con_edit_button_appendTheme" class="button" style="overflow:hidden;position: relative;height: 100%;width: fit-content;padding: 0 15px; border-radius: 3px;border: solid #4b7a4b 4px;box-sizing: border-box;margin-left: 3px;background: #75ba75;">Append</div> <div id="con_edit_button_copyJSON" class="button" style="overflow:hidden;position: relative;height: 100%;width: fit-content;padding: 0 15px;border-radius: 3px;border: solid #4b837e 4px;box-sizing: border-box;margin-left: 3px;background: #6dbfb8;">Copy JSON</div> <div id="con_edit_button_viewJSON" class="button" style="overflow:hidden;position: relative;height: 100%;width: fit-content;padding: 0 15px;border-radius: 3px;border: solid #4b837e 4px;box-sizing: border-box;margin-left: 3px;background: #6dbfb8;">View JSON</div> <div id="con_edit_button_deleteTheme" class="button" style="overflow:hidden;position: relative;height: 100%;width: fit-content;padding: 0 15px;border-radius: 3px;border: solid #974545 4px;box-sizing: border-box;margin-left: 3px;background: #BB5555;">Reset all</div> </div> </div> </div> <div style="display: flex; flex-direction: column; width: 25%; margin-left: 5px; font-size: 14px;"> <div style="height: 100%; background: #00000050; border-radius: 10px; position: sticky; display: flex; flex-direction: column;"> <div style="text-align:center;background: #6dbfb8;padding: 12px;top: 0;border-radius: 10px 10px 0 0;position: sticky;z-index: 1;">ELEMENTS</div> <div id="con_element" style="font-family:'Space Mono', monospace;padding: 15px; position: relative; height: 100%; overflow-y: auto"></div> <div style="padding: 10px;position: sticky;z-index: 1;height: 40px;display: flex;font-size: 12px;text-align: center;line-height: 27px;"> <div id="con_element_button_addColor" class="button" style="overflow:hidden;position: relative;height: 100%;width: 100%;border-radius: 3px;border: solid #4b837e 4px;box-sizing: border-box;background: #6dbfb8;margin-right: 2px;">New color</div> <div id="con_element_button_findColor" class="button" style="overflow:hidden;position: relative;height: 100%;width: 100%;border-radius: 3px;border: solid #785978 4px;box-sizing: border-box;margin-left: 2px;background: #be95be;">Find color</div> </div> </div> </div> </div> <div id="closeButton" style="cursor: pointer; background-color: #BB5555; background-image: url(${closeButtonSvg});background-position: center;background-size: contain;background-repeat: no-repeat;border: 4px solid #974545; border-radius: 5px; height: 25px; width: 25px; margin-left: 5px;box-shadow: 4px 4px rgba(0, 0, 0, 0.3)"></div> ` ) document.getElementById("con_edit_button_text").onclick = function() { document.getElementById("editLabel").innerHTML = "TEXT CHANGER" document.getElementById("editLabel").style.background = "#f5945c" document.getElementById("con_edit").innerHTML = `This text changer uses <w style="color: #f5945d">.replaceAll(pattern, replacement)</w> function with pattern used as RegEx object (global flag). How to RegEx: <w style="color: #5567f1; cursor:pointer;" onclick='window.open("https://www3.ntu.edu.sg/home/ehchua/programming/howto/Regexe.html")'>https://www3.ntu.edu.sg/home/ehchua/programming/howto/Regexe.html</w> ` ls.text.forEach((t, i) => { createEle("div", document.getElementById("con_edit"), null, `${i}: <w style="color: #e6db74">/${t.from}/g</w>`, null, "button").onclick = function() { let regex = prompt("from (RegEx)", t.from) if (!regex) return ls.text[i].from = regex localStorage.customizer = JSON.stringify(ls) document.getElementById("con_edit_button_text").click() } createEle("div", document.getElementById("con_edit"), "margin-left: 40px; margin-bottom: 10px;",`<w style="color: #e6db74">"</w>${t.to}<w style="color: #e6db74">"</w>`, null, "button").onclick = function() { let a = prompt("to (String)", t.to) if (!a) return ls.text[i].to = a localStorage.customizer = JSON.stringify(ls) document.getElementById("con_edit_button_text").click() } }) let textButtons = createEle("div", document.getElementById("con_edit"), "margin-top: 20px; font-size: 12px; display: inline-flex") createEle("div", textButtons, "cursor: pointer; overflow:hidden;height: fit-content;width: fit-content;padding: 3px 7px; border-radius: 3px;border: solid #4b837e 4px;box-sizing: border-box;background: #6dbfb8;", "New Text").onclick = function() { let from = prompt("from (RegEx)") if (!from) return if (ls.text.map(x => x.from).includes(from)) return alert("Text already exists.") let to = prompt("to (String)") if (!to) to = "" ls.text.push({ from: from, to: to }) localStorage.customizer = JSON.stringify(ls) document.getElementById("con_edit_button_text").click() } createEle("div", textButtons, "margin-left: 5px; cursor: pointer; overflow:hidden;height: fit-content;width: fit-content;padding: 3px 7px; border-radius: 3px;border: solid #974545 4px;box-sizing: border-box;background: #bb5555;", "Remove Text").onclick = function() { if (ls.text.length <= 0) return alert("You cannot remove more text.") let indexNumber = prompt("Index:", ls.text.length - 1) if (isNaN(indexNumber) || indexNumber == "" || indexNumber >= ls.text.length || indexNumber < 0) return alert("This index does not exist") indexNumber = Number(indexNumber) if (!confirm(`Are you sure to delete this?\n\n${JSON.stringify(ls.text[indexNumber], null, 4)}`)) return ls.text.splice(indexNumber, 1) localStorage.customizer = JSON.stringify(ls) document.getElementById("con_edit_button_text").click() } } document.getElementById("con_edit_button_appendTheme").onclick = function() { let newObj = prompt("JSON") if (newObj == null) return try { newObj = JSON.parse(newObj) newObj.color.from.forEach((from, index) => { if (from == null) return if (JSON.stringify(ls.color.from).includes(JSON.stringify(from))) return if (typeof from.color != "string") return if (!(typeof from.alpha == "number" || from.alpha == "*")) return let to = newObj.color.to[index] if (!["solid", "linear", "radial", "animated"].includes(to.type)) return if (!(typeof to.alpha == "number" || to.alpha == "*")) return if (to.data == null) return if (from.alpha != "*") from.alpha = Math.min(Math.max(from.alpha ?? 1, 0), 1) if (to.alpha != "*") to.alpha = Math.min(Math.max(to.alpha ?? 1, 0), 1) if (/^#[A-Fa-f0-9]{6}$/g.test(from.color)) from.color = from.color.match(/^#[A-Fa-f0-9]{6}$/g)[0] else return ls.color.from.push(from) ls.color.to.push(to) }) newObj.text.forEach((obj, index) => { if (obj.from == null || obj.from == "" || obj.to == null) return if (ls.text.map(x => x.from).includes(obj.from)) return if (typeof obj.from != "string" || typeof obj.to != "string" ) return ls.text.push(obj) }) } catch (error) { errorMessage(error) } localStorage.customizer = JSON.stringify(ls) document.getElementById("con_edit_button_viewJSON").click() } document.getElementById("con_edit_button_copyJSON").onclick = function() { navigator.clipboard.writeText(JSON.stringify(ls, null, 4)) } document.getElementById("con_edit_button_viewJSON").onclick = function() { document.getElementById("con_edit").innerHTML = `${message}<br><br><br>${syntaxHighlight(JSON.stringify(ls, null, 4))}` } document.getElementById("con_edit_button_deleteTheme").onclick = function() { if (!confirm(`Are you sure to reset all?`)) return ls = { color: { from: [], to: [], }, text: [] } updateElements() document.getElementById("con_edit_button_viewJSON").click() } var animatedObj = [] function getPosition(x_, y_, r_, isRadiusExist) { let string if (!isRadiusExist) string = prompt("x, y", `${x_}, ${y_}`) else string = prompt("x, y, radius", `${x_}, ${y_}, ${r_}`) if (string == null) return string = string.split(",") if ((string.length < 2 && !isRadiusExist) || (string.length < 3 && isRadiusExist)) return alert("Invalid input.") let x = string[0].trim(), y = string[1].trim() if (isNaN(x) || x == "") x = null else x = Number(x) if (isNaN(y) || y == "") y = null else y = Number(y) if (isRadiusExist) { var r = string[2].trim() if (isNaN(r) || r == "") r = null else r = Number(r) if (r < 0) return alert("Radius cannot be less than 0") } return {x, y, r} } function updateElements() { localStorage.customizer = JSON.stringify(ls) getColorElementsFromLocalStorage() } function getColorElementsFromLocalStorage() { document.getElementById("con_element").innerHTML = `<div style="display: flex; flex-direction: column">` + ls.color.from.map((x, index) => ` <div id="con_element_${x.color}-${x.alpha}" style="cursor: pointer; display: flex; flex-direction: row; margin-bottom: 3px"> <w id="con_element_${x.color}-${x.alpha}_a" style="border-radius: 5px 0 0 5px; padding: 5px 10px; background:${addAlpha(x.color, x.alpha == "*" ? null : x.alpha)}; width: 50%;">${x.color} (${x.alpha == "*" ? x.alpha : x.alpha.toFixed(1)})</w> <w id="con_element_${x.color}-${x.alpha}_b" style="border-radius: 0 5px 5px 0; padding: 5px 10px; background:${addAlpha(ls.color.to[index].preview, ls.color.to[index].alpha == "*" ? null : ls.color.to[index].alpha)}; width: 50%;">${ls.color.to[index].type == "solid" ? ls.color.to[index].preview : ls.color.to[index].type} (${ls.color.to[index].alpha == "*" ? ls.color.to[index].alpha : ls.color.to[index].alpha.toFixed(1)})</w> </div> `).toString().replaceAll(",", "") + "</div>" ls.color.from.forEach((x, index) => { let listOfCategory = [`category_solid`, `category_linear`, `category_radial`, `category_animated`] if (ls.color.to[index].type == "solid") ls.color.to[index].preview = ls.color.to[index].data else if (ls.color.to[index].type == "linear" || ls.color.to[index].type == "radial") ls.color.to[index].preview = ls.color.to[index].data.colorStop[0].color else if (ls.color.to[index].type == "animated") ls.color.to[index].preview = ls.color.to[index].data.keyframes[0] localStorage.customizer = JSON.stringify(ls) document.getElementById(`con_element_${x.color}-${x.alpha}_b`).style.background = `${addAlpha(ls.color.to[index].preview, ls.color.to[index].alpha == "*" ? null : ls.color.to[index].alpha)}` document.getElementById(`con_element_${x.color}-${x.alpha}_b`).innerHTML = `${ls.color.to[index].type == "solid" ? ls.color.to[index].preview : ls.color.to[index].type} (${ls.color.to[index].alpha})` document.getElementById(`con_element_${x.color}-${x.alpha}`).onclick = function() { document.getElementById("editLabel").innerHTML = "COLOR CHANGER" document.getElementById("editLabel").style.background = "#75ba75" document.getElementById("con_edit").innerHTML = `from <w id="originalColor" style="cursor: pointer; border-radius: 5px; padding: 5px 10px; background:${addAlpha(x.color, x.alpha == "*" ? null : x.alpha)};">${x.color} (${x.alpha})</w> to <w id="convertColor" style="cursor: default; border-radius: 5px; padding: 5px 10px; background:${addAlpha(ls.color.to[index].preview, ls.color.to[index].alpha == "*" ? null : ls.color.to[index].alpha)};">${ls.color.to[index].type == "solid" ? ls.color.to[index].preview : ls.color.to[index].type} (${ls.color.to[index].alpha})</w>` let type = createEle("div", document.getElementById("con_edit"), "margin-top: 20px", "type ") document.getElementById("originalColor").onclick = function() { let color = prompt("Color (hex6):", ls.color.from[index].color) if (color == null) return if (/^#[A-Fa-f0-9]{6}$/g.test(color)) color = color.match(/^#[A-Fa-f0-9]{6}$/g)[0] else return alert("Invalid hex color.") let alpha = prompt("Alpha (0~1 or * for any) :", ls.color.from[index].alpha) if (alpha != "*") { if (isNaN(alpha)) alpha = 1 else alpha = Number(alpha) alpha = Math.min(Math.max(alpha ?? 1, 0), 1) } ls.color.from[index] = ({color: color, alpha: alpha}) document.getElementById("originalColor").innerHTML = `${color} (${alpha})` document.getElementById("originalColor").style.background = `${addAlpha(color, alpha == "*" ? null : alpha)}` updateElements() } let dropdown = createEle("select", type, ` border-radius: 5px; padding: 5px 10px; background: #6ebfb8; width: fit-content; cursor: pointer; font-family: 'Space Mono', monospace; font-size: 16px; color: white; text-shadow: rgb(0 0 0) 2px 0px 0px, rgb(0 0 0) 1.75517px 0.958851px 0px, rgb(0 0 0) 1.0806px 1.68294px 0px, rgb(0 0 0) 0.141474px 1.99499px 0px, rgb(0 0 0) -0.832294px 1.81859px 0px, rgb(0 0 0) -1.60229px 1.19694px 0px, rgb(0 0 0) -1.97998px 0.28224px 0px, rgb(0 0 0) -1.87291px -0.701566px 0px, rgb(0 0 0) -1.30729px -1.5136px 0px, rgb(0 0 0) -0.421592px -1.95506px 0px, rgb(0 0 0) 0.567324px -1.91785px 0px, rgb(0 0 0) 1.41734px -1.41108px 0px, rgb(0 0 0) 1.92034px -0.558831px 0px; `, ls.color.to[index].type ) dropdown.onchange = function() { if (!confirm(`Reselecting type will erase current data, continue?\n\n${JSON.stringify(ls.color.to[index].data, null, 4)}`)) { updateElements() document.getElementById(`con_element_${x.color}-${x.alpha}`).click() return } ls.color.to[index].type = this.options[this.selectedIndex].text if (ls.color.to[index].type == "solid") ls.color.to[index].data = x.color else if (ls.color.to[index].type == "linear" || ls.color.to[index].type == "radial") { ls.color.to[index].data = { pos: { x0: null, y0: null, x1: null, y1: null, defaultEnd: { x: 100, y: 100 } }, colorStop: [ { offset: 0, color: x.color, }, { offset: 1, color: x.color, } ] } } else if (ls.color.to[index].type == "animated") { ls.color.to[index].data = { duration: 5, keyframes: [ x.color, x.color ] } } if (ls.color.to[index].type == "radial") { ls.color.to[index].data.pos = { x0: -1, y0: -1, r0: 1, x1: 1, y1: 1, r1: null, defaultEnd: { x: null, y: null, r: 3 } } } updateElements() document.getElementById(`con_element_${x.color}-${x.alpha}`).click() } let category = createEle("div", document.getElementById("con_edit"), "display: inline-flex; margin-top: 20px;") let category_content = createEle("div", document.getElementById("con_edit")) listOfCategory.forEach(name => { let ele = createEle("option", dropdown, null, name.split("_")[1]) if (ls.color.to[index].type == name.split("_")[1]) ele.selected = "selected" }) createEle("div", category_content, null, `alpha = ${ls.color.to[index].alpha}`, null, "button").onclick = function() { let alpha = prompt("Alpha (0~1 or * for any) :", ls.color.to[index].alpha) if (alpha != "*") { if (isNaN(alpha)) alpha = 1 else alpha = Number(alpha) alpha = Math.min(Math.max(alpha ?? 1, 0), 1) } ls.color.to[index].alpha = alpha updateElements() document.getElementById(`con_element_${x.color}-${x.alpha}`).click() } if (ls.color.to[index].type == "solid") { /* --------- SOLID --------- */ createEle("div", category_content, null, `color = ${ls.color.to[index].data}`, null, "button").onclick = function() { let color = prompt("Color (hex6):", ls.color.to[index].data) if (color == null) return if (/^#[A-Fa-f0-9]{6}$/g.test(color)) color = color.match(/^#[A-Fa-f0-9]{6}$/g)[0] else return alert("Invalid hex color.") ls.color.to[index].data = color updateElements() document.getElementById(`con_element_${x.color}-${x.alpha}`).click() } } else if (ls.color.to[index].type == "linear" || ls.color.to[index].type == "radial") { /* --------- LINEAR/RADIAL GRADIENT --------- */ createEle("div", category_content, null, `start position = [${ls.color.to[index].data.pos.x0}, ${ls.color.to[index].data.pos.y0}${ls.color.to[index].type == "radial" ? `, ${ls.color.to[index].data.pos.r0}` : ""}]`, null, "button").onclick = function() { let result = getPosition(ls.color.to[index].data.pos.x0, ls.color.to[index].data.pos.y0, ls.color.to[index].type == "radial" ? ls.color.to[index].data.pos.r0 : null, ls.color.to[index].type == "radial") ls.color.to[index].data.pos.x0 = result.x ls.color.to[index].data.pos.y0 = result.y if (ls.color.to[index].type == "radial") ls.color.to[index].data.pos.r0 = result.r updateElements() } createEle("div", category_content, null, `end position = [${ls.color.to[index].data.pos.x1}, ${ls.color.to[index].data.pos.y1}${ls.color.to[index].type == "radial" ? `, ${ls.color.to[index].data.pos.r1}` : ""}]`, null, "button").onclick = function() { let result = getPosition(ls.color.to[index].data.pos.x1, ls.color.to[index].data.pos.y1, ls.color.to[index].type == "radial" ? ls.color.to[index].data.pos.r1 : null, ls.color.to[index].type == "radial") ls.color.to[index].data.pos.x1 = result.x ls.color.to[index].data.pos.y1 = result.y if (ls.color.to[index].type == "radial") ls.color.to[index].data.pos.r1 = result.r updateElements() } createEle("div", category_content, null, `<br>default end position = [${ls.color.to[index].data.pos.defaultEnd.x}, ${ls.color.to[index].data.pos.defaultEnd.y}${ls.color.to[index].type == "radial" ? `, ${ls.color.to[index].data.pos.defaultEnd.r}` : ""}]<br><w style="font-size:12px; color: #e6db74;">This attribute will be used if canvas prototype's end position is undefined.<br>output = start(x/y${ls.color.to[index].type == "radial" ? "/r" : ""}) + def_end(x/y${ls.color.to[index].type == "radial" ? "/r" : ""})</w><br><br>`, null, "button").onclick = function() { let result = getPosition(ls.color.to[index].data.pos.defaultEnd.x, ls.color.to[index].data.pos.defaultEnd.y, ls.color.to[index].type == "radial" ? ls.color.to[index].data.pos.defaultEnd.r : null, ls.color.to[index].type == "radial") ls.color.to[index].data.pos.defaultEnd.x = result.x ls.color.to[index].data.pos.defaultEnd.y = result.y if (ls.color.to[index].type == "radial") ls.color.to[index].data.pos.defaultEnd.r = result.r updateElements() } ls.color.to[index].data.colorStop.forEach((thisColorStop, indexColorStop) => { createEle("div", category_content, null, `color stop ${indexColorStop}: [${thisColorStop.offset}, ${thisColorStop.color}]`, null, "button").onclick = function() { let input = prompt("Float (0~1), Color (hex6)", `${thisColorStop.offset}, ${thisColorStop.color}`) if (input == null) return input = input.split(",") if (input.length <= 1) return alert("Invalid input.") let offset = input[0].trim(), color = input[1].trim() if (isNaN(offset) || offset == "") return alert("Invalid offset.") offset = Math.min(Math.max(offset ?? 1, 0), 1) offset = Number(offset) if (/^#[A-Fa-f0-9]{6}$/g.test(color)) color = color.match(/^#[A-Fa-f0-9]{6}$/g)[0] else return alert("Invalid hex color.") ls.color.to[index].data.colorStop[indexColorStop].offset = offset ls.color.to[index].data.colorStop[indexColorStop].color = color updateElements() document.getElementById(`con_element_${x.color}-${x.alpha}`).click() } }) let stopButtons = createEle("div", category_content, "margin-top: 20px; font-size: 12px; display: inline-flex") createEle("div", stopButtons, "cursor: pointer; overflow:hidden;height: fit-content;width: fit-content;padding: 3px 7px; border-radius: 3px;border: solid #4b837e 4px;box-sizing: border-box;background: #6dbfb8;", "New Color stop").onclick = function() { let indexNumber = prompt("Index:", ls.color.to[index].data.colorStop.length) if (isNaN(indexNumber) || indexNumber == "" || indexNumber > ls.color.to[index].data.colorStop.length || indexNumber < 0) indexNumber = ls.color.to[index].data.colorStop.length indexNumber = Number(indexNumber) ls.color.to[index].data.colorStop.splice(indexNumber, 0, ls.color.to[index].data.colorStop[ls.color.to[index].data.colorStop.length - 1]) updateElements() document.getElementById(`con_element_${x.color}-${x.alpha}`).click() } createEle("div", stopButtons, "margin-left: 5px; cursor: pointer; overflow:hidden;height: fit-content;width: fit-content;padding: 3px 7px; border-radius: 3px;border: solid #974545 4px;box-sizing: border-box;background: #bb5555;", "Remove Color stop").onclick = function() { if (ls.color.to[index].data.colorStop.length <= 2) return alert("You cannot remove more color stop.") let indexNumber = prompt("Index:", ls.color.to[index].data.colorStop.length - 1) if (isNaN(indexNumber) || indexNumber == "" || indexNumber >= ls.color.to[index].data.colorStop.length || indexNumber < 0) return alert("This index does not exist") indexNumber = Number(indexNumber) if (!confirm(`Are you sure to delete this?\n\n${JSON.stringify(ls.color.to[index].data.colorStop[indexNumber], null, 4)}`)) return ls.color.to[index].data.colorStop.splice(indexNumber, 1) updateElements() document.getElementById(`con_element_${x.color}-${x.alpha}`).click() } } else if (ls.color.to[index].type == "animated") { /* --------- ANIMATED --------- */ createEle("div", category_content, null, `duration = ${ls.color.to[index].data.duration}`, null, "button").onclick = function() { let duration = prompt("Interval (second) > 0", ls.color.to[index].data.duration) if (duration == null) return if (isNaN(duration) || duration == "") return alert("Invalid duration.") duration = Number(duration) if (duration <= 0) duration = 5 ls.color.to[index].data.duration = duration updateElements() document.getElementById(`con_element_${x.color}-${x.alpha}`).click() } ls.color.to[index].data.keyframes.forEach((thisKeyframe, indexKeyframe) => { createEle("div", category_content, null, `keyframe ${indexKeyframe}: ${thisKeyframe}`, null, "button").onclick = function() { let keyframe = prompt("Color (hex6)", `${thisKeyframe}`) if (keyframe == null) return if (/^#[A-Fa-f0-9]{6}$/g.test(keyframe)) keyframe = keyframe.match(/^#[A-Fa-f0-9]{6}$/g)[0] else return alert("Invalid hex color.") ls.color.to[index].data.keyframes[indexKeyframe] = keyframe updateElements() document.getElementById(`con_element_${x.color}-${x.alpha}`).click() } }) let keyframeButtons = createEle("div", category_content, "margin-top: 20px; font-size: 12px; display: inline-flex") createEle("div", keyframeButtons, "cursor: pointer; overflow:hidden;height: fit-content;width: fit-content;padding: 3px 7px; border-radius: 3px;border: solid #4b837e 4px;box-sizing: border-box;background: #6dbfb8;", "New Keyframe").onclick = function() { let indexNumber = prompt("Index:", ls.color.to[index].data.keyframes.length) if (isNaN(indexNumber) || indexNumber == "" || indexNumber > ls.color.to[index].data.keyframes.length || indexNumber < 0) indexNumber = ls.color.to[index].data.keyframes.length indexNumber = Number(indexNumber) ls.color.to[index].data.keyframes.splice(indexNumber, 0, ls.color.to[index].data.keyframes[ls.color.to[index].data.keyframes.length - 1]) updateElements() document.getElementById(`con_element_${x.color}-${x.alpha}`).click() } createEle("div", keyframeButtons, "margin-left: 5px; cursor: pointer; overflow:hidden;height: fit-content;width: fit-content;padding: 3px 7px; border-radius: 3px;border: solid #974545 4px;box-sizing: border-box;background: #bb5555;", "Remove Keyframe").onclick = function() { if (ls.color.to[index].data.keyframes.length <= 2) return alert("You cannot remove more color stop.") let indexNumber = prompt("Index:", ls.color.to[index].data.keyframes.length - 1) if (isNaN(indexNumber) || indexNumber == "" || indexNumber >= ls.color.to[index].data.keyframes.length || indexNumber < 0) return alert("This index does not exist") indexNumber = Number(indexNumber) if (!confirm(`Are you sure to delete this?\n\n${JSON.stringify(ls.color.to[index].data.keyframes[indexNumber], null, 4)}`)) return ls.color.to[index].data.keyframes.splice(indexNumber, 1) updateElements() document.getElementById(`con_element_${x.color}-${x.alpha}`).click() } } createEle("div", document.getElementById("con_edit"), "cursor: pointer;right: 15px; top: 15px;overflow:hidden;position: absolute;height: fit-content;width: fit-content;padding: 5px 15px; border-radius: 3px;border: solid #974545 4px;box-sizing: border-box;background: #bb5555;", "Delete this color").onclick = function() { if (!confirm(`Are you sure to delete?\n\n${JSON.stringify(x, null, 4)}`)) return ls.color.from.splice(index, 1) ls.color.to.splice(index, 1) document.getElementById("con_edit_button_viewJSON").click() updateElements() } } if (ls.color.to[index].type == "animated") { animatedObj[index] = { isTriggered: false, color: ls.color.to[index].data.keyframes[0], keyFrames: ls.color.to[index].data.keyframes, totalFrames: 60 * ls.color.to[index].data.duration, currentFrames: 0 } } }) } getColorElementsFromLocalStorage() document.getElementById("con_element_button_addColor").onclick = function() { let color = prompt("Color (hex6):", "#ffffff") if (color == null) return if (/^#[A-Fa-f0-9]{6}$/g.test(color)) color = color.match(/^#[A-Fa-f0-9]{6}$/g)[0] else return alert("Invalid hex color.") let alpha = prompt("Alpha (0~1 or * for any) :", "*") if (alpha != "*") { if (isNaN(alpha)) alpha = 1 else alpha = Number(alpha) alpha = Math.min(Math.max(alpha ?? 1, 0), 1) } if (!JSON.stringify(ls.color.from).includes(JSON.stringify({color: color, alpha: alpha}))) { ls.color.from.push({color: color, alpha: alpha}) ls.color.to.push({ type: "solid", alpha: alpha, preview: color, data: color }) } updateElements() } let findColorArr = [], isFindColor = false document.getElementById("con_element_button_findColor").onclick = function() { if (!isFindColor) { findColorArr = [] isFindColor = true document.getElementById("con_element_button_findColor").style.background = "#BB5555" document.getElementById("con_element_button_findColor").style.border = "solid #974545 4px" document.getElementById("con_element_button_findColor").innerHTML = "Stop" } else { isFindColor = false document.getElementById("con_element_button_findColor").style.background = "#be95be" document.getElementById("con_element_button_findColor").style.border = "solid #785978 4px" document.getElementById("con_element_button_findColor").innerHTML = "Find color" document.getElementById("con_edit").innerHTML = findColorArr.map(x => `<w onclick="navigator.clipboard.writeText('${x.color}')" style="cursor: pointer; display: inline-flex; padding: 5px 10px; background:${addAlpha(x.color, x.alpha)}">${x.color} (${x.alpha.toFixed(1)})</w>`).toString().replaceAll(",", "") } } function convertColor(this_, x0, y0, x1, y1, isStroke) { try { ls.color.from.forEach((obj, index) => { let outputColor, thisObj if (!isStroke) outputColor = this_.fillStyle else outputColor = this_.strokeStyle if (outputColor == obj.color) { if (obj.alpha == "*" || obj.alpha == this_.globalAlpha) { thisObj = ls.color.to[index].data if (ls.color.to[index].type == "solid") outputColor = thisObj if (ls.color.to[index].type == "linear") { x0 = x0 || thisObj.pos.x0 || 0 y0 = y0 || thisObj.pos.y0 || 0 x1 = x1 || thisObj.pos.x1 || x0 + thisObj.pos.defaultEnd.x y1 = y1 || thisObj.pos.y1 || y0 + thisObj.pos.defaultEnd.y outputColor = ctx.createLinearGradient(x0, y0, x1, y1) thisObj.colorStop.forEach(x => { outputColor.addColorStop(x.offset, x.color) }) } if (ls.color.to[index].type == "radial") { let r0, r1 x0 = x0 || thisObj.pos.x0 || (x1 == null ? (x0) : (x1 - (x1 - x0) * 1)) y0 = y0 || thisObj.pos.y0 || (y1 == null ? (y0) : (y1 - (y1 - y0) * 1)) r0 = r0 || thisObj.pos.r0 || 0 x1 = x1 || thisObj.pos.x1 || x0 + thisObj.pos.defaultEnd.x y1 = y1 || thisObj.pos.y1 || y0 + thisObj.pos.defaultEnd.y r1 = r1 || thisObj.pos.r1 || r0 + thisObj.pos.defaultEnd.r outputColor = ctx.createRadialGradient(x0, y0, r0, x1, y1, r1) thisObj.colorStop.forEach(x => { outputColor.addColorStop(x.offset, x.color) }) } if (ls.color.to[index].type == "animated") { // https://stackoverflow.com/questions/53380267/colors-that-change-overtime-in-a-canvas-js-trouble-with-setinterval if (!animatedObj[index]) return if (!animatedObj[index].isTriggered) { animatedObj[index].isTriggered = true localStorage.customizer = JSON.stringify(ls) animatedObj[index].color = [hexToRgb(thisObj.keyframes[0]).r, hexToRgb(thisObj.keyframes[0]).g, hexToRgb(thisObj.keyframes[0]).b] animatedObj[index].keyFrames = thisObj.keyframes.map(x => [hexToRgb(x).r, hexToRgb(x).g, hexToRgb(x).b]) animatedObj[index].totalFrames = 60 * thisObj.duration animatedObj[index].currentFrame = 0 function update() { animatedObj[index].currentFrame = (animatedObj[index].currentFrame + 1) % animatedObj[index].totalFrames let keyFrameIndex = animatedObj[index].currentFrame / (animatedObj[index].totalFrames / animatedObj[index].keyFrames.length) let prev = animatedObj[index].keyFrames[Math.floor(keyFrameIndex) % animatedObj[index].keyFrames.length] let next = animatedObj[index].keyFrames[Math.ceil(keyFrameIndex) % animatedObj[index].keyFrames.length] let inBetweenRatio = keyFrameIndex - Math.floor(keyFrameIndex) animatedObj[index].color[0] = Math.floor((next[0] - prev[0]) * inBetweenRatio) + prev[0] animatedObj[index].color[1] = Math.floor((next[1] - prev[1]) * inBetweenRatio) + prev[1] animatedObj[index].color[2] = Math.floor((next[2] - prev[2]) * inBetweenRatio) + prev[2] requestAnimationFrame(update) } update() } outputColor = rgbToHex(animatedObj[index].color[0], animatedObj[index].color[1], animatedObj[index].color[2]) } if (!isStroke) this_.fillStyle = outputColor else this_.strokeStyle = outputColor if (ls.color.to[index].alpha != "*") this_.globalAlpha = ls.color.to[index].alpha } } }) } catch (error) {errorMessage(error)} } function convertText(text) { ls.text.forEach(t => { let a = new RegExp(t.from, "g") if (a.test(text)) text = text.replaceAll(a, t.to) }) return text } function colorFinder(color, alpha) { if (isFindColor && !findColorArr.map(x => x.color).includes(color) && /^#[A-Fa-f0-9]{6}$/g.test(color) && !ls.color.from.map(x => x.color).includes(color)) findColorArr.push({color: color, alpha: alpha}) } // Credit to lexiyvv and Tinhone for (let ctx of [CanvasRenderingContext2D, OffscreenCanvasRenderingContext2D]) { if (ctx.prototype.RarityColorFillText == undefined) { ctx.prototype.RarityColorFillText = ctx.prototype.fillText; ctx.prototype.RarityColorStrokeText = ctx.prototype.strokeText; ctx.prototype.RarityColorFillRect = ctx.prototype.fillRect; ctx.prototype.RarityColorStroke = ctx.prototype.stroke; ctx.prototype.RarityColorFill = ctx.prototype.fill; ctx.prototype.RarityColorStrokeRect = ctx.prototype.strokeRect; ctx.prototype.RarityColorMeasureText = ctx.prototype.measureText; } else { break }; ctx.prototype.fillRect = function(x, y, width, height) { colorFinder(this.fillStyle, this.globalAlpha) convertColor(this, x, y, x + width, y + height, false) return this.RarityColorFillRect(x, y, width, height); }; ctx.prototype.fill = function(path, fillRule) { colorFinder(this.fillStyle, this.globalAlpha) convertColor(this, null, null, null, null, false) if (path != null) return this.RarityColorFill(path, fillRule); else return this.RarityColorFill(fillRule); } ctx.prototype.fillText = function(text, x, y) { colorFinder(this.fillStyle, this.globalAlpha) convertColor(this, x, y, null, null, false) text = convertText(text) return this.RarityColorFillText(text, x, y); }; ctx.prototype.strokeText = function(text, x, y) { colorFinder(this.fillStyle, this.globalAlpha) convertColor(this, x, y, null, null, true) text = convertText(text) return this.RarityColorStrokeText(text, x, y); }; ctx.prototype.stroke = function(path) { colorFinder(this.fillStyle, this.globalAlpha) convertColor(this, null, null, null, null, true) if (path != null) return this.RarityColorStroke(path); else return this.RarityColorStroke(); }; ctx.prototype.strokeRect = function(x, y, width, height) { colorFinder(this.fillStyle, this.globalAlpha) convertColor(this, x, y, x + width, y + height, true) return this.RarityColorStrokeRect(x, y, width, height); }; ctx.prototype.measureText = function(text) { text = convertText(text) return this.RarityColorMeasureText(text); } } document.documentElement.addEventListener("keydown", function (e) { if (event.keyCode == "192" && event.shiftKey) { if (container.style.transform == "translate(-50%, -50%) scale(1)") { container.style.transform = "translate(-50%, -50%) scale(0)" } else { container.style.transform = "translate(-50%, -50%) scale(1)" } } }); document.getElementById("closeButton").onclick = function() { container.style.transform = "translate(-50%, -50%) scale(0)" } GM_addStyle(` .category { padding: 5px 20px; margin-right: 5px; background: rgba(0, 0, 0, 0.2); border-radius: 5px; cursor: pointer; } .button { cursor: pointer; } ::-webkit-scrollbar { width: 5px; } ::-webkit-scrollbar-track { background: #00000000; } ::-webkit-scrollbar-thumb { background: #444; border-radius: 5px; } ::-webkit-scrollbar-thumb:hover { background: #444; } .string { color: #e6db74; } .number { color: #ae81ff; } .boolean { color: #ae81ff; } .null { color: #ae81ff; } .key { color: #66d9ef; } `)