// ==UserScript==
// @name 导出文献的Bibtex(镜像站)
// @namespace jw23
// @version 0.1.9
// @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://usercontent.beijingbang.top*
// @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_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
==/UserConfig== */
let Authorization = GM_getValue("configure.auth", "");
let organization_name = GM_getValue("configure.id", "");
(function () {
'use strict';
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
GM_setValue("configure.id", findOrgName(JSON.parse(data.session).currentOrganizationId))
cocoMessage.info(3000, `修改当前组织为${findOrgName(JSON.parse(data.session).currentOrganizationId)}`)
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("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, "成功获取令牌和组织ID")
}
}
}
refresh_data()
document.onkeydown = (e) => {
if (e.shiftKey && e.key == "L") {
// e.preventDefault()
console.log("重新获取数据")
refresh_data()
}
if (e.shiftKey && e.key == "S") {
navigator.clipboard
.readText()
.then((v) => {
console.log("正在搜索:", v)
window.location.search = `q=${v}`
})
}
}
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 = "导出为BiBTex"
let btn2 = document.createElement('a')
btn2.style = "cursor:pointer"
btn2.text = "去知网"
btn2.onclick = () => {
GM_setClipboard(item.querySelector('h3').textContent)
window.open("https://kns.cnki.net/kns8/defaultresult/index");
}
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()
GM_setClipboard(response.responseText)
},
onerror: () => {
closeDialog()
}
})
}).catch(err => {
cocoMessage.error("获取BibTex失败:" + err, 3000);
})
}
box.appendChild(btn)
box.appendChild(btn2)
}
}, 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 = "导出为BiBTex"
let btn2 = document.createElement('a')
btn2.style = "cursor:pointer"
btn2.text = "去知网"
btn2.onclick = () => {
GM_setClipboard(item.querySelector('h3').textContent)
window.open("https://kns.cnki.net/kns8/defaultresult/index");
}
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()
GM_setClipboard(response.responseText)
},
onerror: () => {
closeDialog()
}
})
}).catch(err => {
cocoMessage.error("获取BibTex失败:" + err, 3000);
})
}
box.appendChild(btn)
box.appendChild(btn2)
}
}, 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已经复制到剪切板");
closeDialog()
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;
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("导入重复引用", 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, "导入成功", function () {
console.log(response.responseText)
})
}
})
}
})
}
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
}