Webnovel.com - Preload and Sort search results without scrolling.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name WebNovel.com | Full Search | |
// @description Sort your search results by chapter descending order of chapter number. | |
// @author Manciuszz | |
// @version 0.906 | |
// @match https://www.webnovel.com/category* | |
// @match https://www.webnovel.com/search* | |
// @match https://www.webnovel.com/tag* | |
// @match https://www.webnovel.com/ranking* | |
// @match https://www.webnovel.com/profile* | |
// @grant GM_getResourceText | |
// @resource ajaxThrottle https://raw.githubusercontent.com/nemac/ajaxthrottle/master/src/ajaxthrottle.js | |
// @updateURL https://gist.github.com/manciuszz/0b088ff98f22a1df29500185dcdeb70f/raw | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
let typeMap = (function() { | |
let type = (location.href.match(new RegExp("category|search|tag|ranking|profile")) || [])[0]; | |
let loadingIndicatorContainer = ({ | |
"category": ".j_bookList", | |
"search": ".j_list_container", | |
"tag": ".j_bookList", | |
"ranking": ".j_rank_list_wrap", | |
"profile": ".m-stories" | |
})[type]; | |
let dependencyPath = ({ | |
"category": "indexList", | |
"search": "indexSearch", | |
"tag": "indexList", | |
"ranking": "rankingPower" | |
})[type]; | |
let dependencyFnPath = ({ | |
"category": "getMoreBooks", | |
"search": "getMoreResults", | |
"tag": "getMoreBooks", | |
"ranking": "getMoreRank" | |
})[type]; | |
let maxPagesByType = ({ | |
"category": 30, | |
"search": 20, | |
"tag": 29, | |
"ranking": 20 | |
})[type]; | |
return { | |
type, | |
loadingIndicatorContainer, | |
dependencyPath, | |
dependencyFnPath, | |
maxPagesByType | |
}; | |
})(); | |
let config = { | |
maxPages: ((totalWantedResults) => (totalWantedResults / typeMap.maxPagesByType))( 300 ), | |
category: g_data.category, | |
status: ({"All": 0, "Completed": 2, "Ongoing": 1, "Default": parseInt(location.href.replace(/(.*)status=(.*)/g, "$2"))})["Default"], | |
bookType: g_data.bookType, | |
resolveLastUpdates: true | |
}; | |
let fetchData = (function() { | |
let ajaxThrottle = null; | |
return function(params) { | |
if (typeof $.ajaxthrottle === "undefined") { | |
window.eval(GM_getResourceText("ajaxThrottle")); | |
ajaxThrottle = $.ajaxthrottle({ | |
maxConcurrent: 5, | |
timePeriod: 0, | |
numRequestsPerTimePeriod: 0 | |
}); | |
} | |
ajaxThrottle.ajax({ | |
type: "GET", | |
url: params.query, | |
success: function(jsonObject) { | |
try { | |
params.callbackFn(jsonObject); | |
} catch {} | |
}, | |
error: function(errMsg) { | |
console.log(errMsg); | |
}, | |
...params | |
}); | |
}; | |
})(); | |
let getTotalIterations = function(params) { | |
if (typeof params.callbackFn !== "function" && typeof params.totalPath !== "function") | |
return console.log("[getIterations] Bad callback function input."); | |
let itemsPerPage = params.itemsPerPage; | |
return fetchData({query: params.query, callbackFn: function(jsonObject) { | |
let total = params.totalPath(jsonObject); | |
let pages = -~(total / itemsPerPage); | |
params.callbackFn(pages, total); | |
}}); | |
} | |
let processBooks = function(params) { | |
if (typeof params.complete !== "function") | |
return console.log("[processBooks] Bad callback function input."); | |
let createQuery = function(bookId) { | |
return `https://www.webnovel.com/book/${bookId}`; | |
}; | |
let getInfo = function(bookId, callbackFn) { | |
return fetchData({ | |
query: createQuery(bookId), | |
dataType: "html", | |
callbackFn: function(html) { | |
let page = $(html.replace(/\n/g,"")); | |
let g_data = (() => { | |
let scriptCode = "let g_data = {};"; | |
scriptCode += page.filter("script:contains(g_data.book)").text(); | |
scriptCode += "return g_data;"; | |
let g_data = window.eval(`new Function(${JSON.stringify(scriptCode)})()`); | |
return g_data; | |
})(); | |
let details = page.find(".det-hd-detail").children(":not(:contains(week))"); | |
let [genre, chapters, views] = details.get().map(function(child) { return $(child).text().trim() }); | |
callbackFn([genre, chapters, views], g_data); | |
}, | |
error: function (xhr, ajaxOptions, thrownError) { | |
callbackFn((["Unknown", "0", "0"], {})); | |
} | |
}); | |
}; | |
let updateStatus = (function() { | |
if (typeof params.statusCallbackFn !== "function") | |
return (() => {}); | |
return params.statusCallbackFn; | |
})(); | |
return getTotalIterations({ | |
query: params.query(), | |
itemsPerPage: params.itemsPerPage || typeMap.maxPagesByType, | |
totalPath: (jsonObject) => ( params.totalPath || (() => jsonObject.data.total) )(jsonObject), | |
maxPages: params.maxPages || 20, | |
callbackFn: function(totalPages, totalResults) { | |
let promises = []; | |
for (let i=1; i <= Math.min(totalPages, this.maxPages); i++) { // Note: we don't want to loop over ALL 5000+ books... | |
promises.push( | |
new Promise(function(resolve, reject) { | |
fetchData({ | |
query: params.query(i), | |
callbackFn: function(jsonObject, bookItems = params.itemPath(jsonObject)) { | |
resolve(bookItems); | |
} | |
}); | |
}) | |
); | |
} | |
Promise.all(promises).then((items) => { | |
this.returnFn(items.flat(), totalResults); | |
}); | |
}, | |
returnFn: function(bookItems, totalResults) { | |
let promises = []; | |
let totalBookCount = 1; | |
let maxBookCount = bookItems.length; | |
for (let bookItem of bookItems) { | |
promises.push( | |
new Promise(function(resolve, reject) { | |
getInfo(bookItem.bookId, function(page, g_data) { | |
let itemObj = {bookItem: bookItem, page: page, g_data: g_data}; | |
updateStatus(totalBookCount++, maxBookCount, itemObj, totalResults); | |
resolve(itemObj); | |
}); | |
}) | |
); | |
} | |
Promise.all(promises).then((items) => { | |
this.resolvedItemsFn(items); | |
}); | |
}, | |
resolvedItemsFn: function(items) { | |
params.complete(items); | |
}, | |
}); | |
}; | |
let loadingIndicator = function() { | |
if (!$("#loadingIndicator").length) | |
$(typeMap.loadingIndicatorContainer).prepend("<h3 id='loadingIndicator' class='mb8 g_h3'><span class='status'>Preparing...</span> <span class='total'></span></h3>") | |
let scriptBody = (function() { | |
let customOptions = `{ | |
maxPages: ((totalWantedResults) => (totalWantedResults / ${typeMap.maxPagesByType}))( bookCount ), | |
category: ${g_data.category}, | |
status: ({"All": 0, "Completed": 2, "Ongoing": 1, "Default": parseInt(location.href.replace(/(.*)status=(.*)/g, "$2"))})["Default"], | |
bookType: ${g_data.bookType}, | |
resolveLastUpdates: true | |
}`; | |
return function() { | |
let bookCount = parseInt(prompt("How many books would you like to search for?", 999)); | |
if (!bookCount) | |
return; | |
let customOptions = "_INSERT_customOptions"; | |
window.name = JSON.stringify(customOptions); | |
window.location.reload(); | |
}.toString().replace("\"_INSERT_customOptions\"", customOptions); | |
})(); | |
let scriptStr = ["(", scriptBody, ")", "()"].join(""); | |
let counter = 0, indicatorStatus = $("#loadingIndicator > span.status"), indicatorTotal = $("#loadingIndicator > span.total"), loadingIndicatorId = null; | |
return { | |
loadingMsg: function(msg) { | |
counter++; | |
indicatorStatus.text(msg + (".".repeat(counter % 4))); | |
}, | |
start: function() { | |
loadingIndicatorId = setInterval(() => this.loadingMsg("Loading"), 1000); | |
return this; | |
}, | |
clearTimer: function() { | |
if (!counter || !loadingIndicatorId) | |
return; | |
counter = 0; | |
clearInterval(loadingIndicatorId); | |
loadingIndicatorId = null; | |
}, | |
stop: function() { | |
this.clearTimer(); | |
loadingIndicatorId = setInterval(() => this.loadingMsg("Finished loading!"), 1000); | |
setTimeout(() => { | |
$("#loadingIndicator").remove(); | |
this.clearTimer(); | |
}, 3000); | |
return this; | |
}, | |
change: function(min, max, total) { | |
this.clearTimer(); | |
indicatorStatus.text(`Loaded ${min} of ${max}`); | |
if (!indicatorTotal.text().length) | |
indicatorTotal.html(`(Total Books: <a href='javascript:${scriptStr}'>${max < total ? max : total}</a>)`); | |
} | |
} | |
}; | |
(function(options) { | |
let doSearch = function() { | |
if (window.name.indexOf("maxPages") != -1) { | |
options = JSON.parse(window.name); | |
} | |
window.name = ""; | |
let indicator = loadingIndicator(); | |
let replaceContent = function(items, selector, cardFn) { | |
let results = $(selector); | |
results.children().remove(); | |
let sortedItems = items.sort(function(a, b) { | |
let aChapters = parseInt(a.page[1].replace(",", "")); | |
let bChapters = parseInt(b.page[1].replace(",", "")); | |
return bChapters - aChapters; | |
}); | |
for (let data of sortedItems) { | |
results.append(cardFn(data)); | |
} | |
}; | |
let disableScrollHandler = function() { | |
let LBF_Paths = { | |
matchPath: function(pathRegex) { | |
let regex = new RegExp('/' + pathRegex, 'g'); | |
let matchedPaths = Object.keys(LBF.cache).filter((path, id) => path.match(regex)); | |
return matchedPaths[0]; | |
}, | |
get indexSearch() { return this.matchPath('en/js/search/index.*.js'); }, | |
get indexList() { return this.matchPath('en/js/list/index.*.js'); }, | |
get rankingPower() { return this.matchPath('en/js/ranking/power.*.js'); } | |
}; | |
LBF.require(LBF_Paths[typeMap.dependencyPath]).prototype[typeMap.dependencyFnPath] = function(a) { | |
return true; | |
}; | |
}; | |
if (typeMap.type === "category") { | |
let makeCategoryQuery = function(params) { | |
params.orderBy = params.orderBy || 1; | |
let fullQuery = ""; | |
for(let key in params) { | |
fullQuery += `&${key}=${params[key]}`; | |
} | |
return `https://www.webnovel.com/apiajax/category/ajax?orderBy=${params.orderBy}${fullQuery}`; | |
} | |
let createCard = function(data) { | |
return `<li class="g_col_6"> | |
<div class="g_book_item"> | |
<a class="c_000" href="//www.webnovel.com/book/${data.bookItem.bookId}/${data.bookItem.bookName}" title="${data.bookItem.bookName}" data-report-eid="qi_E01" data-report-bid="${data.bookItem.bookId}"> | |
<i class="g_thumb g_thumb_bg pa l0 oh"> | |
<img src="//img.webnovel.com/bookcover/${data.bookItem.bookId}/150/150.jpg?coverUpdateTime=${data.bookItem.coverUpdateTime}" data-original="//img.webnovel.com/bookcover/${data.bookItem.bookId}/150/150.jpg?coverUpdateTime=${data.bookItem.coverUpdateTime}" data-original2="//img.webnovel.com/bookcover/${data.bookItem.bookId}/300/300.jpg?coverUpdateTime=${data.bookItem.coverUpdateTime}" width="140" height="186" alt="${data.bookItem.bookName}" style="display: block;"> | |
</i> | |
<h3 class="mb8 pb4 pt4 g_h3 ell2row">${data.bookItem.bookName}</h3> | |
</a> | |
<p class="c_s mb8 g_tags"> | |
<a href="/category/list?category=${data.bookItem.categoryName}" data-cate="A14" data-report-eid="E06" data-cateid="" title="">${data.page.join(" | ")} | Rated: ${data.bookItem.totalScore}</a> | |
</p> | |
${(function(enabled) { | |
if (!enabled) return ''; | |
let elementId = `status-${data.bookItem.bookId}`; | |
fetchData({ | |
query: `https://www.webnovel.com/apiajax/chapter/GetChapterList?bookId=${data.bookItem.bookId}`, | |
callbackFn: (jsonData) => { | |
let spanElement = $(`#${elementId}`); | |
spanElement.text(`Last Update: ${jsonData.data.bookInfo.newChapterTime} `); | |
spanElement.parent().css({'display': '', 'backgroundColor': ((chapterTime) => chapterTime.match(/second|minute|hour|day/) ? '#b7ff29' : chapterTime.match(/week/) ? '#ff8d29' : '#ed424b')(jsonData.data.bookInfo.newChapterTime)}); | |
} | |
}); | |
return ` | |
<p style="background-color: #ff8d29; color: #000000; padding-left: 10px; border: 1px solid black; width: calc(100% - 40px); text-align: center; display:none;"> | |
<span id="${elementId}">Pending check...</span> | |
</p> | |
`; | |
})(options.resolveLastUpdates)} | |
<a href="//www.webnovel.com/book/${data.bookItem.bookId}/${data.bookItem.bookName}" class="fs16 lh1d5 c_000 oh _txt" title="${data.bookItem.description}">${data.bookItem.description}</a> | |
</div> | |
</li>`; | |
}; | |
let replaceContentItem = (function() { | |
let children = $(".j_bookList > .g_row").children(); | |
return { | |
change: function(index, cardFn, data) { | |
children.eq(index - 1).replaceWith(cardFn(data)); | |
} | |
}; | |
})(); | |
indicator.start(); | |
processBooks({ | |
maxPages: options.maxPages, | |
query: (pageIndex) => makeCategoryQuery({orderBy: 1, pageIndex: pageIndex || 1, bookType: options.bookType, status: options.status, category: options.category}), | |
itemPath: (jsonObject) => jsonObject.data.items, | |
statusCallbackFn: function(totalBookCount, maxBookCount, latestItem, totalResults) { | |
replaceContentItem.change(totalBookCount, createCard, latestItem); | |
indicator.change(totalBookCount, maxBookCount, totalResults); | |
}, | |
complete: function(items) { | |
disableScrollHandler(); | |
replaceContent(items, ".j_bookList > .g_row", createCard); | |
indicator.stop(); | |
} | |
}); | |
} else if (typeMap.type === "tag") { | |
let makeTagQuery = function(params) { | |
params.orderBy = params.orderBy || 1; | |
let fullQuery = ""; | |
for(let key in params) { | |
fullQuery += `&${key}=${params[key]}`; | |
} | |
return `https://www.webnovel.com/apiajax/tag/ajax?orderBy=${params.orderBy}${fullQuery}`; | |
} | |
let createCard = function(data) { | |
return `<li class="g_col_6"> | |
<div class="g_book_item"> | |
<a class="c_000" href="//www.webnovel.com/book/${data.bookItem.bookId}/${data.bookItem.bookName}" title="${data.bookItem.bookName}" data-report-eid="qi_E01" data-report-bid="${data.bookItem.bookId}"> | |
<i class="g_thumb g_thumb_bg pa l0 oh"> | |
<img src="//img.webnovel.com/bookcover/${data.bookItem.bookId}/150/150.jpg?coverUpdateTime=${data.bookItem.coverUpdateTime}" data-original="//img.webnovel.com/bookcover/${data.bookItem.bookId}/150/150.jpg?coverUpdateTime=${data.bookItem.coverUpdateTime}" data-original2="//img.webnovel.com/bookcover/${data.bookItem.bookId}/300/300.jpg?coverUpdateTime=${data.bookItem.coverUpdateTime}" width="140" height="186" alt="${data.bookItem.bookName}" style="display: block;"> | |
</i> | |
<h3 class="mb8 pb4 pt4 g_h3 ell2row">${data.bookItem.bookName}</h3> | |
</a> | |
<p class="c_s mb8 g_tags"> | |
<a href="/category/list?category=${data.bookItem.categoryName}" data-cate="A14" data-report-eid="E06" data-cateid="" title="">${data.page.join(" | ")} | Rated: ${data.bookItem.totalScore}</a> | |
</p> | |
${(function(enabled) { | |
if (!enabled) return ''; | |
let elementId = `status-${data.bookItem.bookId}`; | |
fetchData({ | |
query: `https://www.webnovel.com/apiajax/chapter/GetChapterList?bookId=${data.bookItem.bookId}`, | |
callbackFn: (jsonData) => { | |
let spanElement = $(`#${elementId}`); | |
spanElement.text(`Last Update: ${jsonData.data.bookInfo.newChapterTime} `); | |
spanElement.parent().css({'display': '', 'backgroundColor': ((chapterTime) => chapterTime.match(/second|minute|hour|day/) ? '#b7ff29' : chapterTime.match(/week/) ? '#ff8d29' : '#ed424b')(jsonData.data.bookInfo.newChapterTime)}); | |
} | |
}); | |
return ` | |
<p style="background-color: #ff8d29; color: #000000; padding-left: 10px; border: 1px solid black; width: calc(100% - 40px); text-align: center; display:none;"> | |
<span id="${elementId}">Pending check...</span> | |
</p> | |
`; | |
})(options.resolveLastUpdates)} | |
<a href="//www.webnovel.com/book/${data.bookItem.bookId}/${data.bookItem.bookName}" class="fs16 lh1d5 c_000 oh _txt" title="${data.bookItem.description}">${data.bookItem.description}</a> | |
</div> | |
</li>`; | |
}; | |
let replaceContentItem = (function() { | |
let children = $(".j_bookList > .g_row").children(); | |
return { | |
change: function(index, cardFn, data) { | |
children.eq(index - 1).replaceWith(cardFn(data)); | |
} | |
}; | |
})(); | |
indicator.start(); | |
processBooks({ | |
maxPages: options.maxPages, | |
query: (pageIndex) => makeTagQuery({orderBy: 1, pageIndex: pageIndex || 1, tagId: g_data.tagId, tagName: g_data.tagName, type: 1}), | |
totalPath: (jsonObject) => options.maxPages * typeMap.maxPagesByType, // couldn't find the total value for tag search | |
itemPath: (jsonObject) => jsonObject.data.items, | |
statusCallbackFn: function(totalBookCount, maxBookCount, latestItem, totalResults) { | |
replaceContentItem.change(totalBookCount, createCard, latestItem); | |
indicator.change(totalBookCount, maxBookCount, totalResults); | |
}, | |
complete: function(items) { | |
disableScrollHandler(); | |
replaceContent(items, ".j_bookList > .g_row", createCard); | |
indicator.stop(); | |
} | |
}); | |
} else if (typeMap.type === "search") { | |
let makeSearchQuery = function(query, pageIndex = 0) { | |
return `https://www.webnovel.com/apiajax/search/PageAjax?pageIndex=${pageIndex}&keywords=${query}`; | |
}; | |
let createCard = function(data) { | |
return `<li class="pr pb20 mb12"> | |
<a class="g_thumb g_thumb_mi pa l0 oh" href="//www.webnovel.com/book/${data.bookItem.bookId}/${data.bookItem.bookName}" title="${data.bookItem.bookName}" data-report-eid="qi_G01" data-bid="${data.bookItem.bookId}" data-report-bid="${data.bookItem.bookId}" data-search-type="book" data-bookid="${data.bookItem.bookId}" data-bookname="${data.bookItem.bookName}"> | |
<img src="//img.webnovel.com/bookcover/${data.bookItem.bookId}/150/150.jpg?coverUpdateTime=${data.bookItem.coverUpdateTime}" data-original="//img.webnovel.com/bookcover/${data.bookItem.bookId}/150/150.jpg?coverUpdateTime=${data.bookItem.coverUpdateTime}" data-original2="//img.webnovel.com/bookcover/${data.bookItem.bookId}/300/300.jpg?coverUpdateTime=${data.bookItem.coverUpdateTime}" width="140" height="186" alt="${data.bookItem.bookName}"> | |
</a> | |
<h3 class="mb8 g_h3"><a href="//www.webnovel.com/book/${data.bookItem.bookId}/${data.bookItem.bookName}" class="c_000" title="${data.bookItem.bookName}" data-bookid="${data.bookItem.bookId}" data-bookname="${data.bookItem.bookName}" data-bid="${data.bookItem.bookId}" data-report-bid="${data.bookItem.bookId}">${data.bookItem.bookName}</a></h3> | |
<p class="mb8 g_tags"> | |
<a href="/category/list?category=${data.bookItem.categoryName}" title="">${data.page.join(" | ")} | Rated: ${data.bookItem.totalScore}</a> | |
</p> | |
<p class="fs16 c_000 g_ells">${data.bookItem.description}</p> | |
</li>`; | |
}; | |
indicator.start(); | |
processBooks({ | |
maxPages: options.maxPages, | |
query: (pageIndex) => makeSearchQuery(g_data.kw, pageIndex), | |
itemPath: (jsonObject) => jsonObject.data.bookInfo.bookItems, | |
totalPath: (jsonObject) => jsonObject.data.bookInfo.total, | |
complete: function(items) { | |
disableScrollHandler(); | |
replaceContent(items, ".j_result_wrap", createCard); | |
indicator.stop(); | |
}, | |
statusCallbackFn: function(totalBookCount, maxBookCount, latestItem, totalResults) { | |
indicator.change(totalBookCount, maxBookCount, totalResults); | |
}, | |
}); | |
} else if (typeMap.type === "ranking") { | |
let makeRankingQuery = function(pageIndex = 1, type = 4) { | |
return `https://www.webnovel.com/apiajax/ranking/getRanking?pageIndex=${pageIndex}&type=${type}`; | |
}; | |
let createCard = function(data) { | |
return `<li class="pr"> | |
<div class="top20-item clearfix"> | |
<span class="_rank_num _big">${data.bookItem.ranking}</span> | |
<a class="g_thumb fl" href="//www.webnovel.com/book/${data.bookItem.bookId}/${data.bookItem.bookName}" title="${data.bookItem.bookName}" data-report-eid="qi_J22" data-report-bid="${data.bookItem.bookId}"> | |
<img src="//img.webnovel.com/bookcover/${data.bookItem.bookId}/150/150.jpg?coverUpdateTime=${data.bookItem.coverUpdateTime}" srcset="//img.webnovel.com/bookcover/${data.bookItem.bookId}/180/180.jpg?coverUpdateTime=${data.bookItem.coverUpdateTime}" width="90" height="120" alt="${data.bookItem.bookName}"> | |
</a> | |
<div class="text"> | |
<a href="//www.webnovel.com/book/${data.bookItem.bookId}/${data.bookItem.bookName}" title="${data.bookItem.bookName}" class="_link" data-report-eid="qi_J22" data-report-bid="${data.bookItem.bookId}"><h4 class="ell">${data.bookItem.bookName}</h4></a> | |
<p class="ell fs14 c_s _aut"> | |
<span>Author: <a href="//www.webnovel.com/profile/${data.g_data.book.bookInfo.authorItems[0].guid}">${data.bookItem.authorName}</a></span> | |
</p> | |
<div class="mb4 g_star_num"> | |
<span class="g_star"><svg class="_on"><use xlink:href="#i-star"></use></svg></span> | |
<small class="fs16">${data.bookItem.totalScore}</small> | |
<span><b> | ${data.page.join(" | ")}</b></span> | |
${(function(enabled) { | |
if (!enabled) return ''; | |
let elementId = `status-${data.bookItem.bookId}`; | |
fetchData({ | |
query: `https://www.webnovel.com/apiajax/chapter/GetChapterList?bookId=${data.bookItem.bookId}`, | |
callbackFn: (jsonData) => { | |
let spanElement = $(`#${elementId}`); | |
spanElement.text(`Last Update: ${jsonData.data.bookInfo.newChapterTime} `); | |
spanElement.parent().css({'display': '', 'backgroundColor': ((chapterTime) => chapterTime.match(/second|minute|hour|day/) ? '#b7ff29' : chapterTime.match(/week/) ? '#ff8d29' : '#ed424b')(jsonData.data.bookInfo.newChapterTime)}); | |
} | |
}); | |
return ` | |
<p style="background-color: #ff8d29; color: #000000; padding-left: 10px; border: 1px solid black; width: calc(100% - 40px); text-align: center; display:none;"> | |
<span title="${data.bookItem.tagInfo.map((v) => v.tagName).join(" | ")}" id="${elementId}">Pending check...</span> | |
</p> | |
`; | |
})(options.resolveLastUpdates)} | |
</div> | |
<div class="_link_op" style="position: relative;"> | |
<a href="//www.webnovel.com/book/${data.bookItem.bookId}/${data.bookItem.firstChapterId}" title="read" class="_read" data-report-eid="qi_J31" data-report-bid="${data.bookItem.bookId}">Read</a> | |
<i class="_hr vam">|</i> | |
<a href="javascript:" title="Add to Library" class="j_add_to_lib lib-link fs14" data-bookid="${data.bookItem.bookId}" data-report-eid="qi_J32" data-report-bid="${data.bookItem.bookId}" data-ntype="0"></a> | |
</div> | |
</div> | |
<div class="left-area"> | |
<div class="_power_stone fs24 mb16 tac"> | |
<i class="i_rank_icon _power"></i><strong class="vam j_total_book_ps" data-total="${data.bookItem.amount}">${data.bookItem.amount}</strong> | |
</div> | |
<a href="javascript:" title="Vote Power Stone" class="bt _warning _vote j_vote_power " data-bookid="${data.bookItem.bookId}" data-bookname="${data.bookItem.bookName}" data-noveltype="0" data-report-eid="qi_J25" data-report-bid="${data.bookItem.bookId}" data-report-cid=""> | |
<strong>Vote Power Stone (<small class="j_ps_num">${g_data.login.user.PS}</small>)</strong> | |
</a> | |
<a href="javascript:" title="Vote Power Stone" class="bt _warning _2row _dis_vote _on" ${(g_data.login.user.PS == "0" ? `disabled=""` : "")} data-bookid="${data.bookItem.bookId}" data-bookname="${data.bookItem.bookName}" data-noveltype="0" data-report-eid="qi_J25" data-report-bid="${data.bookItem.bookId}" data-report-cid=""> | |
<strong>Vote Power Stone (${g_data.login.user.PS})</strong> | |
</a> | |
<a href="javascript:" title="Vote Power Stone" class="bt _login _warning j_login " data-report-cid="" data-report-eid="qi_J25" data-report-bid="${data.bookItem.bookId}"> | |
<strong> | |
Vote | |
</strong> | |
</a> | |
</div> | |
</div> | |
</li>`; | |
}; | |
indicator.start(); | |
processBooks({ | |
maxPages: options.maxPages, | |
query: (pageIndex) => makeRankingQuery(pageIndex, g_data.curListInfo.type), | |
itemPath: (jsonObject) => jsonObject.data.rankingItems, | |
totalPath: (jsonObject) => jsonObject.data.total, | |
complete: function(items) { | |
disableScrollHandler(); | |
$(".top3-wrap").remove(); | |
replaceContent(items, "div.j_rank_list_wrap > .top20-wrap:first", createCard); | |
indicator.stop(); | |
}, | |
statusCallbackFn: function(totalBookCount, maxBookCount, latestItem, totalResults) { | |
indicator.change(totalBookCount, maxBookCount, totalResults); | |
}, | |
}); | |
} else if (typeMap.type === "profile") { | |
let makeProfileQuery = function() { | |
return window.location.href; | |
}; | |
let replaceContentNoSort = function(items, selector, cardFn) { | |
let results = $(selector); | |
results.children().remove(); | |
for (let data of items) { | |
results.append(cardFn(data)); | |
} | |
}; | |
let createCard = function(data) { | |
return `<li class="g_book_item g_book_item_sm"> | |
<a class="c_000" href="//www.webnovel.com/book/${data.bookId}/${data.bookName}" title="${data.bookName}" data-report-eid="" data-report-bid="${data.bookId}"> | |
<i class="g_thumb g_thumb_sm pa l0 oh"><img src="//img.webnovel.com/bookcover/${data.bookId}/300/300.jpg?coverUpdateTime=${data.coverUpdateTime}" data-original="//img.webnovel.com/bookcover/${data.bookId}/150/150.jpg?coverUpdateTime=${data.coverUpdateTime}" data-original2="//img.webnovel.com/bookcover/${data.bookId}/300/300.jpg?coverUpdateTime=${data.coverUpdateTime}" width="140" height="186" alt="${data.bookName}" style="display: block;"></i> | |
<h3 class="pb4 pt4 mb8 g_h3 ell2row" style="word-wrap: break-word;">${data.bookName}</h3> | |
</a> | |
<p class="c_s mb8 g_tags"> | |
<a href="/category/list?categoryId=${data.categoryId}" data-cate="A14" data-report-eid="" title="${data.categoryName}">${data.categoryName}</a> | |
</p> | |
<p class="g_star_num db mb8"> | |
<span class="g_star"><svg class="_on"><use xlink:href="#i-star"></use></svg></span> | |
<small>${data.totalScore}</small> | |
${(function(enabled) { | |
if (!enabled) return ''; | |
let elementId = `status-${data.bookId}`; | |
fetchData({ | |
query: `https://www.webnovel.com/apiajax/chapter/GetChapterList?bookId=${data.bookId}`, | |
callbackFn: (jsonData) => { | |
let totalChapterNum = jsonData.data.bookInfo.totalChapterNum; | |
let spanElement = $(`#${elementId}`); | |
spanElement.text(`Last Update: ${jsonData.data.bookInfo.newChapterTime} | Chapters: ${totalChapterNum}`); | |
spanElement.parent().css({'display': '', 'backgroundColor': ((chapterTime) => chapterTime.match(/second|minute|hour|day/) ? '#b7ff29' : chapterTime.match(/week/) ? '#ff8d29' : '#ed424b')(jsonData.data.bookInfo.newChapterTime)}); | |
data.page = [0, totalChapterNum + " Chapters", 0]; | |
} | |
}); | |
return ` | |
<p style="background-color: #ff8d29; color: #000000; padding-left: 10px; border: 1px solid black; width: calc(100% - 40px); text-align: center; display:none;"> | |
<span title="${data.tagInfo.map((v) => v.tagName).join(" | ")}" id="${elementId}">Pending check...</span> | |
</p> | |
`; | |
})(options.resolveLastUpdates)} | |
</p> | |
<p class="fs16 lh1d5 c_000 oh _txt" title="${data.description}">${data.description}</p> | |
</li>`; | |
}; | |
let callbackFn = function(g_data) { | |
$("[for='origStorySwitch']").click(); | |
replaceContentNoSort(g_data.profile.writeItems, "", createCard); | |
let tempId = setInterval(() => { | |
if (typeof g_data.profile.writeItems[g_data.profile.writeItems.length - 1].page !== "undefined") { | |
replaceContent(g_data.profile.writeItems, ".m-stories > ul", createCard); | |
clearInterval(tempId); | |
tempId = null; | |
indicator.stop(); | |
} | |
}, 100); | |
}; | |
indicator.start(); | |
fetchData({ | |
query: makeProfileQuery(), | |
dataType: "html", | |
callbackFn: function(html) { | |
let page = $(html.replace(/\n/g,"")); | |
let g_data = (() => { | |
let scriptCode = "let g_data = {};"; | |
scriptCode += page.filter("script:contains(g_data.profile)").text(); | |
scriptCode += "return g_data;"; | |
let g_data = window.eval(`new Function(${JSON.stringify(scriptCode)})()`); | |
return g_data; | |
})(); | |
callbackFn(g_data); | |
}, | |
error: function (xhr, ajaxOptions, thrownError) { | |
callbackFn((["Unknown", "0", "0"], {})); | |
} | |
}); | |
} | |
}; | |
window.onload = doSearch; | |
})(config); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment