您需要先安装一个扩展,例如 篡改猴、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.1 // @author Tyow#3742 // @match *://*.geoguessr.com/* // @license MIT // @require https://unpkg.com/@popperjs/[email protected]/dist/umd/popper.min.js // @namespace https://greasyfork.org/users/1011193 // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @require https://greasyfork.org/scripts/460322-geoguessr-styles-scan/code/Geoguessr%20Styles%20Scan.js?version=1151668 // ==/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="backgroundReplacerSlantedRoot"> <div id="backgroundReplacerSlantedStart"></div> <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: 60%;"></picture></button> </div> <div id="backgroundReplacerSlantedEnd"></div> </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%; } .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; } `; 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; console.log(Date.now()); } }; // 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 = [ "header_item__", "quick-search_wrapper__", "slanted-wrapper_root__", "slanted-wrapper_variantGrayTransparent__", "slanted-wrapper_start__", "quick-search_searchInputWrapper__", "slanted-wrapper_end__", "slanted-wrapper_right__", "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("#backgroundReplacerPopupWrapper").className = getStyle("header_item__"); document.querySelector("#backgroundReplacerSearchWrapper").className = getStyle("quick-search_wrapper__"); document.querySelector("#backgroundReplacerSlantedRoot").className = getStyle("slanted-wrapper_root__") + " " + getStyle("slanted-wrapper_variantGrayTransparent__"); document.querySelector("#backgroundReplacerSlantedStart").className = getStyle("slanted-wrapper_start__")+ " " + getStyle("slanted-wrapper_right__"); document.querySelector("#backgroundReplacerInputWrapper").className = getStyle("quick-search_searchInputWrapper__"); document.querySelector("#backgroundReplacerSlantedEnd").className = getStyle("slanted-wrapper_end__")+ " " + getStyle("slanted-wrapper_right__"); 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'; } if (document.querySelector('#enableScriptHeader')) { if (localStorage.getItem('blinkEnabled') === 'enabled') { document.querySelector('#enableScriptHeader').checked = true; } } }); } // Global to track whether the most recent image insertion was done on homepage let isHomePage = location.pathname == "/"; 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('[class^=header_header__]') && document.querySelector('#backgroundReplacerPopupWrapper')) { 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_header__]') && document.querySelector('#backgroundReplacerPopupWrapper') === null)) { if (!refresh) { insertHeaderGui(document.querySelector('[class^=header_context__]'), 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); } new MutationObserver(async (mutations) => { updateImage() }).observe(document.body, { subtree: true, childList: true });