您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Replaces the background of the geoguessr pages with your own images
- // ==UserScript==
- // @name GeoGuessr Background Replacer
- // @description Replaces the background of the geoguessr pages with your own images
- // @version 2.1.15
- // @author Tyow#3742
- // @match *://*.geoguessr.com/*
- // @license MIT
- // @require https://unpkg.com/@popperjs/core@2.11.5/dist/umd/popper.min.js
- // @namespace https://greasyfork.org/users/1011193
- // @grant GM_addStyle
- // @grant GM_setValue
- // @grant GM_getValue
- // @require https://update.greasyfork.org/scripts/460322/1408713/Geoguessr%20Styles%20Scan.js
- // ==/UserScript==
- // Some code for popup adapted from blink script: https://greasyfork.org/en/scripts/438579-geoguessr-blink-mode
- /* ############################################################################### */
- /* ##### DON'T MODIFY ANYTHING BELOW HERE UNLESS YOU KNOW WHAT YOU ARE DOING ##### */
- /* ############################################################################### */
- const guiHTMLHeader = `
- <div id="backgroundReplacerPopupWrapper">
- <div id="backgroundReplacerSearchWrapper">
- <div id="backgroundReplacerInputWrapper">
- <div id="backgroundReplacerPopup" style="background: rgba(26, 26, 46, 0.9); padding: 15px; border-radius: 10px; max-height: 80vh; overflow-y: auto; width: 28em">
- <div style="display: flex; justify-content: space-between; align-items: center; margin-top: 10px;">
- <span id="backgroundReplacerLabel1" style="margin: 0; padding-right: 6px;">Add Home Page image</span>
- <input type="url" id="homepageInput" name="homepage" style="background: rgba(255,255,255,0.1); color: white; border: none; border-radius: 5px">
- </div>
- <span>Home Page Images:</span>
- <div id="homePageImages" style="display: flex; justify-content: space-between; align-items: center; margin-top: 10px; flex-direction: column"></div>
- <div style="display: flex; justify-content: space-between; align-items: center; margin-top: 10px;">
- <span id="backgroundReplacerLabel2" style="margin: 0; padding-right: 6px;">Add Other Page Image</span>
- <input type="url" id="otherpagesInput" name="otherpages" style="background: rgba(255,255,255,0.1); color: white; border: none; border-radius: 5px">
- </div>
- <span>Other Pages Images:</span>
- <div id="otherPagesImages" style="display: flex; justify-content: space-between; align-items: center; margin-top: 10px; flex-direction: column"></div>
- </div>
- <button style="width: 59.19px" id="backgroundReplacerToggle"><picture id="backgroundReplacerTogglePicture" style="justify-content: center"><img src="https://www.svgrepo.com/show/342899/wallpaper.svg" style="width: 15px; filter: brightness(0) invert(1); opacity: 100%;"></picture></button>
- </div>
- </div>
- </div>
- `
- let homePageImageList = GM_getValue("homepageImages");
- let otherImages = GM_getValue("otherImages");
- // Defaults
- if (homePageImageList == undefined) {
- homePageImageList = [
- "https://cdn.wallpapersafari.com/6/80/9ZbpYo.jpg",
- "https://cdn.wallpapersafari.com/25/72/dtkc16.jpg",
- "https://i.imgur.com/l9K9IOq.jpg",
- ];
- GM_setValue("homepageImages", homePageImageList);
- }
- if (otherImages == undefined) {
- otherImages = [
- "https://imgur.com/eK23SeH.jpg",
- "https://i.imgur.com/l9K9IOq.jpg"
- ];
- GM_setValue("otherImages", otherImages);
- }
- let hide = false;
- let styles = GM_getValue("backgroundReplacerStyles");
- if (!styles) {
- hide = true;
- styles = {};
- }
- let homePageImgURL;
- //console.log(cn("label_variantWhite__"))
- const setHomePageImg = (img = false) => {
- if (img) {
- homePageImgURL = img;
- } else if(homePageImageList.length) {
- homePageImgURL = homePageImageList[Math.floor((Math.random()*homePageImageList.length))];
- } else {
- homePageImgURL = "";
- }
- // console.log(homePageImgURL);
- }
- setHomePageImg();
- let otherPagesImgURL;
- const setOtherImg = (img = false) => {
- if (img) {
- otherPagesImgURL = img;
- } else if(otherImages.length) {
- otherPagesImgURL = otherImages[Math.floor((Math.random()*otherImages.length))];
- } else {
- otherPagesImgURL = "";
- }
- }
- setOtherImg();
- let css = `.customBackground { bottom: 0;
- display: block;
- height: 100%;
- object-fit: cover;
- pointer-events: none;
- position: fixed;
- right: 0;
- transition: .2s ease-in-out;
- width: 100%;
- z-index: -1;
- }
- .zindex {
- z-index: -1;
- }
- .deleteIcon {
- width: 25px;
- filter: brightness(0) invert(1);
- opacity: 60%;
- }
- .backgroundImage {
- width: 20em;
- }
- .deleteButton {
- width: 59.19px;
- margin-bottom: 8em;
- }
- .backgroundImageWrapper {
- display: flex;
- padding: .5em;
- }
- .backgroundImageWrapper {
- position: relative;
- display: flex; /* To lay out the imageContainer and button side by side */
- align-items: center; /* Vertically center align the contents */
- }
- .imageContainer {
- position: relative;
- width: /* e.g., 300px; */;
- height: /* e.g., 200px; */;
- overflow: hidden;
- cursor: pointer; /* This line changes the cursor */
- }
- .backgroundImage {
- width: 100%;
- height: 100%;
- transition: opacity 0.3s ease;
- vertical-align: bottom;
- }
- .overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: rgba(0,0,0,0.7);
- display: flex;
- justify-content: center;
- align-items: center;
- opacity: 0;
- transition: opacity 0.3s ease;
- }
- .imageContainer:hover .backgroundImage {
- opacity: 0.3;
- }
- .imageContainer:hover .overlay {
- opacity: 1;
- }
- .overlay span {
- color: white;
- font-size: 18px;
- }
- /* Add some margin if you want space between the image and the button */
- .backgroundImageWrapper button {
- margin-left: 10px; /* adjust as needed */
- }
- /* You can style the text within the overlay here */
- .overlay span {
- color: white;
- font-size: 18px;
- text-align: center;
- }
- .deleteIconPicture {
- justifyContent:center;
- }
- .removeBackground {
- display: none
- }
- .shadow {
- box-shadow: 0 .25rem 2.75rem rgba(32,17,46,.2),0 1.125rem 2.25rem -1.125rem rgba(0,0,0,.24),0 1.875rem 3.75rem -.625rem rgba(0,0,0,.16);
- background: rgba(0, 0, 0, 0.35);
- }
- .blurBefore::before {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(0, 0, 0, 0.35);
- filter: blur(10px);
- z-index: -1;
- }
- .highscoreModification {
- border-radius: .5rem;
- background: var(--ds-color-white-10);
- backdrop-filter: blur(11px);
- padding: .5rem;
- }
- .mapInfoModification {
- padding: 1rem;
- border-radius: .5rem;
- background: var(--ds-color-white-10);
- backdrop-filter: blur(12px);
- }
- .textShadow {
- text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5);
- }
- .cardModification {
- backdrop-filter: blur(12px);
- }
- .mapSelectorModification {
- background: var(--ds-color-white-10);
- backdrop-filter: blur(12px);
- }
- .highScoreTitleModification {
- background: var(--ds-color-white-10);
- backdrop-filter: blur(12px);
- border-radius: .5rem;
- padding: .5rem;
- }
- `;
- GM_addStyle(css);
- const showPopup = (showButton, popup) => {
- popup.style.display = 'block';
- Popper.createPopper(showButton, popup, {
- placement: 'bottom',
- modifiers: [
- {
- name: 'offset',
- options: {
- offset: [0, 10],
- },
- },
- ],
- });
- }
- const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
- const iterativeSetTimeout = async (func, initDelay, cond) => {
- while (!cond()) {
- await delay(initDelay);
- await func();
- initDelay *= 2;
- stylesUsed.forEach(style => {
- styles[style] = cn(style);
- });
- }
- };
- // Caching system for styles
- // Basically, we have a browser stored styles object,
- // which contains the most recent classNames found by scanStyles()
- // This is what the script will immediately use upon loading,
- // so that there's no pause in delivering the UI to the user
- // But the script will also fire off this function
- // which will use the above iterativeSetTimeout function to call scanStyles
- // This is so there aren't a thousand calls in quick succession.
- // Once all the classNames we're looking for are found,
- // it will update the local storage and the ui with the (possibly) new classnames
- const stylesUsed = [
- "quick-search_wrapper__",
- "quick-search_searchInputWrapper__",
- "quick-search_searchInputButton__",
- "quick-search_iconSection__",
- ];
- const uploadDownloadStyles = async () => {
- stylesUsed.forEach(style => {
- // styles[style] = cn(style);
- // console.log(style);
- // console.log(cn(style));
- });
- await iterativeSetTimeout(scanStyles, 0.1, () => checkAllStylesFound(stylesUsed) !== undefined);
- if (hide) {
- document.querySelector("#backgroundReplacerPopupWrapper").hidden = "";
- }
- stylesUsed.forEach(style => {
- styles[style] = cn(style);
- // console.log(style);
- // console.log(cn(style));
- });
- setStyles();
- GM_setValue("backgroundReplacerStyles", styles);
- }
- const getStyle = style => {
- return styles[style];
- }
- const setStyles = () => {
- try {
- document.querySelector("#backgroundReplacerSearchWrapper").className = getStyle("quick-search_wrapper__");
- document.querySelector("#backgroundReplacerInputWrapper").className = getStyle("quick-search_searchInputWrapper__");
- document.querySelector("#backgroundReplacerToggle").className = getStyle("quick-search_searchInputButton__");
- document.querySelector("#backgroundReplacerLabel1").className = getStyle("label_sizeXSmall__") + getStyle("label_variantWhite__");
- document.querySelector("#backgroundReplacerLabel2").className = getStyle("label_sizeXSmall__") + getStyle("label_variantWhite__");
- document.querySelector("#backgroundReplacerTogglePicture").className = getStyle("quick-search_iconSection__");
- document.querySelectorAll(".deleteButton").forEach(el => el.className = el.className + " " + getStyle("quick-search_searchInputButton__"));
- } catch (err) {
- console.error(err);
- }
- }
- const insertHeaderGui = async (header, gui) => {
- header.insertAdjacentHTML('afterbegin', gui);
- // Resolve class names
- if (hide) {
- document.querySelector("#backgroundReplacerPopupWrapper").hidden = "true"
- }
- scanStyles().then(() => uploadDownloadStyles());
- setStyles();
- const showButton = document.querySelector('#backgroundReplacerToggle');
- const popup = document.querySelector('#backgroundReplacerPopup');
- popup.style.display = 'none';
- document.addEventListener('click', (e) => {
- const target = e.target;
- if (target == popup || popup.contains(target) || !document.contains(target)) return;
- if (target.matches('#backgroundReplacerToggle, #backgroundReplacerToggle *')) {
- e.preventDefault();
- showPopup(showButton, popup);
- } else {
- popup.style.display = 'none';
- }
- });
- }
- // Global to track whether the most recent image insertion was done on homepage
- let isHomePage = location.pathname == "/";
- const addShadows = () => {
- const mapSelector = document.querySelector("[class^='map-selector_selector__'")
- if (mapSelector && !mapSelector.classList.contains("mapSelectorModification")) {
- mapSelector.classList.add("mapSelectorModification")
- // mapSelector.classList.add("blurBefore")
- }
- const highscoreRoot = document.querySelector("[class^='map-highscore_root__'")
- if (highscoreRoot && !highscoreRoot.classList.contains("highscoreModification")) {
- highscoreRoot.classList.add("highscoreModification")
- }
- const mapStatsText = document.querySelectorAll("[class^='map-stats_mapStatMetricValue__'")
- if (mapStatsText) {
- for (const el of mapStatsText) {
- if (!el.classList.contains("textShadow")) {
- el.classList.add("textShadow")
- }
- }
- }
- const mapInfo = document.querySelector("[class^='map-block_mapInfo__'")
- if (mapInfo && !mapInfo.classList.contains("mapInfoModification")) {
- mapInfo.classList.add("mapInfoModification")
- }
- const howItWorks = document.querySelector("[class^='ranked-system-how-it-works-page_root__'")
- if (howItWorks && !howItWorks.classList.contains("mapInfoModification")) {
- howItWorks.classList.add("mapInfoModification")
- }
- const userStatsCards = document.querySelectorAll("[class^='user-stats-overview_card__'")
- if (userStatsCards) {
- for (const el of userStatsCards) {
- if (!el.classList.contains("cardModification")) {
- el.classList.add("cardModification")
- }
- }
- }
- const mapStats = document.querySelectorAll("[class^='map-stats_mapStat__'")
- if (mapStats) {
- for (const el of mapStats) {
- if (!el.classList.contains("cardModification")) {
- el.classList.add("cardModification")
- }
- }
- }
- const cards = document.querySelectorAll("[class^='card_card__'")
- if (cards) {
- for (const el of cards) {
- if (!el.classList.contains("cardModification")) {
- el.classList.add("cardModification")
- }
- }
- }
- // const highScoreTitle = document.querySelectorAll("[class^='headline_heading__'")
- // for (const el of highScoreTitle) {
- // if (el.textContent == "Highscore" && !el.classList.contains("highScoreTitleModification")) {
- // el.classList.add("highScoreTitleModification")
- // }
- // }
- const mapsCenter = document.querySelector("[class^='maps_center__'")
- if (mapsCenter && !mapsCenter.classList.contains("mapsCenterMoved")) {
- const mapsSwitch = document.querySelector("[class^='map-highscore_switchContainer__'")
- highscoreRoot.insertBefore(mapsCenter, mapsSwitch);
- mapsCenter.classList.add("mapsCenterMoved")
- }
- }
- const homepageModification = () => {
- const startPageWrapper = document.querySelector("[class^=startpage_background__")
- if (startPageWrapper && !startPageWrapper.classList.contains("removeBackground")) {
- startPageWrapper.classList.add('removeBackground');
- }
- const startPageNewWrapper = document.querySelector("[class^=startpage_newWrapper__")
- if (startPageNewWrapper && startPageNewWrapper.style.background != "none") {
- startPageNewWrapper.style.background = "none"
- };
- }
- const otherPageModification = () => {
- const backgroundWrapper = document.querySelector("[class^=background_background__")
- if (backgroundWrapper && !backgroundWrapper.classList.contains("removeBackground")) {
- backgroundWrapper.classList.add('removeBackground');
- }
- }
- const extraStyling = () => {
- addShadows()
- homepageModification()
- otherPageModification()
- }
- const insertBackground = (refresh=false) => {
- let inGame = false;
- let el = document.querySelector("[class^='background_wrapper']");
- if (!el) {
- inGame = true;
- el = document.querySelector("#__next");
- if (!el) return;
- // Because this element has multiple classes, we need to use a different selector
- const def = document.querySelector("[class*=in-game_backgroundDefault__]");
- let reg = /^in-game_backgroundDefault__/;
- if (def) {
- def.classList = Array.from(def.classList).filter(cl => !cl.match(reg));
- }
- const partyRoot = document.querySelector("[class^=party_root__]");
- if (partyRoot) {
- partyRoot.style.background = "none";
- }
- // Without this, you can see the background behind the map in a game summary
- // Purple color used by geoguessr, with .9 alpha
- const purple9 = "rgba(12 12 46 / .9)";
- // .7 alpha
- const purple7 = "rgba(12 12 46 / .7)";
- const gameSummary = document.querySelector("[class^=game-summary_container__");
- if (gameSummary) {
- gameSummary.style.opacity = "1";
- gameSummary.style.backgroundColor = purple9;
- }
- const header = document.querySelector("[class^=game-summary_playedRoundsHeader__");
- if (header) {
- header.style.backgroundColor = purple7;
- }
- }
- // We only want the zindex = -1 to exist in game settings, on other pages it's detrimental
- let img = document.querySelector('.customBackground');
- if (refresh) {
- img.remove();
- img = document.querySelector('.customBackground');
- }
- if (img) {
- if (!inGame) {
- img.classList = Array.from(img.classList).filter(cl => cl != 'zindex');
- }
- // Return if most recent insertion was in same area (homepage vs not)
- if (isHomePage == (location.pathname == "/")) {
- return;
- }
- img.remove();
- // Update isHomePage
- }
- if (!img) {
- img = document.createElement("img")
- img.classList.add("customBackground");
- if (inGame) {
- img.classList.add("zindex");
- } else {
- img.classList = Array.from(img.classList).filter(cl => cl != 'zindex');
- }
- }
- isHomePage = location.pathname == "/";
- if (isHomePage && homePageImgURL) {
- img.src = homePageImgURL;
- } else if (!isHomePage && otherPagesImgURL) {
- img.src = otherPagesImgURL;
- } else {
- return
- }
- el.appendChild(img);
- }
- const updateStorage = (listName, newList) => {
- GM_setValue(listName, newList);
- }
- const validate = (e, homepage) => {
- const patt = new RegExp(".*.(jpg|png|gif|jpeg|webp|svg|avif)","i");
- if (e.key == "Enter") {
- if (patt.test(e.target.value)) {
- if (homepage) {
- let homepageImages = GM_getValue("homepageImages");
- homepageImages.push(e.target.value);
- if (homepageImages.length == 1) {
- homePageImgURL = homepageImages[0];
- }
- GM_setValue("homepageImages", homepageImages);
- homePageImageList = homepageImages
- } else {
- let otherImagesNew = GM_getValue("otherImages");
- otherImagesNew.push(e.target.value);
- if (otherImagesNew.length == 1) {
- otherPagesImgURL = otherImagesNew[0];
- }
- GM_setValue("otherImages", otherImagesNew);
- otherImages = otherImagesNew;
- }
- refreshPopup();
- e.target.value = "";
- } else {
- window.alert("This link doesn't seem to be to an image file, it should end in .jpg, .jpeg, .png, .gif, .webp, .avif, or .svg");
- }
- }
- }
- const removeImage = (image, div, list, listName) => {
- let result = window.confirm("Are you sure you want to remove this image?");
- if (!result) {
- return
- }
- let i = list.indexOf(image);
- if (i != -1) {
- list.splice(i, 1);
- updateStorage(listName, list);
- refreshPopup();
- if (listName == "otherImages" && !list.includes(image)) {
- setOtherImg();
- updateImage(true);
- }
- if (listName == "homepageImages" && !list.includes(image)) {
- setHomePageImg();
- updateImage(true);
- }
- }
- };
- // displays an image in the popup
- const displayImage = (image, imagesDiv, list, listName) => {
- const img = document.createElement("img");
- const div = document.createElement("div");
- div.className = "backgroundImageWrapper";
- const container = document.createElement("div");
- container.className = "imageContainer";
- img.src = image
- img.className = "backgroundImage";
- div.appendChild(container);
- container.appendChild(img);
- const deleteIcon = document.createElement("img");
- deleteIcon.className = "deleteIcon";
- deleteIcon.src = "https://www.svgrepo.com/show/493964/delete-1.svg";
- const deleteButton = document.createElement("button");
- deleteButton.className = getStyle("quick-search_searchInputButton__") + " " + "deleteButton";
- deleteButton.appendChild(deleteIcon);
- deleteButton.addEventListener("click", e => {
- removeImage(image, div, list, listName);
- });
- const overlay = document.createElement("div");
- overlay.className = "overlay";
- const span = document.createElement("span");
- isHomePage = location.pathname == "/";
- span.innerText = ((isHomePage && listName == "homepageImages")
- || (!isHomePage && listName == "otherImages"))
- ? "Make current image" : "You're not on a page where this image will display";
- overlay.appendChild(span);
- container.appendChild(overlay);
- div.appendChild(deleteButton);
- imagesDiv.appendChild(div);
- container.addEventListener('click', function() {
- if (listName == "homepageImages") {
- setHomePageImg(image);
- }
- if (listName == "otherImages") {
- setOtherImg(image);
- }
- insertBackground(true);
- });
- }
- const refreshPopup = () => {
- if (document.querySelector("#backgroundReplacerPopupWrapper") != null && document.querySelector('[class^=header-tablet-desktop_root__]') != null) {
- let div = document.querySelector("#homePageImages");
- while (div.children.length) {
- div.removeChild(div.children[0]);
- }
- div = document.querySelector("#otherPagesImages");
- while (div.children.length) {
- div.removeChild(div.children[0]);
- }
- addPopup(true);
- const showButton = document.querySelector('#backgroundReplacerToggle');
- const popup = document.querySelector('#backgroundReplacerPopup');
- showPopup(showButton, popup);
- }
- }
- const addPopup = (refresh=false) => {
- if ((refresh || (document.querySelector('[class^=header-tablet-desktop_root__]') || document.querySelector('[class^=header-desktop_root__]')) && document.querySelector('#backgroundReplacerPopupWrapper') === null)) {
- if (!refresh) {
- let section = document.querySelector('[class^=header-tablet-desktop_desktopSectionRight__]')
- if (!section) section = document.querySelector('[class^=header-desktop_desktopSectionRight__]')
- insertHeaderGui(section, guiHTMLHeader)
- const homepageInput = document.querySelector("#homepageInput");
- homepageInput.addEventListener("keyup", e => {
- validate(e, true);
- });
- const otherpagesInput = document.querySelector("#otherpagesInput");
- otherpagesInput.addEventListener("keyup", e => {
- validate(e, false);
- });
- }
- const homePageImagesDiv = document.querySelector('#homePageImages');
- if (homePageImagesDiv) {
- // Loop through images and display them
- for (let i = 0; i < homePageImageList.length; i++) {
- displayImage(homePageImageList[i], homePageImagesDiv,homePageImageList, "homepageImages");
- }
- }
- const otherPagesImagesDiv = document.querySelector("#otherPagesImages");
- if (otherPagesImagesDiv) {
- // Loop through images and display them
- for (let i = 0; i < otherImages.length; i++) {
- displayImage(otherImages[i], otherPagesImagesDiv, otherImages, "otherImages");
- }
- }
- }
- }
- const updateImage = (refresh=false) => {
- // Don't do anything while the page is loading
- if (document.querySelector("[class^=page-loading_loading__]")) return;
- addPopup();
- insertBackground(refresh);
- extraStyling()
- }
- new MutationObserver(async (mutations) => {
- updateImage()
- }).observe(document.body, { subtree: true, childList: true });