Novel Sites Enhance

Kakuyomu / Narou / Alphapolis auto bookmark & cheering, hightlight author & unreads, enhance history, download as txt

2024-06-08 या दिनांकाला. सर्वात नवीन आवृत्ती पाहा.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

You will need to install an extension such as Tampermonkey to install this script.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name           Novel Sites Enhance
// @name:ja        小説サイト機能強化
// @namespace      https://greasyfork.org/en/users/1264733
// @version        2024-06-08
// @description    Kakuyomu / Narou / Alphapolis auto bookmark & cheering, hightlight author & unreads, enhance history, download as txt
// @description:ja アルファポリス・カクヨム・なろう 自動しおり、自動応援、ハイライト著者と未読小説、強化閲覧履歴、TXTダウンロード。
// @author         LE37
// @license        MIT
// @include        /^https:\/\/kakuyomu\.jp\/my\/antenna\/reading_histories/
// @include        /^https:\/\/kakuyomu\.jp\/my\/antenna\/works/
// @include        /^https:\/\/kakuyomu\.jp\/works\/[0-9]+$/
// @include        /^https:\/\/kakuyomu\.jp\/works\/[0-9]+\/episodes\/[^\/]+$/
// @include        /^https:\/\/syosetu\.com\/favnovelmain\/list\//
// @include        /^https:\/\/ncode\.syosetu\.com\/[A-z0-9]+\/?$/
// @include        /^https:\/\/ncode\.syosetu\.com\/[A-z0-9]+\/[0-9]+\/?$/
// @include        /^https:\/\/yomou\.syosetu\.com\/rireki\/list\/$/
// @include        /^https:\/\/www\.alphapolis\.co\.jp\/mypage\/notification\/index\/110000/
// @include        /^https:\/\/www\.alphapolis\.co\.jp\/novel\/[0-9]+/[0-9]+/episode/[0-9]+$/
// @grant          GM_getValue
// @grant          GM_setValue
// @grant          GM_registerMenuCommand
// ==/UserScript==

