Greasy Fork

Greasy Fork is available in English.

Google Scholar 第一作者/通讯作者高亮 & 统计

高亮 Google Scholar 档案页中的第一作者和最后作者(通讯作者)论文,并在文章列表顶部显示统计面板(文章数、引用数、h-index、h10-index)。

当前为 2025-12-22 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Google Scholar Author Highlighter & Metrics
// @name:zh-CN   Google Scholar 第一作者/通讯作者高亮 & 统计
// @namespace    https://github.com/Huangmp1996
// @version      3.6
// @description  Highlight papers where you are the first or last author on Google Scholar, and display a metrics panel (Papers, Citations, h-index) at the top of the article list.
// @description:zh-CN 高亮 Google Scholar 档案页中的第一作者和最后作者(通讯作者)论文,并在文章列表顶部显示统计面板(文章数、引用数、h-index、h10-index)。
// @author       Mingpan Huang
// @match        https://scholar.google.com/citations*
// @match        https://scholar.google.com.hk/citations*
// @match        https://scholar.google.cn/citations*
// @match        https://scholar.google.co.jp/citations*
// @match        https://scholar.google.co.uk/citations*
// @match        https://scholar.google.de/citations*
// @match        https://scholar.google.fr/citations*
// @match        https://scholar.google.ca/citations*
// @match        https://scholar.google.com.au/citations*
// @match        https://scholar.google.com.br/citations*
// @match        https://scholar.google.it/citations*
// @match        https://scholar.google.es/citations*
// @match        https://scholar.google.nl/citations*
// @match        https://scholar.google.ru/citations*
// @match        https://scholar.google.se/citations*
// @match        https://scholar.google.ch/citations*
// @match        https://scholar.google.com.tw/citations*
// @match        https://scholar.google.co.in/citations*
// @match        https://scholar.google.com.sg/citations*
// @match        https://scholar.google.co.kr/citations*
// @match        *://scholar.google.*/citations*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=google.com
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    console.log("Scholar Script V3.5: Loaded...");

    // --- 1. 样式定义 ---
    GM_addStyle(`
        /* 高亮颜色定义 */

        /* 第一作者 & 独立作者 (浅蓝色) */
        .highlight-first-author, .highlight-solo-author {
            background-color: #e6f7ff !important;
            border-left: 5px solid #0984e3;
        }

        /* 最后作者/通讯作者 (浅黄色) */
        .highlight-last-author {
            background-color: #fffbea !important;
            border-left: 5px solid #fdcb6e;
        }

        /* 统计面板 - 顶部横幅模式 */
        .metrics-panel-banner {
            background: #f8f9fa;
            border: 1px solid #ddd;
            padding: 12px 20px;
            margin: 0 0 20px 0; /* 底部留白 */
            border-radius: 8px;
            display: flex;
            justify-content: space-around;
            align-items: center;
            box-shadow: 0 1px 3px rgba(0,0,0,0.05);
            font-family: Arial, sans-serif;
        }

        /* 横幅内的布局 */
        .metrics-panel-banner .metric-item {
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        .metrics-panel-banner .metric-val {
            font-size: 20px;
            font-weight: bold;
            color: #1a0dab;
        }
        .metrics-panel-banner .metric-lbl {
            font-size: 12px;
            color: #5f6368;
            margin-top: 4px;
        }

        /* 标题样式 */
        .metrics-header {
            font-weight: bold;
            color: #202124;
            margin-right: 20px;
            font-size: 14px;
        }
    `);

    // --- 2. 工具函数 ---
    function calculateHIndex(citations) {
        citations.sort((a, b) => b - a);
        let h = 0;
        for (let i = 0; i < citations.length; i++) {
            if (citations[i] >= i + 1) h = i + 1;
            else break;
        }
        return h;
    }

    // --- 3. 核心:精准匹配逻辑 ---
    function isAuthorMatch(authorName, targetLast, targetInitial) {
        const name = authorName.toUpperCase();
        if (!name.includes(targetLast)) return false;
        if (targetInitial) {
            const firstChar = name.charAt(0);
            if (firstChar !== targetInitial) {
                return false;
            }
        }
        return true;
    }

    // --- 4. 渲染面板 (仅横幅模式) ---
    function renderDashboard(stats) {
        // 直接寻找文章列表的主容器
        const mainContent = document.getElementById('gsc_art');
        if (!mainContent) return;

        let panel = document.getElementById('gs-custom-stats-panel');

        if (!panel) {
            panel = document.createElement('div');
            panel.id = 'gs-custom-stats-panel';
            panel.className = 'metrics-panel-banner';
            // 插入到文章列表的最上方
            mainContent.prepend(panel);
        }

        // --- 关键修改:检查是否按年份排序 ---
        const urlParams = new URLSearchParams(window.location.search);
        const isSortedByDate = urlParams.get('sortby') === 'pubdate';

        // 只有在按年份排序且有数据时才显示 Since 20XX,否则为空
        const yearDisplay = (isSortedByDate && stats.minYear) ? `Since ${stats.minYear}` : '';

        // 渲染横幅 HTML
        panel.innerHTML = `
            <div class="metrics-header">
                <div>Highlight Metrics</div>
                <div style="font-size:11px; color:#d93025; font-weight:normal; height:13px;">${yearDisplay}</div>
            </div>
            <div class="metric-item"><span class="metric-val">${stats.count}</span><span class="metric-lbl">Papers</span></div>
            <div class="metric-item"><span class="metric-val">${stats.citations}</span><span class="metric-lbl">Citations</span></div>
            <div class="metric-item"><span class="metric-val">${stats.hIndex}</span><span class="metric-lbl">h-index</span></div>
            <div class="metric-item"><span class="metric-val">${stats.h10Index}</span><span class="metric-lbl">h10-index</span></div>
        `;
    }

    // --- 5. 主程序 ---
    function runScript() {
        const profileNameEl = document.getElementById('gsc_prf_in');
        if (!profileNameEl) return;

        // --- A. 解析当前学者姓名 ---
        const fullName = profileNameEl.innerText.trim();
        const nameParts = fullName.split(' ');

        let targetLast = "";
        let targetInitial = "";

        if (nameParts.length > 0) {
            targetLast = nameParts[nameParts.length - 1].toUpperCase();
            targetInitial = nameParts[0].charAt(0).toUpperCase();
        }

        // --- B. 遍历行 ---
        const rows = document.querySelectorAll('.gsc_a_tr');
        rows.forEach(row => {
            if (row.getAttribute('data-gs-processed')) return;

            const authorDiv = row.querySelector('.gs_gray');
            if (!authorDiv) return;

            const authorsText = authorDiv.innerText.replace('...', '');
            const authors = authorsText.split(',').map(s => s.trim()).filter(s => s);

            if (authors.length === 0) return;

            const fAuth = authors[0];
            const lAuth = authors[authors.length - 1];

            row.setAttribute('data-gs-processed', 'true');

            const isFirst = isAuthorMatch(fAuth, targetLast, targetInitial);
            const isLast = isAuthorMatch(lAuth, targetLast, targetInitial);

            // CSS类名已统一颜色
            if (isFirst && authors.length === 1) row.classList.add('highlight-solo-author');
            else if (isFirst) row.classList.add('highlight-first-author');
            else if (isLast) row.classList.add('highlight-last-author');
        });

        // --- C. 统计 ---
        setTimeout(() => {
            const targets = document.querySelectorAll('.highlight-first-author, .highlight-last-author, .highlight-solo-author');
            const citations = [];
            const years = [];

            targets.forEach(row => {
                const citeEl = row.querySelector('.gsc_a_ac');
                if (citeEl) {
                    const txt = citeEl.innerText.trim();
                    if (txt && txt !== '*') {
                        const num = parseInt(txt, 10);
                        if (!isNaN(num)) citations.push(num);
                    } else citations.push(0);
                }
                const yearEl = row.querySelector('.gsc_a_y');
                if (yearEl) {
                    const y = parseInt(yearEl.innerText.trim(), 10);
                    if (!isNaN(y) && y > 1900) years.push(y);
                }
            });

            const minYear = years.length ? Math.min(...years) : null;

            renderDashboard({
                count: targets.length,
                citations: citations.reduce((a,b)=>a+b, 0),
                hIndex: calculateHIndex(citations),
                h10Index: citations.filter(c=>c>=10).length,
                minYear: minYear
            });
        }, 100);
    }

    // --- 6. 启动 ---
    window.addEventListener('load', runScript);

    let attempts = 0;
    const initTimer = setInterval(() => {
        runScript();
        attempts++;
        if (attempts > 5) clearInterval(initTimer);
    }, 1000);

    const tableBody = document.getElementById('gsc_a_b');
    if (tableBody) {
        new MutationObserver((ms) => {
            if (ms.some(m => m.addedNodes.length > 0)) runScript();
        }).observe(tableBody, { childList: true, subtree: true });
    }

})();