Greasy Fork

来自缓存

Greasy Fork is available in English.

Fanfiction Search Plus

Give more options to search

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        Fanfiction Search Plus
// @namespace   http://greasyfork.icu/users/102866
// @description Give more options to search
// @include     https://www.fanfiction.net/*
// @require     https://code.jquery.com/jquery-3.6.0.min.js
// @require     https://cdnjs.cloudflare.com/ajax/libs/list.js/2.3.1/list.min.js
// @author      TiLied
// @version     0.1.02
// @grant       GM_listValues
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_deleteValue
// @require     https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @grant       GM.listValues
// @grant       GM.getValue
// @grant       GM.setValue
// @grant       GM.deleteValue
// ==/UserScript==

let requestDelay = 5000, //robots.txt crawl-delay
	whatPage = 0,
	fanName,
	section,
	listTrue = false,
	_timerCount = 0;

const oneSecond = 1000,
	oneDay = oneSecond * 60 * 60 * 24,
	oneWeek = oneDay * 7,
	oneMonth = oneWeek * 4,
	mRatingAndUpTime = "?&srt=1&r=10";

//prefs
let ff = {},
	debug = false;

/**
* ENUM, BECAUSE WHY NOT ¯\_(ツ)_/¯
* SEE FUNCTION GetPage()
*/
var Page;
(function (Page)
{
	
	Page[Page["ErrorNothing"] = 0] = "ErrorNothing";
	Page[Page["front"] = 1] = "front";
	Page[Page["anime"] = 2] = "anime";
	Page[Page["book"] = 3] = "book";
	Page[Page["cartoon"] = 4] = "cartoon";
	Page[Page["comic"] = 5] = "comic";
	Page[Page["game"] = 6] = "game";
	Page[Page["misc"] = 7] = "misc";
	Page[Page["play"] = 8] = "play";
	Page[Page["movie"] = 9] = "movie";
	Page[Page["tv"] = 10] = "tv";
	Page[Page["Crossovers"] = 11] = "Crossovers";
	Page[Page["ErrorNothing1"] = 12] = "ErrorNothing1";
	Page[Page["ErrorNothing11"] = 100] = "ErrorNothing11";
})(Page || (Page = {}));

//Start
//Function main
void async function Main()
{
	requestDelay += 1000;

	console.log("Fanfiction Search Plus v" + GM.info.script.version + " initialization");
	//Place CSS in head
	SetCSS();
	//Set settings or create
	SetSettings(function ()
	{
		//Check on what page we are and switch. Currently only on pin page
		SwitchPage();
		//Place UI Options
		//SetOption();
		console.log("Page number: " + whatPage + "/" + Page[whatPage] + " page");
	});
}();
//Function main
//End

//Start
//Functions GM_VALUE
async function SetSettings(callBack)
{
	//DeleteValues("all");
	//THIS IS ABOUT fanfiction
	if (HasValue("fsp_ff", JSON.stringify(ff)))
	{
		ff = JSON.parse(await GM.getValue("fsp_ff"));
		console.log(ff);
		SetFFObj();
	}

	//Console log prefs with value
	console.log("*prefs:");
	console.log("*-----*");
	var vals = await GM.listValues();

	//Find out that var in for block is not local... Seriously js?
	for (let i = 0; i < vals.length; i++)
	{
		let str = await GM.getValue(vals[i]);
		console.log("*" + vals[i] + ":" + str);
		const byteSize = str => new Blob([str]).size;
		console.log("Size cache: " + FormatBytes(byteSize(str)) + "");
	}
	console.log("*-----*");

	callBack();
}

//Check if value exists or not.  optValue = Optional
async function HasValue(nameVal, optValue)
{
	var vals = await GM.listValues();

	if (vals.length === 0)
	{
		if (optValue !== undefined)
		{
			GM.setValue(nameVal, optValue);
			return true;
		} else
		{
			return false;
		}
	}

	if (typeof nameVal !== "string")
	{
		return alert("name of value: '" + nameVal + "' are not string");
	}

	for (let i = 0; i < vals.length; i++)
	{
		if (vals[i] === nameVal)
		{
			return true;
		}
	}

	if (optValue !== undefined)
	{
		GM.setValue(nameVal, optValue);
		return true;
	} else
	{
		return false;
	}
}

//Delete Values
async function DeleteValues(nameVal)
{
	var vals = await GM.listValues();

	if (vals.length === 0 || typeof nameVal !== "string")
	{
		return;
	}

	switch (nameVal)
	{
		case "all":
			for (let i = 0; i < vals.length; i++)
			{
				GM.deleteValue(vals[i]);
			}
			break;
		case "old":
			for (let i = 0; i < vals.length; i++)
			{
				if (vals[i] === "debug" || vals[i] === "debugA")
				{
					GM.deleteValue(vals[i]);
				}
			}
			break;
		default:
			for (let i = 0; i < vals.length; i++)
			{
				if (vals[i] === nameVal)
				{
					GM.deleteValue(nameVal);
				}
			}
			break;
	}
}

///Update gm value what:"cache","options"
function UpdateGM(what)
{
	var gmff;

	switch (what)
	{
		case "ff":
			gmff = JSON.stringify(ff);
			GM.setValue("fsp_ff", gmff);
			break;
		case "options":
			gmVal = JSON.stringify(options);
			GM_setValue("imdbe_options", gmVal);
			break;
		default:
			alert("fun:UpdateGM(" + what + "). default switch");
			break;
	}
}
//Functions GM_VALUE
//End

//Start
//Functions create object fanfiction and cache
function SetFFObj()
{
	//Version
	if (typeof ff.version === "undefined")
	{
		ff.version = GM.info.script.version;
		version = ff.version;
	} else
	{
		version = ff.version;
		if (version !== GM.info.script.version)
		{
			ff.version = GM.info.script.version;
			version = ff.version;
		}
	}

	//Fetch
	if (typeof ff.fetch === "undefined")
	{
		ff.fetch = false;
		//version = ff.version;
	} else
	{
		//version = ff.version;
		//if (version !== GM.info.script.version)
		//{
		//	ff.version = GM.info.script.version;
		//	version = ff.version;
		//}
	}

	//Fanfiction
	if (typeof ff.fanfiction === "undefined")
	{
		ff.fanfiction = {};
	}

	if (debug) console.log(ff);
}
//Functions create object option and cache
//End