(()=>{
	'use strict';

	let gMk;
	switch (location.host) {
		case "kakuyomu.jp":
			gMk = "HUN_K";
			break;
		case "ncode.syosetu.com":
		case "syosetu.com":
		case "yomou.syosetu.com":
			gMk = "HUN_N";
			break;
		case "www.alphapolis.co.jp":
			gMk = "HUN_A";
			break;
	}
	// GM menu
	GM_registerMenuCommand("AutoCheering", CCR);
	GM_registerMenuCommand("UpdateHistory", URH);
	GM_registerMenuCommand("DownloadAllEp", DAS);
	GM_registerMenuCommand("AuthorSelect", ADA);
	GM_registerMenuCommand("UnreadCounts", SUN);
	GM_registerMenuCommand("CustomColour", SUC);
	GM_registerMenuCommand("BtnPosition", BPS);
	// Read list
	const URD = GM_getValue(gMk);
	let tlo = URD ? URD : { ATC:false, AUH:false, SDB:false, FAC:"indigo", FCC:"orange", FUC:"red", FAU:3, FAL:[], RRK:{} };
	let atc = tlo.ATC ? tlo.ATC : false;
	let auh = tlo.AUH ? tlo.AUH : false;
	let sdb = tlo.SDB ? tlo.SDB : false;
	let tac = tlo.FAC ? tlo.FAC : "red";
	let tcc = tlo.FCC ? tlo.FCC : "deepskyblue";
	let tuc = tlo.FUC ? tlo.FUC : "orange";
	let tau = tlo.FAU;
	// Right, down, move dist, icon dist
	let tbl = tlo.FBL ? tlo.FBL : [4, 4, 1, 2];
	let rrk = tlo.RRK ? tlo.RRK : {};
	const tal = tlo.FAL;
	// Save list
	function USV() {
		tlo = { ATC:atc, AUH:auh, SDB:sdb, FAC:tac, FCC:tcc, FUC:tuc, FAU:tau, FBL:tbl, FAL:tal, RRK:rrk };
		GM_setValue(gMk, tlo);
	}
	let sFa, sFb, sFc, sFd, sFe;
	sFa = sFb = sFc = sFd = sFe = false;
	let fAuthor;
	const uRi = location.href;
	const rMb = navigator.userAgent.includes("Mobile");
	const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

	switch (true) {
		case uRi.includes("/my/"):
		case uRi.includes("/favnovelmain/"):
		case uRi.includes("/mypage/"):
			FAV();
			break;
		case /^https:\/\/ncode\.syosetu\.com\/[A-z0-9]+\/?$/.test(uRi): {
			if(document.getElementById("novel_honbun")) {
				EPI();
			} else {
				// Add resume button
				const cKey = location.pathname.split("/")[1];
				if(Object.hasOwn(rrk, cKey)) {
					CRB(cKey, "p.novel_title", "https://ncode.syosetu.com/", "/");
				}
			}
			break;
		}
		case uRi.includes("/rireki"):
			//console.log("DoNothing");
			break;
		case /^https:\/\/kakuyomu\.jp\/works\/[0-9]+$/.test(uRi): {
			// Add resume button
			sleep(2000).then(() => {
				const cKey = location.pathname.split("/")[2];
				if (Object.hasOwn(rrk, cKey) && rrk[cKey].epi.length <= 4) {
					const data = JSON.parse(document.getElementById("__NEXT_DATA__").innerHTML);
					const re = new RegExp("Episode:");
					const keys = data.props.pageProps.__APOLLO_STATE__;
					let i = 1;
					for (let key in keys) {
						if (re.test(key)) {
							if (i === parseInt(rrk[cKey].epi)) {
								if (rrk[cKey].epi !== keys[key].id) {
									rrk[cKey].epi = keys[key].id;
									if (auh) {
										USV();
									}
								}
								break;
							}
							i++;
						}
					}
				}
				CRB(cKey, "a[title]", "https://kakuyomu.jp/works/", "/episodes/");
			});
			break;
		}
		default:
			EPI();
	}

	// Episode page
	function EPI() {
		// eCheer button;
		let eCb;
		let rMf = false;
		switch (gMk) {
			case "HUN_K": {
				eCb = document.getElementById("episodeFooter-action-cheerButton");
				if ( document.getElementById("episodeFooter-action-cheerButton-cheer").classList.contains("isShown")
					&& tal.some(name => document.title.includes('(' + name + ') -')) ) {
					rMf = true;
				}
				ANH(location.pathname.split("/")[2], location.pathname.split("/")[4], document.querySelector("h1.js-vertical-composition-item>a").title, null);
				break;
			}
			case "HUN_N": {
				eCb = document.querySelector("a.js-novelgood_change");
				if ( document.querySelector("div.is-empty")
					&& tal.some(name => document.querySelector('div.contents1 a:nth-child(2)').textContent.includes(name)) ) {
					rMf = true;
				}
				// Auto siori/bookmark
				if (document.querySelector("li.bookmark_now")) {
					sleep(Math.floor((Math.random() * (5000 - 2000 + 1)) + 2000)).then(() => {
						document.querySelector("li.bookmark_now>a").click();
					});
				}
				ANH(location.pathname.split("/")[1], location.pathname.split("/")[2], document.title.split(" - ")[0], null);
				break;
			}
			case "HUN_A":
				eCb = document.getElementById("contentMangaLikeBtnCircle");
				if ( !eCb.classList.contains("max")
					&& tal.some(name => uRi.includes(name)) ) {
					rMf = true;
				}
				break;
		}
		if (auh) {
			USV();
		}
		const ioc = new IntersectionObserver((entries) => {
			if (entries[0].intersectionRatio <= 0) return;
			ioc.disconnect();
			if (rMf) {
				eCb.style.backgroundColor = tcc;
				if (atc) {
					if (gMk === "HUN_A") {
						// Randomnumber = Math.floor(Math.random() * (maximum - minimum + 1)) + minimum;
						let x = 0;
						setInterval(function() {
							if (x < parseInt(eCb.getAttribute("data-content-like-limit"))) {
								//console.log(x);
								eCb.click();
							} else {
								return;
							}
							x++;
						}, Math.floor(Math.random() * (1000 - 500 + 1)) + 500);
					} else {
						eCb.click();
					}
					//console.log("===いいね===");
					sleep(1500).then(() => {
						if (gMk === "HUN_K" && !document.getElementById("episodeFooter-action-cheerButton-cheer").classList.contains("isShown")
						|| gMk === "HUN_N" && !document.querySelector("div.is-empty")
						|| gMk === "HUN_A" && document.getElementById("contentMangaLikeBtnCircle").classList.contains("max") ) {
							eCb.style.backgroundColor = "";
						}
					});
				}
			}
		});
		if (gMk !== "HUN_N" || !document.querySelector("span.p-novelgood-form__status")) {
			ioc.observe(eCb);
		}
	}

	// Favorite page
	function FAV() {
		let fNode, fUnreadCount;
		switch (gMk) {
			case "HUN_K":
				fAuthor = "p.widget-antennaList-author";
				fNode = "li.widget-antennaList-item";
				fUnreadCount = "li.widget-antennaList-unreadEpisodeCount";
				break;
			case "HUN_N":
				fAuthor = "div.p-up-bookmark-item__author>a";
				fNode = "li.p-up-bookmark-item";
				fUnreadCount = "span.p-up-bookmark-item__unread-num";
				break;
			case "HUN_A":
				fAuthor = "h2.title>a";
				fNode = "div.content-main";
				fUnreadCount = "a.disp-order";
				break;
		}
		const tNode = document.querySelectorAll(fNode);
		for(let i = 0; i < tNode.length; i++) {
			const fAuthorTag = tNode[i].querySelector(fAuthor);
			const fAuthorName = (gMk === "HUN_A")
									? fAuthorTag.href.match(/\d+$/)[0]
									: fAuthorTag.textContent;
			fAuthorTag.style.color = CHK(fAuthorTag, fAuthorName)
										? tac
										: "";
			const tUnreadCount = tNode[i].querySelector(fUnreadCount);
			const fCurrent = (gMk === "HUN_A") && tUnreadCount
								? parseInt(tUnreadCount.textContent.match(/[0-9]+/)[0])
								: 0;
			let tUnreadNum;
			if (tUnreadCount) {
				tUnreadNum = (gMk === "HUN_K") ? parseInt(tUnreadCount.textContent.match(/[0-9]+/)[0])
								: (gMk === "HUN_N") ? parseInt(tUnreadCount.textContent)
								: parseInt(tNode[i].querySelector("a.total").textContent.match(/[0-9]+/)[0]) - fCurrent;
			} else {
				tUnreadNum = 0;
			}
			const fUnreadColor = CHK(fAuthorTag, fAuthorName) ? tac
									: tUnreadCount && tUnreadNum > tau ? tuc
									: "";
			if (tUnreadCount) {
				if (gMk === "HUN_K") {
					tNode[i].querySelector("a.widget-antennaList-continueReading").style.color = fUnreadColor;
				} else if (gMk === "HUN_N") {
					tNode[i].querySelector("span.p-up-bookmark-item__unread").style.color = fUnreadColor;
				} else {
					tUnreadCount.style.color = fUnreadColor;
				}
			} else {
				if (gMk === "HUN_A") {
					fAuthorTag.style.color = tuc;
				}
			}
		}
	}
	// Check author name
	function CHK(elem, s) {
		const result = tal.some((v) => s === v);
		if (sFa) {
			elem.style.border = result ? "thin solid fuchsia" : "thin solid dodgerblue";
		} else {
			elem.style.border = "none";
		}
		return result;
	}

	// Add fav author
	function ADA() {
		if (!sFa) {
			sFa = true;
			document.addEventListener("click", AAH, true);
		} else {
			sFa = false;
			document.removeEventListener("click", AAH, true);
			USV();
		}
		document.getElementById("nse_asb").textContent = sFa ? "💖" : "💟";
		FAV();
	}
	// Add author handler
	function AAH(e) {
		e.preventDefault();
		if (e.target.closest(fAuthor)) {
			if (gMk === "HUN_A") {
				UTL(e, e.target.href.match(/\d+$/)[0]);
			} else {
				UTL(e, e.target.textContent);
			}
			FAV();
		}
		return false;
	}
	// Update temp list
	function UTL(e, s) {
		const i = tal.findIndex((v) => v === s);
		if (i !== -1) {
			tal.splice(i,1);
		} else {
			tal.push(s);
		}
		//console.log(tal);
		return tal;
	}
	// Auto cheering
	function CCR() {
		atc = !atc;
		const ttt = atc ? "On" : "Off";
		alert("AutoCheering is " + ttt);
		USV();
	}
	// Auto update reading history
	function URH() {
		auh = !auh;
		const ttt = auh ? "On" : "Off";
		alert("AutoUpdateHistory is " + ttt);
		USV();
	}
	// Set unread number
	function SUN() {
		const t = parseInt(prompt("Enter unread counts", tau), 10);
		if (t >= 0) {
			tau = t;
		} else {
			tau = 3;
			alert("Invalid number, default[3] will be used.");
		}
		USV();
		FAV();
	}
	// Set unread colour
	function SUC() {
		if ( uRi.includes("/my/") 
		|| uRi.includes("/favnovelmain/") 
		|| uRi.includes("/mypage/") ) {
			let ccmu;
			if (!sFc) {
				sFc = true;
				CMU();
				ccmu = document.getElementById("nse_cmu");
				ccmu.style.display = "";
				ccmu.addEventListener("click", CSH, true);
			} else {
				sFc = false;
				ccmu = document.getElementById("nse_cmu");
				ccmu.style.display = "none";
				ccmu.removeEventListener("click", CSH, true);
				USV();
			}
		}
	}
	let tCate;
	// Colour select handler
	function CSH(e) {
		e.preventDefault();
		if (e.target.classList.contains("nse_cca")) {
			e.target.textContent = "▣" + e.target.textContent.slice(1);
			const cca = document.getElementsByClassName("nse_cca");
			for(let i = 0; i < cca.length; i++) {
				cca[i].textContent = cca[i] === e.target ? "◉" + cca[i].textContent.slice(1) : "○" + cca[i].textContent.slice(1);
			}
			tCate = e.target.textContent.slice(1, 2);
			let tctc;
			switch (tCate) {
				case "0":
					tctc = tac;
					break;
				case "1":
					tctc = tcc;
					break;
				case "2":
					tctc = tuc;
					break;
			}
			const ccb = document.getElementsByClassName("nse_cco");
			for(let j = 0; j < ccb.length; j++) {
				ccb[j].textContent = ccb[j].style.color === tctc ? "▣ColourTest" : "▢ColourTest";
			}
		} else if (e.target.classList.contains("nse_cco")) {
			const cca = document.getElementsByClassName("nse_cca");
			for(let i = 0; i < cca.length; i++) {
				if (cca[i].textContent.slice(1, 2) === tCate) cca[i].style.color = e.target.style.color;
			}
			switch (tCate) {
				case "0":
					tac = e.target.style.color;
					break;
				case "1":
					tcc = e.target.style.color;
					break;
				case "2":
					tuc = e.target.style.color;
					break;
			}
			const ccb = document.getElementsByClassName("nse_cco");
			for(let j = 0; j < ccb.length; j++) {
				ccb[j].textContent = ccb[j] === e.target ? "▣ColourTest" : "▢ColourTest";
			}
			FAV();
		}
		return false;
	}
	// Create colour list
	function CMU() {
		if (!document.getElementById("nse_cmu")) {
			const cMenu = document.body.appendChild(document.createElement("div"));
			cMenu.id = "nse_cmu";
			const cCates = [ 'AuthorColour', 'ButtonColour', 'UnreadColour' ];
			cCates.forEach((item, index) => {
				let tctc;
				switch (index) {
					case 0:
						tctc = tac;
						break;
					case 1:
						tctc = tcc;
						break;
					case 2:
						tctc = tuc;
						break;
				}
				const cMc = cMenu.appendChild(document.createElement("p"));
				cMc.classList.add("nse_cca");
				cMc.style = 'position: fixed; bottom: ' + (4+index)*5 + '%; right: 22%; z-index: 9999; color: ' + tctc + '; background-color: #393939; padding: 10px;';
				cMc.type = "button";
				cMc.textContent = "○" + index + ". " + item;
			});
			const colors = ['deepskyblue', 'blue', 'lime', 'green', 'fuchsia', 'indigo', 'orange', 'red'];
			colors.forEach((item, index) => {
				const cMb = cMenu.appendChild(document.createElement("p"));
				cMb.classList.add("nse_cco");
				cMb.style = 'position: fixed; bottom: ' + (4+index)*5 + '%; right: 55%; z-index: 9999; color: ' + item + '; background-color: #F3F3F3; padding: 10px;';
				cMb.type = "button";
				cMb.textContent = "▢ColourTest";
			});
			cMenu.style.display = "none";
		}
	}

	// Custom reading history
	function CRH(elem, upa, upb) {
		if (!document.getElementById("nse_rhd")) {
			const crl = document.getElementById(elem);
			if (gMk === "HUN_K") {
				const gBody = document.querySelector("body#page-my-antenna-worksGuest");
				if (gBody) {
					gBody.style.overflow = "auto";
				}
			}
			crl.innerHTML = '<h2>閲覧履歴:</h2>' + 
							'<div id="nse_rhd" style="margin: 1em 0 0 1em;"></div>';
		}
		document.getElementById("nse_rhd").innerHTML = "";
		Object.keys(rrk).reverse().forEach(k => {
			// Update version patch
			if (!rrk[k].bmk) {
				rrk[k].bmk = null;
			}
			const vbmk = rrk[k].bmk === "1" ? " 💖 "
						: rrk[k].bmk === "2" ? " 🖤 "
						: " ❓ ";
			const vlink = (gMk === "HUN_K" && (rrk[k].epi.length <= 4))
						? '<a href="' + upa + k + '/resume_reading" data="' + rrk[k].epi + '" style="margin-left: 1em;">' + rrk[k].tit + '</a>'
						: '<a href="' + upa + k + upb + rrk[k].epi + '" data="' + rrk[k].epi + '" style="margin-left: 1em;">' + rrk[k].tit + '</a>';
			document.getElementById("nse_rhd").innerHTML += '<p style="font-size: 1em; line-height: 2em;">' +
				'<span class="nse_drh" data="' + k + '" type="button" style="color: red; cursor: pointer;">✖</span>' + 
				'<span style="margin-left: 1em;">' + rrk[k].tim + '</span>' + 
				'<span class="nse_brh" data="' + k + '" style="margin-left: 1em; cursor: pointer;">' + vbmk + '</span>' + 
				vlink +
			'</p>';
		});
	}
	// Create resume button
	function CRB(key, elem, upa, upb) {
		const tbtn = document.querySelector(elem).appendChild(document.createElement("a"));
		tbtn.href = upa + key + upb + rrk[key].epi;
		tbtn.textContent = "▶続きから読む";
		tbtn.style = "margin-left: 1em; color: dodgerblue; cursor: pointer;";
	}
	// Create import button
	function CIB(node, etit, eepi) {
		let bookmark;
		const no = document.getElementsByClassName(node);
		for (let i = no.length - 1; i >= 0; i--) {
			switch (location.host) {
				case "kakuyomu.jp": {
					const cno = no[i].querySelectorAll("li");
					let cepi;
					if (uRi.includes("histo")) {
						cepi = (cno.length === 2)
								? cno[1].textContent.slice(3,-1)
								: (cno[2].textContent.slice(3,-1) - cno[1].textContent.slice(2,-1)).toString();
						bookmark = null;
					} else {
						cepi = (cno.length === 2)
								? cno[0].textContent.slice(3,-1)
								: (cno[1].textContent.slice(3,-1) - cno[0].textContent.slice(2,-1)).toString();
						bookmark = "1";
					}
					ANH(no[i].querySelector(eepi).href.split("/")[4], cepi, no[i].querySelector(etit).textContent, bookmark);
					break;
				}
				case "syosetu.com":
				case "yomou.syosetu.com": {
					let title;
					if (location.host.startsWith("y")) {
						title = no[i].querySelector(etit).textContent.slice(0, 12);
						bookmark = null;
					} else {
						title = no[i].querySelector(etit).textContent.slice(3, 15);
						bookmark = "1";
					}
					ANH(no[i].querySelector(etit).href.split("/")[3], no[i].querySelector(eepi).href.split("/")[4], title, bookmark);
					break;
				}
			}
		}
		USV();
		alert("result: " + JSON.stringify(rrk));
	}
	// Add new history
	function ANH(key, epi, title, bmk) {
		let tim = new Date().toISOString().split('T')[0];
		if ( !uRi.includes("/favnovelmain/") 
		&& !uRi.includes("/antenna/works") ) {
			if (Object.hasOwn(rrk, key)) {
				if ( uRi.includes("_histor")
				|| uRi.includes("/rireki") ) {
					tim = rrk[key].tim;
				}
				bmk = rrk[key].bmk;
				delete rrk[key];
			}
		} else {
			rrk[key].bmk = bmk;
		}
		if (!Object.hasOwn(rrk, key)) {
			if (title.length > 12) {
				title = title.slice(0, 12);
			}
			rrk[key] = {"epi": epi, "tit": title, "tim": tim, "bmk": bmk};
		}
	}

	// Download all switch
	function DAS() {
		sdb = !sdb;
		const eee = sdb ? "On" : "Off";
		alert("Download all episodes is " + eee);
		USV();
	}
	// Narou get episodes from download page
	function NGL(title, url, elem) {
		fetch(url).then((response) => {
			if (response.ok) {
				return response.text();
			}
			throw new Error('Something went wrong');
		})
		.then(async (text) => {
			const doc = new DOMParser().parseFromString(text, 'text/html');
			const data = doc.querySelector(elem).textContent;
			const min = parseInt(prompt("Enter a start episode number", "1"));
			const max = parseInt(prompt("Enter a end episode number", "2"));
			let nlc = "";
			if (min >= 1 && min <= max) {
				const ept = data.split("\n");
				for (let i = 1; i < data.split("\n").length - 1; ) {
					const tit = i + ". " + ept[i];
					const url = uRi + i + "/";
					// download episode base on input range
					if (i >= min && i <= max) {
						await sleep(Math.floor((Math.random() * (10000 - 5000 + 1)) + 5000)).then(() => {
							nlc += tit + ": " + url + "\n";
							//console.log(tit, url);
							GEC(tit, url, "div#novel_honbun");
						});
					}
					i++;
				}
			} else {
				alert("Invalid Inputs");
			}
			SAT(title, nlc);
			alert("File downloads completed");
		})
		.catch((error) => {
			//console.log(error);
		});
	}
	// Kakuyomu get episodes list from novel page
	async function KGL(title, url, elem) {
		const data = JSON.parse(document.getElementById(elem).innerHTML);
		const re = new RegExp("Episode:");
		const keys = data.props.pageProps.__APOLLO_STATE__;
		const min = parseInt(prompt("Enter a start episode number", "1"));
		const max = parseInt(prompt("Enter a end episode number", "2"));
		let nlc = "";
		if (min >= 1 && min <= max) {
			let i = 1;
			for (let key in keys) {
				if (re.test(key)) {
					const ttit = i + ". " + keys[key].title;
					const turl = url + "/episodes/" + keys[key].id;
					// download episode base on input range
					if (i >= min && i <= max) {
						await sleep(Math.floor((Math.random() * (10000 - 5000 + 1)) + 5000)).then(() => {
							nlc += ttit + ": " +turl + "\n";
							//console.log(nlc);
							GEC(ttit, turl, "div.widget-episodeBody");
						});
					}
					i++;
				}
			}
		} else {
			alert("Invalid Inputs");
		}
		SAT(title, nlc);
		alert("File downloads completed");
	}
	// Get episode content
	function GEC(title, url, elem) {
		fetch(url).then((response) => {
			if (response.ok) {
				return response.text();
			}
			throw new Error('Something went wrong');
		})
		.then((text) => {
			const doc = new DOMParser().parseFromString(text, 'text/html');
			const data = doc.querySelector(elem).textContent;
			SAT(title, data);
		})
		.catch((error) => {
			//console.log(error);
		});
	}
	// Download current epicode as txt
	function DCE() {
		let elem, title;
		if (gMk === "HUN_K") {
			elem = "div.widget-episodeBody";
			title = document.querySelector("p.widget-episodeTitle")
					? document.querySelector("p.widget-episodeTitle").textContent
					: document.title.replace(/\s/g,"").match(/[^-]+/);
		} else if (gMk === "HUN_N") {
			elem = "div#novel_honbun";
			title = document.querySelector("p.novel_subtitle")
					? document.querySelector("p.novel_subtitle").textContent
					: document.title.split("-")[1];
		}
		const data = document.querySelector(elem).textContent;
		SAT(title, data);
		alert("File downloads completed");
	}
	// Save as txt
	function SAT(title, text) {
		//console.log(text);
		const a = document.createElement("a");
		a.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent(text);
		a.download = title + '.txt';
		document.body.appendChild(a);
		a.click();
		document.body.removeChild(a);
	}

	// Create float buttons
	CFB();
	function CFB() {
		if (!document.getElementById("nse_fcm")) {
			const cDiv = document.body.appendChild(document.createElement("div"));
			cDiv.id = "nse_fcm";
		}
		const posx = tbl[0], posy = tbl[1], diff = tbl[3];
		BCR("nse_fcb", "💠", "right: " + posx + "em;", "bottom: " + posy + "em;", "");
		BCR("nse_rhb", "🕓", "right: " + (posx - diff) + "em;", "bottom: " + posy + "em;", "none");
		BCR("nse_shb", "💾", "right: " + (posx - diff) + "em;", "bottom: " + (posy + diff) + "em;", "none");
		switch (true) {
			case uRi.includes("/antenna/works"):
			case uRi.includes("/favnovelmain/"):
				BCR("nse_asb", "💟", "right: " + posx + "em;", "bottom: " + (posy - diff) + "em;", "none");
				BCR("nse_csb", "🎨", "right: " + (posx + diff) + "em;", "bottom: " + (posy - diff) + "em;", "none");
				BCR("nse_urb", "🔢", "right: " + (posx + diff) + "em;", "bottom: " + posy + "em;", "none");
				BCR("nse_ihb", "💕", "right: " + posx + "em;", "bottom: " + (posy + diff) + "em;", "none");
				break;
			case uRi.includes("_histor"):
			case uRi.includes("/rireki"):
				BCR("nse_ihb", "💕", "right: " + posx + "em;", "bottom: " + (posy + diff) + "em;", "none");
				break;
			case /^https:\/\/ncode\.syosetu\.com\/[A-z0-9]+/.test(uRi):
			case /^https:\/\/kakuyomu\.jp\/works\/[0-9]+/.test(uRi):
				if (sdb) {
					BCR("nse_dlb", "📥", "right: " + (posx + diff) + "em;", "bottom: " + (posy + diff) + "em;", "none");
				}
			break;
		}
		document.getElementById("nse_fcb").addEventListener("click", SCB, true);
	}
	// Button positon tweak
	function BPS() {
		const cfcb = document.getElementById("nse_fcb");
		const cfcm = document.getElementById("nse_fcm");
		if (!sFe) {
			sFe = true;
			cfcb.removeEventListener("click", SCB, true);
			cfcm.removeEventListener("click", FBH, true);
			const bno = document.getElementsByClassName("nse_cfb");
			let bi = bno.length - 1;
			while (bi >= 0) {
				bno[bi].remove();
				--bi;
			}
			const posx = tbl[0], posy = tbl[1], diff = tbl[3];
			BCR("nse_ptu", "⏫", "right: " + posx + "em;", "bottom: " + (posy + diff) + "em;", "");
			BCR("nse_ptd", "⏬", "right: " + posx + "em;", "bottom: " + (posy - diff) + "em;", "");	
			BCR("nse_ptl", "⏪", "right: " + (posx + diff) + "em;", "bottom: " + posy + "em;", "");
			BCR("nse_ptr", "⏩", "right: " + (posx - diff) + "em;", "bottom: " + posy + "em;", "");
			cfcm.addEventListener("click", PTH, true);
		} else {
			sFe = sFb = false;
			cfcm.removeEventListener("click", PTH, true);
			cfcb.remove();
			const tno = document.getElementsByClassName("nse_cft");
			let ti = tno.length - 1;
			while (ti >= 0) {
				tno[ti].remove();
				--ti;
			}
			USV();
			CFB();
		}
	}
	// Position tweak handler
	function PTH(e) {
		const dist = tbl[2];
		switch (e.target.id) {
			// Reset
			case "nse_fcb": 
				tbl = [4, 4, 1, 2];
				break;
			// Up
			case "nse_ptu":
				tbl[1] = tbl[1] + dist;
				break;
			// Down
			case "nse_ptd":
				tbl[1] = tbl[1] - dist;
				break;
			// Left
			case "nse_ptl":
				tbl[0] = tbl[0] + dist;
				break;
			// Right
			case "nse_ptr":
				tbl[0] = tbl[0] - dist;
				break;
			default:
				//console.log("DoNothing");
		}
		const cfcb = document.getElementById("nse_fcb");
		cfcb.style.right = tbl[0] + "em";
		cfcb.style.bottom = tbl[1] + "em";
	}
	// Show child button
	function SCB() {
		if (!sFb) {
			sFb = true;
			document.getElementById("nse_fcm").addEventListener("click", FBH, true);
		} else {
			sFb = false;
			document.getElementById("nse_fcm").removeEventListener("click", FBH, true);
		}
		const no = document.getElementsByClassName("nse_cfb");
		for (let i = 0; i < no.length; i++) {
			no[i].style.display = sFb ? "" : "none";
		}
	}
	// Buttons handler
	function FBH(e) {
		switch (e.target.id) {
			// Author select button
			case "nse_asb":
				ADA();
				break;
			// Colour setting button
			case "nse_csb":
				SUC();
				break;
			// Unreads setting button
			case "nse_urb":
				SUN();
				break;
			// Reading history button
			case "nse_rhb": {
				if (!document.getElementById("nse_rhp")) {
					const cDiv = document.body.appendChild(document.createElement("div"));
					cDiv.id = "nse_rhp";
					const cPos = !rMb ? " width: 50%; left: 25%;" : " width: 98%; left: 1%;";
					cDiv.style = "position: fixed;" + cPos + " overflow-y: scroll; height: 52%; top:10%; z-index: 9999; background-color: #f1f3f5; display: none;";
				}
				const crhp = document.getElementById("nse_rhp");
				if (!sFd) {
					sFd = true;
					crhp.style.display = "";
					if (gMk === "HUN_K") {
						CRH("nse_rhp", "https://kakuyomu.jp/works/", "/episodes/");
					} else if (gMk === "HUN_N") {
						CRH("nse_rhp", "https://ncode.syosetu.com/", "/");
					}
					crhp.addEventListener("click", RHB, true);
				} else {
					sFd = false;
					crhp.style.display = "none";
					// Clear history
					crhp.innerHTML = "";
					crhp.removeEventListener("click", RHB, true);
					USV();
				}
				break;
			}
			// Save history button
			case "nse_shb": {
				let htit, upa, upb;
				if (gMk === "HUN_K") {
					htit = "カクヨム閲覧履歴";
					upa = "https://kakuyomu.jp/works/";
					upb = "/episodes/";
				} else if (gMk === "HUN_N") {
					htit = "なろう閲覧履歴";
					upa = "https://ncode.syosetu.com/";
					upb = "/";
				}
				let htxt = "";
				Object.keys(rrk).reverse().forEach(k => {
					const vbmk = rrk[k].bmk === "1" ? " 💖 "
								: rrk[k].bmk === "2" ? " 🖤 "
								: " ❓ ";
					let kpt = "";
					let vlink = upa + k + upb + rrk[k].epi;
					if (gMk === "HUN_K") {
						if (rrk[k].epi.length <= 4) {
							kpt = "[" + rrk[k].epi + "]";
							vlink = upa + k + "/resume_reading";
						}
					}
					htxt += rrk[k].tim + vbmk + rrk[k].tit.replace(/\n/g,'') + kpt + ": " + vlink + "\n";
				});
				SAT(htit, htxt);
				alert("File downloads completed");
				break;
			}
			// Import history button
			case "nse_ihb":
				if (gMk === "HUN_K") {
					CIB("widget-antennaList-item", "h4.widget-antennaList-title", "a.widget-antennaList-continueReading");
				} else if (gMk === "HUN_N") {
					if (location.host.startsWith("y")) {
						CIB("p-rireki-item", "a.c-card__title", "div.p-rireki-item__button-link>a.c-button");
					} else {
						CIB("p-up-bookmark-item", "div.p-up-bookmark-item__title>a", "a.c-button--sm");
					}
				}
				break;
			// Download button
			case "nse_dlb":
				if ( /^https:\/\/ncode\.syosetu\.com\/[A-z0-9]+\/?$/.test(uRi)
				|| /^https:\/\/kakuyomu\.jp\/works\/[0-9]+$/.test(uRi) ) {
					// Add download all episodes button
					if (gMk === "HUN_N") {
						NGL(document.title, document.querySelector("ul.undernavi li:nth-child(2) a").href, 'select[name="no"]');
					} else {
						KGL(document.title.split("(")[0], uRi, "__NEXT_DATA__");
					}
				} else {
					// Add download current episodes button
					DCE();
				}
				break;
			default:
				//console.log("DoNothing");
		}
		return false;
	}
	// Reading history button
	function RHB(e) {
		if (e.target.classList.contains("nse_drh")) {
			// Delete history button
			const key = e.target.getAttribute("data");
			delete rrk[key];
			CRH();
		} else if (e.target.classList.contains("nse_brh")) {
			// Bookmark history button
			const key = e.target.getAttribute("data");
			rrk[key].bmk = rrk[key].bmk === "1" ? "2" : "1";
			CRH();
		}
	}
	// Button creater
	function BCR(id, text, posx, posy, show) {
		const cButton = document.getElementById("nse_fcm").appendChild(document.createElement("button"));
		cButton.id = id;
		if (id === "nse_fcb") {
			//console.log("💠");
		} else if (sFe) {
			cButton.classList.add("nse_cft");
		} else {
			cButton.classList.add("nse_cfb");
		}
		cButton.textContent = text;
		cButton.style = "position: fixed; width: 44px; height: 44px; z-index: 9999; font-size: 200%; opacity: 50%; cursor:pointer; border: none; padding: unset;" + posx + posy;
		cButton.type = "button";
		cButton.style.display = show;
	}
})();