// ==UserScript==
// @name 筛选ABDC
// @namespace http://tampermonkey.net/
// @version 2025-08-31-004
// @description 小工具
// @author 周利斌
// @match https://so1.imageoss.com/*
// @match https://so3.cljtscd.com/scholar*
// @match https://scispace.com/*
// @match https://scholar.google.com/*
// @match https://*.webofscience.com/*
// @match https://*.scopus.com/*
// @match *://*/*
// @include *webofscience*
// @include *www-scopus-com*
// @icon https://www.google.com/s2/favicons?sz=64&domain=scholar.google.com
// @grant GM_setValue
// @grant GM_getValue
// @license MIT
// ==/UserScript==
(function () {
"use strict";
// 更新网址 https://greasyfork.org/zh-CN/scripts/533214-%E7%AD%9B%E9%80%89abdc
// 更新网址 https://scriptcat.org/zh-CN/script-show-page/3808
const btnStyle = "padding:5px;min-width: auto;"
function appendTo(parentEle, tagName = "a", attrs = {}, functions = {}, id = "") {
attrs = typeof attrs !== "string" ? attrs : { textContent: attrs };
functions = typeof functions !== "function" ? functions : { click: functions };
if (id && document.getElementById(id)) return document.getElementById(id)
const ele = document.createElement(tagName);
if (id) ele.id = id;
for (const key in attrs) { ele[key] = attrs[key]; ele.setAttribute(key, attrs[key]); }
for (const key in functions) ele.addEventListener(key, functions[key]);
if (parentEle) parentEle.appendChild(ele);
return ele;
}
function waitUtilAsync(callback, interval = 1000, timeout = 10000) {
return new Promise((resolve, ) => {
const start = Date.now();
const intervalId = setInterval(() => {
const d = callback();
if (d) {
clearInterval(intervalId);
resolve(d);
} else if (Date.now() - start > timeout) {
clearInterval(intervalId);
resolve(false);
}
}, interval);
});
}
function runOnce(func, time = 100) {
let locked = false;
return function () {
if (locked) return;
locked = true;
setTimeout(() => {
func();
locked = false;
}, time);
};
}
function getRootDiv() {
const existingDiv = document.getElementById('z_root_div');
if (existingDiv) {
return existingDiv;
}
const rootDiv = document.createElement("div")
rootDiv.id = "z_root_div"
rootDiv.style = `
position: fixed;
z-index:999999;
background-color: rgba(198, 198, 198, 0.7);
cursor: move;
border:1px solid rgba(191, 70, 173, 0.7);
max-width: 50vw;
left: 50%;
top: 50%;
user-select:none !important;
`;
rootDiv.classList.add("notranslate");
rootDiv.setAttribute("translate", 'no');
document.body.appendChild(rootDiv);
if (window.GM_getValue&&typeof GM_getValue !== "undefined") {
let savedLeftPercent = GM_getValue('rootDivLeftPercent', 50); // 默认50%
let savedTopPercent = GM_getValue('rootDivTopPercent', 50);
if(savedTopPercent>100||savedTopPercent<0) savedTopPercent = 10
if(savedLeftPercent>100||savedLeftPercent<0) savedLeftPercent = 10
rootDiv.style.left = savedLeftPercent + '%';
rootDiv.style.top = savedTopPercent + '%';
}
let isDragging = false;
let offsetX, offsetY;
rootDiv.addEventListener('mousedown', (e) => {
isDragging = true;
// 暂时去掉transform以便计算精确位置
offsetX = e.clientX - rootDiv.getBoundingClientRect().left;
offsetY = e.clientY - rootDiv.getBoundingClientRect().top;
});
document.addEventListener('mousemove', (e) => {
if (isDragging) {
const newLeftPx = e.clientX - offsetX;
const newTopPx = e.clientY - offsetY;
rootDiv.style.left = newLeftPx + 'px';
rootDiv.style.top = newTopPx + 'px';
}
});
document.addEventListener('mouseup', () => {
if (isDragging) {
isDragging = false;
if ((rootDiv.style.left + "").indexOf("%") == -1) {
const leftPx = parseFloat(rootDiv.style.left);
const topPx = parseFloat(rootDiv.style.top);
let leftPercent = leftPx / document.documentElement.clientWidth * 100;
let topPercent = topPx / document.documentElement.clientHeight * 100;
leftPercent = Math.min(Math.max(leftPercent, 0), 100);
topPercent = Math.min(Math.max(topPercent, 0), 100);
rootDiv.style.left = leftPercent + '%';
rootDiv.style.top = topPercent + '%';
if (typeof GM_setValue !== "undefined") {
GM_setValue('rootDivLeftPercent', leftPercent);
GM_setValue('rootDivTopPercent', topPercent);
}
}
}
});
setTimeout(() => {
if (rootDiv.childElementCount == 0) rootDiv.remove()
}, 2000)
return rootDiv;
}
// Your code here...
waitUtilAsync(() => document.querySelectorAll(".gs_a") > 0, 100).then(() => {
document.querySelectorAll(".gs_a").forEach(f => {
f.classList.add("notranslate");
f.setAttribute("translate", 'no');
})
})
async function filterRank(selector = ".gs_r.gs_or.gs_scl", checkSelector = "input[type=checkbox]") {
if(window.filterRankRegisted){
return;
}
if (!(await waitUtilAsync(() => document.querySelector(selector)))) {
console.log("未找到", selector); setTimeout(()=>filterRank(selector,checkSelector),1000); return; }
console.log("找到", selector);
if(window.filterRankRegisted){
return;
}else{
window.filterRankRegisted =1
}
if (!window.rankInfoObserver) {
console.log("注册 rankInfoObserver")
window.rankInfoObserver =
new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1 && (node.classList.contains('srankSpan')||node.classList.contains('srankDiv'))) {
console.log(`.srankdiv 的数量: ${document.querySelectorAll('.srankSpan,.srankDiv').length} 开启查询`);
queryFilterRunOnce()
}
});
});
});
window.rankInfoObserver.observe(document.body, { childList: true, subtree: true });
console.log("注册 rankInfoObserver 成功",window.rankInfoObserver)
}
const divStat = appendTo(getRootDiv(), "span", "统计信息")
String.prototype.asIF = function () { return parseFloat(this.match(/([\d.]+)$/g)?.[0]) || 0 }
const RankFilter = {}
function checkFunc(rks) {
let hit=1
if (RankFilter.ALL && RankFilter.ALL[1] == 0) {
hit+=1 //
for (const k of Object.keys(RankFilter)) {
if (k != "ALL") {
const [checkFun, q1] = RankFilter[k]
if (q1 == 0) continue //无效条件
const check = checkFun(rks)
if (q1 == 1 && !check) return 0 // 如果未能满足条件 排除
if (q1 == 2 && check) return 0 // 排除所有满足条件的
hit+=1
}
}
}
return hit
//返回值
//0 有条件不符合要求被排除
//1 选中了All,等于不进行查询直接返回所有结果
//2 没选中All,但是也没返回0,也就是当前条件所有条件都是无效条件。
//大于2 至少有一个条件满足了
}
const queryFilterRunOnce = runOnce(queryFilter, 500)
function queryFilter() {
console.log(Object.entries(RankFilter).flat().flat())
const qList = document.querySelectorAll(selector)
let check = 0, checked = 0;
for (const row of qList) {
if (!row.querySelector(".srankInfo")) continue; //对没有等级的不处理
const rks = [...row.querySelectorAll(".srankInfo")].map(a => a.textContent || "")
const ck = checkSelector && row.querySelector(checkSelector)
const ck_result = checkFunc(rks)
// console.log(ck,ckr)
if (ck) { // wos 不隐藏未选中的
if (ck_result>1) {
row.children[0].style.backgroundColor =
getComputedStyle(row.querySelector(".srankInfo")).backgroundColor;
if (ck.checked) {
checked+=1;
} else {
ck.click();
check+=1;
console.log("点击了",check)
}
}
console.log(check,checked)
}
else {//scholar 未选中隐藏
row.style.transition = "border-left .5s, max-height .5s, overflow-y .5s, padding .5s";
if (ck_result) {
if (row.tagName == "TR") {
row.style.display = ""
} else {
row.style.borderLeft = ""
row.style.maxHeight = "";
row.style.overflowY = ""
row.style.padding = ""
}
check++
}
else {
if (row.tagName == "TR") {
row.style.display = "none"
} else {
row.style.borderLeft = "5px solid #300"
row.style.maxHeight = "1px"
row.style.overflowY = "hidden"
row.style.padding = "0px"
}
checked++;
}
}
}
//统计信息
stat_info(qList, check, checked);
}
function createRankFilterBtn(btnTxt = "", checkFunc = () => false) {
const btn = appendTo(getRootDiv(), "button", { type: "button", style: btnStyle, textContent: btnTxt }, () => {
let q1 = RankFilter[btnTxt][1]
q1 = (q1 + 1) % (btnTxt == "ALL" ? 2 : 3);
GM_setValue("RankFilter" + btnTxt, q1)
update_btn(btn, q1)
queryFilterRunOnce()
})
update_btn(btn, window.GM_getValue&&typeof GM_getValue !== "undefined"&&GM_getValue("RankFilter" + btnTxt) * 1 || 0)
function update_btn(btn, q1) {
btn.style.background = [
"rgba(255, 255, 255, 0.7)",
"rgba(246, 163, 234, 0.7)",
"rgba(152, 198, 248, 0.7)"][q1]
const pp = ["", "++", "--"][q1]
btn.textContent = `${pp}${btnTxt}${pp}`;
RankFilter[btnTxt] = [checkFunc, q1];
}
}
createRankFilterBtn("ALL", () => true);
createRankFilterBtn("ABDC A*/A", ms => ms.some(m => m.includes("ABDC A")));
createRankFilterBtn("ABDC B", ms => ms.some(m => m.includes("ABDC B")));
createRankFilterBtn("ABDC", ms => ms.some(m => m.includes("ABDC")));
createRankFilterBtn("FMS", ms => ms.some(m => m.includes("FMS")));
createRankFilterBtn("SSCI", ms => ms.some(m => m.includes("SSCI")));
createRankFilterBtn("SCIE", ms => ms.some(m => m.includes("SCIE")));
createRankFilterBtn("ESCI", ms => ms.some(m => m.includes("ESCI")));
createRankFilterBtn("EI", ms => ms.some(m => m.includes("EI")));
createRankFilterBtn("IF>1", ms => ms.some(m => m.startsWith("IF") && m.asIF() > 1));
createRankFilterBtn("IF>2", ms => ms.some(m => m.startsWith("IF") && m.asIF() > 2));
createRankFilterBtn("IF>8", ms => ms.some(m => m.startsWith("IF") && m.asIF() > 8));
createRankFilterBtn("CiteScore", ms => ms.some(m => m.includes("CiteScore")));
createRankFilterBtn("CiteScore>12", ms => ms.some(m => m.includes("CiteScore") && m.asIF() > 12));
createRankFilterBtn("JCR Q1", ms => ms.some(m => m.includes("JCR Q1")));
createRankFilterBtn("1区", ms => ms.some(m => m.includes("1区")));
createRankFilterBtn("1、2区", ms => ms.some(m => m.includes("1区") || m.includes("2区")));
createRankFilterBtn("4区", ms => ms.some(m => m.includes("4区")));
createRankFilterBtn("区", ms => ms.some(m => m.includes("区")));
createRankFilterBtn("医学", ms => ms.some(m => m.includes("医学")));
createRankFilterBtn("材料", ms => ms.some(m => m.includes("材料")));
createRankFilterBtn("物理", ms => ms.some(m => m.includes("物理")));
createRankFilterBtn("化学", ms => ms.some(m => m.includes("化学")));
createRankFilterBtn("生物", ms => ms.some(m => m.includes("生物")));
if (document.querySelector("#gs_bdy")) {
//设置谷歌查询结果的最小高度,让滚动条一直存在
document.querySelector("#gs_bdy").style.minHeight = "600px";
}
function stat_info(qList, check = 0, checked = 0) {
console.log("stat_info",check,checked)
const checkCount = document.querySelector("#snRecListTop .mat-checkbox")?.textContent || "";
const page = document.querySelector("#snNextPageTop")?.value || "";
const pageAll = document.querySelector("body > app-wos > main > div > div > div.holder > div > div > div.held > app-input-route > app-base-summary-component > div > div.results.ng-star-inserted > app-page-controls.app-page-controls.ng-star-inserted > div > form > div")?.textContent || "";
const rankLength =
[...qList].map(a => a.querySelector(".srankInfo:not(.rcited,.ryear)")).filter(f => f).length;
const birdLength = document.querySelectorAll(".scicrx-btn .scicrx-svgicon").length;
const citeLength = document.querySelectorAll(".rcited").length;
divStat.textContent = `【选中${check}/已选${checked}/当前页${qList.length}/总选中${checkCount} / 页码:${page} ${pageAll} / 引用${citeLength}等级${rankLength}小鸟${birdLength}】`;
}
queryFilter()
}
filterRank(".gs_r.gs_or.gs_scl", "") // scholar
filterRank(".summary-record") // wos
async function initSortElements() {
if (!(await waitUtilAsync(() => document.querySelector("#gs_res_ccl_mid")))) { console.log("未找到initSortElements"); return; }
/**
* 从节点中提取引用次数和 rimf 值,并计算综合得分
* @param {HTMLElement} node - 要处理的节点
* @returns {number} 综合得分
*/
function getCiteCount(node) {
const citeText = node.querySelector('a[href*="cites"]')?.textContent || "0";
const citeInt = parseInt(citeText.match(/(\d+)/)?.[1], 10) || 0;
const rimf = parseInt(node.querySelector('.rimf')?.getAttribute("val"), 10) || 0;
return citeInt * 1000 + rimf;
}
let sortCount = 0
/**
* 对元素进行排序
*/
function sortElements() {
const gsResCclMid = document.getElementById('gs_res_ccl_mid');
if (!gsResCclMid) {
console.error('未找到 gs_res_ccl_mid 元素');
return;
}
const gsOrElements = [...gsResCclMid.querySelectorAll('.gs_or')]
.map(node => ({ node, citeCount: getCiteCount(node) }));
const gsOrElementsSorted = [...gsOrElements].sort((a, b) => b.citeCount - a.citeCount);
// 检查是否需要重新排序
const needResort = gsOrElements.some((element, index) => element.node !== gsOrElementsSorted[index].node);
if (strSort == "开启排序") {
if (needResort) {
console.log("重排", ++sortCount)
gsResCclMid.innerHTML = '';
gsResCclMid.append(...gsOrElementsSorted.map(item => item.node)); // 重新添加排序后的节点
setTimeout(sortElements, 1000);
} else {
setTimeout(sortElements, 5000);
}
}
}
let strSort = window.GM_getValue&&typeof GM_getValue !== "undefined"&&GM_getValue("btnSortElements", "禁用排序")
const btnSortElements = appendTo(getRootDiv(), "button", { type: "button", style: btnStyle, textContent: strSort }, () => {
strSort = strSort == "开启排序" ? "禁用排序" : "开启排序"
GM_setValue("btnSortElements", strSort)
btnSortElements.textContent = strSort;
updateBtnSortElements()
})
function updateBtnSortElements() {
if (strSort == "开启排序") {
setTimeout(sortElements, 1000);
btnSortElements.style.background = "#7ae3bc87"
} else {
btnSortElements.style.background = ""
}
}
updateBtnSortElements()
}
initSortElements()
if (location.href.indexOf("scispace") > -1) {
filterRank("main table tr", "") // scispace
waitUtilAsync(() => document.querySelector(".border-primary [data-icon=files]"))
.then(() => {
// scrollTo(0,650)
let queryCount = 10;
function sciSpaceAutoQuery() {
const queryBtn = document.querySelector(".border-primary [data-icon=files],.border-primary [data-icon=spinner-third]")?.parentElement
if (queryBtn) {
//queryBtn.scrollIntoView({ behavior: "smooth", block: "center" });
if (queryCount > 0) {
setTimeout(sciSpaceAutoQuery, 2000)
if (!queryBtn.getAttribute("disabled")) {
queryCount--;
queryBtn.click()
}
}
} else {
queryToolBtn10.remove()
// queryToolBtn5.remove()
}
}
const queryToolBtn10 = appendTo(getRootDiv(),
"button", { style: btnStyle, textContent: "Scispace加载更多" },
() => {
queryCount = 15;
sciSpaceAutoQuery()
}
)
scrollTo(0,document.querySelector("table").getBoundingClientRect().top+(window.pageYOffset||document.documentElement.scrollTop)-60)
document.querySelector(".border-primary [data-icon=files]").parentElement.click()
})
}
})();