//Start
//Functions Get on what page are we and switch
function SwitchPage()
{
	switch (GetPage(document.URL))
	{
		case 1:
			console.log("front");
			break;
		case 2:
			section = Page[whatPage];
			SetUpForAnime(document.URL);
			break;
		case 3:
			section = Page[whatPage];
			SetUpForBook(document.URL);
			break;
		case 4:
			section = Page[whatPage];
			SetUpForCartoon(document.URL);
			break;
		case 5:
			section = Page[whatPage];
			SetUpForComic(document.URL);
			break;
		case 6:
			section = Page[whatPage];
			SetUpForGame(document.URL);
			break;
		case 7:
			section = Page[whatPage];
			SetUpForMisc(document.URL);
			break;
		case 8:
			section = Page[whatPage];
			SetUpForPlay(document.URL);
			break;
		case 9:
			section = Page[whatPage];
			SetUpForMovie(document.URL);
			break;
		case 10:
			section = Page[whatPage];
			SetUpForTv(document.URL);
			break;
		case 11:
			section = Page[whatPage];
			if (typeof ff.fanfiction[section] === "undefined")
				ff.fanfiction[section] = {};

			fanName = document.URL.match(/\.net\/(.+)\//)[1];

			if (debug) console.log(fanName);

			if (typeof ff.fanfiction[section][fanName] === "undefined" || ff.fanfiction[section][fanName].length === 0)
			{
				if (ff.fetch)
				{
					setTimeout(FetchFics, 1000);
				}
				ff.fanfiction[section][fanName] = [];
				UI("first");
			} else
			{
				if (ff.fetch)
				{
					setTimeout(FetchFics, 1000);
				}
				UI("normal");
				//TODO check updates and etc 
			}

		//console.log($("center:first > a:last-child").trigger());
		//ParseFic($(".z-list")[4]);
			break;
		default:
			break;
	}
}

//On what page are we?
function GetPage(url)
{
	/*
	1-front page
	2-anime page
	3-book page
	4-cartoon page
	5-comic page
	6-game page
	7-misc page
	8-play page
	9-movie page
	10-tv page
	11-Crossovers/ page
	12-Crossover/ page
	13-Crossover/ page
	14-Crossover/ page
	15-Crossover/ page
	100-anything else
	*/
	const reg = new RegExp("https:\\/\\/www\\.fanfiction\\.net");

		if (document.location.pathname === "/")
		{
			whatPage = 1;
		} else if (url.match(new RegExp(reg.source + "/anime", "i")))
		{
			whatPage = 2;
		} else if (url.match(new RegExp(reg.source + "/book", "i")))
		{
			whatPage = 3;
		} else if (url.match(new RegExp(reg.source + "/cartoon", "i")))
		{
			whatPage = 4;
		} else if (url.match(new RegExp(reg.source + "/comic", "i")))
		{
			whatPage = 5;
	} else if (url.match(new RegExp(reg.source + "/game", "i")))
		{
			whatPage = 6;
	} else if (url.match(new RegExp(reg.source + "/misc", "i")))
		{
			whatPage = 7;
	} else if (url.match(new RegExp(reg.source + "/play", "i")))
		{
			whatPage = 8;
	} else if (url.match(new RegExp(reg.source + "/movie", "i")))
		{
			whatPage = 9;
	} else if (url.match(new RegExp(reg.source + "/tv", "i")))
		{
			whatPage = 10;
		} else if (url.match("-Crossovers"))
		{
			whatPage = 11;
	} else if (url.match(new RegExp(reg.source + "/crossover", "i")))
		{
			whatPage = 12;
	} else if (url.match(new RegExp(reg.source + "/crossover", "i")))
		{
			whatPage = 13;
	} else if (url.match(new RegExp(reg.source + "/crossover", "i")))
		{
			whatPage = 14;
	} else if (url.match(new RegExp(reg.source + "/crossover", "i")))
		{
			whatPage = 15;
		} else 
		{
			whatPage = 100;
		}
	return whatPage;
}
//Functions Get on what page are we and switch
//End

//-------------------------
//SET UP STUFF BELOW
//-------------------------
function SetUpForAnime(url)
{
		if (typeof ff.fanfiction[section] === "undefined")
		{
			ff.fanfiction[section] = {};
			//Update GM TODO
		}

		fanName = url.match(/anime\/(\S+)\//)[1]; //TODO!!!!!!!!!!!!!!!!!!!!

		
		if (debug) console.log(fanName);

		if (typeof ff.fanfiction[section][fanName] === "undefined" || ff.fanfiction[section][fanName].length === 0)
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			ff.fanfiction[section][fanName] = [];
			UI("first");
		} else
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			UI("normal");
			//TODO check updates and etc 
		}

		//console.log($("center:first > a:last-child").trigger());
		//ParseFic($(".z-list")[4]);
}
function SetUpForBook(url)
{
		if (typeof ff.fanfiction[section] === "undefined")
		{
			ff.fanfiction[section] = {};
			//Update GM TODO
		}

		fanName = url.match(/book\/(\S+)\//)[1]; //TODO!!!!!!!!!!!!!!!!!!!!

		if (debug) console.log(fanName);

		if (typeof ff.fanfiction[section][fanName] === "undefined" || ff.fanfiction[section][fanName].length === 0)
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			ff.fanfiction[section][fanName] = [];
			UI("first");
		} else
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			UI("normal");
			//TODO check updates and etc 
		}

		//console.log($("center:first > a:last-child").trigger());
		//ParseFic($(".z-list")[4]);
}
function SetUpForCartoon(url)
{
		if (typeof ff.fanfiction[section] === "undefined")
		{
			ff.fanfiction[section] = {};
			//Update GM TODO
		}

		fanName = url.match(/cartoon\/(\S+)\//)[1]; //TODO!!!!!!!!!!!!!!!!!!!!

		if (debug) console.log(fanName);

		if (typeof ff.fanfiction[section][fanName] === "undefined" || ff.fanfiction[section][fanName].length === 0)
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			ff.fanfiction[section][fanName] = [];
			UI("first");
		} else
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			UI("normal");
			//TODO check updates and etc 
		}

		//console.log($("center:first > a:last-child").trigger());
		//ParseFic($(".z-list")[4]);
}
function SetUpForComic(url)
{
		if (typeof ff.fanfiction[section] === "undefined")
		{
			ff.fanfiction[section] = {};
			//Update GM TODO
		}

		fanName = url.match(/comic\/(\S+)\//)[1]; //TODO!!!!!!!!!!!!!!!!!!!!

		if (debug) console.log(fanName);

		if (typeof ff.fanfiction[section][fanName] === "undefined" || ff.fanfiction[section][fanName].length === 0)
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			ff.fanfiction[section][fanName] = [];
			UI("first");
		} else
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			UI("normal");
			//TODO check updates and etc 
		}

		//console.log($("center:first > a:last-child").trigger());
		//ParseFic($(".z-list")[4]);
}
function SetUpForGame(url)
{
		if (typeof ff.fanfiction[section] === "undefined")
		{
			ff.fanfiction[section] = {};
			//Update GM TODO
		}

		fanName = url.match(/game\/(\S+)\//)[1]; //TODO!!!!!!!!!!!!!!!!!!!!

		if (debug) console.log(fanName);

		if (typeof ff.fanfiction[section][fanName] === "undefined" || ff.fanfiction[section][fanName].length === 0)
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			ff.fanfiction[section][fanName] = [];
			UI("first");
		} else
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			UI("normal");
			//TODO check updates and etc 
		}

		//console.log($("center:first > a:last-child").trigger());
		//ParseFic($(".z-list")[4]);
}
function SetUpForMisc(url)
{
		if (typeof ff.fanfiction[section] === "undefined")
		{
			ff.fanfiction[section] = {};
			//Update GM TODO
		}

		fanName = url.match(/misc\/(\S+)\//)[1]; //TODO!!!!!!!!!!!!!!!!!!!!

		if (debug) console.log(fanName);

		if (typeof ff.fanfiction[section][fanName] === "undefined" || ff.fanfiction[section][fanName].length === 0)
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			ff.fanfiction[section][fanName] = [];
			UI("first");
		} else
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			UI("normal");
			//TODO check updates and etc 
		}

		//console.log($("center:first > a:last-child").trigger());
		//ParseFic($(".z-list")[4]);
}
function SetUpForPlay(url)
{
		if (typeof ff.fanfiction[section] === "undefined")
		{
			ff.fanfiction[section] = {};
			//Update GM TODO
		}

		fanName = url.match(/play\/(\S+)\//)[1]; //TODO!!!!!!!!!!!!!!!!!!!!

		if (debug) console.log(fanName);

		if (typeof ff.fanfiction[section][fanName] === "undefined" || ff.fanfiction[section][fanName].length === 0)
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			ff.fanfiction[section][fanName] = [];
			UI("first");
		} else
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			UI("normal");
			//TODO check updates and etc 
		}

		//console.log($("center:first > a:last-child").trigger());
		//ParseFic($(".z-list")[4]);
}
function SetUpForMovie(url)
{
		if (typeof ff.fanfiction[section] === "undefined")
		{
			ff.fanfiction[section] = {};
			//Update GM TODO
		}

		fanName = url.match(/movie\/(\S+)\//)[1]; //TODO!!!!!!!!!!!!!!!!!!!!

		if (debug) console.log(fanName);

		if (typeof ff.fanfiction[section][fanName] === "undefined" || ff.fanfiction[section][fanName].length === 0)
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			ff.fanfiction[section][fanName] = [];
			UI("first");
		} else
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			UI("normal");
			//TODO check updates and etc 
		}

		//console.log($("center:first > a:last-child").trigger());
		//ParseFic($(".z-list")[4]);
}
function SetUpForTv(url)
{
		if (typeof ff.fanfiction[section] === "undefined")
		{
			ff.fanfiction[section] = {};
			//Update GM TODO
		}

		fanName = url.match(/tv\/(\S+)\//)[1]; //TODO!!!!!!!!!!!!!!!!!!!!

		if (debug) console.log(fanName);

		if (typeof ff.fanfiction[section][fanName] === "undefined" || ff.fanfiction[section][fanName].length === 0)
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			ff.fanfiction[section][fanName] = [];
			UI("first");
		} else
		{
			if (ff.fetch)
			{
				setTimeout(FetchFics, 1000);
			}
			UI("normal");
			//TODO check updates and etc 
		}

		//console.log($("center:first > a:last-child").trigger());
		//ParseFic($(".z-list")[4]);
}

