// ==UserScript==
// @name Manga OnlineViewer
// @author Tago
// @supportURL https://github.com/TagoDR/MangaOnlineViewer/issues
// @namespace https://github.com/TagoDR
// @description Shows all pages at once in online view for these sites
// @version 2023.06.20
// @license MIT
// @grant unsafeWindow
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_listValues
// @grant GM_deleteValue
// @grant GM_xmlhttpRequest
// @noframes on
// @connect *
// @require https://cdnjs.cloudflare.com/ajax/libs/tinycolor/1.6.0/tinycolor.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery.imagesloaded/5.0.0/imagesloaded.pkgd.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.9.1/jszip.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/nprogress/0.2.0/nprogress.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/11.4.8/sweetalert2.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js
// @require https://cdn.jsdelivr.net/npm/hotkeys-js@3.10.2/dist/hotkeys.min.js
// @require https://cdn.jsdelivr.net/npm/range-slider-input@2.4.4/dist/rangeslider.nostyle.umd.min.js
// @include /https?:\/\/beta.asurascans.com\/read\/.+\/.+/
// @include /https?:\/\/(www.)?bato.to\/chapter.*/
// @include /https?:\/\/(www.)?(bilibilicomics).com\/.+\/.+/
// @include /https?:\/\/(www.)?comicastle.org\/read\/.+\/\d+.*/
// @include /https?:\/\/(www.)?dynasty-scans.com\/chapters\/.+/
// @include /https?:\/\/(www.)?(asurascans|flamescans|realmscans|void-scans|luminousscans|shimascans|nightscans).(com|org|gg)\/.+/
// @include /https?:\/\/(comics.)?inkr.com\/title\/.+\/chapter\/.+/
// @include /https?:\/\/(www.)?inmanga.com\/ver\/manga\/.+\/.+/
// @include /https?:\/\/(www.)?klmanga.com\/.+chapter.+/
// @include /https?:\/\/(www.)?leitor.net\/manga\/.+\/.+\/.+/
// @include /https?:\/\/(www|en)?.?leviatanscans.com\/(home\/)?manga\/.+\/chapter.+/
// @include /https?:\/\/(www.)?lhtranslation.net\/read.+/
// @include /https?:\/\/(www.)?lynxscans.com\/comics?\/.+/
// @include /https?:\/\/(www.)?mangabuddy.com\/.+\/chapter.+/
// @include /https?:\/\/(www.)?mangadex.org\/chapter\/.+(\/.+)?/
// @include /https?:\/\/(www.)?(fanfox.net|mangahere.cc)\/manga\/.+\/.+\//
// @include /https?:\/\/.{3,4}?(mangafreak).net\/Read.+/
// @include /https?:\/\/(www.)?mangago.me\/.*\/.*\/.*/
// @include /https?:\/\/(www.)?mangahosted.com\/manga\/.+\/.+/
// @include /https?:\/\/(www.)?(mangahub).io\/chapter\/.+\/.+/
// @include /https?:\/\/(www.)?((manganelo|mangakakalot).com\/chapter\/.+\/.+|(manganato|readmanganato|chapmanganato).com\/manga-\w\w\d+\/chapter-\d+)/
// @include /https?:\/\/(www.)?mangapark.(com|me|org|net)\/title\/.+\/.+/
// @include /https?:\/\/(www.)?mangareader.to\/read\/.+\/.+\/.+/
// @include /https?:\/\/(www.)?(mangasee123|manga4life).com\/read-online\/.+/
// @include /https?:\/\/(www.)?mangatigre.net\/.+\/.+\/.+/
// @include /https?:\/\/.*mangatoon.mobi\/.+\/watch\/.+/
// @include /https?:\/\/(www.|m.)?mangatown.com\/manga\/.+\/.+/
// @include /https?:\/\/(www.)?manhuascan.io\/.+chapter.+/
// @include /https?:\/\/(www.)?(mreader|mangageko).com?\/reader\/.*/
// @include /https?:\/\/(www.)?(naniscans).com\/chapters\/.+\/read/
// @include /https?:\/\/(www.)?ninemanga.com\/chapter\/.+\/.+\.html/
// @include /https?:\/\/(www.)?olympusscans.com\/capitulo\/.+\/.+/
// @include /https?:\/\/(www.)?pandamanga.xyz\/.+\/.+\/.+/
// @include /https?:\/\/(www.)?rawdevart.com\/comic\/.+\/.+\//
// @include /https?:\/\/(www.)?readcomicsonline.ru\/comic\/.*\/\d*/
// @include /https?:\/\/(www.)?(funmanga|mngdoom|readmng|mangainn).(com|net)\/.+\/\d+/
// @include /https?:\/\/(www.)?reaperscans.com\/comics\/.+\/chapters\/.+/
// @include /https?:\/\/raw.senmanga.com\/.+\/.+\/?/
// @include /https?:\/\/(www.)?tapas.io\/episode\/.+/
// @include /https?:\/\/(www.)?(tenmanga|gardenmanage).com\/(chapter|statuses)\/.+/
// @include /https?:\/\/(www.)?(almtechnews|animalcanine|animalslegacy|animation2you|animationforyou|anisurion|anitirion|anitoc|cook2love|cooker2love|cookermania|cookernice|cookerready|dariusmotor|enginepassion|fanaticmanga|followmanga|gamesnk|gamesxo|infogames2you|infopetworld|lectortmo|mangalong|mistermanga|motorbakery|motornk|motorpi|mygamesinfo|mynewsrecipes|myotakuinfo|otakunice|otakuworldgames|otakworld|paleomotor|panicmanga|recetchef|recipesaniki|recipescoaching|recipesdo|recipesist|recipesnk|sucrecipes|tmofans|vgmotor|vsrecipes|worldmangas|wtechnews).com\/(viewer|news)\/.+\/(paginated|cascade)/
// @include /https?:\/\/(www.)?unionleitor.top\/leitor\/.+\/.+/
// @include /https?:\/\/(www.)?webnovel.com\/comic\/.+/
// @include /https?:\/\/(www.)?webtoons.com\/.+viewer.+/
// @include /https?:\/\/(www.)?(manga33).com\/manga\/.+/
// @include /https?:\/\/(www.)?(yugenmangas).com\/series\/.+/
// @include /https?:\/\/(www.)?zeroscans.com\/comics\/.+/
// @include /^(?!.*jaiminisbox).*\/read\/.+/
// @include /https?:\/\/.+\/(manga|series|manhua|comic)\/.+\/.+/
// @exclude /https?:\/\/(www.)?tsumino.com\/.+/
// @exclude /https?:\/\/(www.)?pururin.io\/.+/
// @downloadURL none
// ==/UserScript==
(function () {
'use strict';
const asurascans = {
name: "Asura Scans",
url: /https?:\/\/beta.asurascans.com\/read\/.+\/.+/,
homepage: "https://beta.asurascans.com/",
language: ["English"],
category: "manga",
run() {
const { chapterID, data } = JSON.parse(
document.querySelector("#__NEXT_DATA__")?.textContent ?? ""
).props.pageProps;
return {
title: document.querySelector("span.h2")?.textContent?.trim(),
series: document.querySelector(".container a.h6")?.getAttribute("href"),
pages: data.length,
prev: document.querySelector(".prev:not(.disabled)")?.getAttribute("href"),
next: document.querySelector(".next:not(.disabled)")?.getAttribute("href"),
listImages: data.map(
(img) => `https://img.asurascans.com/pages/${chapterID}/${img.uuid}.jpg`
)
};
}
};
const batoto = {
name: "Batoto",
url: /https?:\/\/(www.)?bato.to\/chapter.*/,
homepage: "http://bato.to/",
language: ["English"],
category: "manga",
run() {
const images = [...document.querySelectorAll(".page-img")];
return {
title: document.querySelector(".nav-title a")?.textContent?.trim(),
series: document.querySelector(".nav-title a")?.getAttribute("href"),
pages: images.length,
prev: document.querySelector(".nav-prev a")?.getAttribute("href"),
next: document.querySelector(".nav-next a")?.getAttribute("href"),
listImages: images.map((img) => img.getAttribute("src"))
};
}
};
const bilibilicomics = {
name: "BilibiliComics",
url: /https?:\/\/(www.)?(bilibilicomics).com\/.+\/.+/,
homepage: "https://www.bilibilicomics.com/",
language: ["English"],
category: "manga",
waitEle: ".read-nav",
async run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const api = await fetch(
"https://www.bilibilicomics.com/twirp/comic.v1.Comic/GetImageIndex?device=pc&platform=web&lang=en&sys_lang=en",
{
method: "post",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
ep_id: W.location.href.split("/").pop(),
credential: ""
})
}
).then((res) => res.json()).then(({ data }) => data.images.map((image) => `${image.path}@2000w.webp`)).then(JSON.stringify).then(
(urls) => fetch(
"https://www.bilibilicomics.com/twirp/comic.v1.Comic/ImageToken?device=pc&platform=web&lang=en&sys_lang=en",
{
method: "post",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({ urls })
}
)
).then((res) => res.json()).then(
(tokens) => tokens.data.map((i) => `${i.url}?token=${i.token}`)
);
return {
title: document.querySelector(".read-nav")?.textContent?.trim(),
series: document.querySelector(".manga-title")?.getAttribute("href"),
pages: api.length,
prev: document.querySelector(".navigate a button[title=Previous]")?.parentElement?.getAttribute("href"),
next: document.querySelector(".navigate a button[title=Next]")?.parentElement?.getAttribute("href"),
listImages: api
};
}
};
const comicastle = {
name: "ComiCastle",
url: /https?:\/\/(www.)?comicastle.org\/read\/.+\/\d+.*/,
homepage: "http://www.comicastle.org/",
language: ["English"],
category: "comic",
waitEle: ".form-control option:nth-child(1)",
run() {
const images = [...document.querySelectorAll(".form-control")[1].querySelectorAll("option")];
const chapter = document.querySelectorAll(".form-control")[0].querySelector("option:checked");
return {
title: chapter?.textContent?.trim(),
series: document.querySelector(".navbar-header a")?.getAttribute("href"),
pages: images.length,
prev: chapter?.previousElementSibling?.getAttribute("value"),
next: chapter?.nextElementSibling?.getAttribute("value"),
listImages: images.map((img) => img.getAttribute("alt"))
};
}
};
const dysnatyscans = {
name: "Dynasty-Scans",
url: /https?:\/\/(www.)?dynasty-scans.com\/chapters\/.+/,
homepage: "https://dynasty-scans.com/",
language: ["English"],
category: "manga",
run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
return {
title: document.querySelector("#chapter-title")?.textContent?.trim(),
series: document.querySelector("#chapter-title a")?.getAttribute("href"),
pages: W.pages.length,
prev: document.querySelector("#prev_link")?.getAttribute("href"),
next: document.querySelector("#next_link")?.getAttribute("href"),
listImages: W.pages.map((x) => x.image)
};
}
};
const flamecans = {
name: [
"Asura Scans",
"Flame Scans",
"Realm Scans",
"Voids-Scans",
"Luminous Scans",
"Shimada Scans",
"Night Scans"
],
url: /https?:\/\/(www.)?(asurascans|flamescans|realmscans|void-scans|luminousscans|shimascans|nightscans).(com|org|gg)\/.+/,
homepage: [
"https://www.asura.gg/",
"https://flamescans.org/",
"https://realmscans.com/",
"https://void-scans.com/",
"https://luminousscans.com/",
"https://shimadascans.com/",
"https://nightscans.org/"
],
language: ["English"],
category: "manga",
waitEle: "#chapter option:nth-child(2)",
run() {
const chapter = document.querySelector("#chapter option:checked");
const images = [...document.querySelectorAll("#readerarea img")];
return {
title: document.querySelector(".entry-title")?.textContent?.trim(),
series: document.querySelector(".allc a")?.getAttribute("href"),
pages: images.length,
prev: chapter?.nextElementSibling?.getAttribute("value"),
next: chapter?.previousElementSibling?.getAttribute("value"),
listImages: images.map(
(img) => img.getAttribute("data-src") ?? img.getAttribute("data-lazy-src") ?? img.getAttribute("src")
)
};
}
};
const foolslide = {
name: ["FoOlSlide", "Kireicake"],
url: /^(?!.*jaiminisbox).*\/read\/.+/,
homepage: ["#", "https://reader.kireicake.com"],
language: ["English"],
obs: "Any Site that uses FoOLSlide",
category: "manga",
waitEle: "img.open",
run() {
const chapter = [...document.querySelectorAll(".topbar_left .dropdown_parent:last-of-type li")];
const origin = chapter.findIndex((item) => {
const url = item.querySelector("a")?.getAttribute("href");
if (url)
return window.location.href.startsWith(url);
return false;
});
const pages = [...document.querySelectorAll(".topbar_right .dropdown li")];
const images = [...document.querySelectorAll(".inner img:not(.open)")];
const num = images.length > 1 ? images.length : pages.length;
return {
title: chapter.at(origin)?.querySelector("a")?.textContent?.trim() ?? document.querySelector("title")?.textContent?.trim(),
series: document.querySelector("div.tbtitle div.text a")?.getAttribute("href"),
pages: num,
prev: chapter.at(origin + 1)?.querySelector("a")?.getAttribute("href"),
next: chapter.at(origin - 1)?.querySelector("a")?.getAttribute("href"),
listPages: images.length > 1 ? null : Array(num).fill(0).map((_, i) => `${window.location.href.replace(/\/\d+$/, "")}/${i + 1}`),
listImages: images.length > 1 ? images.map((img) => img.getAttribute("src")) : null,
img: "img.open"
};
}
};
const inkr = {
name: "INKR",
url: /https?:\/\/(comics.)?inkr.com\/title\/.+\/chapter\/.+/,
homepage: "https://inkr.com/",
language: ["English"],
category: "manga",
waitFunc: () => document.querySelector("#editor-v2-scroll-view-id img")?.style.width !== "",
run() {
const images = [...document.querySelectorAll("#editor-v2-scroll-view-id img")];
return {
title: document.querySelector("title")?.textContent?.trim(),
series: document.querySelector("#chapter-detail-viewer-page div div div a")?.getAttribute("href"),
pages: images.length,
prev: document.querySelector('a[aria-label="Previous Chapter"]')?.getAttribute("href"),
next: document.querySelector('a[aria-label="Next Chapter"]')?.getAttribute("href"),
listImages: images.map((img) => img.getAttribute("src")?.replace("/t.", "/p."))
};
}
};
const inmanga = {
name: "InManga",
url: /https?:\/\/(www.)?inmanga.com\/ver\/manga\/.+\/.+/,
homepage: "https://inmanga.com//",
language: ["Spanish"],
category: "manga",
waitVar: "pageController",
run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const images = [...document.querySelectorAll("#PageList option")];
const chapter = document.querySelector("#ChapList option:checked");
const src = W.pageController._containers.pageUrl;
return {
title: document.querySelector("title")?.textContent?.trim(),
series: `../${W.pageController._containers.mangaIdentification}`,
pages: images.length,
prev: chapter?.previousElementSibling?.getAttribute("value"),
next: chapter?.nextElementSibling?.getAttribute("value"),
listImages: images.map(
(img, index) => src.replace("identification", img.getAttribute("value")).replace("pageNumber", index)
)
};
}
};
const klmanga = {
name: "KLManga",
url: /https?:\/\/(www.)?klmanga.com\/.+chapter.+/,
homepage: "https://klmanga.com/",
language: ["Raw"],
category: "manga",
run() {
const images = [...document.querySelectorAll(".chapter-content img")];
const chapter = document.querySelectorAll(".form-control")[0].querySelector("option:checked");
return {
title: document.querySelector("title")?.textContent?.trim(),
series: document.querySelector(".navbar-brand")?.getAttribute("href"),
pages: images.length,
prev: chapter?.nextElementSibling?.getAttribute("value"),
next: chapter?.previousElementSibling?.getAttribute("value"),
listImages: images.map((img) => img.getAttribute("src"))
};
}
};
const leitor = {
name: "Leitor",
url: /https?:\/\/(www.)?leitor.net\/manga\/.+\/.+\/.+/,
homepage: "https://leitor.net/",
language: ["Portuguese"],
category: "manga",
async run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const url = `https://leitor.net/leitor/pages/${W.READER_ID_RELEASE}.json?key=${W.READER_TOKEN}`;
const api = await fetch(url).then((res) => res.json());
const chapter = document.querySelector(".chapter-list .selected");
return {
title: document.querySelector("title")?.textContent?.trim(),
series: document.querySelector(".series-cover a")?.getAttribute("href"),
pages: api.images.length,
prev: chapter?.nextElementSibling?.querySelector("a")?.getAttribute("href"),
next: chapter?.previousElementSibling?.querySelector("a")?.getAttribute("href"),
listImages: api.images.map(
(img) => img.avif ?? img.legacy
)
};
}
};
const leviatanscans = {
name: "LeviatanScans",
url: /https?:\/\/(www|en)?.?leviatanscans.com\/(home\/)?manga\/.+\/chapter.+/,
homepage: "https://leviatanscans.com/",
language: ["English"],
category: "manga",
waitVar: "chapter_data",
run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const images = Array.isArray(W.chapter_data) ? JSON.parse(W.chapter_data) : JSON.parse(
JSON.parse(
CryptoJS.AES.decrypt(W.chapter_data, W.wpmangaprotectornonce, {
format: {
stringify(data) {
const cypher = {
ct: data.ciphertext.toString(CryptoJS.enc.Base64),
iv: data.iv?.toString(),
s: data.salt?.toString()
};
return JSON.stringify(cypher);
},
parse(text) {
const result = JSON.parse(text);
return CryptoJS.lib.CipherParams.create({
ciphertext: CryptoJS.enc.Base64.parse(result.ct),
iv: CryptoJS.enc.Hex.parse(result.iv),
salt: CryptoJS.enc.Hex.parse(result.s)
});
}
}
}).toString(CryptoJS.enc.Utf8)
)
);
return {
title: document.querySelector("#chapter-heading")?.textContent?.trim(),
series: document.querySelector(".back")?.getAttribute("href"),
pages: images.length,
prev: document.querySelector(".prev_page")?.getAttribute("href"),
next: document.querySelector(".next_page")?.getAttribute("href"),
listImages: images
};
}
};
const lhtranslation = {
name: "LHTranslation",
url: /https?:\/\/(www.)?lhtranslation.net\/read.+/,
homepage: "https://lhtranslation.net/",
language: ["English"],
category: "manga",
run() {
const chapter = document.querySelector(".form-control option:checked");
const images = [...document.querySelectorAll("img.chapter-img")];
return {
title: document.querySelector(".chapter-img.tieude font")?.textContent?.trim(),
series: document.querySelector(".navbar-brand.manga-name")?.getAttribute("href"),
pages: images.length,
prev: chapter?.nextElementSibling?.getAttribute("value"),
next: chapter?.previousElementSibling?.getAttribute("value"),
listImages: images.map((img) => img.getAttribute("src"))
};
}
};
const lynxscans = {
name: "LynxScans",
url: /https?:\/\/(www.)?lynxscans.com\/comics?\/.+/,
homepage: "https://lynxscans.com/",
language: ["English"],
category: "manga",
waitAttr: ["#app", "data-page"],
run() {
const data = JSON.parse(document.querySelector("#app").getAttribute("data-page"));
return {
title: document.querySelector("title")?.textContent?.trim(),
series: data.props.home,
pages: data.props.pages.length,
prev: data.props.previousChapter,
next: data.props.nextChapter,
listImages: data.props.pages.map((img) => img.url)
};
}
};
const madarawp = {
name: [
"Madara WordPress Plugin",
"MangaHaus",
"Isekai Scan",
"Comic Kiba",
"Zinmanga",
"mangatx",
"Toonily",
"Mngazuki",
"JaiminisBox",
"DisasterScans",
"ManhuaPlus",
"TopManhua",
"NovelMic",
"Reset-Scans"
],
url: /https?:\/\/.+\/(manga|series|manhua|comic)\/.+\/.+/,
homepage: [
"#",
"https://manhuaus.com",
"https://isekaiscan.com/",
"https://comickiba.com/",
"https://zinmanga.com/",
"https://mangatx.com/",
"https://toonily.net/",
"https://mangazuki.me/",
"https://jaiminisbox.net",
"https://disasterscans.com/",
"https://manhuaplus.com/",
"https://www.topmanhua.com/",
"https://novelmic.com/",
"https://reset-scans.com/"
],
language: ["English"],
obs: "Any Site that uses Madara Wordpress Plugin",
category: "manga",
run() {
const images = [
...document.querySelectorAll(
".wp-manga-chapter-img, .blocks-gallery-item img, .reading-content img"
)
];
return {
title: document.querySelector("#chapter-heading")?.textContent?.trim(),
series: (document.querySelector(".breadcrumb li:nth-child(3) a") ?? document.querySelector(".breadcrumb li:nth-child(2) a"))?.getAttribute("href"),
pages: images.length,
prev: document.querySelector(".prev_page")?.getAttribute("href"),
next: document.querySelector(".next_page")?.getAttribute("href"),
listImages: images.map(
(img) => _.without(
[
img?.getAttribute("src"),
img?.getAttribute("data-src"),
img?.getAttribute("data-full-url")
],
null,
void 0,
""
).pop()
)
};
}
};
const mangabuddy = {
name: "MangaBuddy",
url: /https?:\/\/(www.)?mangabuddy.com\/.+\/chapter.+/,
homepage: "https://mangabuddy.com/",
language: ["English"],
category: "manga",
waitVar: "chapImages",
run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const images = W.chapImages.split(",");
return {
title: document.querySelector(".chapter-info")?.textContent?.trim(),
series: document.querySelector("#breadcrumbs-container div:nth-child(2) a")?.getAttribute("href"),
pages: images.length,
prev: document.querySelector("a.prev")?.getAttribute("href"),
next: document.querySelector("a.next")?.getAttribute("href"),
listImages: images
};
}
};
const mangadex = {
name: "MangaDex",
url: /https?:\/\/(www.)?mangadex.org\/chapter\/.+(\/.+)?/,
homepage: "https://mangadex.org/",
language: ["English"],
category: "manga",
waitEle: ".md--reader-menu a[href^='/chapter/']",
async run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const chapterId = W.location.pathname.match(/\/chapter\/([^/]+)(\/\d+)?/)[1];
const home = `https://api.mangadex.org/at-home/server/${chapterId}`;
const server = await fetch(home).then((res) => res.json());
const images = server.chapter.data;
const chapters = [...document.querySelectorAll(".md--reader-menu a[href^='/chapter/']")].map(
(a) => a.getAttribute("href")
);
return {
title: document.querySelector("title")?.text.replace(" - MangaDex", ""),
series: document.querySelector("a.text-primary[href^='/title/']")?.getAttribute("href"),
pages: images.length,
prev: chapters[0] !== W.location.pathname ? chapters[0] : "#",
next: chapters[1] !== W.location.pathname ? chapters[1] : "#",
listImages: images.map((img) => `${server.baseUrl}/data/${server.chapter.hash}/${img}`)
};
}
};
const mangafox = {
name: ["MangaFox", "MangaHere"],
url: /https?:\/\/(www.)?(fanfox.net|mangahere.cc)\/manga\/.+\/.+\//,
homepage: ["https://fanfox.net/", "https://www.mangahere.cc/"],
language: ["English"],
category: "manga",
waitVar: "chapterid",
async run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const key = document.querySelector("#dm5_key")?.getAttribute("value");
const options = {
method: "GET",
headers: {
"Content-Type": "text/plain"
}
};
const src = Array(W.imagecount).fill(0).map(async (_, i) => {
const url = `chapterfun.ashx?cid=${W.chapterid ?? W.chapter_id}&page=${i}&key=${key}`;
const api = await fetch(url, options).then((res) => res.text());
(0, eval)(api);
return d;
});
const images = await Promise.all(src);
return {
title: document.querySelector(".reader-header-title div")?.textContent?.trim(),
series: document.querySelector(".reader-header-title a")?.getAttribute("href"),
pages: W.imagecount,
prev: W.prechapterurl,
next: W.nextchapterurl,
listImages: images.map((img, i) => img[i === 0 ? 0 : 1])
};
}
};
const mangafreak = {
name: "MangaFreak",
url: /https?:\/\/.{3,4}?(mangafreak).net\/Read.+/,
homepage: "https://mangafreak.net/",
language: ["English"],
category: "manga",
run() {
const chapter = document.querySelector(
".chapter_list select option:checked"
);
const images = [...document.querySelectorAll(".mySlides img")];
return {
title: document.querySelector("title")?.textContent?.trim(),
series: document.querySelector(".title a")?.getAttribute("href"),
pages: images.length,
prev: chapter?.previousElementSibling?.getAttribute("value"),
next: chapter?.nextElementSibling?.getAttribute("value"),
listImages: images.map((img) => img.getAttribute("src"))
};
}
};
const mangago = {
name: "Mangago",
url: /https?:\/\/(www.)?mangago.me\/.*\/.*\/.*/,
homepage: "https://www.mangago.me/",
language: ["English"],
category: "manga",
waitVar: "imgsrcs",
run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const key = CryptoJS.enc.Hex.parse("e11adc3949ba59abbe56e057f20f883e");
const iv = CryptoJS.enc.Hex.parse("1234567890abcdef1234567890abcdef");
const opinion = { iv, padding: CryptoJS.pad.ZeroPadding };
const images = CryptoJS.AES.decrypt(W.imgsrcs, key, opinion).toString(CryptoJS.enc.Utf8).split(",");
return {
title: `${W.manga_name} ${W.chapter_name}`,
series: W.mid,
pages: W.total_pages,
prev: document.querySelector(".recom p:nth-child(5) a")?.getAttribute("href"),
next: W.next_c_url,
listImages: images
};
}
};
const mangahosted = {
name: "mangahosted",
url: /https?:\/\/(www.)?mangahosted.com\/manga\/.+\/.+/,
homepage: "https://mangahosted.com/",
language: ["Portuguese"],
category: "manga",
run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const images = [...document.querySelectorAll("picture img")];
return {
title: $(".breadcrumb li:eq(3)").text().trim(),
series: $(".breadcrumb li:eq(2) a").attr("href"),
pages: images.length,
prev: W.$read_prev,
next: W.$read_next,
listImages: images.map((img) => img.getAttribute("src"))
};
}
};
const mangahub = {
name: "MangaHub",
url: /https?:\/\/(www.)?(mangahub).io\/chapter\/.+\/.+/,
homepage: "https://mangahub.io/",
language: ["English"],
category: "manga",
waitEle: "#select-chapter",
async run() {
function getCookie(name) {
const re = new RegExp(`${name}=([^;]+)`);
const value = re.exec(document.cookie);
return value != null ? decodeURIComponent(value[1]) : null;
}
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const slug = W.CURRENT_MANGA_SLUG ?? window.location.pathname.split("/")[2];
const number = window.location.pathname.split("/")[3].replace("chapter-", "");
const data = { query: `{chapter(x:m01,slug:"${slug}",number:${number}){pages}}` };
const options = {
method: "POST",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
"x-mhub-access": getCookie("mhub_access")
}
};
const api = await fetch("https://api.mghubcdn.com/graphql", options).then(
(res) => res.json()
);
const images = JSON.parse(api?.data.chapter.pages.toString());
return {
title: document.querySelector("#mangareader h3")?.textContent?.trim(),
series: document.querySelector("#mangareader a")?.getAttribute("href"),
pages: images.i.length,
prev: document.querySelector(".previous a")?.getAttribute("href"),
next: document.querySelector(".next a")?.getAttribute("href"),
listImages: images.i.map(
(i) => `https://img.mghubcdn.com/file/imghub/${images.p + i}`
)
};
}
};
const mangakakalot = {
name: ["MangaKakalot", "MangaNelo", "MangaNato"],
url: /https?:\/\/(www.)?((manganelo|mangakakalot).com\/chapter\/.+\/.+|(manganato|readmanganato|chapmanganato).com\/manga-\w\w\d+\/chapter-\d+)/,
homepage: [
"https://mangakakalot.com/page",
"https://www.manganelo.com/",
"https://www.manganato.com/"
],
language: ["English"],
category: "manga",
run() {
const images = [...document.querySelectorAll("#vungdoc img, .container-chapter-reader img")];
return {
title: document.querySelector(
".info-top-chapter h2, .imageOptions-chapter-info-top h1, .panel-chapter-info-top h1"
)?.textContent?.trim(),
series: document.querySelectorAll("span a[title]").item(1).getAttribute("href"),
pages: images.length,
prev: document.querySelector(".navi-change-chapter-btn-prev, .next")?.getAttribute("href"),
next: document.querySelector(".navi-change-chapter-btn-next, .back")?.getAttribute("href"),
listImages: images.map((img) => img.getAttribute("src"))
};
}
};
const mangapark = {
name: "MangaPark",
url: /https?:\/\/(www.)?mangapark.(com|me|org|net)\/title\/.+\/.+/,
homepage: "https://mangapark.net/",
language: ["English"],
category: "manga",
waitEle: "main div div a.btn-primary",
run() {
const json = JSON.parse(document.querySelector("#__NEXT_DATA__")?.innerHTML ?? "");
const data = json.props.pageProps.dehydratedState.queries[0].state.data.data.imageSet;
const images = data.httpLis.map(
(img, index) => `${img}?${data.wordLis[index]}`
);
return {
title: [...document.querySelectorAll(".comic-detail h3 a, .comic-detail h6 span")].map((e) => e.textContent?.trim()).join(" "),
series: document.querySelector(".comic-detail a")?.getAttribute("href"),
pages: images.length,
prev: document.querySelectorAll("main div div a.btn-primary")?.item(0)?.getAttribute("href"),
next: document.querySelectorAll("main div div a.btn-primary")?.item(1)?.getAttribute("href"),
listImages: images
};
}
};
const mangareader = {
name: "Mangareader",
url: /https?:\/\/(www.)?mangareader.to\/read\/.+\/.+\/.+/,
homepage: "https://mangareader.to",
language: ["English"],
category: "manga",
obs: "Some galleries will not be usable",
waitEle: ".ds-image, .iv-card",
async run() {
const chapter = document.querySelector(".chapter-item.active");
const images = [...document.querySelectorAll(".ds-image[data-url], .iv-card[data-url]")];
const src = images.map(async (img) => {
const url = img.getAttribute("data-url");
if (url && img.classList.contains("shuffled")) {
return (await imgReverser(url)).toDataURL();
}
return url;
});
return {
title: document.querySelector(".hr-manga h2")?.textContent?.trim(),
series: document.querySelector(".hr-manga")?.getAttribute("href"),
pages: src.length,
prev: chapter?.nextElementSibling?.querySelector("a")?.getAttribute("href"),
next: chapter?.previousElementSibling?.querySelector("a")?.getAttribute("href"),
listImages: await Promise.all(src)
};
}
};
const mangasee = {
name: ["MangaSee", "Manga4life"],
url: /https?:\/\/(www.)?(mangasee123|manga4life).com\/read-online\/.+/,
homepage: ["https://mangasee123.com/", "https://manga4life.com/"],
language: ["English"],
category: "manga",
waitAttr: [".img-fluid", "src"],
run() {
const src = document.querySelector(".img-fluid")?.getAttribute("src") ?? "";
const script = [...document.querySelectorAll("body script:not([src])")].at(-1)?.textContent;
const textCurChapter = script?.match(/CurChapter = ({.+});/) ?? [];
const CurChapter = JSON.parse(textCurChapter[1]);
const textCHAPTERS = script?.match(/CHAPTERS = (\[\{.+}]);/) ?? [];
const CHAPTERS = JSON.parse(textCHAPTERS[1]);
const CurChapterIndex = CHAPTERS.findIndex(
(chap) => chap.Chapter === CurChapter.Chapter
);
function ChapterURLEncode(reference) {
let ChapterString = CHAPTERS[CurChapterIndex + reference];
if (ChapterString === void 0) {
return "#";
}
ChapterString = ChapterString.Chapter;
let Index = "";
const IndexString = ChapterString.substring(0, 1);
if (IndexString !== "1") {
Index = `-index-${IndexString}`;
}
const Chapter = parseInt(ChapterString.slice(1, -1), 10);
let Odd = "";
const OddString = ChapterString[ChapterString.length - 1];
if (OddString !== "0") {
Odd = `.${OddString}`;
}
return window.location.href.replace(/-chapter-.+/, `-chapter-${Chapter}${Odd}${Index}.html`);
}
return {
title: document.querySelector("title")?.textContent?.replace(/ Page .+/, "").trim(),
series: document.querySelector(".MainContainer a")?.getAttribute("href"),
pages: parseInt(CurChapter.Page, 10),
prev: ChapterURLEncode(-1),
next: ChapterURLEncode(1),
listImages: Array(parseInt(CurChapter.Page, 10)).fill(0).map(
(_, i) => src.replace(
/-\d\d\d.png/,
`-${String(i + 1).padStart(3, "0").slice(-3)}.png`
)
)
};
}
};
const mangatigre = {
name: "MangaTigre",
url: /https?:\/\/(www.)?mangatigre.net\/.+\/.+\/.+/,
homepage: "https://www.mangatigre.net/",
language: ["Spanish"],
category: "manga",
run() {
const images = [...document.querySelectorAll(".chapter-content img")];
const chapter = document.querySelector(".form-control option:checked");
return {
title: document.querySelector(".page-title")?.textContent?.trim(),
series: document.querySelector(".breadcrumb li:nth-child(3) a")?.getAttribute("href"),
pages: images.length,
prev: chapter?.nextElementSibling?.getAttribute("value"),
next: chapter?.previousElementSibling?.getAttribute("value"),
listImages: images.map((img) => img.getAttribute("data-src") ?? img.getAttribute("src"))
};
}
};
const mangatoon = {
name: "MangaToons",
url: /https?:\/\/.*mangatoon.mobi\/.+\/watch\/.+/,
homepage: "https://mangatoon.mobi/",
language: ["English"],
category: "manga",
waitEle: ".pictures img:not(.cover)",
run() {
const images = [...document.querySelectorAll(".pictures img:not(.cover)")];
return {
title: document.querySelector("title")?.textContent?.trim(),
series: document.querySelector(".top-left a")?.getAttribute("href"),
pages: images.length,
prev: document.querySelector(".page-icons-prev")?.getAttribute("href"),
next: document.querySelector(".page-icons-next")?.getAttribute("href"),
listImages: images.map((img) => img.getAttribute("data-src"))
};
}
};
const mangatown = {
name: "MangaTown",
url: /https?:\/\/(www.|m.)?mangatown.com\/manga\/.+\/.+/,
homepage: "https://www.mangatown.com/",
language: ["English"],
category: "manga",
waitVar: "chapter_id",
async run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const key = document.querySelector("#dm5_key")?.getAttribute("value");
const options = {
method: "GET",
headers: {
"Content-Type": "text/plain"
}
};
const src = Array(W.total_pages).fill(0).map(async (_, i) => {
const url = `chapterfun.ashx?cid=${W.chapter_id}&page=${i}&key=${key}`;
const api = await fetch(url, options).then((res) => res.text());
(0, eval)(api);
return d;
});
const images = await Promise.all(src);
const chapter = document.querySelector("#top_chapter_list option:checked");
return {
title: document.querySelector(".title h1")?.textContent,
series: W.series_url,
pages: images.length,
prev: chapter?.previousElementSibling?.getAttribute("value"),
next: chapter?.nextElementSibling?.getAttribute("value"),
listImages: images.map((img, i) => img[i === 0 ? 0 : 1])
};
}
};
const manhuascan = {
name: "ManhuaScan",
url: /https?:\/\/(www.)?manhuascan.io\/.+chapter.+/,
homepage: "https://manhuascan.io/",
language: ["English"],
category: "manga",
waitVar: "imgsrcs",
run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const key = CryptoJS.enc.Hex.parse("e11adc3949ba59abbe56e057f20f131e");
const iv = CryptoJS.enc.Hex.parse("1234567890abcdef1234567890abcdef");
const opinion = { iv, padding: CryptoJS.pad.ZeroPadding };
const images = CryptoJS.AES.decrypt(W.imgsrcs, key, opinion).toString(CryptoJS.enc.Utf8).split(",");
return {
title: document.querySelector("title")?.textContent?.trim(),
series: document.querySelector(".breadcrumb li:nth-child(3) a")?.getAttribute("href"),
pages: images.length,
prev: document.querySelector(".chapter-select a:first-of-type")?.getAttribute("href"),
next: document.querySelector(".chapter-select a:last-of-type")?.getAttribute("href"),
listImages: images
};
}
};
const mreader = {
name: ["MReader", "MangaGeko"],
url: /https?:\/\/(www.)?(mreader|mangageko).com?\/reader\/.*/,
homepage: ["https://www.mreader.co/", "https://www.mangageko.com/"],
language: ["English"],
category: "manga",
run() {
const images = [...document.querySelectorAll("#chapter-reader img")];
return {
title: document.querySelector(".titles")?.textContent?.trim(),
series: document.querySelector(".titles a")?.getAttribute("href"),
pages: images.length,
prev: document.querySelector(".chnav.prev")?.getAttribute("href"),
next: document.querySelector(".chnav.next")?.getAttribute("href"),
listImages: images.map((img) => img.getAttribute("src"))
};
}
};
const naniscans = {
name: "NaniScans",
url: /https?:\/\/(www.)?(naniscans).com\/chapters\/.+\/read/,
homepage: "https://naniscans.com/",
language: ["English"],
category: "manga",
waitVar: "chapterListRoute",
async run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const api = await fetch(W.location.href.replace("read", "json")).then((res) => res.json());
return {
title: document.querySelector("title")?.textContent?.trim(),
series: document.querySelector('a[href^="/titles/"]')?.getAttribute("href"),
pages: api.pages.length,
prev: W.previousChapterRoute,
next: W.nextChapterRoute,
listImages: api.pages.map((i) => i.address)
};
}
};
const ninemanga = {
name: "NineManga",
url: /https?:\/\/(www.)?ninemanga.com\/chapter\/.+\/.+\.html/,
homepage: "https://ninemanga.com/",
language: ["English"],
category: "manga",
run() {
const chapter = document.querySelector("#chapter option:checked");
const pages = [...document.querySelector("#page").querySelectorAll("option")];
return {
title: document.querySelector(".tip a")?.textContent?.trim(),
series: document.querySelector(".subgiude > li:nth-child(2) > a")?.getAttribute("href"),
pages: pages.length,
prev: chapter?.nextElementSibling?.getAttribute("value"),
next: chapter?.previousElementSibling?.getAttribute("value"),
listPages: pages.map((item) => $(item).val()),
img: ".manga_pic"
};
}
};
const olympusscans = {
name: "OlympusScans",
url: /https?:\/\/(www.)?olympusscans.com\/capitulo\/.+\/.+/,
homepage: "https://olympusscans.com/",
language: ["Spanish"],
category: "manga",
waitVar: "__NUXT__",
run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const images = W.__NUXT__.data[W.location.pathname].chapter.pages;
return {
title: document.querySelector("title")?.textContent?.trim(),
series: document.querySelector("h1")?.parentElement?.getAttribute("href"),
pages: images.length,
prev: document.querySelector(".i-heroicons-chevron-left-20-solid")?.parentElement?.getAttribute("href"),
next: document.querySelector(".i-heroicons-chevron-right-20-solid")?.parentElement?.getAttribute("href"),
listImages: images
};
}
};
const pandamanga = {
name: "PandaManga",
url: /https?:\/\/(www.)?pandamanga.xyz\/.+\/.+\/.+/,
homepage: "https://www.pandamanga.com/",
language: ["English"],
category: "manga",
run() {
const chapter = document.querySelector(".select-chapter option:checked");
const data = JSON.parse(document.getElementById("__NEXT_DATA__").textContent);
const images = data.props.pageProps.mangaview.source.split(",").filter((url) => url.length > 0);
return {
title: data.props.pageProps.mangaview.nameSeoChapter,
series: document.querySelector(".allc a")?.getAttribute("href"),
pages: images.length,
prev: chapter?.nextElementSibling?.getAttribute("value"),
next: chapter?.previousElementSibling?.getAttribute("value"),
listImages: images
};
}
};
const rawdevart = {
name: "RawDevart",
url: /https?:\/\/(www.)?rawdevart.com\/comic\/.+\/.+\//,
homepage: "https://rawdevart.com",
language: ["Raw"],
category: "manga",
waitVar: "rconfig",
waitEle: "#chapter-list select",
run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const chapter = document.querySelector("#chapter-list option:checked");
const images = [...document.querySelectorAll("#img-container img")];
return {
title: W.rconfig.chapterTitle,
series: W.rconfig.prefix,
pages: images.length,
prev: chapter?.nextElementSibling?.getAttribute("value"),
next: chapter?.previousElementSibling?.getAttribute("value"),
listImages: images.map((item) => $(item).attr("data-src") ?? $(item).attr("src"))
};
}
};
const readcomicsonline = {
name: "ReadComicsOnline",
url: /https?:\/\/(www.)?readcomicsonline.ru\/comic\/.*\/\d*/,
homepage: "https://readcomicsonline.ru/",
language: ["English"],
category: "comic",
run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const images = [...document.querySelectorAll("#all img")];
return {
title: W.title.replace(/ - Page \d+/, ""),
series: document.querySelector("div.pager-cnt a")?.getAttribute("href"),
pages: W.pages.length,
prev: W.prev_chapter,
next: W.next_chapter,
listImages: images.map((img) => img.getAttribute("data-src"))
};
}
};
const readmangatoday = {
name: ["ReadManga Today", "Funmanga", "MangaDoom", "MangaInn"],
url: /https?:\/\/(www.)?(funmanga|mngdoom|readmng|mangainn).(com|net)\/.+\/\d+/,
homepage: [
"https://www.readmng.com/",
"https://funmanga.com/",
"https://mngdoom.com/",
"https://www.mangainn.net/"
],
language: ["English"],
category: "manga",
run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
return {
title: W.chapter_page_title,
series: W.manga_url,
pages: W.images.length,
prev: W.prev_chapter_url,
next: W.next_chapter_url,
listImages: W.images.map((item) => item.url)
};
}
};
const reaperscans = {
name: "ReaperScans",
url: /https?:\/\/(www.)?reaperscans.com\/comics\/.+\/chapters\/.+/,
homepage: "https://reaperscans.com/",
language: ["English"],
category: "manga",
waitEle: "main div:nth-child(3) img",
run() {
const images = [...document.querySelectorAll("main div:nth-child(3) img")];
return {
title: document.querySelector("title")?.textContent?.trim(),
series: document.querySelector(".fa-list")?.parentElement?.getAttribute("href"),
pages: images.length,
prev: document.querySelector(".fa-arrow-left-long")?.parentElement?.getAttribute("href"),
next: document.querySelector(".fa-arrow-right-long")?.parentElement?.getAttribute("href"),
listImages: images.map((img) => img.getAttribute("data-src") ?? img.getAttribute("src"))
};
}
};
const senmanga = {
name: "SenManga(Raw)",
url: /https?:\/\/raw.senmanga.com\/.+\/.+\/?/,
homepage: "https://raw.senmanga.com/",
language: ["Raw"],
category: "manga",
run() {
const url = `/${window.location.pathname.split("/")[1]}/${window.location.pathname.split("/")[2]}`;
const num = parseInt(
document.querySelector(".page-list select option:last-child")?.getAttribute("value") ?? "0",
10
);
const chapter = [...document.querySelectorAll(".dropdown-chapter li")];
const origin = chapter.findIndex(
(item) => item.querySelector("a")?.getAttribute("href") === window.location.href
);
return {
title: $(".title").text().trim(),
series: document.querySelector(".breadcrumb li:nth-child(2) a")?.getAttribute("href"),
pages: num,
prev: chapter.at(origin + 1)?.querySelector("a")?.getAttribute("href"),
next: chapter.at(origin - 1)?.querySelector("a")?.getAttribute("href"),
listPages: Array(num).fill(0).map((_, i) => `${url}/${i + 1}/`),
img: ".picture"
};
}
};
const tapas = {
name: "KLManga",
url: /https?:\/\/(www.)?tapas.io\/episode\/.+/,
homepage: "https://tapas.io/",
language: ["English"],
category: "manga",
run() {
const images = [...document.querySelectorAll(".viewer__body img.content__img")];
const chapter = document.querySelector(".js-episodes .body__item--selected");
return {
title: document.querySelector(".viewer__header .title")?.textContent?.trim(),
series: document.querySelector(".vw-nav__left a")?.getAttribute("href"),
pages: images.length,
prev: chapter?.previousElementSibling?.getAttribute("data-href"),
next: chapter?.nextElementSibling?.getAttribute("data-href"),
listImages: images.map((img) => img.getAttribute("data-src") ?? img.getAttribute("src"))
};
}
};
const tenmanga = {
name: "TenManga",
url: /https?:\/\/(www.)?(tenmanga|gardenmanage).com\/(chapter|statuses)\/.+/,
homepage: "https://www.tenmanga.com/",
language: ["English"],
category: "manga",
waitVar: "_pageCtrl",
run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const chapter = document.querySelector(
".mangaread-pagenav select option:checked"
);
const images = W._pageCtrl.options.all_imgs_url;
return {
title: document.querySelector(".title h1")?.textContent?.trim(),
series: document.querySelector(".title a:nth-child(2)")?.getAttribute("href"),
pages: images.length,
prev: chapter?.nextElementSibling?.getAttribute("value"),
next: chapter?.previousElementSibling?.getAttribute("value"),
listImages: images
};
}
};
const TMODomains = [
"almtechnews",
"animalcanine",
"animalslegacy",
"animation2you",
"animationforyou",
"anisurion",
"anitirion",
"anitoc",
"cook2love",
"cooker2love",
"cookermania",
"cookernice",
"cookerready",
"dariusmotor",
"enginepassion",
"fanaticmanga",
"followmanga",
"gamesnk",
"gamesxo",
"infogames2you",
"infopetworld",
"lectortmo",
"mangalong",
"mistermanga",
"motorbakery",
"motornk",
"motorpi",
"mygamesinfo",
"mynewsrecipes",
"myotakuinfo",
"otakunice",
"otakuworldgames",
"otakworld",
"paleomotor",
"panicmanga",
"recetchef",
"recipesaniki",
"recipescoaching",
"recipesdo",
"recipesist",
"recipesnk",
"sucrecipes",
"tmofans",
"vgmotor",
"vsrecipes",
"worldmangas",
"wtechnews"
];
const tmofans = {
name: "TuMangaOnline",
url: new RegExp(
`https?:\\/\\/(www.)?(${TMODomains.join("|")}).com\\/(viewer|news)\\/.+\\/(paginated|cascade)`
),
homepage: "https://lectortmo.com/",
language: ["Spanish"],
category: "manga",
run() {
const images = [...document.querySelectorAll(".img-container img, .viewer-container img")];
const pages = [
...document.querySelectorAll(
"div.container:nth-child(4) select#viewer-pages-select option"
)
];
const num = images.length > 1 ? images.length : pages.length;
return {
title: document.querySelector("title")?.textContent?.trim(),
series: document.querySelector('a[title="Volver"]')?.getAttribute("href"),
pages: num,
prev: document.querySelector(".chapter-prev a")?.getAttribute("href"),
next: document.querySelector(".chapter-next a")?.getAttribute("href"),
...images.length > 1 ? {
listImages: images.map((item) => $(item).attr("data-src"))
} : {
listPages: Array(pages.length).fill(0).map((_, i) => `${window.location.href.replace(/\/\d+$/, "")}/${i + 1}`),
img: "#viewer-container img, .viewer-page"
}
};
}
};
const unionmangas = {
name: "UnionMangas",
url: /https?:\/\/(www.)?unionleitor.top\/leitor\/.+\/.+/,
homepage: "https://unionleitor.top/",
language: ["Portuguese"],
category: "manga",
run() {
const chapter = document.querySelector("#capitulo_trocar option:checked");
const images = [...document.querySelectorAll(".img-manga")];
return {
title: document.querySelector(".titulo-leitura")?.textContent?.trim(),
series: document.querySelector(".breadcrumbs a:nth-child(3)")?.getAttribute("href"),
pages: images.length,
prev: chapter?.previousElementSibling?.getAttribute("value"),
next: chapter?.nextElementSibling?.getAttribute("value"),
listImages: images.map((img) => img.getAttribute("src"))
};
}
};
const webnovel = {
name: "WebNovel",
url: /https?:\/\/(www.)?webnovel.com\/comic\/.+/,
homepage: "https://www.webnovel.com/",
language: ["English"],
category: "manga",
waitVar: "g_data",
run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const images = W.g_data.chapter.chapterInfo.chapterPage.map((img) => img.url);
return {
title: document.querySelector("title")?.textContent?.trim(),
series: "./",
pages: images.length,
prev: `${W.g_data.chapter.chapterInfo.preChapterName}_${W.g_data.chapter.chapterInfo.preChapterId}`,
next: `${W.g_data.chapter.chapterInfo.nextChapterName}_${W.g_data.chapter.chapterInfo.nextChapterId}`,
listImages: images
};
}
};
const webtoons = {
name: "WebToons",
url: /https?:\/\/(www.)?webtoons.com\/.+viewer.+/,
homepage: "https://www.webtoons.com/",
language: ["English"],
category: "manga",
run() {
const images = [...document.querySelectorAll("#_imageList img")];
return {
title: document.querySelector(".subj_info")?.textContent?.trim(),
series: document.querySelector(".subj_info a")?.getAttribute("href"),
pages: images.length,
prev: document.querySelector("._prevEpisode")?.getAttribute("href"),
next: document.querySelector("._nextEpisode")?.getAttribute("href"),
listImages: images.map(
(img) => img.getAttribute("data-url") ?? img.getAttribute("data-src") ?? img.getAttribute("src")
)
};
}
};
const wpmanga = {
name: ["Manga33"],
url: /https?:\/\/(www.)?(manga33).com\/manga\/.+/,
homepage: ["https://manga33.com/"],
language: ["English"],
category: "manga",
run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const images = [...document.querySelectorAll(".chapter-content img")];
return {
title: document.querySelector("title")?.textContent?.trim(),
series: document.querySelector(".navbar-brand")?.getAttribute("href"),
pages: images.length,
prev: document.querySelector("a.prev")?.getAttribute("href"),
next: document.querySelector("a.next")?.getAttribute("href"),
listImages: images.map((img) => img.getAttribute("src")),
before() {
if (/all.html$/.exec(W.location.pathname))
return;
if (/\d+-\d+.html$/.exec(W.location.pathname)) {
W.location.pathname = W.location.pathname.replace(/-\d+.html$/, "-all.html");
}
}
};
}
};
const yugenmangas = {
name: "YugenMangas",
url: /https?:\/\/(www.)?(yugenmangas).com\/series\/.+/,
homepage: "https://yugenmangas.com/",
language: ["Spanish"],
category: "manga",
async run() {
const data = JSON.parse(document.querySelector("#__NEXT_DATA__")?.textContent ?? "");
const { id } = data.props.pageProps.data ?? (await fetch(
window.location.href.replace("series", `_next/data/${data.buildId}/series`).concat(".json")
).then((res) => res.json())).pageProps.data;
const api = await fetch(`https://api.yugenmangas.com/series/chapter/${id}`).then(
(res) => res.json()
);
return {
title: document.querySelector("title")?.textContent?.trim(),
series: document.querySelector(".chapter-heading h5 a")?.getAttribute("href"),
pages: api.content.images.length,
prev: document.querySelector(".prev-chap a")?.getAttribute("href"),
next: document.querySelector(".next-chap a")?.getAttribute("href"),
listImages: api.content.images.map((url) => `https://api.yugenmangas.com/${url}`)
};
}
};
const zeroscans = {
name: "ZeroScans",
url: /https?:\/\/(www.)?zeroscans.com\/comics\/.+/,
homepage: "https://zeroscans.com/",
language: ["English"],
category: "manga",
waitVar: "__ZEROSCANS__",
run() {
const W = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
const images = W.__ZEROSCANS__.data.at(0).current_chapter.high_quality;
const chapters = document.querySelectorAll(".v-btn--router");
return {
title: document.querySelector("title")?.textContent?.trim(),
series: document.querySelector(".v-breadcrumbs li:nth-child(2) + a")?.getAttribute("href"),
pages: images.length,
prev: chapters[0]?.getAttribute("href"),
next: chapters[1]?.getAttribute("href"),
listImages: images
};
}
};
const sites = [
asurascans,
batoto,
bilibilicomics,
comicastle,
dysnatyscans,
flamecans,
inkr,
inmanga,
klmanga,
leitor,
leviatanscans,
lhtranslation,
lynxscans,
mangabuddy,
mangadex,
mangafox,
mangafreak,
mangago,
mangahosted,
mangahub,
mangakakalot,
mangapark,
mangareader,
mangasee,
mangatigre,
mangatoon,
mangatown,
manhuascan,
mreader,
naniscans,
ninemanga,
olympusscans,
pandamanga,
rawdevart,
readcomicsonline,
readmangatoday,
reaperscans,
// resetscans, deprecated
senmanga,
tapas,
tenmanga,
tmofans,
unionmangas,
webnovel,
webtoons,
wpmanga,
yugenmangas,
zeroscans,
foolslide,
// Must be at the end because is a generic check
madarawp
// Must be at the end because is a generic check
];
const rangeSliderStyles = ".range-slider{touch-action:none;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none;cursor:pointer;display:block;position:relative;width:100%;height:8px;background:#ddd;border-radius:4px}.range-slider[data-vertical]{height:100%;width:8px}.range-slider[data-disabled]{opacity:.5;cursor:not-allowed}.range-slider .range-slider__thumb{position:absolute;z-index:3;top:50%;width:24px;height:24px;transform:translate(-50%,-50%);border-radius:50%;background:#2196f3}.range-slider .range-slider__thumb:focus-visible{outline:0;box-shadow:0 0 0 6px rgba(33,150,243,.5)}.range-slider[data-vertical] .range-slider__thumb{left:50%}.range-slider .range-slider__thumb[data-disabled]{z-index:2}.range-slider .range-slider__range{position:absolute;z-index:1;transform:translate(0,-50%);top:50%;width:100%;height:100%;background:#51adf6}.range-slider[data-vertical] .range-slider__range{left:50%;transform:translate(-50%,0)}.range-slider input[type=range]{-webkit-appearance:none;pointer-events:none;position:absolute;z-index:2;top:0;left:0;width:0;height:0;background-color:transparent}.range-slider input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none}.range-slider input[type=range]::-moz-range-thumb{width:0;height:0;border:0}.range-slider input[type=range]:focus{outline:0}";
function logScript(...text) {
console.log("MangaOnlineViewer: ", ...text);
return text;
}
function getListGM() {
return typeof GM_listValues !== "undefined" ? GM_listValues() : [];
}
function removeValueGM(name) {
return typeof GM_deleteValue !== "undefined" ? GM_deleteValue(name) : logScript("Removing: ", name);
}
const getInfoGM = typeof GM_info !== "undefined" ? GM_info : {
scriptHandler: "Console",
script: {
name: "Debug",
version: "Testing"
}
};
function getValueGM(name, defaultValue = null) {
if (typeof GM_getValue !== "undefined") {
return GM_getValue(name, defaultValue);
}
logScript("Fake Getting: ", name, " = ", defaultValue);
return defaultValue;
}
function getJsonGM(name, defaultValue = null) {
const result = getValueGM(name, defaultValue);
if (typeof result === "string")
return JSON.parse(result);
return result;
}
function getSettings(defaultSettings) {
return getJsonGM("settings", defaultSettings);
}
function setValueGM(name, value) {
try {
GM_setValue(name, value);
return value.toString();
} catch (e) {
logScript("Fake Setting: ", name, " = ", value);
return String(value);
}
}
function setSettings(value) {
return setValueGM("settings", value);
}
function getBrowser() {
const ua = navigator.userAgent;
let tem;
const M = /(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i.exec(ua) ?? [];
if (/trident/i.test(M[1])) {
tem = /\brv[ :]+(\d+)/g.exec(ua) ?? [];
return `IE ${tem[1] ?? ""}`;
}
if (M[1] === "Chrome") {
tem = /\b(OPR|Edge)\/(\d+)/.exec(ua);
if (tem !== null) {
return tem.slice(1).join(" ").replace("OPR", "Opera");
}
}
const tempM = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, "-?"];
tem = /version\/(\d+)/i.exec(ua);
if (tem !== null) {
tempM.splice(1, 1, tem[1]);
}
return tempM.join(" ");
}
function getEngine() {
return getInfoGM.scriptHandler ?? "Greasemonkey";
}
const isMobile = window.matchMedia("screen and (max-width: 768px)").matches;
const colors = {
dark: {
name: "dark",
50: "#C1C2C5",
100: "#A6A7AB",
200: "#909296",
300: "#5c5f66",
400: "#373A40",
500: "#2C2E33",
600: "#25262b",
700: "#1A1B1E",
800: "#141517",
900: "#101113"
},
gray: {
name: "gray",
50: "#f8f9fa",
100: "#f1f3f5",
200: "#e9ecef",
300: "#dee2e6",
400: "#ced4da",
500: "#adb5bd",
600: "#868e96",
700: "#495057",
800: "#343a40",
900: "#212529"
},
red: {
name: "red",
50: "#fff5f5",
100: "#ffe3e3",
200: "#ffc9c9",
300: "#ffa8a8",
400: "#ff8787",
500: "#ff6b6b",
600: "#fa5252",
700: "#f03e3e",
800: "#e03131",
900: "#c92a2a"
},
pink: {
name: "pink",
50: "#fff0f6",
100: "#ffdeeb",
200: "#fcc2d7",
300: "#faa2c1",
400: "#f783ac",
500: "#f06595",
600: "#e64980",
700: "#d6336c",
800: "#c2255c",
900: "#a61e4d"
},
grape: {
name: "grape",
50: "#f8f0fc",
100: "#f3d9fa",
200: "#eebefa",
300: "#e599f7",
400: "#da77f2",
500: "#cc5de8",
600: "#be4bdb",
700: "#ae3ec9",
800: "#9c36b5",
900: "#862e9c"
},
violet: {
name: "violet",
50: "#f3f0ff",
100: "#e5dbff",
200: "#d0bfff",
300: "#b197fc",
400: "#9775fa",
500: "#845ef7",
600: "#7950f2",
700: "#7048e8",
800: "#6741d9",
900: "#5f3dc4"
},
indigo: {
name: "purple",
50: "#edf2ff",
100: "#dbe4ff",
200: "#bac8ff",
300: "#91a7ff",
400: "#748ffc",
500: "#5c7cfa",
600: "#4c6ef5",
700: "#4263eb",
800: "#3b5bdb",
900: "#364fc7"
},
blue: {
name: "blue",
50: "#e7f5ff",
100: "#d0ebff",
200: "#a5d8ff",
300: "#74c0fc",
400: "#4dabf7",
500: "#339af0",
600: "#228be6",
700: "#1c7ed6",
800: "#1971c2",
900: "#1864ab"
},
cyan: {
name: "cyan",
50: "#e3fafc",
100: "#c5f6fa",
200: "#99e9f2",
300: "#66d9e8",
400: "#3bc9db",
500: "#22b8cf",
600: "#15aabf",
700: "#1098ad",
800: "#0c8599",
900: "#0b7285"
},
teal: {
name: "teal",
50: "#e6fcf5",
100: "#c3fae8",
200: "#96f2d7",
300: "#63e6be",
400: "#38d9a9",
500: "#20c997",
600: "#12b886",
700: "#0ca678",
800: "#099268",
900: "#087f5b"
},
green: {
name: "green",
50: "#ebfbee",
100: "#d3f9d8",
200: "#b2f2bb",
300: "#8ce99a",
400: "#69db7c",
500: "#51cf66",
600: "#40c057",
700: "#37b24d",
800: "#2f9e44",
900: "#2b8a3e"
},
lime: {
name: "lime",
50: "#f4fce3",
100: "#e9fac8",
200: "#d8f5a2",
300: "#c0eb75",
400: "#a9e34b",
500: "#94d82d",
600: "#82c91e",
700: "#74b816",
800: "#66a80f",
900: "#5c940d"
},
yellow: {
name: "yellow",
50: "#fff9db",
100: "#fff3bf",
200: "#ffec99",
300: "#ffe066",
400: "#ffd43b",
500: "#fcc419",
600: "#fab005",
700: "#f59f00",
800: "#f08c00",
900: "#e67700"
},
orange: {
name: "orange",
50: "#fff4e6",
100: "#ffe8cc",
200: "#ffd8a8",
300: "#ffc078",
400: "#ffa94d",
500: "#ff922b",
600: "#fd7e14",
700: "#f76707",
800: "#e8590c",
900: "#d9480f"
},
darkblue: {
name: "darkblue",
50: "#E8F4F9",
100: "#D9DEE9",
200: "#B7C2DA",
300: "#6482C0",
400: "#4267B2",
500: "#385898",
600: "#314E89",
700: "#29487D",
800: "#223B67",
900: "#1E355B"
}
};
const darkest = 10;
const lightest = 95;
function setLightness(hsl, lightness) {
hsl.l = lightness;
return tinycolor(hsl).toHexString();
}
function getTextColor(hex) {
const color = tinycolor(hex);
const hsl = color.toHsl();
return setLightness(hsl, color.isDark() ? lightest : darkest);
}
function isLight(color) {
return tinycolor(color).getBrightness() > 120;
}
function isTextColorLight(element) {
return isLight(window.getComputedStyle(element).color);
}
function svgToUrl(str) {
const cleaned = str.replace(/[\t\n\r]/gim, "").replace(/\s\s+/g, " ");
const encoded = encodeURIComponent(cleaned).replace(/\(/g, "%28").replace(/\)/g, "%29");
return `data:image/svg+xml;charset=UTF-8,${encoded}`;
}
Object.values(colors).map((i) => i["900"]);
const IconArrowBigRight = `
`;
const IconArrowBigLeft = `
`;
const IconFileDownload = `
`;
const IconLoader2 = `
`;
const IconCategory = `
`;
const IconX = `
`;
const IconMenu2 = `
`;
const IconArrowAutofitWidth = `
`;
const IconArrowAutofitHeight = `
`;
const IconZoomInArea = `
`;
const IconZoomOutArea = `
`;
const IconZoomPan = `
`;
const IconArrowAutofitDown = `
`;
const IconArrowAutofitLeft = `
`;
const IconArrowAutofitRight = `
`;
const IconSpacingVertical = `
`;
const IconSettings = `
`;
const IconKeyboard = `
`;
const IconListNumbers = `
`;
const IconBookmarks = `
`;
const IconExternalLink = `
`;
const IconTrash = `
`;
const IconSun = `
`;
const IconMoon = `
`;
const IconCheck = `
`;
const IconPalette = `
`;
const IconBookmark = `
`;
const IconBookmarkOff = `
`;
const IconEye = `
`;
const IconEyeOff = `
`;
const IconZoomCancel = `
`;
const IconZoomIn = `
`;
const IconZoomOut = `
`;
const IconRefresh = `
`;
const IconPhoto = `
`;
const IconPhotoOff = `
`;
const IconPencil = `
`;
const IconDeviceFloppy = `
`;
const IconMessage = `
`;
const styles = ":root {\n --theme-body-background: #25262b;\n --theme-body-text-color: #c1c2c5;\n --theme-text-color: #c1c2c5;\n --theme-primary-color: #1a1b1e;\n --theme-primary-text-color: #c1c2c5;\n --theme-background-color: #25262b;\n --theme-hightlight-color: #2c2e33;\n --theme-border-color: #373a40;\n}\n\n/* Simple Normalizer */\nhtml {\n font-size: 100%;\n}\n\nbody {\n margin: 0;\n font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;\n font-size: 14px;\n line-height: 20px;\n color: #333;\n background-color: #fff;\n padding: 0;\n}\n\na,\na:link,\na:visited,\na:active,\na:focus {\n color: var(--theme-body-text-color);\n text-decoration: none;\n}\n\nimg {\n height: auto;\n vertical-align: middle;\n border: 0 none;\n}\n\n.icon-tabler {\n height: 1rem;\n width: 1rem;\n align-self: center;\n vertical-align: sub;\n}\n\n#nprogress .bar {\n background: #29d;\n position: fixed;\n z-index: 1031;\n top: 0;\n left: 0;\n width: 100%;\n height: 4px;\n}\n\n#MangaOnlineViewer {\n padding-bottom: 40px;\n min-height: 760px;\n min-width: 360px;\n width: 100%;\n height: 100%;\n text-decoration: none;\n color: var(--theme-body-text-color);\n background-color: var(--theme-body-background);\n}\n\n#MangaOnlineViewer #Chapter {\n display: grid;\n grid-template-columns: repeat(1, 1fr);\n min-width: 225px;\n}\n\n#MangaOnlineViewer #Chapter.FluidLTR {\n direction: ltr;\n}\n\n#MangaOnlineViewer #Chapter.FluidRTL {\n direction: rtl;\n}\n\n#MangaOnlineViewer #Chapter.FluidLTR,\n#MangaOnlineViewer #Chapter.FluidRTL {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n}\n\n#MangaOnlineViewer #Chapter.FluidLTR .PageImg,\n#MangaOnlineViewer #Chapter.FluidRTL .PageImg {\n min-width: unset;\n}\n\n#MangaOnlineViewer #Chapter.FluidLTR .MangaPage.DoublePage,\n#MangaOnlineViewer #Chapter.FluidRTL .MangaPage.DoublePage {\n grid-column: span 2;\n}\n\n#MangaOnlineViewer #Chapter.FluidLTR .MangaPage:not(.DoublePage):nth-child(2n),\n#MangaOnlineViewer #Chapter.FluidRTL .MangaPage:not(.DoublePage):nth-child(2n) {\n display: flex;\n justify-content: start;\n}\n\n#MangaOnlineViewer #Chapter.FluidLTR .MangaPage:not(.DoublePage):nth-child(2n-1),\n#MangaOnlineViewer #Chapter.FluidRTL .MangaPage:not(.DoublePage):nth-child(2n-1) {\n display: flex;\n justify-content: end;\n}\n\n#MangaOnlineViewer #Chapter.Vertical .PageContent {\n margin-bottom: 15px;\n}\n\n#MangaOnlineViewer #Chapter.FluidLTR .MangaPage,\n#MangaOnlineViewer #Chapter.FluidRTL .MangaPage {\n width: auto;\n}\n\n#MangaOnlineViewer #Chapter.FluidLTR .ZoomWidth .icon-tabler,\n#MangaOnlineViewer #Chapter.FluidRTL .ZoomWidth .icon-tabler {\n color: red;\n}\n\n#MangaOnlineViewer #SettingsPanel {\n color: var(--theme-text-color);\n padding: 10px;\n position: fixed;\n top: 0;\n left: 0;\n bottom: 0;\n z-index: 1000;\n transition: transform 0.3s ease-in, background-color 0.3s linear;\n transform: translateX(-100%);\n display: flex;\n flex-flow: column;\n gap: 5px;\n overflow-y: auto;\n max-width: 100vw;\n width: 305px;\n}\n\n#MangaOnlineViewer #SettingsPanel.visible {\n transform: translateX(0);\n}\n\n#MangaOnlineViewer #SettingsPanel .ControlLabel {\n display: flex;\n flex-flow: row wrap;\n justify-content: space-between;\n align-items: center;\n}\n\n#MangaOnlineViewer #SettingsPanel .ControlLabelItem {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n\n#MangaOnlineViewer #SettingsPanel .ControlLabelItem:not(.show) {\n display: none;\n}\n\n#MangaOnlineViewer #SettingsPanel input[type='range'] {\n width: 100%;\n}\n\n#MangaOnlineViewer #SettingsPanel .RangeValue {\n display: inline-block;\n color: var(--theme-primary-text-color);\n line-height: 20px;\n text-align: center;\n border-radius: 3px;\n background: var(--theme-primary-color);\n padding: 2px 5px;\n margin-left: 8px;\n}\n\n#MangaOnlineViewer #SettingsPanel datalist {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: center;\n writing-mode: vertical-lr;\n width: 100%;\n}\n\n#MangaOnlineViewer #SettingsPanel datalist option {\n padding: 0;\n}\n\n#MangaOnlineViewer #ThemeSection {\n border: 1px solid var(--theme-body-text-color);\n border-radius: 10px;\n padding: 10px;\n}\n\n#MangaOnlineViewer .closeButton {\n width: fit-content;\n height: fit-content;\n position: absolute;\n right: 10px;\n top: 10px;\n}\n\n#MangaOnlineViewer .overlay {\n position: fixed;\n display: none;\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0, 0, 0, 0.5);\n z-index: 950;\n cursor: pointer;\n}\n\n#MangaOnlineViewer .overlay.visible {\n display: block;\n}\n\n#MangaOnlineViewer .ThemeRadio {\n border: 1px solid var(--theme-text-color);\n color: var(--theme-primary-text-color);\n background-color: var(--theme-primary-color);\n height: 20px;\n width: 20px;\n border-radius: 50%;\n padding: 1px;\n margin: 2px 5px;\n position: relative;\n}\n\n#MangaOnlineViewer .ThemeRadio svg {\n position: absolute;\n top: 15%;\n right: 15%;\n}\n\n#MangaOnlineViewer .ThemeRadio.custom {\n /*background-image: url(\"${svgToUrl(IconPalette)}\");*/\n /*background-position: center;*/\n /*background-repeat: no-repeat;*/\n /*background-size: 80%;*/\n}\n\n#MangaOnlineViewer .ThemeRadio.selected .icon-tabler-check {\n display: inline;\n}\n\n#MangaOnlineViewer .ThemeRadio:not(.selected) .icon-tabler-check {\n display: none;\n}\n\n#MangaOnlineViewer #KeybindingsPanel {\n padding: 10px;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n transition: transform 0.3s ease-in-out;\n transform: translateX(100%);\n line-height: 1.5em;\n z-index: 1000;\n overflow-y: auto;\n width: 360px;\n max-width: 100vw;\n}\n\n#MangaOnlineViewer #KeybindingsPanel.visible {\n transform: translateX(0);\n display: block;\n}\n\n#MangaOnlineViewer #KeybindingsPanel #KeybindingsList {\n display: grid;\n grid-template-columns: 1fr 2fr;\n gap: 5px;\n}\n\n#MangaOnlineViewer #KeybindingsPanel input {\n display: inline-block;\n width: 100%;\n}\n\n#MangaOnlineViewer #KeybindingsPanel #HotKeysRules {\n grid-column: span 2;\n}\n\n#MangaOnlineViewer #BookmarksPanel {\n position: fixed;\n top: 10%;\n width: 50%;\n left: 25%;\n right: 25%;\n text-align: center;\n max-height: 70%;\n transition: transform 0.3s ease-in-out;\n transform: scaleY(0%);\n z-index: 1000;\n}\n\n#MangaOnlineViewer #BookmarksPanel.visible {\n transform: scaleY(100%);\n display: block;\n}\n\n#MangaOnlineViewer #BookmarksList {\n padding: 0 15px;\n overflow: auto;\n max-height: 60vh;\n}\n\n#MangaOnlineViewer #BookmarksList .BookmarkItem {\n display: flex;\n flex-flow: row;\n justify-content: space-between;\n align-items: center;\n padding: 2px;\n}\n\n#MangaOnlineViewer #BookmarksList .bookmarkData {\n flex-basis: 15%;\n}\n\n#MangaOnlineViewer #BookmarksList .bookmarkURl {\n text-overflow: ellipsis;\n overflow: hidden;\n white-space: nowrap;\n flex-basis: 55%;\n}\n\n#MangaOnlineViewer select {\n height: 20px;\n padding: 0;\n margin-bottom: 5px;\n}\n\n#MangaOnlineViewer .ControlButton {\n cursor: pointer;\n border-radius: 5px;\n border-width: 1px;\n padding: 2px;\n min-height: 32px;\n color: var(--theme-primary-text-color);\n background-color: var(--theme-primary-color);\n border-color: var(--theme-border-color);\n}\n\n#MangaOnlineViewer .ControlButton:hover {\n opacity: 0.8;\n}\n\n#MangaOnlineViewer .panel {\n padding: 5px;\n position: inherit;\n border-radius: 5px;\n background-color: var(--theme-background-color);\n}\n\n#MangaOnlineViewer .MangaPage {\n width: 100%;\n display: inline-block;\n text-align: center;\n /*transform: translate3d(0, 0, 0);*/\n /*backface-visibility: hidden;*/\n /*perspective: 1000px;*/\n line-height: 0;\n min-height: 22px;\n min-width: 100%;\n}\n\n#MangaOnlineViewer .PageContent {\n text-align: center;\n display: inline-block;\n overflow-x: auto;\n max-width: 100%;\n transition: all 0.3s ease-in-out;\n height: 100%;\n overflow-y: hidden;\n}\n\n#MangaOnlineViewer .MangaPage.hide .PageContent {\n /*display: none;*/\n height: 0;\n}\n\n#MangaOnlineViewer .MangaPage.hide .PageFunctions {\n /*position:relative;*/\n}\n\n#MangaOnlineViewer .PageContent .PageImg[src=''],\n#MangaOnlineViewer .PageContent .PageImg:not([src]) {\n width: 40vw;\n height: 80vh;\n display: inline-block;\n background-position: center;\n background-repeat: no-repeat;\n background-size: 20%;\n background-color: var(--theme-hightlight-color);\n}\n\n#MangaOnlineViewer .PageContent .PageImg.imgBroken {\n width: 40vw;\n height: 80vh;\n display: inline-block;\n background-position: center;\n background-repeat: no-repeat;\n background-size: 20%;\n background-color: var(--theme-hightlight-color);\n}\n\n#MangaOnlineViewer .Thumbnail .ThumbnailImg[src=''],\n#MangaOnlineViewer .Thumbnail .ThumbnailImg:not([src]) {\n width: 100px;\n height: 150px;\n display: inline-block;\n background-position: center;\n background-repeat: no-repeat;\n background-size: 20%;\n}\n\n#MangaOnlineViewer .fitWidthIfOversize .PageContent .PageImg {\n max-width: 100%;\n}\n\n#MangaOnlineViewer #gotoPage {\n min-width: 35px;\n}\n\n#MangaOnlineViewer #ThemeSelector {\n width: 110px;\n}\n\n#MangaOnlineViewer #Header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-flow: row nowrap;\n transition: transform 0.3s ease-in;\n position: sticky;\n top: 0;\n left: 0;\n right: 0;\n background-color: inherit;\n z-index: 900;\n}\n\n#MangaOnlineViewer #Header.scroll.headroom-hide {\n transform: translateY(-100%);\n}\n\n#MangaOnlineViewer #Header.scroll.headroom-show {\n transform: translateY(-1%);\n}\n\n#MangaOnlineViewer #Header.hover,\n#MangaOnlineViewer #Header.fixed,\n#MangaOnlineViewer #Header.click {\n position: static;\n transform: none;\n}\n\n#MangaOnlineViewer #Header.headroom-end,\n#MangaOnlineViewer #Header.visible,\n#MangaOnlineViewer #Header.fixed {\n transform: translateY(-1%);\n position: sticky;\n}\n\n#MangaOnlineViewer #Header.hover:hover,\n#MangaOnlineViewer #Header.fixed {\n position: sticky;\n}\n\n#MangaOnlineViewer #Header.scroll #menu,\n#MangaOnlineViewer #Header.fixed #menu,\n#MangaOnlineViewer #Header.hover:hover #menu,\n#MangaOnlineViewer #Header:not(.click).visible #menu {\n display: none;\n}\n\n#MangaOnlineViewer #menu {\n position: fixed;\n min-height: 70px;\n width: 100%;\n top: 0;\n z-index: 1;\n color: var(--theme-body-text-color);\n}\n\n#MangaOnlineViewer #Header.click #menu {\n cursor: pointer;\n}\n\n#MangaOnlineViewer #Header.click:not(.headroom-hide) #menu,\n#MangaOnlineViewer #Header.click.headroom-end #menu,\n#MangaOnlineViewer #Header.click.visible #menu {\n position: static;\n width: 50px;\n min-height: unset;\n}\n\n#MangaOnlineViewer #MangaTitle {\n padding: 2px;\n margin: 0;\n font-size: 1.2rem;\n font-weight: normal;\n}\n\n#MangaOnlineViewer #GlobalFunctions {\n display: flex;\n gap: 3px;\n padding-left: 10px;\n flex-wrap: wrap;\n width: 300px;\n z-index: 100;\n}\n\n#MangaOnlineViewer #GlobalFunctions .icon-tabler {\n width: 25px;\n height: 25px;\n}\n\n#MangaOnlineViewer #GlobalFunctions #ZoomSlider {\n display: flex;\n align-items: center;\n}\n\n#MangaOnlineViewer #GlobalFunctions #Zoom {\n margin-left: 5px;\n}\n\n#MangaOnlineViewer #GlobalFunctions #ZoomVal {\n width: 40px;\n display: inline-block;\n color: var(--theme-primary-text-color);\n line-height: 20px;\n text-align: center;\n border-radius: 3px;\n background: var(--theme-primary-color);\n padding: 2px 5px;\n}\n\n#MangaOnlineViewer #ChapterNavigation {\n display: flex;\n flex-flow: column nowrap;\n justify-content: center;\n align-items: end;\n padding-right: 10px;\n width: 300px;\n}\n\n#MangaOnlineViewer .ChapterControl {\n display: flex;\n flex-wrap: nowrap;\n}\n\n#MangaOnlineViewer .ChapterControl .NavigationControlButton {\n display: inline-flex;\n margin-left: 3px;\n justify-content: center;\n align-items: center;\n padding: 5px 10px;\n gap: 0.5em;\n}\n\n#MangaOnlineViewer .ChapterControl .NavigationControlButton .icon-tabler {\n flex-shrink: 0;\n align-self: center;\n width: 1rem;\n height: 1rem;\n}\n\n#MangaOnlineViewer .ChapterControl .NavigationControlButton[href='#'],\n#MangaOnlineViewer .ChapterControl .NavigationControlButton[href=''],\n#MangaOnlineViewer .ChapterControl .NavigationControlButton[href='undefined'] {\n visibility: hidden;\n}\n\n#MangaOnlineViewer .ChapterControl #download.loading {\n cursor: not-allowed;\n pointer-events: none;\n opacity: 0.6;\n}\n\n#MangaOnlineViewer .ChapterControl #download.disabled {\n visibility: hidden;\n}\n\n#MangaOnlineViewer .ViewerTitle {\n text-align: center;\n min-height: 60px;\n /*max-width: 500px;*/\n display: flex;\n justify-content: center;\n align-items: center;\n flex-direction: column;\n padding: 5px;\n flex-basis: 60%;\n}\n\n#MangaOnlineViewer .ViewerTitle #series[href='#'],\n#MangaOnlineViewer .ViewerTitle #series[href=''],\n#MangaOnlineViewer .ViewerTitle #series[href='undefined'] {\n visibility: hidden;\n}\n\n#MangaOnlineViewer .PageFunctions {\n font-family: monospace;\n display: flex;\n justify-content: flex-end;\n align-items: center;\n margin: 0;\n padding: 0;\n gap: 3px;\n position: absolute;\n right: 0;\n}\n\n#MangaOnlineViewer .PageFunctions > .PageIndex {\n background-color: var(--theme-primary-color);\n color: var(--theme-primary-text-color);\n min-width: 20px;\n text-align: center;\n display: inline-block;\n padding: 3px 5px;\n line-height: 1rem;\n border-radius: 5px;\n}\n\n#MangaOnlineViewer .PageFunctions .ControlButton {\n padding: 3px;\n display: flex;\n justify-content: center;\n align-items: center;\n margin: 0;\n border-width: 0;\n min-height: auto;\n opacity: 0.5;\n}\n\n#MangaOnlineViewer .PageFunctions:hover .ControlButton {\n opacity: 1;\n}\n\n#MangaOnlineViewer .PageFunctions .ControlButton:hover {\n opacity: 0.9;\n}\n\n#MangaOnlineViewer .ControlButton.hidden,\n#MangaOnlineViewer.light #ColorScheme > :not(.inverse),\n#MangaOnlineViewer:not(.light) #ColorScheme > .inverse,\n#MangaOnlineViewer .ChapterControl #download.loading > :not(.inverse),\n#MangaOnlineViewer .ChapterControl #download:not(.loading) > .inverse,\n#MangaOnlineViewer .MangaPage.hide .ControlButton.Hide > .inverse,\n#MangaOnlineViewer .MangaPage:not(.hide) .ControlButton.Hide > :not(.inverse),\n#MangaOnlineViewer.bookmarked .ControlButton.Bookmark > :not(.inverse),\n#MangaOnlineViewer:not(.bookmarked) .ControlButton.Bookmark > .inverse,\n#MangaOnlineViewer #CommentsPainel.hide,\n#MangaOnlineViewer #CommentsArea.hide {\n display: none;\n}\n\n#MangaOnlineViewer.hideControls .PageFunctions {\n visibility: hidden;\n}\n\n#MangaOnlineViewer #NavigationCounters {\n margin: 5px;\n width: 100%;\n line-height: 1rem;\n}\n\n#MangaOnlineViewer #Navigation {\n color: var(--theme-text-color);\n background-color: var(--theme-hightlight-color);\n bottom: -180px;\n height: 185px;\n overflow-x: hidden;\n overflow-y: hidden;\n padding-bottom: 20px;\n position: fixed;\n white-space: nowrap;\n width: 100%;\n text-align: center;\n transition: transform 0.3s ease-in, background-color 0.3s linear;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n line-height: 0;\n}\n\n#MangaOnlineViewer #Navigation #Thumbnails {\n overflow-x: auto;\n overflow-y: hidden;\n margin-right: 10px;\n}\n\n#MangaOnlineViewer #Navigation:hover {\n transform: translateY(-180px);\n}\n\n#MangaOnlineViewer #Navigation.disabled {\n display: none;\n}\n\n#MangaOnlineViewer #Navigation.visible {\n transform: translateY(-180px);\n}\n\n#MangaOnlineViewer #Navigation .Thumbnail {\n display: inline-block;\n height: 150px;\n margin: 0 5px;\n border: 1px solid var(--theme-primary-color);\n}\n\n#MangaOnlineViewer #Navigation .Thumbnail .ThumbnailIndex {\n color: var(--theme-text-color);\n background-color: var(--theme-hightlight-color);\n display: block;\n opacity: 0.8;\n position: relative;\n bottom: 25%;\n width: 100%;\n line-height: 1rem;\n}\n\n#MangaOnlineViewer #Navigation .Thumbnail .ThumbnailImg {\n cursor: pointer;\n display: inline-block;\n max-height: 150px;\n min-height: 150px;\n min-width: 80px;\n max-width: 160px;\n}\n\n#MangaOnlineViewer #menu .icon-tabler {\n position: absolute;\n top: 5px;\n left: 10px;\n height: 32px;\n width: 32px;\n}\n\n#MangaOnlineViewer #CommentsPainel {\n padding: 10px;\n}\n\n#MangaOnlineViewer #CommentsArea {\n background: var(--theme-body-background);\n}\n\n#MangaOnlineViewer #CommentsButton {\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n/* Medium devices (landscape phones, tablets) */\n@media (max-width: 992px) {\n #MangaOnlineViewer #Header {\n flex-direction: column;\n }\n\n #MangaOnlineViewer .PageContent .PageImg {\n max-width: 100%;\n }\n\n #MangaOnlineViewer .ViewerTitle {\n order: 1;\n min-height: auto;\n padding: 0;\n margin: 0;\n }\n\n #MangaOnlineViewer #GlobalFunctions {\n flex-wrap: nowrap;\n width: auto;\n order: 3;\n padding: 5px;\n }\n\n #MangaOnlineViewer #ChapterNavigation {\n order: 2;\n }\n\n #MangaOnlineViewer #GlobalFunctions #ZoomSlider,\n #MangaOnlineViewer #GlobalFunctions .ControlButton:not(.tablets, .phones) {\n display: none;\n }\n}\n\n/* Small devices (portrait phones) */\n@media (max-width: 600px) {\n #MangaOnlineViewer #Header {\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: center;\n align-items: center;\n }\n\n #MangaOnlineViewer .ViewerTitle {\n order: 1;\n flex-basis: 100%;\n margin-top: 0;\n height: auto;\n padding: 0;\n }\n\n #MangaOnlineViewer #GlobalFunctions {\n order: 2;\n padding: 0;\n min-width: auto;\n }\n\n #MangaOnlineViewer #ChapterNavigation {\n order: 3;\n width: auto;\n }\n\n #MangaOnlineViewer #Navigation {\n display: none;\n }\n\n #MangaOnlineViewer .PageFunctions {\n padding: 0;\n }\n\n #MangaOnlineViewer .PageFunctions .ControlButton:not(.Bookmark) {\n display: none;\n }\n\n #MangaOnlineViewer .PageFunctions .ControlButton.Bookmark {\n opacity: 1;\n }\n\n #MangaOnlineViewer .PageContent {\n margin: 0;\n width: 100%;\n }\n\n #MangaOnlineViewer .PageContent .PageImg {\n max-width: 100%;\n }\n\n #MangaOnlineViewer #GlobalFunctions #ZoomSlider,\n #MangaOnlineViewer #GlobalFunctions .ControlButton:not(.phones) {\n display: none;\n }\n\n #MangaOnlineViewer #SettingsPanel .DefaultZoomMode,\n #MangaOnlineViewer #SettingsPanel .DefaultZoom,\n #MangaOnlineViewer #SettingsPanel .viewMode,\n #MangaOnlineViewer #SettingsPanel .fitIfOversize,\n #MangaOnlineViewer #SettingsPanel .showThumbnails,\n #MangaOnlineViewer #SettingsPanel .lazyLoadImages,\n #MangaOnlineViewer #SettingsPanel .downloadZip,\n #MangaOnlineViewer #SettingsPanel .minZoom,\n #MangaOnlineViewer #SettingsPanel .zoomStep,\n #MangaOnlineViewer #SettingsPanel .headerType {\n display: none;\n }\n\n #MangaOnlineViewer #KeybindingsPanel {\n display: none;\n }\n\n #MangaOnlineViewer .ChapterControl .download {\n display: none;\n }\n\n #MangaOnlineViewer #Counters {\n display: none;\n }\n}\n\n@-webkit-keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n.animate-spin {\n -webkit-animation: spin 1s linear infinite;\n animation: spin 1s linear infinite;\n}\n\n@-webkit-keyframes spin-reverse {\n 0% {\n transform: rotate(360deg);\n }\n to {\n transform: rotate(0);\n }\n}\n\n@keyframes spin-reverse {\n 0% {\n transform: rotate(360deg);\n }\n to {\n transform: rotate(0);\n }\n}\n\n.animate-spin-reverse {\n -webkit-animation: spin-reverse 1s linear infinite;\n animation: spin-reverse 1s linear infinite;\n}\n";
const cssStyles = `
:root,
.dark,
.dark .default,
[data-theme='dark'] {
--theme-body-background: ${colors.dark["600"]};
--theme-body-text-color: ${colors.dark["50"]};
--theme-text-color: ${colors.dark["50"]};
--theme-primary-color: ${colors.dark["700"]};
--theme-primary-text-color: ${colors.dark["50"]};
--theme-background-color: ${colors.dark["600"]};
--theme-hightlight-color: ${colors.dark["500"]};
--theme-border-color: ${colors.dark["400"]};
}
.light,
.light .default,
[data-theme='light'] {
--theme-body-background: ${colors.gray["50"]};
--theme-body-text-color: ${colors.gray["900"]};
--theme-text-color: ${colors.gray["900"]};
--theme-primary-color: ${colors.gray["300"]};
--theme-primary-text-color: ${colors.gray["900"]};
--theme-background-color: ${colors.gray["50"]};
--theme-hightlight-color: ${colors.gray["500"]};
--theme-border-color: ${colors.gray["100"]};
}
#MangaOnlineViewer .PageContent .PageImg[src=""],
#MangaOnlineViewer .PageContent .PageImg:not([src]) {
background-image: url("${svgToUrl(IconPhoto)}");
}
#MangaOnlineViewer .PageContent .PageImg.imgBroken {
background-image: url("${svgToUrl(IconPhotoOff)}");
}
#MangaOnlineViewer .Thumbnail .ThumbnailImg[src=""],
#MangaOnlineViewer .Thumbnail .ThumbnailImg:not([src]) {
background-image: url("${svgToUrl(IconPhoto)}");
}
#MangaOnlineViewer .ThemeRadio.custom {
/*background-image: url("${svgToUrl(IconPalette)}");*/
}
${styles}
`;
const diffObj = (changed, original) => {
const changes = (object, base) => _.transform(
object,
(result, value, key) => {
if (!_.isEqual(value, base[key])) {
if (_.isArray(value)) {
result[key] = _.difference(value, base[key]);
} else if (_.isObject(value) && _.isObject(base[key])) {
result[key] = changes(value, base[key]);
} else {
result[key] = value;
}
}
}
/* omit accumulator */
);
return changes(changed, original);
};
const en_US = {
ID: "en_US",
NAME: "English (US)",
STARTING: "Starting
Manga OnlineViewer",
RESUME: "Resuming reading from Page ",
WAITING: "Please wait, 3 seconds...",
CHOOSE_BEGINNING: "Choose the Page to start from:",
BUTTON_START: "Start Manga OnlineViewer",
SETTINGS: "Settings",
LANGUAGE: "Language",
COLOR_SCHEME: "Color Scheme",
THEME: "Theme",
THEME_HUE: "Theme Primary Color Hue",
THEME_SHADE: "Theme Primary Color Shade",
DEFAULT_LOAD_MODE: "Default Load Mode",
LOAD_MODE_NORMAL: "Normal(Wait 3 sec)",
LOAD_MODE_ALWAYS: "Always(Immediately)",
LOAD_MODE_NEVER: "Never(Manually)",
LOAD_SPEED: "Load Speed Pages/Second",
DEFAULT_ZOOM: "Default Zoom (between 5 and 200)",
DEFAULT_ZOOM_MODE: "Default Zoom Mode",
MINIMUM_ZOOM: "Minimum Zoom relative to the width of screen (between 30 and 100)",
ZOOM_STEP: "Zoom Change Step (between 5 and 50)",
DEFAULT_VIEW_MODE: "Default View Mode",
VIEW_MODE_VERTICAL: "Vertical",
VIEW_MODE_LEFT: "Left to Right",
VIEW_MODE_RIGHT: "Right to Left",
VIEW_MODE_WEBCOMIC: "WebComic",
FIT_WIDTH_OVERSIZED: "Fit Width if Oversized",
SHOW_THUMBNAILS: "Show Thumbnails",
HIDE_CONTROLS: "Always Hide Page Controls",
HEADER_TYPE: "Change Header Type",
HEADER_HOVER: "Hover",
HEADER_SCROLL: "Scroll",
HEADER_CLICK: "Click",
HEADER_FIXED: "Fixed",
BUTTON_DOWNLOAD: "Download",
DOWNLOAD_ZIP: "Download Zip file",
DOWNLOAD_IMAGES: "Download Images as Zip Automatically",
BUTTON_NEXT: "Next",
NEXT_CHAPTER: "Next Chapter",
BUTTON_PREVIOUS: "Previous",
PREVIOUS_CHAPTER: "Previous Chapter",
BOOKMARKS: "Bookmarks",
BOOKMARK: "Bookmark",
BOOKMARK_REMOVED: "Bookmark Removed",
BOOKMARK_SAVED: "Bookmark Saved",
BOOKMARK_MESSAGE: "Next time you open this chapter it will resume from: