// ==UserScript==
// @name 筛选ABDC
// @namespace http://tampermonkey.net/
// @version 2025-04-19-001
// @description 小工具
// @author 周利斌
// @match https://so1.imageoss.com/*
// @match https://so3.cljtscd.com/scholar*
// @match *://*/*
// @include *webofscience*
// @include *www-scopus-com*
// @icon https://www.google.com/s2/favicons?sz=64&domain=yitlink.com
// @grant GM_setValue
// @grant GM_getValue
// @grant GM.setValue
// @grant GM.getValue
// @license MIT
// ==/UserScript==
(function () {
"use strict";
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, reject) => {
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 getRootDiv() {
const existingDiv = document.getElementById('z_root_div');
if (existingDiv) {
return existingDiv;
}
const rootDiv = appendTo(document.body, "div", {
id: "z_root_div",
style: `
position: fixed;
z-index:999999;
background-color: lightblue;
cursor: move;
border: 2px solid purple;
border-left: 20px solid purple;
`
});
// 使用 appendTo 设置左侧内边距颜色
// appendTo(rootDiv, "div", {
// style: `
// position: absolute;
// left: 0;
// top: 0;
// bottom: 0;
// width: 20px;
// background-color: #660099;
// `
// });
// 恢复 rootDiv 的位置
const savedLeft = GM_getValue('rootDivLeft', 0);
const savedTop = GM_getValue('rootDivTop', 0);
rootDiv.style.left = savedLeft + 'px';
rootDiv.style.top = savedTop + 'px';
let isDragging = false;
let offsetX, offsetY;
rootDiv.addEventListener('mousedown', (e) => {
isDragging = true;
offsetX = e.clientX - rootDiv.offsetLeft;
offsetY = e.clientY - rootDiv.offsetTop;
});
document.addEventListener('mousemove', (e) => {
if (isDragging) {
rootDiv.style.left = (e.clientX - offsetX) + 'px';
rootDiv.style.top = (e.clientY - offsetY) + 'px';
}
});
document.addEventListener('mouseup', () => {
if (isDragging) {
isDragging = false;
// 保存 rootDiv 的位置
GM_setValue('rootDivLeft', parseInt(rootDiv.style.left));
GM_setValue('rootDivTop', parseInt(rootDiv.style.top));
}
});
return rootDiv;
}
// Your code here...
filterRank(".gs_r.gs_or.gs_scl")//scholar
filterRank(".summary-record")//wos
async function filterRank(selector = ".gs_r.gs_or.gs_scl") {
if (!(await waitUtilAsync(() => document.querySelector(selector)))) { console.log("未找到", selector); return; }
console.log("找到", selector);
const lastClickBtn = GM_getValue("lastClickBtn")
const btnClear = appendTo(getRootDiv(), "button", { textContent: "显示全部", style: btnStyle }, () => {
lcb = undefined
GM_setValue("lastClickBtn", "")
for (const row of document.querySelectorAll(selector)) {
const ck = row.querySelector("input[type=checkbox]")
if (ck) {//wos
if (ck.checked) { ck.click() }
row.children[0].style.backgroundColor = "";
}
else {//scholar
row.style.maxHeight = "";
row.style.overflowY = ""
row.style.padding = ""
}
btnClear.textContent = "显示全部"
}
})
createRankBtn("ABDC A*/A", ms => ms.some(m => m.includes("ABDC A")));
createRankBtn("ABDC B", ms => ms.some(m => m.includes("ABDC B")));
createRankBtn("SSCI IF>2", ms => ms.some(m => m.includes("SSCI")) && ms.some(m => m.startsWith("IF") && asIF(m) > 2));
createRankBtn("IF>8", ms => ms.some(m => m.startsWith("IF") && asIF(m) > 8));
createRankBtn("CiteScore>12", ms => ms.some(m => m.includes("CiteScore") && asIF(m) > 12));
/**
*
* @param {string} content
* @returns number
*/
function asIF(content) {
const IF = parseFloat(content.match(/([\d\.]+)$/g)?.[0]) || 0
// console.log(content.match(/[\d\.]+/g), IF, content);
return IF
}
/**
*
* @param {number} id
* @param {string} btnTxt
* @param {(rankInfos:string[])=>boolean} checkFunc
*/
function createRankBtn(btnTxt = "", checkFunc = (a) => false) {
const btn = appendTo(getRootDiv(), "button", { style: btnStyle, textContent: btnTxt }, () => {
if (lcb != btn) {
lcb = btn
GM_setValue("lastClickBtn", btnTxt)
}
const qList = document.querySelectorAll(selector)
let check = 0, checked = 0;
for (const row of qList) {
const rks = [...row.querySelectorAll(".srankInfo")].map(a => a.textContent)
const ck = row.querySelector("input[type=checkbox]")
if (ck) {//wos
if (checkFunc(rks)) {
row.children[0].style.backgroundColor = getComputedStyle(row.querySelector(".srankInfo")).backgroundColor;
if (ck.checked) {
checked++;
} else {
ck.click();
check++;
}
}
}
else {//scholar
if (checkFunc(rks)) {
// row.style.borderLeft= ""
// row.style.backgroundClip= ""
// row.style.backgroundOrigin= ""
// row.style.backgroundImage= ""
row.style.borderLeft = ""
row.style.maxHeight = "";
row.style.overflowY = ""
row.style.padding = ""
check++
}
else {
// row.style.borderLeft= "4px solid transparent"
// row.style.backgroundClip= "padding-box, border-box"
// row.style.backgroundOrigin= "padding-box, border-box"
// row.style.backgroundImage= "linear-gradient(to bottom, #222, #222), linear-gradient(90deg, #8F41E9, #578AEF)"
row.style.borderLeft = "5px solid #300"
row.style.maxHeight = "1px"
row.style.overflowY = "hidden"
row.style.padding = "0px"
}
}
}
{//wos
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 = document.querySelectorAll(".srankdiv").length;
const birdLength = document.querySelectorAll(".gs_ri .scicrx-ico").length;
btnClear.textContent = `显示全部【${check}/${checked}/${qList.length}/${checkCount} / ${page} ${pageAll} ${rankLength} ${birdLength}】`;
}
});
if (btnTxt == lastClickBtn)
btn.click()
return btn
}
}
let lcb = undefined
if (!window.rankInfoObserver) {
window.rankInfoObserver =
new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1 && node.classList.contains('srankdiv')) {
console.log(`.srankdiv 的数量: ${document.querySelectorAll('.srankdiv').length}`);
if (lcb) setTimeout(() => lcb.click(), 500)
}
});
});
});
window.rankInfoObserver.observe(document.body, { childList: true, subtree: true });
}
//设置最小高度,让滚动条一直存在
const gs_bdy = document.querySelector("#gs_bdy");
if (gs_bdy) {
gs_bdy.style.minHeight = "600px";
}
init();
})();