//-------------------------
//CORE STUFF BELOW
//-------------------------

//Start
//Function parse fic
function ParseFic(div)
{
	try
	{
		var tempFic = {},
			tempSplit,
			indexes = [];

		tempFic.fsp_titleh = "https://www.fanfiction.net" + $(div.firstChild).attr("href");
		tempFic.fsp_Id = $(div.firstChild).attr("href").split("/")[2];
		tempFic.fsp_image = $(div.firstChild.firstChild).attr("data-original") || $(div.firstChild.firstChild).attr("src");
		tempFic.fsp_author = $(div).find("a").filter(function ()
		{
			var str = $(this).attr("href");
			if (str.includes("/u/"))
			{
				return this;
			}
		}).text();
		tempFic.fsp_authorh = "https://www.fanfiction.net" + $(div).find("a").filter(function ()
		{
			var str = $(this).attr("href");
			if (str.includes("/u/"))
			{
				return this;
			}
		}).attr("href");
		tempFic.fsp_title = $(div.firstChild.childNodes[1]).text();
		
		tempFic.fsp_summary = $(div).find(".z-indent").contents().filter(function ()
		{
			return this.nodeType === 3;
		})[0].nodeValue;

		tempSplit = $(div).find(".z-indent > .z-padtop2").html().split(" - ");
		if(debug) console.log(tempSplit);

		tempFic.fsp_rated = $.trim(tempSplit[0].substr(tempSplit[0].indexOf(":")).substring(2));
		tempFic.fsp_lag = $.trim(tempSplit[1]);
		if (tempSplit[3].match("Chapters"))
		{
			if (tempSplit[2].match("/"))
				tempFic.fsp_genres = $.trim(tempSplit[2]).split("/");
			else
				tempFic.fsp_genres = $.trim(tempSplit[2]);
			tempFic.fsp_chapters = Number($.trim(tempSplit[3].substr(tempSplit[3].indexOf(":")).substring(2).split(",").join("")));
			tempFic.fsp_words = Number($.trim(tempSplit[4].substr(tempSplit[4].indexOf(":")).substring(2).split(",").join("")));
		} else
		{
			tempFic.fsp_genres = "none";
			tempFic.fsp_chapters = Number($.trim(tempSplit[2].substr(tempSplit[2].indexOf(":")).substring(2).split(",").join("")));
			tempFic.fsp_words = Number($.trim(tempSplit[3].substr(tempSplit[3].indexOf(":")).substring(2).split(",").join("")));
		}

		for (let i = 0; i < tempSplit.length; i++)
		{
			if (tempSplit[i].match("Reviews"))
				tempFic.fsp_reviews = Number($.trim(tempSplit[i].substr(tempSplit[i].indexOf(":")).substring(2).split(",").join("")));
			if (tempSplit[i].match("Favs"))
				tempFic.fsp_favs = Number($.trim(tempSplit[i].substr(tempSplit[i].indexOf(":")).substring(2).split(",").join("")));
			if (tempSplit[i].match("Follows"))
				tempFic.fsp_follows = Number($.trim(tempSplit[i].substr(tempSplit[i].indexOf(":")).substring(2).split(",").join("")));
			if (tempSplit[i].match("Published"))
				tempFic.fsp_publishedRaw = Number($.trim(tempSplit[i].split('"')[1]));
			if (tempSplit[i].match("Updated"))
				tempFic.fsp_updatedRaw = Number($.trim(tempSplit[i].split('"')[1]));
			if (tempSplit[i].match("Complete"))
				tempFic.fsp_complete = true;
		}
		if (typeof tempFic.fsp_reviews === "undefined")
			tempFic.fsp_reviews = 0;
		if (typeof tempFic.fsp_favs === "undefined")
			tempFic.fsp_favs = 0;
		if (typeof tempFic.fsp_follows === "undefined")
			tempFic.fsp_follows = 0;
		if (typeof tempFic.fsp_updatedRaw === "undefined")
			tempFic.fsp_updatedRaw = 0;
		if (typeof tempFic.fsp_complete === "undefined")
			tempFic.fsp_complete = false;

		if (tempFic.fsp_complete)
		{
			if (!tempSplit[tempSplit.length - 2].match("Published"))
				if (!tempSplit[tempSplit.length - 2].match("Updated"))
					tempFic.fsp_characters = tempSplit[tempSplit.length - 2].split(", ");

			if (tempSplit[tempSplit.length - 2].match(/]/))
			{
				let _r = /]|\[/g;
				tempFic.fsp_characters = tempSplit[tempSplit.length - 2].replace(_r, ", ").split(", ");
				if (tempFic.fsp_characters[0] === "")
					tempFic.fsp_characters.shift();

				let temp = tempSplit[tempSplit.length - 2],
					arr = [];
				if(debug) console.log(temp);
				for (let x = 0; x < temp.length; x++)
					if (temp[x] === "[" || temp[x] === "]")
						indexes.push(x);
				if (debug) console.log(indexes);
				if (indexes.length > 2)
				{
					//TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
					arr.push(temp.slice(indexes[0] + 1, indexes[1]).split(", "));

					arr.push(temp.slice(indexes[2] + 1, indexes[3]).split(", "));
					if (debug) console.log(arr);
					tempFic.fsp_relationships = arr;
				} else
				{
					temp = temp.substring(temp.indexOf("[") + 1, temp.indexOf("]"));
					if (debug) console.log(temp);
					tempFic.fsp_relationships = [temp.split(", ")];
				}
			}
		} else
		{
			if (!tempSplit[tempSplit.length - 1].match("Published"))
				if (!tempSplit[tempSplit.length - 1].match("Updated"))
					tempFic.fsp_characters = tempSplit[tempSplit.length - 1].split(", ");

			if (tempSplit[tempSplit.length - 1].match(/]/))
			{
				let _r = /]|\[/g;
				tempFic.fsp_characters = tempSplit[tempSplit.length - 2].replace(_r, ", ").split(", ");
				if (tempFic.fsp_characters[0] === "")
					tempFic.fsp_characters.shift();

				let temp = tempSplit[tempSplit.length - 1],
					arr = [];
				if (debug) console.log(temp);
				for (let x = 0; x < temp.length; x++)
					if (temp[x] === "[" || temp[x] === "]")
						indexes.push(x);
				if (indexes.length > 2)
				{
					//TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
					arr.push(temp.slice(indexes[0] + 1, indexes[1]).split(", "));

					arr.push(temp.slice(indexes[2] + 1, indexes[3]).split(", "));

					tempFic.fsp_relationships = arr;
				} else
				{
					temp = temp.substring(temp.indexOf("[") + 1, temp.indexOf("]"));
					if (debug) console.log(temp);
					tempFic.fsp_relationships = [temp.split(", ")];
				}
			}
		}

		if (typeof tempFic.fsp_characters === "undefined")
			tempFic.fsp_characters = "none";

		if (typeof tempFic.fsp_relationships === "undefined")
			tempFic.fsp_relationships = "none";

		tempFic.fsp_published = Intl.DateTimeFormat(undefined, { year: 'numeric', month: 'short', day: 'numeric' }).format(new Date(tempFic["fsp_publishedRaw"] * 1000));

		tempFic.fsp_updated = tempFic["fsp_updatedRaw"] === 0 ? 0 : Intl.DateTimeFormat(undefined, { year: 'numeric', month: 'short', day: 'numeric' }).format(new Date(tempFic["fsp_updatedRaw"] * 1000));

		if (debug) console.log(tempFic);

		return tempFic;
	} catch (e) { console.error(e); }
}
//Function parse fic
//End


