Greasy Fork is available in English.

知网检索增强

新标签页打开高级检索、高级检索自动生成专业检索式……

// ==UserScript==
// @name         知网检索增强
// @name:zh-CN   知网检索增强
// @name:en      CNKI Search Enhancer
// @version      0.2.2
// @namespace    https://greasyfork.org/users/808185
// @description  新标签页打开高级检索、高级检索自动生成专业检索式……
// @description:en   The new tab opens advanced retrieval and automatically generates retrieval.
// @icon         https://www.cnki.net/favicon.ico
// @author       shujuecn
// @match        *://*/kns8s/AdvSearch*
// @grant        none
// @license      GPL3
// ==/UserScript==

(function () {
    'use strict';

    const styles = `
        .custom-menu-list {
            list-style-type: none;
            margin: 0;
            padding: 0;
            overflow: hidden;
            display: inline-block;
            width: 460px;
            margin-top: -24px;
        }

        .custom-tab {
            float: right;
            height: 43px;
            padding: 0 20px;
            font-size: 15px;
            line-height: 43px;
            color: #2b2b2b;
            cursor: pointer;
        }

        .custom-tab:hover {
            color: red;
        }
    `;

    const styleSheet = document.createElement('style');
    styleSheet.innerText = styles;
    document.head.appendChild(styleSheet);

    const tabs = [
        {
            text: '新标签页检索',
            action: () => {
                const currentUrl = new URL(window.location.href);
                let newUrl;

                if (currentUrl.hostname.includes('kns.cnki.net')) {
                    newUrl = new URL(`${currentUrl.protocol}//${currentUrl.hostname}/kns8s/AdvSearch`);
                }

                if (newUrl) {
                    window.open(newUrl.href, '_blank');
                }
            }
        },
        {
            text: '复制为检索式',
            action: () => {
                const tabElement = event.currentTarget;
                const originalText = tabElement.textContent;

                const fieldList = document.querySelector("#ModuleSearch > div.search-box > div > div.search-classify > div > div.grade-search-content > div.search-mainbox.is-off > div.search-middle > div:nth-child(1) > div.gradeSearch");

                if (fieldList) {
                    const ddElements = fieldList.querySelectorAll('dd');
                    const fieldCount = ddElements.length;
                    const ddIndices = Array.from({ length: fieldCount }, (_, i) => i * 2 + 2);

                    const fieldNames = [];
                    const fieldValues = [];
                    const fieldOptions = [];
                    const logicalOperators = [];

                    ddIndices.forEach((index) => {
                        const nameElement = fieldList.querySelector(`#gradetxt > dd:nth-child(${index}) > div.input-box > div.sort.reopt > div.sort-default > span`);
                        const valueElement = fieldList.querySelector(`#gradetxt > dd:nth-child(${index}) > div.input-box > input[type=text]`);
                        const optionElement = fieldList.querySelector(`#gradetxt > dd:nth-child(${index}) > div.input-box > div.sort.special > div > span`);

                        if (nameElement && valueElement && optionElement) {
                            fieldNames.push(nameElement.textContent);
                            fieldValues.push(valueElement.value);
                            fieldOptions.push(optionElement.textContent);
                        }
                    });

                    const operatorIndices = ddIndices.slice(1);
                    operatorIndices.forEach((index) => {
                        const operatorElement = fieldList.querySelector(`#gradetxt > dd:nth-child(${index}) > div.sort.logical > div > span`);
                        if (operatorElement) {
                            logicalOperators.push(operatorElement.textContent);
                        }
                    });

                    // 1. 替换字段名称中的中文
                    const fieldNameReplacements = {
                        '主题': 'SU %= ', '篇关摘': 'TKA %= ', '关键词': 'KY = ', '篇名': 'TI %= ', '全文': 'FT %= ',
                        '作者': 'AU = ', '第一作者': 'FI = ', '通讯作者': 'RP = ', '作者单位': 'AF = ', '基金': 'FU = ',
                        '摘要': 'AB %= ', '小标题': 'CO %= ', '参考文献': 'RF %= ', '分类号': 'CLC = ', '文献来源': 'LY %= ',
                        'DOI': 'DOI = ', '被引频次': 'CF = ',
                        '题名': 'TI %= ', '导师': 'TU = ', '第一导师': 'FTU = ', '学位授予单位': 'LY %= ',
                        '目录': 'CO %= ', '中图分类号': 'CLC = ', '学科专业名称': 'XF = ', '发表时间': 'PT = '
                    };

                    // 2. 构造检索键值表
                    const searchKeyValues = [];
                    const specialChars = /[*+\-]/; // 定义特殊字符

                    fieldValues.forEach((value, index) => {
                        if (value) {
                            const words = value.split(' '); // 按空格分词
                            const quotedWords = words.map((word, idx) => {
                                if (specialChars.test(word)) { // 如果是特殊字符,直接返回
                                    return word;
                                } else if (/^['"].*['"]$/.test(word)) { // 如果已被引号包裹,直接返回
                                    return word;
                                } else if (/^\(.*\)$/.test(word)) { // 如果被小括号包裹
                                    const subWords = word.slice(1, -1).split('+');
                                    const quotedSubWords = subWords.map(subWord => {
                                        if (specialChars.test(subWord)) {
                                            return subWord;
                                        } else if (/^['"].*['"]$/.test(subWord)) {
                                            return subWord;
                                        } else {
                                            return `'${subWord}'`;
                                        }
                                    });
                                    return `(${quotedSubWords.join(' + ')})`;
                                } else { // 否则在两侧添加单引号,但首位元素如果开头字符为(或末尾元素如果结尾字符为)则处理括号和文字
                                    if (word[0] === '(') { // 首位元素以 ( 开头
                                        const bracketContent = word.slice(1);
                                        const quotedContent = /^['"].*['"]$/.test(bracketContent) ? bracketContent : `'${bracketContent}'`;
                                        return `(${quotedContent}`;
                                    } else if (word[word.length - 1] === ')') { // 末位元素以 ) 结尾
                                        const bracketContent = word.slice(0, -1);
                                        const quotedContent = /^['"].*['"]$/.test(bracketContent) ? bracketContent : `'${bracketContent}'`;
                                        return `${quotedContent})`;
                                    } else {
                                        return `'${word}'`;
                                    }
                                }
                            });
                            const quotedValue = quotedWords.join(' '); // 用空格连接子元素
                            const replacedName = fieldNameReplacements[fieldNames[index]] || fieldNames[index];
                            searchKeyValues.push(`${replacedName}${quotedValue}`);
                        }
                    });

                    // 3. 插入逻辑运算符
                    const searchQuery = searchKeyValues.reduce((acc, curr, index) => {
                        if (index < searchKeyValues.length - 1) {
                            return `${acc}${curr} ${logicalOperators[index]} `;
                        } else {
                            return `${acc}${curr}`;
                        }
                    }, '');

                    // console.log('字段名称:', fieldNames);
                    // console.log('字段值:', fieldValues);
                    // console.log('字段选项:', fieldOptions);
                    // console.log('逻辑运算符:', logicalOperators);
                    // console.log('检索键值表:', searchKeyValues);
                    // console.log('搜索查询字符串:', searchQuery);


                    // 4. 复制到剪切板
                    if (navigator.clipboard) {
                        navigator.clipboard.writeText(searchQuery)
                            .then(() => {
                            console.log('已复制搜索查询字符串到剪切板');
                            tabElement.textContent = '复制成功!';

                            // 鼠标脱离时恢复原始文本
                            const handleMouseLeave = () => {
                                tabElement.textContent = originalText;
                                tabElement.removeEventListener('mouseleave', handleMouseLeave);
                            };
                            tabElement.addEventListener('mouseleave', handleMouseLeave, { once: true });
                        })
                            .catch((err) => {
                            console.error('复制到剪切板时出错:', err);
                            // 使用备用方案进行复制
                            copyTextToClipboard(searchQuery, tabElement, originalText);
                        });
                    } else {
                        // 浏览器不支持 Clipboard API,直接使用备用方案进行复制
                        copyTextToClipboard(searchQuery, tabElement, originalText);
                    }
                } else {
                    console.log('未找到检索字段列表元素');
                }
            }
        }
    ];
    // 备用方案: 创建临时文本区域
    function copyTextToClipboard(text, tabElement, originalText) {
        const textArea = document.createElement('textarea');
        textArea.value = text;
        document.body.appendChild(textArea);
        textArea.select();

        try {
            const successful = document.execCommand('copy');
            if (successful) {
                console.log('已复制搜索查询字符串到剪切板');
                tabElement.textContent = '复制成功!';

                // 鼠标脱离时恢复原始文本
                const handleMouseLeave = () => {
                    tabElement.textContent = originalText;
                    tabElement.removeEventListener('mouseleave', handleMouseLeave);
                };
                tabElement.addEventListener('mouseleave', handleMouseLeave, { once: true });
            } else {
                console.error('无法复制到剪切板');
            }
        } catch (err) {
            console.error('无法访问剪贴板:', err);
        }

        document.body.removeChild(textArea);
    }

    window.addEventListener('load', function () {
        const menuList = document.querySelector('.search-classify-menu');
        const customMenuList = document.createElement('ul');
        customMenuList.className = 'custom-menu-list';

        // 去除知网logo
        const elementToReplace = document.querySelector("#ModuleSearch > div.search-box > div > div.search-left > div");
        if (elementToReplace) {
            const placeholderDiv = document.createElement('div');
            placeholderDiv.style.width = elementToReplace.offsetWidth + 'px';
            placeholderDiv.style.height = elementToReplace.offsetHeight + 'px';
            elementToReplace.parentNode.replaceChild(placeholderDiv, elementToReplace);
        }

        tabs.forEach(tab => {
            const newTab = document.createElement('li');
            newTab.className = 'custom-tab';
            newTab.textContent = tab.text;
            newTab.addEventListener('click', tab.action);
            customMenuList.appendChild(newTab);
        });

        menuList.parentNode.insertBefore(customMenuList, menuList.nextSibling);
    });
})();