筛选ABDC

小工具

// ==UserScript==
// @name         筛选ABDC
// @namespace    http://tampermonkey.net/
// @version      2025-05-30-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=scholar.google.com
// @grant        GM_setValue
// @grant        GM_getValue
// @license MIT
// ==/UserScript==
//https://greasyfork.org/zh-CN/scripts/533214-%E7%AD%9B%E9%80%89abdc
(function () {
    "use strict";
    document.querySelectorAll(".gs_a").forEach(f=>f.setAttribute("translate",'no'))
    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 = document.createElement("div")
        rootDiv.id = "z_root_div"
        rootDiv.style = `
                    position: fixed;
                    z-index:999999;
                    background-color: rgba(42, 38, 43, 0.39);
                    cursor: move;
                    border:1px solid rgba(238, 11, 204, 0.55);
                `;
        document.body.appendChild(rootDiv);
        if (typeof GM_getValue != undefined) {
            // 恢复 rootDiv 的位置
            const savedLeft = GM_getValue('rootDivLeft', 0);
            const savedTop = GM_getValue('rootDivTop', 0);
            document.body.clientHeight
            rootDiv.style.left = (document.body.clientWidth < savedLeft ? 0 : savedLeft) + 'px';
            rootDiv.style.top = (document.body.clientHeight < savedTop ? 0 : 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;
                if (typeof GM_setValue != undefined) {
                    // 保存 rootDiv 的位置
                    GM_setValue('rootDivLeft', parseInt(rootDiv.style.left));
                    GM_setValue('rootDivTop', parseInt(rootDiv.style.top));
                }
            }
        });
        setTimeout(() => {
            if (rootDiv.childElementCount == 0) rootDiv.remove()
        }, 2000)
        return rootDiv;
    }


    // Your code here...

    async function filterRank(selector = ".gs_r.gs_or.gs_scl", checkSelector = "input[type=checkbox]") {
        if (!(await waitUtilAsync(() => document.querySelector(selector)))) { console.log("未找到", selector); return; }
        console.log("找到", selector);
        const lastClickBtnTxt = GM_getValue("lastClickBtnTxt")
        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);
                                }
                                else {//显示全部
                                    const qList = document.querySelectorAll(selector)
                                    stat_info(qList)
                                }

                            }
                        });
                    });
                });
            window.rankInfoObserver.observe(document.body, { childList: true, subtree: true });
        }
        const divStat = appendTo(getRootDiv(), "div", "")
        let btns = []
        createRankBtn("显示全部", (m) => 1)
        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 = btnTxt.includes("全部") ? undefined : btn
                    GM_setValue("lastClickBtnTxt", btnTxt)
                }
                btns.forEach(f => {f.style.backgroundColor = f == btn ? "#e686e6" : ""})
                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)
                    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 未选中隐藏
                        row.style.transition = "border-left .5s, max-height .5s, overflow-y .5s, padding .5s";
                        if (checkFunc(rks)) {
                            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"
                            }
                        }
                    }
                }
                //统计信息
                stat_info(qList, check, checked);
            });
            btns.push(btn)
            if (btnTxt == lastClickBtnTxt) {
                //重新加载的时候需要点击一次。
                btn.click()
            }
            return btn
        }
        if (document.querySelector("#gs_bdy")) {
            //设置谷歌查询结果的最小高度,让滚动条一直存在
            document.querySelector("#gs_bdy").style.minHeight = "600px";
        }

        function stat_info(qList, check = 0, checked = 0) {
            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;
                [...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}】`;
        }
    }
    filterRank(".gs_r.gs_or.gs_scl", "")//scholar
    filterRank(".summary-record")//wos


    if (location.href.indexOf("scispace") > -1) {
        filterRank("main table tr", "")//scispace
        //查询100条

        waitUtilAsync(() => document.querySelector(".border-primary [data-icon=files]"))
            .then(() => {
                function sciSpaceAutoQuery() {
                    const queryBtn = document.querySelector(".border-primary [data-icon=files]")?.parentElement
                    if (queryBtn) {
                        queryBtn.scrollIntoView({ behavior: "smooth", block: "center" });
                        setTimeout(sciSpaceAutoQuery, 2000)
                        if (!queryBtn.getAttribute("disabled")) {
                            queryBtn.click()
                        }
                    } else {
                        queryToolBtn.remove() //按钮消失
                    }
                }
                const queryToolBtn = appendTo(getRootDiv(),
                    "button", { style: btnStyle, textContent: "ss查询100条" },
                    () => {
                        sciSpaceAutoQuery()
                    }
                )
            })

    }
})();