//Start
//Function fetch fics
async function FetchFics()
{
	var zlist = $(".z-list"),
		last,
		parser = new DOMParser();

	for (let i = 0; i < zlist.length; i++)
	{
		ff.fanfiction[section][fanName].push(ParseFic(zlist[i]));
	}

	UI("upFetchCount");

	setTimeout(async function ()
	{
		last = $("center:first > a:last-child, center:last > a:last-child").prev().attr("href");

		if (typeof last === "undefined")
		{
			let _l = $("center:first > a:last-child").attr("href");
			if (typeof _l === "undefined")
				last = 1;
			else
				last = 2;
		} else
			last = Number(last.substr(last.indexOf("p=") + 2));

		if (debug) console.log(last);

		if (last === 1)
			_done();

		_timerCount = (requestDelay / 1000) * (last - 1);

		if (debug) console.log(_timerCount);

		let _display = document.querySelector('#fsp_timer');

		_startTimer(_timerCount, _display);

		//
		//https://stackoverflow.com/a/44476626
		//
		// Returns a Promise that resolves after "ms" Milliseconds
		const timer = ms => new Promise(res => setTimeout(res, ms))

		async function load()
		{ // We need to wrap the loop into an async function for this to work
			for (let i = 2; i <= last; i++)
			{
				let _url;
				if (section === "Crossovers")
					_url = "https://www.fanfiction.net/" + fanName + "/" + mRatingAndUpTime + "&p=" + i;
				else
					_url = "https://www.fanfiction.net/" + section + "/" + fanName + "/" + mRatingAndUpTime + "&p=" + i;

				if (debug)
				{
					console.log("url = " + _url);
					console.log(i);
				}

				await fetch(_url).then((data) =>
				{
					data.text().then(_d =>
					{
						let doc = parser.parseFromString(_d, "text/html"),
							z = $(doc).find(".z-list");

						if (debug) console.log(doc);
						if (debug) console.log(z);

						for (let x = 0; x < z.length; x++)
						{
							ff.fanfiction[section][fanName].push(ParseFic(z[x]));
						}

						UI("upFetchCount");
					});
				}).catch(e =>
				{
					console.warn(e);
				});

				await timer(requestDelay); // then the created Promise can be awaited

				if (i === last)
					_done();
			}
		}

		load();
		//
		//
		//

	}, 300);
	//Get to the next page and thats go on

	function _done()
	{
		ff.fetch = false;
		UpdateGM("ff");
		alert("Done! You can search now, page will reload.");
		console.log("done!");
		if (section === "Crossovers")
			window.location.href = "https://www.fanfiction.net/" + fanName + "/";
		else
			window.location.href = "https://www.fanfiction.net/" + section + "/" + fanName + "/";
	}

	//
	//https://stackoverflow.com/a/20618517
	//timer
	function _startTimer(duration, display)
	{
		var start = Date.now(),
			diff,
			minutes,
			seconds;
		function timer()
		{
			// get the number of seconds that have elapsed since 
			// startTimer() was called
			diff = duration - (((Date.now() - start) / 1000) | 0);

			// does the same job as parseInt truncates the float
			minutes = (diff / 60) | 0;
			seconds = (diff % 60) | 0;

			minutes = minutes < 10 ? "0" + minutes : minutes;
			seconds = seconds < 10 ? "0" + seconds : seconds;

			display.textContent = minutes + ":" + seconds;

			if (diff <= 0)
			{
				// add one second so that the count down starts at the full duration
				// example 05:00 not 04:59
				start = Date.now() + 1000;
			}
		};
		// we don't want to wait a full second before the timer starts
		timer();
		setInterval(timer, 1000);
	}
}
//Function fetch fics
//End


