您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Completely strips the original search engine and replaces it with a more fleshed out version where you can use filters and see more info.
// ==UserScript== // @name A better search // @namespace http://tampermonkey.net/ // @version 0.2.4 // @description Completely strips the original search engine and replaces it with a more fleshed out version where you can use filters and see more info. // @author Lemson // @match https://www.geoguessr.com/search // @match https://www.geoguessr.com/ // @icon https://www.clipartmax.com/png/full/15-150759_search-icon-search-icon-png-blue.png // @grant GM_addStyle // @require https://greasyforks.org/scripts/460322-geoguessr-styles-scan/code/Geoguessr%20Styles%20Scan.js?version=1151668 // @license MIT // @run-at document-idle // ==/UserScript== // Removes the old search, and keeps it gone, hopefully this doesnt fuck anything else up :D const observer = new MutationObserver(() => { setTimeout(() => { document.querySelectorAll('[class*="search_center__"]').forEach((element) => { if (element.parentNode) element.parentNode.remove(); }); }, 200); }); observer.observe(document.documentElement, { childList: true, subtree: true }); if (window.location.href === "https://www.geoguessr.com/") { function newStartPageCSS() { const inputCSS = ` .quicksearch-input{ background-color: rgba(0,0,0,0); border: none; padding-left: 1.3rem; color: white; } `; GM_addStyle(inputCSS); const searchButtonCSS = ` .slanted-button-container{ display: inline-block; scale: .95; transition: .2s; } .slanted-button-container:hover{ transition: .2s; } .slanted-wrapper-root{ position: relative; z-index: 0; } .slanted-wrapper_variantGrayTransparent{ } .slanted-wrapper-start{ left: 0; } .slanted-wrapper-right{ bottom: 0; overflow: hidden; position: absolute; top: 0; width: 50%; z-index: -1; } .slanted-wrapper-right:before{ transform-origin: bottom; border-radius: 0.25rem 0 0 0.25rem; transform: skewX(-12deg); left: 0; padding-right: .0625rem; width: 100%; background: var(--ds-color-black-40); bottom: 0; content: ""; position: absolute; top: 0; z-index: -1; } .slanted-button_root{ --skew-angle: -10deg; --content-skew-angle: 0; --variant-background-color: var(--ds-color-black-20); --border-radius: 0.25rem; --content-color: var(--ds-color-white); --content-padding: 0.6rem 1rem; } .slanted-button_button{ background: none; border: initial; cursor: pointer; margin: 0; min-height: 3rem; padding: 0; display: flex; flex-direction: row-reverse; align-items: center; } .slanted-button_content{ color: var(--content-color); padding: var(--content-padding); } .slanted-button_contentSizeLarge{ --content-padding: 0.6rem 1rem; } .search-button-root{ background-color: transparent; border: unset; cursor: pointer; display: flex; flex: 0 0 3rem; justify-content: center; position: relative; z-index: 1; } .slanted-wrapper-end{ left: 50%; } .slanted-wrapper_right{ bottom: 0; overflow: hidden; position: absolute; top: 0; width: 50%; z-index: -1; } .slanted-wrapper_right:before{ transform-origin: top; border-radius: 0 0.25rem 0.25rem 0; transform: skewX(-12deg); padding-left: .0625rem; right: 0; width: 100%; background: var(--ds-color-black-40); bottom: 0; content: ""; position: absolute; top: 0; z-index: -1; } `; GM_addStyle(searchButtonCSS); } const createNewSearchButton = () => { const baseHTML = ` <div class="slanted-button-container"> <div class="slanted-wrapper-root slanted-wrapper_variantGrayTransparent"> <div class="slanted-wrapper-start slanted-wrapper-right"></div> <button class="slanted-button_root slanted-button_button"> <div class="slanted-button_content slanted-button_contentSizeLarge"> <img src="https://svgur.com/i/142d.svg" alt="Search Icon"> </div> </button> <div class="slanted-wrapper-end slanted-wrapper_right"></div> </div> </div> `; const header = document.querySelector('div[class^="header_context__"]'); const diver = document.createElement("div"); diver.innerHTML = baseHTML; header.insertBefore(diver, document.querySelector(".slanted-button_container__6JmyZ")); return diver; }; const openSearch = () => { if (!searchOpen) { const input = document.createElement("div"); input.innerHTML = ` <input placeholder="Search for maps..." type="text" class="quicksearch-input"> `; document.querySelector(".slanted-button_root").append(input); searchOpen = true; } }; const createEventListeners = () => { searchButton.addEventListener("click", openSearch); document.addEventListener("keydown", function (event) { if (event.key === "Enter" && document.activeElement == document.querySelector(".quicksearch-input")) { const input = document.querySelector(".quicksearch-input"); localStorage.setItem("searchTerm", input.value); window.location.href = "https://www.geoguessr.com/search"; } }); }; let searchOpen = false; const searchButton = createNewSearchButton(); newStartPageCSS(); createEventListeners(); } //Search page \/ if (window.location.href === "https://www.geoguessr.com/search") { function newSearchPageCSS() { const CSS = ` .main-search-div{ height: 100%; } .search-page-main{ width: 100vw; height: 100%; display: flex; align-items: center; justify-content: center; flex-direction: column; } .input-main-container{ width: 100vw; height: 100%; display: flex; justify-content: center; align-items: center; } .search-container{ width: 40%; display: flex; justify-content: center; align-items: center; } .search-input{ width: 100%; background-color: rgb(255 255 255 / 5%); border: 1px solid black; border-radius: 10rem; color: white; font-size: 1.2rem; padding-left: 1.5rem; } .search-results{ } .search-item{ display: flex; flex-direction: row; justify-content: center; gap: 1rem; margin-top: 2rem; padding-bottom: 1rem; border-bottom: 1px solid rgba(0,0,0,0.5); transition: .2s; } .search-item:hover{ transition: .2s; scale: 1.01; } .author-map-name{ width: 20rem; } .map-name{ font-size:1.5rem; } .map-avatar{ width: 4rem; border-radius: .5rem; } .stat-view{ width: 5rem; margin-right: 4rem; display:flex; align-items: center; justify-content: center; flex-direction: column; gap: .2rem; } .dropdown{ width: 40%; display: flex; justify-content: center; align-items: center; flex-direction: column; } .filter-btn { color: white; font-size: 1rem; border: none; cursor: pointer; } .filter-window{ width: 100%; display: flex; justify-content: space-around; } .filter-category-container{ display: flex; flex-direction: column; align-items: center; margin-top: 1rem; margin-bottom: .5rem; } .min-input{ background-color: rgba(0,0,0,0.1); border: 1px solid #6b6b6b; border-radius: .7rem; color: white; width: 8rem; margin-top: .1rem; } .hide{ display: none; } .official-toggle-buttons>*{ color: white; border: 1px solid white; padding: 1rem; padding-top: .4rem; padding-bottom: .4rem; } .selectionmode-toggle-buttons>*{ color: white; border: 1px solid white; padding: 1rem; padding-top: .4rem; padding-bottom: .4rem; } .active-button{ background-color: #563b9a; } .apply-button{ width: 5rem; height: 2.5rem; background-color: transparent; margin-top: 10%; color: white; border: 1px solid white; border-radius: 2rem; margin-left: 35%; } .apply-button:active{ transition: .05s; background-color: rgba(255,255,255,0.2); } `; GM_addStyle(CSS); } const createNewSearchbar = () => { const searchHTML = ` <div class="search-page-main"> <div class="search-container"> <input class="search-input" type="text" placeholder="Search for maps or players..."> </div> <div class="dropdown"> <button class="filter-btn">Filters</button> <div class="filter-window"> <div> <div class="filter-category-container"> <p>Minimum likes</p> <input id="min-likes" class="min-input" type="number" value=${localStorage.getItem("minLikes")}> </div> <div class="filter-category-container"> <p>Minimum locations</p> <input id="min-locs" class="min-input" type="number" value=${localStorage.getItem("minLocs")}> </div> <div class="filter-category-container"> <p>Minimum games played</p> <input id="min-games-played" class="min-input" type="number" value=${localStorage.getItem( "minGamesPlayed" )}> </div> <div class="filter-category-container"> <p>Minimum average score</p> <input id="min-avg-score" class="min-input" type="number" value=${localStorage.getItem("minAvgScore")}> </div> </div> <div> <div class="filter-category-container"> <p>Official</p> <div class="official-toggle-buttons"> <button id="official">Yes</button> <button id="both" class="active-button">All</button> <button id="unofficial">No</button> </div> </div> <div class="filter-category-container"> <p>Selection mode</p> <div class="selectionmode-toggle-buttons"> <button id="handpicked">Handpicked</button> <button id="both" class="active-button">All</button> <button id="polygonal">Polygonal</button> </div> </div> <button class="apply-button">Apply</button> </div> </div> </div> </div> `; const mainDiv = document.querySelector("main"); const dave = document.createElement("div"); dave.classList.add("main-search-div"); dave.innerHTML = searchHTML; mainDiv.append(dave); }; const createResultsFromSearch = async () => { const results = await getResults(localStorage.getItem("searchTerm")); const existingResultsContainer = document.querySelector(".search-results"); if (existingResultsContainer) { existingResultsContainer.remove(); } const div = document.createElement("div"); div.innerHTML = ""; div.classList.add("search-results"); document.querySelector(".search-page-main").append(div); console.log(results); results.forEach((a) => { const html = ` <img class="map-avatar" src="https://avatar.map-making.app/${a.id}"> <div class="author-map-name"> <a href="/maps/${a.id}" target="_" class="map-name">${a.name}</a> <p class="creator-name">Created by: <a href="/user/${a.creatorId}">${a.creator}</a></p> </div> <div class="stat-view likes"> <img style="width: 1.5rem;" src="_next/static/media/like-32.1321332a.svg" title="Likes"> ${a.likes} </div> <div class="stat-view locs"> <img style="width: 1.5rem;" src="_next/static/media/location-32.73fdcf3f.svg" title="Number of locations"> ${a.coordinateCount} </div> <div class="stat-view games"> <img style="width: 1.5rem;" src="_next/static/media/people-32.6e1cc43b.svg" title="Games played"> ${a.numberOfGamesPlayed} </div> <div class="stat-view avgScore"> <img style="width: 2rem;" src="https://i.imgur.com/uRdcYBM.png" title="Average score"> ${a.averageScore} </div> <div class="stat-view howitwascreated"> ${a.locationSelectionMode === 1 ? "Handpicked" : a.locationSelectionMode === 2 ? "Polygonal" : "Official"} </div> `; const resultContainer = document.createElement("div"); resultContainer.classList.add("search-item"); resultContainer.innerHTML = html; div.append(resultContainer); }); }; async function getResults(word) { let mapSearch = await fetch(`https://www.geoguessr.com/api/v3/search/map?page=0&count=50&q=${word}`); if (!mapSearch) { console.log("bad response"); } let data = await mapSearch.json(); let moreData = await getAdditionalData(data); const combinedData = []; const minLength = Math.min(data.length, moreData.length); for (let i = 0; i < minLength; i++) { combinedData.push({ ...moreData[i], ...data[i] }); } let filteredData = await applyFilters(combinedData); return filteredData; } const getAdditionalData = async (data) => { const promises = data.map((map) => fetch(`https://www.geoguessr.com/api/maps/${map.id}`)); const responses = await Promise.all(promises); const extraMapData = await Promise.all( responses.map(async (resp) => { try { return await resp.json(); } catch (error) { console.error( "Something went wrong when looking at the map data:", error, "(You can most likely ignore this message)" ); return null; } }) ); return extraMapData.filter((data) => data !== null); }; const openFilters = () => { console.log("open filters"); }; function applyFilters(data) { let filteredData = data; const minLikes = document.getElementById("min-likes").value; const minLocs = document.getElementById("min-locs").value; const minGamesPlayed = document.getElementById("min-games-played").value; const minAvgScore = document.getElementById("min-avg-score").value; //Filter min likes filteredData = filteredData.filter((item) => { return item.likes >= minLikes; }); //Filter min locs filteredData = filteredData.filter((item) => { return item.coordinateCount >= minLocs; }); //Filter min games played filteredData = filteredData.filter((item) => { return item.numberOfGamesPlayed >= minGamesPlayed; }); //Filter min likes filteredData = filteredData.filter((item) => { return item.averageScore >= minAvgScore; }); //Filter official or not switch (localStorage.getItem("officialSetting")) { case "unofficial": filteredData = filteredData.filter((item) => item.isUserMap); break; case "official": filteredData = filteredData.filter((item) => !item.isUserMap); break; } //Filter selectionMode switch (localStorage.getItem("selectionSetting")) { case "handpicked": filteredData = filteredData.filter((item) => item.locationSelectionMode == 1); break; case "polygonal": filteredData = filteredData.filter((item) => !item.isUserMap == 0); break; } //Save the selected filters localStorage.setItem("minLikes", minLikes); localStorage.setItem("minLocs", minLocs); localStorage.setItem("minGamesPlayed", minGamesPlayed); localStorage.setItem("minAvgScore", minAvgScore); return filteredData; } createResultsFromSearch(); newSearchPageCSS(); createNewSearchbar(); document.querySelector(".search-input").addEventListener("keydown", function (event) { if (event.key === "Enter") { localStorage.setItem("searchTerm", document.querySelector(".search-input").value); createResultsFromSearch(localStorage.getItem("searchTerm")); } }); const officalSelectionBtns = document.querySelectorAll(".official-toggle-buttons > button"); officalSelectionBtns.forEach((button) => { button.addEventListener("click", () => { officalSelectionBtns.forEach((a) => { a.classList.remove("active-button"); }); button.classList.add("active-button"); let officialSetting = button.id.toString(); localStorage.setItem("officialSetting", officialSetting); }); }); const selectionModeSelectionBtns = document.querySelectorAll(".selectionmode-toggle-buttons > button"); selectionModeSelectionBtns.forEach((button) => { button.addEventListener("click", () => { selectionModeSelectionBtns.forEach((a) => { a.classList.remove("active-button"); }); button.classList.add("active-button"); let selectionSetting = button.id.toString(); localStorage.setItem("selectionSetting", selectionSetting); }); }); const applyFiltersBtn = document.querySelector(".apply-button"); applyFiltersBtn.addEventListener("click", () => createResultsFromSearch(localStorage.getItem("searchTerm"))); }