// ==UserScript==
// @name 知网重定向 — 便于使用机构IP登录下载
// @namespace cnki_redirector
// @description 将来自知网主站、知网空间、知网编客、知网百科、知网阅读、知网文化、知网法律、知网医院数字图书馆、手机知网等站点的知网文献页重定向至知网主站`kns.cnki.net`,支持获取知网文献无追踪链接。
// @version 5.0
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAB10lEQVQ4jZVSP8hpcRj+nZs6oiPkpFBkoCxnQCeT0rdQyiCZDAwYTOQsBsOR/aRshlMymsSK/JkMysDgz3KOZDmO8t/vDu79fO51b33P9r71vO/7PO+DwKIbfAuw6Ia/IQhCPB6nKEoURfgWRbfkK/l0OrXb7dlsZrPZIpHI2wU/vha322273fp8Pq/X+6+LXgij0Wi32+VyueVymc1m1+v1/zRsNhuCIFQqVSwWUyqVarV6Op1+Hs9xnCiKsOh+Emia/pyi0+mazSaE8HK59Pv9TCbjcDjy+fyL6FAohGEYwzD3+73RaJjN5mq1utlsptOpxWK5Xq/dbhd8vNq63+8ZhjEajbVabbFYGAyGRCJxPB5Pp1MwGOx0Os8Nk8mE4zipVDoYDFarVblcdjqdBEFgGIaiaKVSQRDE5XKBHpAAAHie93g85/PZbrfP53OKotLptEajsVqtrVZLJpPRNO33+w+HAwaABAAAISRJEsfxbDZbr9f1er1Go0EQJJVKDYfDXq9XKpU8Ho9cLn/aKgjCw9lAIFAoFMLh8Gw2e3T2+/2baCgUCp7no9EoSZLRaNRkMkEIWZbFcfyPv/0ijMfjh6HJZFKr1bIsi6KoRCIBfwH5brx/AseDLUJKQoGcAAAAAElFTkSuQmCC
// @author MkQtS
// @license MIT
// @homepage https://github.com/MkQtS/CNKI-Redirect
// @supportURL https://github.com/MkQtS/CNKI-Redirect/issues
// @run-at document-end
// @match *://*.cnki.net/*
// @match *://*.cnki.com.cn/*
// @exclude *://kns.cnki.net/kns8/*
// @exclude *://*.cnki.net/kcms/detail/frame/*
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
(function CNKI_Redirect(currentUrl) {
'use strict';
function IdentifyCase(url) {
const CNKICASES = ['error', 'kcms', 'kcms2', 'bianke', 'mall', 'space', 'urtpweb', 'wap', 'wenhua', 'xuewen', 'common'],
CNKIREGEXES = [
/^https?:\/\/[\w\.]+\.cnki\.net\/kcms\/detail\/error/,
/^https?:\/\/(\w+\.)?(global|kns|oversea|www)\.cnki\.net\/kcms\/detail\/detail\.aspx\?dbcode=\w+&filename=[\w\.]+$/,
/^https?:\/\/kns\.cnki\.net\/kcms2\/article\/abstract\?/,
/^https?:\/\/bianke\.cnki\.net\/web\/article\//,
/^https?:\/\/mall\.cnki\.net\/magazine\/article\//,
/^https?:\/\/\w+\.cnki\.com\.cn\/article\//,
/^https?:\/\/\w+\.cnki\.net\/urtpweb\/detail\?/,
/^https?:\/\/(read|wap)\.cnki\.net\/(((touch\/)?web\/\w+\/article\/)|(\w+-[\w\.]+\.htm))/,
/^https?:\/\/wh\.cnki\.net\/(m\/)?article\/detail\//,
/^https?:\/\/xuewen\.cnki\.net\/\w+-[\w\.]+\.htm/,
/^https?:\/\/([\w\.]+\.)?cnki\.net\/(law|kcms\d?)\/(article\/|detail(\?|\/detail)|doi\/)/
];
return CNKICASES[CNKIREGEXES.findIndex(cnkiReg => cnkiReg.test(url.toLowerCase()))] || 'skip';
}
const defFileID = { raw: ['clear', 'clear'], target: ['clear', 'clear'] };
function FetchFileID(plat, url) {
url = url.toLowerCase();
let dbcode, filename, fileID = defFileID;
switch (plat) {
case 'bianke': {
let dbinfo = (document.querySelector('.abstract.clearfix > .wrap > .aBtn') || document.querySelector('.articleInfo > .title > .clearfix > strong > a'))?.href.toLowerCase();
if (dbinfo) {
dbcode = dbinfo.replace(/^https?:\/\/bianke\.cnki\.net\/z\/download\/article\/[\w\.]+\/(\w+)\/.+$/, '$1').replace(/^https?:\/\/search\.cnki\.com\.cn\/(\w+)\/.+$/, '$1');
filename = url.replace(/^https?:\/\/bianke\.cnki\.net\/web\/article\/([\w\.]+)\.htm.*$/, '$1');
}
break;
} case 'kns': {
dbcode = document.getElementById('paramdbname')?.value.replace(/(auto|day|last|temp|total|\d+)/gi, '').replace(/^(\w{4})\w+$/, '$1') || document.getElementById('paramdbcode')?.value;
filename = document.getElementById('paramfilename')?.value;
break;
} case 'mall': {
dbcode = document.getElementById('articleType')?.value;
filename = document.getElementById('articleFileName')?.value;
if (!dbcode || !filename) {
let downParams = new URLSearchParams(document.getElementsByClassName('articledownload')[0]?.href.toLowerCase().split('?')[1]);
dbcode = downParams.get('dbtype');
filename = downParams.get('filename');
}
break;
} case 'space': {
let fileinfo = url.replace(/^https?:\/\/(\w+)\.cnki\.com\.cn\/article\/([\w\.-]+)\.htm.*$/, '$1-$2').replace(/^\w+-(\w+)(?:(?:-\d+)|(?:total))-([\w\.]+)$/, '$1-$2');
[dbcode, filename] = fileinfo.split('-');
break;
} case 'urtpweb': {
dbcode = document.getElementById('rescode')?.value;
filename = document.getElementById('filename')?.value;
break;
} case 'wap': {
dbcode = document.getElementById('a_download')?.dataset.type;
filename = document.getElementById('a_download')?.dataset.filename;
if (!dbcode || !filename) {
let fileinfo = url.replace(/^https?:\/\/\w+\.cnki\.net\/(?:touch\/)?(?:web\/)?(\S+)\.htm.*$/, '$1').replace(/^(\w+)\/article\/(?:\w*-)?([\w\.]+)$/, '$1-$2');
[dbcode, filename] = fileinfo.split('-');
const DBLINK = ['conference', 'dissertation', 'journal', 'newspaper', 'huiyi', 'lunwen', 'qikan', 'baozhi'],
DBREAL = ['CIPD', 'CDMD', 'CJFD', 'CCND', 'CIPD', 'CDMD', 'CJFD', 'CCND'];
dbcode = DBREAL[DBLINK.indexOf(dbcode)] || dbcode;
}
break;
} case 'wenhua': {
if (document.getElementById('journalimg') || document.getElementsByClassName('hero-cover-img').length) {
dbcode = 'CJFD';
filename = url.replace(/^https?:\/\/wh\.cnki\.net\/(?:m\/)?article\/detail\/([\w\.]+).*$/, '$1');
}
break;
} case 'xuewen': {
let fileinfo = url.replace(/^https?:\/\/xuewen\.cnki\.net\/(\w+-[\w\.]+)\.htm.*$/, '$1');
[dbcode, filename] = fileinfo.split('-');
break;
} case 'common': {
dbcode = document.getElementById('paramdbcode')?.value;
filename = document.getElementById('paramfilename')?.value;
if (!dbcode || !filename) {
let trackfile = (document.getElementById('addfavtokpc') || document.getElementById('SnapshotSearchButton'))?.onclick.toString();
if (trackfile) {
let fileinfo = trackfile.replace(/^[\S\s]+(?:AddFavToMyCnki|StartSnapShotSearch)\([^')]*'([^']+)'[^')]+'([^']+)'[^)]*\)[\S\s]+$/, '$1-$2');
[dbcode, filename] = fileinfo.split('-');
} else {
let urlParams = new URLSearchParams(url.split('?')[1]);
dbcode = urlParams.get('dbcode') || urlParams.get('dbname')?.replace(/(auto|day|last|temp|total|\d+)/g, '').replace(/^\w+(\w{4})$/, '$1');
filename = urlParams.get('filename');
}
}
break;
}
}
if (!dbcode || !filename) {
console.debug('[CNKI-Redirect] Insufficient file ID. dbcode: %s, filename: %s', dbcode, filename);
} else if (/^\w+$/.test(dbcode) && /^[\w\.]+$/.test(filename)) {
fileID = ((dbcode, filename) => {
let fileIDObj = { raw: [dbcode.toUpperCase(), filename.toUpperCase()] };
dbcode = fileIDObj.raw[0].replace(/^[A-Z]+_([A-Z]+)$/, '$1');
filename = fileIDObj.raw[1];
const cmnDB = ['CCJD', 'CCND', 'CDMD', 'CIPD', 'CJFD', 'CYFD'];
if (!cmnDB.includes(dbcode)) {
const oddDB = ['BNJK', 'BSFD', 'CACM', 'CDMH', 'CLKB', 'IPFD'],
badDB = ['CCVD', 'CISD', 'CLKLP', 'CPVD', 'READ', 'SCOD', 'SCPD', 'SMSD', 'SNAD', 'SOPD'];
if (oddDB.indexOf(dbcode) === badDB.indexOf(dbcode)) {
let dbKey = dbcode.replace(/^C(\w)?F(\w)\w*$/, 'to$2$1').replace(/^\w+(\w)$/, '$1');
const dbKeys = ['D', 'I', 'J', 'M', 'N', 'P', 'Y'],
key2cmn = ['CDMD', 'CIPD', 'CJFD', 'CDMD', 'CCND', 'CIPD', 'CYFD'];
dbcode = key2cmn[dbKeys.indexOf(dbKey)] || 'BAD-DB';
} else {
const odd2cmn = ['CJFD', 'CYFD', 'CJFD', 'CDMD', 'CDMD', 'CIPD'];
dbcode = odd2cmn[oddDB.indexOf(dbcode)] || 'BAD-DB';
}
console.debug('[CNKI-Redirect] Convert dbcode from %s to %s.', fileIDObj.raw[0], dbcode);
}
switch (dbcode) {
case 'BAD-DB': {
[dbcode, filename] = defFileID.target;
console.info('[CNKI-Redirect] The dbcode %s should be abandoned.', fileIDObj.raw[0]);
break;
} case 'CDMD': {
filename = filename.replace(/^([\w\.]+)$/, '$1.NH').replace(/^([\w\.]+?)(\.NH)+$/, '$1.nh');
break;
} case 'CJFD': {
fileIDObj.alter = ['CCJD', filename];
console.debug('[CNKI-Redirect] Set CCJD as alternate dbcode.');
break;
}
}
fileIDObj.target = [dbcode, filename];
return fileIDObj;
})(dbcode, filename);
} else {
console.debug('[CNKI-Redirect] Invalid file ID. dbcode: %s, filename: %s', dbcode, filename);
}
return fileID;
}
function GenerateKcmsUrl(fileID) { // maybe generate kcms2 link
let cnkiUrls = ['clear'], dbFiles = [fileID.alter, fileID.target].filter(dbfile => !!dbfile);
const kcmsHEAD = 'https://kns.cnki.net/kcms/detail/detail.aspx?';
dbFiles.forEach(dbfile => {
let fileparam = 'dbcode=' + dbfile[0] + '&filename=' + dbfile[1];
cnkiUrls.unshift(kcmsHEAD + fileparam);
});
return cnkiUrls;
}
let situation = IdentifyCase(currentUrl);
switch (situation) {
case 'skip': {
console.log('[CNKI-Redirect] Skipped on ' + currentUrl);
break;
} case 'error': {
let candidates = GM_getValue('candidates') || ['clear'];
if (candidates[0] !== 'clear') {
console.log('[CNKI-Redirect] Last target failed, try alternate...');
let targetUrl = candidates.shift();
GM_setValue('candidates', candidates);
window.location.replace(targetUrl);
} else {
let storedSrc = GM_getValue('source') || { sourceUrl: 'clear', fileID: defFileID };
if (storedSrc.sourceUrl !== 'clear') {
console.log('[CNKI-Redirect] All candidates failed. Go back to original page...');
GM_setValue('banRedirect', storedSrc.fileID.raw);
window.location.replace(storedSrc.sourceUrl);
} else {
console.log('[CNKI-Redirect] Original page not found, stay here.');
}
}
break;
} case 'kcms':
case 'kcms2': {
let storedSrc = GM_getValue('source') || { sourceUrl: 'clear', fileID: defFileID };
if (storedSrc.sourceUrl !== 'clear') {
GM_setValue('candidates', ['clear']);
GM_setValue('source', { sourceUrl: 'clear', fileID: defFileID });
}
let targetArea = document.getElementById('DownLoadParts')?.querySelector('.operate-btn') || document.getElementById('DownLoadParts')?.querySelector('.operate-left');
if (!targetArea) {
console.debug('[CNKI-Redirect] buttons area not found.');
} else {
let fileID = FetchFileID('kns', currentUrl);
if (fileID.target[0] !== 'clear') {
if (situation === 'kcms2') {
const kcmsBtnText = '🍋 打开KCMS', kcmsBtnTitle = 'KCMS链接不含跟踪参数,但页面可能缺少部分内容';
let kcmsUrl = GenerateKcmsUrl(fileID)[0];
const kcmsBtn = `<li class='btn-go2kcms'><a id='go2kcms' href='${kcmsUrl}' target='_blank' title='${kcmsBtnTitle}' style='background-color: #6a8; color: #fff'>${kcmsBtnText}</a></li>`;
targetArea.insertAdjacentHTML('beforeend', kcmsBtn);
} else if (fileID.target[0] === 'CDMD') {
let pdfLink = 'https://pay.cnki.net/zscsdoc/download?flag=cnkispace&plat=cnkispace&filename=' + fileID.target[1] + '&dbtype=CDMD&dtype=pdf';
const pdfBtn = `<li class='btn-dlpdf'><a target='_blank' name='pdfDown' href='${pdfLink}'><i></i>PDF下载</a></li>`;
targetArea.insertAdjacentHTML('beforeend', pdfBtn);
}
}
if (storedSrc.sourceUrl !== 'clear') {
const srcBtn = `\n<li class='btn-go2src'><a id='go2src' title='${storedSrc.sourceUrl}' style='background-color: #9a5; color: #fff'>🥝 打开源页面</a></li>`;
targetArea.insertAdjacentHTML('beforeend', srcBtn);
document.getElementById('go2src').addEventListener('click', () => {
GM_setValue('banRedirect', storedSrc.fileID.raw);
window.open(storedSrc.sourceUrl, '_blank');
});
}
}
console.log('[CNKI-Redirect] Ideal case, done.');
break;
} default: {
console.info('[CNKI-Redirect] Rule for %s matched.', situation);
let fileID = FetchFileID(situation, currentUrl);
if (fileID.target[0] !== 'clear') {
console.log('[CNKI-Redirect] Got file ID: dbcode=%s&filename=%s', fileID.target[0], fileID.target[1]);
let banCheck = GM_getValue('banRedirect') || ['clear', 'clear'];
if (fileID.raw.toString() === banCheck.toString()) {
GM_setValue('banRedirect', ['clear', 'clear']);
console.log('[CNKI-Redirect] Redirect for this file is not allowed. Refresh to try again.');
} else {
window.stop();
GM_setValue('source', { sourceUrl: currentUrl, fileID: fileID });
let candidates = GenerateKcmsUrl(fileID), targetUrl = candidates.shift();
GM_setValue('candidates', candidates);
window.location.replace(targetUrl);
}
} else {
console.log('[CNKI-Redirect] No proper file ID found.');
}
break;
}
}
})(window.location.href);