Greasy Fork

Greasy Fork is available in English.

知网-文献-bibtex提取

从知网文献中直接复制引文,支持多种引文格式:BibTeX、GB/T 7714-2015、知网研学、CAJ-CD、MLA、APA、Refworks、EndNote、NoteExpress、NodeFirst

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         知网-文献-bibtex提取
// @namespace    https://github.com/BNDou/getCnkiLiteratureBibTex
// @description  从知网文献中直接复制引文,支持多种引文格式:BibTeX、GB/T 7714-2015、知网研学、CAJ-CD、MLA、APA、Refworks、EndNote、NoteExpress、NodeFirst
// @license      AGPL License
// @version      4.2.1
// @author       BN_Dou
// @match        *://kns.cnki.net/kcms2/article/abstract*
// @match        *://kns.cnki.net/kcms/detail*
// @match        *://kns.cnki.net/kns8s/defaultresult/index*
// @match        *://kns.cnki.net/kns8s/search*
// @match        *://kns.cnki.net/kns8s/AdvSearch*
// @grant        GM_registerMenuCommand
// @grant        GM_setClipboard
// @grant        GM_getValue
// @grant        GM_setValue
// @icon         https://www.cnki.net/favicon.ico
// ==/UserScript==

(function () {
    'use strict';

    // 引文类型定义
    const CITATION_TYPES = {
        'BibTeX': 'BibTex',
        'GB/T 7714-2015': 'GBTREFER',
        '知网研学': 'elearning',
        'CAJ-CD': 'REFER',
        'MLA': 'MLA',
        'APA': 'APA',
        'Refworks': 'Refworks',
        'EndNote': 'EndNote',
        'NoteExpress': 'NoteExpress',
        'NodeFirst': 'NodeFirst'
    };

    // 获取当前引文类型
    let currentCitationType = GM_getValue('citationType', 'BibTeX');

    // 更新按钮文本
    function updateButtonText() {
        // 更新普通按钮
        const buttons = document.querySelectorAll('[id^="bibbtn"]');
        buttons.forEach(button => {
            if (button.querySelector('span')) {
                button.querySelector('span').textContent = currentCitationType;
            }
        });

        // 更新批量复制按钮
        const batchLink = document.querySelector('#batch_bibbtn_li a');
        if (batchLink) {
            batchLink.textContent = `批量复制${currentCitationType}`;
        }
    }

    // 更新菜单项
    function updateMenuItems() {
        // 添加菜单项
        for (const [typeName, _] of Object.entries(CITATION_TYPES)) {
            GM_registerMenuCommand(`🔄 切换到 ${typeName}`, () => switchCitationType(typeName));
        }
    }

    // 切换引文类型
    function switchCitationType(type) {
        currentCitationType = type;
        GM_setValue('citationType', type);
        updateButtonText();
    }

    // 样式定义
    const STYLES = {
        button: `
            width: 65px;
            height: 25px;
            border-radius: 12px;
            background-color: #0f5de5;
            border: none;
            color: white;
            font-size: 12px;
            font-weight: 600;
            text-align: center;
            cursor: pointer;
            transition: all 0.3s ease;
            box-shadow: 0 2px 8px rgba(15, 93, 229, 0.3);
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 4px;
            position: relative;
            z-index: 1;
        `,
        advSearchButton: `
            width: 100%;
            height: 24px;
            padding: 0 8px;
            border-radius: 8px;
            background-color: #f0f0f0;
            border: 1px solid #ddd;
            color: #0f5de5;
            font-size: 12px;
            font-weight: normal;
            text-align: center;
            cursor: pointer;
            transition: all 0.2s ease;
            margin: 4px 0;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            gap: 2px;
        `,
        successAnimation: `
            @keyframes successPulse {
                0% { transform: scale(1); }
                50% { transform: scale(1.1); }
                100% { transform: scale(1); }
            }
        `
    };

    let citationText = '';

    // 创建并注入样式
    function injectStyles() {
        const styleSheet = document.createElement('style');
        styleSheet.textContent = STYLES.successAnimation;
        document.head.appendChild(styleSheet);
    }

    // 创建按钮
    function createButton(isAdvSearch = false) {
        const button = document.createElement('button');
        button.id = "bibbtn";
        button.title = "点击复制引文";
        button.innerHTML = `<span>${currentCitationType}</span>`;
        button.style.cssText = isAdvSearch ? STYLES.advSearchButton : STYLES.button;

        if (isAdvSearch) {
            // 高级检索页面的悬停效果
            button.addEventListener('mouseover', () => {
                button.style.backgroundColor = '#e8e8e8';
                button.style.borderColor = '#ccc';
            });

            button.addEventListener('mouseout', () => {
                button.style.backgroundColor = '#f0f0f0';
                button.style.borderColor = '#ddd';
            });
        } else {
            // 原有页面的悬停效果
            button.addEventListener('mouseover', () => {
                button.style.backgroundColor = '#0d4fc3';
                button.style.transform = 'translateY(-1px)';
                button.style.boxShadow = '0 4px 12px rgba(15, 93, 229, 0.4)';
            });

            button.addEventListener('mouseout', () => {
                button.style.backgroundColor = '#0f5de5';
                button.style.transform = 'translateY(0)';
                button.style.boxShadow = '0 2px 8px rgba(15, 93, 229, 0.3)';
            });
        }

        return button;
    }

    // 显示复制成功提示
    function showCopySuccess(button) {
        const element = document.getElementById(button);
        if (!element) return;

        if (button === 'batch_bibbtn_li') {
            // 批量复制按钮的处理
            const batchLink = element.querySelector('a');
            if (!batchLink) return;

            const originalText = batchLink.textContent;
            batchLink.textContent = '✅ 已复制';
            batchLink.style.animation = 'successPulse 0.5s ease';

            setTimeout(() => {
                batchLink.textContent = originalText;
                batchLink.style.animation = '';
            }, 1500);
        } else {
            // 普通按钮的处理
            const originalText = element.innerHTML;
            // 检查是否为检索页面的按钮
            const isSearchPageButton = button.startsWith('bibbtn_');
            element.innerHTML = `<span style="color: ${isSearchPageButton ? '#0f5de5' : 'white'};">✅ 已复制</span>`;
            element.style.animation = 'successPulse 0.5s ease';

            setTimeout(() => {
                element.innerHTML = originalText;
                element.style.animation = '';
            }, 1500);
        }
    }

    // 获取引文数据
    async function getCitationText(filename = null) {
        try {
            let params;
            if (filename) {
                // 高级检索页面的情况
                params = {
                    FileName: filename,
                    DisplayMode: CITATION_TYPES[currentCitationType],
                    OrderParam: 0,
                    OrderType: 'desc',
                };
            } else {
                // 默认情况
                params = {
                    FileName: document.getElementById("paramdbname").getAttribute("value") + '!' +
                        document.getElementById("paramfilename").getAttribute("value") + '!1!0',
                    DisplayMode: CITATION_TYPES[currentCitationType],
                    OrderParam: 0,
                    OrderType: 'desc',
                };
            }
            // const response = await fetch('https://kns.cnki.net/dm/api/ShowExport', {
            const response = await fetch('https://kns.cnki.net/dm8/api/ShowExport', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                    'Referer': 'https://kns.cnki.net/dm/manage/export.html',
                },
                body: new URLSearchParams(params),
            });

            const data = await response.text();

            // 创建一个临时的div来解析HTML
            const tempDiv = document.createElement('div');
            tempDiv.innerHTML = data;

            let sText = '';
            const displayMode = CITATION_TYPES[currentCitationType].toUpperCase();

            // 使用类似官方的提取逻辑
            if (displayMode === 'MLA' || displayMode === 'APA') {
                // 对于MLA和APA格式,直接获取文本内容
                const items = tempDiv.querySelectorAll('ul.literature-list li');
                sText = Array.from(items)
                    .map(item => item.textContent
                        .replace(/\r/g, '')
                        .replace(/\n/g, '')
                        .replace(/      /g, '')
                        .replace(/  /g, ''))
                    .join('\n');
            } else if (displayMode === "NODEFIRST") {
                // 对于NODEFIRST格式,直接获取文本内容
                const items = tempDiv.querySelectorAll('ul.literature-list li');
                sText = Array.from(items)
                    .map(item => item.innerHTML
                        .replace(/&lt;/g, "<")
                        .replace(/&gt;/g, ">")
                        .replace(/\r/g, "")
                        .replace(/\n/g, "")
                        .replace(/<BR>/g, "\r\n")
                        .replace(/<br>/g, "\r\n"))
                    .join('\n');
            } else {
                // 对于其他格式
                const items = tempDiv.querySelectorAll('ul.literature-list>li');
                sText = Array.from(items)
                    .map(item => {
                        let text = item.innerHTML
                            .replace(/\r/g, '')
                            .replace(/\n/g, '')
                            .replace(/<BR>/g, '\n')
                            .replace(/<br>/g, '\n')
                            .replace(/&lt;/g, '<')
                            .replace(/&gt;/g, '>')
                            .replace(/&nbsp;/g, ' ')
                            .replace(/      {/g, '{')
                            .replace(/      /g, '');

                        // 根据不同格式处理空格
                        if (displayMode === 'GBTREFER') {
                            text = text.replace(/  /g, '');
                        } else if (displayMode === 'REFER' || displayMode === 'NEW' || displayMode === 'NEWDEFINE') {
                            text = text.replace(/    /g, '');
                        } else if (displayMode === 'SELFDEFINE') {
                            text = text.replace(/   /g, '');
                        } else if (displayMode === 'BIBTEX') {
                            text = text.replace(/author = \{(\s+)/g, 'author = {').replace(/(\s+)and(\s+)/g, ' and ');
                        }

                        // 移除所有HTML标签
                        text = text.replace(/<\/?.+?\/?>/g, '');
                        return text;
                    })
                    .join('\n');
            }

            if (!sText) {
                throw new Error('未找到引文数据');
            }

            return sText;
        } catch (error) {
            console.error('获取引文失败:', error);
            return null;
        }
    }

    // 复制引文到剪贴板
    async function copyText(buttonId = 'bibbtn', filename = null) {
        const citationContent = await getCitationText(filename);
        if (citationContent) {
            GM_setClipboard(citationContent);
            showCopySuccess(buttonId);
        }
    }

    // 初始化
    function initialize() {
        injectStyles();

        // 初始化菜单
        updateMenuItems();

        // 根据页面URL决定按钮添加位置
        const currentURL = window.location.href;

        if (currentURL.includes('https://kns.cnki.net/kns8s/defaultresult/index') ||
            currentURL.includes('https://kns.cnki.net/kns8s/AdvSearch') ||
            currentURL.includes('https://kns.cnki.net/kns8s/search')) {
            // 高级检索页面 - 添加定时检测

            // 添加批量操作按钮
            function addBatchButton() {
                const batchOpsBox = document.getElementById('batchOpsBox');
                if (batchOpsBox && !batchOpsBox.querySelector('li[id="batch_bibbtn_li"]')) {
                    // 创建新的li元素
                    const batchLi = document.createElement('li');
                    batchLi.id = 'batch_bibbtn_li';
                    batchLi.className = 'export';

                    // 创建链接
                    const batchLink = document.createElement('a');
                    batchLink.href = 'javascript:void(0)';
                    batchLink.textContent = `批量复制${currentCitationType}`;
                    batchLink.style.color = '#0f5de5';

                    // 为链接绑定点击事件
                    batchLink.addEventListener('click', () => {
                        const checkedBoxes = document.querySelectorAll('.cbItem:checked');
                        if (checkedBoxes.length === 0) {
                            alert('请先选择要复制的文献');
                            return;
                        }
                        const values = Array.from(checkedBoxes).map(cb => cb.value).join(',');
                        copyText('batch_bibbtn_li', values);
                    });

                    // 组装元素
                    batchLi.appendChild(batchLink);
                    batchOpsBox.appendChild(batchLi);
                }
            }

            // 定义检测和添加按钮的函数
            function checkAndAddButtons() {
                // 添加批量操作按钮
                addBatchButton();

                // 添加单个操作按钮
                const operatElements = document.querySelectorAll('.operat, .opts ul.opts-btn');

                Array.from(operatElements).forEach((element, index) => {
                    // 检查该行是否已有按钮
                    if (element.querySelector('button[id^="bibbtn_"]')) return;

                    const button = createButton(true);  // 传入true表示是高级检索页面
                    button.id = `bibbtn_${index}`;

                    // 为opts创建li元素
                    if (element.classList.contains('opts-btn')) {
                        const li = document.createElement('li');
                        li.appendChild(button);
                        element.insertBefore(li, element.firstChild);
                    } else {
                        element.insertBefore(button, element.firstChild);
                    }

                    let filename_param = '';
                    if (element.classList.contains('opts-btn')) {
                        // 对于opts情况,从父级dd中查找cbItem
                        const dd = element.closest('dd');
                        if (dd) {
                            const cbItem = dd.querySelector('.cbItem');
                            if (cbItem) {
                                filename_param = cbItem.value;
                            }
                        }
                    } else {
                        // 对于operat情况,从tr中查找cbItem
                        const resultItem = element.closest('tr');
                        if (resultItem) {
                            const cbItem = resultItem.querySelector('.cbItem');
                            if (cbItem) {
                                filename_param = cbItem.value;
                            }
                        }
                    }

                    if (filename_param) {
                        // 为按钮绑定点击事件
                        $(`#bibbtn_${index}`).click(() => {
                            copyText(`bibbtn_${index}`, filename_param);
                        });
                    }
                });
            }

            // 启动定时检测
            setInterval(checkAndAddButtons, 1000);

        } else {
            // 默认处理
            const otherButtons = document.getElementsByClassName('other-btns')[0];
            if (otherButtons) {
                // 创建按钮元素
                const li = document.createElement('li');
                li.className = 'btn-bibtex';
                li.style.cssText = `
                    width: 65px;
                    height: 25px;
                `;
                const button = createButton();
                li.appendChild(button);

                // 插入到第一个位置
                otherButtons.insertBefore(li, otherButtons.firstChild);

                // 绑定点击事件
                $("#bibbtn").click(() => copyText());
            }
        }
    }

    // 启动脚本
    initialize();
})();