//Start
//Function Search Filter Sort fics
function SearchFilterSort()
{

	let options = {
			valueNames:
			[
				'fsp_title',
				{ name: 'fsp_titleh', attr: 'href' },
				{ name: 'fsp_image', attr: 'src' },
				'fsp_author',
				{ name: 'fsp_authorh', attr: 'href' },
				'fsp_summary',
				'fsp_rated',
				'fsp_lag',
				'fsp_chapters',
				'fsp_words',
				'fsp_reviews',
				'fsp_favs',
				'fsp_follows',
				'fsp_published',
				{ name: 'fsp_publishedRaw', attr: 'data-xutime' },
				'fsp_updated',
				{ name: 'fsp_updatedRaw', attr: 'data-xutime' },
				'fsp_complete',
				'fsp_characters',
				'fsp_relationships',
				'fsp_Id',
				'fsp_genres'
			],

			page: 25,
			pagination: [{
				name: "paginationTop",
				paginationClass: "paginationTop",
				outerWindow: 2,
				innerWindow: 3
			}, {
				name: "paginationBottom",
				paginationClass: "paginationBottom",
				outerWindow: 2,
				innerWindow: 3
			}],
			item: '<div class="fsp_list z-list zhover zpointer" style="min-height:77px;border-bottom:1px #cdcdcd solid;">\
			<a class="fsp_title fsp_titleh stitle" href=""></a>\
			<img class="fsp_image lazy cimage" style="clear: left; float: left; margin-right: 3px; padding: 2px; border: 1px solid rgb(204, 204, 204); border-radius: 2px; display: block;" src="" data-original="" height= "66" width="50" ></img>\
			by <a class="fsp_author fsp_authorh" href=""></a>\
			<div class="fsp_summary z-indent z-padtop"></div>\
			<span class="z-padtop2 xgray">Rated:</span><span class="fsp_rated z-padtop2 xgray"></span>\
			- <span class="z-padtop2 xgray">Language:</span><span class="fsp_lag z-padtop2 xgray"></span>\
			- <span class="z-padtop2 xgray">Genres:</span><span class="fsp_genres z-padtop2 xgray"></span>\
			- <span class="z-padtop2 xgray">Chapters:</span><span class="fsp_chapters z-padtop2 xgray"></span>\
			- <span class="z-padtop2 xgray">Words:</span><span class="fsp_words z-padtop2 xgray"></span>\
			- <span class="z-padtop2 xgray">Reviews:</span><span class="fsp_reviews z-padtop2 xgray"></span>\
			- <span class="z-padtop2 xgray">Favs:</span><span class="fsp_favs z-padtop2 xgray"></span>\
			- <span class="z-padtop2 xgray">Follows:</span><span class="fsp_follows z-padtop2 xgray"></span>\
			- <span class="z-padtop2 xgray">Published:</span><span class="fsp_published fsp_publishedRaw z-padtop2 xgray"></span>\
			- <span class="z-padtop2 xgray">Updated:</span><span class="fsp_updated fsp_updatedRaw z-padtop2 xgray"></span>\
			- <span class="z-padtop2 xgray">Complete:</span><span class="fsp_complete z-padtop2 xgray"></span>\
			- <span class="z-padtop2 xgray">Characters:</span><span class="fsp_characters z-padtop2 xgray"></span>\
			- <span class="z-padtop2 xgray">Relationships:</span><span class="fsp_relationships z-padtop2 xgray"></span></span>\
			- <span class="z-padtop2 xgray">Id:</span><span class="fsp_Id z-padtop2 xgray"></span>'
	};

	let fics = new List('fsp_main', options, ff.fanfiction[section][fanName]);
		
		$("#fsp_resultCount").text(fics.size());

		$('.fsp_searchAuthor').keyup(function ()
		{
			var searchString = $(this).val();
			fics.search(searchString, ['fsp_author']);
		});

		$('.fsp_searchTitle').keyup(function ()
		{
			var searchString = $(this).val();
			fics.search(searchString, ['fsp_title']);
		});

		fics.on("updated", function ()
		{
			$(".fsp_list").unhighlight();
			var search = $(".search").val() || $(".fsp_searchAuthor").val() || $(".fsp_searchTitle").val();
			var words = search.split(" ");
			$(".fsp_list").highlight(words);
			$("#fsp_resultCount").text(fics.matchingItems.length);
		}); // trigger
		
		$('.fsp_filterChapters, .fsp_filterWords, .fsp_filterReviews, .fsp_filterFavs, .fsp_filterFollows, .fsp_filterPublishedA, .fsp_filterPublishedB, .fsp_filterUpdatedA, .fsp_filterUpdatedB, .fsp_filterCharacters, .fsp_filterRelationships').on('keyup change', function ()
		{
			var number = [];

			var raw = [$(".fsp_filterChapters").val(), $(".fsp_filterWords").val(), $(".fsp_filterReviews").val(), $(".fsp_filterFavs").val(), $(".fsp_filterFollows").val(), $(".fsp_filterPublishedA").val(), $(".fsp_filterPublishedB").val(), $(".fsp_filterUpdatedA").val(), $(".fsp_filterUpdatedB").val(), $(".fsp_filterCharacters").val(), $(".fsp_filterRelationships").val()];

			var fsp = ["fsp_chapters", "fsp_words", "fsp_reviews", "fsp_favs", "fsp_follows", "fsp_publishedRaw", "fsp_publishedRaw", "fsp_updatedRaw", "fsp_updatedRaw", "fsp_characters", "fsp_relationships"];

			var im = [];
			if(debug) console.log(raw);
			for (let i = 0; i < raw.length; i++)
			{
				if (raw[i].match(">"))
				{
					number[i] = Number(raw[i].substr(1));
				} else if (raw[i].match("<"))
				{
					number[i] = Number(raw[i].substr(1));
				} else { number[i] = Number(raw[i]); }

				if (i >= 5 && i < 9)
				{
					if (raw[i] === "")
						number[i] = 0;
					else
						number[i] = new Date(raw[i]).getTime() / 1000;
				} else if (i === 9)
				{
					if (raw[i] === "")
						number[i] = 0;
					else
						number[i] = raw[i].split(",");
				} else if (i === 10)
				{
					if (raw[i] === "")
						number[i] = 0;
					else
						number[i] = raw[i].split(",");
				}
			}
			if (debug) console.log(number);
			fics.filter(function (item)
			{
				for (let i = 0; i < raw.length; i++)
				{
					if (raw[i] === "") continue;

					if (i >= 5 && i < 9)
					{
						if (!IsEven(i))
						{
							if (item.values()[fsp[i]] >= number[i])
							{
								im.push(true);
							}
							else
							{
								return false;
							}
						} else
						{
							if (item.values()[fsp[i]] <= number[i])
							{
								im.push(true);
							}
							else
							{
								return false;
							}
						}
					} else if (i === 9)
					{
						if (item.values()[fsp[i]] === "none")
							if (number[i] !== "none")
								return false;
							else
								return true;
						let temp = item.values()[fsp[i]];
						let yn = [];
						let c = 0;
						for (let j = 0; j < temp.length; j++)
						{
							for (let y = 0; y < number[i].length; y++)
							{
								if (temp[j].toUpperCase().match($.trim(number[i][y].toUpperCase())))
								{
									yn[j] = true;
									c++;
									break;
								}
								else
								{
									yn[j] = false;
								}
							}
						}

						if (c >= number[i].length)
							return true;
						else
							return false;

						/*
						if (yn.every(e => e === false))
							return false;
						else
							return true;*/
					} else if (i === 10)
					{
						if (item.values()[fsp[i]] === "none")
							if (number[i] !== "none")
								return false;
							else
								return true;
						let temp = item.values()[fsp[i]].slice(0);
						let tempR = temp.slice(0);

						for (let a = 0; a < temp.length; a++)
						{
							temp[a] = temp[a].join("/");
						}
						for (let a = 0; a < tempR.length; a++)
						{
							tempR[a] = tempR[a].reverse().join("/");
						}

						//console.log(temp);
						//console.log(tempR);

						let yn = [];
						let c = 0;
						for (let j = 0; j < temp.length; j++)
						{
							for (let y = 0; y < number[i].length; y++)
							{
								if (temp[j].toUpperCase().match($.trim(number[i][y].toUpperCase())) || tempR[j].toUpperCase().match($.trim(number[i][y].toUpperCase())))
								{
									yn[j] = true;
									c++;
									break;
								}
								else
								{
									yn[j] = false;
								}
							}
						}

						if (c >= number[i].length)
							return true;
						else
							return false;

						/*if (yn.every(e => e === false))
							return false;
						else
							return true;*/
					} else
					{
						if (raw[i].match(">"))
						{
							if (item.values()[fsp[i]] >= number[i])
							{
								im.push(true);
							}
							else
							{
								return false;
							}
						} else if (raw[i].match("<"))
						{
							if (item.values()[fsp[i]] <= number[i])
								im.push(true);
							else
								return false;
						} else if (item.values()[fsp[i]] === number[i])
						{
							im.push(true);
						} else
							return false;
					}
				}

				if (im.every(e => e === true))
					return true;
				else
					return false;

			}); // Only items with id > 1 are shown in list

			if (raw.every(e => e === ""))
			{
				fics.filter();
			}

			$("#fsp_resultCount").text(fics.matchingItems.length);
		});
}
//Function Search Filter Sort fics
//End

