Измененние UX сайта для удобного чтения и навигации
当前为
// ==UserScript==
// @name MangaLib helper
// @version 0.0.2
// @description Измененние UX сайта для удобного чтения и навигации
// @icon https://mangalib.me/icons/android-icon-192x192.png?333
// @match https://mangalib.me
// @grant unsafeWindow
// @grant GM.xmlHttpRequest
// @run-at document-end
// @require https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.js
// @require https://cdnjs.cloudflare.com/ajax/libs/lscache/1.3.2/lscache.min.js
// @namespace https://greasyfork.org/users/728771
// ==/UserScript==
/* jshint asi: true, esnext: true, -W097 */
(async function ($) {
'use strict'
lscache.flushExpired
unsafeWindow.$ = $;
unsafeWindow.GM_xmlhttpRequest = GM.xmlHttpRequest;
function waitForElm(selector) {
return new Promise(resolve => {
if (document.querySelector(selector)) return resolve(document.querySelector(selector));
const observer = new MutationObserver(mutations => {
if (document.querySelector(selector)) {
observer.disconnect();
resolve(document.querySelector(selector));
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}
const serviceSymbol = Symbol('serviceSymbol');
const funcs = {
[serviceSymbol]: {
senkuroDetails: async (slug) => {
return new Promise(resolve => {
return GM_xmlhttpRequest({
method: "POST",
url: "https://api.senkuro.com/graphql",
data: `{"extensions":{"persistedQuery":{"sha256Hash":"a44132a9483c73f8db43edf8d171c8e108de93ad3c990148f8474ffc546901e9","version":1}},"operationName":"fetchManga","variables":{"slug": "${slug}"}}`,
onload: function (response) {
const data = JSON.parse(response.responseText);
if (!data?.data?.manga) resolve();
if (data.data.manga?.branches.length) resolve(Math.max(...data.data.manga.branches.map(branch => branch.chapters)))
resolve(data.data.manga.chapters);
}
});
})
},
readmangaDetails: async (url) => {
return new Promise(resolve => {
return GM_xmlhttpRequest({
method: "GET",
url,
onload: async response => {
resolve($(response.responseText).find('.chapters tr a').eq(0)[0]?.firstChild.nodeValue.trim());
}
});
})
}
},
Senkuro: async (slug, titles) => {
if (lscache.get(`${slug}--senkuro`)) return lscache.get(`${slug}--senkuro`)
return new Promise(resolve => {
return GM_xmlhttpRequest({
method: "POST",
url: "https://api.senkuro.com/graphql",
data: `{"extensions":{"persistedQuery":{"sha256Hash":"a2ccb7472c4652e21a7914940cce335683d37abdecccfeb6bbf9674e0a5bda80","version":1}},"operationName":"search","variables":{"query":"${titles[0]}","type":"MANGA"}}`,
onload: async response => {
const data = JSON.parse(response.responseText);
const slug = data.data.search.edges[0]?.node.slug;
if (!slug) return;
const chapter = await funcs[serviceSymbol].senkuroDetails(slug);
if (!chapter) return;
lscache.set(`${slug}--senkuro`, [`https://senkuro.com/manga/${slug}/chapters`, chapter], 3600000)
resolve([`https://senkuro.com/manga/${slug}/chapters`, chapter])
}
});
})
},
Readmanga: async (slug, titles) => {
if (lscache.get(`${slug}--readmanga`)) return lscache.get(`${slug}--readmanga`)
return new Promise(resolve => {
return GM_xmlhttpRequest({
method: "GET",
url: `https://readmanga.live/search/suggestion?query=${$('.media-name__main').text()}`,
onload: async response => {
const data = JSON.parse(response.responseText);
const result = data.suggestions.find(suggestion => titles.filter(value => [suggestion.value, ...suggestion.names].includes(value)).length)
if (!result) return;
const slug = result.link;
const chapter = await funcs[serviceSymbol].readmangaDetails(`https://readmanga.live${slug}`);
if (!chapter) return;
lscache.set(`${slug}--readmanga`, [`https://readmanga.live${slug}#chapters-list`, chapter], 3600000)
resolve([`https://readmanga.live${slug}#chapters-list`, chapter])
}
});
})
}
}
// language=HTML
const dropdownDOM = `
<div class='dropdown button_block'>
<button class='dropbtn button button_primary button_block'>
Открыть на сайте
<i class='fa fa-caret-down'></i>
</button>
<div class='dropdown-content media-info-list paper'></div>
</div>
`;
async function mangaPage() {
await waitForElm('.media-name__main');
const slug = new URL(unsafeWindow.location).pathname.match(/[^\/]+/g)
const titles = [
$('.media-name__main').text().trim(),
$('.media-name__alt').text().trim(),
...$('.media-info-list__item_alt-names .media-info-list__value div').toArray().map(function (i) {
return i.innerText
})
]
const teamsWrapper = $('.media-chapters-teams')
const tab = $('.tabs__item[data-key="chapters"]');
let lastChapter = 0;
let team;
if (teamsWrapper.length) {
const teams = teamsWrapper.find('.team-list-item');
for (const node of teams.toArray()) {
const vue = node.__vue__;
const propsData = vue.$options.propsData;
const isSubscribed = propsData.branch.is_subscribed;
const lastTeamChapter = propsData.lastCreatedChapters[propsData.branch.id].chapter_number;
if (isSubscribed || lastChapter < lastTeamChapter) {
lastChapter = lastTeamChapter;
team = node;
if (isSubscribed) break;
}
}
} else lastChapter = $('.media-chapters .media-chapters-list .media-chapter')[0].__vue__.$options.propsData.chapter.chapter_number
const state = $('.media-sidebar button').text().trim();
let url = '';
switch (state) {
case 'Читаю':
tab.click();
$('.media-sidebar button').after(dropdownDOM);
$(".dropdown-content").hide();
$(".dropbtn").click(() => $(".dropdown-content").toggle());
for (const website in funcs) {
const url = await funcs[website](slug, titles);
if (url) $('.dropdown-content').append(`<a href="${url[0]}" class="media-info-list__item">${website} | ${url[1]}</a>`)
}
$(".dropdown-content a").click(() => $(".dropdown-content").hide());
break;
case 'Senkuro':
case 'Readmanga':
tab.click();
url = await funcs[state](slug, titles);
if (url) $('.media-sidebar button').after(`<a href="${url[0]}" class="button button_block button_primary">Продолжить на ${state} | ${url[1]}</a>`);
}
tab.text(`${tab.text()} | ${lastChapter}`);
setTimeout(() => $(team).click(), 100)
}
async function chapterPage() {
$(window).scroll(function () {
const scrolledTo = window.scrollY + window.innerHeight;
const isReachBottom = document.body.scrollHeight === scrolledTo;
if (isReachBottom) {
$('.reader-bookmark').not(".is-marked").click();
$('.reader-next__btn.button_label_right')[0]?.click();
}
});
}
$(document).ready(() => {
const path = new URL(unsafeWindow.location).pathname.replace(/^\//, '');
if (!path.includes('/')) mangaPage();
else if (path.match(/v\d+\/c\d+/)) chapterPage();
});
}).bind(this)(jQuery)
jQuery.noConflict()