// ==UserScript== // @name Manga OnlineViewer // @author Tago // @namespace https://github.com/TagoDR // @description Shows all pages at once in online view for these sites: Asura Scans, Flame Scans, Realm Scans, Alpha-scans, Batoto, ComiCastle, Dynasty-Scans, InManga, KLManga, Leitor, LHTranslation, MangaBuddy, MangaDex, MangaFox, MangaHere, MangaFreak, Mangago, mangahosted, MangaHub, MangaKakalot, MangaNelo, MangaNato, MangaPark, MangaRaw, Mangareader, MangaSee, Manga4life, MangaTigre, MangaTown, ManhuaScan, NineManga, PandaManga, RawDevart, ReadComicsOnline, ReadManga Today, Funmanga, MangaDoom, MangaInn, SenManga(Raw), ShimadaScans, KLManga, TenManga, TuMangaOnline, UnionMangas, WebToons, Manga33, ZeroScans, FoOlSlide, Kireicake, Yuri-ism, Sense-Scans, Madara WordPress Plugin, MangaHaus, Isekai Scan, Comic Kiba, Zinmanga, mangatx, Toonily, Mngazuki, ReaperScans, JaiminisBox, DisasterScans, ManhuaPlus // @version 2022.09.30 // @license MIT // @grant GM_getValue // @grant GM_setValue // @grant GM_listValues // @grant GM_deleteValue // @grant GM_xmlhttpRequest // @connect * // @require https://cdnjs.cloudflare.com/ajax/libs/tinycolor/1.4.2/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.35/sweetalert2.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js // @include /https?:\/\/(www.)?(asura|flamescans|realmscans|alpha-scans).(com|org|gg)\/.+/ // @include /https?:\/\/(www.)?bato.to\/chapter.*/ // @include /https?:\/\/(www.)?comicastle.org\/read\/.+\/[0-9]+.*/ // @include /https?:\/\/(www.)?dynasty-scans.com\/chapters\/.+/ // @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.)?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).com\/manga-\w\w\d+\/chapter-\d+)/ // @include /https?:\/\/(www.)?mangapark.(com|me|org|net)\/(manga|chapter|comic)\/.+\/.+/ // @include /https?:\/\/(www.)?mcreader.net\/reader\/.*/ // @include /https?:\/\/(www.)?mangareader.to\/read\/.+\/.+\/.+/ // @include /https?:\/\/(www.)?(mangasee123|manga4life).com\/read-online\/.+/ // @include /https?:\/\/(www.)?mangatigre.net\/.+\/.+\/.+/ // @include /https?:\/\/(www.|m.)?mangatown.com\/manga\/.+\/.+/ // @include /https?:\/\/(www.)?manhuascan.io\/.+chapter.+/ // @include /https?:\/\/(www.)?ninemanga.com\/chapter\/.+\/.+\.html/ // @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?:\/\/raw.senmanga.com\/.+\/.+\/?/ // @include /https?:\/\/(www.)?shimadascans.com\/.+series.+/ // @include /https?:\/\/(www.)?tapas.io\/episode\/.+/ // @include /https?:\/\/(www.)?(tenmanga|gardenmanage).com\/(chapter|statuses)\/.+/ // @include /https?:\/\/(www.)?(tmofans|lectortmo|followmanga).com\/.+\/.+\/(paginated|cascade)/ // @include /https?:\/\/(www.)?unionleitor.top\/leitor\/.+\/.+/ // @include /https?:\/\/(www.)?webtoons.com\/.+viewer.+/ // @include /https?:\/\/(www.)?(manga33).com\/manga\/.+/ // @include /https?:\/\/(www.)?zeroscans.com\/comics\/.+/ // @include /^(?!.*jaiminisbox).*\/read\/.+/ // @include /https?:\/\/.+\/(manga|series)\/.+\/.+/ // @exclude /https?:\/\/(www.)?tsumino.com\/.+/ // @exclude /https?:\/\/(www.)?pururin.io\/.+/ // @downloadURL none // ==/UserScript== (function () { 'use strict'; // == AsuraScans and FlameScans ==================================================================== var asurasflamecans = { name: ['Asura Scans', 'Flame Scans', 'Realm Scans', 'Alpha-scans'], url: /https?:\/\/(www.)?(asura|flamescans|realmscans|alpha-scans).(com|org|gg)\/.+/, homepage: [ 'https://www.asura.gg/', 'https://flamescans.org/', 'https://realmscans.com/', 'https://alpha-scans.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('src')), }; }, }; // == Batoto ======================================================================================= var 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')), }; }, }; // == ComiCastle =================================================================================== var comicastle = { name: 'ComiCastle', url: /https?:\/\/(www.)?comicastle.org\/read\/.+\/[0-9]+.*/, 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')), }; }, }; // == DynastyScans ================================================================================= var 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), }; }, }; // == InManga =================================================================================== /* eslint-disable no-underscore-dangle */ var 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)), }; }, }; // == FoOlSlide ==================================================================================== var foolslide = { name: ['FoOlSlide', 'Kireicake', 'Yuri-ism', 'Sense-Scans'], url: /^(?!.*jaiminisbox).*\/read\/.+/, homepage: [ '#', 'https://reader.kireicake.com', 'https://www.yuri-ism.net', 'https://sensescans.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(), 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', }; }, }; // == KLManga ====================================================================================== var 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')), }; }, }; // == Leitor ======================================================================================= var 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), }; }, }; // == LHTranslation ================================================================================ var 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')), }; }, }; // == Madara WordPress Plugin ====================================================================== // https://themeforest.net/item/madara-wordpress-theme-for-manga/20849828 var madarawp = { name: [ 'Madara WordPress Plugin', 'MangaHaus', 'Isekai Scan', 'Comic Kiba', 'Zinmanga', 'mangatx', 'Toonily', 'Mngazuki', 'ReaperScans', 'JaiminisBox', 'DisasterScans', 'ManhuaPlus', ], url: /https?:\/\/.+\/(manga|series)\/.+\/.+/, homepage: [ '#', 'https://manhuaus.com', 'https://isekaiscan.com/', 'https://comickiba.com/', 'https://zinmanga.com/', 'https://mangatx.com/', 'https://toonily.net/', 'https://mangazuki.me/', 'https://reaperscans.com/', 'https://jaiminisbox.net', 'https://disasterscans.com/', 'https://manhuaplus.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) => img.getAttribute('src') || img.getAttribute('data-src') || img.getAttribute('data-full-url')), }; }, }; // == MangaBuddy =================================================================================== var mangabuddy = { name: 'MangaBuddy', url: /https?:\/\/(www.)?mangabuddy.com\/.+\/chapter.+/, homepage: 'https://mangabuddy.com/', language: ['English'], category: 'manga', run() { const images = [...document.querySelectorAll('#chapter-images img')]; 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.map((img) => img.getAttribute('src')), }; }, }; // == MangaDex ===================================================================================== var mangadex = { name: 'MangaDex', url: /https?:\/\/(www.)?mangadex.org\/chapter\/.+(\/.+)?/, homepage: 'https://mangadex.org/', language: ['English'], category: 'manga', waitEle: "a[href^='/chapter/']", async run() { const chapterId = window.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("a[href^='/chapter/']"); return { title: document.querySelector('title')?.text.replace(' - MangaDex', ''), series: document.querySelector("a.text-primary[href^='/title/']")?.getAttribute('href'), pages: images.length, prev: chapters[1].getAttribute('href'), next: chapters[0].getAttribute('href'), listImages: images.map((img) => `${server.baseUrl}/data/${server.chapter.hash}/${img}`), }; }, }; // == MangaFox ===================================================================================== var 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()); // eslint-disable-next-line no-eval (0, eval)(api); // @ts-ignore 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]), }; }, }; // == MangaFreak =================================================================================== var 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')), }; }, }; var 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, }; }, }; // == mangahosted =================================================================================== var 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')), }; }, }; // == MangaHub ===================================================================================== var 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 = Object.values(JSON.parse(api?.data.chapter.pages.toString())); return { title: document.querySelector('#mangareader h3')?.textContent?.trim(), series: document.querySelector('#mangareader a')?.getAttribute('href'), pages: images.length, prev: document.querySelector('.previous a')?.getAttribute('href'), next: document.querySelector('.next a')?.getAttribute('href'), listImages: images.map((i) => `https://img.mghubcdn.com/file/imghub/${i}`), }; }, }; // == MangaKakalot ================================================================================= var mangakakalot = { name: ['MangaKakalot', 'MangaNelo', 'MangaNato'], url: /https?:\/\/(www.)?((manganelo|mangakakalot).com\/chapter\/.+\/.+|(manganato|readmanganato).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')), }; }, }; var mangapark = { name: 'MangaPark', url: /https?:\/\/(www.)?mangapark.(com|me|org|net)\/(manga|chapter|comic)\/.+\/.+/, homepage: 'https://mangapark.net/', language: ['English'], category: 'manga', waitVar: 'CryptoJS', run() { const pass = JSON.parse(CryptoJS.AES.decrypt(amWord, amPass).toString(CryptoJS.enc.Utf8)); return { title: document.querySelector('title')?.textContent?.trim(), series: document.querySelector('.nav-title a')?.getAttribute('href'), pages: imgPathLis.length, prev: document.querySelector('.nav-prev a')?.getAttribute('href'), next: document.querySelector('.nav-next a')?.getAttribute('href'), listImages: imgPathLis.map((i, index) => `${imgCdnHost + i}?${pass[index]}`), }; }, }; // == MangaRaw ===================================================================================== var mangaraw = { name: 'MangaRaw', url: /https?:\/\/(www.)?mcreader.net\/reader\/.*/, homepage: 'https://www.manga-raw.club/', 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')), }; }, }; // == Mangareader ================================================================================== var 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', run() { const chapter = document.querySelector('.chapter-item.active'); const images = [ ...document.querySelectorAll('.ds-image:not(.shuffled)[data-url], .iv-card:not(.shuffled)[data-url]'), ]; return { title: document.querySelector('.hr-manga h2')?.textContent?.trim(), series: document.querySelector('.hr-manga')?.getAttribute('href'), pages: images.length, prev: chapter?.nextElementSibling?.querySelector('a')?.getAttribute('href'), next: chapter?.previousElementSibling?.querySelector('a')?.getAttribute('href'), listImages: images.map((img) => img.getAttribute('data-url')), }; }, }; // == MangaSee ===================================================================================== var 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 === undefined) { 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(`000${i + 1}`).slice(-3)}.png`)), }; }, }; // == MangaTigre =================================================================================== var 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')), }; }, }; // == MangaTown ==================================================================================== var 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()); // eslint-disable-next-line no-eval (0, eval)(api); // @ts-ignore 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]), }; }, }; var 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, }; }, }; // == NineManga ==================================================================================== var 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', }; }, }; // == PandaManga ================================================================================== var 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, }; }, }; // == RawDevart =================================================================================== var 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')), }; }, }; // == ReadComicsOnline ============================================================================= var 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')), }; }, }; // == ReadManga.Today ============================================================================== var 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), }; }, }; // == SenManga ===================================================================================== var 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', }; }, }; // == ShimadaScans ================================================================================= var shimadascans = { name: 'ShimadaScans', url: /https?:\/\/(www.)?shimadascans.com\/.+series.+/, homepage: 'https://shimadascans.com/', language: ['English'], category: 'manga', run() { const images = [...document.querySelectorAll('.reading-content img')]; return { title: document.querySelector('title')?.textContent?.trim(), series: document.querySelector('.breadcrumb li:nth-child(2) a')?.getAttribute('href'), pages: images.length, prev: document.querySelector('a.prev_page')?.getAttribute('href'), next: document.querySelector('a.next_page')?.getAttribute('href'), listImages: images.map((img) => img.getAttribute('src')), }; }, }; // == Tapas ====================================================================================== var 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')), }; }, }; // == TenManga ===================================================================================== var 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'); // eslint-disable-next-line no-underscore-dangle 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, }; }, }; // == TMOFans ================================================================================== var tmofans = { name: 'TuMangaOnline', url: /https?:\/\/(www.)?(tmofans|lectortmo|followmanga).com\/.+\/.+\/(paginated|cascade)/, homepage: 'https://lectortmo.com/', language: ['Spanish'], category: 'manga', run() { const images = [...document.querySelectorAll('.img-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'), listPages: images.length > 1 ? null : Array(num) .fill(0) .map((_, i) => `${window.location.href.replace(/\/\d+$/, '')}/${i + 1}`), listImages: images.length > 1 ? images.map((item) => $(item).attr('data-src')) : null, img: '#viewer-container img, .viewer-page', }; }, }; // == UnionMangas ================================================================================= var 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')), }; }, }; // == WebToons ===================================================================================== var 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-src') || img.getAttribute('src')), }; }, }; // == WPManga ====================================================================================== var wpmanga = { name: ['Manga33'], url: /https?:\/\/(www.)?(manga33).com\/manga\/.+/, homepage: ['https://manga33.com/'], language: ['English'], category: 'manga', run() { 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 (window.location.pathname.match(/all.html$/)) return; if (window.location.pathname.match(/\d+-\d+.html$/)) window.location.pathname = window.location.pathname.replace(/-\d+.html$/, '-all.html'); }, }; }, }; // == ZeroScans ==================================================================================== var 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; // eslint-disable-next-line no-underscore-dangle 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, }; }, }; /* eslint-disable no-unused-vars,@typescript-eslint/no-unused-vars */ const sites = [ asurasflamecans, batoto, comicastle, dysnatyscans, inmanga, klmanga, leitor, lhtranslation, mangabuddy, mangadex, mangafox, mangafreak, mangago, mangahosted, mangahub, mangakakalot, mangapark, mangaraw, mangareader, mangasee, mangatigre, mangatown, manhuascan, ninemanga, pandamanga, rawdevart, readcomicsonline, readmangatoday, senmanga, shimadascans, tapas, tenmanga, tmofans, unionmangas, webtoons, wpmanga, zeroscans, foolslide, madarawp, // Must be at the end because is a generic check ]; /* eslint-disable camelcase */ function logScript(...text) { // eslint-disable-next-line no-console console.log('MangaOnlineViewer: ', ...text); return text; } // Replacement function for GM_listValues allowing for debugging in console function getListGM() { return typeof GM_listValues !== 'undefined' ? GM_listValues() : []; } // Replacement function for GM_listValues allowing for debugging in console function removeValueGM(name) { return typeof GM_deleteValue !== 'undefined' ? GM_deleteValue(name) : logScript('Removing: ', name); } // Replacement function for GM_info allowing for debugging in console const getInfoGM = typeof GM_info !== 'undefined' ? GM_info : { scriptHandler: 'Console', script: { name: 'Debug', version: 'Testing', }, }; // Replacement function for GM_getValue allowing for debugging in console 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); } // Replacement function for GM_setValue allowing for debugging in console function setValueGM(name, value) { try { GM_setValue(name, value); // logScript('Setting: ', name, ' = ', value as any); return value.toString(); } catch (e) { logScript('Fake Setting: ', name, ' = ', value); return String(value); } } function setJsonGM(name, value) { return setValueGM(name, JSON.stringify(value)); } function setSettings(value) { return setJsonGM('settings', value); } // See https://stackoverflow.com/a/2401861/331508 for optional browser sniffing code. function getBrowser() { const ua = navigator.userAgent; let tem; let M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || []; if (/trident/i.test(M[1])) { tem = /\brv[ :]+(\d+)/g.exec(ua) || []; return `IE ${tem[1] || ''}`; } if (M[1] === 'Chrome') { tem = ua.match(/\b(OPR|Edge)\/(\d+)/); if (tem !== null) { return tem.slice(1).join(' ').replace('OPR', 'Opera'); } } M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?']; tem = ua.match(/version\/(\d+)/i); if (tem !== null) { M.splice(1, 1, tem[1]); } return M.join(' '); } // See // https://stackoverflow.com/questions/27487828/how-to-detect-if-a-userscript-is-installed-from-the-chrome-store function getEngine() { return getInfoGM.scriptHandler || 'Greasemonkey'; } const isMobile = window.matchMedia('screen and (max-width: 768px)').matches; /** * Checks if a JavaScript value is empty * @example * isEmpty(null) // true * isEmpty(undefined) // true * isEmpty([]) // true * isEmpty({}) // true * isEmpty("") // true * isEmpty(false) // false * isEmpty(0) // false * isEmpty([{},{"0":false},{"":0},{"0":0}]) // false * isEmpty(42) // false * isEmpty([{"":1},{"0":1}]) // false * @param {any} value - item to test * @returns {boolean} true if empty, otherwise false */ function isEmpty(value) { return (value === null || // check for null typeof value === 'undefined' || value === undefined || // check for undefined (typeof value === 'string' && value === '') || // check for empty string (Array.isArray(value) && value.length === 0) || // check for empty array (typeof value === 'object' && Object.keys(value).length === 0)); } /** * Checks if value is nothing. Deep-checks arrays and objects * @example * isNothing(null) // true * isNothing(undefined) // true * isNothing([]) // true * isNothing({}) // true * isNothing("") // true * isNothing(false) // true * isNothing(0) // true * isNothing([{},{"0":false},{"":0},{"0":0}]) // true * isNothing(42) // false * isNothing([{"":1},{"0":1}]) // false * @param {any} value - item to test * @returns {boolean} true if nothing, otherwise false */ function isNothing(value) { const isEmptyObject = (a) => { if (!Array.isArray(a)) { // it's an Object, not an Array const hasNonempty = Object.keys(a).some((element) => !isNothing(a[element])); return hasNonempty ? false : isEmptyObject(Object.keys(a)); } // check if array is really not empty as JS thinks at least one element should be non-empty return !a.some((element) => !isNothing(element)); }; return ( // eslint-disable-next-line eqeqeq value == false || value === 0 || isEmpty(value) || (typeof value === 'object' && isEmptyObject(value))); } 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 svgToUrl(str) { const cleaned = str .replace(/[\t\n\r]/gim, '') // Strip newlines and tabs .replace(/\s\s+/g, ' ') // Condense multiple spaces .replace(/'/gim, '\\i'); // Normalize quotes const encoded = encodeURIComponent(cleaned) .replace(/\(/g, '%28') // Encode brackets .replace(/\)/g, '%29'); return `data:image/svg+xml;charset=UTF-8,${encoded}`; } Object.values(colors).map((i) => i['900']); // Icons from https://tabler-icons.io/ // Icons for Navigation const IconArrowBigRight = ` `; const IconArrowBigLeft = ` `; const IconFileDownload = ` `; const IconLoader2 = ` `; const IconCategory = ` `; // Icons for Global Controls 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 = ` `; // Icons for Page Controls const IconBookmark = ` `; const IconBookmarkOff = ` `; const IconEye = ` `; const IconEyeOff = ` `; const IconZoomCancel = ` `; const IconZoomIn = ` `; const IconZoomOut = ` `; const IconRefresh = ` `; const IconPhoto = ` `; const IconPhotoOff = ` `; // language=CSS var 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']}; } /* Simple Normalizer */ html { font-size: 100%; } body { margin: 0; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px; color: #333; background-color: #fff; padding: 0; } a, a:link, a:visited, a:active, a:focus { color: var(--theme-body-text-color); text-decoration: none; } img { height: auto; vertical-align: middle; border: 0 none; } .icon-tabler { height: 1rem; width: 1rem; align-self: center; vertical-align: sub; } #nprogress .bar { background: #29d; position: fixed; z-index: 1031; top: 0; left: 0; width: 100%; height: 4px; } #MangaOnlineViewer { padding-bottom: 40px; min-height: 760px; min-width: 360px; width:100%; height:100%; text-decoration:none; color: var(--theme-body-text-color); background-color: var(--theme-body-background); } #MangaOnlineViewer #Chapter { display: grid; grid-template-columns: repeat(1, 1fr); min-width: 225px; } #MangaOnlineViewer #Chapter.FluidLTR { direction: ltr; } #MangaOnlineViewer #Chapter.FluidRTL { direction: rtl; } #MangaOnlineViewer #Chapter.FluidLTR, #MangaOnlineViewer #Chapter.FluidRTL { display: grid; grid-template-columns: repeat(2, 1fr); } #MangaOnlineViewer #Chapter.FluidLTR .PageImg, #MangaOnlineViewer #Chapter.FluidRTL .PageImg { min-width: unset; } #MangaOnlineViewer #Chapter.FluidLTR .MangaPage.DoublePage, #MangaOnlineViewer #Chapter.FluidRTL .MangaPage.DoublePage { grid-column: span 2; } #MangaOnlineViewer #Chapter.FluidLTR .MangaPage:not(.DoublePage):nth-child(2n), #MangaOnlineViewer #Chapter.FluidRTL .MangaPage:not(.DoublePage):nth-child(2n) { justify-self: flex-start; } #MangaOnlineViewer #Chapter.FluidLTR .MangaPage:not(.DoublePage):nth-child(2n-1), #MangaOnlineViewer #Chapter.FluidRTL .MangaPage:not(.DoublePage):nth-child(2n-1) { justify-self: flex-end; } #MangaOnlineViewer #Chapter.Vertical .PageContent { margin-bottom: 15px; } #MangaOnlineViewer #Chapter.FluidLTR .MangaPage, #MangaOnlineViewer #Chapter.FluidRTL .MangaPage { width: auto; } #MangaOnlineViewer #Chapter.FluidLTR .ZoomWidth .icon-tabler, #MangaOnlineViewer #Chapter.FluidRTL .ZoomWidth .icon-tabler { color: red; } #MangaOnlineViewer #SettingsPanel { color: var(--theme-text-color); padding: 10px; position: fixed; top: 0; left: 0; bottom: 0; z-index: 1000; transition: transform 0.3s ease-in, background-color 0.3s linear; transform: translateX(-100%); display: flex; flex-flow: column; gap: 5px; overflow-y: auto; max-width: 100vw; width: 305px; } #MangaOnlineViewer #SettingsPanel.visible { transform: translateX(0); } #MangaOnlineViewer #SettingsPanel .ControlLabel { display: flex; flex-flow: row wrap; justify-content: space-between; align-items: center; } #MangaOnlineViewer #SettingsPanel .ControlLabelItem { display: flex; align-items: center; justify-content: space-between; } #MangaOnlineViewer #SettingsPanel .ControlLabelItem:not(.show) { display: none; } #MangaOnlineViewer #ThemeSection{ border: 1px solid var(--theme-body-text-color); border-radius: 10px; padding: 10px; } #MangaOnlineViewer .closeButton { width: fit-content; height: fit-content; position: absolute; right: 10px; top: 10px; } #MangaOnlineViewer .overlay { position: fixed; display: none; width: 100%; height: 100%; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 950; cursor: pointer; } #MangaOnlineViewer .overlay.visible { display: block; } #MangaOnlineViewer .ThemeRadio { border: 1px solid var(--theme-text-color); color: var(--theme-primary-text-color); background-color: var(--theme-primary-color); height: 20px; width: 20px; border-radius: 50%; padding: 1px; margin: 2px 5px; position: relative; } #MangaOnlineViewer .ThemeRadio svg{ position: absolute; top: 15%; right: 15%; } #MangaOnlineViewer .ThemeRadio.custom{ /*background-image: url("${svgToUrl(IconPalette)}");*/ /*background-position: center;*/ /*background-repeat: no-repeat;*/ /*background-size: 80%;*/ } #MangaOnlineViewer .ThemeRadio.selected .icon-tabler-check { display: inline; } #MangaOnlineViewer .ThemeRadio:not(.selected) .icon-tabler-check { display: none; } #MangaOnlineViewer #KeybindingsPanel { padding: 8px; position: fixed; top: 65px; right: 0; transition: transform 0.3s ease-in-out;; transform: translateX(100%); line-height: 1.5em; z-index: 1000; } #MangaOnlineViewer #KeybindingsPanel.visible { transform: translateX(0); display: block; } #MangaOnlineViewer #BookmarksPanel { position: fixed; top: 10%; width: 50%; left: 25%; right: 25%; text-align: center; max-height: 70%; transition: transform 0.3s ease-in-out;; transform: scaleY(0%); z-index: 1000; } #MangaOnlineViewer #BookmarksPanel.visible { transform: scaleY(100%); display: block; } #MangaOnlineViewer #BookmarksList { padding: 0 15px; overflow: auto; max-height: 60vh; } #MangaOnlineViewer #BookmarksList .BookmarkItem { display: flex; flex-flow: row; justify-content: space-between; align-items: center; padding: 2px; } #MangaOnlineViewer #BookmarksList .bookmarkData { flex-basis: 15%; } #MangaOnlineViewer #BookmarksList .bookmarkURl { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; flex-basis: 55%; } #MangaOnlineViewer select { height: 20px; padding: 0; margin-bottom: 5px; } #MangaOnlineViewer .ControlButton { cursor: pointer; border-radius: 5px; border-width: 1px; padding: 2px; min-height: 32px; color: var(--theme-primary-text-color); background-color: var(--theme-primary-color); border-color: var(--theme-border-color); } #MangaOnlineViewer .ControlButton:hover { opacity: 0.8; } #MangaOnlineViewer .panel { padding: 5px; position: inherit; border-radius: 5px; background-color: var(--theme-background-color); } #MangaOnlineViewer .MangaPage { width: 100%; display: inline-block; text-align: center; /*transform: translate3d(0, 0, 0);*/ /*backface-visibility: hidden;*/ /*perspective: 1000px;*/ line-height: 0; min-height: 22px; min-width: 100%; } #MangaOnlineViewer .PageContent { text-align: center; display: inline-block; overflow-x: auto; max-width: 100%; transition: all 0.3s ease-in-out; height: 100%; overflow-y: hidden; } #MangaOnlineViewer .MangaPage.hide .PageContent { /*display: none;*/ height: 0; } #MangaOnlineViewer .MangaPage.hide .PageFunctions { /*position:relative;*/ } #MangaOnlineViewer .PageContent .PageImg[src=""], #MangaOnlineViewer .PageContent .PageImg:not([src]) { width: 40vw; height: 80vh; display: inline-block; background-image: url("${svgToUrl(IconPhoto)}"); background-position: center; background-repeat: no-repeat; background-size: 20%; background-color: var(--theme-hightlight-color); } #MangaOnlineViewer .PageContent .PageImg.imgBroken { width: 40vw; height: 80vh; display: inline-block; background-image: url("${svgToUrl(IconPhotoOff)}"); background-position: center; background-repeat: no-repeat; background-size: 20%; background-color: var(--theme-hightlight-color); } #MangaOnlineViewer .Thumbnail .ThumbnailImg[src=""], #MangaOnlineViewer .Thumbnail .ThumbnailImg:not([src]) { width: 100px; height: 150px; display: inline-block; background-image: url("${svgToUrl(IconPhoto)}"); background-position: center; background-repeat: no-repeat; background-size: 20%; } #MangaOnlineViewer .fitWidthIfOversize .PageContent .PageImg { max-width: 100%; } #MangaOnlineViewer #gotoPage { min-width: 35px; } #MangaOnlineViewer #ThemeSelector { width: 110px; } #MangaOnlineViewer #Header { display: flex; justify-content: space-between; align-items: center; flex-flow: row nowrap; transition: transform 0.3s ease-in; position: sticky; top: 0; left: 0; right: 0; background-color: inherit; z-index: 900; } #MangaOnlineViewer #Header.scroll-hide { transform: translateY(-100%); } #MangaOnlineViewer #Header.scroll-show { transform: translateY(-1%); } #MangaOnlineViewer #Header.mouseOverMenu { position: static; transform: none; } #MangaOnlineViewer #Header.scroll-end, #MangaOnlineViewer #Header.visible { transform: translateY(-1%); position: sticky; } #MangaOnlineViewer #Header.mouseOverMenu:hover { position: sticky; } #MangaOnlineViewer #Header:not(.mouseOverMenu) #menu, #MangaOnlineViewer #Header.mouseOverMenu:hover #menu { display: none; } #MangaOnlineViewer #menu { position: fixed; min-height: 70px; width: 100%; top: 0; z-index: 1; color: var(--theme-body-text-color); } #MangaOnlineViewer #MangaTitle { padding: 2px; margin: 0; font-size: 1.2rem; font-weight: normal; } #MangaOnlineViewer #GlobalFunctions{ display: flex; gap: 3px; padding-left:10px; flex-wrap: wrap; width: 300px; z-index: 100; } #MangaOnlineViewer #GlobalFunctions .icon-tabler{ width: 25px; height: 25px; } #MangaOnlineViewer #ChapterNavigation { display: flex; flex-flow: column nowrap; justify-content: center; align-items: end; padding-right: 10px; width: 300px; } #MangaOnlineViewer .ChapterControl { display: flex; flex-wrap: nowrap; } #MangaOnlineViewer .ChapterControl .NavigationControlButton { display: inline-flex; margin-left: 3px; justify-content: center; align-items: center; padding: 5px 10px; gap: 0.5em; } #MangaOnlineViewer .ChapterControl .NavigationControlButton .icon-tabler { flex-shrink: 0; align-self: center; width: 1rem; height: 1rem; } #MangaOnlineViewer .ChapterControl .NavigationControlButton[value='#'], #MangaOnlineViewer .ChapterControl .NavigationControlButton[value=''], #MangaOnlineViewer .ChapterControl .NavigationControlButton[value='undefined'] { visibility: hidden; } #MangaOnlineViewer .ChapterControl .download.loading{ cursor: not-allowed; } #MangaOnlineViewer .ViewerTitle { text-align: center; min-height: 60px; /*max-width: 500px;*/ display: flex; justify-content: center; align-items: center; flex-direction: column; padding: 5px; flex-basis: 60%; } #MangaOnlineViewer #Counters { } #MangaOnlineViewer .PageFunctions { font-family: monospace; display: flex; justify-content: flex-end; align-items: center; margin: 0; padding: 0; gap: 3px; position: absolute; right: 0; } #MangaOnlineViewer .PageFunctions > .PageIndex { background-color: var(--theme-primary-color); color: var(--theme-primary-text-color); min-width: 20px; text-align: center; display: inline-block; padding: 3px 5px; line-height: 1rem; border-radius: 5px; border-top-right-radius: 5px; border-top-right-radius: 0; } #MangaOnlineViewer .PageFunctions .ControlButton { padding: 3px; display: flex; justify-content: center; align-items: center; margin: 0; border-width: 0; min-height: auto; opacity: 0.5; } #MangaOnlineViewer .PageFunctions:hover .ControlButton { opacity: 1; } #MangaOnlineViewer .PageFunctions .ControlButton:hover { opacity: 0.9; } #MangaOnlineViewer.light #ColorScheme > :not(.inverse), #MangaOnlineViewer:not(.light) #ColorScheme > .inverse, #MangaOnlineViewer .ChapterControl .download.loading > :not(.inverse), #MangaOnlineViewer .ChapterControl .download:not(.loading) > .inverse, #MangaOnlineViewer .MangaPage.hide .ControlButton.Hide > .inverse, #MangaOnlineViewer .MangaPage:not(.hide) .ControlButton.Hide > :not(.inverse), #MangaOnlineViewer.bookmarked .ControlButton.Bookmark > :not(.inverse), #MangaOnlineViewer:not(.bookmarked) .ControlButton.Bookmark > .inverse { display: none; } #MangaOnlineViewer.hideControls .PageFunctions { visibility: hidden; } #MangaOnlineViewer #NavigationCounters { margin: 5px; width: 100%; line-height: 1rem; } #MangaOnlineViewer #Navigation { color: var(--theme-text-color); background-color: var(--theme-hightlight-color); bottom: -180px; height: 185px; overflow-x: hidden; overflow-y: hidden; padding-bottom: 20px; position: fixed; white-space: nowrap; width: 100%; text-align: center; transition: transform 0.3s ease-in, background-color 0.3s linear; border-bottom-left-radius: 0; border-bottom-right-radius: 0; line-height: 0rem; } #MangaOnlineViewer #Navigation #Thumbnails { overflow-x: auto; overflow-y: hidden; margin-right: 10px; } #MangaOnlineViewer #Navigation:hover { transform: translateY(-180px); } #MangaOnlineViewer #Navigation.disabled { display: none; } #MangaOnlineViewer #Navigation.visible { transform: translateY(-180px); } #MangaOnlineViewer #Navigation .Thumbnail { display: inline-block; height: 150px; margin: 0 5px; border: 1px solid var(--theme-primary-color); } #MangaOnlineViewer #Navigation .Thumbnail .ThumbnailIndex { color: var(--theme-text-color); background-color: var(--theme-hightlight-color); display: block; opacity: 0.8; position: relative; bottom: 25%; width: 100%; line-height: 1rem; } #MangaOnlineViewer #Navigation .Thumbnail .ThumbnailImg { cursor: pointer; display: inline-block; max-height: 150px; min-height: 150px; min-width: 80px; max-width: 160px; } #MangaOnlineViewer #menu .icon-tabler { position: absolute; top: 5px; left: 10px; height: 32px; width: 32px; } @media (max-width: 992px) { #MangaOnlineViewer #Header { flex-direction: column; } #MangaOnlineViewer #Header.mouseOverMenu { position: sticky; transition: transform 0.3s ease-in; } #MangaOnlineViewer #Header.scroll-show { transform: translateY(-1%); } #MangaOnlineViewer #Header.scroll-hide { transform: translateY(-100%); } #MangaOnlineViewer .PageContent .PageImg { max-width: 100%; } #MangaOnlineViewer .ViewerTitle { order: 1; min-height: auto; padding: 0px; margin: 0px; } #MangaOnlineViewer #GlobalFunctions { flex-wrap: nowrap; padding: 0; width: auto; order: 3; padding: 5px; } #MangaOnlineViewer #ChapterNavigation { order: 2; } #MangaOnlineViewer #menu { display: none; } #MangaOnlineViewer #GlobalFunctions #keybindings { display: none; } } /* Small devices (landscape phones) */ @media (max-width: 600px) { #MangaOnlineViewer #Header { flex-direction: row; flex-wrap: wrap; justify-content: center; align-items: center; } #MangaOnlineViewer #Header.mouseOverMenu { position: sticky; transition: transform 0.3s ease-in; } #MangaOnlineViewer #Header.scroll-show { transform: translateY(-1%); } #MangaOnlineViewer #Header.scroll-hide { transform: translateY(-100%); } #MangaOnlineViewer .ViewerTitle { order: 1; flex-basis: 100%; margin-top: 0px; } #MangaOnlineViewer #GlobalFunctions { order: 2; padding: 0; } #MangaOnlineViewer #ChapterNavigation { order: 3; width: auto; } #MangaOnlineViewer #Navigation { display: none; } #MangaOnlineViewer .PageFunctions { padding: 0; } #MangaOnlineViewer .PageFunctions .ControlButton:not(.Bookmark) { display: none; } #MangaOnlineViewer .PageFunctions .ControlButton.Bookmark { opacity: 1; } #MangaOnlineViewer .PageContent { margin: 0; width: 100%; } #MangaOnlineViewer .PageContent .PageImg { max-width: 100%; } #MangaOnlineViewer #GlobalFunctions .ControlButton:not(#settings) { display: none; } #MangaOnlineViewer #GlobalFunctions { min-width: auto; } #MangaOnlineViewer #SettingsPanel .DefaultZoom, #MangaOnlineViewer #SettingsPanel .viewMode, #MangaOnlineViewer #SettingsPanel .fitIfOversize, #MangaOnlineViewer #SettingsPanel .showThumbnails, #MangaOnlineViewer #SettingsPanel .lazyLoadImages, #MangaOnlineViewer #SettingsPanel .downloadZip, #MangaOnlineViewer #SettingsPanel .minZoom, #MangaOnlineViewer #SettingsPanel .zoomStep, #MangaOnlineViewer #SettingsPanel .mouseOverMenu { display: none; } #MangaOnlineViewer #KeybindingsPanel { display: none; } #MangaOnlineViewer #menu { display: none; } #MangaOnlineViewer .ViewerTitle { height: auto; padding: 0; } #MangaOnlineViewer .ChapterControl { } #MangaOnlineViewer .ChapterControl .download { display: none; } #MangaOnlineViewer #Counters { display: none; } } @-webkit-keyframes spin { to { transform:rotate(360deg) } } @keyframes spin { to { transform:rotate(360deg) } } .animate-spin { -webkit-animation:spin 1s linear infinite; animation:spin 1s linear infinite } @-webkit-keyframes spin-reverse { 0% { transform:rotate(360deg) } to { transform:rotate(0) } } @keyframes spin-reverse { 0% { transform:rotate(360deg) } to { transform:rotate(0) } } .animate-spin-reverse { -webkit-animation:spin-reverse 1s linear infinite; animation:spin-reverse 1s linear infinite } `; /** * Deep diff between two object, using lodash * * We are comparing two objects: a and b. * Object b is newer and similar to a. * We are looking for changes from a to b. * We assume that data types haven't changed (String to Number). * We assume that parent is either an Array or an Object. * * This is the "easier on the programmer, harder on the computer" method. * It's very inefficient. Worst case is something like O(n^n). * But this works, is easier to understand that the "correct" * implementation, and entertained me for a bit. * * * http://stackoverflow.com/questions/31295545/how-to-get-only-the-changed-values-from-two-json-objects * https://gist.github.com/Yimiprod/7ee176597fef230d1451 * https://stackoverflow.com/questions/8572826/generic-deep-diff-between-two-objects * https://qastack.com.br/programming/31683075/how-to-do-a-deep-comparison-between-2-objects-with-lodash * @param {Object} changed Object compared * @param {Object} original Object to compare with * @return {Object} Return a new object who represent the diff */ 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; } } }); return changes(changed, original); }; const defaultSettings = { theme: 'darkblue', customTheme: '#263e3a', themeShade: 600, colorScheme: 'dark', fitWidthIfOversize: true, showThumbnails: true, downloadZip: false, throttlePageLoad: 1000, zoom: 100, zoomStep: 25, minZoom: 50, loadMode: 'wait', viewMode: 'WebComic', bookmarks: [], lazyLoadImages: false, lazyStart: 50, hidePageControls: false, mouseOverMenu: true, maxReload: 5, }; // Configuration let settings$1 = _.defaultsDeep(getSettings(defaultSettings), defaultSettings); // Force Settings for mobile if (isMobile) { settings$1.lazyLoadImages = true; settings$1.fitWidthIfOversize = true; settings$1.showThumbnails = false; settings$1.viewMode = 'WebComic'; } function useSettings() { return settings$1; } function updateSettings(newValue) { logScript(JSON.stringify(newValue)); settings$1 = { ...settings$1, ...newValue }; setSettings(diffObj(settings$1, defaultSettings)); } function resetSettings() { getListGM().forEach((setting) => removeValueGM(setting)); updateSettings(defaultSettings); } // Clear old Bookmarks const bookmarkTimeLimit = 1000 * 60 * 60 * 24 * 30 * 12; // year const refreshedBookmark = settings$1.bookmarks.filter((el) => Date.now() - el.date < bookmarkTimeLimit); if (settings$1.bookmarks.length !== refreshedBookmark.length) updateSettings({ bookmarks: refreshedBookmark }); // Creates the style element function createStyleElement(id, content) { const style = document.createElement('style'); style.id = id; style.appendChild(document.createTextNode(content)); return style; } // Appends CSS content to the head of the site function appendStyleSheet(id, content) { if (!document.querySelector(`#${id}`)) { const head = document.head || document.querySelector('head'); head.appendChild(createStyleElement(id, content)); } } function removeStyleSheet(id) { document.querySelectorAll(`style[id="${id}"]`).forEach((elem) => elem.remove()); } function replaceStyleSheet(id, content) { removeStyleSheet(id); appendStyleSheet(id, content); } function wrapStyle(id, css) { return ``; } function generateThemeCSS(name, primary, text) { // language=CSS return ` .${name}, [data-theme='${name}'] { --theme-primary-color: ${primary}; --theme-primary-text-color: ${text}; } `; } function getNormalThemeCSS(theme) { return generateThemeCSS(theme.name, theme[useSettings().themeShade], useSettings().themeShade < 500 ? theme['900'] : theme['50']); } function getCustomThemeCSS(hex) { return generateThemeCSS('custom', hex, getTextColor(hex)); } // Add custom Themes to the page function addTheme(theme) { return wrapStyle(theme.name, getNormalThemeCSS(theme)); } function addCustomTheme(hex) { replaceStyleSheet('custom', getCustomThemeCSS(hex)); } const themes = () => Object.values(colors); const themesSelector = [...Object.keys(colors).map((color) => colors[color].name)].map((theme) => ` ${IconCheck} `); function refreshThemes() { themes().forEach((theme) => replaceStyleSheet(theme.name, getNormalThemeCSS(theme))); replaceStyleSheet('custom', getCustomThemeCSS(useSettings().customTheme)); } const themesCSS = themes().map(addTheme).join('') + wrapStyle('custom', getCustomThemeCSS(useSettings().customTheme)); // https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/11.4.20/sweetalert2.css // .swal2-title -> #swal2-title // language=CSS const sweetalert = `.swal2-popup.swal2-toast{box-sizing:border-box;grid-column:1/4!important;grid-row:1/4!important;grid-template-columns:1fr 99fr 1fr;padding:1em;overflow-y:hidden;background:#fff;box-shadow:0 0 1px hsla(0deg,0%,0%,.075),0 1px 2px hsla(0deg,0%,0%,.075),1px 2px 4px hsla(0deg,0%,0%,.075),1px 3px 8px hsla(0deg,0%,0%,.075),2px 4px 16px hsla(0deg,0%,0%,.075);pointer-events:all}.swal2-popup.swal2-toast>*{grid-column:2}.swal2-popup.swal2-toast #swal2-title{margin:.5em 1em;padding:0;font-size:1em;text-align:initial}.swal2-popup.swal2-toast .swal2-loading{justify-content:center}.swal2-popup.swal2-toast .swal2-input{height:2em;margin:.5em;font-size:1em}.swal2-popup.swal2-toast .swal2-validation-message{font-size:1em}.swal2-popup.swal2-toast .swal2-footer{margin:.5em 0 0;padding:.5em 0 0;font-size:.8em}.swal2-popup.swal2-toast .swal2-close{grid-column:3/3;grid-row:1/99;align-self:center;width:.8em;height:.8em;margin:0;font-size:2em}.swal2-popup.swal2-toast .swal2-html-container{margin:.5em 1em;padding:0;font-size:1em;text-align:initial}.swal2-popup.swal2-toast .swal2-html-container:empty{padding:0}.swal2-popup.swal2-toast .swal2-loader{grid-column:1;grid-row:1/99;align-self:center;width:2em;height:2em;margin:.25em}.swal2-popup.swal2-toast .swal2-icon{grid-column:1;grid-row:1/99;align-self:center;width:2em;min-width:2em;height:2em;margin:0 .5em 0 0}.swal2-popup.swal2-toast .swal2-icon .swal2-icon-content{display:flex;align-items:center;font-size:1.8em;font-weight:700}.swal2-popup.swal2-toast .swal2-icon.swal2-success .swal2-success-ring{width:2em;height:2em}.swal2-popup.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line]{top:.875em;width:1.375em}.swal2-popup.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=left]{left:.3125em}.swal2-popup.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=right]{right:.3125em}.swal2-popup.swal2-toast .swal2-actions{justify-content:flex-start;height:auto;margin:0;margin-top:.5em;padding:0 .5em}.swal2-popup.swal2-toast .swal2-styled{margin:.25em .5em;padding:.4em .6em;font-size:1em}.swal2-popup.swal2-toast .swal2-success{border-color:#a5dc86}.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-circular-line]{position:absolute;width:1.6em;height:3em;transform:rotate(45deg);border-radius:50%}.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-circular-line][class$=left]{top:-.8em;left:-.5em;transform:rotate(-45deg);transform-origin:2em 2em;border-radius:4em 0 0 4em}.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-circular-line][class$=right]{top:-.25em;left:.9375em;transform-origin:0 1.5em;border-radius:0 4em 4em 0}.swal2-popup.swal2-toast .swal2-success .swal2-success-ring{width:2em;height:2em}.swal2-popup.swal2-toast .swal2-success .swal2-success-fix{top:0;left:.4375em;width:.4375em;height:2.6875em}.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-line]{height:.3125em}.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-line][class$=tip]{top:1.125em;left:.1875em;width:.75em}.swal2-popup.swal2-toast .swal2-success [class^=swal2-success-line][class$=long]{top:.9375em;right:.1875em;width:1.375em}.swal2-popup.swal2-toast .swal2-success.swal2-icon-show .swal2-success-line-tip{-webkit-animation:swal2-toast-animate-success-line-tip .75s;animation:swal2-toast-animate-success-line-tip .75s}.swal2-popup.swal2-toast .swal2-success.swal2-icon-show .swal2-success-line-long{-webkit-animation:swal2-toast-animate-success-line-long .75s;animation:swal2-toast-animate-success-line-long .75s}.swal2-popup.swal2-toast.swal2-show{-webkit-animation:swal2-toast-show .5s;animation:swal2-toast-show .5s}.swal2-popup.swal2-toast.swal2-hide{-webkit-animation:swal2-toast-hide .1s forwards;animation:swal2-toast-hide .1s forwards}.swal2-container{display:grid;position:fixed;z-index:1060;top:0;right:0;bottom:0;left:0;box-sizing:border-box;grid-template-areas:"top-start top top-end" "center-start center center-end" "bottom-start bottom-center bottom-end";grid-template-rows:minmax(-webkit-min-content,auto) minmax(-webkit-min-content,auto) minmax(-webkit-min-content,auto);grid-template-rows:minmax(min-content,auto) minmax(min-content,auto) minmax(min-content,auto);height:100%;padding:.625em;overflow-x:hidden;transition:background-color .1s;-webkit-overflow-scrolling:touch}.swal2-container.swal2-backdrop-show,.swal2-container.swal2-noanimation{background:rgba(0,0,0,.4)}.swal2-container.swal2-backdrop-hide{background:0 0!important}.swal2-container.swal2-bottom-start,.swal2-container.swal2-center-start,.swal2-container.swal2-top-start{grid-template-columns:minmax(0,1fr) auto auto}.swal2-container.swal2-bottom,.swal2-container.swal2-center,.swal2-container.swal2-top{grid-template-columns:auto minmax(0,1fr) auto}.swal2-container.swal2-bottom-end,.swal2-container.swal2-center-end,.swal2-container.swal2-top-end{grid-template-columns:auto auto minmax(0,1fr)}.swal2-container.swal2-top-start>.swal2-popup{align-self:start}.swal2-container.swal2-top>.swal2-popup{grid-column:2;align-self:start;justify-self:center}.swal2-container.swal2-top-end>.swal2-popup,.swal2-container.swal2-top-right>.swal2-popup{grid-column:3;align-self:start;justify-self:end}.swal2-container.swal2-center-left>.swal2-popup,.swal2-container.swal2-center-start>.swal2-popup{grid-row:2;align-self:center}.swal2-container.swal2-center>.swal2-popup{grid-column:2;grid-row:2;align-self:center;justify-self:center}.swal2-container.swal2-center-end>.swal2-popup,.swal2-container.swal2-center-right>.swal2-popup{grid-column:3;grid-row:2;align-self:center;justify-self:end}.swal2-container.swal2-bottom-left>.swal2-popup,.swal2-container.swal2-bottom-start>.swal2-popup{grid-column:1;grid-row:3;align-self:end}.swal2-container.swal2-bottom>.swal2-popup{grid-column:2;grid-row:3;justify-self:center;align-self:end}.swal2-container.swal2-bottom-end>.swal2-popup,.swal2-container.swal2-bottom-right>.swal2-popup{grid-column:3;grid-row:3;align-self:end;justify-self:end}.swal2-container.swal2-grow-fullscreen>.swal2-popup,.swal2-container.swal2-grow-row>.swal2-popup{grid-column:1/4;width:100%}.swal2-container.swal2-grow-column>.swal2-popup,.swal2-container.swal2-grow-fullscreen>.swal2-popup{grid-row:1/4;align-self:stretch}.swal2-container.swal2-no-transition{transition:none!important}.swal2-popup{display:none;position:relative;box-sizing:border-box;grid-template-columns:minmax(0,100%);width:32em;max-width:100%;padding:0 0 1.25em;border:none;border-radius:5px;background:#fff;color:#545454;font-family:inherit;font-size:1rem}.swal2-popup:focus{outline:0}.swal2-popup.swal2-loading{overflow-y:hidden}#swal2-title{position:relative;max-width:100%;margin:0;padding:.8em 1em 0;color:inherit;font-size:1.875em;font-weight:600;text-align:center;text-transform:none;word-wrap:break-word}.swal2-actions{display:flex;z-index:1;box-sizing:border-box;flex-wrap:wrap;align-items:center;justify-content:center;width:auto;margin:1.25em auto 0;padding:0}.swal2-actions:not(.swal2-loading) .swal2-styled[disabled]{opacity:.4}.swal2-actions:not(.swal2-loading) .swal2-styled:hover{background-image:linear-gradient(rgba(0,0,0,.1),rgba(0,0,0,.1))}.swal2-actions:not(.swal2-loading) .swal2-styled:active{background-image:linear-gradient(rgba(0,0,0,.2),rgba(0,0,0,.2))}.swal2-loader{display:none;align-items:center;justify-content:center;width:2.2em;height:2.2em;margin:0 1.875em;-webkit-animation:swal2-rotate-loading 1.5s linear 0s infinite normal;animation:swal2-rotate-loading 1.5s linear 0s infinite normal;border-width:.25em;border-style:solid;border-radius:100%;border-color:#2778c4 transparent #2778c4 transparent}.swal2-styled{margin:.3125em;padding:.625em 1.1em;transition:box-shadow .1s;box-shadow:0 0 0 3px transparent;font-weight:500}.swal2-styled:not([disabled]){cursor:pointer}.swal2-styled.swal2-confirm{border:0;border-radius:.25em;background:initial;background-color:#7066e0;color:#fff;font-size:1em}.swal2-styled.swal2-confirm:focus{box-shadow:0 0 0 3px rgba(112,102,224,.5)}.swal2-styled.swal2-deny{border:0;border-radius:.25em;background:initial;background-color:#dc3741;color:#fff;font-size:1em}.swal2-styled.swal2-deny:focus{box-shadow:0 0 0 3px rgba(220,55,65,.5)}.swal2-styled.swal2-cancel{border:0;border-radius:.25em;background:initial;background-color:#6e7881;color:#fff;font-size:1em}.swal2-styled.swal2-cancel:focus{box-shadow:0 0 0 3px rgba(110,120,129,.5)}.swal2-styled.swal2-default-outline:focus{box-shadow:0 0 0 3px rgba(100,150,200,.5)}.swal2-styled:focus{outline:0}.swal2-styled::-moz-focus-inner{border:0}.swal2-footer{justify-content:center;margin:1em 0 0;padding:1em 1em 0;border-top:1px solid #eee;color:inherit;font-size:1em}.swal2-timer-progress-bar-container{position:absolute;right:0;bottom:0;left:0;grid-column:auto!important;overflow:hidden;border-bottom-right-radius:5px;border-bottom-left-radius:5px}.swal2-timer-progress-bar{width:100%;height:.25em;background:rgba(0,0,0,.2)}.swal2-image{max-width:100%;margin:2em auto 1em}.swal2-close{z-index:2;align-items:center;justify-content:center;width:1.2em;height:1.2em;margin-top:0;margin-right:0;margin-bottom:-1.2em;padding:0;overflow:hidden;transition:color .1s,box-shadow .1s;border:none;border-radius:5px;background:0 0;color:#ccc;font-family:serif;font-family:monospace;font-size:2.5em;cursor:pointer;justify-self:end}.swal2-close:hover{transform:none;background:0 0;color:#f27474}.swal2-close:focus{outline:0;box-shadow:inset 0 0 0 3px rgba(100,150,200,.5)}.swal2-close::-moz-focus-inner{border:0}.swal2-html-container{z-index:1;justify-content:center;margin:1em 1.6em .3em;padding:0;overflow:auto;color:inherit;font-size:1.125em;font-weight:400;line-height:normal;text-align:center;word-wrap:break-word;word-break:break-word}.swal2-checkbox,.swal2-file,.swal2-input,.swal2-radio,.swal2-select,.swal2-textarea{margin:1em 2em 3px}.swal2-file,.swal2-input,.swal2-textarea{box-sizing:border-box;width:auto;transition:border-color .1s,box-shadow .1s;border:1px solid #d9d9d9;border-radius:.1875em;background:0 0;box-shadow:inset 0 1px 1px rgba(0,0,0,.06),0 0 0 3px transparent;color:inherit;font-size:1.125em}.swal2-file.swal2-inputerror,.swal2-input.swal2-inputerror,.swal2-textarea.swal2-inputerror{border-color:#f27474!important;box-shadow:0 0 2px #f27474!important}.swal2-file:focus,.swal2-input:focus,.swal2-textarea:focus{border:1px solid #b4dbed;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,.06),0 0 0 3px rgba(100,150,200,.5)}.swal2-file::-moz-placeholder,.swal2-input::-moz-placeholder,.swal2-textarea::-moz-placeholder{color:#ccc}.swal2-file:-ms-input-placeholder,.swal2-input:-ms-input-placeholder,.swal2-textarea:-ms-input-placeholder{color:#ccc}.swal2-file::placeholder,.swal2-input::placeholder,.swal2-textarea::placeholder{color:#ccc}.swal2-range{margin:1em 2em 3px;background:#fff}.swal2-range input{width:80%}.swal2-range output{width:20%;color:inherit;font-weight:600;text-align:center}.swal2-range input,.swal2-range output{height:2.625em;padding:0;font-size:1.125em;line-height:2.625em}.swal2-input{height:2.625em;padding:0 .75em}.swal2-file{width:75%;margin-right:auto;margin-left:auto;background:0 0;font-size:1.125em}.swal2-textarea{height:6.75em;padding:.75em}.swal2-select{min-width:50%;max-width:100%;padding:.375em .625em;background:0 0;color:inherit;font-size:1.125em}.swal2-checkbox,.swal2-radio{align-items:center;justify-content:center;background:#fff;color:inherit}.swal2-checkbox label,.swal2-radio label{margin:0 .6em;font-size:1.125em}.swal2-checkbox input,.swal2-radio input{flex-shrink:0;margin:0 .4em}.swal2-input-label{display:flex;justify-content:center;margin:1em auto 0}.swal2-validation-message{align-items:center;justify-content:center;margin:1em 0 0;padding:.625em;overflow:hidden;background:#f0f0f0;color:#666;font-size:1em;font-weight:300}.swal2-validation-message::before{content:"!";display:inline-block;width:1.5em;min-width:1.5em;height:1.5em;margin:0 .625em;border-radius:50%;background-color:#f27474;color:#fff;font-weight:600;line-height:1.5em;text-align:center}.swal2-icon{position:relative;box-sizing:content-box;justify-content:center;width:5em;height:5em;margin:2.5em auto .6em;border:.25em solid transparent;border-radius:50%;border-color:#000;font-family:inherit;line-height:5em;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.swal2-icon .swal2-icon-content{display:flex;align-items:center;font-size:3.75em}.swal2-icon.swal2-error{border-color:#f27474;color:#f27474}.swal2-icon.swal2-error .swal2-x-mark{position:relative;flex-grow:1}.swal2-icon.swal2-error [class^=swal2-x-mark-line]{display:block;position:absolute;top:2.3125em;width:2.9375em;height:.3125em;border-radius:.125em;background-color:#f27474}.swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=left]{left:1.0625em;transform:rotate(45deg)}.swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=right]{right:1em;transform:rotate(-45deg)}.swal2-icon.swal2-error.swal2-icon-show{-webkit-animation:swal2-animate-error-icon .5s;animation:swal2-animate-error-icon .5s}.swal2-icon.swal2-error.swal2-icon-show .swal2-x-mark{-webkit-animation:swal2-animate-error-x-mark .5s;animation:swal2-animate-error-x-mark .5s}.swal2-icon.swal2-warning{border-color:#facea8;color:#f8bb86}.swal2-icon.swal2-warning.swal2-icon-show{-webkit-animation:swal2-animate-error-icon .5s;animation:swal2-animate-error-icon .5s}.swal2-icon.swal2-warning.swal2-icon-show .swal2-icon-content{-webkit-animation:swal2-animate-i-mark .5s;animation:swal2-animate-i-mark .5s}.swal2-icon.swal2-info{border-color:#9de0f6;color:#3fc3ee}.swal2-icon.swal2-info.swal2-icon-show{-webkit-animation:swal2-animate-error-icon .5s;animation:swal2-animate-error-icon .5s}.swal2-icon.swal2-info.swal2-icon-show .swal2-icon-content{-webkit-animation:swal2-animate-i-mark .8s;animation:swal2-animate-i-mark .8s}.swal2-icon.swal2-question{border-color:#c9dae1;color:#87adbd}.swal2-icon.swal2-question.swal2-icon-show{-webkit-animation:swal2-animate-error-icon .5s;animation:swal2-animate-error-icon .5s}.swal2-icon.swal2-question.swal2-icon-show .swal2-icon-content{-webkit-animation:swal2-animate-question-mark .8s;animation:swal2-animate-question-mark .8s}.swal2-icon.swal2-success{border-color:#a5dc86;color:#a5dc86}.swal2-icon.swal2-success [class^=swal2-success-circular-line]{position:absolute;width:3.75em;height:7.5em;transform:rotate(45deg);border-radius:50%}.swal2-icon.swal2-success [class^=swal2-success-circular-line][class$=left]{top:-.4375em;left:-2.0635em;transform:rotate(-45deg);transform-origin:3.75em 3.75em;border-radius:7.5em 0 0 7.5em}.swal2-icon.swal2-success [class^=swal2-success-circular-line][class$=right]{top:-.6875em;left:1.875em;transform:rotate(-45deg);transform-origin:0 3.75em;border-radius:0 7.5em 7.5em 0}.swal2-icon.swal2-success .swal2-success-ring{position:absolute;z-index:2;top:-.25em;left:-.25em;box-sizing:content-box;width:100%;height:100%;border:.25em solid rgba(165,220,134,.3);border-radius:50%}.swal2-icon.swal2-success .swal2-success-fix{position:absolute;z-index:1;top:.5em;left:1.625em;width:.4375em;height:5.625em;transform:rotate(-45deg)}.swal2-icon.swal2-success [class^=swal2-success-line]{display:block;position:absolute;z-index:2;height:.3125em;border-radius:.125em;background-color:#a5dc86}.swal2-icon.swal2-success [class^=swal2-success-line][class$=tip]{top:2.875em;left:.8125em;width:1.5625em;transform:rotate(45deg)}.swal2-icon.swal2-success [class^=swal2-success-line][class$=long]{top:2.375em;right:.5em;width:2.9375em;transform:rotate(-45deg)}.swal2-icon.swal2-success.swal2-icon-show .swal2-success-line-tip{-webkit-animation:swal2-animate-success-line-tip .75s;animation:swal2-animate-success-line-tip .75s}.swal2-icon.swal2-success.swal2-icon-show .swal2-success-line-long{-webkit-animation:swal2-animate-success-line-long .75s;animation:swal2-animate-success-line-long .75s}.swal2-icon.swal2-success.swal2-icon-show .swal2-success-circular-line-right{-webkit-animation:swal2-rotate-success-circular-line 4.25s ease-in;animation:swal2-rotate-success-circular-line 4.25s ease-in}.swal2-progress-steps{flex-wrap:wrap;align-items:center;max-width:100%;margin:1.25em auto;padding:0;background:0 0;font-weight:600}.swal2-progress-steps li{display:inline-block;position:relative}.swal2-progress-steps .swal2-progress-step{z-index:20;flex-shrink:0;width:2em;height:2em;border-radius:2em;background:#2778c4;color:#fff;line-height:2em;text-align:center}.swal2-progress-steps .swal2-progress-step.swal2-active-progress-step{background:#2778c4}.swal2-progress-steps .swal2-progress-step.swal2-active-progress-step~.swal2-progress-step{background:#add8e6;color:#fff}.swal2-progress-steps .swal2-progress-step.swal2-active-progress-step~.swal2-progress-step-line{background:#add8e6}.swal2-progress-steps .swal2-progress-step-line{z-index:10;flex-shrink:0;width:2.5em;height:.4em;margin:0 -1px;background:#2778c4}[class^=swal2]{-webkit-tap-highlight-color:transparent}.swal2-show{-webkit-animation:swal2-show .3s;animation:swal2-show .3s}.swal2-hide{-webkit-animation:swal2-hide .15s forwards;animation:swal2-hide .15s forwards}.swal2-noanimation{transition:none}.swal2-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}.swal2-rtl .swal2-close{margin-right:initial;margin-left:0}.swal2-rtl .swal2-timer-progress-bar{right:0;left:auto}.leave-russia-now-and-apply-your-skills-to-the-world{display:flex;position:fixed;z-index:1939;top:0;right:0;bottom:0;left:0;flex-direction:column;align-items:center;justify-content:center;padding:25px 0 20px;background:#20232a;color:#fff;text-align:center}.leave-russia-now-and-apply-your-skills-to-the-world div{max-width:560px;margin:10px;line-height:146%}.leave-russia-now-and-apply-your-skills-to-the-world iframe{max-width:100%;max-height:55.5555555556vmin;margin:16px auto}.leave-russia-now-and-apply-your-skills-to-the-world strong{border-bottom:2px dashed #fff}.leave-russia-now-and-apply-your-skills-to-the-world button{display:flex;position:fixed;z-index:1940;top:0;right:0;align-items:center;justify-content:center;width:48px;height:48px;margin-right:10px;margin-bottom:-10px;border:none;background:0 0;color:#aaa;font-size:48px;font-weight:700;cursor:pointer}.leave-russia-now-and-apply-your-skills-to-the-world button:hover{color:#fff}@-webkit-keyframes swal2-toast-show{0%{transform:translateY(-.625em) rotateZ(2deg)}33%{transform:translateY(0) rotateZ(-2deg)}66%{transform:translateY(.3125em) rotateZ(2deg)}100%{transform:translateY(0) rotateZ(0)}}@keyframes swal2-toast-show{0%{transform:translateY(-.625em) rotateZ(2deg)}33%{transform:translateY(0) rotateZ(-2deg)}66%{transform:translateY(.3125em) rotateZ(2deg)}100%{transform:translateY(0) rotateZ(0)}}@-webkit-keyframes swal2-toast-hide{100%{transform:rotateZ(1deg);opacity:0}}@keyframes swal2-toast-hide{100%{transform:rotateZ(1deg);opacity:0}}@-webkit-keyframes swal2-toast-animate-success-line-tip{0%{top:.5625em;left:.0625em;width:0}54%{top:.125em;left:.125em;width:0}70%{top:.625em;left:-.25em;width:1.625em}84%{top:1.0625em;left:.75em;width:.5em}100%{top:1.125em;left:.1875em;width:.75em}}@keyframes swal2-toast-animate-success-line-tip{0%{top:.5625em;left:.0625em;width:0}54%{top:.125em;left:.125em;width:0}70%{top:.625em;left:-.25em;width:1.625em}84%{top:1.0625em;left:.75em;width:.5em}100%{top:1.125em;left:.1875em;width:.75em}}@-webkit-keyframes swal2-toast-animate-success-line-long{0%{top:1.625em;right:1.375em;width:0}65%{top:1.25em;right:.9375em;width:0}84%{top:.9375em;right:0;width:1.125em}100%{top:.9375em;right:.1875em;width:1.375em}}@keyframes swal2-toast-animate-success-line-long{0%{top:1.625em;right:1.375em;width:0}65%{top:1.25em;right:.9375em;width:0}84%{top:.9375em;right:0;width:1.125em}100%{top:.9375em;right:.1875em;width:1.375em}}@-webkit-keyframes swal2-show{0%{transform:scale(.7)}45%{transform:scale(1.05)}80%{transform:scale(.95)}100%{transform:scale(1)}}@keyframes swal2-show{0%{transform:scale(.7)}45%{transform:scale(1.05)}80%{transform:scale(.95)}100%{transform:scale(1)}}@-webkit-keyframes swal2-hide{0%{transform:scale(1);opacity:1}100%{transform:scale(.5);opacity:0}}@keyframes swal2-hide{0%{transform:scale(1);opacity:1}100%{transform:scale(.5);opacity:0}}@-webkit-keyframes swal2-animate-success-line-tip{0%{top:1.1875em;left:.0625em;width:0}54%{top:1.0625em;left:.125em;width:0}70%{top:2.1875em;left:-.375em;width:3.125em}84%{top:3em;left:1.3125em;width:1.0625em}100%{top:2.8125em;left:.8125em;width:1.5625em}}@keyframes swal2-animate-success-line-tip{0%{top:1.1875em;left:.0625em;width:0}54%{top:1.0625em;left:.125em;width:0}70%{top:2.1875em;left:-.375em;width:3.125em}84%{top:3em;left:1.3125em;width:1.0625em}100%{top:2.8125em;left:.8125em;width:1.5625em}}@-webkit-keyframes swal2-animate-success-line-long{0%{top:3.375em;right:2.875em;width:0}65%{top:3.375em;right:2.875em;width:0}84%{top:2.1875em;right:0;width:3.4375em}100%{top:2.375em;right:.5em;width:2.9375em}}@keyframes swal2-animate-success-line-long{0%{top:3.375em;right:2.875em;width:0}65%{top:3.375em;right:2.875em;width:0}84%{top:2.1875em;right:0;width:3.4375em}100%{top:2.375em;right:.5em;width:2.9375em}}@-webkit-keyframes swal2-rotate-success-circular-line{0%{transform:rotate(-45deg)}5%{transform:rotate(-45deg)}12%{transform:rotate(-405deg)}100%{transform:rotate(-405deg)}}@keyframes swal2-rotate-success-circular-line{0%{transform:rotate(-45deg)}5%{transform:rotate(-45deg)}12%{transform:rotate(-405deg)}100%{transform:rotate(-405deg)}}@-webkit-keyframes swal2-animate-error-x-mark{0%{margin-top:1.625em;transform:scale(.4);opacity:0}50%{margin-top:1.625em;transform:scale(.4);opacity:0}80%{margin-top:-.375em;transform:scale(1.15)}100%{margin-top:0;transform:scale(1);opacity:1}}@keyframes swal2-animate-error-x-mark{0%{margin-top:1.625em;transform:scale(.4);opacity:0}50%{margin-top:1.625em;transform:scale(.4);opacity:0}80%{margin-top:-.375em;transform:scale(1.15)}100%{margin-top:0;transform:scale(1);opacity:1}}@-webkit-keyframes swal2-animate-error-icon{0%{transform:rotateX(100deg);opacity:0}100%{transform:rotateX(0);opacity:1}}@keyframes swal2-animate-error-icon{0%{transform:rotateX(100deg);opacity:0}100%{transform:rotateX(0);opacity:1}}@-webkit-keyframes swal2-rotate-loading{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@keyframes swal2-rotate-loading{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@-webkit-keyframes swal2-animate-question-mark{0%{transform:rotateY(-360deg)}100%{transform:rotateY(0)}}@keyframes swal2-animate-question-mark{0%{transform:rotateY(-360deg)}100%{transform:rotateY(0)}}@-webkit-keyframes swal2-animate-i-mark{0%{transform:rotateZ(45deg);opacity:0}25%{transform:rotateZ(-25deg);opacity:.4}50%{transform:rotateZ(15deg);opacity:.8}75%{transform:rotateZ(-5deg);opacity:1}100%{transform:rotateX(0);opacity:1}}@keyframes swal2-animate-i-mark{0%{transform:rotateZ(45deg);opacity:0}25%{transform:rotateZ(-25deg);opacity:.4}50%{transform:rotateZ(15deg);opacity:.8}75%{transform:rotateZ(-5deg);opacity:1}100%{transform:rotateX(0);opacity:1}}body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown){overflow:hidden}body.swal2-height-auto{height:auto!important}body.swal2-no-backdrop .swal2-container{background-color:transparent!important;pointer-events:none}body.swal2-no-backdrop .swal2-container .swal2-popup{pointer-events:all}body.swal2-no-backdrop .swal2-container .swal2-modal{box-shadow:0 0 10px rgba(0,0,0,.4)}@media print{body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown){overflow-y:scroll!important}body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown)>[aria-hidden=true]{display:none}body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) .swal2-container{position:static!important}}body.swal2-toast-shown .swal2-container{box-sizing:border-box;width:360px;max-width:100%;background-color:transparent;pointer-events:none}body.swal2-toast-shown .swal2-container.swal2-top{top:0;right:auto;bottom:auto;left:50%;transform:translateX(-50%)}body.swal2-toast-shown .swal2-container.swal2-top-end,body.swal2-toast-shown .swal2-container.swal2-top-right{top:0;right:0;bottom:auto;left:auto}body.swal2-toast-shown .swal2-container.swal2-top-left,body.swal2-toast-shown .swal2-container.swal2-top-start{top:0;right:auto;bottom:auto;left:0}body.swal2-toast-shown .swal2-container.swal2-center-left,body.swal2-toast-shown .swal2-container.swal2-center-start{top:50%;right:auto;bottom:auto;left:0;transform:translateY(-50%)}body.swal2-toast-shown .swal2-container.swal2-center{top:50%;right:auto;bottom:auto;left:50%;transform:translate(-50%,-50%)}body.swal2-toast-shown .swal2-container.swal2-center-end,body.swal2-toast-shown .swal2-container.swal2-center-right{top:50%;right:0;bottom:auto;left:auto;transform:translateY(-50%)}body.swal2-toast-shown .swal2-container.swal2-bottom-left,body.swal2-toast-shown .swal2-container.swal2-bottom-start{top:auto;right:auto;bottom:0;left:0}body.swal2-toast-shown .swal2-container.swal2-bottom{top:auto;right:auto;bottom:0;left:50%;transform:translateX(-50%)}body.swal2-toast-shown .swal2-container.swal2-bottom-end,body.swal2-toast-shown .swal2-container.swal2-bottom-right{top:auto;right:0;bottom:0;left:auto}`; // https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css // language=CSS const normalize = `/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}`; // https://cdnjs.cloudflare.com/ajax/libs/nprogress/0.2.0/nprogress.min.css // language=CSS const nprogress = `#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;-webkit-transform:rotate(3deg) translate(0,-4px);-ms-transform:rotate(3deg) translate(0,-4px);transform:rotate(3deg) translate(0,-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner 400ms linear infinite;animation:nprogress-spinner 400ms linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg)}}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}`; // https://cdn.jsdelivr.net/npm/@gerhobbelt/keyscss@1.1.3-6/keys.css // language=CSS const keycss = `.key,kbd{display:inline;display:inline-block;white-space:nowrap;min-width:1em;padding:0.3em 0.4em 0.2em 0.3em;font-style:normal;font-family:"Lucida Grande", Lucida, Arial, sans-serif;text-align:center;text-decoration:none;border-radius:0.3em;border:none;background-color:#505050;background-color:gradient(linear, left top, left bottom, from(#3c3c3c), to(#505050));color:#fafafa;text-shadow:-1px -1px 0 #464646;-webkit-box-shadow:inset 0 0 1px #969696, inset 0 -0.05em 0.4em #505050, 0 0.1em 0 #1e1e1e, 0 0.1em 0.1em rgba(0, 0, 0, 0.3);box-shadow:inset 0 0 1px #969696, inset 0 -0.05em 0.4em #505050, 0 0.1em 0 #1e1e1e, 0 0.1em 0.1em rgba(0, 0, 0, 0.3);font-size:0.85em;line-height:1;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.key[title],kbd[title]{cursor:help}.dark-keys .key,.dark-keys kbd,.key.dark,kbd.dark{display:inline;display:inline-block;white-space:nowrap;min-width:1em;padding:0.3em 0.4em 0.2em 0.3em;font-style:normal;font-family:"Lucida Grande", Lucida, Arial, sans-serif;text-align:center;text-decoration:none;border-radius:0.3em;border:none;background-color:#505050;background-color:gradient(linear, left top, left bottom, from(#3c3c3c), to(#505050));color:#fafafa;text-shadow:-1px -1px 0 #464646;-webkit-box-shadow:inset 0 0 1px #969696, inset 0 -0.05em 0.4em #505050, 0 0.1em 0 #1e1e1e, 0 0.1em 0.1em rgba(0, 0, 0, 0.3);box-shadow:inset 0 0 1px #969696, inset 0 -0.05em 0.4em #505050, 0 0.1em 0 #1e1e1e, 0 0.1em 0.1em rgba(0, 0, 0, 0.3)}.key.light,.light-keys .key,.light-keys kbd,kbd.light{display:inline;display:inline-block;white-space:nowrap;min-width:1em;padding:0.3em 0.4em 0.2em 0.3em;font-style:normal;font-family:"Lucida Grande", Lucida, Arial, sans-serif;text-align:center;text-decoration:none;border-radius:0.3em;border:none;background-color:#fafafa;background-color:gradient(linear, left top, left bottom, from(#d2d2d2), to(#ffffff));color:#323232;text-shadow:0 0 2px #ffffff;-webkit-box-shadow:inset 0 0 1px #ffffff, inset 0 0 0.4em #c8c8c8, 0 0.1em 0 #828282, 0 0.11em 0 rgba(0, 0, 0, 0.4), 0 0.1em 0.11em rgba(0, 0, 0, 0.9);box-shadow:inset 0 0 1px #ffffff, inset 0 0 0.4em #c8c8c8, 0 0.1em 0 #828282, 0 0.11em 0 rgba(0, 0, 0, 0.4), 0 0.1em 0.11em rgba(0, 0, 0, 0.9)}.key.so,.so-keys .key,.so-keys kbd,kbd.so{display:inline;display:inline-block;white-space:nowrap;min-width:1em;padding:0.3em 0.4em 0.2em 0.3em;font-style:normal;font-family:"Lucida Grande", Lucida, Arial, sans-serif;text-align:center;text-decoration:none;border-radius:0.3em;border:none;margin:0 0.1em;padding:0.1em 0.6em;font-family:Arial, "Helvetica Neue", Helvetica, sans-serif;line-height:1.4;color:#242729;text-shadow:0 1px 0 #FFF;background-color:#e1e3e5;border:1px solid #adb3b9;border-radius:0.27272727em;-webkit-box-shadow:0 1px 0 rgba(12, 13, 14, 0.2), 0 0 0 2px #FFF inset;box-shadow:0 1px 0 rgba(12, 13, 14, 0.2), 0 0 0 2px #FFF inset}.github-keys .key,.github-keys kbd,.key.github,kbd.github{display:inline;display:inline-block;white-space:nowrap;min-width:1em;padding:0.3em 0.4em 0.2em 0.3em;font-style:normal;font-family:"Lucida Grande", Lucida, Arial, sans-serif;text-align:center;text-decoration:none;border-radius:0.3em;border:none;padding:0.27272727em 0.45454545em;font-size:68.75%;line-height:0.90909091;color:#444d56;vertical-align:middle;background-color:#fafbfc;border:solid 1px #c6cbd1;border-bottom-color:#959da5;border-radius:0.27272727em;-webkit-box-shadow:inset 0 -1px 0 #959da5;box-shadow:inset 0 -1px 0 #959da5;font-family:"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;-webkit-box-sizing:border-box;box-sizing:border-box;text-shadow:none}`; var sweetalertStyle = [normalize, sweetalert, nprogress, keycss].join('\n'); function head(manga) { return `