// ==UserScript==
// @name Luogu Search AnyWhere
// @version 0.2
// @description Search AnyWhere in Luogu!
// @author tiger2005
// @match https://www.luogu.com.cn/*
// @icon https://cdn.luogu.com.cn/upload/usericon/3.png
// @grant none
// @license MIT
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @namespace https://greasyfork.org/users/829530
// ==/UserScript==
(function() {
'use strict';
var addedContent = false;
let majorInterval = setInterval(function(){
if($(".user-nav .search-wrap").length == 0)
return;
// clearInterval(majorInterval);
var sicon = $(".user-nav .search-wrap").next();
$(".user-nav .search-wrap").remove();
sicon.css("margin-left", "10px");
if(! addedContent){
$("body").append(`
<style>
.searchAnywhere{
position: fixed;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
background-color: rgba(0, 0, 0, 0.8);
z-index: 999;
transition: 0.2s;
color: white;
}
.searchAnywhereMain{
height: 600px;
width: 750px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
}
.inputArea{
display: block;
width: 100%;
height: 48px;
color: #aaa;
position: relative;
transition: 0.2s;
margin-bottom: 10px;
}
.inputArea > input{
border-radius: 5px;
border: 2px solid #aaa;
height: 48px;
width: 100%;
font-size: 18px;
color: #aaa;
padding: 14px 24px;
outline: 0;
background: transparent;
box-sizing: border-box;
}
.inputArea.onHover > input, .inputArea.onFocus > input{
border: 2px solid white;
}
.inputArea.onHover, .inputArea.onFocus, .inputArea.withContent{
color: white !important;
}
.inputArea.onHover > input, .inputArea.onFocus > input, .inputArea.withContent > input{
color: white !important;
}
.inputArea.withIconLeft > input{
padding-left: 42px;
}
.inputArea.withIconRight > input{
padding-right: 42px;
}
.inputArea > div.iconLeft{
height: 48px;
width: 48px;
position: absolute;
display: grid;
place-items: center;
top: 0px;
left: 0px;
}
.inputArea > div.iconLeft > svg{
width: 20px !important;
height: 20px !important;
}
.userPurple{
color: #cf5bff;
font-weight: bold;
}
.userRed{
color: #e74c3c;
font-weight: bold;
}
.userOrange{
color: #e67e22;
font-weight: bold;
}
.userYellow{
color: #d9a71d;
font-weight: bold;
}
.userGreen{
color: #5eb95e;
font-weight: bold;
}
.userGray{
color: #aaa;
font-weight: bold;
}
.userCheater{
color: #d3961c;
font-weight: bold;
}
.userBlue{
color: #07a2f1;
font-weight: bold;
}
.userGold{
color: #f1c40f;
font-weight: bold;
}
.badgePurple{
background-color: #cf5bff;
}
.badgeRed{
background-color: #e74c3c;
}
.badgeOrange{
background-color: #e67e22;
}
.badgeYellow{
background-color: #d9a71d;
}
.badgeGreen{
background-color: #5eb95e;
}
.badgeGray{
background-color: #999;
}
.badgeCheater{
background-color: #d3961c;
}
.badgeBlue{
background-color: #07a2f1;
}
.badgeBlack{
background-color: #0e1d69;
}
.badgeGold{
background-color: #f1c40f;
}
.searchAnywhereContent{
color: white;
flex: 1;
scrollbar-width: none;
-ms-overflow-style: none;
overflow-x: hidden;
overflow-y: auto;
}
.searchAnywhereContent::-webkit-scrollbar { width: 0 !important; }
.searchUserCard{
background: #444;
border-radius: 10px;
display: flex;
flex-direction: column;
cursor: pointer;
color: white;
padding: 10px;
line-height: 1;
margin-bottom: 10px;
border: 2px solid #888;
box-sizing: border-box;
}
.searchUserCard:hover{
border: 2px solid white;
}
.searchUserCard > div{
width: 100%;
}
.searchUserCardBody{
display: flex;
flex-direction: row;
}
.searchUserCardImg{
height: 36px;
width: 36px;
border-radius: 50%;
margin-right: 10px;
}
.searchUserCardInfo > span:first-child{
font-size: 14px;
margin-bottom: 3px;
display: inline-block;
color: #bbb;
}
.searchUserCardInfo > span:last-child{
font-size: 20px;
}
.searchUserCardMedia{
display: flex;
flex-direction: row;
}
.searchUserCardMedia > div{
margin-top: 5px;
flex: 1;
display: inline-block;
height: 23px;
verticle-align: center;
}
.searchUserCardMedia > div > div{
padding: 4px;
position: relative;
display: inline-block;
background: #777;
margin-right: 15px;
}
.searchUserCardMedia > div > div:after{
width: 10px;
height: 100%;
content: "";
border: 5px;
position: absolute;
top: 0px;
left: 100%;
clip-path: polygon(0 0,100% 50%,0 100%);
background-color: inherit;
}
.userBadgeInfo{
font-size: 14px;
padding: 2px 5px;
border-radius: 5px;
color: white;
font-weight: bold;
margin: 0px 3px;
display: inline-block;
}
.searchProblemCard{
background: #444;
border-radius: 10px;
display: flex;
flex-direction: column;
cursor: pointer;
color: white;
padding: 10px;
line-height: 1;
margin-bottom: 10px;
border: 2px solid #888;
box-sizing: border-box;
}
.searchProblemCard:hover{
border: 2px solid white;
}
.searchProblemCard > div{
width: 100%;
display: flex;
flex-direction: row;
}
.searchProblemCard > div:last-child{
margin-top: 5px;
}
.searchProblemCardTag{
margin-right: 12px;
}
.searchProblemCardTag > div{
padding: 4px;
position: relative;
display: inline-block;
background: #777;
margin-right: 15px;
}
.searchProblemCardTag > div:after{
width: 10px;
height: 100%;
content: "";
border: 5px;
position: absolute;
top: 0px;
left: 100%;
clip-path: polygon(0 0,100% 50%,0 100%);
background-color: inherit;
}
.problemTagInfo{
font-size: 16px;
padding: 4px 7px;
border-radius: 5px;
color: white;
display: inline-block;
}
.searchProblemCardBody > div:first-child{
margin-right: 5px;
}
</style>
<div class='searchAnywhere' style="opacity: 0; display: none;">
<div class='searchAnywhereMain'>
<div class='inputArea withIconLeft'>
<input spellcheck="false" placeholder="Search AnyWhere"/>
<div class="iconLeft"><svg data-v-1ad550c8="" data-v-303bbf52="" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-search fa-w-24"><path data-v-1ad550c8="" data-v-303bbf52="" fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z" class=""></path></svg></div>
</div>
<div class='searchAnywhereContent'>
</div>
</div>
</div>
`);
addedContent = true;
$(".inputArea > input").focus(function(){
$(this).parent().addClass("onFocus");
});
$(".inputArea > input").blur(function(){
$(this).parent().removeClass("onFocus");
if($(this).val().length != 0)
$(this).parent().addClass("withContent");
else
$(this).parent().removeClass("withContent");
});
$(".inputArea").mouseenter(function(){
$(this).addClass("onHover");
});
$(".inputArea").mouseleave(function(){
$(this).removeClass("onHover");
});
}
const problemColors = [ "Gray", "Red", "Orange", "Yellow", "Green", "Blue", "Purple", "Black" ];
const problemNames = [ "暂无评定", "入门", "普及-", "普及/提高-", "普及+/提高", "提高+/省选-", "省选/NOI-", "NOI/NOI+/CTSC" ];
var searchTimeout = null;
const searchInfo = () => {
searchTimeout = null;
var info = $(".inputArea > input").val();
info = $.trim(info);
if(info == ""){
$(".searchAnywhereContent").html("");
return;
}
$(".searchAnywhereContent").html(`<div><div style='text-align: center; margin-bottom: 10px; width: 100%; font-size: 20px;'>加载中……</div></div>`);
$(".searchAnywhereContent > div").unbind('click').click((event) => {
event.stopPropagation();
})
var userHtml = "";
var problemHtml = "";
var finishWorks = 0;
const finishWork = () => {
++ finishWorks;
if(finishWorks == 2){
if(userHtml == "" && problemHtml == "")
$(".searchAnywhereContent").html(`<div><div style='text-align: center; margin-bottom: 10px; width: 100%; font-size: 20px;'>未搜索到相关内容</div></div>`);
else{
$(".searchAnywhereContent").html(`<div>` + userHtml + problemHtml + `</div>`);
$(".searchShowProblems").unbind("click").click(function(event){
window.open(`https://www.luogu.com.cn/problem/list?keyword=${info}&page=1&type=P%7CB%7CCF%7CSP%7CAT%7CUVA`, "_blank");
});
$(".searchUserCard").unbind("click").click(function(event){
window.open(`https://www.luogu.com.cn/user/${$(this).attr("uid")}`, "_blank");
})
$(".searchProblemCard").unbind("click").click(function(event){
window.open(`https://www.luogu.com.cn/problem/${$(this).attr("pid")}`, "_blank");
})
$(".searchAnywhereContent > div").unbind('click').click((event) => {
event.stopPropagation();
})
}
}
};
const getProblemStatus = (x, y) => {
if(!x && !y)
return `<svg data-v-1b44b3e6="" data-v-c06fccc2="" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="minus" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="icon svg-inline--fa fa-minus fa-w-14" data-v-303bbf52="" style="width: 16px; height: 16px; color: #aaa"><path data-v-1b44b3e6="" fill="currentColor" d="M416 208H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h384c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z" class=""></path></svg>`;
if(!y)
return `<svg data-v-1b44b3e6="" data-v-c06fccc2="" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="times" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 352 512" class="icon svg-inline--fa fa-times fa-w-11" data-v-303bbf52="" style="transform: scale(1.2); width: 16px; height: 16px; color: rgb(231, 76, 60);"><path data-v-1b44b3e6="" fill="currentColor" d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z" class=""></path></svg>`;
return `<svg data-v-1b44b3e6="" data-v-c06fccc2="" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="check" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="icon svg-inline--fa fa-check fa-w-16" data-v-303bbf52="" style="width: 16px; height: 16px; color: rgb(82, 196, 26);"><path data-v-1b44b3e6="" fill="currentColor" d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z" class=""></path></svg>`;
}
const getCCFLevel = (x) => {
if(x == null || x < 3)
return "";
var color = "";
if(x <= 5)
color = "#5eb95e";
else if(x <= 7)
color = "#07a2f1";
else
color = "#f1c40f";
return `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 16 16" style="margin: 0px 3px;" fill="${color}" style="margin-bottom: -3px;"><path d="M16 8C16 6.84375 15.25 5.84375 14.1875 5.4375C14.6562 4.4375 14.4688 3.1875 13.6562 2.34375C12.8125 1.53125 11.5625 1.34375 10.5625 1.8125C10.1562 0.75 9.15625 0 8 0C6.8125 0 5.8125 0.75 5.40625 1.8125C4.40625 1.34375 3.15625 1.53125 2.34375 2.34375C1.5 3.1875 1.3125 4.4375 1.78125 5.4375C0.71875 5.84375 0 6.84375 0 8C0 9.1875 0.71875 10.1875 1.78125 10.5938C1.3125 11.5938 1.5 12.8438 2.34375 13.6562C3.15625 14.5 4.40625 14.6875 5.40625 14.2188C5.8125 15.2812 6.8125 16 8 16C9.15625 16 10.1562 15.2812 10.5625 14.2188C11.5938 14.6875 12.8125 14.5 13.6562 13.6562C14.4688 12.8438 14.6562 11.5938 14.1875 10.5938C15.25 10.1875 16 9.1875 16 8ZM11.4688 6.625L7.375 10.6875C7.21875 10.8438 7 10.8125 6.875 10.6875L4.5 8.3125C4.375 8.1875 4.375 7.96875 4.5 7.8125L5.3125 7C5.46875 6.875 5.6875 6.875 5.8125 7.03125L7.125 8.34375L10.1562 5.34375C10.3125 5.1875 10.5312 5.1875 10.6562 5.34375L11.4688 6.15625C11.5938 6.28125 11.5938 6.5 11.4688 6.625Z"></path></svg>`
}
$.ajax({
url: `/api/user/search?keyword=${info}`,
type: 'GET',
success: function(json){
json = json.users;
if(json.length != 0 && json[0] != null){
userHtml = `
<div style='text-align: left; margin-bottom: 10px; width: 100%; font-size: 18px; font-weight: bold'>用户</div>
`;
json.forEach((item) => {
if(item == null)
return;
if(item.color == "Cheater")
item.badge = "作弊者";
userHtml += `
<div class="searchUserCard" uid=${item.uid}>
<div class="searchUserCardBody">
<div class="searchUserCardImg" style="background: url(https://cdn.luogu.com.cn/upload/usericon/${item.uid}.png); background-size: 36px 36px;"></div>
<div class="searchUserCardInfo"><span>UID ${item.uid}</span><br/><div style='display: flex; flex-direction: row'><span class="user${item.color}" style="line-height: 20px">${item.name}</span>${getCCFLevel(item.ccfLevel)}${item.badge != null && item.badge != "" ? `<div class='userBadgeInfo badge${item.color}'>${item.badge}</div>` : ""}</div></div>
</div>
</div>`
});
}
finishWork();
}
});
$.ajax({
url: `/problem/list`,
type: 'GET',
headers: {"x-luogu-type": "content-only"},
data: {
keyword: info,
page: 1,
type: "P|B|CF|SP|AT|UVA"
},
success: function(json){
json = json.currentData.problems;
if(json.count != 0){
problemHtml = `
<div style='text-align: left; margin-bottom: 10px; width: 100%; font-size: 18px; font-weight: bold'>题目<div style="cursor: pointer; float: right; font-weight: normal !important" class="searchShowProblems">查看所有 ${json.count} 道题目</div></div>
`;
for(var i=0; i<json.result.length; i++){
let item = json.result[i];
problemHtml += `
<div class="searchProblemCard" pid=${item.pid}>
<div class="searchProblemCardBody">
<div>${getProblemStatus(item.submitted, item.accepted)}</div>
<div>${item.title}</div>
</div>
<div>
<div class='searchProblemCardTag'><div>题号</div>${item.pid}</div>
<div class='searchProblemCardTag'><div>尝试</div>${item.totalSubmit}</div>
<div class='searchProblemCardTag'><div>通过</div>${item.totalAccepted}</div>
<div style='flex: 1; text-align: right'>
<div class="problemTagInfo badge${problemColors[item.difficulty]}">${problemNames[item.difficulty]}</div>
</div>
</div>
</div>
`;
};
}
finishWork();
}
});
};
$(".inputArea > input").unbind('input propertychange').on('input propertychange', function(){
if(searchTimeout != null)
clearTimeout(searchTimeout);
searchTimeout = setTimeout(searchInfo, 500);
});
let searchAnywhereOpen = false;
sicon.unbind('click').click(function(){
if(! searchAnywhereOpen){
$(".searchAnywhere").css("display", "block");
setTimeout(() => {
$(".searchAnywhere").css("opacity", "1");
}, 20);
}
else{
$(".searchAnywhere").css("opacity", "0");
setTimeout(() => {
$(".searchAnywhere").css("display", "none");
}, 200);
}
searchAnywhereOpen = !searchAnywhereOpen;
});
$(".searchAnywhere").unbind('click').click(() => {
$(".searchAnywhere").css("opacity", "0");
setTimeout(() => {
$(".searchAnywhere").css("display", "none");
}, 200);
searchAnywhereOpen = false;
})
$(".inputArea").unbind('click').click((event) => {
event.stopPropagation();
})
}, 500);
})();