// ==UserScript== // @name 导出文献的Bibtex(镜像站) // @namespace jw23 // @version 0.1.12 // @description 1. 支持从知网和谷歌学术镜像导出BiBTex。2. 从google scholar(谷歌学术)导入文献到petal文献库。 // @author jw23 // @match https://kns.cnki.net/kns8/defaultresult/index // @match https://kns.cnki.net/KXReader/Detai* // @match https://scholar.google.com/scholar* // @match https://scholar.google* // @match https://s.wanfangdata.com.cn/paper* // @match https://*.studiodahu.com* // @match https://usercontent.beijingbang.top* // @match https://*/scholar* // @match https://cite.petal.org* // @match https://www.bing.com* // @match https://usercontent.beijingbang.top* // @match https://*.sciencemag.org/* // @match http*://*.webofknowledge.com/* // @match https://academic.oup.com/* // @match https://academic.microsoft.com/* // @match https://dl.acm.org/doi/* // @match https://ieeexplore.ieee.org/* // @match https://journals.sagepub.com/* // @match https://link.springer.com/* // @match https://onlinelibrary.wiley.com/doi/* // @match https://psycnet.apa.org/* // @match https://pubmed.ncbi.nlm.nih.gov/* // @match https://pubs.rsc.org/* // @match https://pubs.acs.org/doi/* // @match https://pubsonline.informs.org/doi/abs/* // @match https://schlr.cnki.net/Detail/index/* // @match https://schlr.cnki.net//Detail/index/* // @match https://xueshu.baidu.com/usercenter/paper/* // @match https://www.ingentaconnect.com/* // @match https://www.jstor.org/* // @match https://www.nature.com/* // @match https://www.ncbi.nlm.nih.gov/* // @match https://www.researchgate.net/* // @match https://www.sciencedirect.com/* // @match http://www.socolar.com/* // @match https://www.scinapse.io/* // @match https://www.science.org/* // @match https://www.tandfonline.com/* // @match https://www.webofscience.com/wos/* // @require https://raw.githubusercontent.com/ORCID/bibtexParseJs/master/bibtexParse.js // @require https://raw.githubusercontent.com/TheWindRises-2/coco-message/main/coco-message.min.js // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @grant GM_setClipboard // @grant unsafeWindow // @grant GM_getValue // @grant GM_setValue // @grant GM_openInTab // @license MIT // ==/UserScript== /* ==UserConfig== configure: auth: title: 令牌 description: 请输入petal的令牌 type: text id: title: 组织ID description: 请输入您的组织ID type: select bind: $organizations userId: title: 用户ID description: 这是用户ID update: title: 每次访问petal时更新令牌 description: 每次访问将会自动获取令牌和组织ID type: checkbox default: true refStyle: title: 引用格式 description: 引用的格式 type: select bind: $refStyles mainUrl: title: 谷歌学术URL description: 谷歌学术的地址 type: text default: https://scholar.google.com.hk/scholar isCopy: title: 是否复制bibex到剪切板 description: 选中即每次导入自动复制bibtex到剪切板 type: checkbox default: false remainTime: title: 悬浮窗口停留时间 description: 按Shift+S时弹窗停留时间 type: number unit: 毫秒 min: 5000 max: 15000 default: 8000 ==/UserConfig== */ let Authorization = GM_getValue("configure.auth", ""); let organization_name = GM_getValue("configure.id", ""); (function () { 'use strict'; document.querySelectorAll('div.MuiListItemText-root>p.MuiTypography-root.MuiTypography-body2.org-name').forEach((e) => { e.click = () => { refresh_data() } }) GM_registerMenuCommand("去Google Scholar查询", () => { goGoogleScholar() }) document.querySelectorAll('div.MuiListItemText-root>p.MuiTypography-root.MuiTypography-body2.org-name').forEach((e)=>{ e.onclick=()=>{ refresh_data() } }) const refresh_data = () => { var connection = indexedDB.open('localforage', 2) connection.onsuccess = (e) => { var database = e.target.result; var transaction = database.transaction(['keyvaluepairs']); var objectStore = transaction.objectStore('keyvaluepairs'); var req = objectStore.get('persist:root') req.onsuccess = (e) => { let data = JSON.parse(e.target.result) let user = JSON.parse(data.session).user let token = user['access_token'] GM_setValue("configure.userId", user.id) let documents = []; for (let org of user.organizations) { documents.push({ "id": org.id, "value": org.name }) } GM_setValue("documents", documents) GM_setValue("configure.id", findOrgName(JSON.parse(data.session).currentOrganizationId)) cocoMessage.info(3000, `Current organization has been changed to ${findOrgName(JSON.parse(data.session).currentOrganizationId)}`) GM_setValue("organizations", documents.map(v => v.value)) /* GM_xmlhttpRequest({ url: `https://cite.petal.org/api/user/${data.id}/organization/list`, method: "GET", headers: { "Authorization": Authorization, }, onload: response => { let documents = []; let lists = JSON.parse(response.response); for (let item of lists) { documents.push({ "id": item.id, "value": item.name }) } GM_setValue("documents", documents) GM_setValue("organizations", documents.map(v => v.value)) } }) */ GM_setValue("configure.auth", `Bearer ${token}`) cocoMessage.info(3000, "Get the token and id successfully") } } } refresh_data() document.onkeydown = (e) => { if (e.shiftKey && e.key == "S") { goGoogleScholar() } } const [open, closeDialog] = createLoading() const lauch_in_cnki = () => { setTimeout(() => { waitUnUtil("div.divLoading", () => { console.log("开始插入元素") waitUtil(".cbItem", () => { insertToHTML() }, 3000) }, 8000) }, 1000) } if (document.URL.indexOf("cnki") != -1) { lauch_in_cnki() document.onkeydown = (e) => { if (e.key == "Enter") { lauch_in_cnki() } if (e.ctrlKey && e.shiftKey && e.key == "C") { GM_setClipboard(document.getSelection().toString()) } } document.querySelector("input.search-btn").onclick = () => { lauch_in_cnki() } } else if (document.URL.indexOf("scholar.google") != -1) { waitUtil(".gs_r.gs_or.gs_scl", () => { let items = document.querySelectorAll(".gs_r.gs_or.gs_scl") for (let item of items) { let id = item.querySelector('h3>a').id let box = item.querySelector('div.gs_fl:nth-last-child(1)') let btn = document.createElement('a') btn.style = "cursor:pointer" btn.text = "import to petal" let btn2 = document.createElement('a') btn2.style = "cursor:pointer" btn2.text = "go to cnki" let btn3 = document.createElement('a') btn3.style = "cursor:pointer" btn3.text = "copy reference" btn2.onclick = () => { GM_setClipboard(item.querySelector('h3').textContent) window.open("https://kns.cnki.net/kns8/defaultresult/index"); } btn3.onclick = () => { getBixTexPageFromGoogle(id).then(page => { let dom = document.createElement("div") dom.innerHTML = page; let refs = dom.querySelectorAll("#gs_citt tr") let styles = [] for (let ref of refs) { let v = ref.querySelector('.gs_citr').textContent.trim(); let styleName = ref.querySelector('.gs_cith').textContent.trim(); if (GM_getValue("configure.refStyle", "GB/T 7714") == styleName) { GM_setClipboard(v) cocoMessage.info(3000, "Copy reference successfullly") } styles.push({ styleName: styleName, ref: v }) } GM_setValue("refStyles", styles.map(s => s.styleName)) }).catch(err => { cocoMessage.error("复制Ref失败:" + err, 3000); }) } btn.onclick = () => { open() getBixTexPageFromGoogle(id).then(page => { let dom = document.createElement("div") dom.innerHTML = page; let lists = dom.querySelector("#gs_citi") let first = lists.querySelector("a").href GM_xmlhttpRequest({ url: first, method: "GET", onload: (response) => { uploadToPetal(response.responseText) closeDialog() if (GM_getValue("configure.isCopy", false)) GM_setClipboard(response.responseText) }, onerror: () => { closeDialog() } }) }).catch(err => { cocoMessage.error("获取BibTex失败:" + err, 3000); }) } box.appendChild(btn) box.appendChild(btn2) box.appendChild(btn3) } }, 5000) } else if (document.URL.indexOf("wanfangdata") != -1) { waitUtil("div.normal-list", () => { let list = document.querySelectorAll("div.normal-list"); for (let article of list) { let title = article.querySelector("span.title").textContent; let button_group = article.querySelector("div.button-list>div") let btn_google = document.createElement("div") btn_google.className = "wf-list-button" btn_google.textContent = "去Google学术查看" btn_google.onclick = () => { window.open(`https://scholar.google.com.hk/scholar?hl=zh-CN&as_sdt=0%2C5&q=${title}&btnG=`) } button_group.appendChild(btn_google) } }, 8000) } else { waitUtil(".gs_r.gs_or.gs_scl", () => { let items = document.querySelectorAll(".gs_r.gs_or.gs_scl") for (let item of items) { let id = item.querySelector('h3>a').id let box = item.querySelector('div.gs_fl:nth-last-child(1)') let btn = document.createElement('a') btn.style = "cursor:pointer" btn.text = "import to petal" let btn2 = document.createElement('a') btn2.style = "cursor:pointer" btn2.text = "go to cnki" btn2.onclick = () => { GM_setClipboard(item.querySelector('h3').textContent) window.open("https://kns.cnki.net/kns8/defaultresult/index"); } let btn3 = document.createElement('a') btn3.style = "cursor:pointer" btn3.text = "copy reference" btn3.onclick = () => { getBixTexPageFromGoogle(id).then(page => { let dom = document.createElement("div") dom.innerHTML = page; let refs = dom.querySelectorAll("#gs_citt tr") let styles = [] for (let ref of refs) { let v = ref.querySelector('.gs_citr').textContent.trim(); let styleName = ref.querySelector('.gs_cith').textContent.trim(); if (GM_getValue("configure.refStyle", "GB/T 7714") == styleName) { GM_setClipboard(v) cocoMessage.info(3000, "Copy reference successfullly") } styles.push({ styleName: styleName, ref: v }) } GM_setValue("refStyles", styles.map(s => s.styleName)) }).catch(err => { cocoMessage.error("复制Ref失败:" + err, 3000); }) } btn.onclick = () => { open() getBixTexPageFromGoogle(id).then(page => { let dom = document.createElement("div") dom.innerHTML = page; let first = dom.querySelector("#gs_citi>a.gs_citi").href GM_xmlhttpRequest({ url: first, method: "GET", onload: (response) => { uploadToPetal(response.responseText) closeDialog() if (GM_getValue("configure.isCopy", false)) GM_setClipboard(response.responseText) }, onerror: () => { closeDialog() } }) }).catch(err => { cocoMessage.error("Failed in getting bibtex:" + err, 3000); }) } box.appendChild(btn) box.appendChild(btn2) box.appendChild(btn3) } }, 5000) } setInterval(() => { closeDialog() }, 5000) })(); function createLoading() { let loading = document.createElement("div"); loading.style = `position: fixed; left: 0; right: 0; bottom: 0; top: 0; background: rgb(0 0 0 / 70%); display: flex; justify-content: center; align-items: center; z-index: 9999` loading.id = "jw-loading" loading.className = "jw-hidden" loading.innerHTML = ` ` document.body.appendChild(loading) let dialog = document.querySelector('#jw-loading') return [() => { dialog.className = "" }, () => { dialog.className = "jw-hidden" }] } function insertToHTML() { const [open, closeDialog] = createLoading() let items = document.querySelectorAll('.cbItem') for (let checkbox of items) { checkbox.onchange = (e) => { open() let filename = e.target.value; getBixTex(filename).then(bib => { cocoMessage.info(3000, "BiBTex has been copied to clipboard"); closeDialog() if (GM_getValue("configure.isCopy", false)) GM_setClipboard(bib) }).catch((err) => { cocoMessage.error("获取BibTex失败:" + err, 3000); closeDialog() }) } } let box = document.querySelector("#batchOpsBox") let li = document.createElement("li") li.textContent = "导出所有的BibTex" li.onclick = () => { let total = "" for (let checkbox of items) { if (checkbox.checked) { getBixTex(checkbox.value).then(bib => { total += bib; if (GM_getValue("configure.isCopy", false)) GM_setClipboard(total) }) } } cocoMessage.info(3000, "BiBTex全部导出成功"); } box.appendChild(li) let lines = document.querySelectorAll("tbody>tr") for (let line of lines) { let icon = document.createElement('td'); icon.textContent = "BibTex" icon.style = "cursor:pointer" let checkbox = line.querySelector('.cbItem') icon.onclick = () => { getBixTex(checkbox.value).then(bib => { uploadToPetal(bib) }) } line.appendChild(icon) } } function getBixTex(filename) { // GM_xmlhttpRequest({ // url: "https://kns.cnki.net/dm/api/ShowExport", // headers: { "Content-Type": "application/x-www-form-urlencoded" }, // data: encodeURIComponent(`FileName=${filename}&DisplayMode=BibTex&OrderParam=0&OrderType=desc&SelectField=&PageIndex=1&PageSize=20&language=&uniplatform=NZKPT&random=${Math.random()}`), // onload: (response) => { // console.log("得到结果:"); // console.log(response.responseText) // }, // onerror: (err) => { // console.log("获取BixTex失败", err) // } // }) return fetch("https://kns.cnki.net/dm/api/ShowExport", { "headers": { "accept": "text/plain, */*; q=0.01", "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", "content-type": "application/x-www-form-urlencoded", "sec-ch-ua": "\"Chromium\";v=\"110\", \"Not A(Brand\";v=\"24\", \"Microsoft Edge\";v=\"110\"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "\"Windows\"", "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-origin", "x-requested-with": "XMLHttpRequest" }, "referrerPolicy": "strict-origin-when-cross-origin", "body": `FileName=${filename}&DisplayMode=BibTex&OrderParam=0&OrderType=desc&SelectField=&PageIndex=1&PageSize=20&language=&uniplatform=NZKPT&random=0.6662329504606448`, "method": "POST", "mode": "cors", "credentials": "include" }).then(respose => respose.text()).then(result => { let dom = document.createElement("div") dom.innerHTML = result; let bib = dom.querySelector("li") // console.log(bib.textContent) return bib.textContent }) } function waitUtil(ele, callback, timeout) { let success = false; let id = setInterval(function () { let target = document.querySelector(ele) if (target != null) { success = true clearInterval(id); callback(target) } }, 100) setTimeout(() => { if (!success) { clearInterval(id) console.log("[何碧]页面超时") } }, timeout) } function waitUnUtil(ele, callback, timeout) { let success = false; let id = setInterval(function () { let target = document.querySelector(ele) if (target == null) { success = true clearInterval(id); callback(target) } }, 100) setTimeout(() => { if (!success) { clearInterval(id) cocoMessage.error("[何碧]页面超时", 3000); console.log("[何碧]页面超时") } }, timeout) } function getBixTexPageFromGoogle(id) { return fetch(`/scholar?q=info:${id}:scholar.google.com/&output=cite&scirp=1&hl=zh-CN`, { "headers": { "accept": "*/*", "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", "sec-ch-ua": "\"Chromium\";v=\"110\", \"Not A(Brand\";v=\"24\", \"Microsoft Edge\";v=\"110\"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "\"Windows\"", "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-origin", "x-requested-with": "XHR" }, "referrerPolicy": "origin-when-cross-origin", "body": null, "method": "GET", "mode": "cors", "credentials": "include" }).then(response => response.text()).then(result => { return result }) } function uploadToPetal(bib) { if (GM_getValue("configure.auth", "") == "" || GM_getValue("configure.id", "") == "") { cocoMessage.info(3000, "您没有填写petal的令牌和组织ID", function () { GM_openInTab("https://cite.petal.org", { active: true, insert: true }) }) return } let bibJSON = bibtexParse.toJSON(bib); let subs = [] for (let i = 0; i < bibJSON.length; i++) { console.log(bibJSON[i]) subs.push({ "meta_json": JSON.stringify({ "title": bibJSON[i].entryTags.title, "author": bibJSON[i].entryTags.author, "journal": bibJSON[i].entryTags.journal, "volume": bibJSON[i].entryTags.volume, "number": bibJSON[i].entryTags.number, "pages": bibJSON[i].entryTags.pages, "year": bibJSON[i].entryTags.year, "entrytype": bibJSON[i].entryType, "citekey": bibJSON[i].citationKey, "editor": "" }) }) } GM_xmlhttpRequest({ url: `https://cite.petal.org/api/organization/${getCurrentID()}/document/list`, method: "GET", headers: { "Authorization": Authorization, }, onload: (response) => { let lists = JSON.parse(response.response) // console.log(Authorization) console.log(lists) for (let item of lists) { if (!bibJSON[0]) { cocoMessage.error("失败,可能需要验证是否为人机", 3000) } if (item.meta.citekey == bibJSON[0].citationKey && !item.is_trash) { cocoMessage.warning(`导入重复引用(${GM_getValue("configure.id", "")})`, 3000) return } } GM_xmlhttpRequest({ url: "https://cite.petal.org/api/document/create_stubs", method: "POST", headers: { "Authorization": Authorization, "Content-Type": "application/json" }, data: JSON.stringify({ "organization_id": getCurrentID(), "stubs": subs }), onload: response => { cocoMessage.info(3000, `导入成功(${GM_getValue("configure.id", "")})`, function () { console.log(response.responseText, ":", GM_getValue("configure.id", "")) }) } }) } }) } function goGoogleScholar() { if (document.getSelection().toString().trim() != "") { let scholarWindow = window.open(`${GM_getValue("configure.mainUrl", "https://scholar.google.com.hk/scholar")}?q=${document.getSelection().toString()}`, "scholarWindow", "popup,width=600,height=1000"); setTimeout(() => { scholarWindow.close() }, GM_getValue("configure.remainTime", 5000)) } else { navigator.clipboard .readText() .then((v) => { let scholarWindow = window.open(`${GM_getValue("configure.mainUrl", "https://scholar.google.com.hk/scholar")}?q=${v}`, "scholarWindow", "popup,width=600,height=1000"); setTimeout(() => { scholarWindow.close() }, GM_getValue("configure.remainTime", 5000)) }).catch(_ => { cocoMessage.error("document不在焦点", 3000) }) } } function getCurrentID() { for (let doc of GM_getValue("documents", "")) { if (GM_getValue("configure.id", "") == doc.value) { return doc.id } } return "" } function findOrgName(id) { for (let doc of GM_getValue("documents", "")) { if (id == doc.id) { return doc.value } } return GM_getValue("documents", "")[1].value }