//-------------------------
//UI/Events STUFF BELOW
//-------------------------

//Start
//Function UI add 
function UI(what)
{
		var a,
			s;
		switch (what)
		{
			case "first":
				{
					let _span = $("#content_wrapper_inner > span:first");
					if (_span.length === 0)
					{
						_span = $("#content_wrapper_inner > a[title='Feed']");
					}

					let _div = $("<div style='float: right;'></div>");

					_div.prepend("<span id=fsp_timer>00:00</span> | ");
					_div.prepend("<span id=fsp_fetchCount>" + ff.fanfiction[section][fanName].length + "</span> | ");
					_div.prepend("<a id=fsp_fetch>Fetch fanfics</a> | ");

					_span.after(_div);
					SetEvents(what, a);
					break;
				}
			case "normal":
				{
					let div = $("<div id=fsp_main></div>").html('<div id=fsp_searchOptions>\
					<div id=fsp_filters>\
					<div id=fsp_filterZeroGrid>\
					<input class="filter fsp_searchAuthor" type="text" placeholder="Search Author" />\
					<input class="filter fsp_searchTitle" type="text" placeholder="Search Title" />\
					</div>\
					<hr size="1" noshade="">\
					<div id=fsp_filterOneGrid>\
					<input class="filter fsp_filterChapters" type="text" pattern="(>|<|)\\d+" placeholder="Filter Chapters (x,>x,<x)" title="Example:>10 means every fanfic with more than 10 chapters" />\
					<input class="filter fsp_filterWords" type="text" pattern="(>|<|)\\d+" placeholder="Filter Words (x,>x,<x)" title="Example:>10000 means every fanfic with more than 10000 words" />\
					<input class="filter fsp_filterReviews" type="text" pattern="(>|<|)\\d+" placeholder="Filter Reviews (x,>x,<x)" title="Example:>15 means every fanfic with more than 15 reviews" />\
					<input class="filter fsp_filterFavs" type="text" pattern="(>|<|)\\d+" placeholder="Filter Favs (x,>x,<x)" title="Example:>150 means every fanfic with more than 150 favs" />\
					<input class="filter fsp_filterFollows" type="text" pattern="(>|<|)\\d+" placeholder="Filter Follows (x,>x,<x)" title="Example:>100 means every fanfic with more than 100 follows" />\
					</div>\
					<hr size="1" noshade="">\
					<div id=fsp_filterTwoGrid>\
					<label for="fsp_filterPublishedA">Published After:</label>\
					<input id="fsp_filterPublishedA" class="filter fsp_filterPublishedA" type="date" placeholder="Published After" title="" />\
					<label for="fsp_filterPublishedB">Published Before:</label>\
					<input id="fsp_filterPublishedB" class="filter fsp_filterPublishedB" type= "date" placeholder="Published Before" title= "" />\
					<label for="fsp_filterUpdatedA">Updated After:</label>\
					<input id="fsp_filterUpdatedA" class="filter fsp_filterUpdatedA" type= "date" placeholder="Updated After" title= "" />\
					<label for="fsp_filterUpdatedB">Updated Before:</label>\
					<input id="fsp_filterUpdatedB" class="filter fsp_filterUpdatedB" type= "date" placeholder="Updated Before" title= "" />\
					</div>\
					<hr size="1" noshade="">\
					<div id=fsp_filterTreeGrid>\
					<input id="fsp_filterCharacters" class="filter fsp_filterCharacters" type="text" placeholder="Characters (x,x)" title="Example:Elsa,Anna" />\
					<input id="fsp_filterRelationships" class="filter fsp_filterRelationships" type= "text" placeholder="Relationships (x/x)" title="Example:Elsa/Anna" />\
					</div>\
					<hr size="1" noshade="">\
					</div>\
					<div id=fsp_sortGrid>\
					<button  class="sort" data-sort="fsp_author">Sort by author</button >\
					<button  class="sort" data-sort="fsp_title">Sort by title</button >\
					<button  class="sort" data-sort="fsp_rated">Sort by rated</button >\
					<button  class="sort" data-sort="fsp_lag">Sort by language</button >\
					<button  class="sort" data-sort="fsp_chapters">Sort by chapters</button >\
					<button  class="sort" data-sort="fsp_words">Sort by words</button >\
					<button  class="sort" data-sort="fsp_reviews">Sort by reviews</button >\
					<button  class="sort" data-sort="fsp_favs">Sort by favs</button >\
					<button  class="sort" data-sort="fsp_follows">Sort by follows</button >\
					<button  class="sort" data-sort="fsp_publishedRaw">Sort by published</button >\
					<button  class="sort" data-sort="fsp_updatedRaw">Sort by updated</button >\
					<button  class="sort" data-sort="fsp_complete">Sort by complete</button >\
					</div>\
					<input class="search" placeholder="Global Search" />\
					<span id=fsp_resultCount></span>\
					<ul class="paginationTop pagination"></ul>\
					</div>\
					<hr size="1" noshade="">\
					<ul class="list">\
					</ul>\
					<ul class="paginationBottom pagination"></ul>\
					</div>');

					let _c = $(".lc-wrapper");

					if (_c.length === 0)
					{
						_c = $("#content_wrapper_inner > center");
					}

					_c.nextAll().wrapAll("<div id=fsp_wrap />");

					$("#fsp_wrap").after(div);
					$(div).hide();

					let _span = $("#content_wrapper_inner > span:first");
					if (_span.length === 0)
					{
						_span = $("#content_wrapper_inner > a[title='Feed']");
					}

					let _div = $("<div style='float: right;'></div>");

					_div.prepend("<span id=fsp_timer>0:00</span> | ");
					_div.prepend("<span id=fsp_fetchCount>" + ff.fanfiction[section][fanName].length + "</span> | ");
					_div.prepend("<a id=fsp_fetch>Update fanfics</a> | ");
					_div.prepend("<a id=fsp_search>Search fanfics</a> | ");

					_span.after(_div);
					SetEvents(what); //TODO EVENTS
					break;
				}
			case "upFetchCount":
				$("#fsp_fetchCount").text(ff.fanfiction[section][fanName].length);
				break;
			default:
				break;
		}
		
}
//Function UI add
//End

