Library Helper

A userscript that display links between different libraries and book stores.

当前为 2019-10-11 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Library Helper
  3. // @namespace https://gf.qytechs.cn/en/users/263753-chihchun
  4. // @version 1.1
  5. // @description A userscript that display links between different libraries and book stores.
  6. // @author Rex Tsai <rex.cc.tsai@gmail.com>
  7. // @match http://book.tpml.edu.tw/webpac/bookDetail.do*
  8. // @match https://book.douban.com/subject/*
  9. // @match https://books.google.com.tw/books*
  10. // @match https://play.google.com/store/books/details/*
  11. // @match https://share.readmoo.com/book/*
  12. // @match https://readmoo.com/book/*
  13. // @match https://www.amazon.cn/dp/*
  14. // @match https://www.amazon.cn/gp/product/*
  15. // @match https://www.amazon.com/*/dp/*
  16. // @match https://www.amazon.com/gp/product/*
  17. // @match https://www.books.com.tw/products/*
  18. // @match https://www.goodreads.com/book/show/*
  19. // @match https://www.kobo.com/tw/zh/ebook*
  20. // @match https://www.taaze.tw/goods/*
  21. // @match https://www.taaze.tw/usedList.html?oid=*
  22. // @grant none
  23. // @require https://cdnjs.cloudflare.com/ajax/libs/js-yaml/3.13.1/js-yaml.min.js
  24. // @run-at document-idle
  25. // @license MIT; https://github.com/chihchun/library-helper/blob/master/LICENSE
  26. // @supportURL https://github.com/chihchun/library-helper/issues
  27. // ==/UserScript==
  28.  
  29. (function() {
  30. 'use strict';
  31.  
  32. var metadata_yaml = `
  33. amazon.com:
  34. matches:
  35. - https://www.amazon.com/gp/product/*
  36. type: 'XPATH'
  37. metadata:
  38. title: "//span[@id='ebooksProductTitle']"
  39. authors: "//span[contains(@data-a-popover, 'Author Dialog')]/a"
  40. asin: "//form[@id='sendSample']/input[@name='ASIN.0']/@value"
  41. amazon.com/dp:
  42. matches:
  43. - https://www.amazon.com/*
  44. type: 'XPATH'
  45. metadata:
  46. title: "//span[@id='ebooksProductTitle']"
  47. authors: //span[contains(@class, 'author')]/a
  48. asin: "//form[@id='sendSample']/input[@name='ASIN.0']/@value"
  49.  
  50. amazon.cn:
  51. matches:
  52. - "https://www.amazon.cn/gp/product/*"
  53. - "https://www.amazon.cn/dp/*"
  54. type: 'XPATH'
  55. metadata:
  56. title: "//span[@id='ebooksProductTitle']"
  57. authors: "//span[contains(@class,'author')]/a"
  58. asin: "//form[@id='sendSample']/input[@name='ASIN.0']/@value"
  59.  
  60. books.com.tw:
  61. matches:
  62. - "https://www.books.com.tw/products/*"
  63. type: 'XPATH'
  64. metadata:
  65. title: "//h1"
  66. origtitle: "//h2/a[contains(@href,'https://search.books.com.tw/search/query/cat/all/key')]"
  67. isbn: "//li[contains(text(),'ISBN')]"
  68. price: "//ul[@class='price']/li/em"
  69. sellingprice: "//b[@itemprop='price']"
  70. authors: "//a[contains(@href,'adv_author')]"
  71. publishdate: "//li[contains(text(),'出版日期')]"
  72.  
  73. books.google.com.tw:
  74. matches:
  75. - "https://books.google.com.tw/books/*"
  76. type: 'XPATH'
  77. metadata:
  78. title: "//meta[@property='og:title']/@content"
  79. authors: "//a[contains(@href,'q=inauthor')]"
  80.  
  81. goodreads.com:
  82. matches:
  83. - "https://www.goodreads.com/book/show/*"
  84. type: 'XPATH'
  85. metadata:
  86. title: "//meta[@property='og:title']/@content"
  87. authors: "//a[@class='authorName']/span[@itemprop='name']"
  88. isbn: "//meta[@property='books:isbn']/@content"
  89.  
  90. kobo.com:
  91. matches:
  92. - "https://www.kobo.com/tw/zh/ebook*"
  93. type: 'JSON-LD'
  94. metadata:
  95. title: '//span[@class="title product-field"]'
  96. authors: '//a[@class="contributor-name"]'
  97. origtitle: "//span[contains(@class, 'subtitle')]"
  98.  
  99. play.google.com:
  100. matches:
  101. - "https://play.google.com/store/books/details/*"
  102. type: 'JSON-LD'
  103. metadata:
  104.  
  105. readmoo.com:
  106. matches:
  107. - "https://share.readmoo.com/book/*"
  108. - "https://readmoo.com/book/*"
  109. type: 'XPATH'
  110. metadata:
  111. title: "//h2"
  112. isbn: "//span[@itemprop='ISBN']"
  113. authors: "//span[@itemprop='name']/a"
  114.  
  115. taaze.tw:
  116. matches:
  117. - "https://www.taaze.tw/goods/*"
  118. type: 'XPATH'
  119. metadata:
  120. title: "//div[contains(@class, 'mBody')]//h1"
  121. origtitle: "//div[contains(@class, 'mBody')]//h2"
  122. isbn: "//meta[@property='books:isbn']/@content"
  123. authors: "//div[@class='authorBrand']//a[contains(@href,'rwd_searchResult.html?keyType%5B%5D=2')]"
  124. taaze.tw/used:
  125. matches:
  126. - "https://www.taaze.tw/usedList.html*"
  127. type: 'XPATH'
  128. metadata:
  129. origtitle: "//div[contains(@class, 'hide')]//div[@class='title-next']"
  130. authors: "//a[contains(@href,'rwd_searchResult.html?keyType%5B%5D=2')]"
  131.  
  132. tpml.edu.tw:
  133. matches:
  134. - "http://book.tpml.edu.tw/webpac/bookDetail.do*"
  135. type: 'XPATH'
  136. metadata:
  137. title: "//h3"
  138. authors: "//a[contains(@href,'search_field=PN')]"
  139.  
  140. `;
  141. var search_yaml = `
  142. 博客來: "https://search.books.com.tw/search/query/key/"
  143. Kobo: "https://www.kobo.com/tw/zh/search?query="
  144. GooglePlay: "https://play.google.com/store/search?c=books&q="
  145. AmazonCN: "https://www.amazon.cn/s?rh=n%3A116169071&k="
  146. 豆瓣: "https://search.douban.com/book/subject_search?search_text="
  147. Goodreads: "https://www.goodreads.com/search?q="
  148. Google: "https://www.google.com/search?tbm=bks&q="
  149. TPML: "http://book.tpml.edu.tw/webpac/bookSearchList.do?search_field=FullText&search_input="
  150. 讀冊: "https://www.taaze.tw/rwd_searchResult.html?keyword%5B%5D="
  151. Readmoo: "https://share.readmoo.com/search/keyword?q="
  152. Amazon: "https://www.amazon.com/s?i=digital-text&k="
  153. `;
  154.  
  155. var keywords = ['title', 'authors', 'origtitle', 'isbn', 'asin'];
  156.  
  157.  
  158. parse_metadata();
  159.  
  160. function parse_metadata() {
  161. var rules = jsyaml.load(metadata_yaml);
  162. var data = {};
  163.  
  164. // parse the ld+json
  165. var jsons = evaluate('//script[@type="application/ld+json"]');
  166. if(jsons.length > 0) {
  167. jsons.forEach(function(json) {
  168. var ld = JSON.parse(json);
  169. console.debug(ld);
  170. if(ld['@type'] == "Book") {
  171. data['title'] = [ld['name']];
  172.  
  173. if(ld['isbn'] != undefined) {
  174. data['isbn']= [ld['isbn']];
  175. }
  176.  
  177. if(ld['workExample'] != undefined && ld['workExample']['isbn'] != undefined ) {
  178. data['isbn']= [ld['workExample']['isbn']];
  179. }
  180.  
  181. data['authors'] = [];
  182. if(ld['author'] != undefined) {
  183. if(Array.isArray(ld['author'])) {
  184. ld['author'].forEach(function (author) {
  185. data['authors'].push(author['name']);
  186. })
  187. } else {
  188. data['authors'].push(ld['author']['name']);
  189. }
  190. }
  191. }
  192. })
  193. }
  194.  
  195. // parse the metadata by xpath
  196. for (var domain in rules) {
  197. rules[domain]['matches'].forEach(function (match) {
  198. if(document.URL.match(match)) {
  199. var metadata = rules[domain]['metadata'];
  200. for (var key in metadata) {
  201. data[key] = evaluate(metadata[key]);
  202. }
  203. return;
  204. }
  205. })
  206. }
  207.  
  208. // Links to other websites
  209. if(Object.keys(data).length > 0) {
  210. console.debug(data);
  211. var dialog = inject();
  212. var urlsforsearch = jsyaml.load(search_yaml);
  213. for (var service in urlsforsearch) {
  214. var url = urlsforsearch[service];
  215. var html = `<div>${service}: `;
  216. keywords.forEach(function(key) {
  217. if(data[key] != undefined) {
  218. data[key].forEach(function(val) {
  219. var href = url + encodeURI(val);
  220. html += `<a href="${href}" target="_blank">${val}</a> `;
  221. })
  222. }
  223. })
  224. html += "</div>";
  225. dialog.insertAdjacentHTML('beforeend', html)
  226. }
  227. }
  228. }
  229.  
  230.  
  231. function inject () {
  232. var div = document.createElement('div');
  233. div.id = "libraryhelper";
  234. div.className = "libraryhelper";
  235. div.textContent = 'Library helper';
  236. // Make the DIV element draggable:
  237. dragElement(div);
  238. document.body.appendChild(div);
  239.  
  240. var style = document.createElement('style');
  241. style.innerHTML = `
  242. div.libraryhelper {
  243. color: blueviolet;
  244. border: 1px solid #d3d3d3;
  245. background-color: rgba(255, 255, 255, 0.6);
  246.  
  247. position: fixed;
  248. top: 150px;
  249. right: 0px;
  250.  
  251. width: 30vw;
  252. max-height: 50vh;
  253. padding: 10px;
  254.  
  255. overflow-x: scroll;
  256. cursor: move;
  257. z-index: 9999;
  258. }
  259. `;
  260. document.head.appendChild(style);
  261. return div;
  262. }
  263.  
  264.  
  265. function dragElement(elmnt) {
  266. var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  267. if (document.getElementById(elmnt.id + "header")) {
  268. // if present, the header is where you move the DIV from:
  269. document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
  270. } else {
  271. // otherwise, move the DIV from anywhere inside the DIV:
  272. elmnt.onmousedown = dragMouseDown;
  273. }
  274.  
  275. function dragMouseDown(e) {
  276. e = e || window.event;
  277. e.preventDefault();
  278. // get the mouse cursor position at startup:
  279. pos3 = e.clientX;
  280. pos4 = e.clientY;
  281. document.onmouseup = closeDragElement;
  282. // call a function whenever the cursor moves:
  283. document.onmousemove = elementDrag;
  284. }
  285.  
  286. function elementDrag(e) {
  287. e = e || window.event;
  288. e.preventDefault();
  289. // calculate the new cursor position:
  290. pos1 = pos3 - e.clientX;
  291. pos2 = pos4 - e.clientY;
  292. pos3 = e.clientX;
  293. pos4 = e.clientY;
  294. // set the element's new position:
  295. elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
  296. elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
  297. }
  298.  
  299. function closeDragElement() {
  300. // stop moving when mouse button is released:
  301. document.onmouseup = null;
  302. document.onmousemove = null;
  303. }
  304. }
  305.  
  306. function evaluate(xpath, doc = document.documentElement) {
  307. var evaluator = new XPathEvaluator();
  308. var result = evaluator.evaluate(xpath, doc, null, XPathResult.ANY_TYPE, null);
  309. var node = null;
  310. var texts = [];
  311.  
  312. while(node = result.iterateNext()) {
  313. var text;
  314.  
  315. if (node instanceof Attr) {
  316. text = node.value
  317. } else {
  318. text = node.innerText;
  319. }
  320. if(text == undefined) {
  321. console.error(xpath + " not found on " + document.URL);
  322. continue;
  323. }
  324. // fixing up content
  325. text = text.replace("ISBN:", "").
  326. replace("出版日期:", "");
  327. texts.push(text);
  328. }
  329. return texts;
  330. }
  331. })();
  332.  

QingJ © 2025

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