您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Lets you customize the status bar
当前为
// ==UserScript== // @name Custom Status Bar // @description Lets you customize the status bar // @version 1.1.0 // @license MIT // @author zorby#1431 // @namespace https://greatest.deepsurf.us/en/users/986787-zorby // @match https://www.geoguessr.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=geoguessr.com // ==/UserScript== function pathMatches(path) { return location.pathname.match(new RegExp(`^/(?:[^/]+/)?${path}$`)) } function getIndex(element) { if (!element) return -1 let i = 0 while (element = element.previousElementSibling) { i++ } return i } const OBSERVER_CONFIG = { characterDataOldValue: false, subtree: true, childList: true, characterData: false } const SCRIPT_PREFIX = "csb__" const CONFIG_KEY = SCRIPT_PREFIX + "config" const STYLE_ID = SCRIPT_PREFIX + "style" const PERCENTAGE_INPUT_CLASS = SCRIPT_PREFIX + "percentage-input" const COLOR_INPUT_CLASS = SCRIPT_PREFIX + "color-input" const TEXT_INPUT_CLASS = SCRIPT_PREFIX + "text-input" const DELETE_BUTTON_CLASS = SCRIPT_PREFIX + "delete-button" const STANDARD_BUTTON_CLASS = SCRIPT_PREFIX + "standard-button" const DOWN_BUTTON_CLASS = SCRIPT_PREFIX + "down-button" const UP_BUTTON_CLASS = SCRIPT_PREFIX + "up-button" const CUSTOMIZE_STATUS_BAR_BUTTON_ID = SCRIPT_PREFIX + "customize-status-bar-button" const ADD_GRADIENT_NODE_BUTTON_ID = SCRIPT_PREFIX + "add-gradient-node-button" const CUSTOMIZE_STATUS_BAR_SCREEN_ID = SCRIPT_PREFIX + "customize-status-bar-screen" const GRADIENT_NODE_LIST_ID = SCRIPT_PREFIX + "gradient-node-list" const TEXT_COLOR_NODE_LIST_ID = SCRIPT_PREFIX + "text-color-node-list" const RESUME_BUTTON_ID = SCRIPT_PREFIX + "resume-button" const defaultNode = () => ({ color: "#000000", percentage: 100 }) const DEFAULT_GRADIENT_NODES = [ { color: "#000000", percentage: 0 }, { color: "#000000", percentage: 100 } ] const DEFAULT_TEXT_COLORS = [ "#b0b0b0", "#ffffff" ] const configString = localStorage.getItem(CONFIG_KEY) let gradientNodes = DEFAULT_GRADIENT_NODES let textColors = DEFAULT_TEXT_COLORS if (configString) { const config = JSON.parse(configString) gradientNodes = config.gradient textColors = config.textColors } const CUSTOMIZE_STATUS_BAR_BUTTON = ` <button id="${CUSTOMIZE_STATUS_BAR_BUTTON_ID}" class="button_button__CnARx button_variantSecondary__lSxsR"> Customize status bar </button> <div class="game-menu_divider__f2BbL"></div> ` const GRADIENT_NODE = ` <div style=" display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: auto; "> <div class="grid-element"> <input type="number" class="${PERCENTAGE_INPUT_CLASS}" min="0" max="100" step="any" required></input> <div style="font-weight: 700;">%</div> </div> <div class="grid-element" style="flex-direction: row-reverse"> <button class="${DELETE_BUTTON_CLASS}">X</button> <button class="${STANDARD_BUTTON_CLASS} ${DOWN_BUTTON_CLASS}">v</button> <button class="${STANDARD_BUTTON_CLASS} ${UP_BUTTON_CLASS}" style="margin-left: 1rem;">^</button> <input type="color" class="${COLOR_INPUT_CLASS}" ></input> <input type="text" class="${TEXT_INPUT_CLASS}" style="width: 4.5rem;" pattern="[0-9a-fA-F]{6}" required></input> <div style="font-weight: 700;">#</div> </div> </div> ` const appendTextColorNode = (parent, label, index) => { const html = ` <div style=" display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: auto; "> <div class="grid-element"> <div style="font-weight: 700;">${label}</div> </div> <div class="grid-element" style="flex-direction: row-reverse"> <input type="color" class="${COLOR_INPUT_CLASS}" ></input> <input type="text" class="${TEXT_INPUT_CLASS}" style="width: 4.5rem;" pattern="[0-9a-fA-F]{6}" required></input> <div style="font-weight: 700;">#</div> </div> </div> ` parent.insertAdjacentHTML("beforeend", html) const textColorNode = parent.lastElementChild const colorInput = textColorNode.querySelector(`.${COLOR_INPUT_CLASS}`) const colorTextInput = textColorNode.querySelector(`.${TEXT_INPUT_CLASS}`) colorInput.value = textColors[index] colorTextInput.value = textColors[index].substring(1) colorInput.oninput = () => { textColors[index] = colorInput.value colorTextInput.value = textColors[index].substring(1) updateStatusBarStyles() } colorTextInput.oninput = () => { textColors[index] = "#" + colorTextInput.value colorInput.value = textColors[index] updateStatusBarStyles() } } const generateGradientString = () => { return `linear-gradient(180deg, ${ gradientNodes.map((node) => `${node.color} ${node.percentage}%`).join(",") })` } const updateStatusBarStyles = () => { const style = document.getElementById(STYLE_ID) style.innerHTML = ` .slanted-wrapper_variantPurple__95_Ub { --variant-background-color: ${generateGradientString()}; } .slanted-wrapper_variantPurple__95_Ub .status_label__SNHKT { color: ${textColors[0]} } .slanted-wrapper_variantPurple__95_Ub .status_value__xZMNY { color: ${textColors[1]} } ` localStorage.setItem(CONFIG_KEY, JSON.stringify({ "gradient": gradientNodes, "textColors": textColors })) } const appendGradientNode = (parent) => { parent.insertAdjacentHTML("beforeend", GRADIENT_NODE) const gradientNode = parent.lastElementChild const percentageInput = gradientNode.querySelector(`.${PERCENTAGE_INPUT_CLASS}`) const colorInput = gradientNode.querySelector(`.${COLOR_INPUT_CLASS}`) const colorTextInput = gradientNode.querySelector(`.${TEXT_INPUT_CLASS}`) const deleteButton = gradientNode.querySelector(`.${DELETE_BUTTON_CLASS}`) const upButton = gradientNode.querySelector(`.${UP_BUTTON_CLASS}`) const downButton = gradientNode.querySelector(`.${DOWN_BUTTON_CLASS}`) const updateInputs = () => { percentageInput.value = gradientNodes[getIndex(gradientNode)].percentage colorInput.value = gradientNodes[getIndex(gradientNode)].color colorTextInput.value = gradientNodes[getIndex(gradientNode)].color.substring(1) } gradientNode.updateInputs = updateInputs updateInputs() percentageInput.oninput = () => { gradientNodes[getIndex(gradientNode)].percentage = percentageInput.value updateStatusBarStyles() } colorInput.oninput = () => { gradientNodes[getIndex(gradientNode)].color = colorInput.value colorTextInput.value = gradientNodes[getIndex(gradientNode)].color.substring(1) updateStatusBarStyles() } colorTextInput.oninput = () => { gradientNodes[getIndex(gradientNode)].color = "#" + colorTextInput.value colorInput.value = gradientNodes[getIndex(gradientNode)].color updateStatusBarStyles() } deleteButton.onclick = () => { gradientNodes.splice(getIndex(gradientNode), 1) gradientNode.remove() updateStatusBarStyles() } upButton.onclick = () => { let temp = gradientNodes[getIndex(gradientNode)].color gradientNodes[getIndex(gradientNode)].color = gradientNodes[getIndex(gradientNode) - 1].color gradientNodes[getIndex(gradientNode) - 1].color = temp parent.children[getIndex(gradientNode) - 1].updateInputs() updateInputs() updateStatusBarStyles() } downButton.onclick = () => { let temp = gradientNodes[getIndex(gradientNode)].color gradientNodes[getIndex(gradientNode)].color = gradientNodes[getIndex(gradientNode) + 1].color gradientNodes[getIndex(gradientNode) + 1].color = temp parent.children[getIndex(gradientNode) + 1].updateInputs() updateInputs() updateStatusBarStyles() } } const CUSTOMIZE_STATUS_BAR_SCREEN = ` <div id="${CUSTOMIZE_STATUS_BAR_SCREEN_ID}" class="game-menu_gameMenu__8ON8f"> <style> .${PERCENTAGE_INPUT_CLASS}, .${COLOR_INPUT_CLASS}, .${TEXT_INPUT_CLASS}, .${DELETE_BUTTON_CLASS}, .${STANDARD_BUTTON_CLASS} { background: rgba(255,255,255,0.1); color: white; border: none; border-radius: 5px; font-family: var(--default-font); font-size: var(--font-size-14); padding: 0.5rem; } .${PERCENTAGE_INPUT_CLASS}, .${COLOR_INPUT_CLASS} { width: 3rem; } .${PERCENTAGE_INPUT_CLASS}, .${TEXT_INPUT_CLASS} { text-align: center; -moz-appearance: textfield; } .${PERCENTAGE_INPUT_CLASS}::-webkit-outer-spin-button, .${PERCENTAGE_INPUT_CLASS}::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } .${COLOR_INPUT_CLASS} { height: 100%; padding: 0.25rem; } .${COLOR_INPUT_CLASS}::-webkit-color-swatch-wrapper { padding: 0; } .${COLOR_INPUT_CLASS}::-webkit-color-swatch { border: none; border-radius: 5px; } .${TEXT_INPUT_CLASS}:invalid, .${PERCENTAGE_INPUT_CLASS}:invalid { background: rgba(209, 27, 38, 0.1); color: var(--color-red-60); } .${DELETE_BUTTON_CLASS}, .${STANDARD_BUTTON_CLASS} { width: 2rem; user-select: none; } .${DELETE_BUTTON_CLASS} { background: rgba(209, 27, 38, 0.1); } .${DELETE_BUTTON_CLASS}:hover, .${STANDARD_BUTTON_CLASS}:hover, .${COLOR_INPUT_CLASS}:hover { cursor: pointer; } .${DELETE_BUTTON_CLASS}:hover { background: var(--color-red-60); } .${STANDARD_BUTTON_CLASS}:hover { background: var(--color-grey-70); } #${CUSTOMIZE_STATUS_BAR_SCREEN_ID} .grid-element { display: flex; align-items: center; gap: 0.5rem; } </style> <div class="game-menu_innerContainer__jEQ9E"> <p class="game-menu_header__KeQ7F">Customize Status Bar</p> <div class="game-menu_volumeContainer__dRQtK" style="display: flex; flex-direction: column; gap: 0.4rem;"> <p class="game-menu_subHeader___oVKH">Gradient</p> <div id="${GRADIENT_NODE_LIST_ID}" style="display: flex; flex-direction: column; gap: 0.4rem; max-height: 10rem; overflow-y: auto;"></div> <button id="${ADD_GRADIENT_NODE_BUTTON_ID}" class="button_button__CnARx button_variantSecondary__lSxsR">Add node</button> </div> <div class="game-menu_volumeContainer__dRQtK" style="display: flex; flex-direction: column; gap: 0.4rem;"> <p class="game-menu_subHeader___oVKH">Text colors</p> <div id="${TEXT_COLOR_NODE_LIST_ID}" style="display: flex; flex-direction: column; gap: 0.4rem;"></div> </div> <div class="game-menu_divider__f2BbL"></div> <button id="${RESUME_BUTTON_ID}" class="button_button__CnARx button_variantPrimary__xc8Hp">Resume Game</button> </div> </div> ` const onCustomizeStatusBarButtonClick = () => { document.querySelector(".game-menu_gameMenu__8ON8f .button_variantPrimary__xc8Hp").click() const gameLayout = document.querySelector(".game-layout") gameLayout.insertAdjacentHTML("beforeend", CUSTOMIZE_STATUS_BAR_SCREEN) const addGradientNodeButton = document.getElementById(ADD_GRADIENT_NODE_BUTTON_ID) addGradientNodeButton.onclick = () => { gradientNodes.push(defaultNode()) appendGradientNode(gradientNodeList) } const resumeButton = document.getElementById(RESUME_BUTTON_ID) resumeButton.onclick = () => { document.querySelector(".game-layout__status").style.zIndex = null document.getElementById(CUSTOMIZE_STATUS_BAR_SCREEN_ID).remove() } document.querySelector(".game-layout__status").style.zIndex = "30" const gradientNodeList = document.getElementById(GRADIENT_NODE_LIST_ID) for (const i in gradientNodes) { appendGradientNode(gradientNodeList) } const textColorNodeList = document.getElementById(TEXT_COLOR_NODE_LIST_ID) appendTextColorNode(textColorNodeList, "Labels", 0) appendTextColorNode(textColorNodeList, "Values", 1) } const injectCustomizeStatusBarButton = (settingsScreen) => { settingsScreen.insertAdjacentHTML("afterend", CUSTOMIZE_STATUS_BAR_BUTTON) document.getElementById(CUSTOMIZE_STATUS_BAR_BUTTON_ID).onclick = onCustomizeStatusBarButtonClick } const onMutations = () => { if (!pathMatches("game/.+")) return if (!document.getElementById(STYLE_ID)) { const style = document.createElement("style") style.id = STYLE_ID document.body.appendChild(style) updateStatusBarStyles() } const settingsScreen = document.querySelector(".in-game_layout__7zzGJ > .game-menu_gameMenu__8ON8f .game-menu_divider__f2BbL") if (settingsScreen && !document.querySelector(`#${CUSTOMIZE_STATUS_BAR_BUTTON_ID}`)) { injectCustomizeStatusBarButton(settingsScreen) } } const observer = new MutationObserver(onMutations) observer.observe(document.body, OBSERVER_CONFIG)