//Start
//Function set events 
function SetEvents(what, target)
{
		switch (what)
		{
			case "first":
				$("#fsp_fetch").click(function ()
				{
					if (window.confirm("Be patient. It will take some time to fetch ALL fanfics for this fandom. !!!DO NOT CLOSE AND RELOAD THIS TAB!!!"))
					{
						//Update GM fetch is true
						ff.fetch = true;
						UpdateGM("ff");
						if (section === "Crossovers")
							window.location.href = "https://www.fanfiction.net/" + fanName  + "/" + mRatingAndUpTime;
						else
							window.location.href = "https://www.fanfiction.net/" + section + "/" + fanName + "/" + mRatingAndUpTime;
						//Began fetching
						//MAKE MARKS ON FANFICS GREEN PARSED BLACK/RED NOT AND MIDDLE GROUND
					}
				});
				break;
			case "normal":
				$("#fsp_fetch").click(function ()
				{
					if (window.confirm("Be patient. It will take some time to UPDATE fanfics for this fandom. !!!DO NOT CLOSE AND RELOAD THIS TAB!!!"))
					{
						ff.fetch = true;
						ff.fanfiction[section][fanName].length = 0; //TODO ACTUAL UPDATE
						UpdateGM("ff");
						if (section === "Crossovers")
							window.location.href = "https://www.fanfiction.net/" + fanName + "/" + mRatingAndUpTime;
						else
							window.location.href = "https://www.fanfiction.net/" + section + "/" + fanName + "/" + mRatingAndUpTime;
						//Began fetching
						//MAKE MARKS ON FANFICS GREEN PARSED BLACK/RED NOT AND MIDDLE GROUND
					}
				});
				$("#fsp_search").click(function ()
				{
					$("#fsp_wrap").toggle(1000);
					$("#fsp_main").toggle(1000);
					if (!listTrue)
					{
						SearchFilterSort();
						listTrue = true;
					}
				});
				break;
			default:
				break;
		}


}
//Function set events
//End

//Start
//Function place css
function SetCSS()
{
	$("head").append($("<!--Start of Fanfiction Search Plus v" + GM.info.script.version + " CSS-->"));

	$("head").append($("<style type=text/css></style>").text("#fsp_fetch { \
		cursor: pointer;\
	}"));

	$("head").append($("<style type=text/css></style>").text("#fsp_search { \
		cursor: pointer;\
	}"));

	$("head").append($("<style type=text/css></style>").text(".pagination li { \
		cursor: pointer;\
		display: inline-block;\
		padding: 5px;\
		margin-top: 5px;\
		margin-bottom: 5px;\
		align-content: center;\
	}"));

	$("head").append($("<style type=text/css></style>").text('.sort {\
		padding: 8px 30px;\
		border-radius: 6px;\
		border: none;\
		display: inline-block;\
		color: #fff;\
		text-decoration: none;\
		background-color: #28a8e0;\
		height: 30px;\
	}\
.sort:hover {\
		text-decoration: none;\
		background-color:#1b8aba;\
	}\
.sort:focus {\
		outline: none;\
	}\
.sort:after {\
		width: 0;\
		height: 0;\
		border-left: 5px solid transparent;\
		border-right: 5px solid transparent;\
		border-bottom: 5px solid transparent;\
		content: "";\
		position: relative;\
		top: -10px;\
		right: -5px;\
	}\
.sort.asc:after {\
		width: 0;\
		height: 0;\
		border-left: 5px solid transparent;\
		border-right: 5px solid transparent;\
		border-top: 5px solid #fff;\
		content: "";\
		position: relative;\
		top: 13px;\
		right: -5px;\
	}\
.sort.desc:after {\
		width: 0;\
		height: 0;\
		border-left: 5px solid transparent;\
		border-right: 5px solid transparent;\
		border-bottom: 5px solid #fff;\
		content: "";\
		position: relative;\
		top: -10px;\
		right: -5px;\
}'));

	$("head").append($("<style type=text/css></style>").text('.highlight{background-color:yellow; }'));

	$("head").append($("<style type=text/css></style>").text('.pagination {display: flex;\
	justify-content: center;}'));

	$("head").append($("<style type=text/css></style>").text('.active {font-size: 20px;'));

	$("head").append($("<style type=text/css></style>").text('#fsp_resultCount {display: flex;\
	justify-content: center;\
	font-size: 25px;\
	background-color: #4e4d4d;\
	color: white;\
		}'));

	$("head").append($("<style type=text/css></style>").text('#fsp_sortGrid {display: grid;\
	grid-template-columns: repeat(6, 1fr);\
	grid-gap: 5px;}'));

	$("head").append($("<style type=text/css></style>").text('.search {\
		width: 75%;\
		margin-bottom: 5px;\
		text-align: center;\
		background: linear-gradient(#eee, #fff);\
		border: 1px solid rgba(255, 255, 255, 0.6);\
		box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.4);\
		padding: 5px;\
		position: relative;\
		display: block;\
		margin-top: 5px;\
		margin-right: auto;\
		margin-bottom: 5px;\
		margin-left: auto;}'));

	$("head").append($("<style type=text/css></style>").text('#fsp_filterOneGrid {display: grid;\
	grid-template-columns: repeat(5, 1fr);\
	grid-gap: 5px;\
	margin: 5px;}'));

	$("head").append($("<style type=text/css></style>").text('#fsp_filterTwoGrid {display: grid;\
	grid-template-columns: repeat(4, 0.2fr);\
	grid-gap: 5px;\
	margin: 5px;}'));

	$("head").append($("<style type=text/css></style>").text('#fsp_filterZeroGrid {display: grid;\
	grid-template-columns: repeat(2, 1fr);\
	grid-gap: 5px;\
	margin: 5px;}'));

	$("head").append($("<style type=text/css></style>").text('#fsp_filterTreeGrid {display: grid;\
	grid-template-columns: repeat(2, 1fr);\
	grid-gap: 5px;\
	margin: 5px;}'));

	$("head").append($("<!--End of Fanfiction Search Plus v" + GM.info.script.version + " CSS-->"));
}
//Function place css
//End


