修复脚本“豆瓣读书揭示北大馆藏+荐购/CALIS 高校馆际互借”中关于CALISS联合目录查询与馆际互借部分功能
// ==UserScript== // @name 豆瓣读书揭示北大馆藏+荐购/CALIS 高校馆际互借(修复) // @namespace lib.pku.edu.cn // @author Kawatabi // @version 1.0.0 // @description 修复脚本“豆瓣读书揭示北大馆藏+荐购/CALIS 高校馆际互借”中关于CALISS联合目录查询与馆际互借部分功能 // @description:en Repair CALIS union catalog query and interlibrary loan functions // @match https://book.douban.com/subject/* // @match https://book.douban.com/subject_search* // @match https://book.douban.com/search/* // @match https://book.douban.com/series/* // @match https://book.douban.com/tag/* // @match https://book.douban.com/author/*/books* // @match https://book.douban.com/works/* // @match https://www.douban.com/doulist/* // @match https://www.douban.com/search* // @match https://www.douban.com/* // @require https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/3.2.1/jquery.min.js // @connect api.douban.com // @connect opac.calis.edu.cn // @grant GM_xmlhttpRequest // @grant GM_setClipboard // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @license MIT // ==/UserScript== var Douban_PKUL_Styles = "@charset utf-8;"; Douban_PKUL_Styles += " .searchOPACinList label { background-color: #545652; }"; Douban_PKUL_Styles += " #searchOPACinList { margin-left: 10px; vertical-align: text-bottom; }"; Douban_PKUL_Styles += " .PKULib, #CALIS { margin-right: 10px; font-size: 9px; }"; Douban_PKUL_Styles += " .PKULibLogo {height: 16px; vertical-align: text-bottom; padding-right: 2px;}"; Douban_PKUL_Styles += " #formPKULPurchase { width: 260px; font-size: 9px; background-color: rgba(144, 160, 238, 0.25); padding: 4px; border-radius: 6px; }"; Douban_PKUL_Styles += " #formPKULPurchase input, #formPKULPurchase label, #formPKULPurchase span { font-size: 9px; }"; Douban_PKUL_Styles += " #formPKULPurchase b { font-size: 12px; }"; Douban_PKUL_Styles += " .PKULibPurchaseTheme { font-size: 13px; color: #03155f; font-weight: bold; }"; Douban_PKUL_Styles += " #formPKULPurchase textarea { border-color: red; font-size: 10px; width: 250px; min-height: 40px; resize: vertical; }"; Douban_PKUL_Styles += " #paraTI, #paraAU { width: 213px; }"; Douban_PKUL_Styles += " #paraPUB { width: 124px; } #paraPD { width: 50px; } "; Douban_PKUL_Styles += " #paraPR { width: 72px; } #paraISBN { width: 100px; } "; Douban_PKUL_Styles += " #paraID { width: 100px; } "; Douban_PKUL_Styles += " #buttSUBMIT { width: 255px; height:36px; font-size: 16px !important; letter-spacing: 0.8px; border-radius: 6px; color: #03155f; background-color: orange; font-weight: bold; } "; Douban_PKUL_Styles += " .purchas###bmitted { color: gray !important; } "; Douban_PKUL_Styles += " #CALISholding { color: #666666; background-color: white; border-radius: 6px; padding: 6px; max-width: 224px; max-height: 396px; overflow-y: scroll; } "; Douban_PKUL_Styles += " #CALISholding li { border: none; display: block; padding-left: 6px; line-height: 12px; } "; Douban_PKUL_Styles += " #CALISholding li a { padding: 2px; font-size: 10px; } "; GM_addStyle(Douban_PKUL_Styles); // 油猴脚本附加 CSS function getDoc(url, meta, callback) { // XHR 回调函数模板——使用该函数可成功跨域请求 HTML GM_xmlhttpRequest({ method: 'GET', url: url, headers: { 'User-agent': window.navigator.userAgent, 'Content-type': null }, onload: function(responseDetail) { var doc = ''; if (responseDetail.status == 200) { doc = new DOMParser().parseFromString(responseDetail.responseText, 'text/html'); if (doc === undefined) { doc = document.implementation.createHTMLDocument(""); doc.querySelector('html').innerHTML = responseText; } } callback(doc, responseDetail, meta); } }); } function getJSON(url, meta, callback) { // XHR 回调函数模板——使用该函数可成功跨域请求 JSON GM_xmlhttpRequest({ method: 'GET', url: url, headers: { 'User-agent': window.navigator.userAgent, //'Content-type': 'application/json', 'Accept': 'application/atom+xml,application/xml,text/xml' }, onload: function(responseDetail) { var doc = {}; if (responseDetail.status == 200) { doc = $.parseJSON(responseDetail.responseText); } callback(doc, responseDetail, meta); } }); } function addCALIS(pageISBN) { // 检查当前页面是否为豆瓣图书的具体图书页面,如果不是则直接返回 if (!(window.location.hostname == "book.douban.com" && /^\/subject\/\d+\/$/.test(window.location.pathname))) return; // 创建一个包含“检索中,请稍等”提示文本的 <span> 元素,并将其加入 $CALIS 按钮元素中 var $searchingCALIS = $("<span id='searchingCALIS'>……检索中,请稍等</span>"); var $CALIS = $("<span id='CALIS'>CALIS 高校图书馆</span>").append($searchingCALIS); // 创建一个 <li> 元素来容# CALIS 高校图书馆按钮 var $CALISLI = $("<li id='CALISLI' style='border: none'></li>").append($CALIS); // 将 CALIS 按钮插入到页面的指定位置,在 ID 为 PKULibLI 的 <li> 元素之后 $("#PKULibLI").after($CALISLI); // 定义用于查询 CALIS 联合目录的 URL,使用传入的 ISBN 进行查询 var urlSearchISBN = "http://opac.calis.edu.cn/doSimpleQuery.do?actionType=doSimpleQuery&dbselect=all&indexkey=bath.isbn|frt&langBase=default&maximumRecords=50&operation=searchRetrieve&pageno=1&pagingType=0&query=%28bath.isbn%3D%22" + pageISBN + "%22%29&sortkey=title&startRecord=1&version=1.1"; // 使用 getDoc 函数来进行异步 HTTP 请求,获取馆藏信息 getDoc(urlSearchISBN, null, function(calisR###lt, resp, meta) { var holdingLink = calisR###lt.querySelector('a[href^="javascript:getholding"]'); if (holdingLink) { var oid4Holding = holdingLink.getAttribute('href').match(/"([a-f0-9]{32})"/)[1]; console.log("oid4Holding:", oid4Holding); // 输出 oid4Holding 到控制台 var urlSearchHolding = "http://opac.calis.edu.cn/opac/showHolding.do?subact=enterpage&fromType4Holding=fromSimpleList&langBase4Holding=null&targetName=showHoldingWindow&oid4Holding=" + oid4Holding; console.log("urlSearchHolding:", urlSearchHolding); getDoc(urlSearchHolding, null, function(holdingR###lt, resp, meta) { // 获取高校馆藏分布 var $ul = $("<ul id='CALISholding' class='CALIStoggle'></ul>"); var $CALISlibs = $("input[name='libcodeCheckBox']", holdingR###lt); // 其中包含高校图书馆名称 $searchingCALIS.remove(); $ul.prepend($("<li class='CALIStoggle' style='padding-left: 4px;'><a href='" + urlSearchHolding + "' target='_blank' title='申请 CALIS 馆际互借'>> 从以下图书馆申请馆际互借</a></li>")); $CALIS.after($ul); $CALISlibs.each(function(){ $ul.append($("<li>" + $(this).val().match(/\|(.+)/)[1] + "</li>")); // 逐个生成高校图书馆 ul }); $(".CALIStoggle").hide(); // 初始隐藏 if ($CALISlibs.length > 0) { $CALIS.text($CALIS.text() + "(" + $CALISlibs.length + ")"); $CALIS.wrap($("<a href='#' target='_blank' title='显示/隐藏 CALIS 高校馆藏分布'></a>")); $CALIS.on("click", function(event) { event.preventDefault(); // 阻止 <a> 的默认行为 $(".CALIStoggle").toggle(); return false; }); } else { $CALIS.wrap($("<a href='" + urlSearchISBN + "00' target='_blank' title='CALIS 联合目录检索'></a>")); // 这里给 maximumRecords 增加到 100 } }); } else { $searchingCALIS.text("(无馆藏信息)"); } }); } function generatePurchaseForm(thisBook) { // 在 .PKULib 之后生成荐购界面 /* if (!(window.location.hostname == "book.douban.com" && /^\/subject\/\d+\/$/.test(window.location.pathname))) return; // 只在图书页面生成荐购单 var $form = $("<form id='formPKULPurchase' method='post' action='http://162.105.138.200/uhtbin/cgisirsi/0/0/0/65/PURCHASE' accept-charset='UTF-8' target='_blank'></form>"); $form.append($("<p class='PKULibPurchaseTheme'>如果尚未收藏,打算推荐购买?</p>")).append($("<hr />")); $form.append($("<b>书目信息</b>")).append($("<span>(已自动填写,请核对订正)</span>")).append($("<br />")); $form.append($("<label for='paraTI'>书 名 </label>")); $form.append($("<input type='text' name='entry^1^TITLE^题名' id='paraTI' class='PKULib_MUST' size='30' value='" + thisBook.title + (thisBook.subtitle.length > 0 ? ". " + thisBook.subtitle : "") + "' placeholder='必填' required='required' />")).append($("<br />")); $form.append($("<label for='paraAU'>著 者 </label>")); $form.append($("<input type='text' name='entry^2^AUTHOR^著者' id='paraAU' class='PKULib_MUST' size='30' value='" + thisBook.author.join("、") + (thisBook.translator.length > 0 ? ";" + thisBook.translator.join("、") + " 译" : "") + "' placeholder='必填;包括作者、编者、译者等' required='required' />")).append($("<br />")); $form.append($("<label for='paraPUB'>出版社 </label>")); $form.append($("<input type='text' name='entry^3^PUBLISHER^出版者' id='paraPUB' size='17' value='" + thisBook.publisher + "' placeholder='出版社、出版商' />")); $form.append($("<label for='paraPD'> 年月 </label>")); $form.append($("<input type='text' name='entry^4^DATE^日期' id='paraPD' size='7' value='" + (/\d{2,4}[-\/\.]\d{1,2}/.test(thisBook.pubdate) ? thisBook.pubdate.match(/\d{2,4}[-\/\.]\d{1,2}/)[0] : thisBook.pubdate) + "' placeholder='出版年月' />")).append($("<br />")); $form.append($("<label for='paraPR'>定 价 </label>")); $form.append($("<input type='text' name='entry^7^PRICE^价格' id='paraPR' size='10' value='" + thisBook.price + "' placeholder='图书价格' />")); $form.append($("<label for='paraISBN'> ISBN </label>")); $form.append($("<input type='text' name='entry^5^ISBN/ISSN^ISBN/ISSN' id='paraISBN' size='14' value='" + (thisBook.isbn13 ? thisBook.isbn13 : (thisBook.isbn10 ? thisBook.isbn10.replace(/^SH/, "统一书号 ") :"(无)")) + "' placeholder='国际标准书号' />")).append($("<br />")); $form.append($("<hr />")); $form.append($("<label for='paraWHY' style='color: red;'><b>推荐理由<br /></b></label>")); $form.append($("<textarea name='entry^6^CITED_IN^推荐原因' id='paraWHY' class='PKULib_MUST' size='14' maxlength='140' placeholder='为何推荐这本书?——对专业的重要性、作者知名度、版本价值,等等' autofocus='autofocus' required='required' rows='3'></textarea>")).append($("<br />")); $form.append($("<span>如果是外文书 </span>")); $form.append($("<input type='radio' name='entry^8^NOTE^注释' id='paraNOTEhold' value='希望预约' />")); $form.append($("<label for='paraNOTEhold'>希望预约可借复本 </label>")); $form.append($("<input type='radio' name='entry^8^NOTE^注释' id='paraNOTEnohold' value='不必预约或非外文书' checked='checked' />")); $form.append($("<label for='paraNOTEnohold'>无所谓</label>")).append($("<br />")); $form.append($("<label for='paraID'><b>校园卡号</b> </label>")); $form.append($("<input type='text' name='alt_id' id='paraID' size='14' value='' placeholder='推荐人的校园卡号' />")).append($("<br />")); $form.append($("<span>【说明】建议正确填写校园卡号,有助于图书馆决定是否订购,并且用户还可在 e-Library 中跟踪订购状态(限同时最多 3 个)</span>")).append($("<br />")); $form.append($("<hr />")); $form.append($("<input type='submit' value='推荐给北大图书馆订购' id='buttSUBMIT' />")); $form.append($("<input type='hidden' name='entry^9^SOURCE^来源' size='10' value='" + thisBook.alt + "' />")); $form.hide(); // 初始隐藏 $(".PKULib").after($form); // 生成表单;下面继续挂接事件函数 $(".PKULib a").on("click", function(){ // 强制用户先用书名查询 OPAC 至少一次才能显示荐购界面 $("#formPKULPurchase").show(); }); $form.on("submit", trySubmitRecomm); // 挂接事件:尝试提交(验证 + 提交) $(".PKULib_MUST").on("input", function() { // 必填字段如果空值,立刻用红色提醒 var color = $(this).val().length > 0 ? "initial" : "red"; $(this).prev("label").css("color", color); // 就是栏位提示文字 label $(this).css("border-color", color); }); */ } function addPKULib($rootSelector, subjectID, byAppend, pageTI, pageISBN) { // 两个 bak 参数为图书页面提供的备用数据 var $PKULib = $("<span class='PKULib'><img class='PKULibLogo' src='https://lib.pku.edu.cn/portal/sites/default/files/favicon.ico' /></span>"); var $PKULibText = $(byAppend ? "<span>北大图书馆</span>" : "<span>北#大学图书馆</span>"); // 部分情况显示简称 var $searching = $("<span class='searchingPKULib'>……检索中,请稍等</span>"); $PKULib = $PKULib.append($PKULibText).append($searching); if (byAppend) // 创建北大图书馆项目,根据参数区分插入的位置 $($rootSelector).append($PKULib); // 容器末尾:适用于通用搜索结果、作品版本列表 else $($rootSelector).prepend($PKULib); // 容器开头:适用于其他情况 // 调用豆瓣图书 API 获得图书信息 getJSON("https://api.douban.com/v2/book/" + subjectID, null, function(thisBook, resp, meta) { var urlSearchTI = "http://162.105.138.200/uhtbin/cgisirsi/x/北大中心馆/0/5?searchdata1=" + (thisBook.title ? thisBook.title : pageTI) + "&library=ALL&srchfield1=TI%5ETITLE%5E%5ETitle%25"; if (thisBook.isbn13 || pageISBN) { // 豆瓣著录了 ISBN var urlSearchISBN = "http://162.105.138.200/uhtbin/isbn/" + (thisBook.isbn13 ? thisBook.isbn13 : pageISBN); // 按 ISBN 查询北大 OPAC getDoc(urlSearchISBN, null, function(opacR###lt, resp, meta) { var searchSumHits = $("div.searchsum_hits .content p", opacR###lt).text(); // 单个命中结果 if (!searchSumHits.match(/\d+\D+of\D+\d+/)) { // 无论中英文版 OPAC 都是这个格式 var hitListSearchSummary = $("#hitlist .hit_list .hitstop .searchsummary", opacR###lt).text(); // 多个命中结果 if (!/检索到\D+\d+\D+题名/.test(hitListSearchSummary) && !/search found\D+\d+\D+titles/.test(hitListSearchSummary)) { $PKULib.append($("<a href='" + urlSearchTI + "' target='_blank' title='尝试检索书名'>(ISBN 查无结果)</a>")); $searching.remove(); // 删除等待检索提示(必须在 getDoc callback 函数中执行,否则可能还在等待时就删除了) generatePurchaseForm(thisBook); // 尝试生成荐购表单(仅图书页面有),初始隐藏,用户点击一次链接后触发显示 return; } } $PKULib.wrap($("<a href='" + urlSearchISBN + "' target='_blank' title='馆藏目录检索''></a>")); // 生成按 ISBN 检索的链接 $searching.remove(); }); } else { // 豆瓣没有著录 ISBN $PKULib.append($("<a href='" + urlSearchTI + "&sort_by=PBYR" + "' target='_blank' title='尝试检索书名'>(该书无 ISBN)</a>")); // 因为没有 ISBN 所以增加 sort_by 参数,按从旧到新排序搜索结果 $searching.remove(); generatePurchaseForm(thisBook); // 尝试生成荐购表单(仅图书页面有) } }); } (function() { 'use strict'; console.log("豆瓣读书揭示北大馆藏+荐购--自执行函数已运行"); var hostName = window.location.hostname; // 当前网址的域名 var pathName = window.location.pathname; // 当前网址的路径 var searchString = window.location.search; // 当前网址的 GET 参数串 // 页面顶部创建一个复选框,用于设置是否在列表中检索馆藏——该功能不稳定,访问频繁了可能会受到限制 $(".global-nav-items ul").append($("<li class='searchOPACinList'><input type='checkbox' id='searchOPACinList' /><label for='searchOPACinList' title='该功能受豆瓣 API 访问频率限制,可能不准确;勾选后,请刷新页面'>批量查馆藏</label></li>")); $("#searchOPACinList").attr("checked", GM_getValue("searchOPACinList") ? "checked" : undefined, undefined); // 获取初始值,默认不在列表中检索馆藏 $("#searchOPACinList").change(function(){ GM_setValue("searchOPACinList", $("#searchOPACinList").attr("checked") ? true : false); // 设置是否在列表中检索馆藏——注意,设置存储为 true/false $("#searchOPACinList").next("label").text("批量查馆藏(刷新后生效)"); }); // 以下开始:根据 URL 判断页面类型,分别添加 UI // 豆瓣读书的图书页面 if (hostName == "book.douban.com" && /^\/subject\/\d+\/$/.test(pathName)) { if($("#borrowinfo").length <= 0) // 如果借阅信息面板不存在,模仿常例创建之 $("#buyinfo").after($("<div class='gray_ad' id='borrowinfo'><h2><span>在哪儿借这本书</span> · · · · · ·</h2><ul class='bs more-after'></ul></div>")); $("#borrowinfo ul.more-after").prepend($("<li style='border: none' id='PKULibLI'></li>")); var pageTI = $("h1 span").text(); var pageISBN = $("#info").text().match(/ISBN: (\d+X?)/, "i")[1]; addPKULib("#PKULibLI", window.location.pathname.match(/\d+/)[0], false, pageTI, pageISBN); // 添加北大馆藏揭示界面 addCALIS(pageISBN); // 添加 CALIS 联合目录揭示(仅图书页面有) } if (!$("#searchOPACinList").attr("checked")) // 如果不批量检索馆藏目录,中断 return; // 豆瓣读书的搜索结果(两种 URL 格式) console.log("豆瓣读书揭示北大馆藏+荐购--批量查馆藏选项开启,程序继续"); if (hostName == "book.douban.com" && (pathName == "/subject_search" || /search\/.+/.test(pathName))) if ($(".cart-actions .add2cart").length > 0) $(".cart-actions .add2cart").each(function() { // 在每个 .cart-actions 之内添加链接,其中 .add2cart[name] 为豆瓣图书 subject ID addPKULib($(this).parents(".cart-actions")[0], $(this).attr("name")); }); else { // 豆瓣无搜索结果时,也提供图书馆的全面搜索链接 if ($("#TryLibSearch").length <= 0) // 支持其他脚本也在此处扩展 $($("h2")[1]).before($("<h2 id='TryLibSearch'><span>图书馆目录检索</span> · · · · · ·</h2>")); if (/search\/.+/.test(pathName)) // 格式 1:search/searchstring var currentSearch = pathName.match(/search\/(.+)/)[1]; else // 格式 2:subject_search?search_text=stringstring&cat=1001 var currentSearch = searchString.match(/\?search_text=(.+?)&cat=/)[1]; var urlSearchANY = "http://162.105.138.200/uhtbin/cgisirsi/x/北大中心馆/0/5?searchdata1=" + currentSearch + "&library=ALL&srchfield1=GENERAL^SUBJECT^GENERAL^^全面检索"; $("#TryLibSearch").after($("<p class='pl'>> <a href='" + urlSearchANY + "' rel='nofollow' target='_blank' title='馆藏目录检索'><img class='PKULibLogo' src='https://lib.pku.edu.cn/portal/sites/default/files/favicon.ico' />北#大学图书馆</a></p>")); } // 豆瓣通用搜索结果 if (hostName == "www.douban.com" && pathName == "/search") $("h3>span:contains('[书籍]')").each(function() { // 在每个 .doulist-subject 之内添加链接,其中 a[data-cate='1001'][data-id] 为豆瓣图书 subject ID addPKULib($(this).parents("h3")[0], $($(this).siblings("a")[0]).attr("href").match(/subject%2F(\d+?)%2F/)[1], true); }); // 豆瓣读书的丛书页面、图书标签浏览页面 if (hostName == "book.douban.com" && (/^\/series\/\d+$/.test(pathName) || /^\/tag\/.+$/.test(pathName))) // 路径末尾无 / $(".cart-actions .add2cart").each(function() { // 在每个 .cart-actions 之内添加链接,其中 .add2cart[name] 为豆瓣图书 subject ID addPKULib($(this).parents(".cart-actions")[0], $(this).attr("name")); }); // 豆列页面(空豆列无需任何增强内容) if (hostName == "www.douban.com" && /^\/doulist\/\d+\/$/.test(pathName)) $("a[data-cate='1001']").each(function() { // 在每个 .doulist-subject 之内添加链接,其中 a[data-cate='1001'][data-id] 为豆瓣图书 subject ID addPKULib($(this).parents(".doulist-add-btn")[0], $(this).attr("data-id")); }); // 作者作品页面 if (hostName == "book.douban.com" && /^\/author\/\d+\/books$/.test(pathName)) $(".author-collect").each(function() { // 在每个 .author-collect 之内添加链接,其中包含豆瓣图书 subject ID addPKULib(this, $($(this).parents("dl")[0]).html().match(/subject\/(\d+?)\//)[1]); }); // 图书版本页面 if (hostName == "book.douban.com" && /^\/works\/\d+$/.test(pathName)) $(".bkdesc .pl2").each(function() { // 在每个 .bkdesc .about 之内添加链接,其中 .bkdesc .pl2[href] 为豆瓣图书 subject ID addPKULib($(this).parents(".bkdesc").children(".about")[0], $(this).attr("href").match(/subject\/(\d+?)\//)[1], true); }); // 广播:首页、翻页、个人状态等均包含 if (hostName == "www.douban.com" && (searchString === "" || /^\?p=\d/.test(searchString))) $(".block-subject .pic a").each(function() { // 在每个 .block-subject 之内添加链接,其中 .block-subject .pic a[href] 可能包含豆瓣图书 subject ID if (/^https:\/\/book\./.test($(this).attr("href"))) // 判断确实是图书的广播 addPKULib($(this).parents(".block-subject")[0], $(this).attr("href").match(/subject\/(\d+?)\//)[1], true); }); })();