// ==UserScript== // @name Fanfiction Search Plus // @namespace https://greasyfork.org/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 // @downloadURL https://update.greasyfork.icu/scripts/35841/Fanfiction%20Search%20Plus.user.js // @updateURL https://update.greasyfork.icu/scripts/35841/Fanfiction%20Search%20Plus.meta.js // ==/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: '