//-------------------------
//Tools STUFF BELOW
//-------------------------

/*
 * jQuery Highlight plugin
 *
 * Based on highlight v3 by Johann Burkard
 * http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html
 *
 * Code a little bit refactored and cleaned (in my humble opinion).
 * Most important changes:
 *  - has an option to highlight only entire words (wordsOnly - false by default),
 *  - has an option to be case sensitive (caseSensitive - false by default)
 *  - highlight element tag and class names can be specified in options
 *
 * Usage:
 *   // wrap every occurrence of text 'lorem' in content
 *   // with <span class='highlight'> (default options)
 *   $('#content').highlight('lorem');
 *
 *   // search for and highlight more terms at once
 *   // so you can save some time on traversing DOM
 *   $('#content').highlight(['lorem', 'ipsum']);
 *   $('#content').highlight('lorem ipsum');
 *
 *   // search only for entire word 'lorem'
 *   $('#content').highlight('lorem', { wordsOnly: true });
 *
 *   // search only for the entire word 'C#'
 *   // and make sure that the word boundary can also
 *   // be a 'non-word' character, as well as a regex latin1 only boundary:
 *   $('#content').highlight('C#', { wordsOnly: true , wordsBoundary: '[\\b\\W]' });
 *
 *   // don't ignore case during search of term 'lorem'
 *   $('#content').highlight('lorem', { caseSensitive: true });
 *
 *   // wrap every occurrence of term 'ipsum' in content
 *   // with <em class='important'>
 *   $('#content').highlight('ipsum', { element: 'em', className: 'important' });
 *
 *   // remove default highlight
 *   $('#content').unhighlight();
 *
 *   // remove custom highlight
 *   $('#content').unhighlight({ element: 'em', className: 'important' });
 *
 *
 * Copyright (c) 2009 Bartek Szopka
 *
 * Licensed under MIT license.
 *
 */

(function (factory)
{
	if (typeof define === 'function' && define.amd)
	{
		// AMD. Register as an anonymous module.
		define(['jquery'], factory);
	} else if (typeof exports === 'object')
	{
		// Node/CommonJS
		factory(require('jquery'));
	} else
	{
		// Browser globals
		factory(jQuery);
	}
}(function (jQuery)
{
	jQuery.extend({
		highlight: function (node, re, nodeName, className, callback)
		{
			if (node.nodeType === 3)
			{
				var match = node.data.match(re);
				if (match)
				{
					// The new highlight Element Node
					var highlight = document.createElement(nodeName || 'span');
					highlight.className = className || 'highlight';
					// Note that we use the captured value to find the real index
					// of the match. This is because we do not want to include the matching word boundaries
					var capturePos = node.data.indexOf(match[1], match.index);

					// Split the node and replace the matching wordnode
					// with the highlighted node
					var wordNode = node.splitText(capturePos);
					wordNode.splitText(match[1].length);

					var wordClone = wordNode.cloneNode(true);
					highlight.appendChild(wordClone);
					wordNode.parentNode.replaceChild(highlight, wordNode);
					if (typeof callback === 'function')
					{
						callback(highlight);
					}
					return 1; //skip added node in parent
				}
			} else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children
				!/(script|style)/i.test(node.tagName) && // ignore script and style nodes
				!(node.tagName === nodeName.toUpperCase() && node.className === className))
			{ // skip if already highlighted
				for (var i = 0; i < node.childNodes.length; i++)
				{
					i += jQuery.highlight(node.childNodes[i], re, nodeName, className, callback);
				}
			}
			return 0;
		}
	});

	jQuery.fn.unhighlight = function (options)
	{
		var settings = {
			className: 'highlight',
			element: 'span'
		};

		jQuery.extend(settings, options);

		return this.find(settings.element + '.' + settings.className).each(function ()
		{
			var parent = this.parentNode;
			parent.replaceChild(this.firstChild, this);
			parent.normalize();
		}).end();
	};

	jQuery.fn.highlight = function (words, options, callback)
	{
		var settings = {
			className: 'highlight',
			element: 'span',
			caseSensitive: false,
			wordsOnly: false,
			wordsBoundary: '\\b'
		};

		jQuery.extend(settings, options);

		if (typeof words === 'string')
		{
			words = [words];
		}
		words = jQuery.grep(words, function (word, i)
		{
			return word !== '';
		});
		words = jQuery.map(words, function (word, i)
		{
			return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
		});

		if (words.length === 0)
		{
			return this;
		}

		var flag = settings.caseSensitive ? '' : 'i';
		// The capture parenthesis will make sure we can match
		// only the matching word
		var pattern = '(' + words.join('|') + ')';
		if (settings.wordsOnly)
		{
			pattern =
				(settings.wordsBoundaryStart || settings.wordsBoundary) +
				pattern +
				(settings.wordsBoundaryEnd || settings.wordsBoundary);
		}
		var re = new RegExp(pattern, flag);

		return this.each(function ()
		{
			jQuery.highlight(this, re, settings.element, settings.className, callback);
		});
	};
}));

function IsEven(n)
{
	return n === parseFloat(n) ? !(n % 2) : void 0;
}

//https://stackoverflow.com/a/18650828
function FormatBytes(bytes, decimals = 2)
{
	if (bytes === 0) return '0 Bytes';

	const k = 1024;
	const dm = decimals < 0 ? 0 : decimals;
	const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

	const i = Math.floor(Math.log(bytes) / Math.log(k));

	return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}