Skip to content

Instantly share code, notes, and snippets.

@manciuszz
Last active January 17, 2020 23:34
Show Gist options
  • Save manciuszz/0b088ff98f22a1df29500185dcdeb70f to your computer and use it in GitHub Desktop.
Save manciuszz/0b088ff98f22a1df29500185dcdeb70f to your computer and use it in GitHub Desktop.
Webnovel.com - Preload and Sort search results without scrolling.
// ==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