sciDownload

任何页面,sci论文下载,显示期刊信息

  1. // ==UserScript==
  2. // @name sciDownload
  3. // @namespace http://tampermonkey.net/
  4. // @version 4.1
  5. // @description 任何页面,sci论文下载,显示期刊信息
  6. // @author Polygon
  7. // @icon data:image/png;base64,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
  8. // @match *://*/*
  9. // @grant unsafeWindow
  10. // @grant GM_xmlhttpRequest
  11. // @grant GM_download
  12. // @grant GM_addStyle
  13. // @grant GM_setValue
  14. // @grant GM_getValue
  15. // @grant GM_openInTab
  16. // @grant unsafeWindow
  17. // @grant window.close
  18. // @connect *
  19. // @run-at document-idlm
  20. // ==/UserScript==
  21. (function() {
  22. 'use strict'
  23. const notification = (function() {
  24. 'use strict';
  25. GM_addStyle(`
  26. #notification {
  27. box-sizing: border-box;
  28. position: fixed;
  29. left: calc(50% - 365.65px / 2);
  30. display: flex;
  31. flex-direction: row;
  32. align-items: center;
  33. justify-content: center;
  34. height: 50px;
  35. background-color: #ff7675;
  36. border-radius: 50px;
  37. padding: 0 0px 0px 20px;
  38. top: -50px;
  39. transition: top .5s ease-out;
  40. z-index: 9999999999;
  41. }
  42. #notification .content {
  43. display: flex;
  44. align-items: center;
  45. justify-content: center;
  46. color: white;
  47. font-size: 25px;
  48. }
  49. #notification .closeBox {
  50. margin: 0 10px;
  51. transform: rotate(90deg);
  52. cursor: pointer;
  53. }
  54. #notification .closeBox .progress {
  55. margin: 0 10px;
  56. cursor: pointer;
  57. }
  58. #notification .closeBox .progress .circle {
  59. stroke-dasharray: 100;
  60. animation: progressOffset 0s linear;
  61. }
  62. @keyframes progressOffset {
  63. from {
  64. stroke-dashoffset: 100;
  65. }
  66. to {
  67. stroke-dashoffset: 0;
  68. }
  69. }
  70. `)
  71. return {
  72. open(info, timeout, autoClose=true) {
  73. let eles = document.querySelectorAll('#notification')
  74. for (let i=0;i<eles.length;i++) {
  75. this.close(eles[i])
  76. }
  77. this.box = document.createElement('div')
  78. this.box.setAttribute('id', 'notification')
  79. this.box.innerHTML = `
  80. <div class="content"></div>
  81. <svg class="closeBox" width="40" height="40">
  82. <g class="close" style="stroke: white; stroke-width: 2; stroke-linecap: round;">
  83. <line x1="13" y1="13" x2="27" y2="27"/>
  84. <line x1="13" y1="27" x2="27" y2="13"/>
  85. </g>
  86. <g class="progress" fill="transparent" stroke-width="3">
  87. <circle class="background" cx="20" cy="20" r="16" stroke="rgba(255,255,255,0.15)"/>
  88. <circle class="circle" cx="20" cy="20" r="16" stroke="rgba(255,255,255,1)"/>
  89. </g>
  90. </svg>
  91. `
  92. document.body.appendChild(this.box)
  93. this.box.querySelector('.content').innerHTML = info
  94. let width = getComputedStyle(this.box).width
  95. this.box.style.left = `clac(50%-${width}/2)`
  96. this.box.querySelector('.closeBox .progress .circle').style['animation-duration'] = `${timeout}s`
  97. this.box.style.top = '100px'
  98. this.box.querySelector('.closeBox .progress').addEventListener('click', () => {
  99. console.log('you close...')
  100. this.close()
  101. console.log('you clear...')
  102. })
  103. if (autoClose) {
  104. setTimeout(() => {
  105. console.log('timeout close...')
  106. this.close()
  107. console.log('timeout clear ...')
  108. }, timeout * 1000)
  109. }
  110. },
  111. close(ele=null) {
  112. if (!ele) {ele=this.box}
  113. ele.style['transition-duration'] = '.23s'
  114. ele.style['transition-timing-function'] = 'eaer-out'
  115. ele.style.top = '-50px'
  116. setTimeout(() => {
  117. try {
  118. document.body.removeChild(this.box)
  119. } catch {
  120. console.log('clear')
  121. }
  122. }, 1000)
  123. }
  124. }
  125. })();
  126.  
  127. const utils = {
  128. api: 'http://muise.icu:5000/sciDownload',
  129. doiRegex: new RegExp(/10\.\d{4,9}\/[-\._;\(\)\/:A-z0-9]+/),
  130. pdfRegex: /(content-type|Content-Type).+(pdf|binary|application|stream)/g,
  131. timeout: 25,
  132. autoMax: {b: true, time: 1},
  133. scihubURL: 'sci-hub.ee',
  134. // 适配ipad,userscript暂不支持GM_getValue,GM_setValue
  135. get switchState() {
  136. if (typeof(GM_getValue) == 'undefined') {
  137. return this._switchSate
  138. } else {
  139. return GM_getValue('sciDownload-state', 'max')
  140. }
  141. },
  142. set switchState(value) {
  143. if (typeof(GM_setValue) == 'undefined') {
  144. this._switchSate = value
  145. } else {
  146. GM_setValue('sciDownload-state', value)
  147. }
  148. },
  149. _switchSate: 'max',
  150. color:
  151. {
  152. success: '#e74c3c',
  153. flash: '#00b894',
  154. fail: '#2c3e50'
  155. },
  156. svg:
  157. {
  158. doi: `<svg class="progressBox" width="40" height="40">
  159. <g fill="transparent" stroke-width="2.5">
  160. <circle class="progress-background" cx="20" cy="20" r="11" stroke="rgba(255,255,255,0.23)"/>
  161. <circle class="progress" cx="20" cy="20" r="11" stroke="rgba(255,255,255,1)" stroke-linecap="round"/>
  162. </g>
  163. </svg>`,
  164. pdf: `<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2519" width="30" height="30"><path d="M478.08 192.192a58.88 58.88 0 0 1 46.208 23.168c23.104 32.064 21.312 99.84-8.832 199.68A536.832 536.832 0 0 0 625.664 557.44c37.312-7.04 74.688-12.416 112-12.416 83.52 1.792 96 41.024 94.144 64.128 0 60.544-58.688 60.544-88.896 60.544v0.064a226.048 226.048 0 0 1-131.52-53.504c-72.832 16-142.144 39.168-211.456 67.712C344.96 782.08 293.312 832 248.96 832c-8.96 0-19.584-1.792-26.752-7.168A52.48 52.48 0 0 1 192 776.704c0-16 3.52-60.608 172.352-133.568a1267.776 1267.776 0 0 0 94.208-221.056c-21.312-42.752-67.584-147.84-35.584-201.344 10.688-19.648 32-30.272 55.168-28.544z m-118.4 475.84c-42.688 20.736-95.232 58.368-90.112 82.368 3.392 16 21.888 10.56 55.424-16.384 21.056-27.072 24.512-41.088 34.688-65.984z m386.304-79.808c-17.152 0-32.384 0-49.536 7.68 19.072 19.2 29.504 28.608 48.64 32.448 13.312 3.84 39.104 10.752 46.72-9.28 7.68-19.968-7.616-30.848-45.824-30.848zM494.08 492.8a2572.16 2572.16 0 0 1-46.592 104.704l104.704-34.88c-21.76-22.272-41.344-43.392-58.112-69.824z m-16.64-223.488c-10.496 1.664-16.192 15.04-18.88 24.512-5.504 27.008 8.96 57.088 27.072 78.528 14.912-22.528 18.688-43.328 12.928-73.92-6.976-20.48-14.016-30.208-21.12-29.12z" fill="#ffffff" p-id="2520"></path></svg>`,
  165. switch: `<svg width="40" height="40">
  166. <g stroke="white" stroke-width="3" stroke-linecap="round">
  167. <line x1="10" y1="20" x2="30" y2="20"/>
  168. <line class="switch" x1="10" y1="20" x2="30" y2="20"/>
  169. </g>
  170. </svg>`
  171. },
  172. style() {
  173. let div = document.createElement('div')
  174. div.innerHTML = this.svg.doi
  175. div.style.opacity = '0'
  176. document.body.appendChild(div)
  177. try{
  178. this.progressTotaLength = div.querySelector('.progress').getTotalLength()
  179. } catch {
  180. this.progressTotaLength = 68.66967010498047
  181. }
  182. document.body.removeChild(div)
  183. return `
  184. #sciDownloadBox {
  185. display: flex;
  186. position: fixed;
  187. height: 40px;
  188. bottom: 75px;
  189. font-family: NexusSans,Arial,Helvetica,Lucida Sans Unicode,Microsoft Sans Serif,Segoe UI Symbol,STIXGeneral,Cambria Math,Arial Unicode MS,sans-serif;
  190. font-size: 18px;
  191. cursor: pointer;
  192. box-shadow: 0px 0px 20px rgba(0, 0, 0, .1);
  193. transition: left .23s ease-out, opacity .23s, right .23s ease-out;
  194. z-index: 9999999999;
  195. }
  196. #sciDownloadBox * {
  197. box-sizing: border-box;
  198. }
  199. #sciContent {
  200. position: relative;
  201. overflow: hidden;
  202. box-sizing: border-box;
  203. display: flex;
  204. height: 40px;
  205. align-items: center;
  206. justify-content: space-around;
  207. vertical-align: middle;
  208. white-space: nowrap;
  209. color: white;
  210. background-color: ${this.color.fail};
  211. opacity: 0.72;
  212. transition: width .23s ease-out, opacity .23s, background-color .23s;
  213. }
  214. #sciContent[loading] #sciState .progress {
  215. opacity: 0;
  216. }
  217. #sciContent[loading]::before {
  218. content: '';
  219. position: absolute;
  220. left: 0;
  221. top: 0;
  222. width: 100%;
  223. height: 100%;
  224. opacity: 0;
  225. background-color: white;
  226. z-index: -1;
  227. animation: loading 2.3s linear infinite;
  228. }
  229. @keyframes loading {
  230. from {
  231. opacity: 0;
  232. }
  233. 50% {
  234. opacity: 0.3;
  235. }
  236. to {
  237. opacity: 0;
  238. }
  239. }
  240. #sciSwitch {
  241. width: 40px;
  242. height: 40px;
  243. color: white;
  244. background-color: #00b894;
  245. opacity: 0.72;
  246. transition: width .23s ease-out, opacity .23s;
  247. z-index: 1;
  248. }
  249. #sciContent #sciState {
  250. position: relative;
  251. overflow: hidden;
  252. display: flex;
  253. align-items: center;
  254. justify-content: center;
  255. width: 40px;
  256. height: 40px;
  257. opacity: 1;
  258. }
  259. #sciContent #sciText {
  260. position: relative;
  261. overflow: hidden;
  262. height: 100%;
  263. display: flex;
  264. align-items: center;
  265. justify-content: center;
  266. color: white;
  267. padding-left: 5px;
  268. padding-right: 10px;
  269. opacity: 1;
  270. text-decoration: none;
  271. transition: width .23s ease-out;
  272. }
  273. #sciContent:hover {
  274. opacity: 0.99 !important;
  275. }
  276. #sciSwitch:hover {
  277. opacity: 1 !important;
  278. }
  279. /* left svg progress */
  280. #sciContent #sciState .progressBox {
  281. transform: rotate(-90deg);
  282. }
  283. #sciContent #sciState .progress {
  284. stroke-dasharray: ${this.progressTotaLength};
  285. stroke-dashoffset: ${this.progressTotaLength};
  286. transition: stroke-dashoffset .23s linear;
  287. }
  288. #sciContent[progress] #sciState .progress {
  289. animation: progressOffset ${this.timeout}s linear forwards;
  290. }
  291. @keyframes rotator {
  292. from {
  293. transform: rotate(-90deg);
  294. }
  295. to {
  296. transform: rotate(180deg);
  297. }
  298. }
  299. #sciContent[download-noprogress] #sciState .progressBox {
  300. animation: rotator 2.3s linear infinite;
  301. }
  302. @keyframes dash {
  303. from {
  304. stroke-dashoffset: ${this.progressTotaLength};
  305. }
  306. 50% {
  307. stroke-dashoffset: ${this.progressTotaLength / 4};
  308. transform:rotate(135deg);
  309. }
  310. to {
  311. stroke-dashoffset: ${this.progressTotaLength};
  312. transform:rotate(450deg);
  313. }
  314. }
  315. #sciContent[download-noprogress] #sciState .progress {
  316. stroke-dashoffset: 0;
  317. transform-origin: center;
  318. animation: dash 2.3s ease-in-out infinite;
  319. }
  320. @keyframes progressOffset {
  321. from {
  322. stroke-dashoffset: ${this.progressTotaLength};
  323. }
  324. to {
  325. stroke-dashoffset: 0;
  326. }
  327. }
  328. @keyframes progressRecover {
  329. to {
  330. stroke-dashoffset: ${this.progressTotaLength};
  331. }
  332. }
  333. /* progress animation */
  334. @keyframes progress {
  335. to {
  336. width: 100%;
  337. }
  338. }
  339. /* switch button animation */
  340. #sciSwitch svg .switch {
  341. transform: rotate(0deg);
  342. transform-origin: center center;
  343. transition: transform .23s ease-out .15s;
  344. }
  345. /* ripple effect */
  346. #sciDownloadBox .ripple {
  347. position: absolute;
  348. background: #fff;
  349. transform: translate(-50%, -50%);
  350. pointer-events: none;
  351. border-radius: 50%;
  352. animation: ripple 1s linear;
  353. }
  354. @keyframes ripple{
  355. from {
  356. width: 0px;
  357. height: 0px;
  358. opacity: 0.5;
  359. }
  360. to {
  361. width: 500px;
  362. height: 500px;
  363. opacity: 0;
  364. }
  365. }
  366. `
  367. },
  368. initBox(doi) {
  369. let createBox = () => {
  370. this.sciDownloadBox = document.createElement('div')
  371. this.sciDownloadBox.setAttribute('id', 'sciDownloadBox')
  372. this.sciDownloadBox.setAttribute('doi', doi)
  373. this.sciDownloadBox.innerHTML = `
  374. <div id="sciSwitch">${this.svg.switch}</div>
  375. <div id="sciContent">
  376. <div id="sciState"></div>
  377. <a id="sciText"></a>
  378. </div>
  379. `
  380. document.body.appendChild(this.sciDownloadBox)
  381. // 绑定变量
  382. this.sciContent = this.sciDownloadBox.querySelector('#sciContent')
  383. this.sciState = this.sciDownloadBox.querySelector('#sciState')
  384. this.sciText = this.sciDownloadBox.querySelector('#sciText')
  385. // 改变doi文字
  386. this.changeContent(this.svg.doi, doi)
  387. // 缓入准备
  388. this.sciDownloadBox.style.right = -this.getElementWidth(this.sciDownloadBox) + 'px'
  389. setTimeout(() => {
  390. // 设置right属性,触发缓入动画
  391. this.sciDownloadBox.style.right = '0px'
  392. }, 230)
  393. this.sciSwitch = this.sciDownloadBox.querySelector('#sciSwitch')
  394. // 最大化/最小化按钮点击事件绑定
  395. this.sciSwitch.addEventListener('click', this.switchEvent)
  396. // 涟漪效果点击事件
  397. this.sciState.addEventListener('click', this.rippleClickEvent)
  398. this.sciText.addEventListener('click', this.rippleClickEvent)
  399. // 当窗口调整时,自适应
  400. window.onresize = () => {
  401. setTimeout(() => {
  402. this.sciSwitch.click()
  403. this.sciSwitch.click()
  404. })
  405. }
  406. }
  407. // 添加sciTool逻辑
  408. if (this.sciDownloadBox) {
  409. // 存在,设置left属性缓出
  410. this.sciDownloadBox.style.left = getComputedStyle(utils.sciDownloadBox).left
  411. this.sciDownloadBox.style.left = document.body.clientWidth + 'px'
  412. GM_addStyle(`
  413. #sciContent[progress] #sciState .progress {
  414. animation: progressOffset 25s linear forwards;
  415. }
  416. `)
  417. setTimeout(() => {
  418. // 缓出结束,让其消失,并创建新的
  419. this.sciDownloadBox.remove()
  420. createBox.apply(this)
  421. }, 230);
  422. } else {
  423. // 可能首次打开页面,直接创建
  424. createBox.apply(this)
  425. }
  426.  
  427. },
  428. rippleClickEvent(event) {
  429. // 这一步让sciState内svg的点击事件传播到sciState,而svg本身不产生动画效果
  430. let parent
  431. for (let i=0;i<event.path.length;i++) {
  432. if (event.path[i].id.match(/(sciText|sciState)/)) {
  433. parent = event.path[i]
  434. break
  435. }
  436. }
  437. let x = event.offsetX
  438. let y = event.offsetY
  439. let ripple = document.createElement("span")
  440. ripple.setAttribute('class', 'ripple')
  441. ripple.style.left = `${x}px`
  442. ripple.style.top = `${y}px`
  443. parent.appendChild(ripple)
  444. // timeout数值越大涟漪扩散越慢
  445. setTimeout(() => {
  446. ripple.remove()
  447. }, 1000)
  448. event.stopPropagation();
  449. },
  450. switchEvent(event) {
  451. if (utils.switchState == 'max') {
  452. utils.sciDownloadBox.style.left = getComputedStyle(utils.sciDownloadBox).left
  453. utils.sciDownloadBox.style.left = document.body.clientWidth - 40 * 2 + 'px'
  454. utils.sciDownloadBox.style.right = -utils.getElementWidth(utils.sciDownloadBox) + 'px'
  455. utils.sciSwitch.querySelector('svg .switch').style.transform = 'rotate(90deg)'
  456. utils.switchState = 'min'
  457. } else if (utils.switchState == 'min') {
  458. utils.sciDownloadBox.style.left = ''
  459. utils.sciDownloadBox.style.right = '0px'
  460. utils.sciSwitch.querySelector('svg .switch').style.transform = 'rotate(0deg)'
  461. utils.switchState = 'max'
  462. }
  463. },
  464. startProgress(doi) {
  465. if (document.querySelectorAll(`#sciDownloadBox[doi="${doi}"]`).length == 0) return
  466. this.sciContent.setAttribute('progress', '')
  467. // 有progress属性的有进度条动画
  468. GM_addStyle(`
  469. /* 背景色进度条 */
  470. #sciContent[progress] #sciText::before {
  471. content: "";
  472. position: absolute;
  473. right: 0;
  474. bottom: 0;
  475. height: 40px;
  476. width: 0%;
  477. background-color: ${this.color.flash};
  478. opacity: 1;
  479. z-index: -1;
  480. animation: progress ${this.timeout}s linear forwards;
  481. }
  482. `)
  483. },
  484. getContentWidth(ele, content) {
  485. let oldContent = ele.innerHTML
  486. ele.innerHTML = content
  487. let width = this.getElementWidth(ele)
  488. ele.innerHTML = oldContent
  489. return width
  490. },
  491. getElementWidth(ele) {
  492. return parseFloat(window.getComputedStyle(ele).width.replace('px', ''))
  493. },
  494. changeContent(state, text, callback=null) {
  495. if (state) {this.sciState.innerHTML = state}
  496. let ele = this.sciText
  497. let oldWidth = this.getElementWidth(ele)
  498. let newWidth = this.getContentWidth(ele, text)
  499. ele.style.width = oldWidth + 'px'
  500. setTimeout(() => {
  501. ele.style.width = newWidth + 'px'
  502. ele.innerHTML = text
  503. setTimeout(() => {
  504. ele.style.width = 'fit-content'
  505. if (callback) {callback()}
  506. }, 230)
  507. if (this.switchState == 'min' & this.autoMax.b){
  508. this.sciSwitch.click()
  509. setTimeout(() => {
  510. this.sciSwitch.click()
  511. }, this.autoMax.time * 1000 > 230 ? this.autoMax.time * 1000 : 230)
  512. }
  513. }, 230)
  514. },
  515. getDoi() {
  516. let doi, select, res
  517. let selection = window.getSelection().toString()
  518. let sourceText = document.body.innerHTML
  519. res = selection.match(this.doiRegex)
  520. if (res) {
  521. doi = res[0]
  522. select = true
  523. } else {
  524. res = sourceText.match(this.doiRegex)
  525. if (res) {
  526. doi = res[0]
  527. select = false
  528. }
  529. }
  530. if (doi) {
  531. doi = doi.replace(/[\/\.]\w*?pdf/, '').split(';')[0]
  532. }
  533. return [doi, select]
  534.  
  535. },
  536. check_update_journal_info(journal_name) {
  537. let key = `sciDownload-journal-${journal_name}`
  538. console.log(key)
  539. if (!GM_getValue(key, undefined)) {
  540. GM_xmlhttpRequest({
  541. method: 'POST',
  542. url: "https://easyscholar.cc/homeController/getQueryTable.ajax",
  543. data: `page=1&limit=10&sourceName=${journal_name}`,
  544. headers: {
  545. "Content-Type": "application/x-www-form-urlencoded"
  546. },
  547. responseType: "json",
  548. onload: function (res) {
  549. let value
  550. if (res.response.data.length) {
  551. let info = res.response.data[0]
  552. value = `${info.paperName} | ${info.sciif}/${info.sciif5} | ${info.sciBase} | ${info.sciUp}`
  553. } else {
  554. value = journal_name || "No More Information"
  555. }
  556. console.log(value)
  557. GM_setValue(key, value)
  558. }
  559. })
  560. } else {
  561. console.log('read from GM_getValue')
  562. console.log(GM_getValue(key, undefined))
  563. }
  564. },
  565. LocalSearch(doi) {
  566. // 1 开启动画效果
  567. this.initBox(doi)
  568. setTimeout(() => {
  569. this.startProgress(doi)
  570. }, 230)
  571. // 2 生成data,这里按顺序查询,不太会用js的并发只能按照顺序了,应该不会很慢
  572. let data = {journal_name: ""}
  573. let totalSource = 4
  574. let failSource = 0
  575. let setData = (value) => {
  576. if (value.url) {
  577. if (!Object.keys(data).includes("url")) {
  578. data = {...data, ...value}
  579. }
  580. } else {
  581. failSource += 1
  582. }
  583. }
  584. // 2.1 查询unpaywall
  585. const unpaywall = `https://api.unpaywall.org/v2/${doi}?email=polygon@unpaywall.com`
  586. GM_xmlhttpRequest({
  587. method: 'GET',
  588. url: unpaywall,
  589. responseType: 'json',
  590. onload: function (res) {
  591. const unpaywallData = res.response
  592. let journal_name = unpaywallData['journal_name']
  593. setTimeout(() => {
  594. utils.check_update_journal_info(journal_name)
  595. data.journal_name = journal_name
  596. }, 0);
  597. let url
  598. try {
  599. url = unpaywallData['best_oa_location']['url_for_pdf'] || unpaywallData['best_oa_location']['url_for_landing_page']
  600. } catch {
  601. url = ''
  602. if (Object.prototype.hasOwnProperty.call(unpaywallData, 'title')) {
  603. console.log(`unpaywall: ${unpaywallData['title']}`)
  604. researchgate(unpaywallData['title']) // 交给researchgate
  605. }
  606. }
  607. setData({
  608. message: 'unpaywall.org',
  609. url: url
  610. })
  611. }
  612. })
  613. // 2.2 查询scihub
  614. const scihub = `https://${this.scihubURL}/${doi}`
  615. GM_xmlhttpRequest({
  616. method: 'GET',
  617. url: scihub,
  618. onload: function (res) {
  619. let url = res.response.match(/\/\/(.+pdf)[^\'\"]/)
  620. if (url) {
  621. url = 'https://' + url[1]
  622. } else {
  623. url = ''
  624. }
  625. setData({
  626. message: utils.scihubURL,
  627. url: url
  628. })
  629. }
  630. })
  631. // 2.3 查询researchgate
  632. let isConsistent = (s1, s2) => {
  633. if (s1 == null || s2 == null) return false
  634. let matchArray = [], strArray = [s1, s2]
  635. for (let i=0;i<strArray.length;i++) {
  636. matchArray.push(strArray[i].toLowerCase().match(/\w+/g))
  637. }
  638. [s1, s2] = matchArray
  639. if (s1.length != s2.length) return false
  640. let b = true
  641. for (let i=0;i<s1.length;i++) {
  642. if (s1[i] != s2[i]) return false
  643. }
  644. return b
  645. }
  646. let researchgate = (paperTitle) => {
  647. const url = `https://www.researchgate.net/search.SearchBox.html?query=${paperTitle}&activeTab=publication`
  648. GM_xmlhttpRequest({
  649. method: 'GET',
  650. url: url,
  651. headers: {'accept': 'application/json'},
  652. responseType: 'json',
  653. onload: function (res) {
  654. try {
  655. const resData = res.response['result']['state']['searchSearch']['publication']['items']
  656. console.log(resData)
  657. let url = ''
  658. let i = 0
  659. while (i < resData.length) {
  660. let item = resData[i]
  661. if (!Object.prototype.hasOwnProperty.call(item['urls'], 'download')){
  662. console.log(`researchgate: no url`)
  663. } else {
  664. const pdfURL = 'https://www.researchgate.net/' + item['urls']['download']
  665. // 标题是否一致
  666. if (isConsistent(item['title'], paperTitle)) {
  667. url = pdfURL
  668. console.log(`researchgate[${i}]: best title`)
  669. console.log(paperTitle)
  670. console.log(item['title'])
  671. break
  672. } else {
  673. console.log(`researchgate: not consistent`)
  674. }
  675. }
  676. i += 1
  677. }
  678. setData({
  679. message: 'researchgate.net',
  680. url: url
  681. })
  682. } catch {
  683. console.log(res)
  684. }
  685. }
  686. })
  687. }
  688. // 2.4 出版商地址
  689. GM_xmlhttpRequest({
  690. method: 'GET',
  691. url: `https://doi.org/${doi}`,
  692. onload: function (res) {
  693. let publisherURL = res.finalUrl
  694. // https://linkinghub.elsevier.com/retrieve/pii/S016980952100421X
  695. if (publisherURL.includes('linkinghub.elsevier.com')) {
  696. let pdfURL = 'https://www.sciencedirect.com/science/article/pii/' + publisherURL.match(/pii\/(.+)/)[1] + '/pdfft'
  697. console.log(pdfURL)
  698. GM_xmlhttpRequest({
  699. method: 'GET',
  700. url: pdfURL,
  701. onload: function (res) {
  702. let url = ''
  703. res = res.response.match(/"(https?:\/\/pdf.+)"/)
  704. if (res && res.length == 2) {
  705. url = res[1]
  706. } else {
  707. url = ''
  708. }
  709. setData({
  710. message: 'sciencedirect.com',
  711. url: url
  712. })
  713. }
  714. })
  715. } else if (publisherURL.includes('springer.com')) {
  716. let pdfURL = 'https://link.springer.com/content/pdf/' + publisherURL.match(/article\/(.+)/)[1] + '.pdf'
  717. console.log(pdfURL)
  718. let func = (res) => {
  719. let url
  720. let contentType = res.responseHeaders.match(/content-type:(.+)/)[1]
  721. console.log(contentType)
  722. if (contentType.includes('pdf')) {
  723. url = pdfURL
  724. } else {
  725. url = ''
  726. }
  727. setData({
  728. message: 'springer.com',
  729. url: url
  730. })
  731. }
  732. GM_xmlhttpRequest({
  733. method: 'GET',
  734. url: pdfURL,
  735. onprogress: func,
  736. onload: func
  737. })
  738. } else {
  739. setData({
  740. message: 'sciencedirect.com',
  741. url: null
  742. })
  743. }
  744. // 补充其他
  745. }
  746. })
  747. // 3 等待data结果,并提交至下一个函数
  748. let total = 0
  749. const interval = 10
  750. let id = setInterval(() => {
  751. if (Object.keys(data).includes('url')) {
  752. console.log(data)
  753. utils.getPdf(data, doi)
  754. clearInterval(id)
  755. } else if (failSource == totalSource) {
  756. data = {
  757. message: 'NotSupport',
  758. url: ''
  759. }
  760. utils.getPdf(data, doi)
  761. clearInterval(id)
  762. } else if (total > this.timeout * 1000) {
  763. data = {
  764. message: 'Timeout',
  765. url: ''
  766. }
  767. utils.getPdf(data, doi)
  768. clearInterval(id)
  769. }
  770. total += interval
  771. }, interval)
  772. },
  773. getPdf(data, doi) {
  774. if (document.querySelectorAll(`#sciDownloadBox[doi="${doi}"]`).length == 0) return
  775. // 进入下载,收回timeout倒计时的进度条
  776. let progress = this.sciState.querySelector('svg .progress')
  777. let currentOffset = getComputedStyle(progress)['stroke-dashoffset']
  778. GM_addStyle(`
  779. #sciContent[progress] #sciState .progress {
  780. stroke-dashoffset: ${currentOffset};
  781. animation: progressRecover .23s linear;
  782. }
  783. `)
  784. setTimeout(() => {
  785. // 动画结束后改变sciContent状态
  786. this.sciContent.removeAttribute('progress')
  787. }, 230)
  788. this.changeContent(null, data.message)
  789. // api相应速度太快,可能清除不掉过一段时间才出现的进度条, 检测一秒钟
  790. let exit = true
  791. switch (data.message) {
  792. case 'NotSupport':
  793. this.log('不支持该文章,退出...')
  794. break
  795. case 'Timeout':
  796. this.log('请求超时,退出...')
  797. break
  798. default:
  799. this.log('请求pdf中...')
  800. exit = false
  801. }
  802. setTimeout(() => {
  803. progress.style['stroke-dashoffset'] = '0'
  804. }, 230*2)
  805. if (exit) {
  806. return
  807. }
  808. utils.sciContent.style['background-color'] = utils.color.flash
  809. let openInTab = (url) => {
  810. if (typeof(GM_openInTab) == 'undefined') {
  811. var a = document.createElement('a')
  812. a.href = url
  813. a.target = '_blank'
  814. document.body.appendChild(a)
  815. a.click()
  816. a.remove()
  817. } else {
  818. GM_openInTab(url, {active: false, insert: true})
  819. }
  820. }
  821. utils.sciText.onclick = () => { openInTab(data.url) }
  822. utils.sciState.onclick = () => { openInTab(data.url) }
  823. // 开始缓存同时尝试打开链接,可将下行反注释即可
  824. // window.open(pdfURL)
  825. let failSetting = () => {
  826. progress.style['stroke-dashoffset'] = '0'
  827. utils.sciContent.style['background-color'] = utils.color.fail
  828. }
  829. let lastTime, currentTime, lastDone=0, currentDone, size
  830. setTimeout(() => {
  831. this.sciContent.setAttribute('loading', '')
  832. progress.style['stroke-dashoffset'] = utils.progressTotaLength
  833. }, 230*2)
  834. GM_xmlhttpRequest({
  835. method: 'GET',
  836. url: data.url,
  837. responseType: 'blob',
  838. onprogress: function(res) {
  839. if (document.querySelectorAll(`#sciDownloadBox[doi="${doi}"]`).length == 0) return
  840. utils.sciContent.removeAttribute('loading')
  841. if (!res.responseHeaders.match(utils.pdfRegex)) {
  842. failSetting()
  843. return
  844. }
  845. // 波浪冲击效果待完成
  846. let rippleUp = (opacity) => {
  847. let ripple = document.createElement("span")
  848. ripple.setAttribute('class', 'ripple')
  849. ripple.style.backgroundColor = 'white'
  850. ripple.style.zIndex = -1
  851. ripple.style.opacity = opacity
  852. ripple.style.left = `${utils.getElementWidth(utils.sciContent)}px`
  853. ripple.style.top = '20px'
  854. utils.sciContent.appendChild(ripple)
  855. // timeout数值越大涟漪扩散越慢
  856. setTimeout(() => {
  857. ripple.remove()
  858. }, 1E3)
  859. }
  860. currentTime = new Date().getTime()
  861. currentDone = res.done
  862. if (!lastTime | currentTime - lastTime >= 300) {
  863. size = (currentDone - lastDone) / 1024 / 1024 * 5
  864. rippleUp(size > 1 ? 1 : size)
  865. lastTime = currentTime
  866. lastDone = currentDone
  867. let tip
  868. let key = `sciDownload-journal-${data.journal_name}`
  869. let journal_info = GM_getValue(key, "...")
  870. if (res.lengthComputable) {
  871. utils.sciContent.setAttribute('download-progress', '')
  872. let percent = res.done / res.total
  873. progress.style['stroke-dashoffset'] = utils.progressTotaLength * (1 - percent)
  874. // 下载进度条
  875. GM_addStyle(`
  876. #sciContent[download-progress] #sciText::before {
  877. content: "";
  878. position: absolute;
  879. right: 0;
  880. bottom: 0;
  881. height: 40px;
  882. width: ${percent * 100}%;
  883. background-color: white;
  884. opacity: 0.5;
  885. z-index: -1;
  886. transition: width .23s linear;
  887. }
  888. `)
  889. tip = `${journal_info} | ${(res.done / 1024 / 1024).toFixed(2)}M | ${(res.total / 1024 / 1024).toFixed(2)}M | ${(percent * 100).toFixed(2)}% | ${data.message}`
  890. } else {
  891. tip = `${journal_info} | ${(res.done / 1024 / 1024).toFixed(2)}M | --M | --% | ${data.message}`
  892. utils.sciContent.setAttribute('download-noprogress', '')
  893. }
  894. utils.sciContent.setAttribute('title', tip)
  895. utils.sciSwitch.setAttribute('title', tip)
  896. }
  897. },
  898. onload: function(res) {
  899. if (document.querySelectorAll(`#sciDownloadBox[doi="${doi}"]`).length == 0) return
  900. let size = (res.response.size / 1024 / 1024).toFixed(2)
  901. let key = `sciDownload-journal-${data.journal_name}`
  902. let journal_info = GM_getValue(key, "...")
  903. let tip = `${journal_info} | ${size}M | ${data.message}`
  904. utils.sciContent.setAttribute('title', tip)
  905. utils.sciSwitch.setAttribute('title', tip)
  906. setTimeout(() => {
  907. utils.sciContent.removeAttribute('loading')
  908. if (!res.responseHeaders.match(utils.pdfRegex)) {
  909. failSetting()
  910. notification.open('pdf加载失败了,亲自点一下吧~', 3)
  911. return
  912. }
  913. utils.sciContent.removeAttribute('download-progress')
  914. utils.sciContent.removeAttribute('download-noprogress')
  915. setTimeout(() => {
  916. utils.sciContent.style['background-color'] = utils.color.success
  917. }, 230);
  918. let fileURL = URL.createObjectURL(new Blob([res.response], {type: 'application/pdf'}))
  919. let title = doi.split('/').slice(1).join('/')
  920. let titleRes = res.responseHeaders.match(/filename=(.+)/)
  921. if (titleRes) {
  922. title = decodeURI(titleRes[1].split(';')[0]).replace('.pdf', '').replace('"', '').replace('"', '')
  923. }
  924. utils.sciText.removeAttribute('href')
  925. let downloadPdf = (fileURL, title) => {
  926. let aTag = document.createElement('a')
  927. aTag.setAttribute('href', fileURL)
  928. aTag.setAttribute('download', `${title}.pdf`)
  929. aTag.click()
  930. }
  931. utils.sciText.onclick = () => {
  932. setTimeout(() => {
  933. if ((typeof(GM_setValue) == 'undefined')) {
  934. downloadPdf(fileURL, title)
  935. } else {
  936. let win = window.open()
  937. win.document.write(`<iframe name="${title}" src="${fileURL}" frameborder="0" style="border:0; top:0px; left:0px; bottom:0px; right:0px; width:100%; height:100%;" allowfullscreen></iframe>`)
  938. win.document.title = title
  939. }
  940. }, 1E3)
  941. }
  942. utils.log('缓存pdf成功')
  943. utils.sciContent.style['background'] = utils.color.success
  944. utils.changeContent(utils.svg.pdf, title)
  945. utils.sciState.onclick = () => {
  946. downloadPdf(fileURL, title)
  947. }
  948. // 防止此时还没变更
  949. let key = `sciDownload-journal-${data.journal_name}`
  950. let value
  951. if (utils.sciContent.getAttribute('title').includes('...')) {
  952. // 设置一个循环等着
  953. let id = setInterval(() => {
  954. value = GM_getValue(key, undefined)
  955. if (value !== undefined) {
  956. let tip = utils.sciContent.getAttribute('title')
  957. .replace('...', value)
  958. utils.sciContent.setAttribute('title', tip)
  959. utils.sciSwitch.setAttribute('title', tip)
  960. clearInterval(id)
  961. }
  962. }, 200);
  963. }
  964. }, 230*3);
  965. }
  966. })
  967. },
  968. log(text) {
  969. console.log('[sciDownload]', text)
  970. }
  971. }
  972. try{
  973. GM_addStyle(utils.style())
  974. } catch {
  975. utils.log('添加style失败,退出...')
  976. return
  977. }
  978. let lastDoi = null
  979. let lastIsSelect = false
  980. setInterval(function () {
  981. let [doi, select] = utils.getDoi()
  982. if (!doi | doi == lastDoi | (lastIsSelect && !select)) {
  983. return
  984. }
  985. lastDoi = doi
  986. lastIsSelect = select
  987. let a = 'background: #00b894; color: #fff; opacity: 0.75;'
  988. let b = 'background: #2c3e50; color: #fff; opacity: 0.75;'
  989. console.log(`%c sciDownload %c ${doi} `, a, b)
  990. utils.LocalSearch(doi)
  991. }, 500)
  992. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址