// ==UserScript==
// @name 优书网 <=> 知轩藏书
// @namespace http://tampermonkey.net/
// @description [知轩藏书/早安电子书/书荒网]添加优书网评分和直链,优书网书籍详情页添加[知轩藏书/早安电子书/龙凤互联/书荒网]下载链接
// @require http://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.js
// @require http://greasyfork.icu/scripts/368137-encodetogb2312/code/encodeToGb2312.js
// @author tianxin
// @match *://zxcs8.com/sort/*
// @match *://zxcs8.com/post/*
// @match *://zxcs8.com/index.php?keyword=*
// @match *://www.zxcs8.com/sort/*
// @match *://www.zxcs8.com/post/*
// @match *://www.zxcs8.com/index.php?keyword=*
// @match *://www.yousuu.com/book/*
// @match *://www.zadzs.com/txt/*
// @match *://www.nordfxs.com/*
// @match *://www.15huang.com/style/*.html
// @match *://www.15huang.com/style/*
// @match *://www.15huang.com/e/search/result/*
// @match *://www.15huang.com/tags-etagid*.html
// @grant GM.xmlHttpRequest
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
// @connect www.yousuu.com
// @connect www.zxcs8.com
// @connect www.zadzs.com
// @connect www.nordfxs.com
// @connect www.15huang.com
// @version 0.5
// ==/UserScript==
// 请求类型 1:获取评分 2:获取下载
const REQUEST_TYPE_SOURCE = 1;
const REQUEST_TYPE_DOWNLOAD = 2;
// 下载链接缓存时间,默认7天
const DOWNLOAD_EXPIRED_TIME = 86400 * 7 * 1000;
// 优书网评分缓存时间,默认两小时
const SEARCH_EXPIRED_TIME = 2 * 3600 * 1000;
// 优书网最大搜索数目,默认5个
const MAX_SEARCH_NUM = 5;
// 简单封装GM_getValue和GM_setValue,支持过期时间
let storage = {
setValue:(key,value) => {
let data = JSON.stringify({value:value,time:new Date().getTime()});
return GM_setValue(key, data);
},
getValue:(key,expired) => {
let value = GM_getValue(key);
if(value !== undefined){
let dataObj = JSON.parse(value);
if(new Date().getTime() - dataObj.time > expired){
return undefined;
}else{
return dataObj.value;
}
}else{
return undefined;
}
},
}
let getResponse = (options) => {
return new Promise((resolve,reject) => {
GM_xmlhttpRequest({
method: options.method,
url: options.url,
data:options.data,
headers:options.headers,
synchronous:options.synchronous,
anonymous :options.anonymous,
onload: (response) => {
if (response.status >= 200 && response.status < 400){
console.log(response);
resolve(response.responseText);
}
else{
console.log(
'Error getting ' + options.url + ' (' + this.status + ' ' + this.statusText +
'): ' + this.responseText);
}
},
onerror: (response) => {
console.log('Error during GM_xmlhttpRequest to ' + options.url + ': ' + response.statusText);
}
});
});
}
let getResult = (options) => {
return new Promise((reslove,reject) => {
options.data = options.data || '';
options.method = options.method || 'GET';
options.headers = options.headers || '' ;
options.synchronous = options.synchronous || false;
options.anonymous = options.anonymous || false;
let key = JSON.stringify(options);
let value = storage.getValue(key,options.type === REQUEST_TYPE_DOWNLOAD ? DOWNLOAD_EXPIRED_TIME : SEARCH_EXPIRED_TIME);
if(value !== undefined){
reslove(value);
}else{
getResponse(options).then(response => { storage.setValue(key,response); reslove(response)});
}
});
}
let sourceConfig = {
'zxcs8.post':{
bookName:(item) => {
return document.querySelector('h1').innerText.match('《(.*?)》')[1];
},
bookAuthor:(item) => {
return document.querySelector('h1').innerText.split(':').pop();
},
maxNum:MAX_SEARCH_NUM,
rateItem:(rate,rateNum,bookLink) => {
return '<p class="yinyong"><span style="font-size:14px;color:#FF0000;font-weight:bold">优书网评分: <a href = "' + bookLink + '" target="_blank">' + rate + '</a></span><p><p class="yinyong"><span style="font-size:14px;color:#FF0000;font-weight:bold">评分人数: ' + rateNum + '</span><p>';
},
anchorObj:(item) => {
let obj = document.querySelector('.yinyong');
return !obj ? document.querySelector('.pagefujian') : obj;
},
anchorPos:'beforebegin',
},
'zxcs8.sort':{
bookName:(item) => {
return item.firstElementChild.innerText.match('《(.*?)》')[1];
},
bookAuthor:(item) => {
return item.firstElementChild.innerText.split(':').pop();
},
maxNum:MAX_SEARCH_NUM,
rateItem:(rate,rateNum,bookLink) => {
return '<a href= "' + bookLink + '" target = "_blank"> 评分:' + rate + ' 人数:' + rateNum + '</a>'
},
anchorObj:(item) => {
return item.lastElementChild.querySelector('div');
},
anchorPos:'beforebegin',
},
'zadzs':{
bookName:(item) => {
return document.querySelector('h3[title]').title;
},
bookAuthor:(item) => {
return document.querySelector('h3[title]>span>a').innerText;
},
maxNum:MAX_SEARCH_NUM,
rateItem:(rate,rateNum,bookLink) => {
return '<tr><td width="42px">评分:</td><td><a href="'+ bookLink + '" target = "blank">'+ rate + '</a></td></tr><tr><td width="42px">人数:</td><td>' + rateNum + '</td></tr>';
},
anchorObj:(item) => {
return document.querySelector('.m-bookstatus>table>tbody');
},
anchorPos:"afterbegin",
},
'15huang.detail':{
bookName:(item) => {
return document.querySelector('h1.postss-title').innerText.match('《(.*?)》')[1];
},
bookAuthor:(item) => {
return document.querySelector('.novelright>a').innerText;
},
maxNum:MAX_SEARCH_NUM,
rateItem:(rate,rateNum,bookLink) => {
return '<div class="novelright">优书评分:<a href="'+ bookLink + '" target="_blank">' + rate + '</a></div><div class="novelright">评分人数:' + rateNum + '</div>';
},
anchorObj:(item) => {
return document.querySelector('.novelright>em').parentNode;
},
anchorPos:"afterend",
},
'15huang.category':{
bookName:(item) => {
return item.querySelector('h2.posts-title>a').innerText.match('《(.*?)》')[1];
},
bookAuthor:(item) => {
return item.querySelector('h2.posts-title>a').innerText.split('_').pop();
},
maxNum:MAX_SEARCH_NUM,
rateItem:(rate,rateNum,bookLink) => {
return '<li><i class="fa fa-star" style="color: #691b15;"></i> <a title="" target="_blank" href="' + bookLink + '">' + rate + '分</a></li><li><i class="fa fa-user-circle" style="color: #691b15;"></i> ' + rateNum + '人</li>';
},
anchorObj:(item) => {
return item.querySelector('ul.posts-meta');
},
anchorPos:"beforeend",
},
};
let sourceHandle = {
'zxcs8.post':(site,callback) => {
let option = {site:site,item:''};
return callback(option);
},
'zxcs8.sort':(site,callback) => {
let bookList = Array.prototype.slice.call(document.querySelectorAll('#plist'));
bookList.forEach((item) => {
let option = {site:site,item:item};
callback(option);
});
},
'zadzs':(site,callback) => {
let option = {site:site,item:''};
return callback(option);
},
'15huang.detail':(site,callback) => {
let option = {site:site,item:''};
return callback(option);
},
'15huang.category':(site,callback) => {
let bookList = Array.prototype.slice.call(document.querySelectorAll('article'));
bookList.forEach((item) => {
let option = {site:site,item:item};
callback(option);
});
},
};
let sourceRoute = {
'www.zxcs8.com':() => {
let tag = location.pathname.split('/')[1];
if(['sort','post'].includes(tag)){
return 'zxcs8.' + tag;
}
// 搜索页面
if(location.pathname.indexOf('index.php') !== -1){
return 'zxcs8.sort';
}
},
'zxcs8.com':() => {
let tag = location.pathname.split('/')[1];
if(['sort','post'].includes(tag)){
return 'zxcs8.' + tag;
}
// 搜索页面
if(location.pathname.indexOf('index.php') !== -1){
return 'zxcs8.sort';
}
},
'www.zadzs.com':() => {
return 'zadzs';
},
'www.15huang.com':() => {
let pathname = location.pathname;
// 分类页面
if(pathname.search(/index(_\d+)?\.html/ig) !== -1){
return '15huang.category';
}
// 搜索结果
if(pathname === '/e/search/result/'){
return '15huang.category';
}
// 作者
if(pathname.indexOf('tags-etagid') !== -1){
return '15huang.category';
}
// 详情页
if(pathname.indexOf('.html') !== -1){
return '15huang.detail';
}
return '15huang.category';
},
};
let downloadConfig = {
"zxcs8":{
'siteName':'知轩藏书',
'searchConfig':(args) => {
return {url:'http://www.zxcs8.com/index.php?keyword=' + args.bookName};
},
'bookList':(item) => {
return Array.prototype.slice.call(item.getElementsByTagName('dl'));
},
'bookName':(item) => {
return item.children["0"].innerText.match('《(.*?)》')[1];
},
'bookAuthor':(item) => {
return item.children["0"].innerText.split(':').pop();
},
'bookLink':(item) => {
return item.children["0"].getElementsByTagName('a')[0].href;
},
'downloadLink':(item) => {
return item.querySelector('.down_2>a').href;
},
},
"zadzs":{
'siteName':'早安电子书',
'searchConfig':(args) => {
return {url:'http://www.zadzs.com/plus/search.php?kwtype=0&q=' + args.bookName};
},
'bookList':(item) => {
return Array.prototype.slice.call(item.getElementsByClassName('searchItem'));
},
'bookName':(item) => {
return item.querySelector('.book>h5>a').innerText;
},
'bookAuthor':(item) => {
return item.querySelector('.book>.price').innerText.split(':').pop();
},
'bookLink':(item) => {
return 'http://www.zadzs.com' + item.querySelector('.book>.cover').pathname;
},
'downloadLink':(item) => {
return 'http://www.zadzs.com' + item.querySelector('.book>cover').href;
},
},
"nordfxs":{
'siteName':'龙凤互联',
'searchConfig':(args) => {
let form = new FormData();
form.append("formhash","dfae824e");
form.append("srchtxt",args.bookName);
form.append("searchsubmit","yes");
return {url:"http://www.nordfxs.com/search.php?mod=forum",data:form,method:"POST",'bookName':args.bookName};
},
'bookList':(item) => {
return Array.prototype.slice.call(item.getElementsByClassName('pbw'));
},
'bookName':(item) => {
return item.querySelector('.xs3>a').innerText.split('(').shift().replace(/[《,》]/g,'');
},
'bookAuthor':(item) => {
return item.querySelector('.xs3>a').innerText.split(':').pop();
},
'bookLink':(item) => {
return item.querySelector('.xs3>a').href;
},
'downloadLink':(item) => {
return item.querySelector('.xs3>a').href;
},
},
"15huang":{
'siteName':'书荒网',
'searchConfig':(args) => {
let data = 'show=title%2Cwriter&tbname=news&tempid=1&keyboard=' + encodeToGb2312(args.bookName,true) + '&Submit22=%CB%D1%CB%F7';
let headers = {"Content-Type": "application/x-www-form-urlencoded","Cookie":"olqyclastsearchtime=" + (Date.parse(new Date) / 1000 - 48)};
return {url:"http://www.15huang.com/e/search/index.php",data:data,method:"POST",headers:headers,bookName:args.bookName};
},
'bookList':(item) => {
return Array.prototype.slice.call(item.getElementsByClassName('posts type-post'));
},
'bookName':(item) => {
return item.querySelector('h2.posts-title>a').innerText.match('《(.*?)》')[1];
},
'bookAuthor':(item) => {
return item.querySelector('h2.posts-title>a').innerText.split('_').pop();
},
'bookLink':(item) => {
return item.querySelector('h2.posts-title>a').href;
},
'downloadLink':(item) => {
return item.querySelector('.showDown>li>a').href;
},
},
};
let downloadHandle = {
'zxcs8':(options) => {
let siteConfig = downloadConfig[options.site];
return new Promise((resolve,reject) => {
getResult({url:options.bookLink,type:REQUEST_TYPE_DOWNLOAD}).then((response) => {
let html = document.createElement( 'html' );
html.innerHTML = response;
let downloadLink = siteConfig.downloadLink(html);
resolve({downloadLink:downloadLink,siteName:siteConfig.siteName})
});
});
},
'zadzs':(options) => {
return new Promise((resolve,reject) => {
let args = {downloadLink:options.bookLink,siteName:downloadConfig[options.site].siteName};
resolve(args);
});
},
'nordfxs':(options) => {
return new Promise((resolve,reject) => {
let args = {downloadLink:options.bookLink,siteName:downloadConfig[options.site].siteName};
resolve(args);
});
},
'15huang':(options) => {
let siteConfig = downloadConfig[options.site];
return new Promise((resolve,reject) => {
getResult({url:options.bookLink,type:REQUEST_TYPE_DOWNLOAD}).then((response) => {
let html = document.createElement( 'html' );
html.innerHTML = response;
let downloadLink = siteConfig.downloadLink(html);
resolve({downloadLink:downloadLink,siteName:siteConfig.siteName})
});
});
},
};
let downloadRoute = {
'www.yousuu.com':() => {
return 'www.yousuu.com';
}
};
let insertDownloadButton = () => {
let select = '<div class="btn-group" id="download-list" style="display:none"><button type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-warning dropdown-toggle"><span>去下载</span> <span class="caret"></span></button><ul class="dropdown-menu" id="download-items"></ul></div>';
let obj = document.getElementsByClassName('sokk-book-button-groups')[0];
obj.insertAdjacentHTML('beforeend',select);
}
let removeElement = element => {
if(element){
let parent = element.parentNode;
if(parent){
parent.removeChild(element);
}
}
}
let addDownLoadLink = (options,config) => {
let siteConfig = config[options.site];
let youBookName = document.getElementsByClassName('col-sm-7')[0].children[0].innerText;
let youBookAuthor = document.getElementsByClassName('list-unstyled list-sm')["0"].firstChild.children["0"].innerText;
getResult(Object.assign(siteConfig.searchConfig({bookName:youBookName}),{type:REQUEST_TYPE_DOWNLOAD})).then((response) => {
let html = document.createElement( 'html' );
html.innerHTML = response;
let bookList = siteConfig.bookList(html)
if(bookList.length > 0){
bookList.some((item) => {
let bookLink = siteConfig.bookLink(item);
let matchBookName = siteConfig.bookName(item);
let matchBookAuthor = siteConfig.bookAuthor(item);
console.log(matchBookName + ':' + youBookName + ';' + matchBookAuthor + ':' + youBookAuthor)
if(matchBookName === youBookName && matchBookAuthor === youBookAuthor){
downloadHandle[options.site]({site:options.site,bookLink:bookLink}).then(insertDownLink);
}
});
}
})
};
let insertDownLink = options => {
let obj = document.querySelector('#download-items');
let item = '<li><a href="'+ options.downloadLink +'" target="_blank">'+ options.siteName + '</a></li>';
obj.insertAdjacentHTML('beforeend',item);
if(obj.parentElement.style.display === 'none'){
obj.parentElement.setAttribute('style', 'display:run-in');
}
}
let getRate = args => {
let bookName = args.bookName;
let bookAuthor = args.bookAuthor;
let maxNum = (args.maxNum === undefined) ? 1 : args.maxNum;
return new Promise((resolve,reject) => {
getResult({url:'http://www.yousuu.com/search/' + bookName + bookAuthor + '?type=all',type:REQUEST_TYPE_SOURCE}).then((response) => {
let rate = 0;
let rateNum = 0;
let bookLink = '';
let html = document.createElement( 'html' );
html.innerHTML = response;
//let matchBook = Array.slice(Array.prototype.slice.call(html.getElementsByClassName('col-lg-4 col-md-6 col-xs-12')),0,maxNum);
let matchBook = Array.prototype.slice.call(html.getElementsByClassName('col-lg-4 col-md-6 col-xs-12')).slice(0,maxNum);
matchBook.some((item) => {
let matchTitle = item.querySelector('.title');
let matchName = matchTitle.innerText;
let matchAbstract = item.querySelector('.abstract').innerHTML.split('<br>');
let matchAuthor = matchAbstract[0].split(':')[1].replace(/<\/?[^>]*>/g,'').trim();
console.log(matchAuthor + ':' + bookAuthor + ';' + matchName + ':' + bookName);
if(matchAuthor === bookAuthor && matchName === bookName){
rate = item.querySelector('.num2star').innerHTML;
rateNum = item.querySelector('.rating').innerText.replace(/[^0-9]/ig,"");
bookLink = 'http://www.yousuu.com' + matchTitle.querySelector('a').pathname;
return true;
}
});
resolve({rate:rate,rateNum:rateNum,bookLink:bookLink});
});
});
}
let insertRate = options => {
let siteConfig = sourceConfig[options.site];
let args = {bookName:siteConfig.bookName(options.item),bookAuthor:siteConfig.bookAuthor(options.item),maxNum:siteConfig.maxNum};
getRate(args).then((data) => {
let rate = data.rate;
let rateNum = data.rateNum;
let bookLink = data.bookLink;
if(rate !== 0 || rateNum !== 0){
let rateItem = siteConfig.rateItem(rate,rateNum,bookLink);
siteConfig.anchorObj(options.item).insertAdjacentHTML(siteConfig.anchorPos,rateItem);
}
});
}
(() => {
'use strict';
console.log(document.cookie);
if(Object.keys(sourceRoute).includes(location.hostname)){
let site = sourceRoute[location.hostname]();
sourceHandle[site](site,insertRate);
}
if(Object.keys(downloadRoute).includes(location.hostname)){
let sites = Object.keys(downloadConfig);
insertDownloadButton();
sites.forEach((item) => {
let options = {site:item};
addDownLoadLink(options,downloadConfig);
});
}
})();