// ==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: Batoto, BilibiliComics, ComiCastle, Dynasty-Scans, Asura Scans, Flame Scans, Realm Scans, Voids-Scans, Luminous Scans, Shimada Scans, Night Scans, ManhwaFreak, OzulScansEn, INKR, InManga, KLManga, Leitor, LHTranslation, LynxScans, MangaBuddy, MangaDex, MangaFox, MangaHere, MangaFreak, Mangago, mangahosted, MangaHub, MangasIn, MangaKakalot, MangaNelo, MangaNato, MangaPark, Mangareader, MangaSee, Manga4life, MangaTigre, MangaToons, MangaTown, ManhuaScan, MReader, MangaGeko, NaniScans, NineManga, OlympusScans, PandaManga, RawDevart, ReadComicsOnline, ReadManga Today, Funmanga, MangaDoom, MangaInn, ReaperScans, SenManga(Raw), KLManga, TenManga, TuMangaOnline, TuManhwas, UnionMangas, WebNovel, WebToons, Manga33, YugenMangas, ZeroScans, FoOlSlide, Kireicake, Madara WordPress Plugin, MangaHaus, Isekai Scan, Comic Kiba, Zinmanga, mangatx, Toonily, Mngazuki, JaiminisBox, DisasterScans, ManhuaPlus, TopManhua, NovelMic, Reset-Scans, LeviatanScans, Dragon Tea
// @version 2023.08.09
// @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.3/dist/hotkeys.min.js
// @require https://cdn.jsdelivr.net/npm/range-slider-input@2.4.4/dist/rangeslider.nostyle.umd.min.js
// @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|asura|flamescans|realmscans|void-scans|luminousscans|shimascans|nightscans|manhwafreak|ozulscansen).(com|org|gg|xyz)\/.+/
// @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.)?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.)?mangas.in\/manga\/.+\/.+\/\d+/
// @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.)?tumanhwas.com\/view\/.+/
// @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|ch|novel)\/.+\/.+/
// @exclude /https?:\/\/(www.)?tsumino.com\/.+/
// @exclude /https?:\/\/(www.)?pururin.io\/.+/
// @downloadURL none
// ==/UserScript==
(function () {
'use strict';
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",
"ManhwaFreak",
"OzulScansEn"
],
url: /https?:\/\/(www.)?(asurascans|asura|flamescans|realmscans|void-scans|luminousscans|shimascans|nightscans|manhwafreak|ozulscansen).(com|org|gg|xyz)\/.+/,
homepage: [
"https://www.asura.gg/",
"https://flamescans.org/",
"https://realmscans.com/",
"https://void-scans.com/",
"https://luminousscans.com/",
"https://shimadascans.com/",
"https://nightscans.org/",
"https://manhwafreak.com/",
"https://ozulscansen.com/"
],
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")?.naturalWidth !== void 0 && document.querySelectorAll("#editor-v2-scroll-view-id > div > div").length > 2,
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 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)
};
}
};
function findImages() {
return [
...document.querySelectorAll(
".wp-manga-chapter-img, .blocks-gallery-item img, .reading-content img"
)
].map(
(img) => img?.getAttribute("src") ?? img?.getAttribute("data-src") ?? img?.getAttribute("data-full-url")
);
}
const madarawp = {
name: [
"Madara WordPress Plugin",
"MangaHaus",
"Isekai Scan",
"Comic Kiba",
"Zinmanga",
"mangatx",
"Toonily",
"Mngazuki",
"JaiminisBox",
"DisasterScans",
"ManhuaPlus",
"TopManhua",
"NovelMic",
"Reset-Scans",
"LeviatanScans",
"Dragon Tea"
],
url: /https?:\/\/.+\/(manga|series|manhua|comic|ch|novel)\/.+\/.+/,
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/",
"https://leviatanscans.com/",
"https://dragontea.ink/"
],
language: ["English"],
obs: "Any Site that uses Madara Wordpress Plugin",
category: "manga",
waitFunc: () => findImages().every(
(s) => s && /^([\t\n])*(https?:\/\/)?.+(jpg|jpeg|png|gif|bmp|webp)$/.test(s)
),
run() {
const images = findImages();
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
};
}
};
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 mangasin = {
name: "MangasIn",
url: /https?:\/\/(www.)?mangas.in\/manga\/.+\/.+\/\d+/,
homepage: "https://mangas.in/",
language: ["Spanish"],
category: "manga",
run() {
const images = [...document.querySelectorAll("#all img")];
const chapter = document.querySelector("#chapter-list li.active");
return {
title: document.querySelector("title")?.textContent?.trim(),
series: document.querySelector("#navbar-collapse-1 ul:nth-child(2) a")?.getAttribute("href"),
pages: images.length,
prev: chapter?.nextElementSibling?.firstElementChild?.getAttribute("href"),
next: chapter?.previousElementSibling?.firstElementChild?.getAttribute("href"),
listImages: images.map((img) => img.getAttribute("data-src"))
};
}
};
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 tumanhwas = {
name: "TuManhwas",
url: /https?:\/\/(www.)?tumanhwas.com\/view\/.+/,
homepage: "https://tumanhwas.com/",
language: ["Spanish"],
category: "manga",
run() {
const chapter = Array.from(document.querySelectorAll(".listupd a"));
const images = [...document.querySelectorAll("#chapter_imgs img")];
return {
title: document.querySelector(".releases h1")?.textContent?.trim(),
series: chapter.find((el) => el.textContent?.includes("Lista"))?.getAttribute("href"),
pages: images.length,
prev: chapter.find((el) => el.textContent?.includes("Anterior"))?.getAttribute("href"),
next: chapter.find((el) => el.textContent?.includes("Siguiente"))?.getAttribute("href"),
listImages: images.map((item) => $(item).attr("src"))
};
}
};
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,
mangasin,
mangakakalot,
mangapark,
mangareader,
mangasee,
mangatigre,
mangatoon,
mangatown,
manhuascan,
mreader,
naniscans,
ninemanga,
olympusscans,
pandamanga,
rawdevart,
readcomicsonline,
readmangatoday,
reaperscans,
// resetscans, deprecated
senmanga,
tapas,
tenmanga,
tmofans,
tumanhwas,
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 isDark(color) {
return tinycolor(color).getBrightness() <= 120;
}
function isBackgroundColorDark(element) {
return isDark(window.getComputedStyle(element).backgroundColor);
}
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: